ExAgent.MCP.Protocol (ExAgent v0.5.0)

Copy Markdown View Source

Pure JSON-RPC 2.0 encoding/decoding for the Model Context Protocol, plus the mapping from an MCP tool spec to an ExAgent.Tool.

Splitting the protocol out (no ports, no processes) keeps it trivially unit-testable. ExAgent.MCP.Client owns the transport and calls into here.

Summary

Functions

Decode one complete JSON-RPC line.

Encode a JSON-RPC notification (no id, no reply expected).

Encode a JSON-RPC request (with an id) as a newline-terminated binary.

Extract the concatenated text from an MCP tools/call result.

Map an MCP tools/list entry to an ExAgent.Tool whose call forwards to the server via call_fun. The tool's result text is extracted from the MCP content blocks; an MCP isError: true becomes {:error, _}.

Types

decoded()

@type decoded() ::
  {:response, id :: term(), result :: map()}
  | {:error_response, id :: term(), error :: map()}
  | {:notification, method :: String.t(), params :: map()}
  | :ignore

Functions

decode(line)

@spec decode(binary()) :: decoded()

Decode one complete JSON-RPC line.

  • a response with a result{:response, id, result}
  • a response with an error{:error_response, id, error}
  • a notification (no id) → {:notification, method, params}
  • anything unparseable / unrelated → :ignore

encode_notification(method, params)

@spec encode_notification(String.t(), map()) :: iodata()

Encode a JSON-RPC notification (no id, no reply expected).

encode_request(id, method, params)

@spec encode_request(term(), String.t(), map()) :: iodata()

Encode a JSON-RPC request (with an id) as a newline-terminated binary.

result_to_text(result)

@spec result_to_text(map()) :: {:ok, String.t()} | {:error, term()}

Extract the concatenated text from an MCP tools/call result.

to_tool(spec, call_fun)

@spec to_tool(map(), (String.t(), map() -> {:ok, String.t()} | {:error, term()})) ::
  ExAgent.Tool.t()

Map an MCP tools/list entry to an ExAgent.Tool whose call forwards to the server via call_fun. The tool's result text is extracted from the MCP content blocks; an MCP isError: true becomes {:error, _}.