API密钥生命周期管理服务

MCPGate是一个专为MCP服务器和AI代理设计的超快速API密钥生命周期和验证服务,可作为sidecar部署,几分钟内保护AI基础设施。

  • by gatesuite
  • Python
  • 本地部署
  • 更新于
在 GitHub 查看 PatelFarhaan/mcpgate

使用说明

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 始终返回HTTP 200 OK --检查 valid boolean,而不是状态代码。这 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使用 请释放 对于自动版本控制:

  1. 承诺 main 跟随 约定式提交 (feat:, fix:, docs:等等)
  2. 发布请打开一个PR,其中包含版本更新+更改日志条目
  3. 合并PR时,会自动创建一个新标签和GitHub Release
  4. 标签创建触发Docker镜像和Helm chart发布到GHCR
  5. 完整的E2E测试套件在发布任何发布工件之前运行——没有损坏的版本到达用户手中

MIT许可证。看 许可证 了解详情。