View Source MCP over SSE
This library provides a simple implementation of the Model Context Protocol (MCP) over Server-Sent Events (SSE).
For more information about the Model Context Protocol, visit: Model Context Protocol Documentation
Installation
For Phoenix Applications:
- Add the required configuration to
config/config.exs
:
# Configure MIME types for SSE
config :mime, :types, %{
"text/event-stream" => ["sse"]
}
# Configure the MCP Server
config :nws_mcp_server, :mcp_server, MCP.DefaultServer
# config :nws_mcp_server, :mcp_server, YourApp.YourMCPServer
- Add to your dependencies in
mix.exs
:
def deps do
[
{:mcp_sse, "~> 0.1.0"}
]
end
- Configure your router (
lib/your_app_web/router.ex
):
pipeline :sse do
plug :accepts, ["sse"]
end
scope "/" do
pipe_through :sse
get "/sse", SSE.ConnectionPlug, :call
pipe_through :api
post "/message", SSE.ConnectionPlug, :call
end
For Plug Applications with Bandit:
Create a new Plug application with supervision:
mix new your_app --sup
Add the required configuration to
config/config.exs
:import Config # Configure MIME types for SSE config :mime, :types, %{ "text/event-stream" => ["sse"] } # Configure the MCP Server config :your_app, :mcp_server, YourApp.MCPServer
Add dependencies to
mix.exs
:def deps do [ {:mcp_sse, "~> 0.1.0"}, {:plug, "~> 1.14"}, {:bandit, "~> 1.2"} ] end
Configure your router (
lib/your_app/router.ex
):defmodule YourApp.Router do use Plug.Router plug Plug.Parsers, parsers: [:urlencoded, :json], pass: ["text/*"], json_decoder: Jason plug :match plug :ensure_session_id plug :dispatch # Middleware to ensure session ID exists def ensure_session_id(conn, _opts) do case get_session_id(conn) do nil -> # Generate a new session ID if none exists session_id = generate_session_id() %{conn | query_params: Map.put(conn.query_params, "sessionId", session_id)} _session_id -> conn end end # Helper to get session ID from query params defp get_session_id(conn) do conn.query_params["sessionId"] end # Generate a unique session ID defp generate_session_id do Base.encode16(:crypto.strong_rand_bytes(8), case: :lower) end forward "/sse", to: SSE.ConnectionPlug forward "/message", to: SSE.ConnectionPlug match _ do send_resp(conn, 404, "Not found") end end
Set up your application supervision (
lib/your_app/application.ex
):defmodule YourApp.Application do use Application @impl true def start(_type, _args) do children = [ {Bandit, plug: YourApp.Router, port: 4000} ] opts = [strategy: :one_for_one, name: YourApp.Supervisor] Supervisor.start_link(children, opts) end end
Session Management
The MCP SSE server requires a session ID for each connection. The router automatically:
- Uses an existing session ID from query parameters if provided
- Generates a new session ID if none exists
- Ensures all requests to
/sse
and/message
endpoints have a valid session ID
Configuration Options
The Bandit server can be configured with additional options in your application module:
# Example with custom port and HTTPS
children = [
{Bandit,
plug: YourApp.Router,
port: System.get_env("PORT", "4000") |> String.to_integer(),
scheme: :https,
certfile: "priv/cert/selfsigned.pem",
keyfile: "priv/cert/selfsigned_key.pem"
}
]
The use MCPServer
macro provides:
- Built-in message routing
- Protocol version validation
- Default implementations for optional callbacks
- JSON-RPC error handling
- Logging
You only need to implement the required callbacks (handle_ping/1
and handle_initialize/2
) and any optional callbacks for features you want to support.
Protocol Documentation
For detailed information about the Model Context Protocol, visit: Model Context Protocol Documentation
Features
- Full MCP server implementation
- SSE connection management
- JSON-RPC message handling
- Tool registration and execution
- Session management
- Automatic ping/keepalive
- Error handling and validation
Contributing
...
Quick Demo
To see the MCP server in action:
Start the Phoenix server:
mix phx.server
In another terminal, run the demo client script:
elixir examples/mcp_client.exs
The client script will:
- Connect to the SSE endpoint
- Initialize the connection
- List available tools
- Call the upcase tool with example input
- Display the results of each step
This provides a practical demonstration of the MCP protocol flow and server capabilities.
Other Notes
Example Client Usage
// Connect to SSE endpoint
const sse = new EventSource('/sse');
// Handle endpoint message
sse.addEventListener('endpoint', (e) => {
const messageEndpoint = e.data;
// Use messageEndpoint for subsequent JSON-RPC requests
});
// Send initialize request
fetch('/message?sessionId=YOUR_SESSION_ID', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'initialize',
params: {
protocolVersion: '2024-11-05',
capabilities: {}
}
})
});