Authorization header, but this is configurable via Bearer Auth Header Name if upstream passthrough headers would otherwise conflict.
Unified Endpoint
This endpoint provides access to all enabled MCP servers.- Endpoint:
http://localhost:3000/mcp - Method:
POST
Group-Specific Endpoint
For targeted access to specific server groups, use the group-based HTTP endpoint.- Endpoint:
http://localhost:3000/mcp/{group} - Method:
POST - Parameters:
{group}: The ID or name of the group.
Server-Specific Endpoint
For direct access to individual servers, use the server-specific HTTP endpoint.- Endpoint:
http://localhost:3000/mcp/{server} - Method:
POST - Parameters:
{server}: The name of the server.
Note: If a server name and group name are the same, the group will take precedence.
Protocol compatibility
The same/mcp URL serves legacy 2025-11-25 and modern 2026-07-28
clients. Group and server URLs use the same protocol handling. Changing the URL
from /sse to /mcp does not by itself upgrade the client’s protocol revision.
MCP Apps passthrough also depends on the route and visible upstream servers;
$smart routes remain ordinary MCP routes. See MCP Apps.
Upstream Streamable HTTP connections default to automatic protocol negotiation:
MCPHub probes server/discover, uses 2026-07-28 stateless HTTP when supported,
and falls back to the legacy handshake according to the SDK’s negotiation rules.
The downstream protocol is independent of the upstream protocol. Set the
per-server protocolNegotiation: "legacy" option to skip probing for a legacy
server. Stdio and legacy SSE upstreams continue using legacy negotiation.
List revalidation also runs before tool calls, prompt retrieval and resource
reads so newly added or removed entries affect routing. Resource templates are
fetched on demand. A failed refresh excludes only that upstream from the current
request; healthy upstreams and built-in content remain available. Partial lists
advertise zero TTL, and later requests retry the failed upstream. No background list polling is
introduced, and a list does not update until a relevant request occurs. Changed
shared lists notify connected legacy downstream clients after refresh; personal
lists remain scoped to their own runtime. Upstream list discovery remains
single-page; a response with
nextCursor receives no positive freshness hint.
When passthroughHeaders are configured, modern discovery uses a disposable,
request-local client and snapshots. Lists and pending refreshes are never shared
between forwarded identities, even with positive TTLs or absent headers. This
adds a discovery round trip per relevant request; those snapshots do not update
global descriptors, embeddings or list-change broadcasts.
Automatic negotiation does not treat every connection failure as evidence of a
legacy server. HTTP probe timeouts, network outages and authentication errors
can fail the connection rather than trigger fallback. See the
upstream configuration guide.
Elicitation forwarding
For legacy clients, MCPHub forwards form and URLelicitation/create requests
during tool calls when the upstream server has perSessionClient: true.
The isolated connection advertises only the modes declared by its downstream
client; the legacy empty elicitation: {} capability means form support only.
Accept, decline and cancel responses are returned to the upstream unchanged.
URL completion notifications are sent only to the owning downstream session.
This works for direct calls and Smart Routing’s call_tool.
Calls on an elicitation-enabled upstream connection run sequentially within
each downstream session because legacy requests do not provide a portable
parent-call identifier. Other sessions use separate connections. Cancelling a
call, ending the session, or a timeout aborts the pending interaction. A thrown
request error discards the upstream connection without automatically replaying
the call, so the next call creates a fresh connection and any upstream session
state must be rebuilt. A completed tool response with isError is still returned
normally.
Shared upstream connections, personal-credential/OAuth runtimes, prompt/resource
operations, and modern 2026-07-28 multi-round-trip elicitation are not supported
in this initial implementation. These paths do not advertise elicitation.
Enabling X-MCPHub-State-Id does not enable elicitation for modern clients.
Form elicitation is for ordinary structured input. Passwords, API keys and
access tokens must use URL mode, with sensitive input collected on the upstream
service’s page rather than in an MCP form. MCPHub does not add a dashboard input
dialog; the connected MCP client provides the interaction UI.
Migrating a client
- Replace the legacy SSE client transport with an HTTP transport, and change
/sseto/mcp, preserving any group, server or user route scope. Keep the same bearer credentials. A legacy HTTP client can make this transport change while retaining its initialization and session lifecycle. - To adopt
2026-07-28, use a client that supports modern HTTP discovery and the per-request metadata envelope. The SDK v2 HTTP client supplies the protocol and routing headers. A custom client must send the normal JSON-RPC body withparams._meta["io.modelcontextprotocol/protocolVersion"]set to2026-07-28and declare client capabilities underparams._meta["io.modelcontextprotocol/clientCapabilities"]. Send matchingMCP-Protocol-VersionandMcp-Methodheaders;tools/callandprompts/getrequireMcp-Namematchingparams.name, andresources/readrequires it matchingparams.uri. Use names returned by that route’s list response. - For
perSessionClientor OpenAPIcookieSessiontools, create a UUID v4 for each independent workflow and send it asX-MCPHub-State-Idon tool calls. Authenticated OpenAI/ChatGPT tool calls that cannot set this extension may instead reuse their client-declared_meta["openai/session"]conversation hint when the explicit header is absent. MCPHub hashes that hint before use; it is correlation metadata, not authentication. An invalid explicit state header fails closed and never falls back. Stateless tools need neither value. Reuse is limited to the same bearer credential, user and route. State is process-local, expires after 30 idle minutes, and requires sticky routing across replicas. Restart, expiry or credential/route changes require rebuilding application state. See explicit-state configuration. - Verify listing and calling tools with your actual permissions, then verify
resource reads and MCP Apps if used. Modern Apps hosts must declare their UI
capability on each request. Modern calls do not depend on
Mcp-Session-IdorenableSessionRebuild; those remain legacy mechanisms.
Cache behavior
Modern list results combine upstream discovery snapshots with live configuration, permissions and per-request capabilities. Positive hints are private and bounded by the shortest remaining upstream freshness and a maximum five-second projection window. Discovery age is retained; repeated gateway lists do not renew it. Empty upstream lists also participate in the bound. Unknown/invalid TTLs, partial pages, failed or incomplete discovery, gateway-generated Smart Routing entries, built-in prompts/resources and unknown hosted permission freshness use zero. Every request rebuilds its projection. Configuration, membership, permission or runtime changes detected during projection force zero. List refreshes revoke the previous snapshot’s freshness before awaiting a new envelope; failure, disconnect or replacement cannot establish new freshness. Template lists use the actual responses fetched for that request. No gateway response cache is added. Clients may retain an already-issued list for at most five seconds after a local configuration/permission change; server invalidation cannot retract client caches. Keep caches separate by endpoint, authenticated identity and per-request capabilities. A cached list never authorizes a call: MCPHub rechecks access on each request. Use zero when an upstream supplies no trustworthy freshness. Modern resource reads continue to bypass the SDK response cache, subtract handling time from a valid upstream TTL and restrict sharing to the requesting authorization context. Missing or invalid TTLs, built-in resources and gateway-generated read errors use zero. Legacy response shapes and session behavior are unchanged.Legacy SSE deprecation plan
The public notice will accompany the next release that includes this plan. The release notes must record the actual notice date N and the earliest eligible removal date N + 12 calendar months. This page and the ADR do not establish an earlier notice date. Until those release notes are published, the notice date and removal date remain unannounced. The intended removal release is the first major release on or after that date that meets the readiness criteria below. If readiness is incomplete, postpone removal and publish the revised target; do not shorten the notice window. Publish the exact target release and date at least 90 days before removal, remind users 30 days before removal, and include the migration instructions in each announcement. Actual removal requires a separate reviewed change.Preserve route scope and authentication
Prefix every URL below with the deployment’s configured base path, if any. Group IDs remain preferable to names; retain the same route identifier and bearer header name/value. Group names take precedence over server names.
Smart Routing suffixes such as
$smart/{group} are preserved as well. The SSE
client follows its advertised message URL and session query; do not translate
that message URL into a new HTTP endpoint. Replace the connection URL and let
the HTTP transport manage its requests.
First migrate the transport independently of the protocol revision. For example,
with the legacy TypeScript SDK (@modelcontextprotocol/sdk 1.29.0, the version
used by MCPHub’s independent compatibility tests):
Mcp-Session-Id lifecycle. Existing
stateful tools keep session isolation; no X-MCPHub-State-Id is required for
this transport-only migration. Modern discovery and explicit state are a
separate upgrade described above. Existing SSE session IDs cannot be carried
across transports: start a new HTTP workflow and repeat any login/tool setup.
Readiness and removal boundaries
Before announcing the exact removal target, maintainers must record:- The release containing the public notice and its actual date, plus proof that at least 12 calendar months will have elapsed at removal.
- Passing legacy SSE and Streamable HTTP compatibility suites throughout the window, including authenticated global, group, server and user routes, session continuation/rebuild and stateful-tool isolation.
- A migration checklist for each documented client integration: HTTP transport, preserved route/authentication, successful listing/calling, and resources, Apps or stateful workflows where used. Record tested client versions and unresolved blockers; there must be zero unresolved release-blocking migration defects. Operator confirmations may supplement this checklist; absence of telemetry is not evidence that nobody uses SSE.
- Published release notes and documentation in both languages, the 90-day and 30-day reminders, and a support path for deployments that still use SSE.
handleSseConnection, handleSseMessage, SSE transport construction,
and SSE-only tests/imports. Audit every caller before removing shared helpers.
Retain /mcp POST/GET/DELETE routes, legacy HTTP initialization, session maps
and cleanup, session creation/rebuild locks, bearer/group authorization,
perSessionClient isolation, OpenAPI cookie jars and modern explicit state.
The server-legacy package also supplies shared legacy HTTP/OAuth functionality;
SSE retirement alone is not grounds to remove the dependency.
The plan is tracked in #1272.
The notice release must open a separate removal task carrying the actual dates,
client checklist and inventory. Legacy 2025-11-25 Streamable HTTP remains
supported independently.