Documentation
¶
Overview ¶
Package proxy wires the ingress MCP server to one or more egress backends, merging tools/list across them and routing tools/call by namespace prefix, per spec.md §5.1, and dispatching each tools/call through resile's resilience primitives per the policy governing it (FEATURE-010 onward). Schema firewall and output guardrail logic lives elsewhere, added by their own features.
Index ¶
- func MergeCapabilities(routes []Route) *mcp.ServerCapabilities
- func Middleware(routes []Route, router *NotificationRouter, policies *policy.Resolver, ...) mcp.Middleware
- type NotificationRouter
- func (r *NotificationRouter) Attach(server *mcp.Server)
- func (r *NotificationRouter) HandleLog(ctx context.Context, req *mcp.LoggingMessageRequest)
- func (r *NotificationRouter) HandleProgress(ctx context.Context, req *mcp.ProgressNotificationClientRequest)
- func (r *NotificationRouter) TrackProgress(session *mcp.ServerSession, originalToken any) (token string, done func())
- type Route
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func MergeCapabilities ¶
func MergeCapabilities(routes []Route) *mcp.ServerCapabilities
MergeCapabilities returns the union of every route's backend capabilities (tools, resources, prompts), per spec.md §5.1: a capability is present if any backend advertises it, and its ListChanged/Subscribe flags are true if any backend sets them. The gateway itself never registers tools, resources, or prompts on its own SDK server (routing is done via middleware instead), so without this its initialize response would advertise none of them regardless of what the backends actually offer.
func Middleware ¶
func Middleware(routes []Route, router *NotificationRouter, policies *policy.Resolver, maxResponseBytes int64, m *metrics.Metrics, logger *slog.Logger) mcp.Middleware
Middleware returns an mcp.Middleware that merges tools/list across all routes, renaming each tool with its backend's prefix, and routes tools/call to the backend whose prefix matches the requested tool name (longest prefix wins), stripping the prefix before forwarding. A tools/call for a name that matches no route falls through to next, which reports it as an unknown tool exactly as a single, un-proxied server would. router tracks progress tokens across the call so a later notifications/progress from the backend can be routed back to the right client (FEATURE-009); notifications/cancelled needs no handling here, since it propagates automatically through ctx (see NotificationRouter's doc comment). policies resolves the (unprefixed) tool name to the policy governing its dispatch, if any (FEATURE-010 onward). Every tools/call is also validated against its tool's own inputSchema before dispatch (FEATURE-017), via a schema cache private to this Middleware call, and a successful response's text content is clamped to maxResponseBytes, truncated with a notice appended if it doesn't fit (FEATURE-018).
Types ¶
type NotificationRouter ¶
type NotificationRouter struct {
// contains filtered or unexported fields
}
NotificationRouter forwards backend-initiated notifications (notifications/progress, notifications/message) to the gateway's clients, per spec.md §5.1 (FEATURE-009). notifications/cancelled (client→backend) needs no glue here: the SDK propagates context cancellation from an incoming request straight through to the outgoing backend call that shares its context, which is exactly what Middleware's callTool does.
A single backend connection is shared by every client session proxied through it (see Route), so progress notifications can't be routed by their wire token alone: two different clients may legally choose the same token independently. TrackProgress rewrites the token the gateway sends to the backend to one unique to this in-flight call, so the response can be routed back to the right client and restored to the client's original token.
func NewNotificationRouter ¶
func NewNotificationRouter() *NotificationRouter
NewNotificationRouter creates a router with no attached server; Attach must be called before any notifications/message can be broadcast (there is nowhere to broadcast progress notifications, that part works regardless, since each is routed to a specific session recorded by TrackProgress rather than to every connected session).
func (*NotificationRouter) Attach ¶
func (r *NotificationRouter) Attach(server *mcp.Server)
Attach records the gateway's own ingress server, whose currently connected sessions are the destination for backend log broadcasts. It must be called once, before the server starts accepting connections.
func (*NotificationRouter) HandleLog ¶
func (r *NotificationRouter) HandleLog(ctx context.Context, req *mcp.LoggingMessageRequest)
HandleLog is a mcp.ClientOptions.LoggingMessageHandler that broadcasts a backend's notifications/message to every client currently connected to the gateway, since log messages aren't tied to any one in-flight request.
func (*NotificationRouter) HandleProgress ¶
func (r *NotificationRouter) HandleProgress(ctx context.Context, req *mcp.ProgressNotificationClientRequest)
HandleProgress is a mcp.ClientOptions.ProgressNotificationHandler that resolves a backend's notifications/progress back to the client session that issued the originating call, restoring the client's own progress token before forwarding.
func (*NotificationRouter) TrackProgress ¶
func (r *NotificationRouter) TrackProgress(session *mcp.ServerSession, originalToken any) (token string, done func())
TrackProgress registers session as the destination for progress notifications carrying the returned synthetic token, and returns a cleanup func the caller must run once the backend call this token was issued for has completed. If originalToken is nil (the client requested no progress updates), it returns an empty token and a no-op cleanup.
type Route ¶
Route associates one egress backend with the namespace prefix used to disambiguate its tools from every other backend's. Prefix may be empty only when it is the sole route (config.Load enforces this). ID is the backend's config.Backend.ID, carried through purely for structured logging (FEATURE-021) — egress.Backend itself exposes no identifier of its own.