Urchin.Transport.StreamableHTTP (Urchin v0.4.0)

Copy Markdown View Source

A Plug implementing the MCP Streamable HTTP transport (revision 2025-11-25).

Mount it at a single endpoint path serving POST, GET and DELETE:

forward "/mcp", to: Urchin.Transport.StreamableHTTP, init_opts: [server: MyServer]

or run it standalone via Urchin.start_link/2 / Urchin.Endpoint.

Options

  • :server (required) - a module implementing Urchin.Server
  • :init_arg - argument passed to Urchin.Server.init/1 once per session (default nil)
  • :allowed_origins - :all, a list of allowed Origin values, or nil to allow missing origins plus localhost (default nil)
  • :require_session - reject post-initialize requests without a session id (default true)
  • :enable_get - offer the GET SSE stream (default true)
  • :allow_delete - allow client session termination via DELETE (default true)
  • :min_log_level - default minimum log level for new sessions (default "info")
  • :request_timeout - per-request handler timeout in ms (default 60_000)
  • :validate_protocol_version - validate the MCP-Protocol-Version header (default true)
  • :max_sessions - reject new sessions with 503 once this many are active (default nil, unlimited). The cap is enforced atomically before the server's init/1 runs, and is global across all sessions in the app.
  • :session_idle_timeout - terminate a session after this many ms without client activity (default nil, never). A session serving a request is not reaped.
  • :session_max_lifetime - terminate a session this many ms after it was created, regardless of activity (default nil, never). Set it above your longest expected tool run, since it can expire a session mid-request.
  • :expose_internal_errors - return raised-exception messages to the client instead of a generic error (default false). Exceptions are always logged; enable only in development.
  • :sse_buffer_limit - the maximum number of recent general-stream (GET SSE) events each session keeps for resumption replay. Defaults to nil, which preserves the session's internal default of 100. A positive integer or nil.
  • :auth - an Urchin.Auth (or keyword options) to require OAuth 2.1 bearer tokens on every request; nil (default) serves MCP unauthenticated. The metadata discovery endpoint is served by Urchin.Endpoint/Urchin.Auth.Metadata, not this plug.

The transport enforces the spec by default and these behaviors are not configurable: it validates a DSL tool's tools/call arguments against its input schema (a hand-written call_tool/3 validates its own arguments), rejects operation requests received before notifications/initialized (only ping is allowed pre-init), and surfaces a tool handler's {:error, binary} as an isError CallToolResult.

The plug reads the raw request body itself, so mount it before any JSON body parser.