MCP SSE Gateway
一个用 Go 语言实现的 MCP (Model Context Protocol) SSE 网关。
它作为 HTTP SSE 服务与后端 MCP CLI 进程之间的桥梁,让你可以通过 HTTP 接口调用各种 MCP 工具服务。
┌──────────────┐ HTTP/SSE ┌─────────────────┐ STDIO ┌─────────────────┐
│ Client │ ◄──────────────► │ MCP SSE │ ◄────────────► │ MCP Server │
│ (Claude等) │ │ Gateway │ │ (npx/python等) │
└──────────────┘ └─────────────────┘ └─────────────────┘快速开始
1. 编译
go build -o mcp-sse-gateway ./cmd/gateway/2. 配置
创建 config.json(与可执行文件同目录):
{
"auth_token": "你的超级安全Token至少64字符",
"listen_addr": ":8080",
"services": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxx"
}
}
}
}3. 运行
# 最简单的方式 - 自动查找 config.json,自动写日志
./mcp-sse-gateway
# 查看帮助
./mcp-sse-gateway -h命令行参数
| 参数 | 默认值 | 说明 |
|---|---|---|
-config | 自动查找 | 配置文件路径 |
-log-level | info | 日志级别: debug, info, warn, error |
-log-file | ./gateway.log | 日志文件路径 |
-no-log | false | 禁用日志文件输出(仅控制台) |
-no-color | false | 禁用控制台颜色 |
示例
# 开发模式 - debug 级别日志
./mcp-sse-gateway -log-level debug
# 生产模式 - 指定配置和日志路径
./mcp-sse-gateway -config /etc/mcp-gateway/config.json -log-file /var/log/mcp-gateway.log
# 容器模式 - 仅控制台输出,无颜色
./mcp-sse-gateway -no-log -no-color配置文件搜索顺序
如果不指定 -config,会按以下顺序查找:
config.json./config.json./config/config.json/etc/mcp-sse-gateway/config.json
配置文件说明
{
"auth_token": "your-512-bit-secure-token",
"listen_addr": ":8080",
"keepalive_interval": 30,
"services": {
"服务名": {
"command": "执行命令",
"args": ["参数1", "参数2"],
"env": {
"环境变量名": "值"
}
}
}
}| 字段 | 必填 | 默认值 | 说明 |
|---|---|---|---|
auth_token | ✅ | - | Bearer Token,建议 64+ 字符 |
listen_addr | ❌ | :8080 | HTTP 监听地址 |
keepalive_interval | ❌ | 30 | SSE 心跳间隔(秒) |
services | ✅ | - | 服务配置映射 |
services.*.command | ✅ | - | 执行命令(如 npx, python) |
services.*.args | ❌ | [] | 命令参数 |
services.*.env | ❌ | {} | 专属环境变量(与系统环境合并) |
配置示例
{
"auth_token": "mcp-gateway-super-secret-token-please-change-me-to-something-secure",
"listen_addr": ":8080",
"keepalive_interval": 30,
"services": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx"
}
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/documents"],
"env": {}
},
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"],
"env": {}
},
"sqlite": {
"command": "uvx",
"args": ["mcp-server-sqlite", "--db-path", "/data/mydb.sqlite"],
"env": {}
}
}
}API 使用
1. 建立 SSE 连接
curl -N \
-H "Authorization: Bearer your-token" \
http://localhost:8080/sse/github响应(SSE 流):
event: endpoint
data: /sse/github?sessionId=550e8400-e29b-41d4-a716-446655440000
: heartbeat
event: message
data: {"jsonrpc":"2.0","id":1,"result":{...}}2. 发送 JSON-RPC 消息
curl -X POST \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{}}}' \
"http://localhost:8080/sse/github?sessionId=550e8400-e29b-41d4-a716-446655440000"响应:
{"status":"accepted"}实际的 JSON-RPC 响应会通过 SSE 流推送。
工作流程
1. 客户端 GET /sse/{service} → 建立 SSE 连接
2. 网关按需启动对应的 MCP 子进程
3. 网关返回 endpoint 事件,包含 sessionId
4. 客户端 POST /sse/{service}?sessionId=xxx → 发送 JSON-RPC 请求
5. 网关将请求写入子进程 stdin
6. 子进程 stdout 输出 → 封装为 SSE event 推送给客户端
7. 客户端断开 → 网关自动 kill 子进程安全特性
1. Bearer Token 鉴权
所有请求必须携带 Authorization: Bearer 头。
2. 伪装 404
无论是 Token 错误、路径不存在、还是 IP 被封禁,统一返回 HTTP 404:
404 Not Found
Not Found
The requested URL was not found on this server.
这样攻击者无法通过响应码判断攻击是否生效。
3. Fail2Ban(内存版)
- 规则:1 分钟内错误 > 10 次 → 封禁该 IP 1 小时
- 触发条件:Token 错误、缺少 Authorization 头
- 封禁期间:直接返回 404,不进行 Token 比对(防止 CPU 消耗攻击)
4. 常量时间比较
Token 比对使用 crypto/subtle.ConstantTimeCompare,防止时序攻击。
日志说明
日志格式
2026-01-24 10:30:15.123 [INFO ] [SECURITY] [鉴权成功] ip=127.0.0.1 path=/sse/github method=GET日志分类
| 分类 | 说明 |
|---|---|
SYSTEM | 系统启动、关闭 |
CONFIG | 配置加载 |
SECURITY | 鉴权、封禁事件 |
SESSION | 会话创建、断开 |
PROCESS | 子进程启动、停止 |
GATEWAY | HTTP/SSE 消息转发 |
日志级别
debug < info < warn < error
- debug: 详细的消息转发日志
- info: 正常操作日志(默认)
- warn: 安全警告(鉴权失败、封禁)
- error: 错误事件
项目结构
mcp-sse-gateway/
├── cmd/gateway/
│ └── main.go # 程序入口
├── internal/
│ ├── config/
│ │ └── config.go # 配置加载
│ ├── logger/
│ │ └── logger.go # 日志模块
│ ├── security/
│ │ ├── auth.go # Token 鉴权
│ │ └── fail2ban.go # IP 封禁
│ ├── process/
│ │ └── manager.go # 子进程管理
│ └── gateway/
│ ├── server.go # HTTP/SSE 服务
│ └── session.go # 会话管理
├── config.json # 配置文件
├── go.mod
├── go.sum
└── README.md部署建议
Systemd 服务
/etc/systemd/system/mcp-sse-gateway.service:
[Unit]
Description=MCP SSE Gateway
After=network.target
[Service]
Type=simple
User=mcp-gateway
WorkingDirectory=/opt/mcp-sse-gateway
ExecStart=/opt/mcp-sse-gateway/mcp-sse-gateway
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetNginx 反向代理
location /mcp/ {
proxy_pass http://127.0.0.1:8080/;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 86400s;
}Docker
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o mcp-sse-gateway ./cmd/gateway/
FROM alpine:latest
RUN apk add --no-cache nodejs npm
WORKDIR /app
COPY --from=builder /app/mcp-sse-gateway .
COPY config.json .
EXPOSE 8080
CMD ["./mcp-sse-gateway", "-no-color"]License
MIT
