MCP认证原型
安全 模型上下文协议 (MCP)服务器演示企业级访问控制模式。用Python和 FastMCP v2,该原型实现了基于JWT的身份验证和基于范围的工具授权,专为在Google Kubernetes Engine上部署而设计。
这表明了什么
- 基于令牌的身份验证:每个MCP请求都需要一个有效的JWT承载令牌
- 基于范围的授权:令牌范围决定客户端可以查看和调用哪些工具
- 纵深防御:工具列表筛选和工具调用验证(两个独立的检查)
- 结构化审计日志记录:每个身份验证决策都以JSON格式记录在云日志系统中
- 12因素配置:通过环境变量进行所有设置
- Kubernetes就绪:健康和准备状态探测端点
建筑
Client (Claude Code, MCP client)
│
│ Authorization: Bearer
▼
┌─────────────────────────────────┐
│ FastMCP Server (port 8080) │
│ │
│ ┌───────────────────────────┐ │
│ │ AuthMiddleware │ │
│ │ 1. Extract Bearer token │ │
│ │ 2. Validate JWT (sig+exp)│ │
│ │ 3. Filter tools by scope │ │
│ │ 4. Block unauthorized │ │
│ └───────────────────────────┘ │
│ │
│ ┌───────────┐ ┌─────────────┐ │
│ │ get_public│ │get_confiden-│ │
│ │ _info │ │tial_info │ │
│ │ │ │ │ │
│ │ scope: │ │ scope: │ │
│ │ public: │ │ confidenti- │ │
│ │ read │ │ al:read │ │
│ └───────────┘ └─────────────┘ │
│ │
│ /health /ready /mcp │
└─────────────────────────────────┘访问控制矩阵
| 令牌范围 | 可见工具 | 可以调用 |
|---|---|---|
["public:read"] | get_public_info 只有 | get_public_info 只有 |
["public:read", "confidential:read"] | 两种工具 | 两种工具 |
[] | 无 | 无 |
| 无令牌/过期/无效 | 拒绝(AuthError) | 拒绝(AuthError) |
快速开始
先决条件
- Python 3.11+
- 紫外线 包管理器
安装并运行
# Install dependencies
uv sync
# Start the server
uv run python -m src.server服务器启动于 http://localhost:8080 与:
- MCP端点:
POST /mcp(流式HTTP传输) - 健康检查:
GET /health - 准备状态检查:
GET /ready
生成令牌
# Public access only
uv run python -m scripts.generate_token --sub alice --scope public:read
# Full access
uv run python -m scripts.generate_token --sub bob --scope public:read confidential:read
# Expired token (for testing rejection)
uv run python -m scripts.generate_token --sub charlie --scope public:read --exp-hours -1注: 令牌必须使用服务器使用的相同密钥进行签名。默认情况下,两者 使用dev-secret-change-me如果您使用自定义密钥运行服务器(例如。,MCP_JWT_SECRET_KEY=my-secret),您必须生成具有匹配项的令牌--secret标志: ``bash uv run python -m scripts.generate_token --sub alice --scope public:read --secret my-secret``
与克劳德代码连接
# Generate a token
TOKEN=$(uv run python -m scripts.generate_token --sub myuser --scope public:read confidential:read 2>&1 | grep "^Token:" | cut -d' ' -f2)
# Add the MCP server to Claude Code
claude mcp add --transport http mcp-auth-prototype http://localhost:8080/mcp \
--header "Authorization: Bearer $TOKEN"卷曲测试
# Generate a token
TOKEN=$(uv run python -m scripts.generate_token --sub alice --scope public:read 2>&1 | grep "^Token:" | cut -d' ' -f2)
# Initialize MCP session
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer $TOKEN" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'发展
# Run tests
uv run pytest -v
# Lint
uv run ruff check .码头工人
在部署到Kubernetes之前,在本地构建和测试容器映像。
塑造形象
# Build the Docker image
docker build -t mcp-auth-prototype:local .多阶段构建创建了一个仅包含运行时依赖项的最小~150MB映像。
运行容器
# Run with a custom JWT secret (required for production)
docker run -p 8080:8080 -e MCP_JWT_SECRET_KEY=my-secret mcp-auth-prototype:local
# Run with debug logging
docker run -p 8080:8080 \
-e MCP_JWT_SECRET_KEY=my-secret \
-e MCP_LOG_LEVEL=debug \
mcp-auth-prototype:local测试容器
# Verify health endpoint
curl http://localhost:8080/health
# Verify readiness endpoint
curl http://localhost:8080/ready
# Generate a token (must use --secret matching the container's MCP_JWT_SECRET_KEY)
TOKEN=$(uv run python -m scripts.generate_token --sub alice --scope public:read --secret my-secret 2>&1 | grep "^Token:" | cut -d' ' -f2)
# Test MCP initialization against the container
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer $TOKEN" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'项目结构
mcp-auth-prototype/
├── src/
│ ├── server.py # MCP server, auth middleware, health endpoints
│ ├── auth.py # JWT validation and scope extraction
│ ├── tools.py # Tool-to-scope mapping registry
│ └── config.py # Environment-based configuration (pydantic-settings)
├── documents/
│ ├── public.md # Sample public company document
│ └── confidential.md # Sample confidential strategy document
├── scripts/
│ └── generate_token.py # CLI utility to mint JWT tokens
├── tests/
│ ├── conftest.py # Shared test fixtures (token factories)
│ ├── test_auth.py # Unit tests for JWT validation (16 tests)
│ └── test_tools.py # Integration tests for tool authorization (6 tests)
├── helm/
│ └── mcp-server/ # Helm chart for Kubernetes deployment
│ ├── Chart.yaml # Chart metadata (name, version)
│ ├── values.yaml # Default configuration values
│ ├── values-dev.yaml # Dev environment overrides
│ └── templates/
│ ├── _helpers.tpl # Reusable Go template helpers
│ ├── deployment.yaml # Deployment (2 replicas, probes, env vars)
│ ├── service.yaml # ClusterIP Service on port 8080
│ ├── configmap.yaml # Document content (public.md, confidential.md)
│ ├── serviceaccount.yaml # K8s ServiceAccounts with Workload Identity
│ ├── secretstore.yaml # ESO connection to GCP Secret Manager
│ └── externalsecret.yaml # Syncs JWT key from GCP to K8s Secret
├── terraform/ # Infrastructure as Code
│ ├── main.tf # Provider and backend configuration
│ ├── variables.tf # Input variables
│ ├── outputs.tf # Output values
│ ├── gke.tf # GKE cluster definition
│ ├── artifact-registry.tf # Container registry
│ ├── secret-manager.tf # Secret Manager resources
│ ├── iam.tf # Service accounts and IAM bindings
│ └── github-wif.tf # Workload Identity Federation for GitHub Actions
├── .github/
│ └── workflows/
│ └── ci.yaml # CI pipeline (lint, test, build, push, update Helm)
├── argocd/
│ └── application.yaml # ArgoCD Application (GitOps auto-sync)
├── pyproject.toml # Dependencies and tool configuration
└── uv.lock # Locked dependency versionsCI/CD管道
每一次推动 main 触发自动管道:
git push ──▶ GitHub Actions ──▶ ArgoCD ──▶ GKE Cluster
│ │
├─ Lint (ruff) ├─ Detects values.yaml change
├─ Test (pytest) ├─ Renders Helm chart
├─ Build image └─ Rolling update (zero downtime)
├─ Push to Artifact Registry (git SHA tag)
└─ Update helm/mcp-server/values.yaml- 没有存储凭据:GitHub Actions通过工作负载身份联合会(OIDC令牌交换)向GCP进行身份验证
- 不可变图像标签Docker镜像被标记为git commit SHA(例如。,
a1b2c3d),不latest - GitOps:ArgoCD不断地将集群状态与Git中的状态进行协调,包括在有人手动修改集群时进行自我修复
设计决策
文档存储:ConfigMap(原型)与生产替代方案
在此原型中,文档内容(public.md, confidential.md)直接内联在Helm图表中的Kubernetes ConfigMap中。这在这里是合适的,因为:
- 我们只有2个小型静态文档(总共约1KB)
- 它使Helm chart保持独立且易于理解
- 赫尔姆
.Files.Get函数无法读取图表目录外的文件
这种方法无法扩展。 ConfigMgr限制为1MB,文档更改需要完整的Helm升级(这会触发pod滚动更新),并且没有版本控制或独立的生命周期管理。
文件密集型系统的生产替代方案:
| 模式 | 何时使用 | 如何工作 |
|---|---|---|
| 对象存储(GCS/S3) | 最常见。独立的文档生命周期,许多文档 | 应用程序在运行时通过Workload Identity从云存储桶中获取。支持版本控制、CDN、细粒度IAM。 |
| 数据库(PostgreSQL/Firestore) | 文档需要元数据、搜索、关系 | 应用程序根据请求查询数据库。完整的CRUD、索引、事务处理。 |
| Git仓库+sidecar | GitOps繁重的组织,文档作为代码 | sidecar/init容器克隆了一个单独的文档仓库。Git的版本历史记录,供审查的PR。 |
| 内容API微服务 | 大规模、多消费者 | 专用服务管理文档。MCP服务器变成了一个精简的编排层。 |
关键原则: 将文档生命周期与应用程序生命周期解耦。 MCP服务器应可独立于内容更新进行部署。
配置
所有设置都是从环境变量中读取的 MCP_ 前缀:
| 变量 | 默认值 | 描述 |
|---|---|---|
MCP_HOST | 0.0.0.0 | 要绑定的网络接口 |
MCP_PORT | 8080 | 服务器端口 |
MCP_LOG_LEVEL | info | 记录冗长(debug, info, warning, error) |
MCP_JWT_SECRET_KEY | dev-secret-change-me | JWT签名密钥(在生产中覆盖) |
MCP_JWT_ALGORITHM | HS256 | JWT签名算法 |
MCP_DOCUMENTS_DIR | documents | 文档文件的路径 |
您还可以在 .env 文件(gitignored)。
技术栈
| 组件 | 技术 | 目的 |
|---|---|---|
| MCP服务器 | FastMCP v2 | 带中间件挂钩的MCP协议 |
| 身份验证 | PyJWT | JWT令牌验证 |
| 配置 | 媒染剂设置 | 键入环境变量配置 |
| HTTP服务器 | Uvicorn | ASGI服务器 |
| 测试 | pytest+httpx | 单元和集成测试 |
| Linting | 拉夫 | 快速Python linter |
| 包管理器 | 紫外线 | 快速Python包管理器 |
| 基础设施 | 地形 | 基础设施作为GCP资源的代码 |
| 容器注册表 | GCP工件注册表 | Docker镜像存储 |
| 编排 | 谷歌Kubernetes引擎 | 容器编排 |
| 秘密 | GCP秘密管理器+ESO | 安全秘密管理 |
| CI | 自动剥皮、测试、构建、推送 | |
| CD | ArgoCD | GitOps持续部署 |
| CI→GCP身份验证 | 工作负载身份联合 | 基于OIDC的身份验证,无存储密钥 |
路线图
看 实施_加载图.md 完整的建造计划。当前状态:
- \[x\] 第0阶段:项目脚手架
- \[x\] 第一阶段:配备工具的MCP服务器
- \[x\] 第2阶段:身份验证和授权
- \[x\] 第3阶段:测试
- \[x\] 第四阶段:Docker化
- \[x\] 第五阶段:GCP基础设施+地形+GKE
- \[x\] 第6阶段:Helm图表
- \[x\] 第7阶段:GitHub操作CI管道
- \[x\] 第8阶段:ArgoCD
- \[x\] 第9阶段:端到端验证
- \[\]第10阶段:TLS入口(HTTPS)——入口控制器、证书管理器、Let’s Encrypt、加密外部访问
- \[\]第11阶段:OAuth2令牌服务——通过Google OAuth2、开发人员CLI、Claude Code集成发行生产令牌
- \[\]第12阶段:自动缩放和弹性——HPA、集群自动缩放、PDB、负载平衡、使用Locust进行负载测试
