Runtime Surface¶
pymcp-kit keeps the built-in surface intentionally narrow. This page is the quickest way to see what the package exposes at runtime and what capability toggles affect the MCP handshake.
Supported Protocol Versions¶
ServerSettings.protocol_mode defaults to "stable", which enables:
2025-11-252025-06-182025-03-262024-11-05
Draft 2026-07-28 support is opt-in:
ServerSettings(protocol_mode="draft") # draft only
ServerSettings(protocol_mode="dual") # draft plus stable versions
ServerSettings(protocol_versions=(...)) can be used as an explicit override for compatibility testing.
Stable Session Request Flow¶
Stable MCP revisions use initialize to negotiate server capabilities and an MCP-Session-Id to bind later Streamable HTTP requests to the active session:
sequenceDiagram
autonumber
participant Client
participant HTTP as HTTP Transport
participant Runtime
participant Session as Session Manager
participant Handler
Client->>HTTP: POST /mcp initialize<br/>MCP-Protocol-Version
HTTP->>Runtime: validate JSON-RPC request
Runtime->>Runtime: resolve_protocol_revision(...)
Runtime->>Session: create session
Runtime->>Handler: initialize
Handler-->>Runtime: serverInfo and capabilities
Runtime-->>HTTP: initialize result
HTTP-->>Client: 200 OK<br/>MCP-Session-Id
Client->>HTTP: POST /mcp tools/list<br/>MCP-Session-Id
HTTP->>Session: load session
Session-->>HTTP: active session context
HTTP->>Runtime: dispatch request
Runtime->>Runtime: InitGateMiddleware
Runtime->>Runtime: CapabilityGateMiddleware
Runtime->>Runtime: AuthorizationGateMiddleware
Runtime->>Handler: tools/list
Handler-->>Runtime: tools result
Runtime-->>HTTP: JSON-RPC response
HTTP-->>Client: 200 OK
Client->>HTTP: DELETE /mcp<br/>MCP-Session-Id
HTTP->>Session: close session
HTTP-->>Client: 204 No Content Draft Stateless Request Flow¶
Draft 2026-07-28 requests skip initialize/sessions entirely. Every request carries its own protocol version, client info, and client capabilities in params._meta, and is validated against matching HTTP headers before dispatch:
sequenceDiagram
autonumber
participant Client
participant HTTP as HTTP Transport
participant Dispatcher
participant Handler
Client->>HTTP: POST /mcp<br/>MCP-Protocol-Version<br/>Mcp-Method, optional Mcp-Name<br/>params._meta(protocolVersion, clientInfo, clientCapabilities)
HTTP->>HTTP: origin check
HTTP->>HTTP: request_body_protocol_version(data)
HTTP->>HTTP: looks_like_stateless_request() == true
HTTP->>HTTP: validate_stateless_http_metadata()
Note right of HTTP: Requires matching MCP-Protocol-Version and params._meta.protocolVersion.<br/>Requires supported version in ServerSettings.protocol_versions.<br/>Requires Mcp-Method to match body.method.<br/>Requires Mcp-Name for tools/call, resources/read, and prompts/get.<br/>Failures return HEADER_MISMATCH or UNSUPPORTED_PROTOCOL_VERSION.
alt method is subscriptions/listen
HTTP-->>Client: stream_stateless_subscription() SSE
else JSON-RPC request
HTTP->>Dispatcher: process_stateless_jsonrpc_message(data)
Dispatcher->>Dispatcher: require_request_metadata(data)
Dispatcher->>Dispatcher: resolve_protocol_revision(version, settings)
Dispatcher->>Dispatcher: build_stateless_dispatch_context(...)
Note right of Dispatcher: Creates a transient DispatchContext with<br/>protocol_revision and request_metadata.
Dispatcher->>Dispatcher: ErrorBoundary
Dispatcher->>Dispatcher: InitGateMiddleware (bypassed for stateless)
Dispatcher->>Dispatcher: RevisionMethodGateMiddleware
Note right of Dispatcher: Blocks initialize, ping, subscribe, and methods<br/>not available for the resolved revision.
Dispatcher->>Dispatcher: CapabilityGateMiddleware
Dispatcher->>Dispatcher: AuthorizationGateMiddleware
Dispatcher->>Handler: call_next()
Handler->>Handler: rpc_method handler runs<br/>(server/discover, tools/list, tools/call, etc.)
Handler->>Handler: ctx.payloads() -> PayloadFactory_2026_07_28
Handler-->>Dispatcher: resultType="complete", ttlMs=0, cacheScope="private"
Dispatcher-->>HTTP: JSON-RPC response
HTTP-->>Client: 200 OK, no MCP-Session-Id<br/>result includes resultType, ttlMs, cacheScope
end Errors short-circuit at the HTTP layer (HEADER_MISMATCH, UNSUPPORTED_PROTOCOL_VERSION) before ever reaching the dispatcher, and RequestMetadataError short-circuits inside dispatch_stateless for malformed or missing _meta fields.
Conformance¶
pymcp-kit passes the official MCP conformance suite in full against the 2025-11-25 specification. The suite runs in CI on every change (the MCP Conformance workflow) against the fixture server in tests/conformance_server.py, covering tool content blocks (text, image, audio, embedded, and mixed resources), tool errors, progress, logging, sampling, elicitation (including SEP-1034 defaults and SEP-1330 enum variants), resource reads/templates/subscriptions, and prompt retrieval.
Built-In HTTP Endpoints¶
GET /: basic server metadataPOST /mcp: Streamable HTTP MCP endpointGET /mcp: transport support endpoint for streamable sessionsDELETE /mcp: session termination endpoint for streamable sessions
Implemented MCP Methods¶
Lifecycle¶
initializepingnotifications/initializednotifications/cancelled
When draft 2026-07-28 is enabled, stateless requests use per-request _meta instead of initialize, and server/discover is available for discovery. Draft extension capabilities are advertised under capabilities.extensions. For example, task support is exposed as io.modelcontextprotocol/tasks instead of the stable top-level tasks key.
Tools¶
tools/listtools/call
Tool registration supports optional declaration metadata:
titleoutputSchemaannotations(readOnlyHint,destructiveHint,openWorldHint,idempotentHint)icons
Tool handlers may return structuredContent alongside content blocks in tools/call results.
A tool can also accept a request_context parameter to report progress. When the client includes _meta.progressToken on the tools/call request, report_progress emits notifications/progress; otherwise it is a no-op.
from pymcp import tool_registry
from pymcp.runtime.context import RequestContext
@tool_registry.register(title="Long job")
async def longJobTool(request_context: RequestContext) -> dict:
await request_context.report_progress(0, 100)
await request_context.report_progress(100, 100, message="done")
return {"content": [{"type": "text", "text": "complete"}]}
from pymcp import tool_registry
@tool_registry.register(
title="Add numbers",
output_schema={"type": "object", "properties": {"sum": {"type": "number"}}},
annotations={"readOnlyHint": True, "destructiveHint": False},
)
def addNumbersTool(a: float, b: float) -> dict:
total = a + b
return {
"content": [{"type": "text", "text": str(total)}],
"structuredContent": {"sum": total},
}
Prompts¶
prompts/listprompts/get
Resources¶
resources/listresources/templates/listresources/readresources/subscriberesources/unsubscribenotifications/resources/updated
resources/subscribe and resources/unsubscribe return an empty result ({}) per the spec; subscription state is observable through notifications/resources/updated, not the response body.
Roots (Client Capability)¶
Roots are a client capability. The server can request the client's roots via the request_roots_list() helper (sends roots/list to the client).
notifications/roots/list_changed(client -> server notification)
Completions¶
completion/complete
Prompt argument completions are resolved from each argument's schema.enum or explicit completion list. Resource template variable completions are resolved from optional variables metadata passed to register_template().
@prompt_registry.register(
arguments=[
{
"name": "language",
"required": True,
"schema": {"type": "string", "enum": ["python", "javascript", "rust"]},
}
]
)
def languagePrompt(language: str) -> str:
return language
@resource_registry.register_template(
uri_template="memo://{topic}",
variables={"topic": {"completion": ["welcome", "release-notes"]}},
)
def memoTemplate(topic: str) -> str:
return topic
Tasks¶
tasks/listtasks/gettasks/canceltasks/resultnotifications/tasks/statusnotifications/progress
Elicitation (Client Capability)¶
Elicitation is a client capability. The server sends elicitation/create to the client via the request_elicitation() helper. Both form and url modes are supported.
Sampling (Client Capability)¶
Sampling is a client capability. The server sends sampling/createMessage to the client via the request_sampling() helper.
Logging¶
logging/setLevelnotifications/message(server -> client log notification viasend_log_message())
Request and Result Metadata (_meta)¶
JSON-RPC requests may carry _meta at the top level or under params. The runtime validates that each present _meta value is an object and merges top-level and params metadata for handlers that need progress tokens or related-task links.
Tool handlers lift _meta from a tool result body onto the JSON-RPC response envelope so clients receive metadata alongside the MCP result object. Task result responses use the same envelope-level _meta pattern.
Capability Settings¶
CapabilitySettings controls which fragments appear in initialize.result.capabilities and which optional runtime behaviors are enabled.
Per the MCP 2025-11-25 spec, server capabilities (advertised in the initialize response) include: tools, prompts, resources, logging, completions, tasks, and experimental. Client capabilities (roots, sampling, elicitation) are declared by the client in the initialize request and are NOT part of server capability settings.
| Setting | Default | Effect |
|---|---|---|
tools_list_changed | False | Advertise tools.listChanged support. |
prompts_list_changed | False | Advertise prompts.listChanged support. |
resources_list_changed | False | Advertise resources.listChanged support. |
resources_subscribe | True | Advertise and enable resource subscription support. |
advertise_empty_prompts | False | Expose a prompts capability even when no prompts are registered. |
advertise_empty_resources | False | Expose a resources capability even when no resources are registered. |
logging_enabled | False | Advertise logging capability. |
completions_enabled | False | Advertise completions capability and enable completion/complete handler. |
tasks_enabled | True | Expose task capability fragments and task handlers. |
tasks_tool_call | True | Advertise task augmentation for tools/call. |
tasks_list | True | Advertise the tasks/list capability fragment. |
tasks_cancel | True | Advertise the tasks/cancel capability fragment. |
mcp_apps_enabled | False | Advertise io.modelcontextprotocol/ui support. |
mcp_apps_mime_types | ("text/html;profile=mcp-app",) | MIME types advertised for MCP Apps UI support. |
mcp_apps_settings | None | Explicit settings object for io.modelcontextprotocol/ui; overrides mcp_apps_mime_types when set. |
oauth_client_credentials_enabled | False | Advertise io.modelcontextprotocol/oauth-client-credentials support. |
oauth_client_credentials_settings | None | Explicit settings object for OAuth client credentials support. |
enterprise_managed_authorization_enabled | False | Advertise io.modelcontextprotocol/enterprise-managed-authorization support. |
enterprise_managed_authorization_settings | None | Explicit settings object for enterprise-managed authorization support. |
extensions | None | Dict of spec extension capabilities to advertise under capabilities.extensions. |
experimental_features | None | Dict of experimental server features to advertise. |
Extension flags only advertise support. Auth token validation, enterprise policy enforcement, and MCP Apps rendering/hosting remain the responsibility of the application integrating the framework.
Server Metadata¶
By default, ServerSettings() uses:
Those defaults flow into both:
- the HTTP root payload returned from
GET / initialize.result.serverInfofor session-based revisionsserver/discover.result.serverInfofor draft stateless revisions
Current Non-Goals¶
- SSE and HTTP NDJSON transports are intentionally not bundled
- metrics and tracing are not part of the shipped surface
- the package focuses on a practical MCP server core rather than a full framework stack