Message Handling
How Hermes handles MCP protocol messages.
Message Types
MCP uses JSON-RPC 2.0 with three message types:
- Requests - Expect a response (have ID)
- Responses - Reply to requests (match ID)
- Notifications - One-way messages (no ID)
Message Flow
Client ──request──> Transport ──> Server
<─response── Transport <──
Message Encoding
Use Hermes.MCP.Message
for all message operations:
# Encode request
{:ok, encoded} = Message.encode_request(%{
method: "tools/call",
params: %{name: "calculator", arguments: %{}}
}, "req_123")
# Encode response
{:ok, encoded} = Message.encode_response(%{
result: %{answer: 42}
}, "req_123")
# Encode notification
{:ok, encoded} = Message.encode_notification(%{
method: "cancelled",
params: %{requestId: "req_123"}
})
# Decode any message
{:ok, [decoded]} = Message.decode(json_string)
Message Guards
Check message types:
case decoded do
msg when Message.is_request(msg) ->
handle_request(msg)
msg when Message.is_response(msg) ->
handle_response(msg)
msg when Message.is_notification(msg) ->
handle_notification(msg)
end
Request IDs
Hermes generates unique request IDs:
id = Hermes.MCP.ID.generate_request_id()
# => "req_hyp_abcd1234efgh5678"
Timeouts
Requests timeout after 30 seconds by default:
# Custom timeout
Hermes.Client.call_tool(client, "slow_tool", %{}, timeout: 60_000)
On timeout:
- Request removed from pending
- Cancellation sent to server
- Error returned to caller
Common Patterns
Client Side
# The client handles correlation automatically
{:ok, response} = Hermes.Client.call_tool(client, "tool", %{})
Server Side
def handle_request(%{"method" => method, "id" => id}, frame) do
result = process_method(method)
{:reply, result, frame}
end
def handle_notification(%{"method" => "cancelled"}, frame) do
# Cancel any running operation
{:noreply, frame}
end