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 — the 223 contract fixtures arbitrate when the three runtimes disagree.
Install
The Go toolchain resolves modules from the immutable git tag — no registry is involved beyond Go’s standard proxy.
go get github.com/theory-cloud/apptheory@v1.14.0
Pin a specific release tag from the releases page. 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/runtime |
Core runtime: apptheory.New, Context, Request, Response, route registration, middleware. |
github.com/theory-cloud/apptheory/runtime/mcp |
MCP Streamable HTTP transport, sessions, resumable SSE. |
github.com/theory-cloud/apptheory/runtime/oauth |
OAuth protected-resource metadata, PKCE, DCR, token-store helpers. |
github.com/theory-cloud/apptheory/testkit |
Deterministic test environment (clock, ID queue, event builders). |
github.com/theory-cloud/apptheory/testkit/mcp |
In-process MCP client for unit tests. |
github.com/theory-cloud/apptheory/pkg/limited |
DynamoDB-backed cross-instance rate limiter. |
github.com/theory-cloud/apptheory/pkg/jobs |
Jobs-ledger primitives. |
github.com/theory-cloud/apptheory/pkg/sanitization |
Safe logging helpers. |
Minimal app
package main
import (
"context"
"encoding/json"
"github.com/aws/aws-lambda-go/lambda"
apptheory "github.com/theory-cloud/apptheory/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.
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/runtime"
"github.com/theory-cloud/apptheory/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 all 223 contract fixtures on every commit. Any behavioral divergence between Go, TypeScript, and Python is treated as a contract bug — fix the implementation, or update the fixture and prove the change holds in all three runtimes.
Next reads
- API Reference — full surface table
- HTTP Runtime tiers — P0 / P1 / P2
- Event Shape Dispatch — when
HandleLambdacalls what - Contract Fixtures — the 223-fixture covenant