Construct a server from a fully-populated Implementation, letting the author advertise a display title (>= 2025-06-18) plus description, websiteUrl, and icons (>= 2025-11-25) in the initialize / server/discover serverInfo. Fields newer than the negotiated protocol version are stripped from the wire response (see Implementation.forVersion), so older peers see only what they understand. Mirrors the client's full Implementation clientInfo support.
The acknowledged subset for a single subscriptions/listen request's filter, built from exactly that one stream's opt-in (draft basic/utilities/subscriptions Acknowledgment). Each subscription is independent — identified by its own listen request id (§Multiple Concurrent Subscriptions) — so the ack a transport sends as the first event on a stream MUST reflect only THAT request's opt-in, never the server-wide accumulation across other (or already-closed) concurrent streams. The three list-changed types appear as booleans ({ "<type>": true }) and resourceSubscriptions as the agreed string[] of URIs; an empty object when the filter opted into nothing.
Advertise a draft protocol extension (e.g. "io.modelcontextprotocol/tasks") with an optional per-extension settings object. The identifier and its settings appear in the extensions field of the server capabilities sent during initialize / server/discover, per the draft Extension Negotiation rules. settings defaults to an empty object.
Let a transport point this server at the ConnectionState it owns for a mount/connection at wire-up (mountMcp / runStdio / serveStdio). Used by the notify/push path, which fires OUTSIDE any request and therefore cannot receive the state as a dispatch argument. This sets the fallback activeConnection; HTTP requests still resolve their own per-session state.
Capabilities this server advertises, derived from what is registered.
The capabilities advertised by the connected client (valid after initialize).
The extension identifiers and settings the connected client advertised (valid after initialize). Json.undefined if the client advertised none.
The tasks capability the connected client advertised (valid after initialize). Null if the client advertised none.
The session tokens (Mcp-Session-Ids) that currently have a connected GET SSE stream, so a caller can pingClient(token) each live session in turn rather than guessing. Empty when there is no push channel or no session-scoped GET stream is open.
The most recently set log level (default "info").
Opt out of the default input-schema validation, so tool arguments are passed to handlers unchecked. Discouraged — the spec says servers MUST validate tool inputs — but available for handlers that perform their own validation or intentionally accept arbitrary arguments.
Validate each tool call's arguments against the tool's registered inputSchema before the handler is invoked. Per the spec (server/tools § Security Considerations, all versions), "Servers MUST: Validate all tool inputs", so this is **on by default**. § Error Handling classifies an inputSchema violation (missing required property or wrong type) as an *input-validation* error, i.e. a Tool Execution Error: a tools/call whose arguments do not conform yields a CallToolResult with isError:true and a descriptive text content block (so the model can self-correct), NOT a JSON-RPC -32602 protocol error. Tools without an inputSchema are unaffected. This method is retained for explicitness and to re-enable validation after disableInputSchemaValidation.
Advertise the logging capability and accept logging/setLevel.
Opt in to enforcing each tool's declared outputSchema before the result is sent. Per the spec, "If an output schema is provided: Servers MUST provide structured results that conform to this schema." With validation enabled, both halves of that MUST are enforced: a successful result for a tool that declares an outputSchema must (a) carry structuredContent and (b) have it conform to the schema. A handler that omits structuredContent or emits non-conforming structuredContent surfaces a clear internal error (so the bug is caught at the server) rather than silently shipping bad output. Tools without an outputSchema are unaffected; tool-execution errors (isError:true) and MRTR InputRequiredResults are exempt (the MUST governs successful structured results). Off by default to preserve existing behaviour.
Advertise the prompts listChanged capability so capabilities() emits prompts: { listChanged: true }. Declare this (before initialize / server/discover) when the server may add or remove prompts at runtime and will emit notifications/prompts/list_changed via notifyPromptsListChanged.
Advertise the resources subscribe capability and accept resources/subscribe + resources/unsubscribe.
Advertise the resources listChanged capability so capabilities() emits resources: { listChanged: true }. Declare this (before initialize / server/discover) when the server may add or remove resources or resource templates at runtime and will emit notifications/resources/list_changed via notifyResourcesListChanged.
Advertise the 2025-11-25 tasks capability, i.e. support for task-augmented requests. list/cancel indicate support for tasks/list and tasks/cancel; requests is the nested-by-category object describing which requests may be task-augmented. Its spec shape is the nested form {"tools": {"call": {}}}, NOT a flat "tools/call" key. Build it with TaskRequests, for example enableTasks(true, true, TaskRequests().tool().toJson()). The capability appears in the tasks field of the server capabilities sent during initialize / server/discover.
Advertise the tools listChanged capability so capabilities() emits tools: { listChanged: true }. Declare this (before initialize / server/discover) when the server may add or remove tools at runtime and will emit notifications/tools/list_changed via notifyToolsListChanged.
Dispatch a single parsed message. Returns the JSON-RPC response for requests, or Nullable.init for notifications (which get no reply). ctx is the channel for any server->client traffic the handler emits; when omitted, a NullContext is used (no streaming).
Convenience overload using a NullContext (no server->client channel).
Process a raw wire payload (single message or batch) and return the raw response text, or empty string when there is nothing to send back (e.g. a notification, or an all-notification batch). Parse/envelope failures become JSON-RPC error responses with a null id.
As handleRaw, but dispatched against an explicit per-request ConnectionState (for the Streamable HTTP batch back-compat path, which resolves the request's session/per-request state in the transport). The batch version gate and per-message dispatch both consult conn instead of the single bound activeConnection, so a session that negotiated 2025-03-26 can actually reach the legacy batch path and a modern session is gated on its own negotiated version. null falls back to the no-arg behaviour.
As handleRaw, but with a server->client write sink for transports (such as stdio) that can deliver out-of-band frames on the same channel. Each message is dispatched with a StdioContext bound to sink, so a handler's ctx.log() / ctx.reportProgress() are serialised and pushed to sink as they happen — before the request's reply, which is still returned as the string result. When sink is null a NullContext is used (no streaming), preserving the in-process behaviour of the no-argument overload.
As handleRaw(text, sink), but with the stdio server->client request channel: when a handler calls ctx.sendRequest (e.g. via ctx.sample/ctx.elicit) serverRequest(method, params) is invoked to write the request and block the current task for the client's reply (the DuplexChannel correlates it on its read loop). null reproduces the no-channel behaviour (server->client requests throw).
Whether a client is currently subscribed to updates for uri.
The per-stream SubscriptionFilter parsed from the most recent subscriptions/listen request handled by this server. The transport reads it immediately after routing a listen request so it can attach the exact opt-in to that stream's push-channel listener (draft basic/utilities/subscriptions §Notification Filter), ensuring a notification is delivered only to a stream that explicitly requested its type.
The statefulness model this server was constructed with (default stateless). Transports derive session minting from this.
The protocol version negotiated with the client (valid after initialize).
Send an *unsolicited* JSON-RPC notification to every client currently listening on the standalone GET SSE stream. This is the public entry point for server-initiated traffic outside an in-flight request — e.g. a notifications/resources/updated for a subscribed resource, or a notifications/tools/list_changed. Returns the number of listeners the notification was delivered to; 0 when no GET stream is open (or the server is not on a Streamable HTTP transport). On a stdio server with an active draft subscriptions/listen it is additionally written to stdout (stamped with the listen subscriptionId), since that transport shares one channel for all server->client traffic.
Emit a notifications/elicitation/complete for a URL-mode elicitation, telling the client an out-of-band interaction it was asked to complete (via RequestContext.elicitUrl) has finished, so the client can stop waiting on it (basic/utilities/elicitation §"Completion Notifications for URL Mode Elicitation"). Per spec the notification MUST carry the elicitationId that correlates it with the original request. It is delivered on the standalone GET SSE stream (the unsolicited server->client channel); returns the number of listeners reached, or 0 when no GET stream is open (or the server is not on a Streamable HTTP transport). Throws invalidParams on an empty elicitationId.
Broadcast a notifications/prompts/list_changed to every client listening on the standalone GET SSE stream, informing them the set of available prompts changed (per the server/prompts List Changed Notification). Returns the number of listeners reached; 0 when no GET stream is open. Call after a runtime registerDynamicPrompt (or a removal). For the draft protocol, the notification is suppressed unless a client opted in via subscriptions/listen with promptsListChanged:true.
Notify subscribers that a watched resource changed by emitting a notifications/resources/updated on the standalone GET SSE stream (per server/resources Subscriptions: "Server delivers notifications/resources/updated ... whenever a watched resource changes"). Per ResourceUpdatedNotificationParams in every spec version (2024-11-05 .. 2025-11-25 .. draft) the params carry exactly { "uri": ... } (plus the inherited optional _meta); there is no title field on this notification (a resource's title lives on the Resource object in resources/list). It is delivered only when a client is currently subscribed to uri (via resources/subscribe); for an unsubscribed URI it is a no-op returning 0. For the draft protocol the notification is additionally suppressed unless a client opted in via subscriptions/listen with resourceSubscriptions:true. Returns the number of GET-stream listeners reached; 0 when no GET stream is open.
Broadcast a notifications/resources/list_changed to every client listening on the standalone GET SSE stream, informing them the set of available resources changed (per the server/resources List Changed Notification). Returns the number of listeners reached; 0 when no GET stream is open. Call after a runtime registerResource / registerResourceTemplate (or a removal). For the draft protocol, the notification is suppressed unless a client opted in via subscriptions/listen with resourcesListChanged:true.
Broadcast a notifications/tools/list_changed to every client listening on the standalone GET SSE stream, informing them the set of available tools changed (per the server/tools List Changed Notification). Returns the number of listeners reached; 0 when no GET stream is open. Call after a runtime registerDynamicTool / removeTool. For the draft protocol, the notification is suppressed unless a client opted in via subscriptions/listen with toolsListChanged:true.
Initiate a server->client ping on the standalone GET SSE push channel and block until a connected client acknowledges with the spec-mandated empty result (basic/utilities/ping: "Either the client or server can initiate a ping by sending a ping request"). This is the server-side counterpart to McpClient.ping(), exposing the SHOULD-periodic connection-health probe the spec describes for either party. The probe rides the same push channel notify uses, and the client's reply is correlated via the shared StreamCoordinator when it POSTs the response.
Initiate a server->client ping on the GET SSE stream owned by the session sessionId (its Mcp-Session-Id). On a stateful HTTP server every per-session GET stream is registered under its session id as the listener's owner token, so the probe must be scoped to that token to reach the right stream — an empty token (the no-arg pingClient) matches no session-scoped listener and would always fail. The waiter is likewise scoped to sessionId, so only a response POSTed under the same session resolves it. Throws as the no-arg form does, plus when no GET stream is connected for that session.
Register a *dynamic* prompt with the handler that produces its messages.
Register a *dynamic* prompt whose handler may, on a stateless (MRTR) draft request, ask the client for more input instead of returning a final result.
Register a *dynamic* tool with a context-aware handler (progress / logging / sampling / elicitation available via ctx).
Register a *dynamic* tool with a simple handler that ignores the request context. See registerDynamicTool(Tool, ToolHandler) for when to use the dynamic path versus the typed UDA layer.
Register a *dynamic* tool whose handler may ask the client for more input on a stateless (MRTR) request. The handler branches on ctx.isStateless: when stateless it reads ctx.inputResponses and returns either ToolResponse.complete or ToolResponse.inputRequired; otherwise it may call the blocking ctx.elicit/ctx.sample. A server that wants to serve both protocol eras handles both branches here.
Register a direct resource with a reader for its contents. An optional per-resource draft CacheableResult freshness hint is emitted on this resource's resources/read response (draft protocol only).
Register a resource template with a reader receiving the matched URI and captured {var} parameters. An optional per-template draft CacheableResult freshness hint is emitted on a matching resources/read (draft only).
Register a resource template whose reader also receives the per-request RequestContext, so a template handler can log, poll cancellation, or elicit through the real request channel. Otherwise identical to the context-less overload.
Unregister a previously registered direct resource by URI. Returns true if a resource was removed, false if no resource with that URI was registered. The mirror of registerResource; pair with notifyResourcesListChanged to inform connected clients that the available resource set changed.
Unregister a previously registered tool by name. Returns true if a tool was removed, false if no tool with that name was registered. Pair with notifyToolsListChanged to inform connected clients that the tool list changed.
Enforce the lifecycle rule that the server SHOULD NOT respond to a request (other than ping) before the client has sent notifications/initialized. With this enabled, a stateful session that receives e.g. tools/list after the initialize response but before notifications/initialized is rejected with -32002. initialize and ping are always allowed; the stateless path (which has no per-session initialized handshake) is exempt. OFF by default — the rule is a SHOULD and well-behaved clients send initialized immediately.
Enable the opt-in secure codec for the MRTR (SEP-2322) requestState. Once enabled, the dispatch path transparently wraps every outgoing requestState (tool, prompt, and task input-required results) and verifies every echoed incoming one — handlers keep calling inputRequired!T / requestStateAs!T against plaintext. This delivers the three SEP-2322 protections at once: integrity (the client cannot tamper with the opaque state), expiry (ttl), and user-binding (the echoed state must belong to the currently authenticated subject, defending against replay/hijack).
The server->client push channel for *unsolicited* traffic — the messages a server sends on the standalone SSE stream a client opens with an HTTP GET to the MCP endpoint (basic/transports §Listening for Messages from the Server), outside any in-flight POST. The Streamable HTTP transport creates it (sharing the supplied StreamCoordinator) when the mount is set up; it is created lazily on first access so callers can hold a reference before mounting. Use notify (or the returned channel's emit) to deliver notifications/requests to every connected GET listener.
The active server->client push channel, or null if none has been created (e.g. the server is not mounted on a Streamable HTTP transport).
Build the per-session plain-GET eligibility predicate a standalone GET stream registers with the push channel, bound to the session's resolved ConnectionState. The Streamable HTTP transport supplies this when opening the GET stream so notifications/resources/updated delivery consults the listener's own per-session subscriptions (keyed on Mcp-Session-Id) instead of the shared fallback connection.
Register a completer for a single (reference, argumentName) pair, so a consumer no longer hand-routes every completable argument inside one global setCompletionRequestHandler delegate. The delegate receives the partial value typed so far and returns the candidate completions; the server wraps them in a CompleteResult (use CompleteResult.prefixMatch for the common prefix-matching case). Declaring any completer advertises the completions capability. completion/complete dispatch tries the global handler first (when set) and falls back to a matching per-argument completer, then to an empty CompleteResult.
Observe inbound client-originated notifications.
Set the handler for completion/complete, receiving a parsed, typed CompleteRequest (the ref, the argument name/value, and any context.arguments) rather than raw Json. Declaring it advertises the completions capability. Use request.isPrompt / request.isResource to route to the appropriate per-target completer.
Configure the per-list draft CacheableResult freshness hint (ttlMs/cacheScope) emitted on a specific */list result when speaking the draft protocol. listMethod MUST be one of tools/list, resources/list, resources/templates/list, or prompts/list. Per-resource and per-template hints are supplied at registration time instead.
Set the maximum number of items returned per */list page (server/utilities/pagination). When size > 0, tools/list, resources/list, resources/templates/list and prompts/list return at most size items per response and emit an opaque nextCursor whenever more results remain; the client passes that cursor back as params.cursor to fetch the next page (the bundled McpClient list helpers follow these cursors automatically). A size of 0 (the default) disables pagination: each list returns its full contents in a single response with no cursor.
Convenience observer fired specifically on notifications/roots/list_changed. Invoked in addition to any handler registered via setClientNotificationHandler. Set to react to client root-list changes without inspecting the method string yourself.
Declare the client capabilities a registered tool's handler requires.
The effective input schema of a registered tool (the default empty-object schema if none was provided), or Json.undefined if the tool is unknown. Used by the transport for draft x-mcp-header validation.
If msg is a draft subscriptions/listen request, serve it on the stdio transport's single channel and return true; otherwise return false (the caller dispatches it normally). On the stdio transport every message shares one stdout channel, so — unlike Streamable HTTP — there is no separate SSE stream to open. Per the draft, a subscriptions/listen reply is NOT a { acknowledged: true } JSON-RPC result (the schema defines no such Result); the acknowledgement is a notifications/subscriptions/acknowledged notification that MUST be the first message on the stream. This records the opted-in change-notification filters, installs writeLine as the delivery sink (so subsequent notify*/notifyResourceUpdated are written to stdout, each stamped with the listen id as io.modelcontextprotocol/subscriptionId), and writes the stamped acknowledgement as that leading message. Pre-draft versions never defined subscriptions/listen, so they take the normal path (returns false) and the request is answered conventionally.
Construct a stateful server (opt-in, pre-draft only). initialize mints an Mcp-Session-Id and creates per-session state; the draft is excluded from negotiation; the full feature set (elicitation, GET stream, subscriptions, logging/setLevel) is available, all session-isolated.
As stateful(string, string, ...), from a full Implementation.
Construct a stateless server (the default mode). On the draft protocol this is modern stateless (per-request _meta, MRTR); on pre-draft it is legacy stateless (no-op initialize, no session id, correlation features error). No Mcp-Session-Id is ever minted. See the README "Statefulness" section.
As stateless(string, string, ...), from a full Implementation.
The transport-agnostic core of an MCP server.
McpServer owns registration and JSON-RPC dispatch. It has no I/O: feed it parsed messages via handle (or raw text via handleRaw) and it returns the response to write back. Transports (stdio, HTTP) are thin drivers over this.