All notable changes to this project are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
This release makes the server enforce the MCP specification by default. Several behaviors that were previously absent or lenient are now always on; see Changed for the breaking details and how to adapt.
Added
- Session lifecycle limits:
:max_sessions(reject new sessions with503past a cap — enforced atomically, before the server'sinit/1runs, so a rejected session pays no init cost and the cap holds under concurrent initializes),:session_idle_timeout(terminate after inactivity; a session serving a request is not reaped) and:session_max_lifetime(terminate a fixed time after creation). All default tonil(unlimited) and are validated as positive integers at startup. Session processes are nowrestart: :temporaryso an ended session is never resurrected under its old id. - Declarative tool scopes:
tool "name", scopes: ["files:write"], ...enforces the scopes againstctx.authbefore the handler runs, failing closed when the request carries no authorization. :expose_internal_errorstransport option (defaultfalse). Unexpected exceptions and malformed handler returns are now logged in full but return a generic message to the client; enable the option to surface the detail in development. DeliberateUrchin.Errorvalues and{:error, message}returns are never redacted — theirmessage/datareach the client unchanged.- Capability guards:
Urchin.Context.create_message/3,elicit/3andlist_roots/2return an error without contacting the client when it did not advertise the matchingsampling/elicitation/rootscapability. 415 Unsupported Media Typefor POST requests whoseContent-Typeis notapplication/json.SECURITY.mdwith a threat model, deployment checklist and vulnerability reporting.:sse_buffer_limittransport option (defaultnil, preserving the session's internal default of100) forwarding the per-session GET-stream replay buffer size to the session; previously only configurable onUrchin.Sessiondirectly.
Changed
The following are now enforced by default, with no opt-out, for MCP spec compliance. They are
breaking relative to 0.2.0.
- A DSL tool's
tools/callarguments are validated against itsinput_schemabefore the handler runs; a mismatch is returned as aCallToolResultwithisError: trueso the model can self-correct. A tool that declares noinput_schemanow defaults to an object that accepts no properties (additionalProperties: false), so unexpected arguments are rejected — declare an explicitinput_schemato accept arbitrary fields. A non-objectargumentsvalue is a malformed request and remains a JSON-RPCinvalid_paramserror. Servers that implementcall_tool/3by hand validate their own arguments.Urchin.Schemaimplements the supported (minimal) JSON Schema subset. - Operation requests received before the client sends
notifications/initializedare rejected withinvalid_request; onlypingis allowed before initialization (the lifecycle's pings-and-logging exception is for the server's own requests, not the client'slogging/setLevel). Clients must complete the lifecycle handshake before issuing other requests. - A
tools/callhandler's{:error, message}(string) is returned as aCallToolResultwithisError: trueso the model can self-correct. A protocol error returned as{:error, %Urchin.Error{}}is always a JSON-RPC error. (Previously a string handler error became a JSON-RPC internal error.) - Duplicate literal tool names within a server are rejected at compile time (a silently shadowed duplicate was previously accepted, with the last declaration winning); non-literal names (a variable or expression) cannot be compared statically and are not checked. Urchin enforces no tool-name pattern (the MCP schema imposes none); servers should still follow the MCP naming recommendations.
initializerequiresprotocolVersion(string),capabilities(object) andclientInfo(with a stringnameandversion); a missing or mistyped field is aninvalid_paramserror rather than a silently-defaulted value. The server'sserverInfomust likewise carry a stringnameandversion.- The
MCP-Protocol-Versionheader is validated onDELETE, matchingPOSTandGET. completion/completerequest params are validated (refas aref/prompt/ref/resourceunion,argument.name/valueas strings,context.argumentsvalues as strings) and returninvalid_paramswhen malformed. Results are capped at 100 values — a handler returning more is truncated to the top 100 (already ranked by relevance) withhasMoreset — and a non-conforming result shape (non-stringvalues, etc.) is an internal error.- A tool's
input_schemaandoutput_schemamust be JSON Schema objects whose roottypeis"object"(per the MCP tools spec); the DSL rejects a non-conforming schema at compile time. logging/setLevelis now a library builtin: when the server advertises theloggingcapability (viause Urchin.Server, logging: true) it succeeds and applies the level to the session even without aset_log_level/2callback. The level is validated against the MCP log levels (invalid_paramsotherwise), an exportedset_log_level/2still runs as a hook, and the session level is updated only after the hook succeeds. Servers that do not advertiseloggingreturnmethod_not_found.
Fixed
- README no longer claims unqualified "resumable SSE streams"; resumption is scoped to the GET stream, matching the implementation.
0.2.0 - 2026-06-05
Added
- Optional OAuth 2.1 authorization (
Urchin.Auth). Urchin can act as an OAuth 2.1 resource server: RFC 9728 Protected Resource Metadata discovery (served at the well-known URI and advertised viaWWW-Authenticate: resource_metadata), pluggable token validation (Urchin.Auth.TokenValidator), RFC 8707 audience binding, scope enforcement and401/403/400challenges. Enable it with the:authoption on the transport,Urchin.EndpointorUrchin.start_link/2, or compose theUrchin.Auth.PlugandUrchin.Auth.Metadataplugs. Validated claims reach handlers asctx.auth. Authorization remains off by default.
0.1.0 - 2026-06-04
Initial release: a Model Context Protocol (MCP) server library implementing the
2025-11-25 specification over the Streamable HTTP transport.
Added
- Server authoring via the
Urchin.Serverbehaviour and atool/resource/resource_template/promptDSL with automatic capability derivation. - Tools, resources (plus templates and subscriptions), prompts, completion and logging.
- Server-initiated requests over SSE: sampling, elicitation and roots.
- Progress notifications, cancellation, pagination and resumable SSE streams.
- A mountable
Plug(Urchin.Transport.StreamableHTTP) and a standalone Bandit endpoint (Urchin.Endpoint,Urchin.start_link/2), plusUrchin.broadcast/2for fan-out notifications.