Hermes.MCP.Message (hermes_mcp v0.3.2)
Handles encoding and decoding of MCP protocol messages.
This module provides functions for parsing, validating, and creating MCP (Model Context Protocol) messages using the JSON-RPC 2.0 format.
Message Types
The MCP protocol uses JSON-RPC 2.0 messages in these forms:
- Requests - Messages sent from client to server that expect a response
- Responses - Messages sent from server to client in response to a request
- Notifications - Messages sent in either direction that don't expect a response
- Errors - Error responses sent from server to client
Examples
# Decode MCP messages from a string
{:ok, messages} = Hermes.MCP.Message.decode(json_string)
# Encode a request
{:ok, request_json} = Hermes.MCP.Message.encode_request(%{"method" => "ping", "params" => %{}}, "req_123")
# Encode a notification
{:ok, notif_json} = Hermes.MCP.Message.encode_notification(%{"method" => "notifications/progress", "params" => %{...}})
Summary
Functions
Decodes a JSON string into MCP message(s).
Encodes a notification message into a JSON-RPC 2.0 compliant string.
Encodes a progress notification message.
Encodes a request message into a JSON-RPC 2.0 compliant string.
Converts a message to the appropriate domain object based on its type.
Validates a message according to the MCP protocol.
Types
@type message() :: map()
Represents any MCP protocol message.
MCP protocol uses JSON-RPC 2.0 with several message formats:
Request Message:
{ "jsonrpc": "2.0", "method": String, // Method name like "ping", "resources/list" "params": Object, // Method parameters "id": String|Number // Request identifier }
Notification Message:
{ "jsonrpc": "2.0", "method": String, // Method name like "notifications/progress" "params": Object // Notification parameters }
Response Message:
{ "jsonrpc": "2.0", "result": Object, // Result value "id": String|Number // Request identifier }
Error Message:
{ "jsonrpc": "2.0", "error": { "code": Number, // Error code "message": String, // Error message "data": Any // Optional error data }, "id": String|Number // Request identifier }
Functions
@spec decode(String.t()) :: {:ok, [message()]} | {:error, Hermes.MCP.Error.t()}
Decodes a JSON string into MCP message(s).
This function handles both single messages and newline-delimited message streams.
Parameters
data
- The JSON string to decode
Returns
{:ok, messages}
where messages is a list of parsed MCP messages{:error, error}
if parsing fails
Examples
iex> Hermes.MCP.Message.decode(~s({"jsonrpc":"2.0","result":{},"id":"req_123"}))
{:ok, [%{"jsonrpc" => "2.0", "result" => %{}, "id" => "req_123"}]}
iex> {:error, error} = Hermes.MCP.Message.decode("invalid")
iex> error.code
-32700
iex> error.reason
:parse_error
@spec encode_notification(map()) :: {:ok, String.t()} | {:error, Hermes.MCP.Error.t()}
Encodes a notification message into a JSON-RPC 2.0 compliant string.
Parameters
notification
- A map containing the notification method and params
Returns
{:ok, json_string}
with the encoded notification and a newline{:error, error}
if encoding fails
Examples
iex> {:ok, notif} = Hermes.MCP.Message.encode_notification(%{"method" => "notifications/progress", "params" => %{"progress" => 50}})
iex> notif_map = notif |> String.trim() |> JSON.decode!()
iex> notif_map["jsonrpc"]
"2.0"
iex> notif_map["method"]
"notifications/progress"
iex> notif_map["params"]["progress"]
50
@spec encode_progress_notification(String.t() | integer(), number(), number() | nil) :: {:ok, String.t()} | {:error, Hermes.MCP.Error.t()}
Encodes a progress notification message.
Parameters
progress_token
- The token that was provided in the original request (string or integer)progress
- The current progress value (number)total
- Optional total value for the operation (number)
Returns
{:ok, json_string}
with the encoded notification and a newline{:error, error}
if encoding fails
Examples
iex> {:ok, notif} = Hermes.MCP.Message.encode_progress_notification("token123", 50)
iex> notif_map = notif |> String.trim() |> JSON.decode!()
iex> notif_map["method"]
"notifications/progress"
iex> notif_map["params"]["progressToken"]
"token123"
iex> notif_map["params"]["progress"]
50
@spec encode_request(map(), String.t() | integer()) :: {:ok, String.t()} | {:error, Hermes.MCP.Error.t()}
Encodes a request message into a JSON-RPC 2.0 compliant string.
Parameters
request
- A map containing the request method and paramsid
- A unique identifier for the request
Returns
{:ok, json_string}
with the encoded request and a newline{:error, error}
if encoding fails
Examples
iex> {:ok, request} = Hermes.MCP.Message.encode_request(%{"method" => "ping", "params" => %{}}, "req_123")
iex> request_map = request |> String.trim() |> JSON.decode!()
iex> request_map["jsonrpc"]
"2.0"
iex> request_map["method"]
"ping"
iex> request_map["id"]
"req_123"
@spec to_domain(message()) :: {:ok, Hermes.MCP.Response.t() | Hermes.MCP.Error.t()} | {:error, Hermes.MCP.Error.t()}
Converts a message to the appropriate domain object based on its type.
Parameters
message
- A parsed JSON-RPC 2.0 message
Returns
{:ok, domain_object}
with the appropriate type (Response or Error){:error, error}
if conversion fails
Examples
iex> resp = %{"jsonrpc" => "2.0", "result" => %{"data" => "value"}, "id" => "req_123"}
iex> {:ok, response} = Hermes.MCP.Message.to_domain(resp)
iex> response.__struct__
Hermes.MCP.Response
iex> err = %{"jsonrpc" => "2.0", "error" => %{"code" => -32700, "message" => "Parse error"}, "id" => "req_123"}
iex> {:ok, error} = Hermes.MCP.Message.to_domain(err)
iex> error.__struct__
Hermes.MCP.Error
@spec validate(map()) :: :ok | {:error, Hermes.MCP.Error.t()}
Validates a message according to the MCP protocol.
Performs basic validation to ensure the message conforms to the JSON-RPC 2.0 structure and has valid MCP-specific fields.
Parameters
message
- The parsed JSON message to validate
Returns
:ok
if the message is valid{:error, error}
if validation fails
Examples
iex> Hermes.MCP.Message.validate(%{"jsonrpc" => "2.0", "result" => %{}, "id" => "req_123"})
:ok
iex> {:error, error} = Hermes.MCP.Message.validate(%{"jsonrpc" => "1.0", "result" => %{}, "id" => "req_123"})
iex> error.reason
:invalid_request