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:

  1. Requests - Messages sent from client to server that expect a response
  2. Responses - Messages sent from server to client in response to a request
  3. Notifications - Messages sent in either direction that don't expect a response
  4. 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

Types

Represents any MCP protocol message.

Functions

Decodes a JSON string into MCP message(s).

Encodes a notification message into a JSON-RPC 2.0 compliant string.

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

message()

@type message() :: map()

Represents any MCP protocol message.

MCP protocol uses JSON-RPC 2.0 with several message formats:

  1. Request Message:

    {
      "jsonrpc": "2.0",
      "method": String, // Method name like "ping", "resources/list"
      "params": Object, // Method parameters
      "id": String|Number // Request identifier
    }
  2. Notification Message:

    {
      "jsonrpc": "2.0",
      "method": String, // Method name like "notifications/progress"
      "params": Object  // Notification parameters
    }
  3. Response Message:

    {
      "jsonrpc": "2.0",
      "result": Object, // Result value
      "id": String|Number // Request identifier
    }
  4. 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

decode(data)

@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

encode_notification(notification)

@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

encode_progress_notification(progress_token, progress, total \\ nil)

@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

encode_request(request, id)

@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 params
  • id - 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"

is_error(data)

(macro)

is_notification(data)

(macro)

is_request(data)

(macro)

is_response(data)

(macro)

to_domain(message)

@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

validate(message)

@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