mcp

package
v1.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 23, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

Package mcp provides runtime MCP transport callers, normalization helpers, and support utilities used by generated MCP adapters.

Package mcp OAuth helpers (oauth.go) provide protected-resource formatting shared by generated MCP servers and hand-written middleware. Pure formatting and URL canonicalization — no token validation happens here.

Index

Constants

View Source
const HeaderKeySessionID = "Mcp-Session-Id"

HeaderKeySessionID is the MCP streamable HTTP session header.

View Source
const ProtectedResourceMetadataPrefix = "/.well-known/oauth-protected-resource"

ProtectedResourceMetadataPrefix is the RFC 9728 well-known prefix for OAuth 2.0 protected-resource metadata.

Variables

View Source
var (
	ErrInvalidSessionID  = errors.New("invalid session ID")
	ErrSessionTerminated = errors.New("session terminated")
)
View Source
var ErrEmptyResourceURL = errors.New("mcp/oauth: cannot derive canonical resource URL")

ErrEmptyResourceURL signals that CanonicalizeResourceURL could not derive a non-empty scheme+host for the request. Callers should treat this as a 400 Bad Request because RFC 9728 requires `resource` to be a fully-qualified URI.

View Source
var ErrInvalidForwardedHeaders = errors.New("mcp/oauth: invalid forwarded identity header")

ErrInvalidForwardedHeaders signals that a forwarded-identity header was present on the request but failed validation (contained control or delimiter characters, or was otherwise unsafe to embed in a URL). Callers that derive the RFC 8707 canonical resource URI from request context should treat this as a 400 Bad Request rather than silently falling back to `r.Host`: a malformed forwarded header signals either a misconfigured proxy or a client probing for injection vectors, and either way the safe response is to reject the request.

Functions

func BuildBearerChallenge added in v1.1.0

func BuildBearerChallenge(resourceMetadataURL, scope string) string

BuildBearerChallenge formats the WWW-Authenticate header value for an OAuth 2.0 Bearer challenge per RFC 6750 §3. resourceMetadataURL is required; scope is appended only when non-empty.

func BuildInvalidTokenChallenge added in v1.1.0

func BuildInvalidTokenChallenge(resourceMetadataURL, errorDescription string) string

BuildInvalidTokenChallenge formats the WWW-Authenticate header for an RFC 6750 §3 invalid_token response (used for audience mismatches, expired tokens, and revoked tokens). errorDescription is optional and should be a short human-readable string safe to surface to clients.

func CanonicalizeChallengeOrigin added in v1.1.1

func CanonicalizeChallengeOrigin(r *http.Request, trustProxy bool) string

CanonicalizeChallengeOrigin derives an origin URL suitable for embedding in a WWW-Authenticate `resource_metadata` parameter. Unlike the strict CanonicalizeResourceURL, this function never returns an error: if forwarded headers are malformed (or the caller did not opt into trusting them), it falls back to the request scheme and Host header so the challenge still points at some reachable origin.

This fallback is deliberate and narrow: a challenge is a formatting artifact inside a 401 response, not an identity claim. Emitting a slightly-wrong URL back to a client whose request carried malformed proxy headers is better than emitting nothing. Do not use this function to populate the PRM `resource` field — use CanonicalizeResourceURL there so the request fails loudly on malformed input.

func CanonicalizeResourceURL added in v1.1.0

func CanonicalizeResourceURL(r *http.Request, trustProxy bool) (string, error)

CanonicalizeResourceURL derives the RFC 8707 canonical resource URI from an incoming request.

When trustProxy is true, the function honors X-Forwarded-Proto and X-Forwarded-Host (and RFC 7239 Forwarded) so the value reflects the client-visible URL behind a trusted reverse proxy; a forwarded header present but malformed is rejected with ErrInvalidForwardedHeaders.

When trustProxy is false (the default for a generated server without TrustProxyHeaders() in the DSL), forwarded headers are ignored entirely and the origin is derived from r.Host + r.TLS only. This is the safe default for any server reachable directly by clients: without it, an attacker with direct access controls the PRM `resource` field advertised to clients.

Returns ErrEmptyResourceURL when no scheme+host can be derived. Callers should surface errors as 400 Bad Request rather than emitting a PRM document or challenge URL built from attacker-influenced or unusable inputs.

