@hile/mcp
Expose tools, resources, and prompts from independently deployed Hile microservices through one discovered, multi-instance MCP gateway.Start with the distributed MCP gateway guide for an end-to-end multi-service composition.
Use When
- Different microservices own different MCP tools, resources, and prompts.
- One MCP endpoint must expose a unified, dynamically discovered catalog.
- Providers need independent deployments, replicas, scopes, and failure isolation.
- Existing exact-peer
Application.streamPeer()semantics such as cancellation and backpressure must carry MCP execution frames. - Remote clients use Streamable HTTP or a local MCP host launches a stdio process.
Do Not Use When
- One process owns a small static MCP server and does not need Hile discovery or distributed routing.
- Work must survive process crashes or be retried later; use a durable queue rather than an MCP invocation stream.
- The Micro network is untrusted and no gateway-to-provider credential verifier is configured.
- A resource should be renamed by the gateway. Resource URIs are provider-owned identities and are never rewritten.
Install
20.12.0 or newer is required. @hile/mcp uses the official @modelcontextprotocol/server and @modelcontextprotocol/node v2 packages internally; applications do not need to register protocol handlers by hand.
Imports
Copy-Paste Example
Create one default-exported capability permcps/**/*.mcp.ts file:
Application is listening:
*.mcp.ts, *.mcp.js, *.mcp.tsx, *.mcp.jsx, and *.mcp.mjs. Every matched file must default-export one value created by defineMcpTool, defineMcpResource, or defineMcpPrompt. A load or registration failure rolls back the whole provider batch.
For a programmatic provider, compose already-defined capabilities with defineMcpProvider() and pass the immutable result directly to attachMcpProvider():
name. A tool, resource, or prompt cannot be placed in the wrong record.
More Examples
Public entry points:
Static resource:
completions is supported only for declared prompt arguments and RFC 6570 template variables. The gateway routes completion requests to the selected provider instance; static resources do not have completion arguments.
After mutable resource data changes, publish the official resource-updated notification through the attachment. Template variables are expanded with the SDK’s RFC 6570 implementation:
Application. The source validates provider ID, instance ID, fingerprint, and URI before the gateway notifies subscribed MCP clients. Sessionful stdio connections keep a per-connection resource subscription set; the Streamable HTTP adapter publishes through the official endpoint notifier and subscription event bus. Stateless HTTP requests do not retain subscriptions across requests.
Unified gateway and Streamable HTTP adapter:
@hile/mcp acts only as the OAuth Resource Server. Token issuance, client registration, consent, and authorization-server persistence remain owned by your identity platform.
Process-local stdio adapter:
gateway.inspect() for health endpoints and diagnostics. It returns each provider’s ready or conflict state, instance count, fingerprints, conflict reasons, and the currently exposed public names.
Public API Reference
Definitions and handler context
Names must match
[A-Za-z0-9._-]{1,128}. Metadata is cloned and frozen when the definition is created. Invalid schemas, annotations, cache hints, completion keys, access policies, timer values, URIs, or retry combinations fail before publication.
Every handler receives McpInvocationContext: the ingress-owned Hile executionContext, an abort signal, a verified or explicitly trusted principal, optional inputResponses, requestState, and awaited emit.progress() / emit.log() methods. Completion handlers receive the same executionContext, and capability access.authorize(principal, input, executionContext) receives it as its third argument. Credential mode binds the serialized execution context into the signed invocation descriptor, so a carrier cannot be substituted after signing. Treat requestState as untrusted unless the gateway configures the official SDK verifier. Template resource variables use the SDK Variables shape, so a variable may be a string or string array.
Provider and discovery APIs
One Application may attach multiple providers. The provider host shares one dispatcher and resource-update publication per Application, while manifests and attachment cleanup remain instance-scoped.
Gateway API
createMcpGateway(options) starts the source, builds the initial catalog, and returns McpGateway with inspect() and close().
inspect() is read-only operational state. It reports provider readiness, instance counts, fingerprints, conflicts, and currently exposed names; it does not invoke discovery or providers.
HTTP and stdio adapters
Close the HTTP endpoint or stdio handle before closing the shared gateway.
authInfo is process-level stdio identity; without it, scoped capabilities remain hidden.
Invocation credentials, testing, and errors
HileMcpErrorCode values are INVALID_DEFINITION, DUPLICATE_CAPABILITY, PROVIDER_ATTACH_FAILED, PROVIDER_UNAVAILABLE, CATALOG_CONFLICT, and GATEWAY_CLOSED. Transport-level MCP failures are still returned according to the official SDK behavior; callers should not assume every tool error rejects the client promise.
Compose With
@hile/microsupplies Registry snapshots, retained provider manifests, exact-peer routing, cancellation, and streaming.@hile/httpmounts the Streamable HTTP middleware without creating a second public listener.@hile/coreowns startup and reverse-order cleanup.- Standard Schema implementations such as Zod provide tool and prompt schemas.
@hile/redis-idempotencycan protect side effects inside tools; MCP retry annotations are not a business idempotency guarantee.
Runtime And Lifecycle Notes
- Provider files are immutable definitions. Runtime instance IDs, fingerprints, Registry topics, and message routes are owned by the attachment.
- One
Applicationmay attach multiple providers. A shared application-scoped dispatcher routes by provider instance and remains registered until the last attachment closes. - Every provider instance publishes a retained manifest. Discovery retrieves all matching manifests and their Registry-observed publisher addresses in one batched snapshot.
- A provider is exposed only when every live instance for its provider ID has the same fingerprint. Mixed rolling deployments are reported as conflicts and fail closed.
- Public tool and prompt names default to
providerId.localName. Configurenaming.aliasesand the.,-, or_separator when required. Resource URIs remain unchanged. - Compatible instances are selected round-robin. Invocation is bound to the selected instance ID, fingerprint, and publisher address so a stale catalog cannot execute a replacement implementation.
idempotent-failoverperforms at most one alternate-instance retry and is legal only for tools annotated both read-only and idempotent. A completed result, client cancellation, or downstream notification failure is never retried.- Provider execution emits progress, log, and one terminal-result frame through a bounded internal channel. Producer emits await capacity, and request cancellation reaches validation, authorization, handlers, connection establishment, and streaming.
- Concurrent cold calls to one provider address share connection establishment. Registry discovery is polled into a cached catalog instead of queried per MCP request.
- Long-lived stdio and subscription-capable servers receive catalog diffs and
list_changednotifications. Stateless HTTP requests use the cached current projection. - Prompt and resource-template completion uses the official SDK completion API and executes on the owning provider. Resource updates use official subscription notifications, are delivered only to matching subscriptions, and are accepted from every currently discovered compatible instance fingerprint.
- Capability metadata supports official titles, descriptions, icons, annotations,
_meta, resource size, and cache hints. Gateway-level list/read cache hints are configured withcacheHints. - OAuth mode delegates Bearer verification, challenges, and metadata documents to official SDK helpers; the package does not implement an authorization server.
- HTTP authentication determines the external principal and scopes. Capability
access.scopes,access.authorize, and gatewayisCapabilityExposedare separate defense layers. - Credential mode signs a short-lived, replay-protected envelope bound to the provider, instance, fingerprint, capability, input, and principal. Prefer one Ed25519 Gateway authority: the Gateway keeps the private key, while every Provider can consume the same non-secret public key from deployment configuration. Providers cannot mint Gateway credentials. Verifiers bound accepted lifetime and allow a small explicit clock tolerance. During rotation, Providers may accept overlapping
publicKeysbefore the Gateway switches its private key. HMAC remains available for compatibility and requires a different key per provider or trust domain. - Capability-required scopes are part of the discovered Provider manifest.
MCP_SCOPE_ALL(*) is an explicit administrator grant covering every discovered capability scope; use it only when automatic access to future Providers is intended. trusted-internalis explicit, propagates an unsigned principal, and is appropriate only when every peer able to reach the Micro operation is trusted.- Client-echoed
requestStatestaysunknownunlessrequestState.verifyis configured on the gateway. - Close the external transport first, then the gateway, then provider attachments, and finally the underlying Micro applications. Close operations are serialized; provider teardown withdraws discovery before removing handlers.
Anti-Patterns
- Writing MCP capabilities in arbitrary boot files instead of default-exported
mcps/**/*.mcp.*files. - Omitting explicit HTTP authentication mode or gateway-to-provider invocation security.
- Reusing one HMAC secret across unrelated providers or security domains.
- Setting
idempotent-failoveron a write tool or treating a retry annotation as exactly-once execution. - Querying the Registry for every tool call instead of using
createHileMcpProviderSource(). - Routing by namespace after selecting an instance; use the built-in exact-peer source.
- Trusting
requestState, client claims, resource variables, or handler input before validation. - Creating a second HTTP listener only for MCP when an existing Hile HTTP server can mount the middleware.
Verification Checklist
- Every
*.mcp.*file has one default export created by the matchingdefineMcp*function. - Tool
inputSchema, optionaloutputSchema, promptargsSchema, and resource URIs/templates compile successfully. - The provider application is listening before
attachMcpProvider()runs. - Provider and gateway use matching credential configuration, with distinct secrets per provider.
- Host, Origin, and authentication policies are explicit on the HTTP adapter.
- OAuth deployments return protected-resource metadata and a Bearer challenge that points to it.
gateway.inspect()reportsreadyand the expected tool, resource, and prompt names.- Two compatible replicas appear under one provider ID and receive calls; a mixed-fingerprint rollout produces a conflict instead of routing.
- Progress/log delivery, client cancellation, timeout, and bounded backpressure are exercised.
- Scoped capabilities are hidden without the scope and callable with it over both HTTP and configured stdio.
- Adding or removing a provider updates a long-lived client’s catalog notification.
- Prompt and resource-template completion returns provider-owned suggestions, and a subscribed resource receives
notifications/resources/updatedafternotifyResourceUpdated(). - Transport, gateway, attachments, and applications close without retained manifests, live handlers, sockets, or timers.
Complete Tutorial
Continue with the distributed MCP gateway guide for provider layout, gateway boot, HTTP and stdio transports, multi-instance routing, security boundaries, performance, failure semantics, and verification.Package-Local References
The npm package shipsREADME.md, AI.md, and ARCHITECTURE.md alongside its runtime entry points.