使用说明
MCPGate
Ultra-fast API key lifecycle and verification service for MCP servers and AI agents.
Deploy as a sidecar. Secure your AI infrastructure in minutes.
欢迎来到存储库 MCPGate -专门为MCP服务器和AI代理构建的API密钥生命周期和验证服务。此仓库包含MCPGate服务的源代码、Kubernetes的Helm chart、用于本地开发的Docker Compose配置和完整的E2E测试套件。请查看 为什么选择MCPGate? 下面的部分了解更多详细信息。
为什么选择MCPGate?
随着人工智能代理(Claude Desktop、Cursor、定制MCP客户端)越来越多地通过模型上下文协议连接到远程SaaS应用程序,开发人员需要一种方法来向用户发布安全的、限定范围的API密钥。
MCPGate与您现有的人工身份验证提供商并肩而立 (Auth0、职员、Firebase、AuthGate)。而那些处理 人类OAuth,MCPGate手柄 机器授权 -为编程客户端颁发、验证和撤销API密钥。
我们查看了已经存在的内容:
| 解决方案 | 问题 |
|---|---|
| 手卷烟 | 容易出错——bcrypt 72字节截断、密钥解析错误、纯文本泄漏 |
| API网关(香港,Tyk) | 简单用例的繁重、昂贵、复杂的配置 |
| OAuth令牌 | 专为人类登录流而非机器客户端而设计 |
| 静态秘密 | 无轮换、无范围、无撤销、无审计跟踪 |
MCPGate填补了空白 -一个专门构建的API密钥服务,具有条纹式密钥、bcrypt存储、O(1)验证和大约50MB的容器。
MCPGate与自己滚动
| MCPGate | 自己动手 | |
|---|---|---|
| 密钥格式 | mcp_sk_<id>_<secret> --可扫描、明确 |
无论你构建什么 |
| 存储 | SHA-256预散列+bcrypt——默认正确 | 容易出错 |
| 查找速度 | O(1)——数据库中索引的公共ID | 通常为O(n)——全表扫描 |
| 范围 | 每个键上都有JSON blob | 额外的模式工作 |
| 到期 | 内置,每次验证都会检查 | 经常被遗忘 |
| 撤销 | 通过API硬删除 | 各不相同 |
| 审计跟踪 | last_used_at 每次验证时都会更新 |
额外工作 |
| 部署 | 单个容器,包括Helm chart | 你构建它 |
何时使用MCPGate: 您正在构建一个MCP服务器或AI工具,它接受程序化请求,需要一个完整的密钥管理解决方案——发布、验证、范围、到期、审计——而无需自己编写任何内容。
何时不: 您需要OAuth流、SSO、MFA或人工登录UI。使用AuthGate、Auth0、Clerk或类似工具,并在机器密钥旁边运行MCPGate。
目录
特性
- 快速验证 --通过O(1)索引查找进行亚毫秒密钥验证
- 默认安全 --SHA-256预散列+加密;从未存储纯文本密钥
- 条纹风格按键 --可识别格式(
mcp_sk_abc123_xyz987)用户一眼就能认出 - 范围访问 --将任意JSON作用域附加到任何键(
{"read": true, "write": false}) - 过期支持 --可选
expires_in_days在每次验证时强制执行密钥创建 - 审计跟踪 —
last_used_at每次成功验证时都会更新时间戳 - Kubernetes就绪 --带有HPA、PDB、只读文件系统、非root用户的Helm chart
- Docker优先 --多阶段构建,~50MB镜像,docker编写本地开发
- 异步&快速 --基于FastAPI+asyncpg构建,具有完全异步I/O
- 经过全面测试 --E2E测试套件通过GitHub Actions屏蔽每个版本
快速开始
一个命令部署(推荐)
curl -O https://raw.githubusercontent.com/gatesuite/mcpgate/main/docker-compose.yml
export ADMIN_API_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
docker compose up -d
检查一下:
curl http://localhost:8000/health
# {"status": "ok", "service": "mcpgate"}
就这样 没有要创建的配置文件,也没有要克隆的仓库。Postgres被捆绑在同一个堆栈中。已出版 ghcr.io/gatesuite/mcpgate:latest 使用图像。
固定特定版本而不是 latest:
export MCPGATE_VERSION=1.2.3
docker compose up -d
对于贡献者:从源代码构建
git clone https://github.com/gatesuite/mcpgate.git
cd mcpgate
# 1. Create your env file
cp .env.example .env
# Edit .env — set a strong ADMIN_API_KEY
# 2. Start the stack (builds from local Dockerfile)
make app-up
中的编写文件 deployments/docker-compose/ 从以下位置读取凭据 .env 并构建生产Dockerfile目标。
没有Docker
# Prerequisites: Python 3.12+, PostgreSQL
pip install -r requirements.txt
export DATABASE_URL=postgresql+asyncpg://user:pass@localhost:5432/mcpgate
export ADMIN_API_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
uvicorn app.main:app --host 0.0.0.0 --port 8000
环境变量
| 变量 | 必填 | 默认 | 描述 |
|---|---|---|---|
DATABASE_URL |
✅ | — | 异步PostgreSQL类型(postgresql+asyncpg://...) |
ADMIN_API_KEY |
✅ | — | 管理端点的主令牌(创建/列出/撤销) |
KEY_PREFIX |
— | mcp_sk_ |
所有生成的密钥前都添加了前缀 |
PROJECT_NAME |
— | MCPGate |
API响应中的服务名称 |
VERSION |
— | 1.0.0 |
版本字符串 |
API_V1_STR |
— | /api/v1 |
API路由前缀 |
生成强管理员密钥:
python3 -c "import secrets; print(secrets.token_hex(32))"
保持 ADMIN_API_KEY 在生产中的秘密管理器中(AWS秘密管理器、GCP秘密管理器,HashiCorp Vault或Kubernetes秘密)。永远不要承诺。
集成指南
运作原理
User Your App (backend) MCPGate MCP Server
│ │ │ │
│── signup ───────▶│ │ │
│ │── POST /keys ─────▶│ │
│ │◀─ {key: mcp_sk_…} ─│ │
│◀── key ──────────│ │ │
│ │ │ │
│── MCP request ───────────────────────────────────────── ▶│
│ │ │◀─ POST /verify ─│
│ │ │── {valid:true} ▶│
│◀── response ─────────────────────────────────────────────│
第一步——发放密钥
当用户连接MCP客户端时,您的后端会为他们创建一个密钥。这 ADMIN_API_KEY 授权此调用——永远不要将其暴露给浏览器或最终用户。
import httpx
async def create_mcp_key(user_id: str, name: str) -> str:
async with httpx.AsyncClient() as client:
resp = await client.post(
"http://mcpgate:8000/api/v1/keys",
headers={"Authorization": f"Bearer {ADMIN_API_KEY}"},
json={
"user_id": user_id,
"name": name,
"scopes": {"read": True, "write": True},
"expires_in_days": 365,
},
)
return resp.json()["key"] # mcp_sk_... — return to user, never store
这
key字段只返回一次 在创建时。立即将其显示给用户,不要存储它——如果他们丢失了,请发布一个新的。
步骤2——验证每个MCP请求
您的MCP服务器调用 /verify 在处理任何请求之前。端点未经身份验证,并针对高频呼叫进行了优化:
async def verify_mcp_key(key: str) -> dict | None:
async with httpx.AsyncClient() as client:
resp = await client.post(
"http://mcpgate:8000/api/v1/verify",
json={"key": key},
)
data = resp.json()
return data if data["valid"] else None
async function verifyMcpKey(key: string) {
const resp = await fetch("http://mcpgate:8000/api/v1/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ key }),
});
const data = await resp.json();
return data.valid ? { userId: data.user_id, scopes: data.scopes } : null;
}
步骤3——在服务器中强制执行作用域
MCPGate逐字返回作用域——您的MCP服务器决定它们的含义:
async def handle_mcp_request(key: str, tool: str):
auth = await verify_mcp_key(key)
if auth is None:
raise Unauthorized("Invalid API key")
scopes = auth.get("scopes") or {}
if tool in ("write_file", "execute_code") and not scopes.get("write"):
raise Forbidden("Key does not have write scope")
# proceed...
第4步——需要时撤销
当用户断开客户端连接或您检测到密钥泄露时:
curl -X DELETE http://mcpgate:8000/api/v1/keys/{key_id} \
-H "Authorization: Bearer $ADMIN_API_KEY"
撤销后,任何使用该密钥的验证调用都会返回 {"valid": false, "error": "Invalid API key"} 立即。
API 参考
所有管理终结点都需要 Authorization: Bearer <ADMIN_API_KEY> 头球这 /verify 端点是 故意未经身份验证 --它是MCP服务器在每次请求时调用的高频路径。
| 端点 | 方法 | 身份验证 | 描述 |
|---|---|---|---|
/api/v1/keys |
POST | Admin | 创建新的API密钥 |
/api/v1/keys/{user_id} |
GET | Admin | 列出用户的所有密钥 |
/api/v1/keys/{key_id} |
DELETE | Admin | 撤销并删除密钥 |
/api/v1/verify |
POST | 无 | 验证传入密钥 |
/health |
GET | 无 | 健康检查 |
创建密钥
curl -X POST http://localhost:8000/api/v1/keys \
-H "Authorization: Bearer $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"user_id": "usr_12345",
"name": "Claude Desktop",
"scopes": {"read": true, "write": false},
"expires_in_days": 365
}'
答复:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"key": "mcp_sk_3f8a1c2e9b4d7f6a_4a7b2c9e...",
"prefix": "mcp_sk_3f8a1c2e9b4d7f6a_...9b",
"user_id": "usr_12345",
"name": "Claude Desktop",
"created_at": "2026-04-16T10:00:00Z"
}
验证密钥
curl -X POST http://localhost:8000/api/v1/verify \
-H "Content-Type: application/json" \
-d '{"key": "mcp_sk_3f8a1c2e9b4d7f6a_4a7b2c9e..."}'
成功响应:
{
"valid": true,
"user_id": "usr_12345",
"scopes": {"read": true, "write": false},
"error": null
}
故障响应:
{ "valid": false, "user_id": null, "scopes": null, "error": "Invalid API key" }
注:
/verify始终返回HTTP200 OK--检查validboolean,而不是状态代码。这error字段包含原因("Invalid key format","Invalid API key",或"Key expired").
列出用户密钥
curl http://localhost:8000/api/v1/keys/usr_12345 \
-H "Authorization: Bearer $ADMIN_API_KEY"
返回一个按最新顺序排列的密钥数组(不包括纯文本密钥)。
撤销密钥
curl -X DELETE http://localhost:8000/api/v1/keys/{key_id} \
-H "Authorization: Bearer $ADMIN_API_KEY"
退货 204 No Content这是一个难以删除的操作——关键记录将从数据库中永久删除。
管理密钥
查找过期密钥
MCPGate不会自动删除过期的密钥——验证会拒绝它们,但行会保留在数据库中。定期清理:
-- List expired keys
SELECT id, user_id, prefix, expires_at
FROM api_keys
WHERE expires_at < NOW();
-- Bulk delete expired keys older than 30 days
DELETE FROM api_keys
WHERE expires_at < NOW() - INTERVAL '30 days';
查找未使用的密钥
从未使用过或长时间未使用过的密钥是轮换或撤销的候选者:
-- Keys never used
SELECT id, user_id, prefix, created_at
FROM api_keys
WHERE last_used_at IS NULL
AND created_at < NOW() - INTERVAL '30 days';
-- Keys unused in the last 90 days
SELECT id, user_id, prefix, last_used_at
FROM api_keys
WHERE last_used_at < NOW() - INTERVAL '90 days';
表面 last_used_at 在面向用户的密钥管理UI中,这样用户就可以自己识别过时的密钥。
旋转钥匙
没有就地旋转——发出一个新密钥,将其交给用户,然后删除旧密钥:
# 1. Create a replacement
new_key = await create_mcp_key(user_id="usr_12345", name="Claude Desktop (rotated)")
# 2. Deliver new_key to the user
# 3. Delete the old key (fetch its ID via GET /keys/{user_id} first)
await client.delete(f"http://mcpgate:8000/api/v1/keys/{old_key_id}",
headers={"Authorization": f"Bearer {ADMIN_API_KEY}"})
审核密钥使用情况
这 last_used_at 列是您最简单的审计工具。为了获得更丰富的分析,请通过日志管道运行MCPGate的访问日志——每个 POST /api/v1/verify 通话已被记录。
部署
Docker Compose(本地开发)
make app-up # builds production image, starts MCPGate + Postgres
make app-logs # tail logs
make app-down # stop (volumes preserved)
Kubernetes (Helm 通过 OCI)
第一步:创建秘密
kubectl create secret generic mcpgate-secrets \
--from-literal=DATABASE_URL="postgresql+asyncpg://user:pass@host:5432/mcpgate" \
--from-literal=ADMIN_API_KEY="$(openssl rand -hex 32)"
步骤2:安装图表
helm install mcpgate oci://ghcr.io/gatesuite/charts/mcpgate \
--set existingSecret=mcpgate-secrets
或者使用 values.yaml 以(权力)否决
helm install mcpgate oci://ghcr.io/gatesuite/charts/mcpgate -f my-values.yaml
要改为从源代码安装,请执行以下操作:
helm install mcpgate ./deployments/helm/mcpgate -f my-values.yaml
生产默认值包括: HPA(1-10个副本,CPU/内存扩展),拓扑分布,只读根文件系统,非根用户,启动/活性/就绪探测,零停机滚动更新。
Kubernetes(原始清单)
如果你更喜欢原始清单而不是Helm,这里有一个最小的生产设置:
apiVersion: v1
kind: Secret
metadata:
name: mcpgate-secrets
type: Opaque
stringData:
DATABASE_URL: postgresql+asyncpg://user:pass@postgres:5432/mcpgate
ADMIN_API_KEY: your-strong-secret-here
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcpgate
spec:
replicas: 2
selector:
matchLabels: { app: mcpgate }
template:
metadata:
labels: { app: mcpgate }
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: mcpgate
image: ghcr.io/gatesuite/mcpgate:latest
ports:
- containerPort: 8000
envFrom:
- secretRef:
name: mcpgate-secrets
livenessProbe:
httpGet: { path: /health, port: 8000 }
readinessProbe:
httpGet: { path: /health, port: 8000 }
resources:
requests: { cpu: 50m, memory: 64Mi }
limits: { cpu: 250m, memory: 128Mi }
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
---
apiVersion: v1
kind: Service
metadata:
name: mcpgate
spec:
selector: { app: mcpgate }
ports:
- port: 8000
targetPort: 8000
根据需要通过您的入口控制器(NGINX、Traefik、云负载均衡器)进行公开。MCPGate永远不应该暴露在公共互联网上——把它放在只能由后端和MCP服务器访问的私有服务网格上。
制定目标
| 命令 | 描述 |
|---|---|
make app-up |
从源代码构建并启动MCPGate+Postgres |
make app-down |
停止MCPGate(保留卷) |
make app-logs |
MCPGate集装箱尾部日志 |
make docs-up |
启动文档开发服务器(端口4321) |
make docs-down |
停止文档容器 |
make test-run |
运行E2E测试(pytest+真实Postgres) |
make test-down |
停止测试容器 |
make clean |
擦除容器、卷、缓存 |
技术栈
| 层 | 技术 |
|---|---|
| 框架 | FastAPI(异步Python) |
| 数据库 | PostgreSQL+asyncpg+SQLAlchemy |
| 哈希 | bcrypt+SHA-256预哈希 |
| 验证 | Pydantic v2 |
| 容器 | Docker(多级,~50MB) |
| 编排 | Helm/Docker编写 |
| CI/CD | GitHub操作+发布请 |
| 测试 | pytest+pytest-asyncio+httpx |
建筑
Kubernetes部署
graph TB
Backend["🖥️ Your Backend"]
MCPServer["🤖 Your MCP Server"]
subgraph EKS Cluster
subgraph Private["Private (ClusterIP Services)"]
MG["MCPGate<br/>ClusterIP :8000"]
PG["PostgreSQL<br/>ClusterIP :5432"]
end
end
Backend -->|"Admin API<br/>(create/list/revoke)"| MG
MCPServer -->|"Verify on every request<br/>POST /verify"| MG
MG -->|"Async"| PG
style MG fill:#10b981,stroke:#047857,color:#fff
style PG fill:#6366f1,stroke:#4338ca,color:#fff
style Backend fill:#3b82f6,stroke:#1d4ed8,color:#fff
style MCPServer fill:#f59e0b,stroke:#d97706,color:#fff
密钥验证流程
sequenceDiagram
actor User
participant Client as MCP Client (Claude Desktop, etc.)
participant MCP as Your MCP Server
participant MG as MCPGate
participant DB as PostgreSQL
User->>Client: Use MCP tool
Client->>MCP: Request with API key header
MCP->>MG: POST /api/v1/verify { key: "mcp_sk_..." }
MG->>MG: Extract public_id from key
MG->>DB: SELECT WHERE public_id = ? AND is_active = true
DB-->>MG: Return key record
MG->>MG: bcrypt.checkpw(sha256(key), stored_hash)
MG->>MG: Check expires_at
MG->>DB: UPDATE last_used_at = now()
MG-->>MCP: { valid: true, user_id, scopes }
MCP->>MCP: Check scopes, enforce permissions
MCP-->>Client: Tool result
Client-->>User: Display result
项目结构
mcpgate/
├── app/
│ ├── main.py # FastAPI entrypoint, lifespan, CORS
│ ├── api/
│ │ └── routes.py # All 5 endpoints: create, list, delete, verify, health
│ ├── core/
│ │ ├── config.py # Settings via pydantic-settings + .env
│ │ ├── database.py # Async SQLAlchemy engine + session factory
│ │ └── security.py # Key generation, SHA-256 pre-hash, bcrypt hash/verify
│ ├── models/
│ │ └── api_key.py # ApiKey SQLAlchemy ORM model
│ └── schemas/
│ └── api_key.py # Pydantic request/response schemas
├── tests/
│ ├── conftest.py # Fixtures: client, admin_headers, DB setup/teardown
│ ├── test_health.py # Health endpoint
│ ├── test_keys.py # Create, list, revoke (with/without auth)
│ └── test_verify.py # Valid, invalid, expired, revoked, tampered keys
├── deployments/
│ ├── docker-compose/ # Dev compose (builds from local Dockerfile)
│ └── helm/mcpgate/ # Kubernetes Helm chart
├── docs/ # Astro/Starlight documentation site
├── .github/workflows/ # CI/CD: release-please, e2e, docker-publish, docs
├── Dockerfile # Multi-stage: production + test targets
├── docker-compose.yml # End-user zero-config deploy (pulls from GHCR)
├── docker-compose.test.yml # Test runner (make test-run)
└── Makefile # app-*, docs-*, test-* targets
安全
- SHA-256+加密存储 --从未存储的纯文本密钥;不可逆单向散列
- 每键独特的盐 --相同的键在数据库中产生不同的哈希值
- 恒定时间验证 —
bcrypt.checkpw在热路径上防止定时攻击 - 撤销时硬删除 --没有可能意外重新启用的软删除状态
- 非根容器 --在Docker和Kubernetes中以无特权用户身份运行
- 只读根文件系统 --容器在运行时无法修改自己的映像
- 管理员密钥分离 --主令牌从不接触验证热路径;
/verify未经设计验证 - 威胁模型和强化 --看看 安全文件 完整的威胁模型和生产清单
通过以下方式私下报告漏洞 GitHub安全公告.
发布过程
MCPGate使用 请释放 对于自动版本控制:
- 承诺
main跟随 约定式提交 (feat:,fix:,docs:等等) - 发布请打开一个PR,其中包含版本更新+更改日志条目
- 合并PR时,会自动创建一个新标签和GitHub Release
- 标签创建触发Docker镜像和Helm chart发布到GHCR
- 完整的E2E测试套件在发布任何发布工件之前运行——没有损坏的版本到达用户手中
MIT许可证。看 许可证 了解详情。