Operators who cannot vouch for forwarded headers should either leave trustProxy at its default or declare the resource identifier explicitly in the MCP DSL; generated code uses the declared value and does not call this function in the pinned case.

func CoerceQuery

func CoerceQuery(m map[string][]string) map[string]any

CoerceQuery converts a URL query map into a JSON-friendly object: - Repeated parameters become arrays preserving input order - "true"/"false" (case-insensitive) become booleans - RFC3339/RFC3339Nano values become time.Time - Numeric strings become int64 or float64 when obvious It does not coerce "0"/"1" to booleans.

func EncodeJSONToString

func EncodeJSONToString(
	ctx context.Context,
	newEncoder func(context.Context, http.ResponseWriter) goahttp.Encoder,
	v any,
) (string, error)

EncodeJSONToString encodes v into JSON using the provided encoder factory. The factory should produce an Encoder bound to the given ResponseWriter.

func EnsureSessionID

func EnsureSessionID(ctx context.Context) string

EnsureSessionID returns the existing session ID from ctx or creates a new one. When a response writer is present in ctx, the created session ID is emitted on the MCP session header.

func MarshalCanonicalJSON

func MarshalCanonicalJSON(v any) ([]byte, error)

MarshalCanonicalJSON encodes v into JSON using explicit JSON tags when present and otherwise falling back to snake_case field names for exported struct fields.

Contract: - Map keys must be strings (including named string aliases). - Unsupported map key kinds fail fast instead of being silently dropped.

func NewSessionID

func NewSessionID() string

NewSessionID generates a transport-agnostic MCP session identifier.

func ProtectedResourceMetadataPath added in v1.1.0

func ProtectedResourceMetadataPath(mountPath string) string

ProtectedResourceMetadataPath returns the RFC 9728 §3.1 path-suffixed metadata path for a server mounted at mountPath. The root alias "/.well-known/oauth-protected-resource" is produced when mountPath is empty or "/".

func RequestHeadersFromContext added in v1.0.7

func RequestHeadersFromContext(ctx context.Context) http.Header

RequestHeadersFromContext returns request-scoped MCP headers from ctx.

func ResponseWriterFromContext

func ResponseWriterFromContext(ctx context.Context) http.ResponseWriter

ResponseWriterFromContext returns the active HTTP response writer from ctx.

func SessionIDFromContext

func SessionIDFromContext(ctx context.Context) string

SessionIDFromContext returns the MCP session ID stored in ctx.

func ToolCallErrorFromResponse added in v1.1.0

func ToolCallErrorFromResponse(textParts []string, fallbackResult any) error

ToolCallErrorFromResponse converts an MCP isError content payload into a Go error while preserving the compact text message when present.

func UnmarshalCanonicalJSON

func UnmarshalCanonicalJSON(data []byte, dst any) error

UnmarshalCanonicalJSON decodes JSON into dst using explicit JSON tags when present and otherwise matching snake_case keys to exported struct fields.

func WithOAuthChallenge added in v1.1.0

func WithOAuthChallenge(handler http.Handler, mountPath string, challenge ChallengeBuilder) http.Handler

WithOAuthChallenge wraps a handler so that 401 responses missing a resource_metadata-carrying WWW-Authenticate header are augmented with the spec-compliant challenge built by challenge. Responses that already include resource_metadata are left untouched so consumer middleware can still override the default.

func WithRequestHeaders added in v1.0.7

func WithRequestHeaders(ctx context.Context, headers http.Header) context.Context

WithRequestHeaders stores request-scoped MCP headers in ctx.

func WithResponseWriter

func WithResponseWriter(ctx context.Context, w http.ResponseWriter) context.Context

WithResponseWriter stores the active HTTP response writer in ctx.

func WithSessionID

func WithSessionID(ctx context.Context, sessionID string) context.Context

WithSessionID stores the MCP session ID in ctx.

func WriteInvalidToken added in v1.1.0

func WriteInvalidToken(w http.ResponseWriter, resourceMetadataURL, errorDescription string)

WriteInvalidToken writes a 401 response with the invalid_token Bearer challenge. Consumers call this from a verifier wrapper when a decoded token carries the wrong audience, is expired, or is revoked.

