Capabilities
What mcpkit ships today — feature inventory tracked alongside the spec.
Source: CAPABILITIES.md
MCPKit ¶
Version ¶
0.2.43
Provides ¶
- mcp-protocol-negotiation: Version negotiation supporting MCP 2025-11-25 and 2024-11-05
- mcp-initialization-gating: Enforces initialize/initialized handshake before accepting requests
- mcp-tool-error-semantics: Spec-compliant isError tool results (not JSON-RPC errors) for handler failures
- mcp-tool-result-meta-extras:
core.ToolResultMeta.Extras(map[string]any) carries extension- or feature-namespaced metadata alongside the typedNextCursor+RelatedTaskfields. CustomMarshalJSON/UnmarshalJSONspread the map at the top level of the_metaobject; typed fields win on key collision. Mirrors theErrorData.Extrapattern incore/jsonrpc.go. Unlocks extension fixtures that need vendor keys on tool results without per-fixture library forks — pdf-server’sdisplay_pdfuses it to emit_meta.interactEnabled/_meta.viewUUIDmatching upstream’s wire shape - mcp-sse-transport: HTTP+SSE transport (MCP 2024-11-05) with per-session SSE streams
- mcp-streamable-http-transport: Streamable HTTP transport (MCP 2025-03-26) with Mcp-Session-Id header sessions
- mcp-dual-transport: Both SSE and Streamable HTTP simultaneously via WithSSE/WithStreamableHTTP options
- mcp-graceful-shutdown: ListenAndServeGraceful with SSE hub drain on SIGTERM
- mcp-auth-middleware: Bearer token (constant-time), Claims propagation via ClaimsProvider, JWT/OIDC via oneauth sub-module
- mcp-auth-submodule: mcpkit/ext/auth — JWTValidator, MountAuth (PRM), WWW-Authenticate builders, RequireScope, OAuthTokenSource, DiscoverMCPAuth, ValidatePKCES256, DefaultClientRegistration. Generic OAuth (RegisterClient, ClientCredentialsSource, ValidateHTTPS, ValidateCIMDURL) re-exported from oneauth/client via type aliases (#158).
- mcp-extensions: Extension/Stability/ExtensionProvider system — sub-modules declare spec version + stability in initialize
- mcp-annotations: Annotations field on ToolDef/ResourceDef/PromptDef + RegisterExperimental* helpers
- mcp-client-auth: WithClientBearerToken, WithTokenSource — auth header injection on all client requests
- mcp-auth-conformance: 14/14 required auth conformance scenarios passing (210/210 checks)
- mcp-tool-timeout: context.WithTimeout wrapper for tool execution
- mcp-allowed-roots: WithAllowedRoots + core.IsPathAllowed — per-session sandbox enforcement using intersection of static server roots and dynamic client roots. Handler-side helper, not automatic middleware. (#197)
- mcp-roots-fetch-timeout: WithRootsFetchTimeout — configurable deadline for server-to-client roots/list requests. Default 30s. (#198)
- mcp-resources: resources/list, resources/read, resources/templates/list with URI template matching
- mcp-prompts: prompts/list, prompts/get with argument passing
- mcp-prompt-argument-schema: PromptArgument.Schema — declarative JSON Schema on prompt arguments (mirrors ToolDef.InputSchema). Clients render typed inputs; server-side validation tracked by #184 (#87)
- mcp-content-cardinality-tolerance: Defensive parsing of
contentfield cardinality across PromptMessage/SamplingMessage/ToolResult/ResourceResult/Content.resource. Accepts both single-object and array forms on read; always emits spec-canonical form on write (#81) - mcp-pagination: Generic cursor-based pagination for all list methods
- mcp-cancellation: notifications/cancelled with inflight request tracking and context cancellation
- mcp-logging: logging/setLevel + notifications/message via EmitLog() with per-session atomic log level
- mcp-streamable-sse-streaming: Streamable HTTP POST returns SSE stream when Accept: text/event-stream, enabling mid-request notifications with delivery order guarantees
- mcp-notification-ordering: Client receives notifications (logging, progress) before tool results across all transports; WithNotificationHandler works on Streamable HTTP, SSE, and in-memory
- mcp-progress: notifications/progress via EmitProgress() with _meta.progressToken
- mcp-completion: completion/complete for argument autocompletion
- mcp-dns-rebinding-protection: Origin header validation on Streamable HTTP (WithAllowedOrigins)
- mcp-resource-subscriptions: resources/subscribe, resources/unsubscribe, notifications/resources/updated via WithSubscriptions() + Server.NotifyResourceUpdated()
- mcp-broadcast: Server.Broadcast(method, params) sends notifications to ALL connected sessions unconditionally (no subscription required)
- mcp-sampling: Server-to-client sampling/createMessage via Sample() — server asks client LLM for inference
- mcp-elicitation: Server-to-client elicitation/create via Elicit() — server asks client for user input
- mcp-elicitation-url-mode: URL-mode elicitation (SEP-1036) — ElicitURL() sends mode=“url” with URL + elicitationId for out-of-band user interaction. ElicitationCap{Form, URL} structured capability. notifications/elicitation/complete for completion signaling. ErrCodeURLElicitationRequired (-32042) with composable error data (FineGrainedAuth-ready). Client mode validation + WithElicitationURLSupport(). 5/5 conformance scenarios.
- mcp-authorization-denial-experimental: EXPERIMENTAL AuthorizationDenial envelope + RemediationHint types for FineGrainedAuth (draft SEP). ScopeStepUpHint helper for UC2 scope escalation. NewAuthorizationDenialError composer. Examples: elicitation/ (UC1 consent), fine-grained-auth/ (UC2 Keycloak scope step-up). UC3 (RAR/PSD2) stubbed pending oneauth RAR.
- mcp-conformance: Official MCP conformance test suite integration (30/30 server passing, 14/14 auth passing)
- mcp-client: Go MCP client for Streamable HTTP — Connect, ToolCall, ReadResource, ListTools, ListResources
- mcp-testutil: TestClient wrapper for e2e testing MCP servers (httptest + testing.T integration)
- mcp-auth-e2e: E2E auth tests with real oneauth AS (31 tests: JWT validation, transport auth, scopes, PRM, WWW-Authenticate, reconnection, middleware)
- mcp-server-middleware: Request/response middleware chain (WithMiddleware, LoggingMiddleware) — intercepts after auth, before dispatch
- mcp-client-logging: Transport debug logging (WithClientLogging) — logs method, latency, errors for every operation
- mcp-client-reconnect: Automatic reconnection with exponential backoff (WithMaxRetries, WithReconnectBackoff) �� re-initializes MCP session on transient errors
- mcp-client-auth-retry: Client transport 401/403 handling — doWithAuthRetry, ScopeAwareTokenSource, ClientAuthError
- mcp-sub-packages: core/server/client package split — types in core, server+transports in server/, client in client/
- mcp-in-process-transport: server.NewInProcessTransport + client.WithTransport — typed *Request/*Response, no HTTP (for tests/embedded)
- mcp-stdio-transport: Content-Length framed JSON-RPC over stdin/stdout — Server.RunStdio() + client.WithStdioTransport() for editor-spawned MCP servers (Cursor, Claude Desktop)
- mcp-stateless-mode: WithStateless — no sessions, fresh dispatcher per request (for serverless/CLI)
- mcp-session-management: Server.CloseSession/CloseAllSessions — programmatic session teardown
- mcp-structured-output: StructuredContent + OutputSchema on ToolDef/ToolResult — typed tool output
- mcp-server-run: Server.Run(addr) — simple blocking entry point defaulting to Streamable HTTP
- mcp-error-codes: ErrCodeServerError (-32000) + documented JSON-RPC error code ranges
- mcp-typed-handler-contexts: ToolContext, ResourceContext, PromptContext — typed handler contexts with IDE-discoverable methods (EmitLog, EmitProgress, Sample, Elicit, AuthClaims, etc.). BaseContext shared across all handler types. ToolContext adds EmitProgress/EmitContent. Free functions preserved as thin wrappers. (#179)
- mcp-sealed-handler-returns: core.ToolResponse / core.PromptResponse sealed interfaces — ToolHandler returns (core.ToolResponse, error); PromptHandler returns (core.PromptResponse, error). ToolResponse variants: ToolResult (sync “complete” envelope), InputRequiredResult (SEP-2322 “input_required”), CreateTaskResult (SEP-2663 “task”), GoAsyncResult (in-process spawn signal, never serialized). Sealing via unexported toolResponse()/promptResponse() marker methods — third-party types can’t impersonate a core variant. core.ToolResult shed three sentinel fields (IsInputRequired, InputRequests, GoAsync) that previously carried in-process plumbing alongside wire fields — now a pure wire shape. Dispatch type-switches on the variant; only InputRequiredResult gets reshaped (fresh requestState mint). ext/tasks middleware intercepts GoAsyncResult to spawn the SEP-2663 continuation goroutine. ctx.RequestInput returns typed (core.InputRequiredResult, error) directly. core.TypedTool[X, core.ToolResponse] is a first-class shape alongside Out=core.ToolResult / string. Migration: docs/HANDLER_RETURNS_MIGRATION.md. Sequenced before SEP-2322 prompt scenarios (#452) — InputRequiredResult plugs into PromptResponse via a one-line marker. (#486, #487)
- mcp-parametric-tests: forAllTransports — core client tests run against all 4 transports as subtests (Streamable HTTP, SSE, in-memory, stdio)
- mcp-apps-extension: MCP Apps (io.modelcontextprotocol/ui) extension negotiation — server advertises via WithExtension(UIExtension{}), client detects via ServerSupportsUI()
- mcp-apps-ui-metadata: UIMetadata, UICSPConfig, UIVisibility types on ToolDef._meta.ui and ResourceReadContent._meta.ui
- mcp-apps-resource-serving: ui:// resources with text/html;profile=mcp-app MIME type, template resources for parameterized URIs
- mcp-apps-visibility: Tool visibility filtering — UIVisibilityModel/UIVisibilityApp, client-side ListToolsForModel() excludes app-only tools
- mcp-apps-ref-validation: RefValidator interface — extensions validate tool-to-resource references at server startup
- mcp-apps-resource-notification: NotifyResourcesChanged(ctx) — tool handlers signal resource list changes to clients
- mcp-apps-register-helper: RegisterAppTool (ext/ui) — registers tool + resource pair in one call via ToolResourceRegistrar interface. Auto-detects template URIs (containing
{) and routes to RegisterResourceTemplate; concrete URIs use RegisterResource. - mcp-apps-display-modes: UIMetadata.SupportedDisplayModes — apps declare inline/fullscreen/pip support. RequestDisplayMode(ctx, mode) emits notifications/ui/displayMode. (#185)
- mcp-apps-template-resources: RegisterAppTool auto-detects template URIs and registers resource templates. TemplateHandler field on AppToolConfig for parameterized HTML serving (SSR). (#190)
- mcp-apps-template-fallback: RegisterAppTool auto-generates concrete fallback resource (
ui://{host}/{tool}/latest) for template URIs when TemplateHandler is provided. Wraps tool handler to capture args, delegates fallback to TemplateHandler with stored params. Transparent to consumers — removed when hosts support template substitution. (#213) - mcp-uri-template-helpers: core.URITemplateVars/core.IsTemplateURI — RFC 6570 template detection using yosida95/uritemplate (replaces string-based
{checks) - mcp-apps-elicitation-meta: ElicitationRequest._meta.ui and CreateMessageRequest._meta.ui — app metadata on server-to-client requests. ElicitWithApp/SampleWithApp helpers in ext/ui. (#191)
- mcp-apps-conformance: 21 MCP Apps conformance tests (tool metadata, resources, visibility, fallback, negotiation)
- mcp-apps-compat-parity: Drop-in mcpkit-Go fixtures under
examples/apps/compat/<name>/that mimic upstreammodelcontextprotocol/ext-appsTypeScript examples byte-for-byte at the protocol surface (tool name + title + input/output schema +_meta.uishape + verbatimdist/mcp-app.html), so upstream’sbasic-hostPlaywright suite runs unmodified against the Go binary.scripts/apps-playwright-test.shorchestratesbasic-host+ the fixture + a per-fixture__snapshots__/<key>.pngbaseline;UPDATE_SNAPSHOTS=1regenerates the committed PNG. Two modes share one wrapper: native (host OS, fastloads app UIiteration; the visual screenshot test is Docker-pinned and will fail on non-Linux hosts intentionally) and Docker (make test-apps-playwright-docker, runs everything insidemcr.microsoft.com/playwright:v1.57.0-noble— same image upstream uses fortest:e2e:docker). DOCKER mode runs a stricttools/listparity check that spins up upstream’s TypeScript reference server on a side port and JSON-diffs both — protocol surface drift fails the build. First fixture:basic-server-vanillajs(PR 534 introduced the drop-in; PR 537 added Docker mode, single canonical baseline, artifact visibility, and the drift check; PR 538 closed the surface-drift gaps and flipped the gate strict). Cluster expansion: PR 540 added thebasic-server-preact/react/solid/svelte/vueframework variants; PR 543 addedquickstart+transcript-server; PR 545 addedsheet-music-servervia the newTypedAppToolConfig.InputSchemaOverrideescape hatch (closes 542); PR 549 addedintegration-server(5/5 including the 3 interaction tests),system-monitor-server,cohort-heatmap-server,customer-segmentation-server, anddebug-server(3 fresh schema-gen gaps surfaced and tracked in issue 548); PR 552 addedmap-server,threejs-server,shadertoy-server, andwiki-explorer-server(introducesOutputSchemaOverridefor nullable types like upstream’sz.string().nullable()); PR 553 addedbudget-allocator-serverandscenario-modeler-server(rich nested data shapes — scenario-modeler uses OutputSchemaOverride for its nullablebreakEvenMonth); PR 555 addedpdf-serveragainst upstream’s default-without---enable-interact4-tool surface; the follow-up PR expanded it to the 9-tool--enable-interactsurface with a Go-side command queue + long-poll + viewer-rendezvous backend (queue.go, bytes.go) and lands 16/17 reachable Playwright tests across servers.spec.ts + pdf-annotations.spec.ts + pdf-incremental-load.spec.ts + pdf-viewer-zoom.spec.ts (1 form-field-names test deferred — needs streaming AcroForm parser to satisfy the noforms-30%-budget invariant; 3 LLM-gated tests auto-skip via ANTHROPIC_API_KEY) — 21 of 25 upstream examples now pass strict tools/list parity + visual regression in DOCKER mode. The umbrella also markslazy-auth-serverandvideo-resource-serveras SKIP (not in upstream’sservers.spec.ts). Drop-in pattern + wiring overview + pinning doc:examples/apps/compat/README.md. Umbrella tracking: issue 533. - mcp-apps-tool-title:
core.ToolDef.Title— optional human-readable display name distinct fromName. Per MCP spec hosts SHOULD prefer Title for user-facing surfaces (dropdowns, button labels); Name remains the machine identifier passed totools/call. Exposed onext/ui.AppToolConfig.Titleandext/ui.TypedAppToolConfig.Title. (PR 538) - mcp-apps-tool-execution:
ext/ui.AppToolConfig.Executionandext/ui.TypedAppToolConfig.Execution— pass-through tocore.ToolDef.Execution, lets app-compat fixtures declare&core.ToolExecution{TaskSupport: core.TaskSupportForbidden}to match upstream’s signal that the tool doesn’t participate in async tasks. (PR 538) - mcp-apps-tool-output-schema:
RegisterTypedAppToolnow propagatesOutputSchemaderived bycore.TypedTool[In, Out]through to the wire. Previous behavior dropped it silently. (PR 538) - mcp-apps-tool-input-schema-override:
ext/ui.TypedAppToolConfig.InputSchemaOverride— when set, bypasses invopop-driven struct-tag reflection and uses the caller-supplied schema verbatim. Necessary for fixtures whose default / description values contain commas (invopop’s tag parser truncates at the first comma) or that need JSON Schema 2020-12 features struct tags can’t express. Sheet-music compat fixture uses this for its 11-line ABC notation default. (PR 545, closes 542) - mcp-apps-tool-output-schema-override:
ext/ui.TypedAppToolConfig.OutputSchemaOverride+core.WithOutputSchemaOverride(schema)— symmetric mirror of the input-side override for the OutputSchema field. Use when Out’s auto-derived schema can’t be made byte-identical to an external reference: nullable types (z.string().nullable()wants{"type": ["string","null"]}/anyOfform which Go reflection won’t produce),interface{}/anyfields invopop reflects to schemas strict MCP-SDK clients reject. Wiki-explorer compat fixture uses this for its nullableerrorfield. (PR 552) - mcp-tool-schema-patch:
core.WithInputSchemaPatch(fn)/core.WithOutputSchemaPatch(fn)+ matchingext/ui.TypedAppToolConfig.{Input,Output}SchemaPatchfunc fields. Reflection runs first; the patch function receives a*core.SchemaBuilderover the live schema map and edits in place viaProp(name)/Require(...)/Raw().*core.PropertyBuildercovers Type/Desc/Default/Min/Max/MinLength/MaxLength/Enum/Pattern/Required +Replace(rawSchema)for the irreducible cases (nullable, anyOf, record-of-union). Precedence:Overridewins overPatchwhen both are set. Shrinks the override boilerplate ~60-70% on the easy “tweak a few fields” cases while keeping the struct as the source of truth. (PR closing issue 556; refactors 8 compat fixtures opportunistically) - mcp-any-reflects-to-empty-schema: invopop emits
"<field>": trueforinterface{}/anyproperties — spec-valid JSON Schema 2020-12 but rejected by the MCP TypeScript SDK’s zod validator.core.GenerateSchema[T]()now post-processes the output to replace bare-trueschemas insideproperties.*/items/allOf|anyOf|oneOfwith{}. Mirrors zod’sz.unknown()emit. Removes the per-fixtureInputSchemaOverrideworkaround (debug-server’sPayload anyfield reflects cleanly now). Issue 548 Gap 2. - mcp-apps-app-only-tool-helper:
ext/ui.AppToolConfig.ResourceURIis now optional. EmptyResourceURIskips the companionRegisterResourcecall entirely; the tool still lands with itsUIMetadata(Visibility, CSP, Permissions, …) and no_meta.ui.resourceUri. Removes thecore.TypedTool+ Title-mutation + manualsrv.RegisterToolpattern for app-only tools without their own UI resource. Issue 548 Gap 3. - mcp-apps-meta-dual-resourceuri:
core.ToolMeta.MarshalJSONemits_meta["ui/resourceUri"]alongside the nested_meta.ui.resourceUriwhen set;UnmarshalJSONaccepts either form (nested wins on disagreement). Mirrors upstream@modelcontextprotocol/ext-apps/server’sregisterAppToolRESOURCE_URI_META_KEYbehavior so older client codepaths reading either form work transparently. (PR 538) - mcp-schema-dollar-schema:
core/schema.go’s invopop-backed default schema generator now emits$schema(defaults to draft-2020-12) instead of stripping it. Self-describing schemas help clients pick the right validator; was a silent omission before. Tools using the default generator now carry$schemaon everyinputSchema/outputSchema. (PR 538) - mcp-apps-bridge: MCP App Bridge — framework-agnostic JS for iframe postMessage protocol. TypeScript source → compiled JS + .d.ts. Global MCPApp singleton: on/off/once events, callTool, readResource, sendMessage, updateModelContext, openLink, downloadFile, requestDisplayMode. Style utilities (applyTheme, applyStyleVariables, applyFonts). AbortSignal/timeout support. Bidirectional tool calls (oncalltool, onlisttools). Auto ResizeObserver + CustomEvent dispatch for HTMX. Graceful no-op when not hosted. (v0.2.18–v0.2.23)
- mcp-apps-bridge-template: BridgeData + BridgeTemplateDef() — Go html/template integration for explicit bridge inclusion. template.JS for safe unescaped JS. Single
<script type="module">pattern. (v0.2.19) - mcp-apps-bridge-serve: ServeBridge() HTTP handler at /_mcpkit/mcp-app-bridge.js — serves bridge JS for external
<script src>loading - mcp-apps-bridge-inject: InjectAppBridge(html) + AppShellHTML(title, body) — convenience helpers for inline bridge injection
- mcp-json-no-html-escape: core.MarshalJSON with SetEscapeHTML(false) — JSON-RPC responses preserve literal <, >, & matching Node.js/Python behavior. Fixes HTML content in resource responses for MCP Apps hosts. (v0.2.19)
- mcp-protogen: experimental/ext/protogen — protoc plugin (protoc-gen-go-mcp) generates MCP registrations from proto service definitions. Proto annotations (mcp_tool, mcp_resource, mcp_prompt, mcp_service) with full field support. In-process, gRPC forwarding, and ConnectRPC forwarding variants for all three primitives. JSON Schema derived from proto messages. Uses typed handler contexts. Published to buf.build/mcpkit/protogen. (#211, #216, #217, #218)
- mcp-protogen-tool-annotations: mcp_tool annotation fields: name, description, timeout, structured_output, result_summary. Validated at generation time (invalid names/timeouts are fatal errors). Namespace prefix via mcp_service.namespace. (#216)
- mcp-protogen-resources: mcp_resource annotation → server.Resource (static) or server.ResourceTemplate (parameterized). URI template detection via core.IsTemplateURI. runtime.BindParams delegates to protokit PopulateFromMap for type-coerced field binding. (#217)
- mcp-protogen-prompts: mcp_prompt annotation → server.Prompt with auto-derived PromptArguments from request message fields. runtime.BindPromptArgs and ProtoPromptResult helpers. (#218)
- mcp-protogen-grpc-errors: RPCError extracts gRPC status code, message, and details (proto Any) as StructuredError with {code, message, details} JSON. Agents can parse and recover programmatically. (#224)
- mcp-protogen-result-summary: mcp_tool.result_summary template: “Slide {position} updated (v{version})”. runtime.ProtoSummaryStructuredResult renders from response fields. (#224)
- mcp-protogen-embedded-templates: Codegen templates use go:embed (templates/file.go.tmpl) instead of Go string constants
- mcp-protogen-buf: Proto module published to buf.build/mcpkit/protogen. experimental/ext/protogen/Makefile with build, lint, generate, push targets
- mcp-protogen-typed-contexts: Generated in-process server interfaces use typed handler contexts (ToolContext, ResourceContext, PromptContext) instead of context.Context. Gives impls direct access to ctx.Sample(), ctx.Elicit(), ctx.EmitProgress() etc. gRPC/Connect client interfaces unchanged.
- mcp-protogen-sampling: mcp_sampling annotation on tool methods — generates pre-configured SampleForXxx() helper with system_prompt, max_tokens, include_context, model preferences. Service-level default_sampling in mcp_service; method-level overrides.
- mcp-protogen-elicitation: mcp_elicit annotation on tool methods — schema_message references a proto message, JSON Schema auto-derived via schema.FromMessage(). Generates typed ElicitXxx() helper returning (*SchemaMsg, action, error). Uses generic runtime.BindElicitResult[T] for type-safe unmarshaling.
- mcp-protogen-completions: completable_fields on mcp_resource and mcp_prompt annotations. Generates deduplicated Completer interface + RegisterXxxMCPCompletions dispatcher.
- mcp-client-toolcall-full: Client.ToolCallFull returns *core.ToolResult directly — preserves IsError, all Content blocks, and StructuredContent. Tool-level errors returned in result, not as Go errors. (#215)
- mcp-dynamic-registration: Registry.AddTool/RemoveTool/AddResource/RemoveResource/AddPrompt/RemovePrompt — thread-safe runtime registration with automatic notifications/*/list_changed broadcast via OnChange callback
- mcp-session-timeout: WithSessionTimeout — idle session cleanup for Streamable HTTP (timer + ref counting to avoid closing mid-execution)
- mcp-sse-resumption: WithSSEGracePeriod — SSE sessions survive brief disconnects with grace timer. Client reconnects via ?sessionId= query param; server replays missed events via Last-Event-ID header. Principal-bound for security.
- mcp-server-capabilities-typed: core.ServerCapabilities, ToolsCap, ResourcesCap, PromptsCap — typed structs for initialize response capabilities
- mcp-command-transport: CommandTransport — spawn subprocess MCP servers, communicate via stdio, graceful SIGTERM/SIGKILL shutdown, stderr capture, env passthrough. WithCommandTransport client option supports reconnection (auto-restart).
- mcp-tool-exec: ToolExec — wrap CLI binaries as MCP tools with structured I/O. ExecConfig supports static/dynamic args, env, dir, timeout. BuildArgs callback maps JSON tool arguments to CLI flags.
- mcp-modify-request: WithModifyRequest — client-side HTTP request hook for injecting custom headers (tracing, tenant IDs). Runs before auth, applies to Streamable HTTP + SSE transports.
- mcp-sse-retry-hint: core.EmitSSERetry — tool/resource/prompt handlers emit raw SSE
retry:field to tell clients how long to wait before reconnecting. Both SSE (2024-11-25) and Streamable HTTP transports. Streamable HTTP routes the hint from the POST handler to the session’s GET SSE stream. Hint-only (no disconnect). Combines with WithSSEGracePeriod + WithEventStore for drop-and-resume patterns. (#72, #202) - mcp-tool-context-detach: core.DetachFromClient — tool handlers opt into surviving client disconnect and per-tool timeout. Uses context.WithoutCancel to strip cancellation while preserving session state. Combine with EmitSSERetry + GracePeriod + EventStore for long-running tools with result replay on reconnect. (#203)
- mcp-auth-refresh-callback: OAuthTokenSource.OnToken — optional callback fired after successful refresh_token grant by the underlying oneauth AuthClient. Use for external persistence without implementing CredentialStore. (#137)
- mcp-auth-refresh-flow: OAuthTokenSource.Token() attempts the refresh_token grant before falling back to LoginWithBrowser. Long-running clients (agents, CLI tools) no longer re-prompt for browser consent on every token expiry. Default in-memory cred store when CredStore is nil; TokenForScopes wipes stored credential to force full re-login on scope step-up; scope-coverage check skips refresh when stored credential doesn’t cover requested scopes. (#196)
- mcp-typed-tool-registration: core.TypedTool[In, Out] and core.TextTool[In] — generic typed handlers with auto-derived JSON Schema from Go struct tags (via invopop/jsonschema). Zero schema drift between InputSchema and handler parameters. Out type dispatch: string → TextResult (no OutputSchema), struct → StructuredResult (auto OutputSchema), core.ToolResult → passthrough, core.ToolResponse → passthrough (for handlers returning polymorphic sealed-interface variants — InputRequiredResult, GoAsyncResult, CreateTaskResult). TextTool[In] is sugar for TypedTool[In, string]. SchemaGenerator interface with default invopop binding, overridable via core.SetSchemaGenerator(). (#242, #486)
- mcp-io-transport: Server.RunIO(ctx, r, w) — Content-Length framed JSON-RPC over arbitrary io.Reader/io.Writer streams. Generalizes stdio transport for Unix sockets, named pipes, SSH tunnels, test fixtures. Client: WithIOTransport(r, w). RunStdio/WithStdioTransport delegate to RunIO/WithIOTransport. (#253)
- mcp-logging-transport: core.LoggingTransport — wire-level JSON-RPC message tracing decorator for any core.Transport. Logs method names, direction (→/←), latency. Optional LogBodies for full JSON output. Complements server middleware (method-level) with transport-level visibility. (#254)
- mcp-auto-pagination: Client iterators c.Tools(ctx), c.Resources(ctx), c.ResourceTemplates(ctx), c.Prompts(ctx) — iter.Seq2 auto-pagination over cursor-based list responses. Generic paginate[T] helper handles cursor threading and context cancellation. (#252)
- mcp-custom-method-handlers: server.HandleMethod / WithMethodHandler — register handlers for custom JSON-RPC methods (e.g., events/poll, events/stream). Dispatched after initialization, participates in middleware. Built-in MCP methods cannot be overridden (panics). Uses core.MethodContext typed context. (#266)
- mcp-method-context: core.MethodContext — typed handler context for custom method handlers. Embeds BaseContext (EmitLog, Sample, Elicit, AuthClaims, Notify, DetachFromClient). Matches ToolContext/ResourceContext pattern.
- mcp-events-library-experimental: experimental/ext/events — EXPERIMENTAL library for MCP Events spec (triggers-events-wg). EventSource interface, TypedSource[Data] with auto-derived payloadSchema, Register() for protocol methods, WebhookRegistry with HMAC-SHA256 signing (ts+"."+body), TTL, retry with backoff, SSRF validation. cursorGap detection for ring buffer wrap. (#264)
- mcp-events-spec-alignment-2026-06: experimental/ext/events — wire-shape + behavior alignment with the
panyam/mcpconformance2026-06-15 spec batch. Wire-breaking. Inner subscription field renamedparams→arguments(spec commit082166f0) on all four events methods (subscribe/poll/unsubscribe/stream); Go fieldParams→Argumentsswept acrossRegisterParams/WebhookTarget/SubscribeOpts/ Go SDK options / GORM column.ttlMstristate request field (absent / int /null) decoded viajson.RawMessage(preserves the tristate Go’s*int64collapses) →WebhookRegistry.NegotiateExpirypolicy oracle (clamp envelope[MinWebhookTTL, MaxWebhookTTL]+WithUnsafeWebhookTTLBypasshonored +WithAllowInfiniteWebhookTTLgates null acceptance);refreshBeforeresponse is*time.Timeeverywhere (always present, RFC3339 or JSON null per spec commit99f3589c); SDKSubscription.RefreshBefore()returns*time.Time(nil = no-expiry; refresh loop drops to 1h health-check cadence).DeliveryStatus.Throttled bool+RetryAfterMs *int64projector-only (spec commit21be9c31; adopters wire from their own throttle state).410 Goneis abandon-without-failure: retry-loopcase http.StatusGone:returns withoutrecordDeliveryFailuresoActive=true/LastError=Nonepreserved (spec commit905ade36).DefaultWebhookAckTimeout = 5snamed constant pinned (spec commitb506e347). PRs 778 + 779 closed issues #762 / #763 / #765-partial / #767 / #760; integration testTestNoExpirySurvival_RestartSurvivalThroughGORMSQLite(stores/gorm) pins end-to-end restart-survival. - mcp-events-no-expiry-subscriptions: experimental/ext/events —
WithAllowInfiniteWebhookTTL()opt-in (default OFF) gates the registry into acceptingttlMs: nullrequests + returningrefreshBefore: nullper spec PR1 commit99f3589c§“Subscription TTL”.WebhookTarget.ExpiresAtflipped to*time.Timewith compiler-enforced nil guards across prune loop /Targets()/DeliverToTarget(nil = no-expiry sentinel; finite-TTL targets exempted from no-expiry GC).warnIfInfiniteTTLWithDefaultStorefires a stark construction-time warning when the opt-in is set withoutWithWebhookStore(persistent)(no-expiry subs in the in-memory store violate the spec’s “MUST persist across restarts” obligation; warning not reject so dev/test setups can opt in). Failure-based GC via newDeliveryStatus.FailingContinuouslySince *time.Timeanchor (never reset by sliding suspend window, only by successful delivery) —recordDeliveryFailurecheckstarget.ExpiresAt == nil && now - *FailingContinuouslySince > r.noExpiryFailureGCWindowand firesPostTerminatedwith distinguishableControlErrormessage (“no-expiry subscription dropped after sustained delivery failure”).WithNoExpiryFailureGCWindow(d)tunes the threshold (defaultDefaultNoExpiryFailureGCWindow = 72h). Suspend transition is suppressed in the same call when the GC drop fires (the drop supersedes the same-call pause). GORM columnstatus_failing_continuously_sincenullable round-trip. Wire-projected as RFC3339 diagnostic so subscribers can observe the GC anchor on subscribe-refresh.FailingContinuouslySinceis deliberately distinct fromFailedSince: the suspend state machine resetsFailedSinceaftersuspendWindowquiet periods (reactivation by refresh), whereas the failure-GC trigger needs “really been broken across N days, with no intervening success” — two anchors, two cleanup paths. Verification persistence — the other spec-listed no-expiry obligation — stays under #490. PRs 779 + 783 closed issues #760 / #764. Whole-enchilada walkthrough beats (Phase 6b) demonstrate end-to-end viamake webhookknobsTTL_MS=null+EXIT_AFTER=N; helper targetsmake psql-webhooks+make restart-event-servers(PR 786, partial advance on #696). - mcp-telegram-events-example: experimental/telegram-events — Reference server implementing MCP Events with Telegram. Three delivery modes: push (Broadcast+SSE), poll (events/poll), webhook (events/subscribe+HMAC POST). 21 tests. Companion to Clare Liguori’s TypeScript impl. (#264)
- mcp-slog-handler: core.MCPLogHandler — slog.Handler that routes structured log records through MCP notifications/message. SlogToMCPLevel/MCPToSlogLevel bidirectional mapping. Respects per-session logging/setLevel. (#248)
- mcp-client-setloglevel: Client.SetLogLevel(level) — convenience method for logging/setLevel. Client.ListPrompts() for prompts/list.
- mcp-tasks-experimental: server/tasks_v1.go + server/task_*.go — MCP Tasks protocol (spec 2025-11-25). Async tool execution with lifecycle tracking. server.RegisterTasksV1() installs middleware + tasks/get, tasks/result, tasks/list, tasks/cancel handlers. InMemoryTaskStore with channel-based WaitForResult/WaitForUpdate. Client helpers (client.GetTaskV1, ToolCallAsTaskV1, etc.) in client/tasks_v1.go. v1 path is FROZEN (renamed from RegisterTasks during SEP-2663 migration); new code should target the v2 surface.
- mcp-tasks-side-channel: TaskContext.TaskElicit/TaskSample — background tasks can elicit user input or request LLM sampling via the tasks/result side-channel. The tasks/result handler proxies requests through its live POST SSE connection. Status transitions: working → input_required → working.
- mcp-tasks-detach-background: core.DetachForBackground(ctx) — returns a context suitable for background goroutines. Server registers a detach strategy that replaces the dead POST-scoped requestFunc with the session-level persistent push (GET SSE). Aligns with future sub-task spawning (#281).
- mcp-tasks-parent-task: TaskInfo.ParentTaskID — backward-compatible extension field for sub-task trees. Foundation for SpawnTool/WaitForTask threading model (#281).
- mcp-tasks-cancel-propagation: context.WithCancel on background goroutines — Cancel() fires ctx.Done() so tool handlers can exit early. activeTask struct consolidates channel + cancel func.
- mcp-tasks-status-notifications: notifications/tasks/status sent on cancel, completion, and status changes (Option 1 — from live handler context). Queue-based delivery deferred (#288).
- mcp-tasks-atomic-store: StoreTerminalResult — atomic result + status transition with terminal guard. Prevents cancel→completed race and double-completion.
- mcp-tasks-progress-token: _meta.progressToken from tools/call preserved on TaskContext.ProgressToken(). Background goroutine uses client’s token for EmitProgress.
- mcp-tasks-client-helpers: client/tasks_v1.go — ToolCallAsTaskV1 (variadic TaskCallOptionsV1), WaitForTaskV1(ctx), IsToolTaskV1, GetTaskV1/GetTaskPayloadV1/ListTasksV1/CancelTaskV1. v1 path frozen — see mcp-tasks-v2-client for the canonical names.
- mcp-tasks-post-sse-closure: POST-scoped SSE writer signals closure via atomic flag. Background goroutines get silent no-ops instead of panics on dead ResponseWriter.
- mcp-server-withmux: server.WithMux — TransportOption for registering additional HTTP routes (auth PRM, health checks) on the server’s mux while staying on srv.Run()’s graceful shutdown path.
- mcp-sending-middleware: server.NotifyInterceptor + RequestInterceptor — wrap outgoing notifications and server-to-client requests. WithNotifyInterceptor/WithRequestInterceptor options. (#244)
- mcp-client-middleware: client.ClientMiddleware — call-level middleware on Client.Call path. WithClientMiddleware option. Method+params visibility for tracing, logging, metrics. (#245)
- mcp-session-hijack-protection: Streamable HTTP session hijacking prevention — binds Claims.Subject at session creation, verifies on POST/GET/DELETE. Mirrors SSE transport protection. (#249)
- mcp-public-methods: server.WithPublicMethods — bypass auth for specified JSON-RPC methods. Pre-auth capability discovery (tools/list without token). (#76)
- mcp-ctx-progress: ToolContext.Progress(progress, total, message) — token-free progress emission. Dispatch stashes progressToken from _meta.progressToken into ToolContext. ToolContext.ProgressToken() accessor.
- mcp-typed-app-tool: ext/ui.RegisterTypedAppTool[In, Out] — typed variant of RegisterAppTool. Auto-derives InputSchema from In type. Delegates to RegisterAppTool for all app-specific wiring.
- mcp-auth-examples: examples/auth/ — 5 persistent MCP servers demonstrating auth patterns: bearer (:8081), JWT/JWKS (:8082), scopes (:8083), session hijacking (:8084), pre-auth discovery (:8085). Shared common/ module. mcp.json for VS Code.
- mcp-tasks-types: core.TaskStatus (working/input_required/completed/failed/cancelled), TaskInfo, ToolExecution, TasksCap, ClientTasksCap — wire types for MCP Tasks spec 2025-11-25. ToolDef.Execution declares per-tool task support (required/optional/forbidden).
- mcp-tasks-server-plumbing: Server.SetTasksCap(), Server.UseMiddleware(), Registry.ToolDef() — server-side hooks for tasks capability advertisement and middleware injection post-construction.
- mcp-tasks-library: server/task_*.go + server/tasks_v1.go — MCP Tasks v1 protocol (spec 2025-11-25, wire-format parity with TS SDK). server.RegisterTasksV1(TasksConfigV1) installs middleware + method handlers. Side-channel elicitation/sampling via TaskContext.TaskElicit/TaskSample (v1 path; v2 path goes through tasks/update). v1 client helpers in client/tasks_v1.go.
- mcp-tasks-callbacks: server.TaskCallbacks — per-tool GetTask/GetResult overrides for the external proxy pattern (Step Functions, CI pipelines). Registered via server.Tool{TaskCallbacks: &TaskCallbacks{…}}. creatorToolForTask map dispatches to callbacks; falls through to TaskStore when callback returns false.
- mcp-tasks-conformance: conformance/tasks/ — 27 scenarios testing full Tasks v1 protocol surface. Uses official TS SDK client against both Go and TS reference servers. Covers lifecycle, errors, TTL, concurrency, session isolation, elicitation, sampling, progress, status notifications, related-task _meta.
- mcp-tasks-v2-extension: SEP-2663 io.modelcontextprotocol/tasks extension — tasks v2 is advertised under capabilities.extensions, not the v1 capabilities.tasks slot. core.TasksExtensionID + ClientSupportsTasks(ctx) helper. Server.RegisterExtension for runtime declaration (mirrors WithExtension). v2 server gates tools/call task creation and tasks/* methods on negotiation; unsupported clients get -32601 for tasks/* and synchronous tools/call.
- mcp-tasks-v2-types: core/task_v2.go — SEP-2663 wire types: CreateTaskResult (flat
Result & Task— embeds TaskInfoV2 so resultType/taskId/status/ttlMs/… all sit at the top level, no nestedtaskwrapper), DetailedTask discriminated union with WorkingTask/InputRequiredTask/CompletedTask/FailedTask/CancelledTask aliases, GetTaskResult alias, TaskInfoV2 (ttlMs, pollIntervalMs, both integer milliseconds per the 2026-05-07 SEP-2663 commit aligning all duration suffixes; no parentTaskId), UpdateTaskRequest, empty-ack UpdateTaskResult/CancelTaskResult, TaskError (JSON-RPC shape). resultType is camelCase like the rest of the MCP wire (Luca confirmed camelCase is the SEP-2322 spec standard). - mcp-tasks-v2-mrtr: SEP-2322 MRTR base types in core/task_v2.go — InputRequest{Method, Params}, InputRequests/InputResponses typed map aliases, ResultTypeComplete/Incomplete constants. server v2 runtime tracks per-task v2InputState (monotonic key generator + per-key waiter channels); TaskContext.TaskElicit/TaskSample park on input_required, tasks/get surfaces inputRequests, tasks/update delivers responses to waiters. External-backed tools (Temporal/SQS) hook in via planned TaskCallbacks.OnInputResponse extension point.
- mcp-tasks-v2-server: ext/tasks/tasks.go (separate go.mod) — tasks.Register(tasks.Config) is the canonical SEP-2663 entry point. Internals: taskV2Middleware gates on extension (session OR SEP-2575 per-request _meta), gateOnTasksExtension wraps tasks/get/update/cancel handlers, toTaskInfoV2 boundary helper passes internal TaskInfo (ms) through to wire TaskInfoV2 unchanged (both surfaces use integer milliseconds). tasks.Config.DefaultTTLMs replaces the older DefaultTTLSeconds knob. tasks/cancel returns empty {} ack but still notifies DetailedTask via SSE. ext/tasks defines its own TaskContext/WithTaskContext/GetTaskContext/activeTask/generateTaskID (duplicated from server/ rather than shared) so v1 retirement is pure deletion in server/.
- mcp-tasks-v2-mcp-name-header: SEP-2243 Mcp-Name HTTP response header carries the new taskId on task-creating tools/call responses. Generic plumbing via core.WithResponseHeaderCollector(ctx) + core.SetResponseHeader(ctx, k, v) + core.CollectResponseHeaders(ctx). Streamable HTTP transport applies staged headers in both JSON and SSE paths (before sseStarted flip in writeSSE). v2 task middleware stages Mcp-Name after task creation.
- mcp-tasks-v2-per-request-caps: SEP-2575 _meta.io.modelcontextprotocol/clientCapabilities — per-request capability override on tools/call. core.PerRequestClientCaps(json.RawMessage) decoder + core.ClientSupportsExtensionForRequest(ctx, id, raw) combines session-level + per-request declarations (additive). v2 middleware checks both for the tasks extension before creating a task.
- mcp-tasks-v2-client: client/tasks.go — canonical SEP-2663 helpers: ToolCall (returns polymorphic *ToolCallResult with Sync or Task; IsTask() helper for branch readability), GetTask (TaskOptions{RequestState}), UpdateTask (typed core.UpdateTaskRequest, client-side TaskID guard), CancelTask (TaskOptions), WaitForTask (WaitOptions{PollInterval, RequestState}; honors server’s PollIntervalMs with 1s floor + 30s ceiling; threads requestState echo automatically; abort-on-cancel via ctx cancellation per SEP-2663 commit a1ed0703). client.WithTasksExtension() option.
- mcp-tasks-v2-hybrid-removed: the prior RegisterTasksHybrid that stacked v1 and v2 on one server was removed when v2 moved to ext/tasks/ (a sub-module can’t be co-orchestrated with v1’s unexported helpers without exporting them, and we chose not to). Servers needing both during a rolling-upgrade window should register independently: server.RegisterTasksV1 + tasks.Register, accepting that the last HandleMethod registration wins on the tasks/get and tasks/cancel slots.
- mcp-tasks-v2-conformance: SEP-2663 + SEP-2322 + SEP-2575 + SEP-2243 server-conformance migrated upstream to the
panyam/mcpconformancefork (branchfeat/tasks-mrtr-extension). 8 ClientScenario classes / ~33 checks for tasks (tasks-lifecycle,tasks-capability-negotiation,tasks-wire-fields,tasks-request-state,tasks-mrtr-input,tasks-request-headers,tasks-dispatch-and-envelope,tasks-status-notifications); 1 class / 7 checks + 1 SKIPPED (mrtr-tasks-composition) undersrc/scenarios/server/mrtr/for MRTR↔Tasks composition coverage that complements SEP-2322’s PR 188. Brand-neutral, language-agnostic runner — fixture wired viaTASKS_SERVER_URL/TASKS_SERVER_CMD(andMRTR_SERVER_URL/MRTR_SERVER_CMD);sh -cspawn, TCP-poll readiness, no log-line scanning.make testconf-tasks-v2/testconf-mrtrbuild the example Go fixtures + invoke the fork’s vitest run + chain a local sentinel run for future mcpkit-stricter scenarios. Localconformance/tasks-v2/andconformance/mrtr/folders host placeholder vitest tests today; reserved for assertions that go beyond what the spec mandates (mcpkit picks the louder/safer option where spec is silent). Mcp-Name response header echo is covered by Go testsTestV2_McpName*/TestV2_McpMethod*(mcpkit-specific behavior, not in the brand-neutral suite). Migration guide: docs/TASKS_V2_MIGRATION.md. - mcp-stateless-wire: SEP-2575 stateless wire — full alternate protocol shape served alongside the legacy session wire on one URL. Server: stateless.Dispatcher sub-package (server/stateless/) consumes a stateless.Backend interface implemented by server/statelessBackend (adapter to *Server + *Registry). Three wire modes via stateless.Mode (LegacyOnly/Dual/Stateless) + server.WithStatelessMode + MCPKIT_STATELESS_MODE env + stateless.DefaultMode package var — option > env > default precedence. Shipping default = ModeDual (additive: existing servers gain stateless wire transparently). Dispatcher handles server/discover (DiscoverResult{supportedVersions, capabilities, serverInfo}), validates per-request _meta envelope (-32602 on miss), validates protocol version against core.SupportedStatelessVersions (-32004 + UnsupportedProtocolVersionData{supported, requested}), routes tools/list, tools/call, resources/list/read, resources/templates/list, prompts/list/get, completion/complete. Removed legacy methods (initialize/ping/logging/setLevel/resources/(un)subscribe) short-circuit to -32601 + HTTP 404 via stateless.HTTPStatusForCode mapper. subscriptions/listen runs an SSE stream (server/streamable_subscriptions.go): first frame notifications/subscriptions/acknowledged with subscriptionId in _meta, every notification stamped via stateless.NewTaggedFrame, server.broadcast fans list-changed events to both legacy GET SSE sessions AND stateless subscribers. Header/_meta cross-check via core.ErrCodeHeaderMismatch (-32020 — shared with SEP-2243 routing-header path; both surfaces emit core.HeaderMismatchData {reason, header, expected, received, …extra}). Cap-gated tool errors via typed core.MissingCapabilityError → -32021 + core.MissingRequiredClientCapabilityData{requiredCapabilities: ClientCapabilities object}. ctx.Sample / ctx.Elicit route through MRTR on stateless wire (server-initiated push forbidden per SEP-2575) via core.NewSamplingInputRequest / core.NewElicitationInputRequest + matching DecodeSamplingInputResponse / DecodeElicitationInputResponse. Client side: client.ClientMode (LegacyOnly/Adaptive/Stateless) + client.WithClientMode + MCPKIT_CLIENT_MODE env + client.DefaultClientMode (default LegacyOnly for backward compat; Adaptive is migration-path opt-in). Adaptive Connect probes server/discover, falls back to legacy initialize on -32601/404. Once classified stateless, every outgoing call carries the _meta envelope (auto-injected via wrapParamsForStatelessWire) and the MCP-Protocol-Version HTTP header; 4xx-with-JSON-content-type bodies decoded as JSON-RPC errors instead of HTTPStatusError. Conformance: 25/26 pass via
make testconf-stateless(1 known-fail = upstream test’s array-shape assertion vs schema’s object-shape requiredCapabilities; tracked for upstream alignment). Fixture: examples/stateless/ runnable server with cart tools (create_cart/add_item/checkout via server.HandleStore[Cart]) + 5 diagnostic tools the upstream conformance scenario drives by name (test_missing_capability, test_streaming_elicitation, test_logging_tool, test_trigger_tool_change, test_trigger_prompt_change). UPSTREAM_AUDIT.md rowserver-statelessflipped partial 2/20/4 → fork-covered 19/1/2/4 (out-of-box testserver) / 25/1/0/0 (with fixture’s diagnostic tools). - mcp-handle-store: SEP-2567 explicit-handle pattern is design guidance only — no wire contract, no upstream conformance. Any storage a tool handler can call (Redis, SQL, sync.Map, custom RPC) satisfies the pattern. mcpkit ships server.HandleStore[T] as opt-in scaffolding for the on-ramp case: server.HandleStore[T] interface (Mint/Put/Get/Delete/Len/Close) for the create_*() → handle pattern, with server.InMemoryHandleStore[T] as the default impl returned by server.NewHandleStore[T]. Three options: WithHandleDefaultTTL (per-store default; 0=no expiry), WithHandleIDPrefix (cosmetic “cart-AB12…”, “doc-…”), WithHandleGCInterval (background sweep; 0=lazy-only). IDs are base32-encoded 128-bit crypto-random (26 chars) optionally prefixed; collision-resistant for any sane store size; time-derived fallback on rand.Read failure. Negative ttl forces “never expires” even when defaultTTL is set; lazy expiry on Get returns ok=false past TTL with no gc goroutine required. Persistent backends (Redis etc.) tracked in #471 — drop in via the HandleStore[T] interface with zero parent-package changes. Admin endpoints tracked in #472. Demonstrated end-to-end in examples/stateless/’s cart story; add_item uses HandleStore.Put for in-place update so the cart_id stays stable across rounds (the SEP-2567 happy path).
- mcp-mrtr-ephemeral: SEP-2322 ephemeral MRTR (Multi Round-Trip Requests) — server returns InputRequiredResult{resultType:“input_required”, inputRequests, requestState}, client retries SAME tools/call with inputResponses + echoed requestState. Renamed from IncompleteResult / “incomplete” in SEP-2322 commit de6d76fb (merged 2026-05-06) per dsp-ant request, since “incomplete” collided semantically with task-creating tools/call responses. Stateless: dispatch decodes prior answers from requestState (core.MRTRRoundState), merges with current round’s inputResponses, surfaces unified map to handler. core.InputRequiredResult type + Sign/VerifyMRTRState (HMAC-SHA256, 24h default TTL) + Encode/DecodeMRTRStatePlaintext (no-key fallback). server.WithRequestStateSigning(key, ttl) — shared by MRTR and SEP-2663 Tasks (TasksConfig.RequestStateKey overrides per-RegisterTasks). Tool handler API: ctx.RequestInput(reqs) returns typed (core.InputRequiredResult, error) — satisfies the sealed ToolResponse interface (PR #487). Accessors: ctx.InputResponse(key) / HasInputResponses() / RequestState(). Client API: client.CallToolWithInputs(ctx, c, name, args, handler, opts…) auto-loops; client.DefaultInputHandler(c) routes inputRequests through Client.HandleServerRequestWithContext (single source of truth — same dispatcher real server-initiated requests use). Conformance scenarios live in the
panyam/mcpconformancefork (src/scenarios/server/mrtr/ephemeral-flow.ts, 7 checks + 1 SKIPPED — MRTR→Tasks composition tracked in matching server/mrtr_test.go skip). The fork tests both legacy and stateless wires by default — mcpkit passes legacy; stateless wire fails on round-2 due to a pre-existingcallToolForStatelessgap (does not decode inputResponses / requestState). PinMCP_WIRE_MODES=legacyto scope to what mcpkit currently supports end-to-end. Local sentinel inconformance/mrtr/reserved for mcpkit-stricter scenarios. - mcp-list-ttl: SEP-2549 cache-freshness hint on every paginated list response (tools/list, prompts/list, resources/list, resources/templates/list).
TTL *int(seconds) on all four core list result types — pointer semantics preserve the spec’s three states: nil = absent (no guidance), &0 = “do not cache”, &N>0 = fresh for N seconds. server.WithListTTL(seconds) Option (negative = unset, ≥0 = emitted) — uniform across endpoints. Client-side: new ListXPage(cursor) typed helpers (ListToolsPage / ListPromptsPage / ListResourcesPage / ListResourceTemplatesPage) return the full result envelope so callers can read TTL alongside NextCursor; existing zero-arg ListTools() etc. + Tools(ctx) iterator unchanged for backward compat. 5 conformance checks (SDK-based) on the panyam/mcpconformancependingbranch undersrc/scenarios/server/list-ttl/, exercised across 3 server processes (positive / zero / unset) viamake testconf-list-ttl. - mcp-file-inputs-experimental: SEP-2356 file inputs — Phase 1 fully shipped (server + client + conformance 7/7). Surfaces: core.FileInputDescriptor{Accept, MaxSize int}, core.FileInputProperty / FileInputArrayProperty / ExtractFileInputDescriptor / StripFileInputKeywords schema helpers, core.EncodeDataURI / DecodeDataURI / IsDataURI codec (RFC 2397 base64, percent-encoded
name=matching url.PathEscape), core.ValidateFileInput + core.FileMatchesAccept + typed errors (FileTooLargeError, FileTypeNotAcceptedError) with sentinels (ErrFileTooLarge, ErrFileTypeNotAccepted) and reason constants (FileInputReasonTooLarge, FileInputReasonTypeNotAccepted), ClientCapabilities.FileInputs marker + core.HasFileInputs(ctx). Server: server.WithFileInputValidation() Option (auto-validates file args in tools/call) + capability gating (dispatcher strips x-mcp-file from tools/list and elicitation requestedSchema for clients without the cap, per spec MUST NOT). Client: client.WithFileInputs() advertises the cap on initialize, client.FileInputsFromTool(tool) extracts per-property descriptors, client.PrepareFileArg(path, desc) reads + validates + encodes a file in one call. Conformance: 7 SDK-based checks on the panyam/mcpconformancependingbranch undersrc/scenarios/server/file-inputs/, runnable viamake testconf-file-inputs. examples/file-inputs/ demokit walkthrough drives the full surface — three tools (upload_image with image/ + 5 MiB cap, analyze_documents with PDF array, process_any_file unconstrained) plus the apps-mode wrappers via ext/ui (selectFile/selectFiles bridge primitive — see mcp-apps-bridge-selectfile-experimental). - mcp-apps-bridge-selectfile-experimental: SEP-2356 Phase 2.1 — bridge
mcp.selectFile(descriptor)andmcp.selectFiles(descriptor)JS primitives. Synthesize a hidden<input type="file">in the iframe DOM, await selection, run client-side validation (accept patterns + maxSize, mirroring server-side ValidateFileInput planned in #361), and resolve with an RFC 2397 base64 data URI carrying a percent-encodedname=parameter that matchescore.EncodeDataURIbyte-for-byte. Sentinel errors (MCPFileSelectionCanceled,MCPFileTooLarge,MCPFileTypeNotAccepted) align with server-side-32602reasons. Picker code lives in ext/ui/assets/file-picker.ts (extracted module; imported by mcp-app-bridge.ts + bundled via esbuild). examples/file-inputs/apps/ ships two reference HTML pages (single-image and multi-PDF) demonstrating the in-iframe picker → tools/call round-trip. Wire-shape-agnostic: data URI is also the SEP-2631FileValue.inlineBase64payload variant. Issue #358; deferred host-mediated variant tracked in #370. - mcp-otel-trace-contract: SEP-414 Phase 1 contract surface — dependency-free
core.TracerProvider/core.Span/core.Attributeinterfaces withcore.NoopTracerProviderdefault (zero-alloc).core.TraceContext{Traceparent, Tracestate}+IsZero();ExtractTraceContext/InjectTraceContextoperate onmap[string]anyrepresenting MCP_meta. W3C version-00 structural validation — malformed traceparent drops tracestate too (W3C “MUST NOT forward”).MetaKeyTraceparent/MetaKeyTracestateconstants are bare W3C names (NOT underio.modelcontextprotocol/— that namespace is reserved for MCP-defined fields).WithTraceContext/TraceContextFromContextthread the value throughcontext.Context.BaseContext.TraceContext()accessor on every typed handler context (ToolContext/PromptContext/ResourceContext/MethodContext). Issue 312 P1; PR 644. Phase 2 (PR 649) wires this contract into the server dispatch path. - mcp-otel-server-spans: SEP-414 Phase 2 server-side propagation.
server.WithTracerProvider(tp core.TracerProvider)installs an outermost trace middleware that wraps every JSON-RPC dispatch in an inbound span; nil andcore.NoopTracerProvider{}both skip the install entirely (zero overhead on the unconfigured path). Inbound trace context resolution:params._meta.traceparentwins over thecore.WithTraceContextvalue on ctx (out-of-band, populated by the SEP-2028 HTTPtraceparent/tracestateheader bridge that fires once atstreamable_transport.handlePostand is inherited by every downstream dispatch path). Span attributes:mcp.methodalways,mcp.tool.nameon tools/call,mcp.session.idwhen present,mcp.error.code+Span.RecordErroron JSON-RPC errors,mcp.tool.is_error="true"onToolResult.IsError. Outbound_meta.traceparent/_meta.tracestateinjected on every server-to-client message (notifications + sampling/elicitation/roots) via internal wraps ofsc.notify/sc.requestusing newcore.WrapSessionNotifyFunc/core.WrapSessionRequestFunchelpers (mirror the existingApplySessionNotifyFilterpattern); the wrap sits OUTSIDE any user-registered NotifyInterceptor / RequestInterceptor, so user interceptors observe the augmented wire form. Explicit handler-set_meta.traceparenton outbound messages wins over inject. Newcore.ExtractTraceContextFromParams(json.RawMessage)helper lets the middleware decouple from method-specific envelope shapes (tools/call’s name/arguments, prompts/get’s name, …). Tests use a fake TracerProvider (no go.opentelemetry.io/otel dep). Phase 4 (ext/otel/adapter) shipped subsequently; Phase 3 client-side spans (P3) and Phase 5 polished walkthroughs (P5) still TODO. Issue 312 P2; PR 649. Doc: docs/SEP_414_OTEL.md. - mcp-otel-adapter: SEP-414 Phase 4 OpenTelemetry SDK adapter — new
ext/otel/sub-module (separatego.mod, mirrorsext/auth//ext/tasks//ext/ui/).otel.NewProvider(otelTP trace.TracerProvider, opts ...Option) *Providerreturns acore.TracerProviderimplementation backed by an OTelTracerProvider. Parses inboundcore.TraceContextinto an OTelSpanContext(Remote=true) and installs as the new span’s parent viatrace.ContextWithSpanContext. AfterStartSpan, formats the new span’sSpanContextinto a W3C version-00 traceparent and re-attaches viacore.WithTraceContextso the SEP-414 P2 outbound_metainjection stamps the child span’s traceparent on every server-to-client message.core.Attribute→attribute.String.Span.End()CAS-guarded for idempotency (prevents the SDK’s “span already ended” warning under double-End scenarios).Span.RecordError(err)emits both an OTel exception event AND setscodes.Errorstatus.WithInstrumentationName(name)option overrides the default"github.com/panyam/mcpkit/server"library identifier. Library deps:go.opentelemetry.io/otel{,/trace}. Tests drive the real OTel SDK withsdktrace.NewTracerProvider(WithSyncer(InMemoryExporter))— no fake. Panics fast on nilotelTP(silent no-op would lose spans without surfacing the misconfig). Runnable example:examples/otel/stdout/(separate go.mod, usesstdouttraceexporter — no collector infra required).make test-otel+make test-otel-examplegates. Polished walkthroughs (Jaeger / OTLP) deferred to P5. Issue 312 P4. Doc:ext/otel/README.md. - mcp-otel-active-span-accessor: SEP-414 P6 contract-gap closer (issue 661).
core.SpanFromContext(ctx) core.Spanreturns the currently-active mcpkit Span (or a no-op Span when none is attached) — sibling ofTraceContextFromContext. Symmetriccore.WithActiveSpan(ctx, span) Contextis what TracerProvider adapter authors call afterStartSpanto publish the span into ctx; nil span is a no-op. Both the in-treeNoopTracerProviderand theext/oteladapter follow the pattern, so the “after StartSpan, SpanFromContext returns the same span” contract holds regardless of provider.BaseContext.Span()accessor on every typed handler context (ToolContext/PromptContext/ResourceContext/MethodContext) delegates to the package-level helper. Returned span is NEVER nil — call sites that always-decorate (auth middleware addingmcp.auth.*attributes, tool handlers adding result-shape hints) work without nil-checking and without anext/otelimport. Enrichment pattern:core.SpanFromContext(ctx).SetAttribute(k, v)— the documented alternative to nesting a child span when the goal is “decorate, not nest”. Unblocks issue 658 (ext/authjwks/introspection/oauth attributes). Tests drive the contract through both Noop andext/otelSDK paths. Issue 663 (P6 umbrella) child D. Doc: docs/SEP_414_OTEL.md “Active-span accessor” section. - mcp-otel-span-links: SEP-414 P6 contract-gap closer (issue 662). Adds the OTel-aligned span-links surface to
core/:core.Link{TraceContext, Attributes}mirrors the OpenTelemetry specLinkdefinition (upstream trace identity + per-link attributes that observability backends render in their link UI aslink.kind=originated-from/sibling-task/ etc.);core.LinkFromTraceContext(tc) Linkconvenience for the no-attribute simple case; widenedcore.Spaninterface gainsAddLink(Link)for mid-flight link discovery (matches OTel Go SDK v1.30+Span.AddLinkidiom); newcore.LinkedTracerProviderinterface extendsTracerProviderwithStartSpanLinked(ctx, name, []Link, attrs...)for the option-at-start common case; package-levelcore.StartSpanLinked(tp, ...)helper type-asserts and falls back to plainStartSpanwhen the provider doesn’t implement the wider interface (links silently dropped). Capability-widening pattern (sibling interface + helper) kept the baseTracerProviderinterface unchanged so non-tracing test fakes didn’t churn; theSpaninterface widening required one-lineAddLinkno-ops onnoopSpan+ the twoserver/clienttest-filefakeSpanstructs.ext/otel.Providerimplements bothLinkedTracerProviderandSpan.AddLink— links flow through tooteltrace.Link{SpanContext, Attributes}via the existingtraceContextToSpanContexthelper; invalidcore.Linkentries (zero or W3C-malformedTraceContext) silently dropped at both entry points;AddLinkafterEndis a no-op (CAS-guarded wrapper). Unblocks issue 659 (ext/taskstask.execute+ poll spans — landed in PR 719) and the detached edge of issue 664 (server outbound reverse-call spans linked to originating request). Tests cover Noop fallback,LinkedTracerProviderrouting,LinkFromTraceContexthelper, OTel link emission, per-link attribute flow-through, invalid-link filtering, mid-flightAddLink, andAddLinkafterEnd. Issue 663 (P6 umbrella) child E. Doc: docs/SEP_414_OTEL.md “Span links” section. - mcp-otel-new-root-span: SEP-414 P6 contract complement to span links, landed alongside the
ext/tasksconsumer work (issue 659; PR 719).core.WithNewRootSpan(ctx) Contextmarks ctx so the nextTracerProvider.StartSpan/StartSpanLinkedcall produces a span with NO parent — even when ctx carries an inherited parent (acore.TraceContextattached upstream, or the adapter’s own internal span context, e.g. the OTeltrace.SpanContextinstalled by a previous StartSpan). Adapter-facing companioncore.IsNewRootSpanRequested(ctx) boolis what TracerProvider implementations read to decide whether to strip any inherited parent before starting the span. Motivation: async surfaces whose spanned work conceptually outlives the originating request —task.execute(issue 659), events-bus producer spans, future surfaces — must NOT nest under their few-ms originator. Without the marker,context.WithoutCancelinsidecore.DetachForBackgroundwould silently re-parent under the (already-ended) create span.ext/otel.Provider.startSpanInternalhonors the marker by stripping any OTel parent SpanContext on ctx beforetracer.Start— when the marker fires,core.TraceContext-based parent install is skipped entirely (the whole point is suppressing inheritance). NoopTracerProvider and adapters that don’t honor the marker silently ignore it (best-effort by design — the spawned span starts under whatever parent ctx happened to carry, which degrades to the same trace tree the unmarked path would produce). Pair withcore.StartSpanLinked+ aLinkback to the originating span so the lifecycle stays navigable. Tests inext/otel/provider_test.gopin five cases: inherits-from-TraceContext (default), detaches-from-core-TraceContext, detaches-from-OTel-parent, plain-ctx-still-produces-root, StartSpanLinked-with-NewRootSpan-emits-root-with-links. Issue 663 (P6 umbrella) child E follow-on. Doc: docs/SEP_414_OTEL.md “New-root-span marker” section. - mcp-otel-auth-tracing: SEP-414 P6 first surface child (issue 658; PR 694, closed).
ext/auth/JWTConfig.TracerProvider core.TracerProvideropts the validator into instrumentation: anauth.jwks_lookupsub-span (withmcp.auth.jwks.kidattribute; records error on failure) wraps eachJWKSKeyStore.GetKeyByKidcall, capturing both cache-hit and cache-miss latency. Active dispatch span getsmcp.auth.method = "jwt"(stamped early — failure paths included), plusmcp.auth.subject/issuer/scopes/cache_hitset after claims extraction, viacore.SpanFromContext. ext/auth depends oncore.TracerProvideronly for its own spans (no compile-time dep on ext/otel). Nil and Noop both produce zero spans / attributes / allocation. ThejwksKeyFuncmethod becamejwksKeyFuncCtx(ctx)closure because jwt-go’sKeyfuncsignature has no ctx param — needed for the span boundary. Tests use a localfakeTracerProvidermirroringserver/trace_middleware_test.go’s shape (no OTel SDK dep). Doc:ext/auth/docs/DESIGN.md§ Tracing. Transport-level 401 (pre-middleware) documented as known gap. Out of scope (deferred):auth.introspect(no introspection validator in ext/auth today),auth.oauth_exchange(TokenSource paths client-side), scope_middleware instrumentation. Outbound traceparent on JWKS HTTP filed and shipped as panyam/oneauth#254 → v0.1.14; mcpkit consumes viamcp-otel-auth-oneauth-bridge. - mcp-otel-auth-oneauth-bridge: PR 699 threads oneauth v0.1.14+ tracing through ext/auth so an end-to-end auth trace shows the inside of the JWKS call too. Three layered helpers enable this without abstraction leakage: (1)
ext/otel.Provider.OTelTracerProvider() trace.TracerProvider— Provider stashes the underlying OTel TP and exposes it for callers that need to hand the same TP to downstream libraries which take the OTel SDK type directly. (2)examples/common/otel.UnderlyingOTelTP(tp core.TracerProvider) trace.TracerProvider— type-asserts to*mcpotel.Providerand returns the underlying TP, or nil for Noop / non-mcpotel providers (oneauth’s options no-op cleanly on nil). (3)ext/auth/JWTConfig.OneauthTracerProvider trace.TracerProvider— when set, threaded viakeys.WithTracerProvideron the JWKSKeyStore so oneauth’s internal JWKS work (refresh, key_lookup, signature_verify) emits spans on the same OTel pipeline as ext/auth’sauth.jwks_lookup. Two distinct fields (one core, one OTel) because oneauth’s API takes the OTel SDK type directly and unwrapping inside ext/auth would couple it to ext/otel. ext/auth gains OTel as a transitive dep via oneauth v0.1.14+ anyway, so the import cost is zero. Test pattern: live SDK TP +tracetest.InMemoryExporter+oneauth.*span-name prefix check (not exact names) so oneauth can evolve its vocabulary without breaking the wiring assertion. Adopter pattern inexamples/auth/common/setup.goviaWithMCPTracerProvider/WithOneauthTracerProvidervariadic options onEnv.NewValidator— the 6 existing call sites keep working untouched. CI gotcha: when bumping oneauth in ext/auth (or any one module), sweep ALL modules in lock-step (currently 8 use oneauth: cmd/testclient, ext/auth, tests/keycloak, tests/e2e, examples/auth, examples/fine-grained-auth, examples/events/discord, examples/events/telegram) — leaving even one behind fails CI’stest-authjob because tests/e2e fails MVS resolution with stale go.mods. Runmake tidy-allafter the sweep. oneauth v0.1.17 is the current pin (v0.1.14 = the original tracing release; v0.1.15-17 follow-up fixes). - mcp-otel-tasks-tracing: SEP-414 P6 final surface child (issue 659; PR 719, closed).
ext/tasks/tasks.Config.TracerProvider core.TracerProvideropts the v2 task runtime into span-link instrumentation of the async lifecycle. Atools/callthat creates a task spawns a background goroutine whose work outlives the create span — parent-child doesn’t fit, so the runtime emitstask.executeas a NEW root trace (viacore.WithNewRootSpan+core.StartSpanLinked) carrying aLinkback to the originatingtools/callcreate span. Attributes:mcp.task.idstamped at StartSpan,mcp.task.statusstamped at End from the final stored status (completed/failed/cancelled/input_required).RecordErrorfires on protocol-level failures (mwErr, resp.Error, unexpected result shape, panic recover); handler-returned errors map tocompletedwithIsError=trueper SEP-2663 semantics — they DO NOT call RecordError. End is exactly once (defer guards). Everytasks/get/tasks/update/tasks/canceldispatch span additionally callscore.SpanFromContext(ctx).AddLink(core.LinkFromTraceContext(rt.getOrigin(taskID)))so backends can pivot from any poll into the whole lifecycle. TheoriginTCruntime map stashing the create-span identity per taskID outlives theactiveTaskentry (so terminal-state polls still get the link) — matches the existingtaskErrors/creatorToolForTasklifecycle. Sync-as-completed path (wrapSyncAsCompletedTask) deliberately emits NOtask.executespan — the work lives inside the create span and a zero-duration wrapper would be noise; only the GoAsync path getstask.execute. ext/tasks depends oncoreonly — no compile-time dep on ext/otel (testify added as a test-only dep). Tests use a recording fakecore.TracerProvider+core.LinkedTracerProviderto prove ext/tasks calls the contract correctly without dragging ext/otel into its module graph; end-to-end materialization is proven byext/otel/provider_test.go’s coverage ofWithNewRootSpanscrub +StartSpanLinked+AddLinkpaths against a real OTel SDK exporter. 8 newTestTrace_*cases cover: GoAsync→completion, →handler-error (v2 completed-with-IsError semantics), →panic (RecordError + failed), →cancel; AddLink on tasks/get / tasks/update / tasks/cancel dispatch spans; sync-as-completed skip; Noop zero-overhead; new-root marker propagation. Nil orcore.NoopTracerProvider{}(the default) skips the install entirely — zero overhead, zero allocation. Issue 663 (P6 umbrella) child A — completes the auth (658) + ui (660) + events (683) + tasks (659) surface set. Doc:ext/tasks/README.md§ Tracing +docs/SEP_414_OTEL.md§ ext/tasks task lifecycle spans. - mcp-events-fanout-spans: SEP-414 P6 fanout instrumentation (issue 724; PR 730, closed).
experimental/ext/events/Config.TracerProvider core.TracerProvideropts every Source implementingTracerProviderInstaller(notablyYieldingSource) into per-yield fanout span emission. Oneevents.fanoutspan per emitted event (NOT per subscriber) wraps the per-subscriber fanout loop insideYieldingSource.yield, carrying aggregate counts on attributes:events.subscribers.total/events.subscribers.delivered/events.subscribers.dropped_by_match/events.transforms.applied, plusmcp.event.name+mcp.event.idfor cross-referencing. Parented by the yield ctx so the span stitches into the originating request trace (when yield runs inside a request handler) or starts a fresh trace (when yield runs from a background feeder). Zero-subscriber yields skip span emission entirely — idle sources (feeders running with no subscribers registered yet) are dominant; emitting empty fanout spans every 2s would flood Tempo with noise. Noop short-circuit — thetp.(core.NoopTracerProvider)type-assertion avoids the StartSpan call entirely on the unconfigured path. Internal API change:YieldingSource.deliverEventToSlotwidened to return(matched, transformed bool)so the parent yield can stamp accurate counts. PublicTracerProviderInstallerinterface (SetTracerProvider(tp core.TracerProvider)) lets TypedSource authors and future sources opt in identically; the install loop inevents.Registertype-asserts each Source and silently skips ones that don’t implement it. Per-subscriberevents.match/events.transformspans (option (a) from the issue body) deliberately NOT shipped — would scale linearly with subscribers × events and need sampling design; the aggregate counts onevents.fanoutcover the diagnosable shape (operators see “this yield went to 10 subs, 7 dropped by Match”). Webhook-side + poll-side Match/Transform are NOT instrumented in this PR either — webhook fanout already hasevents.webhook.deliver(PR 714); poll-side is covered by the events/poll dispatch span. Tests use a fakeTP recording machinery (reused fromwebhook_span_test.go) covering: EmitsSpanWithAttributes, DroppedByMatch, TransformsApplied, NoSubscribers_SkipsSpan, ParentedByYieldCtx, EventAttributesPresent, NoopTracerProvider_NoSpansEmitted, RegisterInstallsTracerProvider. kitchen-sink adopts viaevents.Config{... TracerProvider: tp}(the same tp threaded bycommonotel.SetupTelemetrypermcp-otel-examples-adoption). Issue 663 (P6 umbrella) child — completes the events surface alongsidemcp-events-bus-trace-relay. Doc:experimental/ext/events/README.md+docs/SEP_414_OTEL.md§ Events fanout span emission +examples/events/kitchen-sink/README.md§ Tracing. - mcp-events-bus-trace-relay: SEP-414 P6 events surface child (issue 683; PR 712 + PR 714 closed; SSE-side follow-up issue 715 closed by PR 722). Bidirectional W3C Trace Context propagation across the experimental/ext/events module — yield → emit hook → webhook delivery → HTTPSource inject. Four gates, three carriers, one caller-preserves rule. Carriers per gate: ctx (in-process),
event.Meta.traceparent(persistent, survives JSON serialization + replay), HTTPtraceparentheader (cross-process wire). PR 712 changedyield(data)closure /SetEmitHook(func(Event))/SetMetaFunc(func(Data))/WebhookRegistry.Deliver(event)/ package-levelEmit / EmitToWebhookssignatures to take ctx — backward-incompatible by design (user OK’d; experimental module).YieldingSource.yieldstampsevent.Meta.traceparentfromcore.TraceContextFromContext(ctx)using core.InjectTraceContext; metaFunc-set traceparent wins (uniform with core.InjectTraceContextIntoParams + Apps Bridge relay).WebhookRegistry.deliverextracts via newtraceContextForDelivery(ctx, event)helper (ctx → event.Meta fallback) and stamps W3Ctraceparent+ optionaltracestateHTTP headers on every retry attempt.HTTPSource.serveInjectextracts inboundtraceparent/tracestateHTTP headers, buildscore.TraceContext, attaches viacore.WithTraceContext(r.Context(), tc), callss.yield(ctx, data)— closes the cross-process round-trip. PR 714 addedWebhookRegistry.WithWebhookTracerProvider(tp core.TracerProvider)option emittingevents.webhook.deliverspan around each retry loop with attributeswebhook.target.id,webhook.url,mcp.event.name,http.method,http.response.status_code,webhook.retry.attempts;RecordErroron retries-exhausted with the categorical failure bucket (connection_refused/timeout/tls_error/http_4xx/http_5xx). Nil/Noop = zero overhead unconfigured. PR 714 also widenedServer.Broadcast(ctx, method, params)soevents.Emitcan thread ctx through the SSE push path; PR 722 (issue 715) closed the loose end —Server.Broadcastnow readscore.TraceContextFromContext(ctx)and injects_meta.traceparent/_meta.tracestateonto notification params (viacore.InjectTraceContextIntoParams) before the fan-out loop, so SSE-pushed notifications stitch into the originating yield’s trace. Caller-set_meta.traceparentwins (delegates to the helper’s contract). Per-transportbroadcastcallbacks widened tofunc(context.Context, string, any)as a forward-compat seam (currently unused inside; the per-requesttrace_middleware.WrapSessionNotifyFunctargetssc.notifyon a per-requestSessionCtx, not the transport-level base notifyFunc dispatchers expose to broadcasts — injecting once at the Server level keeps tracing concerns out of the transport-level loops).events.webhook.deliverspan live-verified against the LGTM stack (docker/observability/+examples/whole-enchilada/events/event-server): span landed in Tempo with all 6 attributes set,webhook.retry.attempts=4(initial + 3 retries),STATUS_CODE_ERRORon the connection-refused failure path, duration matched 0.5s+1s+2s backoff exactly. Adopter sweep (PR 714):examples/events/discord+examples/events/telegramupdated to new ctx-aware closures. whole-enchiladaevent-serverwiresevents.WithWebhookTracerProvider(tp)against the existingcommonotel.SetupTelemetryTP. Cross-replica peer-fanout Emitter (Redis pubsub) tracked on issue 634 + multi-replica stage-3 telemetry on issue 639 — both have concrete design comments documenting the trace propagation requirements. Spec-note tracker for upstream events-WG consideration: issue 713 (internal-only marker; push upstream if cross-SDK interop interest surfaces). Doc:experimental/ext/events/README.md§ Tracing across the events bus +docs/SEP_414_OTEL.md§ Events bus trace context relay. - mcp-apps-bridge-trace-relay: SEP-414 P6 ext/ui surface child (issue 660; PR 702, closed). Bidirectional W3C trace context propagation across the iframe ↔ host postMessage boundary so a browser-side trace stitches with the backend tool-call span. Two opt-in surfaces, both off by default — zero overhead on the unconfigured path on either side. TS bridge:
MCPApp.setTraceContextProvider(fn)registers a provider the bridge calls before each outboundrequest()/notify(); result merges intoparams._meta.traceparent/_meta.tracestate. Caller-set_metawins (provider is a fallback — mirrors Go-sidecore.InjectTraceContextIntoParams). Provider throws are caught + logged; request still sends without propagation. Non-object params (positional arrays, scalars) returned untouched. Bundle stays dep-free (no OTel JS dependency). Go AppHost:ui.WithTracerProvider(tp core.TracerProvider)option opts the forward path into instrumentation.handleAppRequestextracts inboundparams._meta.traceparentviacore.ExtractTraceContextFromParams, wraps the forward in anapps.host.forwardspan parented by the iframe’s traceparent. The outboundclient.Callpreserves the iframe’s_meta.traceparenton the wire (SEP-414 P3’s caller-set-wins contract), so the server’s P2 dispatch span stitches as a child. Test gotcha: the “outbound preserves” Go test installsserver.WithTracerProvider(...)on the inner server because P2’s trace middleware is what readsparams._meta.traceparentoff the wire and attaches to the handler’s ctx — without it,TraceContextFromContextreturns zero even when the wire carried a traceparent. Demo wiring inexamples/apps/vanilla/dice.html(per-page-load fresh random traceparent viasetTraceContextProvider+window.__MCPAPP_DEMO_TRACEPARENT__exposed for an on-page TraceID indicator). Host adoption inexamples/host/01-apphost/main.go(passesui.WithTracerProvider(tp)toNewAppHost);examples/host/02-multi-server/main.gocatches up to PR 689’s client-side telemetry sweep (was missed because it doesn’t usecommon.RunServer). End-to-end trace shape:iframe traceparent → apps.host.forward → server tools/call → tool handler. Open spec question (whether the Apps spec should mandate the relay for cross-SDK interop) documented indocs/APPS_DESIGN.md§ Tracing; filed upstream only if working-group interest surfaces. Out of scope (deferred follow-ups in PR 702): AppHost lifecycle spans (apps.host.start/close), response-side relay (server → iframe), browser-side OTel JS demo wiring, ServerRegistryWithTracerProvider. - mcp-otel-examples-adoption: SEP-414 uniform observability surface across mcpkit examples (issue 666; PR 684 server-side + PR 689 client-side).
examples/common/otel/setup.goshipsSetupTelemetry(ctx, opts...) (core.TracerProvider, ShutdownFunc, error)with a four-valueEXPORTERselector:""(default, Noop + no-op shutdown, zero overhead),"stdout"(stdouttrace + sync),"otlp"(otlptracegrpc + sync; TCP-probed endpoint, falls back to Noop with a warning log on dial failure),"auto"(same probe semantics but silent Noop fallback — operator opted into maybe-on-maybe-off). The 500ms TCP probe inprobeOTLPEndpointis load-bearing:otlptracegrpc.Newis lazy and returns non-nil on refused endpoints, so the probe is what actually delivers the “dead stack never breaks make demo” contract. Functional options:WithServiceName(string)/WithExporter(string)/WithOTLPEndpoint(string)/WithStdoutWriter(*os.File)/WithResourceAttr(k,v)/WithInstrumentationName(string). HonorsOTEL_SERVICE_NAME/OTEL_EXPORTER_OTLP_ENDPOINT/OTEL_RESOURCE_ATTRIBUTESas fallbacks under explicit options.SetupClientTelemetry(ctx, opts...)is the same helper pre-set toWithInstrumentationName("github.com/panyam/mcpkit/client")so client-side spans group correctly in OTel backends. Threading point: newcommon.ServerConfig.TracerProviderfield;RunServerwires it viaserver.WithTracerProvider(cfg.TracerProvider)when non-nil. Flag pair:common.RegisterTelemetryFlags(flag.CommandLine)for binaries that callflag.Parse;common.ExporterFromArgs()for walkthroughs (mirrorscommon.ServerURL’sos.Argsscan pattern — noflag.Parseneeded). Service-name convention:<example-name>server,<example-name>-hostclient — Grafana’s service filter distinguishes the two halves of each stitched trace. Adopted across 10 example servers + 9 walkthroughs in this same convention.examples/otel/stdout/is the documented carve-out (defaultExporter="stdout"because the demo’s whole purpose is showing spans). Pattern lives inexamples/CONVENTIONS.md§Telemetry wiring so/example-newand future examples follow it. Issue 667 closed by PR 725 —examples/events/kitchen-sink/adopted the same wiring (SetupTelemetry +events.WithWebhookTracerProvider(tp)+server.WithTracerProvider(tp)); PR 725 also caught up the pre-existingcommon.MCPServerOptionsbaseline gap. Issue 724 closed by PR 730 — fanout-side spans now emit via the newevents.Config.TracerProviderseam (oneevents.fanoutspan per yield carrying aggregate counts; seemcp-events-fanout-spans). - mcp-mrtr-trace-stitching: SEP-414 P6 MRTR multi-round trace stitching (issue 682; PR 732, closed). SEP-2322 MRTR splits one logical operation across N JSON-RPC dispatches (round 1 → InputRequiredResult → client gathers input → round 2 retries with inputResponses); without intervention each round mints its own W3C trace, so operators looking at round N can’t navigate back to round 1. PR 732 adds a vendor-namespaced wire field
_meta.io.modelcontextprotocol/tracelinkcarrying round-1’s W3C traceparent. Rounds 2+ stamp it client-side; server middleware reads it and callscore.SpanFromContext(ctx).AddLink(...)on the round-N dispatch span. Star semantic — every round 2+ links to round 1, NOT the immediately-previous round, so backends show round 1 as the anchor regardless of MRTR loop depth (one click from any round to the originating request). Vendor-namespaced because W3C doesn’t define atracelinkfield; bare names reserved for W3C-defined keys. mcpkit-specific today; candidate for upstream MCP WG standardization for cross-SDK interop (Discord post draft in PR 732 description). Newcoreprimitives:MetaKeyTraceLinkconstant,ExtractTraceLinkFromParams/InjectTraceLinkIntoParamshelpers (mirror existing trace-context helpers; tracestate intentionally NOT carried — link payload is identity-only, not full propagation context), andWithCapturedTraceContext/CapturedTraceContextHolderctx-holder primitive that lets caller code learn what TraceContext its outbound call emitted under (used by CallToolWithInputs to snapshot round-1’s identity ONCE for the star anchor). Client identity capture flow:CallToolWithInputsopens withcore.WithCapturedTraceContext(ctx); client trace middleware writes round-1’s outbound TraceContext into the holder aftertp.StartSpan; CallToolWithInputs snapshots*holderafter round 1’s c.Call returns and stamps it viacore.InjectTraceLinkIntoParamson every subsequent round. API refactor:Client.Callbody extracted to package-privateClient.callImpl(ctx, ...)so MRTR can thread the holder-bearing ctx through the middleware chain — publicClient.Callstays ctx-free (passescontext.Background()) for back-compat. Considered alternatives + rejected: (1) embed traceparent inrequestState— muddies the opaque requestState contract; (2) continue an outer span across rounds — span duration would include user-paced gather-input time, distorting timing semantics. End-to-end correctness proof:ext/otel/mrtr_tracelink_e2e_test.godrives a real CallToolWithInputs against real OTel-SDK-backed server + client and asserts the recorded round-2 server span has an OTel Link whose TraceID matches round-1’s. Unit tests at every layer (core helpers; client middleware capture path; server AddLink emission; client MRTR star semantic with 3 rounds; malformed-tracelink silent drop; no-TP zero-overhead). Walkthrough beat added inexamples/mrtr/walkthrough.godescribing the stitched trace + Grafana navigation hint. Issue 663 (P6 umbrella) child. Doc:docs/SEP_414_OTEL.md§ MRTR multi-round trace stitching +docs/MRTR_TUTORIAL.mdtracing section. - mcp-otel-client-spans: SEP-414 Phase 3 client-side propagation.
client.WithTracerProvider(tp core.TracerProvider)installs an outermostClientMiddlewarethat wraps everyClient.Callin a span; nil andcore.NoopTracerProvider{}both skip the install entirely (zero overhead on the unconfigured path). User-registeredClientMiddleware(auth retry, header injection, custom logging) runs INSIDE the trace span. Outbound span attributes:mcp.methodalways,mcp.tool.nameontools/call,mcp.client.session.idwhen present,mcp.error.code+Span.RecordErroron*RPCError. Outbound_meta.traceparent/_meta.tracestateare stamped onto params viacore.InjectTraceContextIntoParams— explicit caller-set entries win. Inbound server-to-client requests (sampling/createMessage,elicitation/create,roots/list) are wrapped in a span emitted byHandleServerRequestWithContext:params._meta.traceparentis extracted viacore.ExtractTraceContextFromParams, attached to ctx viacore.WithTraceContextso the handler observes the parent throughcore.TraceContextFromContext(ctx), and the wrap span carries the inbound trace context as its parent. Malformed inbound traceparent drops to zero per W3C (“MUST NOT forward”); dispatch still succeeds. Tests use a fakeTracerProvider— nogo.opentelemetry.io/oteldependency inclient/.core.InjectTraceContextIntoParamswas promoted from the server-internal helper so both wires share one implementation. Issue 312 P3. Doc: docs/SEP_414_OTEL.md. - mcp-w3c-baggage-propagation: W3C Baggage end-to-end propagation through mcpkit (issue 739).
core.Baggageis an opaquestringtype sibling tocore.TraceContext.Tracestate— the W3C Baggage list (key=valuepairs) is parsed by adapters, not mcpkit core.MetaKeyBaggage = "baggage"is the bare W3C wire-key constant (noio.modelcontextprotocol/namespace — same rule MetaKeyTraceparent / MetaKeyTracestate follow).ExtractBaggage/InjectBaggageoperate onmap[string]anyenvelopes;ExtractBaggageFromParams/InjectBaggageIntoParamsonjson.RawMessageparams (caller-set wins on inject — mirror ofInjectTraceContextIntoParams). Structural validation: visible-ASCII + spaces only (rejects CRLF / control chars / high-bit bytes — defendsHTTPForwardTransportfrom header-injection); 8KB per-value cap (defends against amplification per SEP-2028 §“Security Implications”).WithBaggage/BaggageFromContextthread the value throughcontext.Context.BaseContext.Baggage()accessor on every typed handler context (ToolContext/PromptContext/ResourceContext/MethodContext). Server trace middleware extracts inbound_meta.baggage(in-band wins over the SEP-2028 HTTPBaggageheader bridge) and stamps outbound_meta.baggageon every server-to-client message; client trace middleware does the symmetric work onClient.Calloutbound params and on inbound server-to-client request dispatch. Streamable transport bridges the HTTPBaggageheader into ctx alongsidetraceparent/tracestate. Ships independently of upstream SEP-2028 — W3C Baggage is a stable standard since 2022. Issue 739. Doc: docs/SEP_414_OTEL.md § Adjacent: W3C Baggage propagation + HTTP forward helper. - mcp-http-forward-transport:
core.HTTPForwardTransport(base http.RoundTripper) http.RoundTripperwraps anhttp.Client’sTransportso a tool handler’s downstream HTTP calls automatically carry the W3C Trace Context (Traceparent/Tracestate) and W3C Baggage (Baggage) headers derived from the request’s Context. Closes the end-to-end observability loop the SEP-414 work opened — without it, a handler that calls a third-party HTTP API drops the trace at the MCP boundary. Readscore.TraceContextFromContext(req.Context())+core.BaggageFromContext(req.Context()); when both are zero, the request is forwarded to base untouched (zero allocation, no clone). When at least one is non-zero, the request is cloned (perhttp.RoundTrippercontract — implementations MUST NOT modify the input request) and the corresponding headers are stamped on the clone. Caller-setTraceparent/Tracestate/Baggageheaders on the request are preserved — the wrap never clobbers explicit intent (same precedence the MCP-wireInjectTraceContextIntoParamsenforces). Nil base falls back tohttp.DefaultTransportso the helper composes in one line. Issue 739; part of the actionable scope from SEP-2028 — ships independently because the W3C standards it propagates are stable. The SEP’s largerheaderGroupsconfigurable forwarding API is deferred until upstream advances. Doc: docs/SEP_414_OTEL.md § Adjacent: W3C Baggage propagation + HTTP forward helper.
Module ¶
github.com/panyam/mcpkit
Location ¶
newstack/mcpkit/main
Stack Dependencies ¶
Core module (github.com/panyam/mcpkit) ¶
- servicekit (github.com/panyam/servicekit) v0.0.26 — SSEConn/SSEHub, ListenAndServeGraceful, StreamableServe, HTTPStatusError (with Header), MaxErrorBodySize, CORS middleware with functional options (CORSAllowMethods, CORSAllowHeaders, CORSExposeHeaders)
Sub-module: ext/auth (github.com/panyam/mcpkit/ext/auth) ¶
- oneauth (github.com/panyam/oneauth) v0.0.71 — JWT/OIDC validation, testutil.TestAuthServer; separate go.mod
Sub-module: experimental/ext/protogen (github.com/panyam/mcpkit/experimental/ext/protogen) ¶
- protokit (github.com/panyam/protokit) v0.0.5 — proto descriptor test utilities, PopulateFieldFromPath (dot-path field binding with type coercion), wire package (proto wire-format decoding helpers for extension extraction)
Integration ¶
Go Module ¶
require github.com/panyam/mcpkit v0.2.9
Basic Server (Streamable HTTP) ¶
import (
"github.com/panyam/mcpkit/core"
"github.com/panyam/mcpkit/server"
)
srv := server.NewServer(
core.ServerInfo{Name: "my-server", Version: "0.1.0"},
server.WithBearerToken("secret"),
server.WithToolTimeout(30 * time.Second),
)
srv.RegisterTool(def, handler)
srv.Run(":8787") // defaults to Streamable HTTP
Client ¶
import (
"github.com/panyam/mcpkit/client"
"github.com/panyam/mcpkit/core"
)
c := client.NewClient(url, core.ClientInfo{Name: "my-client", Version: "1.0"})
c.Connect()
result, _ := c.ToolCall("greet", map[string]any{"name": "world"})
Status ¶
Active
Conventions ¶
- Functional options pattern for server and transport configuration
- SSE infrastructure from servicekit (SSEConn, SSEHub); MCP-specific middleware in mcpkit
- Transport/protocol separation: dispatch layer shared across SSE and Streamable HTTP
- Per-session Dispatchers via newSession() (tool registry shared by reference)
- SSEData union type for SSE wire format (text for URLs, JSON for responses)
- Conformance suite validates spec compliance via baseline.yml