@hackage / mcp-server

Library for building Model Context Protocol (MCP) servers

Changelog

Revision history for mcp-server

0.2.0.0 - 2026-07-31

A major overhaul of the handler API. The headline changes: the handler boundary is no longer stringly typed, and the server is dual-era — it speaks both the legacy initialize-handshake revisions and the stateless 2026-07-28 revision.

Dual-era protocol support (2026-07-28)
  • Requests that declare a protocol revision in their params _meta (io.modelcontextprotocol/protocolVersion) are served statelessly with the modern result envelope: resultType: "complete", the server identity in result _meta, and — on tools/list, prompts/list, resources/list, resources/read and server/discover — the required ttlMs/cacheScope fields (configurable via CacheHints on the transport configs; default: no caching, private). Requests without modern _meta are served byte-identically to before under the revision negotiated by initialize.
  • New server/discover method (mandatory in 2026-07-28, and the backwards-compatibility probe): supported revisions of both eras, handler-gated capabilities, server identity and instructions.
  • Declaring an unsupported revision returns UnsupportedProtocolVersionError (-32022) listing the supported set.
  • A legacy client proposing 2026-07-28 via initialize negotiates down to 2025-11-25: an initializing client is legacy by definition.
  • Handlers can read the declared revision, client info and client capabilities from the ClientContext (clientProtocolVersion/clientInfo/clientCapabilities); the new anonymousContext builds an empty context.
  • HTTP: modern requests get the 2026-07-28 request-metadata validation — the MCP-Protocol-Version header must match the body's declared revision, Mcp-Method must match the body method, and Mcp-Name must match params.name/params.uri for tools/call/resources/read/ prompts/get (with =?base64?…?= sentinel decoding); violations return 400 with HeaderMismatch (-32020). Unknown methods return HTTP 404 and unsupported revisions HTTP 400, so era-probing clients can distinguish them. Legacy requests keep the relaxed pre-2026 rules.
Change notifications and subscriptions/listen
  • New MCP.Server.Notifications: create an McpNotifier with newMcpNotifier, hand its NotificationSource to a transport (stdioNotifications/httpNotifications), and call notifyToolsListChanged/notifyPromptsListChanged/ notifyResourcesListChanged/notifyResourceUpdated when things change.
  • subscriptions/listen (2026-07-28) is served on both transports: the mandatory acknowledgment comes first with the honored filter, every message is tagged with the subscription id, only opted-into types are delivered, and streams end gracefully (closure responses at stdio EOF; closing the SSE stream cancels over HTTP, notifications/cancelled over stdio). HTTP streams send periodic keep-alive comments and X-Accel-Buffering: no.
  • Legacy stdio clients receive spontaneous untagged notifications after initialize. Capabilities are era- and transport-aware: listChanged is advertised only where delivery is possible (stdio legacy push, or modern subscriptions/listen), and subscribe only to modern clients.
  • defaultHttpConfig is now re-exported from MCP.Server.
