OptionalportOptionalpathHTTP path to mount the MCP endpoint on. Defaults to '/mcp'.
OptionalonCalled when the server starts listening.
OptionalreadinessAsync check called before the server begins accepting connections.
Throw to abort boot — serve() logs the error and calls process.exit(1),
so the server never enters listen mode and no traffic arrives during the
check. Use to validate external dependencies (database pools, credential
fetches) so a misconfigured deployment fails loudly at startup rather than
silently on the first live request.
OptionaltaskCustom task store. Defaults to a shared InMemoryTaskStore.
The default InMemoryTaskStore only evicts tasks that have a TTL set.
For long-running servers, always set ttl when creating tasks, or
provide a store with automatic eviction to prevent unbounded memory growth.
OptionalpublicCanonical public URL of this MCP endpoint (e.g. https://my-agent.example.com/mcp).
Required when protectedResource is configured — the RFC 9728 resource
field, the RFC 6750 resource_metadata URL on 401 challenges, and the
JWT audience your tokens must carry are all derived from it. Setting this
defends against attacker-controlled Host header phishing: without it,
the server would advertise whatever host a caller happened to send.
Must be an absolute https:// URL whose path matches the mount path.
Multi-host. Pass a function (host) => string when one process
fronts multiple hostnames (white-label publishers, multi-brand
adapters). The resolver runs per unique host — the returned URL is
cached and used as the RFC 9728 resource, the 401-challenge
resource_metadata, and the JWT audience for that host. Each
resolved URL's path must match the mount path, same as the static
form. Setting trustForwardedHost is recommended when
behind a proxy.
OptionalauthenticateAuthentication middleware applied to every request. When configured,
missing or invalid credentials produce a 401 with a compliant
WWW-Authenticate header — no request reaches the MCP transport
without passing. Use helpers from ./auth: verifyApiKey,
verifyBearer, or anyOf(verifyApiKey(...), verifyBearer(...)).
OptionalprotectedAdvertise OAuth 2.0 protected-resource metadata at
/.well-known/oauth-protected-resource<mountPath>. Requires publicUrl.
Pass a function (host) => ProtectedResourceMetadata for multi-host
deployments whose authorization servers or supported scopes vary per
hostname (e.g., each white-label seller has its own AS). The resolver
runs once per unique host and the result is cached. The static form
still works when every host uses the same PRM body.
OptionalreuseReuse the agent returned by the factory across requests instead of
closing it after each. Default false (one fresh server per
request — the pre-existing behavior).
When true, serve() DOES NOT call agentServer.close() between
requests. The factory is still called on every request; it's the
caller's responsibility to cache AdcpServer instances (typically
keyed on ctx.host) and return the cached instance when possible.
Concurrent requests to the same cached server are serialized per
instance — McpServer.connect() rejects when a transport is
already attached, so the framework wraps the
connect→handleRequest→release cycle in a per-instance async
mutex. Requests for DIFFERENT cached servers still run in parallel.
Use when createAdcpServer(...) setup cost (tool registration,
handler wiring) is a measurable portion of request latency — common
in multi-host deployments with many tools per host. Trade-off:
throughput per unique host drops to 1 in flight at a time. For
higher concurrency per host, cache a small pool of servers in the
factory and round-robin.
Cleanup is the caller's responsibility: listen for the process
shutdown signal and call close() on every cached server to
release MCP Tasks timers, HTTP keepalives, etc.
Incompatible with long-lived server→client streams. MCP's
Protocol._onclose aborts in-flight handlers and clears progress
tokens when the transport closes. In stateless HTTP mode (the
default this helper uses) each request is already single-response,
so this is a no-op concern. Don't enable reuseAgent if you
eventually wire a transport that keeps an SSE channel open across
multiple logical requests.
OptionaltrustTrust X-Forwarded-Host and RFC 7239 Forwarded: host=... for
host resolution and for reconstructing the public URL on 401
challenges. Default false — an attacker-controlled header can't
flip the advertised OAuth resource URL unless you opt in.
Your proxy must OVERWRITE the forwarded headers on ingress, not append. The framework trusts the first (left-most) entry in a chain. With an overwriting proxy that's the sanitized value; with an appending proxy it's whatever the client sent — an attacker picks it. Common behavior:
proxy_set_header X-Forwarded-Host $host;
or equivalent before enabling this flag.Verify your proxy's behavior against a request that already has
X-Forwarded-Host: attacker.example in it before turning this on.
OptionalprePre-MCP middleware — runs after authentication but before MCP transport
is connected. Intended for transport-layer concerns like RFC 9421
request-signature verification: the agent's body is already buffered
into (req as any).rawBody before the middleware fires so signature
verifiers can hash it without racing the transport's own body read.
Return true to signal the middleware handled the response (e.g. a
401 with WWW-Authenticate); the transport is skipped. Return false
to continue into MCP dispatch.
Throwing from the middleware produces a 500 with a generic body.
Port to listen on. Defaults to PORT env var or 3001.