Fire in da houseTop Tip:Paying $100+ per month for Perplexity, MidJourney, Runway, ChatGPT and other tools is crazy - get all your AI tools in one site starting at $15 per month with Galaxy AI Fire in da houseCheck it out free

mcp_sse

MCP.Pizza Chef: kEND

mcp_sse is an Elixir-based MCP server library that implements the Model Context Protocol using Server-Sent Events (SSE) as the transport layer. It enables real-time, unidirectional streaming of structured context data to LLM clients, facilitating seamless integration of live environment data into AI workflows. This lightweight, efficient server supports secure and scalable context delivery, ideal for developers building AI agents and copilots that require continuous context updates.

Use This MCP server To

Stream real-time context data to LLM clients via SSE Integrate live environment data into AI workflows Enable continuous context updates for AI agents Build scalable MCP servers with Elixir and SSE Deliver structured model context securely and efficiently

README

MCP over SSE

Releases Documentation MCP Specification Version Downloads License CI Last Commit

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.

Table of Contents

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

Create your own MCP server

You must implement the MCPServer behaviour.

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.

The use MCPServer macro provides:

  • Built-in message routing
  • Protocol version validation
  • Default implementations for optional callbacks
  • JSON-RPC error handling
  • Logging

See DefaultServer for a default implementation of the MCPServer behaviour.

Installation

For Phoenix Applications:

  1. 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 :mcp_sse, :mcp_server, YourApp.YourMCPServer
  1. Add to your dependencies in mix.exs:
def deps do
  [
    {:mcp_sse, "~> 0.1.6"}
  ]
end
  1. 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
  post "/message", SSE.ConnectionPlug, :call
end
  1. Run your application:
mix phx.server

For Plug Applications with Bandit:

  1. Create a new Plug application with supervision:
mix new your_app --sup
  1. 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 :mcp_sse, :mcp_server, YourApp.YourMCPServer
  1. Add dependencies to mix.exs:
def deps do
  [
    {:mcp_sse, "~> 0.1.6"},
    {:plug, "~> 1.14"},
    {:bandit, "~> 1.2"}
  ]
end
  1. 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: JSON

  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
  1. 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
  1. Run your application:
mix run --no-halt

Usage

With MCP Inspector

  • Start the inspector:
MCP_SERVER_URL=localhost:4000 npx @modelcontextprotocol/inspector@latest
  • Navigate to http://localhost:6274/
  • Make sure your server is running
  • Click Connect
  • You can now list tools and call them

With Cursor

  • Open Cursor Settings
  • Navigate to the MCP tab
  • Click Add new global MCP server
  • Fill in the ~/.cursor/mcp.json with:
{
  "mcpServers": {
    "your-mcp-server": {
      "url": "http://localhost:4000/sse"
    }
  }
}
  • Make sure your server is running
  • Ask Cursor to run one of your tools

Configuration

Port and HTTPS

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"
  }
]

Paths

You can customize the paths used for the SSE and message endpoints:

config :mcp_sse,
  sse_path: "/mcp/sse",    # Default: "/sse"
  message_path: "/mcp/msg" # Default: "/message"

This allows you to use custom paths in your routers:

# Phoenix
scope "/mcp" do
  pipe_through :sse
  get "/sse", SSE.ConnectionPlug, :call
  post "/msg", SSE.ConnectionPlug, :call
end

# Plug
forward "/mcp/sse", to: SSE.ConnectionPlug
forward "/mcp/msg", to: SSE.ConnectionPlug

Keepalive

The SSE connection sends periodic keepalive pings to prevent connection timeouts. You can configure the ping interval or disable it entirely in config/config.exs:

# Set custom ping interval (in milliseconds)
config :mcp_sse, :sse_keepalive_timeout, 30_000  # 30 seconds

# Or disable pings entirely
config :mcp_sse, :sse_keepalive_timeout, :infinity

Quick Demo

To see the MCP server in action:

  1. Start a server in one terminal:
# Our example server
elixir dev/example_server.exs

# Your Phoenix application
mix phx.server

# Your Plug application
mix run --no-halt
  1. In another terminal, run the demo client script:
elixir dev/example_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 Model Context 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: {}
    }
  })
});

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

MCP Response Formatting

When implementing tool responses in your MCP server, the content must follow the MCP specification for content types. The response content should be formatted as one of these types:

# Text content
{:ok,
 %{
   jsonrpc: "2.0",
   id: request_id,
   result: %{
     content: [
       %{
         type: "text",
         text: "Your text response here"
       }
     ]
   }
 }}

# Image content
{:ok,
 %{
   jsonrpc: "2.0",
   id: request_id,
   result: %{
     content: [
       %{
         type: "image",
         data: "base64_encoded_image_data",
         mimeType: "image/png"
       }
     ]
   }
 }}

# Resource reference
{:ok,
 %{
   jsonrpc: "2.0",
   id: request_id,
   result: %{
     content: [
       %{
         type: "resource",
         resource: %{
           name: "resource_name",
           description: "resource description"
         }
       }
     ]
   }
 }}

For structured data like JSON, you should convert it to a formatted string:

def handle_call_tool(request_id, %{"name" => "list_companies"} = _params) do
  companies = fetch_companies()  # Your data fetching logic

  {:ok,
   %{
     jsonrpc: "2.0",
     id: request_id,
     result: %{
       content: [
         %{
           type: "text",
           text: JSON.encode!(companies, pretty: true)
         }
       ]
     }
   }}
end

For more details on response formatting, see the MCP Content Types Specification.

Contributing

  • Fork the repository and clone it
  • Create a new branch in your fork
  • Make your changes and commit them
  • Push the changes to your fork
  • Open a pull request in upstream

mcp_sse FAQ

How does mcp_sse handle real-time data delivery?
It uses Server-Sent Events (SSE) to push continuous, unidirectional context updates to clients.
What programming language is mcp_sse built with?
mcp_sse is implemented in Elixir, leveraging its concurrency and fault-tolerance features.
Can mcp_sse be used with multiple LLM providers?
Yes, it is provider-agnostic and works with OpenAI, Anthropic Claude, and Google Gemini models.
Is mcp_sse suitable for high-scale deployments?
Yes, Elixir's lightweight processes and SSE's efficient streaming make it scalable for large workloads.
How secure is the data transmission in mcp_sse?
SSE supports secure HTTPS transport, and mcp_sse can be configured to enforce authentication and encryption.
Does mcp_sse support bidirectional communication?
No, SSE is unidirectional; for bidirectional needs, other MCP transport types should be considered.
Where can I find documentation and releases for mcp_sse?
Documentation and releases are available on its GitHub repository and Hex.pm package page.