Resource templates and completions
  • Record constructors of a resource type now derive as resource /templates/ (UserProfile { userId :: Text }resource://user_profile/{userId}): the derived read handler matches template URIs, percent-decodes the path segments, and parses them into the constructor's (typed) fields. deriveResourceTemplates derives the resources/templates/list handler advertising them; the method carries the modern cacheability envelope.
  • New completions handler slot serving completion/complete for prompt arguments and resource-template parameters (CompletionRef, CompletionResult, capped at 100 values per the spec). The completions capability is advertised automatically.
  • McpServerHandlers gains resourceTemplates and completions fields; the new noHandlers value lets you construct handler sets by record update so future fields don't break your code.
Typed tool arguments and results (BREAKING)
  • Tool arguments arrive as full JSON values (Map Text Value). The Template Haskell derivation decodes records recursively and now supports list fields, enumeration fields (all-nullary data types, wired as string enums), and nested record fields in addition to the primitives. Primitive parsing is lenient: native JSON types or their string representations are both accepted (many clients send numbers/booleans as strings). Prompt arguments remain string-valued per the MCP specification.
  • inputSchema is generated as a real JSON Schema (Schema/SchemaType ADT with enum, items and nested objects), replacing the flat InputSchemaDefinition* types that silently typed every non-primitive field as a string.
  • Tool handlers produce a ToolResult: multiple content blocks, structuredContent, _meta, and isError. Tool execution failures should be reported via isError (see toolError) so the model can see them — per spec — instead of surfacing as JSON-RPC protocol errors. The ToToolResult class keeps simple handlers simple: returning Content or Text still works unchanged.
  • Prompt handlers produce a PromptResult (optional description plus a multi-message conversation with user/assistant roles) via the analogous ToPromptResult class.
  • Content gains audio and resource_link variants; embedded resources now carry their full contents as the spec requires.
  • ToolDefinition gains outputSchema; tools/call responses carry structuredContent.
  • Handler types are fixed to IO — the monad parameter was unusable through the public API (both transports required IO).
Transport fixes
  • stdio: a blank line on stdin no longer terminates the server, EOF shuts down cleanly instead of crashing, and malformed input is answered with proper JSON-RPC error responses (-32700/-32600, id: null).
  • stdio: raw request bodies are no longer logged to stderr by default (tool arguments may carry sensitive data) — only message summaries. runMcpServerStdioWithConfig with stdioVerbose = True restores full body logging.
  • JSON-RPC: messages are classified by shape (method/id presence) instead of parse-fallthrough, so a request with a malformed id is answered with an error rather than silently dropped as a notification. Request ids must be integral.
  • HTTP: new httpAllowedOrigins policy on HttpConfig (Origin validation / DNS-rebinding protection, a spec MUST); accepted notifications return 202 with no body; malformed bodies get JSON-RPC error responses; the Access-Control-Allow-Origin header is set consistently on every response and echoes the validated origin (with Vary: Origin) when a policy is configured.
  • HTTP (BREAKING): the non-spec GET "discovery" endpoint is removed — the MCP endpoint now answers GET with 405 Method Not Allowed, matching the spec (no revision defines a GET discovery response, and 2026-07-28 requires 405 here).
  • Integer tool arguments bound the scientific-notation exponent (1024, the same bound aeson uses) so a tiny payload like 1e1000000000 cannot force allocation of a gigabyte-sized Integer.

0.1.0.21 - 2026-07-31

  • BREAKING: every handler (prompt/resource/tool; list and get/read/call) now receives a ClientContext as its first argument, so a server can behave differently depending on who is calling. On stdio the context is anonymous; on HTTP it carries the request's bearer token and the principal returned by the authorization callback.
  • BREAKING: HttpConfig gains an httpAuthorize field — an optional callback that validates the presented Authorization: Bearer token and returns an application-defined principal (Nothing rejects with 401). As it now holds a function, HttpConfig no longer derives Show/Eq.
  • HTTP transport: accept requests without an MCP-Protocol-Version header (the spec says to assume 2025-03-26), exempt initialize from the header check (it negotiates its version in the body), and keep rejecting a present but unsupported header with 400. Previously every request without the header was rejected, locking out pre-2025-06-18 clients.
  • initialize now advertises only the capabilities that actually have handlers, so strict clients no longer drop the server when e.g. prompts/list answers "not supported".
  • CORS: preflight OPTIONS requests are exempt from authorization (browsers send no credentials on preflight) and Authorization is included in Access-Control-Allow-Headers.
  • http-simple-example is now built with -threaded, which Warp requires; previously every request crashed with a TimerManager error.

0.1.0.20 - 2026-07-31

  • Fix protocol version negotiation: echo back any compatible revision the client proposes (2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25) instead of always responding with the server's own version. Fixes clients (e.g. Claude Code) that disconnect when they receive a different version than requested.
  • Apply the same negotiation to the HTTP transport's MCP-Protocol-Version header check, which previously rejected anything other than 2025-06-18.
  • Default/fallback advertised version bumped to 2025-11-25.

0.1.0.19 - ???

  • Improve handler code generated by TemplateHaskell functions in MCP.Server.Derive:
    • Don't repeat Map.fromList for each argument in map lookup
    • Properly handle argument parse errors (Return InvalidParams error instead of crashing mcp server with error)

0.1.0.18 - 2026-02-09

  • Switch default-language to GHC2021 to support broader range of GHC versions (9.6 - 9.12)

0.1.0.17 -- 2026-01-28

  • Implement protocol version negotiation according to spec
  • Remove unused dependencies, fix GHC warnings
  • Add tested-with and haskell-ci generated GitHub Actions config

0.1.0.16 -- 2026-01-19

  • Bump template-haskell dependency upper bound

0.1.0.15 -- 2025-08-13

  • Update to MCP spec 2025-06-18

0.1.0.14 -- 2025-06-26

  • Bump version bounds before adding to Stackage
  • Remove support for JSON-RPC batching

0.1.0.13 -- 2025-06-17

  • Better handling of UTF-8 in logs

0.1.0.12 -- 2025-06-17

  • Fix unicode handling
  • Refactor transports to remove unneeded functions
  • Add unicode handling tests

0.1.0.11 -- 2025-06-17

  • Refactor transports and add HTTP streaming support
  • Add MCP.Server.Handlers module
  • Add MCP.Server.Transport.Http and MCP.Server.Transport.Stdio modules

0.1.0.10 -- 2025-06-13

  • Fix resources handling

0.1.0.9 -- 2025-06-13

  • Bump versions of dependencies
  • Port tests to hspec

0.1.0.8 -- 2025-06-12

  • Support for nestable data types

0.1.0.7 -- 2025-06-09

  • Documentation updates

0.1.0.6 -- 2025-06-09

  • Remove pagination support

0.1.0.5 -- 2025-06-09

  • Add descriptions to constructors and fields

0.1.0.4 -- 2025-06-09

  • Clean up build configuration

0.1.0.3 -- 2025-06-09

  • Refactor example modules
  • Fix JSON to Haskell type conversion

0.1.0.0 -- 2025-06-05

  • First version. Released on an unsuspecting world.