Hermolaos.Transport.Http (Hermolaos v0.3.0)

View Source

HTTP/SSE transport for MCP communication with remote servers.

This transport connects to remote MCP servers using HTTP POST for sending messages and optionally Server-Sent Events (SSE) for receiving streamed responses and server-initiated messages.

How It Works

  1. Messages are sent via HTTP POST to the server endpoint
  2. Responses come as either JSON (immediate) or SSE stream
  3. Session state is maintained via Mcp-Session-Id header
  4. Optional GET endpoint can open persistent SSE stream for server notifications

Example

{:ok, transport} = Hermolaos.Transport.Http.start_link(
  owner: self(),
  url: "http://localhost:3000/mcp"
)

:ok = Hermolaos.Transport.Http.send_message(transport, %{
  "jsonrpc" => "2.0",
  "id" => 1,
  "method" => "initialize",
  "params" => %{}
})

Messages Sent to Owner

  • {:transport_ready, pid} - Transport is ready
  • {:transport_message, pid, map} - Received a JSON message
  • {:transport_closed, pid, reason} - Connection closed
  • {:transport_error, pid, error} - Error occurred

Options

  • :owner - PID to receive messages (required)
  • :url - Server endpoint URL (required)
  • :headers - Additional HTTP headers (default: [])
  • :req_options - Options passed to Req (default: [])
  • :connect_timeout - Connection timeout in ms (default: 30000)
  • :receive_timeout - Response timeout in ms (default: 60000)

Performance Notes

This transport uses Req with Finch for connection pooling. Multiple concurrent requests share the same connection pool, making it efficient for high-throughput scenarios.

Summary

Functions

Sends a message asynchronously (non-blocking).

Returns a specification to start this module under a supervisor.

Closes the transport.

Checks if the transport is connected.

Returns transport information and statistics.

Sends a JSON-RPC message to the server via HTTP POST.

Starts the HTTP transport.

Types

option()

@type option() ::
  {:owner, pid()}
  | {:url, String.t()}
  | {:headers, [{String.t(), String.t()}]}
  | {:req_options, keyword()}
  | {:connect_timeout, pos_integer()}
  | {:receive_timeout, pos_integer()}
  | {:name, GenServer.name()}

state()

@type state() :: %{
  owner: pid(),
  url: String.t(),
  session_id: String.t() | nil,
  headers: [{String.t(), String.t()}],
  req_options: keyword(),
  connect_timeout: pos_integer(),
  receive_timeout: pos_integer(),
  connected: boolean(),
  pending_requests: %{required(reference()) => pid()},
  stats: stats()
}

stats()

@type stats() :: %{
  requests_sent: non_neg_integer(),
  responses_received: non_neg_integer(),
  errors: non_neg_integer()
}

Functions

cast_message(transport, message)

@spec cast_message(GenServer.server(), map()) :: :ok

Sends a message asynchronously (non-blocking).

The HTTP request is performed in a background task.

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

close(transport)

@spec close(GenServer.server()) :: :ok

Closes the transport.

connected?(transport)

@spec connected?(GenServer.server()) :: boolean()

Checks if the transport is connected.

info(transport)

@spec info(GenServer.server()) :: map()

Returns transport information and statistics.

send_message(transport, message)

@spec send_message(GenServer.server(), map()) :: :ok | {:error, term()}

Sends a JSON-RPC message to the server via HTTP POST.

This is a synchronous call that waits for the HTTP request to complete. The response message(s) will be sent to the owner process.

Examples

:ok = Hermolaos.Transport.Http.send_message(transport, %{
  "jsonrpc" => "2.0",
  "id" => 1,
  "method" => "tools/list"
})

start_link(opts)

@spec start_link(keyword()) :: {:ok, pid()} | {:error, term()}

Starts the HTTP transport.

Options

  • :owner - PID to receive transport messages (required)
  • :url - The MCP server endpoint URL (required)
  • :headers - Additional HTTP headers (default: [])
  • :req_options - Options passed to Req (default: [])
  • :connect_timeout - Connection timeout in ms (default: 30000)
  • :receive_timeout - Response timeout in ms (default: 60000)
  • :name - GenServer name (optional)

Examples

{:ok, pid} = Hermolaos.Transport.Http.start_link(
  owner: self(),
  url: "http://localhost:3000/mcp",
  headers: [{"authorization", "Bearer token"}]
)