# `Hermolaos.Protocol.JsonRpc`
[🔗](https://github.com/nyo16/hermolaos/blob/v0.5.0/lib/hermolaos/protocol/json_rpc.ex#L1)

JSON-RPC 2.0 encoding and decoding for MCP protocol messages.

This module handles the low-level JSON-RPC 2.0 message format used by MCP.
All MCP communication uses JSON-RPC 2.0 as the underlying protocol.

## Message Types

- **Request**: Has `id`, `method`, and optional `params`
- **Response**: Has `id` and either `result` or `error`
- **Notification**: Has `method` and optional `params`, but no `id`

## Examples

    # Encoding a request
    iex> Hermolaos.Protocol.JsonRpc.encode_request(1, "tools/list", %{})
    ~s({"id":1,"jsonrpc":"2.0","method":"tools/list","params":{}})

    # Decoding a response
    iex> Hermolaos.Protocol.JsonRpc.decode(~s({"jsonrpc":"2.0","id":1,"result":{"tools":[]}}))
    {:ok, {:response, %{"jsonrpc" => "2.0", "id" => 1, "result" => %{"tools" => []}}}}

# `decoded_message`

```elixir
@type decoded_message() :: {message_type(), map()}
```

# `error_object`

```elixir
@type error_object() :: %{
  :code =&gt; integer(),
  :message =&gt; String.t(),
  optional(:data) =&gt; term()
}
```

# `error_response`

```elixir
@type error_response() :: %{
  jsonrpc: String.t(),
  id: id() | nil,
  error: error_object()
}
```

# `id`

```elixir
@type id() :: integer() | String.t()
```

# `message_type`

```elixir
@type message_type() :: :request | :notification | :response | :error_response
```

# `notification`

```elixir
@type notification() :: %{
  :jsonrpc =&gt; String.t(),
  :method =&gt; String.t(),
  optional(:params) =&gt; params()
}
```

# `params`

```elixir
@type params() :: map() | list()
```

# `request`

```elixir
@type request() :: %{
  :jsonrpc =&gt; String.t(),
  :id =&gt; id(),
  :method =&gt; String.t(),
  optional(:params) =&gt; params()
}
```

# `response`

```elixir
@type response() :: %{jsonrpc: String.t(), id: id(), result: term()}
```

# `classify_message`

```elixir
@spec classify_message(map()) :: {:ok, decoded_message()} | {:error, :invalid_message}
```

Classifies a decoded JSON-RPC message by its type.

## Examples

    iex> Hermolaos.Protocol.JsonRpc.classify_message(%{"jsonrpc" => "2.0", "id" => 1, "method" => "ping"})
    {:ok, {:request, %{"jsonrpc" => "2.0", "id" => 1, "method" => "ping"}}}

# `decode`

```elixir
@spec decode(String.t()) ::
  {:ok, decoded_message()} | {:error, :parse_error | :invalid_message}
```

Decodes a JSON-RPC 2.0 message and classifies its type.

Returns `{:ok, {type, message}}` where type is one of:
- `:request` - A request expecting a response
- `:notification` - A notification (no response expected)
- `:response` - A successful response
- `:error_response` - An error response

## Examples

    iex> Hermolaos.Protocol.JsonRpc.decode(~s({"jsonrpc":"2.0","id":1,"method":"ping"}))
    {:ok, {:request, %{"jsonrpc" => "2.0", "id" => 1, "method" => "ping"}}}

    iex> Hermolaos.Protocol.JsonRpc.decode(~s({"jsonrpc":"2.0","method":"notifications/initialized"}))
    {:ok, {:notification, %{"jsonrpc" => "2.0", "method" => "notifications/initialized"}}}

    iex> Hermolaos.Protocol.JsonRpc.decode("invalid json")
    {:error, :parse_error}

# `decode!`

```elixir
@spec decode!(String.t()) :: decoded_message()
```

Decodes a JSON-RPC 2.0 message, raising on error.

## Examples

    iex> Hermolaos.Protocol.JsonRpc.decode!(~s({"jsonrpc":"2.0","id":1,"result":{}}))
    {:response, %{"jsonrpc" => "2.0", "id" => 1, "result" => %{}}}

# `encode_error_response`

```elixir
@spec encode_error_response(id() | nil, integer(), String.t(), term()) :: String.t()
```

Encodes a JSON-RPC 2.0 error response.

## Parameters

- `id` - The request ID (can be nil if request couldn't be parsed)
- `code` - Error code (integer)
- `message` - Human-readable error message
- `data` - Optional additional error data

## Examples

    iex> Hermolaos.Protocol.JsonRpc.encode_error_response(1, -32600, "Invalid Request")
    ~s({"error":{"code":-32600,"message":"Invalid Request"},"id":1,"jsonrpc":"2.0"})

# `encode_notification`

```elixir
@spec encode_notification(String.t(), params() | nil) :: String.t()
```

Encodes a JSON-RPC 2.0 notification message (no response expected).

Notifications are like requests but without an `id` field, meaning
the server should not send a response.

## Parameters

- `method` - The RPC method name
- `params` - Optional parameters (map or list)

## Examples

    iex> Hermolaos.Protocol.JsonRpc.encode_notification("notifications/initialized", %{})
    ~s({"jsonrpc":"2.0","method":"notifications/initialized","params":{}})

# `encode_request`

```elixir
@spec encode_request(id(), String.t(), params() | nil) :: String.t()
```

Encodes a JSON-RPC 2.0 request message.

## Parameters

- `id` - Unique request identifier (integer or string)
- `method` - The RPC method name
- `params` - Optional parameters (map or list)

## Examples

    iex> Hermolaos.Protocol.JsonRpc.encode_request(1, "tools/list", %{})
    ~s({"id":1,"jsonrpc":"2.0","method":"tools/list","params":{}})

    iex> Hermolaos.Protocol.JsonRpc.encode_request("abc", "ping", nil)
    ~s({"id":"abc","jsonrpc":"2.0","method":"ping"})

# `encode_response`

```elixir
@spec encode_response(id(), term()) :: String.t()
```

Encodes a JSON-RPC 2.0 success response.

## Parameters

- `id` - The request ID being responded to
- `result` - The result data

## Examples

    iex> Hermolaos.Protocol.JsonRpc.encode_response(1, %{tools: []})
    ~s({"id":1,"jsonrpc":"2.0","result":{"tools":[]}})

# `error_response?`

```elixir
@spec error_response?(map()) :: boolean()
```

Checks if a response is an error response.

# `get_error`

```elixir
@spec get_error(map()) :: error_object() | nil
```

Extracts the error object from an error response.

# `get_id`

```elixir
@spec get_id(map()) :: id() | nil
```

Extracts the request/response ID from a message.

## Examples

    iex> Hermolaos.Protocol.JsonRpc.get_id(%{"id" => 42})
    42

    iex> Hermolaos.Protocol.JsonRpc.get_id(%{"method" => "notify"})
    nil

# `get_method`

```elixir
@spec get_method(map()) :: String.t() | nil
```

Extracts the method name from a request or notification.

# `get_params`

```elixir
@spec get_params(map()) :: params() | nil
```

Extracts parameters from a request or notification.

# `get_result`

```elixir
@spec get_result(map()) :: term() | nil
```

Extracts the result from a successful response.

# `message_type`

```elixir
@spec message_type(map()) :: message_type() | :unknown
```

Returns the message type of a decoded message.

## Examples

    iex> Hermolaos.Protocol.JsonRpc.message_type(%{"jsonrpc" => "2.0", "id" => 1, "method" => "ping"})
    :request

# `valid_notification?`

```elixir
@spec valid_notification?(map()) :: boolean()
```

Validates that a message is a proper JSON-RPC 2.0 notification.

# `valid_request?`

```elixir
@spec valid_request?(map()) :: boolean()
```

Validates that a message is a proper JSON-RPC 2.0 request.

## Examples

    iex> Hermolaos.Protocol.JsonRpc.valid_request?(%{"jsonrpc" => "2.0", "id" => 1, "method" => "ping"})
    true

    iex> Hermolaos.Protocol.JsonRpc.valid_request?(%{"id" => 1, "method" => "ping"})
    false

# `valid_response?`

```elixir
@spec valid_response?(map()) :: boolean()
```

Validates that a message is a proper JSON-RPC 2.0 response (success or error).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
