Distributed MCP Gateway
Use this guide to build independent MCP providers, automatic Registry discovery, compatible replicas, and one secured Streamable HTTP or stdio gateway.
API details: @hile/mcp. Protocol baseline: MCP 2026-07-28.
Complete Example
This recipe composes independent Hile microservices into one MCP server. Each provider owns its capability files and deployment lifecycle. The gateway discovers compatible replicas, applies public naming and authorization, and exposes the cached catalog over the official Streamable HTTP or stdio adapter.1. Define capabilities under mcps
defineMcpResource() for static or RFC 6570 template resources and defineMcpPrompt() for prompts. The package API page contains complete examples for all three capability kinds.
Prompt arguments and RFC 6570 template variables may declare completions. Tools and static resources cannot. Official metadata fields such as title, description, icons, _meta, tool/resource annotations, resource size, and cacheHint are preserved across discovery.
2. Attach each provider after Micro listen
3. Create the unified gateway
startup: 'allow-empty' when the gateway may start before providers. Use require-provider when an empty initial catalog is a deployment error.
4. Mount Streamable HTTP on the existing server
authenticate supplies the external MCP identity and scopes. The invocation credential independently authenticates the gateway to each provider. responseMode passes through to the official SDK, keepAliveMs controls stream keepalives, and maxSubscriptions bounds HTTP subscription listeners.
For a public endpoint, choose { authentication: { mode: 'public' } } explicitly and expose only capabilities designed for anonymous use. An omitted authentication policy is rejected.
For OAuth, select mode: 'oauth' with an official OAuthTokenVerifier, required scopes, and metadata containing the public MCP resource URL plus Authorization Server metadata. The middleware serves /.well-known/oauth-protected-resource/mcp and /.well-known/oauth-authorization-server, and emits an RFC-compliant Bearer challenge. Authorization Server behavior is intentionally outside this package.
5. Inspect discovery and multi-instance state
conflict is omitted from the public catalog until all live replicas converge on one fingerprint.
6. Use stdio for a process-local MCP host
authInfo is process-level policy. Without it, scoped capabilities are hidden. Do not write application logs to stdout in a stdio server because stdout belongs to the MCP transport.
Capability Behavior
All capability handlers receive
signal, verified principal, optional inputResponses, untrusted-or-verified requestState, and awaited emit.progress() / emit.log() methods.
This gateway projects server-owned tools, resources, and prompts. Roots and sampling belong to the connected client/server session rather than a discovered provider definition, and durable Tasks belong in an application job system. No MCP-over-WebSocket or custom pagination protocol is added.
Completion callbacks receive the partial value, cancellation signal, verified principal, and already supplied string arguments. When mutable resource data changes, call attachment.notifyResourceUpdated(resourceName, variables?); connected clients subscribed to the expanded URI receive notifications/resources/updated. Sessionful stdio connections track subscriptions per connection. Streamable HTTP uses the official endpoint notifier and subscription event bus; stateless HTTP requests cannot retain a subscription across requests.
Discovery And Routing
- Each attachment publishes one instance-scoped retained manifest.
- The source retrieves matching manifests and Registry-observed publisher addresses in one snapshot call.
- The gateway validates schemas and metadata, groups instances by provider ID, and rejects fingerprint or public-name conflicts.
- Compatible instances are selected round-robin from the cached catalog.
- Invocation carries the expected provider ID, instance ID, fingerprint, capability, input, and credential.
- The source streams to the selected publisher address. The provider verifies identity and credential before validation, authorization, and handler execution.
- Progress and log frames are forwarded; one terminal result completes the MCP call.
Security Boundaries
- HTTP authentication and Host/Origin allowlists protect the public transport.
- Gateway catalog visibility checks required scopes before registration and can apply
isCapabilityExposedpolicy. - Provider authorization repeats scope checks and optional capability-local
authorize()after schema validation. - Credential mode proves that the invocation was minted for the exact provider instance, fingerprint, capability, and input. Nonces are replay-protected and expire.
- Prefer one Ed25519 Gateway authority for all discovered Providers. Keep its private key only in the Gateway and distribute the matching public key to Providers; the public key is not a credential and Providers cannot mint invocations. Rotate without downtime by publishing overlapping Provider
publicKeys, switching the Gateway private key, then removing the retired public key. - HMAC remains available for compatibility. Configure a separate HMAC secret for each provider or trust domain because a symmetric-key holder can mint credentials within that domain.
- Required scopes travel in each discovered capability manifest. Grant
MCP_SCOPE_ALL(*) to an external principal only as an explicit administrator policy that intentionally includes capabilities registered in the future. trusted-internalsends an unsigned principal. Use it only on a Micro mesh where every reachable peer is trusted to impersonate the gateway.requestStateis client-echoed state. Treat it asunknownunless the gateway is configured with the official SDK request-state verifier.
Performance And Failure Semantics
- Registry discovery is one batched snapshot per poll, not one query per provider or request.
- Catalog parsing and public projection happen only when the snapshot changes.
- One shared resource-update publication is reused per provider
Application; consumers do not create one subscription per provider instance. - Cold concurrent calls to one address share connection establishment; established Micro clients are reused.
- The execution channel is bounded.
emit.progress()andemit.log()await capacity so a slow MCP client cannot create an unbounded provider buffer. - Abort signals and the tool timeout cover connect, validation, authorization, handler execution, notification delivery, and stream consumption.
- Only read-only and idempotent tools may request one alternate-instance failover attempt. Mid-stream failure before a terminal result is eligible; cancellation and completed results are not.
- Closing the gateway rejects new work, aborts active streams, waits for them to settle, then closes discovery.
User Intent
Use this recipe when the user asks for MCP capability decoupling, unified MCP output, automatic discovery, multiple providers, or multiple replicas in a Hile microservice architecture.Packages To Use
@hile/mcp@hile/micro@hile/httpwhen exposing Streamable HTTP on a Hile server@hile/corefor lifecycle composition- A Standard Schema implementation such as Zod
@hile/redis-idempotencyinside side-effecting tools when retries must not duplicate business effects
Implementation Steps
- Define each capability as a default export under the owning service’s
mcpsdirectory. - Start the provider
Application, then attach the directory with explicit invocation security. - Give compatible replicas the same provider ID and immutable definitions.
- Start a gateway-side
Application, provider source, keyring, and gateway. - Mount Streamable HTTP on the existing public server or serve stdio for a local process integration.
- Configure external authentication, scopes, aliases, startup policy, diagnostics, and lifecycle cleanup.
- Verify discovery, capability behavior, replica routing, conflict handling, streaming, cancellation, security, and dynamic list changes.
Failure And Cleanup Behavior
- Invalid local definitions fail before publication; a partial loader registration is rolled back.
- Invalid or spoofed network manifests are omitted from discovery.
- Mixed fingerprints, duplicate public names, and duplicate resource identities fail closed at catalog construction.
- The selected provider verifies the expected instance and fingerprint, so a process replacement at the same address cannot execute against a stale catalog.
- Provider close withdraws the retained manifest before unregistering the dispatcher and unloading definitions. If withdrawal fails, the callable state remains available for a retry.
- Gateway close is an active invocation barrier. HTTP and stdio handles still have their own lifecycle and should be closed first.
Verification Checklist
- The official MCP client can list and invoke tools, read both resource forms, and get prompts.
- The official MCP client can complete prompt arguments and resource-template variables.
- A subscribed resource receives an updated notification after the provider calls
notifyResourceUpdated(). - OAuth mode serves discovery metadata and rejects missing, expired, or under-scoped Bearer tokens with official SDK responses.
- Structured tool output satisfies
outputSchema. - Progress, logging, and an
InputRequiredResultmulti-round-trip flow reach the client. - An unauthenticated or under-scoped client cannot discover or call protected capabilities.
- A direct Micro caller cannot forge a privileged principal in credential mode.
- Two matching replicas route successfully and appear as one public provider.
- A mismatched replica changes the provider to
conflictwithout affecting unrelated providers. - Adding, removing, or changing a provider updates a long-lived client’s catalog notification.
- Cancelling a call stops connection or handler work and does not trigger failover.
- Shutdown leaves no provider publication, operation handler, active stream, connection waiter, interval, or owned HTTP listener.