Implementation of the Model Context Protocol (MCP) RPC functionality.
This module handles HTTP requests and responses according to the MCP specification, supporting both synchronous and streaming communication patterns. It also handles the core JSON-RPC message processing for the protocol.
Protocol versions
The server supports multiple protocol revisions in tandem on the same endpoint:
2026-07-28, which carries the protocol version, client identity, and capabilities in each request's_metaand never performs aninitializehandshake, and- the initialize-based revisions (
2025-06-18and2025-03-26), which negotiate viainitializeand may use theMcp-Session-Idheader.
The revision is selected per request: an initialize request (or a
request carrying an initialize-based/absent MCP-Protocol-Version header
and no per-request version _meta) is served with initialize-based
semantics; a request declaring its protocol version in _meta (or in the
MCP-Protocol-Version header) is served statelessly per the 2026-07-28
revision.
Summary
Functions
Validate the Origin header of a request per the Streamable HTTP
transport's DNS-rebinding protection requirement.
Get the MCP server instructions, if any. Returns the configured instructions
option (a string) or the result of calling it as a 1-arity function with the
request opts, or nil when unset. Forwarded on the initialize response so
hosts can prime the model with server-level guidance (analogous to a scoped
system prompt) alongside per-tool descriptions.
Get the MCP server name
Get the MCP server version
Handle HTTP DELETE request for session termination
Process an HTTP GET request.
Process an HTTP POST request containing JSON-RPC messages
Create a standard JSON-RPC error response
Parse the JSON-RPC request
Process a single JSON-RPC message
Computes the sandbox domain for an mcp_ui_resource from the MCP server URL.
Send an SSE event over the chunked connection
The protocol versions this server supports, newest first.
Functions
Validate the Origin header of a request per the Streamable HTTP
transport's DNS-rebinding protection requirement.
Returns :ok when the request carries no Origin header (non-browser
clients), when the origin's host is a localhost value, when the origin
matches the request host over HTTPS, or when it is explicitly allowed by
the :allowed_origins option (a list of origin strings, or a 1-arity
predicate function). Returns :forbidden otherwise — respond with HTTP
403.
Get the MCP server instructions, if any. Returns the configured instructions
option (a string) or the result of calling it as a 1-arity function with the
request opts, or nil when unset. Forwarded on the initialize response so
hosts can prime the model with server-level guidance (analogous to a scoped
system prompt) alongside per-tool descriptions.
Get the MCP server name
Get the MCP server version
Handle HTTP DELETE request for session termination
Process an HTTP GET request.
Responds 405 Method Not Allowed, which Streamable HTTP (2025-03-26
onward) permits for servers that never send unsolicited server-to-client
messages — this server has none to send (2026-07-28 clients use
subscriptions/listen over POST instead). The previous behavior of
opening an SSE stream and emitting an endpoint event was the wire
signature of the deprecated 2024-11-05 HTTP+SSE transport, which caused
dual-transport clients to switch to it and wait forever for responses on
the GET stream.
Process an HTTP POST request containing JSON-RPC messages
Create a standard JSON-RPC error response
Parse the JSON-RPC request
Process a single JSON-RPC message
Computes the sandbox domain for an mcp_ui_resource from the MCP server URL.
MCP hosts render UI resources in sandboxed iframes, and each host determines the iframe's origin differently:
| Host | Domain format | Behavior |
|---|---|---|
| Claude | {sha256_hash}.claudemcpcontent.com | Hash derived from the MCP server endpoint URL. |
| ChatGPT | {connector_id}.web-sandbox.oaiusercontent.com | Auto-assigned by ChatGPT; ignores the domain field. |
Since ChatGPT ignores domain entirely, this function generates a Claude-compatible
value so that a single configuration works across both hosts.
When domain is set to :auto (the default), this is called automatically at
request time using the server URL derived from the incoming connection.
Examples
iex> AshAi.Mcp.Server.sandbox_domain("http://localhost:4000/mcp")
"0307c5dc3988887979d60ecbb5101189.claudemcpcontent.com"
Send an SSE event over the chunked connection
The protocol versions this server supports, newest first.