Capabilities

What mcpkit ships today — feature inventory tracked alongside the spec.

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 typed NextCursor + RelatedTask fields. Custom MarshalJSON / UnmarshalJSON spread the map at the top level of the _meta object; typed fields win on key collision. Mirrors the ErrorData.Extra pattern in core/jsonrpc.go. Unlocks extension fixtures that need vendor keys on tool results without per-fixture library forks — pdf-server’s display_pdf uses it to emit _meta.interactEnabled / _meta.viewUUID matching 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 content field 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 upstream modelcontextprotocol/ext-apps TypeScript examples byte-for-byte at the protocol surface (tool name + title + input/output schema + _meta.ui shape + verbatim dist/mcp-app.html), so upstream’s basic-host Playwright suite runs unmodified against the Go binary. scripts/apps-playwright-test.sh orchestrates basic-host + the fixture + a per-fixture __snapshots__/<key>.png baseline; UPDATE_SNAPSHOTS=1 regenerates the committed PNG. Two modes share one wrapper: native (host OS, fast loads app UI iteration; the visual screenshot test is Docker-pinned and will fail on non-Linux hosts intentionally) and Docker (make test-apps-playwright-docker, runs everything inside mcr.microsoft.com/playwright:v1.57.0-noble — same image upstream uses for test:e2e:docker). DOCKER mode runs a strict tools/list parity 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 the basic-server-preact/react/solid/svelte/vue framework variants; PR 543 added quickstart + transcript-server; PR 545 added sheet-music-server via the new TypedAppToolConfig.InputSchemaOverride escape hatch (closes 542); PR 549 added integration-server (5/5 including the 3 interaction tests), system-monitor-server, cohort-heatmap-server, customer-segmentation-server, and debug-server (3 fresh schema-gen gaps surfaced and tracked in issue 548); PR 552 added map-server, threejs-server, shadertoy-server, and wiki-explorer-server (introduces OutputSchemaOverride for nullable types like upstream’s z.string().nullable()); PR 553 added budget-allocator-server and scenario-modeler-server (rich nested data shapes — scenario-modeler uses OutputSchemaOverride for its nullable breakEvenMonth); PR 555 added pdf-server against upstream’s default-without---enable-interact 4-tool surface; the follow-up PR expanded it to the 9-tool --enable-interact surface 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 marks lazy-auth-server and video-resource-server as SKIP (not in upstream’s servers.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 from Name. Per MCP spec hosts SHOULD prefer Title for user-facing surfaces (dropdowns, button labels); Name remains the machine identifier passed to tools/call. Exposed on ext/ui.AppToolConfig.Title and ext/ui.TypedAppToolConfig.Title. (PR 538)
  • mcp-apps-tool-execution: ext/ui.AppToolConfig.Execution and ext/ui.TypedAppToolConfig.Execution — pass-through to core.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: RegisterTypedAppTool now propagates OutputSchema derived by core.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"]} / anyOf form which Go reflection won’t produce), interface{} / any fields invopop reflects to schemas strict MCP-SDK clients reject. Wiki-explorer compat fixture uses this for its nullable error field. (PR 552)
  • mcp-tool-schema-patch: core.WithInputSchemaPatch(fn) / core.WithOutputSchemaPatch(fn) + matching ext/ui.TypedAppToolConfig.{Input,Output}SchemaPatch func fields. Reflection runs first; the patch function receives a *core.SchemaBuilder over the live schema map and edits in place via Prop(name) / Require(...) / Raw(). *core.PropertyBuilder covers Type/Desc/Default/Min/Max/MinLength/MaxLength/Enum/Pattern/Required + Replace(rawSchema) for the irreducible cases (nullable, anyOf, record-of-union). Precedence: Override wins over Patch when 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>": true for interface{} / any properties — 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-true schemas inside properties.* / items / allOf|anyOf|oneOf with {}. Mirrors zod’s z.unknown() emit. Removes the per-fixture InputSchemaOverride workaround (debug-server’s Payload any field reflects cleanly now). Issue 548 Gap 2.
  • mcp-apps-app-only-tool-helper: ext/ui.AppToolConfig.ResourceURI is now optional. Empty ResourceURI skips the companion RegisterResource call entirely; the tool still lands with its UIMetadata (Visibility, CSP, Permissions, …) and no _meta.ui.resourceUri. Removes the core.TypedTool + Title-mutation + manual srv.RegisterTool pattern for app-only tools without their own UI resource. Issue 548 Gap 3.
  • mcp-apps-meta-dual-resourceuri: core.ToolMeta.MarshalJSON emits _meta["ui/resourceUri"] alongside the nested _meta.ui.resourceUri when set; UnmarshalJSON accepts either form (nested wins on disagreement). Mirrors upstream @modelcontextprotocol/ext-apps/server’s registerAppTool RESOURCE_URI_META_KEY behavior 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 $schema on every inputSchema / 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/mcpconformance 2026-06-15 spec batch. Wire-breaking. Inner subscription field renamed paramsarguments (spec commit 082166f0) on all four events methods (subscribe / poll / unsubscribe / stream); Go field ParamsArguments swept across RegisterParams / WebhookTarget / SubscribeOpts / Go SDK options / GORM column. ttlMs tristate request field (absent / int / null) decoded via json.RawMessage (preserves the tristate Go’s *int64 collapses) → WebhookRegistry.NegotiateExpiry policy oracle (clamp envelope [MinWebhookTTL, MaxWebhookTTL] + WithUnsafeWebhookTTLBypass honored + WithAllowInfiniteWebhookTTL gates null acceptance); refreshBefore response is *time.Time everywhere (always present, RFC3339 or JSON null per spec commit 99f3589c); SDK Subscription.RefreshBefore() returns *time.Time (nil = no-expiry; refresh loop drops to 1h health-check cadence). DeliveryStatus.Throttled bool + RetryAfterMs *int64 projector-only (spec commit 21be9c31; adopters wire from their own throttle state). 410 Gone is abandon-without-failure: retry-loop case http.StatusGone: returns without recordDeliveryFailure so Active=true/LastError=None preserved (spec commit 905ade36). DefaultWebhookAckTimeout = 5s named constant pinned (spec commit b506e347). PRs 778 + 779 closed issues #762 / #763 / #765-partial / #767 / #760; integration test TestNoExpirySurvival_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 accepting ttlMs: null requests + returning refreshBefore: null per spec PR1 commit 99f3589c §“Subscription TTL”. WebhookTarget.ExpiresAt flipped to *time.Time with compiler-enforced nil guards across prune loop / Targets() / DeliverToTarget (nil = no-expiry sentinel; finite-TTL targets exempted from no-expiry GC). warnIfInfiniteTTLWithDefaultStore fires a stark construction-time warning when the opt-in is set without WithWebhookStore(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 new DeliveryStatus.FailingContinuouslySince *time.Time anchor (never reset by sliding suspend window, only by successful delivery) — recordDeliveryFailure checks target.ExpiresAt == nil && now - *FailingContinuouslySince > r.noExpiryFailureGCWindow and fires PostTerminated with distinguishable ControlError message (“no-expiry subscription dropped after sustained delivery failure”). WithNoExpiryFailureGCWindow(d) tunes the threshold (default DefaultNoExpiryFailureGCWindow = 72h). Suspend transition is suppressed in the same call when the GC drop fires (the drop supersedes the same-call pause). GORM column status_failing_continuously_since nullable round-trip. Wire-projected as RFC3339 diagnostic so subscribers can observe the GC anchor on subscribe-refresh. FailingContinuouslySince is deliberately distinct from FailedSince: the suspend state machine resets FailedSince after suspendWindow quiet 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 via make webhook knobs TTL_MS=null + EXIT_AFTER=N; helper targets make 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 nested task wrapper), 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/mcpconformance fork (branch feat/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) under src/scenarios/server/mrtr/ for MRTR↔Tasks composition coverage that complements SEP-2322’s PR 188. Brand-neutral, language-agnostic runner — fixture wired via TASKS_SERVER_URL / TASKS_SERVER_CMD (and MRTR_SERVER_URL / MRTR_SERVER_CMD); sh -c spawn, TCP-poll readiness, no log-line scanning. make testconf-tasks-v2 / testconf-mrtr build the example Go fixtures + invoke the fork’s vitest run + chain a local sentinel run for future mcpkit-stricter scenarios. Local conformance/tasks-v2/ and conformance/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 tests TestV2_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 row server-stateless flipped 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/mcpconformance fork (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-existing callToolForStateless gap (does not decode inputResponses / requestState). Pin MCP_WIRE_MODES=legacy to scope to what mcpkit currently supports end-to-end. Local sentinel in conformance/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/mcpconformance pending branch under src/scenarios/server/list-ttl/, exercised across 3 server processes (positive / zero / unset) via make 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/mcpconformance pending branch under src/scenarios/server/file-inputs/, runnable via make 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) and mcp.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-encoded name= parameter that matches core.EncodeDataURI byte-for-byte. Sentinel errors (MCPFileSelectionCanceled, MCPFileTooLarge, MCPFileTypeNotAccepted) align with server-side -32602 reasons. 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-2631 FileValue.inlineBase64 payload 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.Attribute interfaces with core.NoopTracerProvider default (zero-alloc). core.TraceContext{Traceparent, Tracestate} + IsZero(); ExtractTraceContext / InjectTraceContext operate on map[string]any representing MCP _meta. W3C version-00 structural validation — malformed traceparent drops tracestate too (W3C “MUST NOT forward”). MetaKeyTraceparent / MetaKeyTracestate constants are bare W3C names (NOT under io.modelcontextprotocol/ — that namespace is reserved for MCP-defined fields). WithTraceContext / TraceContextFromContext thread the value through context.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 and core.NoopTracerProvider{} both skip the install entirely (zero overhead on the unconfigured path). Inbound trace context resolution: params._meta.traceparent wins over the core.WithTraceContext value on ctx (out-of-band, populated by the SEP-2028 HTTP traceparent / tracestate header bridge that fires once at streamable_transport.handlePost and is inherited by every downstream dispatch path). Span attributes: mcp.method always, mcp.tool.name on tools/call, mcp.session.id when present, mcp.error.code + Span.RecordError on JSON-RPC errors, mcp.tool.is_error="true" on ToolResult.IsError. Outbound _meta.traceparent / _meta.tracestate injected on every server-to-client message (notifications + sampling/elicitation/roots) via internal wraps of sc.notify / sc.request using new core.WrapSessionNotifyFunc / core.WrapSessionRequestFunc helpers (mirror the existing ApplySessionNotifyFilter pattern); the wrap sits OUTSIDE any user-registered NotifyInterceptor / RequestInterceptor, so user interceptors observe the augmented wire form. Explicit handler-set _meta.traceparent on outbound messages wins over inject. New core.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 (separate go.mod, mirrors ext/auth/ / ext/tasks/ / ext/ui/). otel.NewProvider(otelTP trace.TracerProvider, opts ...Option) *Provider returns a core.TracerProvider implementation backed by an OTel TracerProvider. Parses inbound core.TraceContext into an OTel SpanContext (Remote=true) and installs as the new span’s parent via trace.ContextWithSpanContext. After StartSpan, formats the new span’s SpanContext into a W3C version-00 traceparent and re-attaches via core.WithTraceContext so the SEP-414 P2 outbound _meta injection stamps the child span’s traceparent on every server-to-client message. core.Attributeattribute.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 sets codes.Error status. 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 with sdktrace.NewTracerProvider(WithSyncer(InMemoryExporter)) — no fake. Panics fast on nil otelTP (silent no-op would lose spans without surfacing the misconfig). Runnable example: examples/otel/stdout/ (separate go.mod, uses stdouttrace exporter — no collector infra required). make test-otel + make test-otel-example gates. 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.Span returns the currently-active mcpkit Span (or a no-op Span when none is attached) — sibling of TraceContextFromContext. Symmetric core.WithActiveSpan(ctx, span) Context is what TracerProvider adapter authors call after StartSpan to publish the span into ctx; nil span is a no-op. Both the in-tree NoopTracerProvider and the ext/otel adapter 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 adding mcp.auth.* attributes, tool handlers adding result-shape hints) work without nil-checking and without an ext/otel import. 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/auth jwks/introspection/oauth attributes). Tests drive the contract through both Noop and ext/otel SDK 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 spec Link definition (upstream trace identity + per-link attributes that observability backends render in their link UI as link.kind=originated-from / sibling-task / etc.); core.LinkFromTraceContext(tc) Link convenience for the no-attribute simple case; widened core.Span interface gains AddLink(Link) for mid-flight link discovery (matches OTel Go SDK v1.30+ Span.AddLink idiom); new core.LinkedTracerProvider interface extends TracerProvider with StartSpanLinked(ctx, name, []Link, attrs...) for the option-at-start common case; package-level core.StartSpanLinked(tp, ...) helper type-asserts and falls back to plain StartSpan when the provider doesn’t implement the wider interface (links silently dropped). Capability-widening pattern (sibling interface + helper) kept the base TracerProvider interface unchanged so non-tracing test fakes didn’t churn; the Span interface widening required one-line AddLink no-ops on noopSpan + the two server/client test-file fakeSpan structs. ext/otel.Provider implements both LinkedTracerProvider and Span.AddLink — links flow through to oteltrace.Link{SpanContext, Attributes} via the existing traceContextToSpanContext helper; invalid core.Link entries (zero or W3C-malformed TraceContext) silently dropped at both entry points; AddLink after End is a no-op (CAS-guarded wrapper). Unblocks issue 659 (ext/tasks task.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, LinkedTracerProvider routing, LinkFromTraceContext helper, OTel link emission, per-link attribute flow-through, invalid-link filtering, mid-flight AddLink, and AddLink after End. 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/tasks consumer work (issue 659; PR 719). core.WithNewRootSpan(ctx) Context marks ctx so the next TracerProvider.StartSpan / StartSpanLinked call produces a span with NO parent — even when ctx carries an inherited parent (a core.TraceContext attached upstream, or the adapter’s own internal span context, e.g. the OTel trace.SpanContext installed by a previous StartSpan). Adapter-facing companion core.IsNewRootSpanRequested(ctx) bool is 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.WithoutCancel inside core.DetachForBackground would silently re-parent under the (already-ended) create span. ext/otel.Provider.startSpanInternal honors the marker by stripping any OTel parent SpanContext on ctx before tracer.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 with core.StartSpanLinked + a Link back to the originating span so the lifecycle stays navigable. Tests in ext/otel/provider_test.go pin 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.TracerProvider opts the validator into instrumentation: an auth.jwks_lookup sub-span (with mcp.auth.jwks.kid attribute; records error on failure) wraps each JWKSKeyStore.GetKeyByKid call, capturing both cache-hit and cache-miss latency. Active dispatch span gets mcp.auth.method = "jwt" (stamped early — failure paths included), plus mcp.auth.subject / issuer / scopes / cache_hit set after claims extraction, via core.SpanFromContext. ext/auth depends on core.TracerProvider only for its own spans (no compile-time dep on ext/otel). Nil and Noop both produce zero spans / attributes / allocation. The jwksKeyFunc method became jwksKeyFuncCtx(ctx) closure because jwt-go’s Keyfunc signature has no ctx param — needed for the span boundary. Tests use a local fakeTracerProvider mirroring server/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 via mcp-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.Provider and 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 via keys.WithTracerProvider on the JWKSKeyStore so oneauth’s internal JWKS work (refresh, key_lookup, signature_verify) emits spans on the same OTel pipeline as ext/auth’s auth.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 in examples/auth/common/setup.go via WithMCPTracerProvider / WithOneauthTracerProvider variadic options on Env.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’s test-auth job because tests/e2e fails MVS resolution with stale go.mods. Run make tidy-all after 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.TracerProvider opts the v2 task runtime into span-link instrumentation of the async lifecycle. A tools/call that creates a task spawns a background goroutine whose work outlives the create span — parent-child doesn’t fit, so the runtime emits task.execute as a NEW root trace (via core.WithNewRootSpan + core.StartSpanLinked) carrying a Link back to the originating tools/call create span. Attributes: mcp.task.id stamped at StartSpan, mcp.task.status stamped at End from the final stored status (completed / failed / cancelled / input_required). RecordError fires on protocol-level failures (mwErr, resp.Error, unexpected result shape, panic recover); handler-returned errors map to completed with IsError=true per SEP-2663 semantics — they DO NOT call RecordError. End is exactly once (defer guards). Every tasks/get / tasks/update / tasks/cancel dispatch span additionally calls core.SpanFromContext(ctx).AddLink(core.LinkFromTraceContext(rt.getOrigin(taskID))) so backends can pivot from any poll into the whole lifecycle. The originTC runtime map stashing the create-span identity per taskID outlives the activeTask entry (so terminal-state polls still get the link) — matches the existing taskErrors / creatorToolForTask lifecycle. Sync-as-completed path (wrapSyncAsCompletedTask) deliberately emits NO task.execute span — the work lives inside the create span and a zero-duration wrapper would be noise; only the GoAsync path gets task.execute. ext/tasks depends on core only — no compile-time dep on ext/otel (testify added as a test-only dep). Tests use a recording fake core.TracerProvider + core.LinkedTracerProvider to prove ext/tasks calls the contract correctly without dragging ext/otel into its module graph; end-to-end materialization is proven by ext/otel/provider_test.go’s coverage of WithNewRootSpan scrub + StartSpanLinked + AddLink paths against a real OTel SDK exporter. 8 new TestTrace_* 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 or core.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.TracerProvider opts every Source implementing TracerProviderInstaller (notably YieldingSource) into per-yield fanout span emission. One events.fanout span per emitted event (NOT per subscriber) wraps the per-subscriber fanout loop inside YieldingSource.yield, carrying aggregate counts on attributes: events.subscribers.total / events.subscribers.delivered / events.subscribers.dropped_by_match / events.transforms.applied, plus mcp.event.name + mcp.event.id for 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 — the tp.(core.NoopTracerProvider) type-assertion avoids the StartSpan call entirely on the unconfigured path. Internal API change: YieldingSource.deliverEventToSlot widened to return (matched, transformed bool) so the parent yield can stamp accurate counts. Public TracerProviderInstaller interface (SetTracerProvider(tp core.TracerProvider)) lets TypedSource authors and future sources opt in identically; the install loop in events.Register type-asserts each Source and silently skips ones that don’t implement it. Per-subscriber events.match / events.transform spans (option (a) from the issue body) deliberately NOT shipped — would scale linearly with subscribers × events and need sampling design; the aggregate counts on events.fanout cover 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 has events.webhook.deliver (PR 714); poll-side is covered by the events/poll dispatch span. Tests use a fakeTP recording machinery (reused from webhook_span_test.go) covering: EmitsSpanWithAttributes, DroppedByMatch, TransformsApplied, NoSubscribers_SkipsSpan, ParentedByYieldCtx, EventAttributesPresent, NoopTracerProvider_NoSpansEmitted, RegisterInstallsTracerProvider. kitchen-sink adopts via events.Config{... TracerProvider: tp} (the same tp threaded by commonotel.SetupTelemetry per mcp-otel-examples-adoption). Issue 663 (P6 umbrella) child — completes the events surface alongside mcp-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), HTTP traceparent header (cross-process wire). PR 712 changed yield(data) closure / SetEmitHook(func(Event)) / SetMetaFunc(func(Data)) / WebhookRegistry.Deliver(event) / package-level Emit / EmitToWebhooks signatures to take ctx — backward-incompatible by design (user OK’d; experimental module). YieldingSource.yield stamps event.Meta.traceparent from core.TraceContextFromContext(ctx) using core.InjectTraceContext; metaFunc-set traceparent wins (uniform with core.InjectTraceContextIntoParams + Apps Bridge relay). WebhookRegistry.deliver extracts via new traceContextForDelivery(ctx, event) helper (ctx → event.Meta fallback) and stamps W3C traceparent + optional tracestate HTTP headers on every retry attempt. HTTPSource.serveInject extracts inbound traceparent / tracestate HTTP headers, builds core.TraceContext, attaches via core.WithTraceContext(r.Context(), tc), calls s.yield(ctx, data) — closes the cross-process round-trip. PR 714 added WebhookRegistry.WithWebhookTracerProvider(tp core.TracerProvider) option emitting events.webhook.deliver span around each retry loop with attributes webhook.target.id, webhook.url, mcp.event.name, http.method, http.response.status_code, webhook.retry.attempts; RecordError on retries-exhausted with the categorical failure bucket (connection_refused / timeout / tls_error / http_4xx / http_5xx). Nil/Noop = zero overhead unconfigured. PR 714 also widened Server.Broadcast(ctx, method, params) so events.Emit can thread ctx through the SSE push path; PR 722 (issue 715) closed the loose end — Server.Broadcast now reads core.TraceContextFromContext(ctx) and injects _meta.traceparent / _meta.tracestate onto notification params (via core.InjectTraceContextIntoParams) before the fan-out loop, so SSE-pushed notifications stitch into the originating yield’s trace. Caller-set _meta.traceparent wins (delegates to the helper’s contract). Per-transport broadcast callbacks widened to func(context.Context, string, any) as a forward-compat seam (currently unused inside; the per-request trace_middleware.WrapSessionNotifyFunc targets sc.notify on a per-request SessionCtx, 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.deliver span 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_ERROR on the connection-refused failure path, duration matched 0.5s+1s+2s backoff exactly. Adopter sweep (PR 714): examples/events/discord + examples/events/telegram updated to new ctx-aware closures. whole-enchilada event-server wires events.WithWebhookTracerProvider(tp) against the existing commonotel.SetupTelemetry TP. 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 outbound request() / notify(); result merges into params._meta.traceparent / _meta.tracestate. Caller-set _meta wins (provider is a fallback — mirrors Go-side core.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. handleAppRequest extracts inbound params._meta.traceparent via core.ExtractTraceContextFromParams, wraps the forward in an apps.host.forward span parented by the iframe’s traceparent. The outbound client.Call preserves the iframe’s _meta.traceparent on 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 installs server.WithTracerProvider(...) on the inner server because P2’s trace middleware is what reads params._meta.traceparent off the wire and attaches to the handler’s ctx — without it, TraceContextFromContext returns zero even when the wire carried a traceparent. Demo wiring in examples/apps/vanilla/dice.html (per-page-load fresh random traceparent via setTraceContextProvider + window.__MCPAPP_DEMO_TRACEPARENT__ exposed for an on-page TraceID indicator). Host adoption in examples/host/01-apphost/main.go (passes ui.WithTracerProvider(tp) to NewAppHost); examples/host/02-multi-server/main.go catches up to PR 689’s client-side telemetry sweep (was missed because it doesn’t use common.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 in docs/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, ServerRegistry WithTracerProvider.
  • 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.go ships SetupTelemetry(ctx, opts...) (core.TracerProvider, ShutdownFunc, error) with a four-value EXPORTER selector: "" (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 in probeOTLPEndpoint is load-bearing: otlptracegrpc.New is 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). Honors OTEL_SERVICE_NAME / OTEL_EXPORTER_OTLP_ENDPOINT / OTEL_RESOURCE_ATTRIBUTES as fallbacks under explicit options. SetupClientTelemetry(ctx, opts...) is the same helper pre-set to WithInstrumentationName("github.com/panyam/mcpkit/client") so client-side spans group correctly in OTel backends. Threading point: new common.ServerConfig.TracerProvider field; RunServer wires it via server.WithTracerProvider(cfg.TracerProvider) when non-nil. Flag pair: common.RegisterTelemetryFlags(flag.CommandLine) for binaries that call flag.Parse; common.ExporterFromArgs() for walkthroughs (mirrors common.ServerURL’s os.Args scan pattern — no flag.Parse needed). Service-name convention: <example-name> server, <example-name>-host client — 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 in examples/CONVENTIONS.md §Telemetry wiring so /example-new and future examples follow it. Issue 667 closed by PR 725examples/events/kitchen-sink/ adopted the same wiring (SetupTelemetry + events.WithWebhookTracerProvider(tp) + server.WithTracerProvider(tp)); PR 725 also caught up the pre-existing common.MCPServerOptions baseline gap. Issue 724 closed by PR 730 — fanout-side spans now emit via the new events.Config.TracerProvider seam (one events.fanout span per yield carrying aggregate counts; see mcp-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/tracelink carrying round-1’s W3C traceparent. Rounds 2+ stamp it client-side; server middleware reads it and calls core.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 a tracelink field; 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). New core primitives: MetaKeyTraceLink constant, ExtractTraceLinkFromParams / InjectTraceLinkIntoParams helpers (mirror existing trace-context helpers; tracestate intentionally NOT carried — link payload is identity-only, not full propagation context), and WithCapturedTraceContext / CapturedTraceContextHolder ctx-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: CallToolWithInputs opens with core.WithCapturedTraceContext(ctx); client trace middleware writes round-1’s outbound TraceContext into the holder after tp.StartSpan; CallToolWithInputs snapshots *holder after round 1’s c.Call returns and stamps it via core.InjectTraceLinkIntoParams on every subsequent round. API refactor: Client.Call body extracted to package-private Client.callImpl(ctx, ...) so MRTR can thread the holder-bearing ctx through the middleware chain — public Client.Call stays ctx-free (passes context.Background()) for back-compat. Considered alternatives + rejected: (1) embed traceparent in requestState — 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.go drives 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 in examples/mrtr/walkthrough.go describing 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.md tracing section.
  • mcp-otel-client-spans: SEP-414 Phase 3 client-side propagation. client.WithTracerProvider(tp core.TracerProvider) installs an outermost ClientMiddleware that wraps every Client.Call in a span; nil and core.NoopTracerProvider{} both skip the install entirely (zero overhead on the unconfigured path). User-registered ClientMiddleware (auth retry, header injection, custom logging) runs INSIDE the trace span. Outbound span attributes: mcp.method always, mcp.tool.name on tools/call, mcp.client.session.id when present, mcp.error.code + Span.RecordError on *RPCError. Outbound _meta.traceparent / _meta.tracestate are stamped onto params via core.InjectTraceContextIntoParams — explicit caller-set entries win. Inbound server-to-client requests (sampling/createMessage, elicitation/create, roots/list) are wrapped in a span emitted by HandleServerRequestWithContext: params._meta.traceparent is extracted via core.ExtractTraceContextFromParams, attached to ctx via core.WithTraceContext so the handler observes the parent through core.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 fake TracerProvider — no go.opentelemetry.io/otel dependency in client/. core.InjectTraceContextIntoParams was 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.Baggage is an opaque string type sibling to core.TraceContext.Tracestate — the W3C Baggage list (key=value pairs) is parsed by adapters, not mcpkit core. MetaKeyBaggage = "baggage" is the bare W3C wire-key constant (no io.modelcontextprotocol/ namespace — same rule MetaKeyTraceparent / MetaKeyTracestate follow). ExtractBaggage / InjectBaggage operate on map[string]any envelopes; ExtractBaggageFromParams / InjectBaggageIntoParams on json.RawMessage params (caller-set wins on inject — mirror of InjectTraceContextIntoParams). Structural validation: visible-ASCII + spaces only (rejects CRLF / control chars / high-bit bytes — defends HTTPForwardTransport from header-injection); 8KB per-value cap (defends against amplification per SEP-2028 §“Security Implications”). WithBaggage / BaggageFromContext thread the value through context.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 HTTP Baggage header bridge) and stamps outbound _meta.baggage on every server-to-client message; client trace middleware does the symmetric work on Client.Call outbound params and on inbound server-to-client request dispatch. Streamable transport bridges the HTTP Baggage header into ctx alongside traceparent / 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.RoundTripper wraps an http.Client’s Transport so 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. Reads core.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 (per http.RoundTripper contract — implementations MUST NOT modify the input request) and the corresponding headers are stamped on the clone. Caller-set Traceparent / Tracestate / Baggage headers on the request are preserved — the wrap never clobbers explicit intent (same precedence the MCP-wire InjectTraceContextIntoParams enforces). Nil base falls back to http.DefaultTransport so 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 larger headerGroups configurable 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