Documentation
¶
Overview ¶
Package mcphttp carries the HTTP plumbing an MCP server needs around the protocol handler: the middleware chain, the health and RFC 9728 metadata endpoints, and the bearer-token authentication that turns a token into the per-request values tool handlers read.
It lives here rather than in a server's main package because a main package cannot be imported. Authentication especially must not be forked: two copies drift, and the one that drifts is the one that stops rejecting what it should.
Index ¶
- Constants
- func Auth(resources config.Resources, validator *auth.Validator, next http.Handler) http.Handler
- func Chain(h http.Handler, mws ...func(http.Handler) http.Handler) http.Handler
- func Health(mux *http.ServeMux, path string)
- func LimitBody(maxBodySize int64, next http.Handler) http.Handler
- func Log(logger *slog.Logger, skipPaths map[string]struct{}, next http.Handler) http.Handler
- func ProtectedResource(mux *http.ServeMux, resources config.Resources, ...)
- func RequestInfo(next http.Handler) http.Handler
- func SSELog(logger *slog.Logger, next http.Handler) http.Handler
- func Sentry(resources config.Resources, next http.Handler) http.Handler
- func StripProfile(profiles []string, next http.Handler) http.Handler
- func Tracer(resources config.Resources, skipPaths map[string]struct{}, next http.Handler) http.Handler
Constants ¶
const DefaultMaxBodySize = 10 * 1024 * 1024 // 10 MB
DefaultMaxBodySize is the request body limit LimitBody applies unless a server passes its own. Pin the same value into mcp.StreamableHTTPOptions.MaxRequestBodyBytes: left at zero the SDK applies its own smaller default, silently tightening the limit this one advertises.
Variables ¶
This section is empty.
Functions ¶
func Auth ¶
Auth authenticates every request with a bearer token, rejecting the ones that carry no usable credential and populating the context of the ones that do.
Unauthenticated paths are the ones that cannot require a token: health checks, browser favicon probes, and the /.well-known OAuth metadata an unauthorised client fetches to discover where to authorise. A request with no Authorization header on any other path is allowed through only when its JSON body names a protocol method auth.Bypass whitelists, which is how a client negotiates capabilities before it holds a token.
func Chain ¶
Chain applies middlewares so the first argument is the outermost wrapper (runs first on the request, last on the response).
func Log ¶
Log records one line per request: the trace id, the request and response, and the installation and user the token resolved to. Request bodies go through logsafe, which strips anything token-shaped.
skipPaths keeps the noisy endpoints out: health checks fire constantly, and an SSE stream lives as long as the request, so its body is logged by SSELog instead.
func ProtectedResource ¶
func ProtectedResource(mux *http.ServeMux, resources config.Resources, groups []*toolsets.ToolsetGroup)
ProtectedResource registers the RFC 9728 protected-resource metadata an unauthorised client fetches to discover where and for what to authorise.
The advertised scopes come from what the registered groups declare, so a scope a client may ask for always has a group behind it. A group declaring a scope the authorization server does not know will still fail at registration — the authorization server keeps its own catalogue, and this endpoint cannot check against it.
func RequestInfo ¶
RequestInfo attaches the per-request trace and installation info every later middleware and the MCP logging middleware read.
func SSELog ¶
SSELog logs a Server-Sent Events endpoint, where the long-lived GET stream and the short-lived POST message deliveries need different treatment: the stream is logged once when it opens and once when it closes, so a connection held for hours does not sit unlogged, or buffer its body until it ends.
func Sentry ¶
Sentry scopes a Sentry hub to the request so a panic or reported error carries the request that caused it. A no-op when no DSN is configured.
func StripProfile ¶
StripProfile checks whether the request path starts with a known profile name, and if so strips it and sets a "TW-MCP-Profile" header. This lets clients use URLs like "/project-manager/endpoint" to reach "/endpoint" with a profile context.
func Tracer ¶
func Tracer(resources config.Resources, skipPaths map[string]struct{}, next http.Handler) http.Handler
Tracer wraps the handler in Datadog APM tracing, naming each span by method and path. Returns next unchanged when APM is disabled.
skipPaths drops the endpoints not worth a trace: health checks are constant noise, and a long-lived SSE stream does not fit a request span. Every /.well-known path is skipped too.
Types ¶
This section is empty.