MCP Gateway
MCP Gateway — 阶段 1(KLIP,只读)
一个单一的安全服务,让经授权的 Energi-Up 员工通过 Claude 以自然语言查询 KLIP, 而无需打开应用程序。严格只读。
部署目标(PRD Q3,现已关闭):<gateway-hostname> -> <gateway-public-ip>
(ECS-MCP,ap-southeast-5)。请注意主机名是 mcp-gw,而不是 v0.9 文档所假设的
mcp.example.com;PRD/TSD 应相应更新。
文档: PRD v0.9 · TSD v0.9 · 实施指南 · 设计评审 · 部署手册
1. 固定版本(T-1)
MCP 规范修订版和 SDK 版本在此固定。当 SDK 自己的文档与设计文档不一致时,以 SDK 为准——它定义了确切的 API。
组件 | 固定版本 | 备注 |
| 1.30.0(精确版本,无脱字符) | 发布于 2026 年 7 月 27 日。提供 OAuth AS、Streamable HTTP 传输和 |
MCP 协议修订版 | 2025-11-25 已实现;线格式与 | 修订版 |
Node.js | 22 LTS ( | |
TypeScript | 5.9.3, | |
express | 5.2.1 | 从 TSD 的 4.x 提升而来:SDK 依赖 |
zod | 4.4.3 | 从 TSD 的 3.x 提升而来:SDK 的类型定义面向 zod 4。注意 |
jose | 6.2.9 | RS256 网关令牌。 |
| 2.1.0 | 替代 |
axios | 1.19.0 | KLIP 客户端,位于方法守卫之后。 |
pg | 8.23.0 | |
PostgreSQL | 16 (container) | |
nginx | nginx.org mainline | 配置位于 |
Related MCP server: Snowflake MCP Server
2. 布局
src/
core/ config, logger, db, audit, cache, rateLimit, semaphore, migrate, errors
adapters/klip/ routes(APPENDIX A) · fields(APPENDIX A) · client(guard) · session · paginate · normalize
tools/klip/ 9 tool definitions + shared parameter plumbing
mcp/ server, envelope, runner
auth/ keys, hub(OIDC RP), users, clients, tokens, provider, loginPage
http/ app, consent(Hub + break-glass), health, origin, clientIp
migrations/ idempotent SQL (001 schema, 002 Hub OIDC)
deploy/ nginx config, backup sidecar
test/ 120 tests + mock KLIP and mock Hub fixtures分层规则(T-3):tools → adapters → core。除入口点外,没有任何模块导入 http/。所有业务规范化都位于 adapters/klip/normalize.ts 中,该文件不导入任何内容,因此可以独立进行单元测试。
3. 身份验证 — Downstream Hub OIDC
试点用户使用 Downstream Hub(OIDC)登录。网关仍然是 Claude 与之通信的授权服务器;Hub 是其自身 /authorize 流程中的一个步骤。
我们特意不使用 SDK 的 ProxyOAuthServerProvider。代理会把 Hub 令牌交给 Claude,这会破坏 RFC 8707 的受众绑定,让 Claude 获得比 klip:read 更广泛的 Hub 作用域,并将令牌签发移出我们的控制,从而使 S8 紧急停止开关无法再使活动会话失效。
Claude ──/authorize──▶ gateway ──302──▶ Downstream Hub ──302──▶ /authorize/hub/callback
│ │
│ validate id_token (sig/iss/aud/nonce) │
│ check the pilot ALLOWLIST │
◀──────────────────────────────────────────────┘
└──302 code──▶ Claude ──/token──▶ gateway token (klip:read)身份验证不是授权。 Hub 证明某人的身份;users 表决定他们是否可以使用连接器。阶段 1 使用一个共享的 KLIP 服务账户,因此每个被接纳的用户都可以读取 MCP_READONLY 能读取的所有内容(评审 H8)——试点成员资格就是数据访问控制。不在列表中的 Hub 账户会收到 403,<= 15 的上限由 user:add 强制执行。
该流程中运行两次 PKCE 交换;不要混淆它们。Claude 自己的 code_challenge 保护 Claude→网关这一段(由 SDK 处理);网关在服务端保存的单独验证器保护网关→Hub 这一段。
使用哪个 Hub 客户端?网关自己的——而不是 KLIP 的
为 MCP Gateway 注册一个新的 OIDC 客户端。不要复用 KLIP 的 Hub 注册,即使 KLIP 已经在测试 DWS Hub 中注册。
Hub 只涉及两个信任边界之一:
信任边界 | 凭据 | 是否涉及 Hub? |
Claude → gateway(谁在提问) | 网关自己的 Hub 客户端 + 试点允许列表 | 是 |
gateway → KLIP(读取数据) |
| 否——PRD §7 明确排除 |
复用 KLIP 的客户端会以具体方式破坏第一个边界。网关根据其自身的 HUB_CLIENT_ID 验证 ID 令牌的 aud;共享 KLIP 的客户端 ID 意味着在 KLIP 登录期间签发的 ID 令牌会被网关接受,这正是 confused-deputy 的形态。独立的客户端才能使两个依赖方可区分。这还使重定向 URI 允许列表、客户端密钥、轮换计划、Hub SSO 审计条目和禁用开关相互独立——关闭连接器的 Hub 客户端绝不能导致 KLIP 登录中断。
这些都不需要在 KLIP 侧做任何更改。K1–K4 不受影响。
两个 Hub 实例,两次注册
在每个 Hub 中分别注册网关,并将其与匹配的 KLIP 配对:
阶段 | KLIP_ENV | HUB_ISSUER | 客户端 |
4–6(构建、暂存 UAT) |
| testing DWS Hub | testing Hub 中的网关客户端 |
7+(生产切换) |
| production Hub | production Hub 中一个独立的网关客户端 |
该配对在启动时强制执行,因为在一个方向上弄错是危险的,而不仅仅是混乱:
KLIP_ENV=production+ testingHUB_ISSUER→ 网关拒绝启动。 否则,任何能够创建测试 Hub 账户的人都可能接触到真实的商业数据。KLIP_ENV=staging+ productionHUB_ISSUER→ 发出警告并继续。
hub:check 会打印它检测到的配对,因此切换是可验证的:
pairing: KLIP staging <-> Hub testingDWS Hub 的要求(它不是标准 OIDC)
依据 Docs/SSO-TARGET-APP-INTEGRATION.md。其中四项与 OIDC 客户端库默认假设不同,三项会直接失败:
DWS Hub | |
客户端类型 | public,PKCE S256 — |
发现 |
|
令牌正文 | JSON;表单编码返回 |
作用域 | 仅 |
| 在令牌请求中为必填,且必须逐字节完全匹配 |
因此,HUB_DISCOVERY_URL 被显式配置,HUB_CLIENT_SECRET 是可选的,HUB_TOKEN_BODY 默认为 json,并在 unsupported_grant_type 时一次性回退到表单(记录哪种方式有效,以便固定下来)。
设置
在 Hub 中将网关注册为 OIDC 客户端,重定向 URI 为
<PUBLIC_URL>/authorize/hub/callback。它是一个公共客户端——不要索取密钥。将
HUB_ISSUER、HUB_DISCOVERY_URL和HUB_CLIENT_ID放入/opt/mcp/.env。在任何试点用户尝试之前进行验证:
docker compose exec -T gateway node dist/cli.js hub:check这会打印要注册的重定向 URI,运行发现,并在 Hub 未公布 S256 PKCE 时发出警告。网关还会在启动时探测发现,并在 /healthz 中将其报告为 hub_oidc。
添加试点用户(无密码——由 Hub 进行身份验证):
docker compose exec -T gateway node dist/cli.js user:add someone@example.com "Their Name"应急账户
只允许一个本地密码账户,用于 Hub 宕机或配置错误时:
docker compose exec -T gateway node dist/cli.js user:add-break-glass it-emergency@example.com它隐藏在登录页面的披露项后面,首次使用时强制更改密码,并且每次通过它登录都会以高严重级别记录审计,标记为 break_glass: true。Hub 认证的用户完全无法使用密码路径,因此关闭 Hub 并不是回退到无人设置的密码的方式。
一旦 Hub 路径在生产环境中得到验证,请设置 BREAK_GLASS_ENABLED=false,以彻底移除密码面。
可选组门控 — DWS Hub 上不可用
HUB_REQUIRED_GROUP 会针对 groups 声明增加第二重检查。DWS Hub 不签发该声明(它只通告 openid profile email),因此设置它将拒绝所有用户。hub:check 会在您这样做时发出警告。users 表中的试点白名单仍然是授权控制手段。
IdP 发起的登录
Hub 可以从其仪表板磁贴将用户直接推送到回调。这对连接器来说行不通:回调的存在是为了完成 Claude 发起的授权请求,因此没有授权请求到达时,就没有可据以签发 code 的对象。网关会检测到这种情况并提示"请从 Claude 开始",而不是以"登录已过期"的方式失败。
4. 快速开始(本地)
npm ci
docker run -d --name mcpgw-devdb -e POSTGRES_DB=gateway -e POSTGRES_USER=gateway \
-e POSTGRES_PASSWORD=devpassword -p 127.0.0.1:55432:5432 postgres:16-alpine
cp .env.example .env.dev # then edit: PUBLIC_URL=http://localhost:8787, DATABASE_URL=...55432...
npx tsx test/fixtures/mockKlip.ts 5099 & # mock KLIP, behaves like the real one
set -a; . ./.env.dev; set +a
npm run migrate
npx tsx src/index.ts创建试点用户(适用于 TTY 或管道输入):
printf 'a-strong-password\na-strong-password\n' | npx tsx src/cli.ts user:add you@example.com "Your Name"5. 测试
npm test测试套件 | 覆盖范围 |
| Incoterm × 状态 × null 矩阵、kg→MT、舍入顺序、负未结数量、WIB 时间戳 |
| T-6 的穷举方法/路径表、目录穿越与源逃逸 |
| T-5 信封、截断 |
| 有界抓取发布 |
| 针对模拟 KLIP 的全部 9 个工具;非 GET 请求绝不触达 KLIP;401 重新登录; |
| RFC 8707 受众绑定:仅接受此服务器的规范资源 |
| S5 脱敏 — 对字符串激进,对数字无作用 |
| 针对模拟提供方的 Hub OIDC:发现文档签发者不匹配、外部签名密钥、错误的 issuer/audience、过期令牌、缺失和重放的 nonce、无 email 声明 |
|
|
| 生产 KLIP 配测试 Hub 拒绝启动;正常配对不受影响 |
| 从发现文档中选择令牌端点认证方法,包括仅 POST 和公共客户端 Hub |
| 精确建模 DWS Hub: |
模拟 Hub 是一个可工作的迷你 OIDC 提供方 — 真实的发现文档、真实的 JWKS、真实的 RS256 ID 令牌,以及授权码上的 PKCE 验证 — 并配备伪造坏令牌所需的每一个旋钮,因为负面用例才是重点。
模拟 KLIP 夹具刻意复现了真实系统的怪癖:以 MT 标注的千克、混合语言状态、标准四种之外的 incoterm、null 数量、超量交付的合同、被静默钳制为 100 的 limit,以及携带提示注入载荷的合同备注。
6. 部署
完整的主机特定流程,包含真实 IP、主机名和安全组表:deploy/RUNBOOK.md。
# on ECS-MCP
cd /opt/mcp && git pull
docker compose build gateway && docker compose up -d
curl -fsS http://127.0.0.1:8787/healthz管理 CLI — 请注意它在容器内部运行,因为主机仅安装 Docker,没有 Node.js:
docker compose exec -T gateway node dist/cli.js user:list
docker compose exec -T gateway node dist/cli.js audit:summary --days 7
docker compose exec -T gateway node dist/cli.js audit:export --from 2026-08-01 --to 2026-09-01 --out /tmp/audit.csv
docker compose exec -T gateway node dist/cli.js routes:verify # probes KLIP, reports Appendix A gaps紧急开关(S8) — 目标在 5 分钟内:
docker compose exec -T gateway node dist/cli.js tokens:revoke-all --reason "incident 2026-xx"
docker compose stop gateway应用容器不健康时的破窗操作:
docker compose exec -T db psql -U gateway -d gateway -c "UPDATE oauth_tokens SET revoked_at=now() WHERE revoked_at IS NULL;"7. 附录 A 是硬性门禁
src/adapters/klip/routes.ts 和 src/adapters/klip/fields.ts 保存着适配器所依赖的每一个 KLIP 路径、查询参数名、页大小上限、响应字段名和枚举值。当前每一项均未验证。
该门禁是可执行的,而非文书性的:当 KLIP_ENV=production 时,只要有任何路由未验证,进程拒绝启动。针对 staging 运行 routes:verify,记录结果,为每条路由设置 verified: true 并设置 enums.verified = true。
有两个字段比其他字段更重要:
maxLimit— KLIP 实际接受的最大limit。如果它静默钳制为 100,那么KLIP_PAGE_SIZE为 1000 时一页会变成十页,从而破坏延迟目标。enums.*— 规范的状态和 incoterm 值。任何未映射的值都会从总计中排除并附上数据质量说明,绝不使用默认值。
8. 与 TSD v0.9 的偏差
每一项都来自设计评审,并在其调用点处有注释。
# | 变更 | 原因 |
B3 | 仅当 Origin 存在且无效时才拒绝 | 规范仅要求对存在且无效的 Origin 返回 403。Claude 以服务器对服务器方式调用连接器,可能不发送 Origin;若对缺失的 Origin 也拒绝,则每次工具调用都会返回 403。 |
B4 |
| RFC 8707 audience 绑定。在 PRM 文档中增加了 |
B5 | 无状态传输;身份信息来自每个请求中的令牌 | 2026-07-28 修订版移除了协议会话。T-4 通过构造即满足——不存在可劫持的会话。 |
B6 | 熔断开关运行 | 宿主机没有 Node.js,因此 |
B7 | nginx 是反滥用基线;按用户限流以 OAuth | 所有流量均来自 Anthropic 的共享出口网段,因此按 IP 限流会把整个试点项目放入同一个桶中。增加了 |
H1 | OAuth 基于 SDK 的 | SDK 自带 AS,包括吊销和默认限流。只有存储和用户认证是我们自己的。 |
H2 | 下游 Hub OIDC 是登录路径,另设一个应急本地账户 | 从阶段 2 提前引入。Hub 负责认证;用户表仍作为试点白名单。 |
H3 | 为 Anthropic 的 | Anthropic 发布稳定的出口网段并建议白名单; |
H4 | 截断的结果发布 | 四条不同的路径都可能导致一个看似正确实则错误的数字。 |
H5 | 短 TTL 缓存;第 2..N 页并发获取;页大小限制为 | 十次串行往返无法满足 P95 ≤ 5 s。 |
H6 | 新增第 9 个工具 | 没有它,拼写错误的工厂名称会返回空集合,而这会被解读为"没有任何 outstanding"。 |
H7 |
| 规范中硬编码的 "KLIP production" 字符串会导致每次预演 UAT 的答案都声称自己是生产环境。 |
H9 |
| 通过删除分区进行保留是仅追加触发器唯一允许的方式;U5 的导出没有实现;客户端 IP 原本都会记录为 127.0.0.1。 |
H10 | 备份侧车、容器健康检查、 | 在 TSD 中有规定,但指南中从未实现,因此上线时不会存在。 |
— | 未知工具参数通过 | PRD 8.1 要求拒绝;普通的 |
— | 工具返回针对 | 类型化数据而非嵌入散文中的 JSON —— 减少转写错误,这正是 M1 所衡量的。 |
9. 仍待处理事项
附录 A 对账 (P1) —— 阻止投产,启动时强制检查。
KLIP 侧 K1–K4 ——
MCP_READONLY角色、svc-mcp账户、安全组规则。离机备份和经过测试的恢复 —— 侧车目前仅写入本地。
deploy/nginx/mcp.conf中的真实企业出口 CIDR,然后启用两行被注释掉的return 403行。负载测试,按 30 并发用户容量 NFR 执行。
Hub 客户端注册 —— 网关自身的注册,先在测试 DWS Hub 中:
HUB_ISSUER、客户端 ID 和密钥,并在 Hub 侧注册重定向 URI<PUBLIC_URL>/authorize/hub/callback。确认 Hub 的 email 和 groups 声明名称,然后运行hub:check。阶段 7 需要在生产 Hub 中进行第二次独立注册。决定是否设置
HUB_REQUIRED_GROUP,以及在 Hub 路径在生产环境中验证后是否将BREAK_GLASS_ENABLED=false。
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseAqualityBmaintenanceEnables read-only querying of the gong-nl-db Postgres database through natural language via Claude Desktop.9
- AlicenseNot gradedqualityDmaintenanceEnables natural language queries against Snowflake Gold-layer tables through Claude Desktop, allowing users to ask business questions in plain English without SQL knowledge.MIT
- AlicenseNot gradedqualityCmaintenanceGives Claude live access to your Observe tenant, enabling natural language queries about errors, logs, and metrics without writing OPAL pipelines.17MIT
- FlicenseNot gradedqualityBmaintenanceEnables natural language interaction with Kintone data via Claude, allowing listing apps, field definitions, querying and modifying records.
Related MCP Connectors
WHOOP recovery, strain, sleep and workouts in Claude via official WHOOP OAuth. Free, open source.
Connect Claude to Fathom meeting recordings, transcripts, and summaries
Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/dwsitproject-hub/MCP-Gateway'
If you have feedback or need assistance with the MCP directory API, please join our Discord server