mcpField
M
MCP over SSE

MCP over SSE

本地服务
github.com/kEND开发工具6479 周前更新

关于 MCP over SSE

MCP over SSE 是一个 Elixir 库,用于以 Server-Sent Events 作为传输方式实现 MCP 服务。适合希望在 Phoenix 或基于 Plug 与 Bandit 的应用中构建 MCP 服务的 Elixir 开发者。

主要功能

  1. 01基于 MCPServer behaviour 实现服务
  2. 02SSE 连接管理与 JSON-RPC 消息处理
  3. 03工具注册与执行
  4. 04会话管理与自动心跳保活
  5. 05支持 Phoenix 与 Plug + Bandit 部署

适用场景

  1. 01在 Phoenix 应用中提供 MCP 工具接口
  2. 02通过 MCP Inspector 或 Cursor 连接本地 Elixir 服务进行调试

MCP 相关

该项目是一个用于构建 MCP 服务的库,通过 SSE 端点(默认 /sse 与 /message)向 MCP 客户端提供工具。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"mcp_sse": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/inspector"
],
"env": {
"MCP_SERVER_URL": "<MCP_SERVER_URL>"
}
}
}
}
启动命令npx -y @modelcontextprotocol/inspector
需要配置的环境变量
MCP_SERVER_URL

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@modelcontextprotocol/inspector

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

基于 SSE 的 MCP

Releases Documentation MCP Specification Version Downloads License CI Last Commit

此库提供了一个基于服务器发送事件(SSE)的模型上下文协议(MCP)简易实现。

有关模型上下文协议的更多信息,请访问: 模型上下文协议文档。

目录

功能

  • 完整的 MCP 服务实现
  • SSE 连接管理
  • JSON-RPC 消息处理
  • 工具注册和执行
  • 会话管理
  • 自动 ping/保活
  • 错误处理和验证

创建自己的 MCP 服务

你必须实现 MCPServer behaviour。

你只需实现必需的回调(handle_ping/1 和 handle_initialize/2),以及你希望支持的功能对应的可选回调。

use MCPServer 宏提供:

  • 内置消息路由
  • 协议版本验证
  • 可选回调的默认实现
  • JSON-RPC 错误处理
  • 日志记录

请参阅 DefaultServer,了解 MCPServer behaviour 的默认实现。

安装

用于 Phoenix 应用:
  1. 在 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. 在 mix.exs 中添加依赖:
def deps do
  [
    {:mcp_sse, "~> 0.1.6"}
  ]
end
  1. 配置路由器(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. 运行应用:
mix phx.server
用于搭配 Bandit 的 Plug 应用:
  1. 创建一个带监督机制的新 Plug 应用:
mix new your_app --sup
  1. 在 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. 在 mix.exs 中添加依赖:
def deps do
  [
    {:mcp_sse, "~> 0.1.6"},
    {:plug, "~> 1.14"},
    {:bandit, "~> 1.2"}
  ]
end
  1. 配置路由器(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. 设置应用监督(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. 运行应用:
mix run --no-halt

使用方法

搭配 MCP Inspector 使用
  • 启动 Inspector:
MCP_SERVER_URL=localhost:4000 npx @modelcontextprotocol/inspector@latest
  • 打开 http://localhost:6274/
  • 确保你的服务正在运行
  • 点击 Connect
  • 现在可以列出工具并调用它们
搭配 Cursor 使用
  • 打开 Cursor 设置
  • 前往 MCP 选项卡
  • 点击 Add new global MCP server
  • 在 ~/.cursor/mcp.json 中填写:
{
  "mcpServers": {
    "your-mcp-server": {
      "url": "http://localhost:4000/sse"
    }
  }
}
  • 确保你的服务正在运行
  • 让 Cursor 运行其中一个工具

配置

端口和 HTTPS

可以在应用模块中为 Bandit 服务配置其他选项:

# 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"
  }
]
路径

你可以自定义 SSE 和消息端点使用的路径:

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

这样你就可以在路由器中使用自定义路径:

# 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
保持连接

SSE 连接会定期发送保持连接的 ping,以防止连接超时。 你可以在 config/config.exs 中配置 ping 间隔,或将其完全禁用:

# 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

快速演示

要查看 MCP 服务的运行情况:

  1. 在一个终端中启动服务:
# Our example server
elixir dev/example_server.exs

# Your Phoenix application
mix phx.server

# Your Plug application
mix run --no-halt
  1. 在另一个终端中运行演示客户端脚本:
elixir dev/example_client.exs

客户端脚本将:

  • 连接到 SSE 端点
  • 初始化连接
  • 列出可用工具
  • 使用示例输入调用 upcase 工具
  • 显示每个步骤的结果

这演示了模型上下文协议的实际流程和服务功能。

其他说明

客户端用法示例
// 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: {}
    }
  })
});
会话管理

MCP SSE 服务要求每个连接都具有会话 ID。路由器会自动:

  • 使用查询参数中提供的现有会话 ID
  • 如果没有会话 ID,则生成新的会话 ID
  • 确保对 /sse 和 /message 端点的所有请求都具有有效的会话 ID
MCP 响应格式

在 MCP 服务中实现工具响应时,内容必须遵循 MCP 规范中有关内容类型的规定。 响应内容应采用以下类型之一进行格式化:

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

对于 JSON 等结构化数据,应将其转换为格式化字符串:

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

有关响应格式的更多详情,请参阅 MCP 内容类型规范。

贡献

  • Fork 此仓库并克隆它
  • 在你的 fork 中创建一个新分支
  • 进行更改并提交
  • 将更改推送到你的 fork
  • 在上游仓库中发起 pull request

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。