Go Runtime
The Go runtime is the most complete implementation of the AppTheory contract and ships with the broadest middleware and CDK surface. It is a reference implementation, not the source of truth — 271 single-envelope contract fixtures run in Go, TypeScript, and Python, while the remaining two route-algebra and facade-inventory vectors are loaded by Go and CDK-TS tests only.
Install
The Go toolchain resolves modules from the immutable git tag — no registry is involved beyond Go’s standard proxy.```bash go get github.com/theory-cloud/apptheory/v4@v4.2.1
The command is stamped from the latest published release at Pages build time (pages.yml resolves it with
`gh release view`), so the module path and version always match a release that actually exists. Pin a specific release
tag from the [releases page](https://github.com/theory-cloud/AppTheory/releases) when you need a different line.
AppTheory does not publish to the npm or PyPI registries; the Go module is the only language artifact that ships through
Go's normal toolchain path.
Module layout (see `api-snapshots/go.txt` for the exact exported surface):
| Package | Purpose |
| --- | --- |
| `github.com/theory-cloud/apptheory/v4/runtime` | Core runtime: `apptheory.New`, `Context`, `Request`, `Response`, route registration, middleware. |
| `github.com/theory-cloud/apptheory/v4/runtime/mcp` | MCP Streamable HTTP transport, sessions, resumable SSE. |
| `github.com/theory-cloud/apptheory/v4/runtime/mcproutes` | Versioned MCP endpoint and OAuth route algebra. |
| `github.com/theory-cloud/apptheory/v4/runtime/mcpfacade` | Golden-path MCP/OAuth facade registration and metadata composition. |
| `github.com/theory-cloud/apptheory/v4/runtime/oauth` | OAuth protected-resource metadata, PKCE, DCR, token-store helpers. |
| `github.com/theory-cloud/apptheory/v4/testkit` | Deterministic test environment (clock, ID queue, event builders). |
| `github.com/theory-cloud/apptheory/v4/testkit/mcp` | In-process MCP client for unit tests. |
| `github.com/theory-cloud/apptheory/v4/pkg/limited` | DynamoDB-backed cross-instance rate limiter. |
| `github.com/theory-cloud/apptheory/v4/pkg/jobs` | Jobs-ledger primitives. |
| `github.com/theory-cloud/apptheory/v4/pkg/sanitization` | Safe logging helpers. |
## Minimal app
```go
package main
import (
"context"
"encoding/json"
"github.com/aws/aws-lambda-go/lambda"
apptheory "github.com/theory-cloud/apptheory/v4/runtime"
)
func main() {
app := apptheory.New()
app.Get("/ping", func(ctx *apptheory.Context) (*apptheory.Response, error) {
return apptheory.Text(200, "pong"), nil
})
lambda.Start(func(ctx context.Context, event json.RawMessage) (any, error) {
return app.HandleLambda(ctx, event)
})
}
HandleLambda is the only entrypoint you need for any AWS trigger. It detects the event shape and dispatches to the right adapter — see Event Shape Dispatch for the full table.
Tier selection
The default tier is P2. To opt down:
app := apptheory.New(apptheory.WithTier(apptheory.TierP0))
See HTTP Runtime for what each tier includes.
Deterministic tests
The Go testkit fixes time, request IDs, and AWS event shapes so handler tests do not depend on AWS:
func TestHello(t *testing.T) {
env := testkit.NewWithTime(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC))
env.IDs.Queue("req-1")
app := env.App()
app.Get("/hello", func(ctx *apptheory.Context) (*apptheory.Response, error) {
return apptheory.MustJSON(200, map[string]any{
"now_unix": ctx.Now().Unix(),
"id": ctx.NewID(),
}), nil
})
event := testkit.APIGatewayV2Request("GET", "/hello", testkit.HTTPEventOptions{
Headers: map[string]string{"x-request-id": "request-1"},
})
resp := env.InvokeAPIGatewayV2(context.Background(), app, event)
if resp.StatusCode != 200 {
t.Fatalf("expected 200, got %d", resp.StatusCode)
}
}
env.IDs.Queue(...) pre-fills the ID generator so any ctx.NewID() call returns the queued value in order — handler tests stay deterministic across rerolls.
Route registration
Fluent route registration fails closed: invalid patterns, duplicate method/pattern pairs, and nil handlers panic during registration instead of silently producing a dead route. Strict helpers remain deprecated compatibility wrappers when a caller still needs an error-returning shape, and their errors now use canonical AppTheoryError messages where applicable:
if _, err := app.GetStrict("/users/{id}", h); err != nil {
t.Fatal(err)
}
TypeScript and Python follow the same fail-closed registration contract.
For the parameterized four-kind MCP OAuth surface, use mcpfacade.RegisterMCPFacade rather than copying route
patterns. FacadeConfig requires per-kind scopes, an application-owned MCP handler, and one explicit absolute-URL
mode. URLModePublicBaseURL is for front-door/CDN deployments; URLModeRequestHost derives scheme and host from each
request for direct API Gateway custom domains and tests. Optional HandlerFactory plug points attach app-owned
authorize/token behavior as a pair. See Go MCP Facade Helper.
HTTP error format
Default HTTP error envelopes are nested under error. AppTheoryError is the canonical client-safe error type for new code; AppError remains supported for code/message compatibility. Any HTTP error whose code string is EMPTY_BODY or INVALID_JSON is remapped by the default nested envelope to app.bad_request. To match Lift’s flat shape and preserve those legacy codes/messages:
app := apptheory.New(apptheory.WithHTTPErrorFormat(apptheory.HTTPErrorFormatFlatLegacy))
// or
app := apptheory.New(apptheory.WithLegacyHTTPErrorShape())
This setting applies to HTTP only. AppSync and WebSocket error payloads keep their existing shapes regardless.
MCP runtime
import (
"context"
"encoding/json"
apptheory "github.com/theory-cloud/apptheory/v4/runtime"
"github.com/theory-cloud/apptheory/v4/runtime/mcp"
)
srv := mcp.NewServer("example", "1.0.0")
_ = srv.Registry().RegisterTool(mcp.ToolDef{
Name: "echo",
Description: "Echo back the provided message.",
InputSchema: json.RawMessage(`{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}`),
}, func(ctx context.Context, args json.RawMessage) (*mcp.ToolResult, error) {
var in struct{ Message string `json:"message"` }
if err := json.Unmarshal(args, &in); err != nil {
return nil, err
}
content := []mcp.ContentBlock{
{Type: "text", Text: in.Message},
}
return &mcp.ToolResult{Content: content}, nil
})
app := apptheory.New()
h := srv.Handler()
app.Post("/mcp", h)
app.Get("/mcp", h)
app.Delete("/mcp", h)
See the MCP Method Surface for the full Streamable HTTP contract, and Remote MCP for OAuth-protected deployments.
What’s verified
The Go runtime passes the 271 generic runner fixtures and directly consumes the MCP route-algebra and facade-inventory tables, covering all 273 contract vectors on every commit. Any behavioral divergence on a shared surface is treated as a contract bug — fix the implementation, or update the fixture and prove the change holds across every participating runtime.
Next reads
- API Reference — full surface table
- HTTP Runtime tiers — P0 / P1 / P2
- Event Shape Dispatch — when
HandleLambdacalls what - Contract Fixtures — the 273-fixture covenant