func WriteUnauthorized added in v1.1.0

func WriteUnauthorized(w http.ResponseWriter, resourceMetadataURL, scope string)

WriteUnauthorized writes a spec-compliant 401 response with a Bearer challenge. The resource_metadata parameter points the client at the Protected Resource Metadata document; scope is the space-delimited list of scopes the server advertises. When scope is empty the scope parameter is omitted entirely (RFC 6750 does not require it).

Types

type Broadcaster

type Broadcaster interface {
	// Subscribe registers a new subscriber and returns a Subscription. The caller
	// must Close the subscription when done.
	Subscribe(ctx context.Context) (Subscription, error)
	// Publish delivers an event to all current subscribers.
	Publish(ev any)
	// Close closes the broadcaster and all current subscriptions.
	Close() error
}

Broadcaster provides a minimal, concurrency-safe publish/subscribe abstraction used by generated MCP adapters to stream server-initiated events (for example status notifications and resource updates). Implementations must allow Subscribe, Publish and Close to be called from multiple goroutines.

The event payload type is intentionally untyped (any) so generated packages can publish values of their locally-generated result types without creating cyclic dependencies across packages.

Close terminates the broadcaster and all active subscriptions. After Close, future Subscribe calls succeed with a closed Subscription and Publish becomes a no-op.

func NewChannelBroadcaster

func NewChannelBroadcaster(buf int, drop bool) Broadcaster

NewChannelBroadcaster constructs an in-memory Broadcaster backed by buffered channels. The returned implementation is safe for concurrent use.

Buffering and back-pressure:

  • buf controls the size of each subscriber channel buffer.
  • When drop is true, Publish does not block; if a subscriber channel is full the event is dropped for that subscriber.
  • When drop is false, Publish blocks until each subscriber has available buffer space, applying back-pressure to publishers.

type CallRequest

type CallRequest struct {
	// Suite identifies the MCP toolset (server name) associated with the tool.
	Suite string
	// Tool is the MCP-local tool identifier (without the suite prefix).
	Tool string
	// Payload is the JSON-encoded tool arguments produced by the runtime.
	Payload json.RawMessage
}

CallRequest describes the toolset/tool invocation issued by the runtime.

type CallResponse

type CallResponse struct {
	// Result is the JSON payload returned by the MCP server.
	Result json.RawMessage
	// Structured carries the full structured MCP content payload, including text
	// items that may also contribute to Result.
	Structured json.RawMessage
}

CallResponse captures the MCP tool result returned by the caller.

func NormalizeToolCallResponse

func NormalizeToolCallResponse(textParts []string, structured any, fallbackResult any) (CallResponse, error)

NormalizeToolCallResponse converts raw text parts and structured content into the canonical CallResponse representation used by MCP callers.

Text parts are concatenated in order. If the combined text is valid JSON, it becomes Result directly; otherwise it is marshaled as a JSON string. If no text is present, fallbackResult is marshaled into Result. Structured is marshaled into Structured when non-nil.

type Caller

type Caller interface {
	CallTool(ctx context.Context, req CallRequest) (CallResponse, error)
}

Caller invokes MCP tools on behalf of the runtime-generated adapters. It is implemented by transport-specific clients (stdio, HTTP streaming, etc.).

type CallerFunc

type CallerFunc func(ctx context.Context, req CallRequest) (CallResponse, error)

CallerFunc adapts a function to implement Caller.

func (CallerFunc) CallTool

func (f CallerFunc) CallTool(ctx context.Context, req CallRequest) (CallResponse, error)

CallTool implements Caller.

type ChallengeBuilder added in v1.1.0

type ChallengeBuilder func(r *http.Request, mountPath string) string

ChallengeBuilder formats a WWW-Authenticate header value for a request against the server mounted at mountPath. Generated packages export OAuthChallengeHeader matching this signature.

type HTTPOptions

type HTTPOptions struct {
	Endpoint      string
	Client        *http.Client
	ClientName    string
	ClientVersion string
	InitTimeout   time.Duration
}

HTTPOptions configures the HTTP Caller.

type Notification

