@adcp/sdk API Reference - v7.9.0
    Preparing search index...

    Interface ServeOptions

    interface ServeOptions {
        port?: number;
        path?: string;
        onListening?: (url: string) => void;
        readinessCheck?: () => Promise<void>;
        taskStore?: TaskStore;
        publicUrl?: string | ((host: string) => string);
        authenticate?: Authenticator;
        protectedResource?:
            | ProtectedResourceMetadata
            | ((host: string) => ProtectedResourceMetadata);
        reuseAgent?: boolean;
        trustForwardedHost?: boolean;
        preTransport?: (
            req: IncomingMessage & { rawBody?: string },
            res: ServerResponse<IncomingMessage>,
        ) => Promise<boolean>;
    }
    Index

    Properties

    port?: number

    Port to listen on. Defaults to PORT env var or 3001.

    path?: string

    HTTP path to mount the MCP endpoint on. Defaults to '/mcp'.

    onListening?: (url: string) => void

    Called when the server starts listening.

    readinessCheck?: () => Promise<void>

    Async 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.

    serve(createAgent, {
    readinessCheck: () => store.probe(),
    });
    taskStore?: TaskStore

    Custom 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.

    publicUrl?: string | ((host: string) => string)

    Canonical 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.

    authenticate?: Authenticator

    Authentication 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(...)).

    protectedResource?:
        | ProtectedResourceMetadata
        | ((host: string) => ProtectedResourceMetadata)

    Advertise 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.

    reuseAgent?: boolean

    Reuse 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.

    trustForwardedHost?: boolean

    Trust 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:

    • Overwrite (safe to trust): Fly.io, Cloud Run, GCP HTTPS LB.
    • Append (NOT safe without extra config): AWS ALB default, nginx default. These need 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.

    preTransport?: (
        req: IncomingMessage & { rawBody?: string },
        res: ServerResponse<IncomingMessage>,
    ) => Promise<boolean>

    Pre-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.