ExMCP.Protocol.ResponseBuilder (ex_mcp v0.9.0)

View Source

Utility module for building JSON-RPC 2.0 responses in MCP.

This module centralizes all response building logic to ensure consistency across the codebase and reduce duplication.

Summary

Functions

Builds a batch response error (when batch requests are not supported).

Builds an error JSON-RPC response.

Builds a standard MCP error response using error code atoms.

Builds a JSON-RPC notification (no id field).

Builds a JSON-RPC request (used for server-to-client requests).

Builds a successful JSON-RPC response.

Builds a tool error response with proper MCP structure.

Checks if a response is an error response.

Checks if a response is a notification (has no id).

Functions

build_batch_error(protocol_version \\ "2025-06-18")

@spec build_batch_error(String.t()) :: map()

Builds a batch response error (when batch requests are not supported).

Examples

iex> ResponseBuilder.build_batch_error("2025-06-18")
%{
  "jsonrpc" => "2.0",
  "id" => nil,
  "error" => %{
    "code" => -32600,
    "message" => "Batch requests are not supported in protocol version 2025-06-18"
  }
}

build_error_response(error, id)

Builds an error JSON-RPC response.

Accepts either raw parameters or an ExMCP.Error struct.

Examples

iex> ResponseBuilder.build_error_response(-32601, "Method not found", nil, 123)
%{
  "jsonrpc" => "2.0",
  "id" => 123,
  "error" => %{
    "code" => -32601,
    "message" => "Method not found"
  }
}

iex> ResponseBuilder.build_error_response(-32602, "Invalid params", %{"expected" => "string"}, 123)
%{
  "jsonrpc" => "2.0",
  "id" => 123,
  "error" => %{
    "code" => -32602,
    "message" => "Invalid params",
    "data" => %{"expected" => "string"}
  }
}

iex> error = %ExMCP.Error.ProtocolError{code: -32601, message: "Method not found"}
iex> ResponseBuilder.build_error_response(error, 123)
%{
  "jsonrpc" => "2.0",
  "id" => 123,
  "error" => %{
    "code" => -32601,
    "message" => "Method not found"
  }
}

build_error_response(code, message, data \\ nil, id)

@spec build_error_response(integer() | struct(), String.t() | any(), any(), any()) ::
  map()

build_mcp_error(error_atom, id, custom_message \\ nil, data \\ nil)

@spec build_mcp_error(atom(), any(), String.t() | nil, any()) :: map()

Builds a standard MCP error response using error code atoms.

Examples

iex> ResponseBuilder.build_mcp_error(:method_not_found, 123)
%{
  "jsonrpc" => "2.0",
  "id" => 123,
  "error" => %{
    "code" => -32601,
    "message" => "Method not found"
  }
}

iex> ResponseBuilder.build_mcp_error(:invalid_params, 123, "Missing required field")
%{
  "jsonrpc" => "2.0",
  "id" => 123,
  "error" => %{
    "code" => -32602,
    "message" => "Missing required field"
  }
}

build_notification(method, params)

@spec build_notification(String.t(), map()) :: map()

Builds a JSON-RPC notification (no id field).

Examples

iex> ResponseBuilder.build_notification("resources/list_changed", %{})
%{
  "jsonrpc" => "2.0",
  "method" => "resources/list_changed",
  "params" => %{}
}

build_request(method, params, id)

@spec build_request(String.t(), map(), any()) :: map()

Builds a JSON-RPC request (used for server-to-client requests).

Examples

iex> ResponseBuilder.build_request("tools/call", %{"name" => "test"}, "req-123")
%{
  "jsonrpc" => "2.0",
  "id" => "req-123",
  "method" => "tools/call",
  "params" => %{"name" => "test"}
}

build_success_response(result, id)

@spec build_success_response(any(), any()) :: map()

Builds a successful JSON-RPC response.

Examples

iex> ResponseBuilder.build_success_response(%{"tools" => []}, 123)
%{
  "jsonrpc" => "2.0",
  "id" => 123,
  "result" => %{"tools" => []}
}

build_tool_error(error_text, is_error \\ true, id)

@spec build_tool_error(String.t(), boolean(), any()) :: map()

Builds a tool error response with proper MCP structure.

Examples

iex> ResponseBuilder.build_tool_error("Tool execution failed", true, 123)
%{
  "jsonrpc" => "2.0",
  "id" => 123,
  "result" => %{
    "content" => [
      %{
        "type" => "text",
        "text" => "Tool execution failed"
      }
    ],
    "isError" => true
  }
}

error_response?(response)

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

Checks if a response is an error response.

Examples

iex> ResponseBuilder.error_response?(%{"error" => %{"code" => -32601}})
true

iex> ResponseBuilder.error_response?(%{"result" => %{}})
false

notification?(message)

@spec notification?(map()) :: boolean()

Checks if a response is a notification (has no id).

Examples

iex> ResponseBuilder.notification?(%{"jsonrpc" => "2.0", "method" => "test"})
true

iex> ResponseBuilder.notification?(%{"jsonrpc" => "2.0", "id" => 1, "method" => "test"})
false