type Notification struct {
	Type    string  `json:"type"`
	Message *string `json:"message,omitempty"`
	Data    any     `json:"data,omitempty"`
}

Notification describes a server-initiated status update that can be broadcast to connected MCP clients via the Events stream. It carries a machine-usable type, an optional human-readable message, and optional structured data.

type SessionCaller

type SessionCaller struct {
	// contains filtered or unexported fields
}

SessionCaller implements Caller by wrapping an MCP SDK ClientSession. It is used by transport-specific callers (stdio, HTTP, SSE) to unify tool invocation.

func NewHTTPCaller

func NewHTTPCaller(ctx context.Context, opts HTTPOptions) (*SessionCaller, error)

NewHTTPCaller creates an HTTP-based Caller and performs MCP initialize handshake.

func NewSSECaller

func NewSSECaller(ctx context.Context, opts HTTPOptions) (*SessionCaller, error)

NewSSECaller creates an SSE-based Caller and performs the MCP initialize handshake.

func NewSessionCaller

func NewSessionCaller(session *mcp.ClientSession, cancel context.CancelFunc) *SessionCaller

NewSessionCaller returns a new SessionCaller wrapping the provided SDK session.

func NewStdioCaller

func NewStdioCaller(ctx context.Context, opts StdioOptions) (*SessionCaller, error)

NewStdioCaller launches the target command using CommandTransport, performs the MCP initialize handshake via Client.Connect, and returns a Caller that wraps the resulting ClientSession.

func (*SessionCaller) CallTool

func (c *SessionCaller) CallTool(ctx context.Context, req CallRequest) (CallResponse, error)

CallTool invokes tools/call over the transport using the SDK session.

func (*SessionCaller) Close

func (c *SessionCaller) Close() error

Close terminates the session and releases resources.

type StdioOptions

type StdioOptions struct {
	Command       string
	Args          []string
	Env           []string
	Dir           string
	ClientName    string
	ClientVersion string
	InitTimeout   time.Duration
}

StdioOptions configures the stdio-based MCP caller.

type StreamableHTTPSessions

type StreamableHTTPSessions struct {
	// contains filtered or unexported fields
}

StreamableHTTPSessions tracks issued MCP session IDs and active long-lived listeners for generated streamable HTTP transports.

func NewStreamableHTTPSessions

func NewStreamableHTTPSessions() *StreamableHTTPSessions

NewStreamableHTTPSessions creates a store for issued sessions and active stream listeners.

func (*StreamableHTTPSessions) HasIssued

func (s *StreamableHTTPSessions) HasIssued() bool

HasIssued reports whether the store has any active issued sessions.

func (*StreamableHTTPSessions) Issue

func (s *StreamableHTTPSessions) Issue(sessionID string)

Issue records a session ID as valid for future requests.

func (*StreamableHTTPSessions) RegisterListener

func (s *StreamableHTTPSessions) RegisterListener(sessionID string, cancel context.CancelFunc) (func(), error)

RegisterListener atomically validates a session and associates a cancelable stream with it.

func (*StreamableHTTPSessions) Terminate

func (s *StreamableHTTPSessions) Terminate(sessionID string) error

Terminate marks a session as terminated and cancels any active listeners.

func (*StreamableHTTPSessions) Validate

func (s *StreamableHTTPSessions) Validate(sessionID string) error

Validate reports whether a session is currently valid.

type Subscription

type Subscription interface {
	// C returns a receive-only channel for events.
	C() <-chan any
	// Close unregisters the subscription.
	Close() error
}

Subscription represents a live registration with a Broadcaster.

The channel returned by C delivers events in publish order. The channel is closed when either Close is called on the subscription or the owning Broadcaster is closed. Close is idempotent and safe to call multiple times.

type ToolCallError added in v1.1.0

type ToolCallError struct {
	Message string
}

ToolCallError reports a remote MCP tool failure that was returned as an isError tool result rather than a transport/protocol error.

func (*ToolCallError) Error added in v1.1.0

func (e *ToolCallError) Error() string

Directories

Path Synopsis
Package retry defines shared types and helpers for producing standardized retryable errors and compact repair prompts used across generated clients.
Package retry defines shared types and helpers for producing standardized retryable errors and compact repair prompts used across generated clients.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL