Hermes.Server.Transport.SSE (hermes_mcp v0.10.0)

SSE (Server-Sent Events) transport implementation for MCP servers.

This module provides backward compatibility with the HTTP+SSE transport from MCP protocol version 2024-11-05. It supports multiple concurrent client sessions through Server-Sent Events for server-to-client communication and HTTP POST for client-to-server messages.

Features

  • Multiple concurrent client sessions
  • Server-Sent Events for real-time server-to-client communication
  • Separate SSE and POST endpoints (as per 2024-11-05 spec)
  • Automatic session cleanup on disconnect
  • Integration with existing Phoenix/Plug applications

Usage

SSE transport is typically started through the server supervisor:

Hermes.Server.start_link(MyServer, [],
  transport: :sse,
  sse: [port: 8080, sse_path: "/sse", post_path: "/messages"]
)

For integration with existing Phoenix/Plug applications:

# In your router
forward "/sse", Hermes.Server.Transport.SSE.Plug,
  server: MyApp.MCPServer,
  mode: :sse

forward "/messages", Hermes.Server.Transport.SSE.Plug,
  server: MyApp.MCPServer,
  mode: :post

Message Flow

  1. Client connects to SSE endpoint, receives "endpoint" event with POST URL
  2. Client sends messages via POST to the endpoint URL
  3. Server responses are pushed through the SSE connection
  4. Connection closes on client disconnect or server shutdown

Configuration

  • :port - HTTP server port (default: 8080)
  • :sse_path - Path for SSE connections (default: "/sse")
  • :post_path - Path for POST messages (default: "/messages")
  • :server - The MCP server process to connect to
  • :name - Process registration name

Summary

Types

SSE transport options

t()

Functions

Returns a specification to start this module under a supervisor.

Gets the endpoint URL that should be sent to clients.

Gets the SSE handler process for a session.

Handles an incoming message from a client with request context.

Registers an SSE handler process for a session.

Routes a message to a specific session's SSE handler.

Sends a message to the client via the active SSE connection.

Shuts down the transport connection.

Starts the SSE transport.

Unregisters an SSE handler process for a session.

Types

option()

@type option() ::
  {:server, GenServer.server()}
  | {:name, GenServer.name()}
  | {:base_url, String.t()}
  | {:post_path, String.t()}
  | GenServer.option()

SSE transport options

  • :server - The server process (required)
  • :name - Name for registering the GenServer (required)
  • :base_url - Base URL for constructing endpoint URLs
  • :post_path - Path for POST endpoint (default: "/messages")

t()

@type t() :: GenServer.server()

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

get_endpoint_url(transport)

@spec get_endpoint_url(GenServer.server()) :: String.t()

Gets the endpoint URL that should be sent to clients.

This constructs the URL that clients should use for POST requests.

get_schema(atom)

get_sse_handler(transport, session_id)

@spec get_sse_handler(GenServer.server(), String.t()) :: pid() | nil

Gets the SSE handler process for a session.

Returns the pid of the process handling SSE for this session, or nil if no SSE connection exists.

handle_message(transport, session_id, message, context)

@spec handle_message(GenServer.server(), String.t(), map(), map()) ::
  {:ok, binary() | nil} | {:error, term()}

Handles an incoming message from a client with request context.

Called by the Plug when a message is received via HTTP POST.

parse_options(data)

parse_options!(data)

register_sse_handler(transport, session_id)

@spec register_sse_handler(GenServer.server(), String.t()) :: :ok | {:error, term()}

Registers an SSE handler process for a session.

Called by the Plug when establishing an SSE connection. The calling process becomes the SSE handler for the session.

route_to_session(transport, session_id, message)

@spec route_to_session(GenServer.server(), String.t(), binary()) ::
  :ok | {:error, term()}

Routes a message to a specific session's SSE handler.

Used for targeted server notifications to specific clients.

send_message(transport, message)

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

Sends a message to the client via the active SSE connection.

This broadcasts to all active SSE connections for the session.

Parameters

  • transport - The transport process
  • message - The message to send

Returns

  • :ok if message was sent successfully
  • {:error, reason} otherwise

shutdown(transport)

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

Shuts down the transport connection.

This terminates all active sessions managed by this transport.

Parameters

  • transport - The transport process

start_link(opts)

@spec start_link(Enumerable.t(option())) :: GenServer.on_start()

Starts the SSE transport.

unregister_sse_handler(transport, session_id)

@spec unregister_sse_handler(GenServer.server(), String.t()) :: :ok

Unregisters an SSE handler process for a session.

Called when the SSE connection is closed.