内容
# MCP 策略网关
MCP 预检和运行时策略工具,适用于希望在连接前评估目标 MCP 的人员。
当前产品方向有两种模式:
1. **PlayMCP 主机预检 MCP**:PlayMCP/Kakao 的公共远程 MCP 接口。用户询问是否可以连接另一个 MCP,并接收基于库存的风险标签、决策辅助、推荐的网关策略和操作员移交。此模式不得调用目标 MCP。
2. **运行时策略网关**:本地或托管的内联 MCP 代理。它位于 MCP 客户端和目标 MCP 服务器之间,仅暴露策略批准的工具,在 `tools/call` 之前强制执行允许/阻止/批准决策,并为每个决策记录审计/证据。
> 状态:**stdio 核心的 MVP 验收强化证据已完成** — 工具中介、过滤暴露、调用时强制执行、批准、输出策略、快照差异、审计读取隐私、目标注册保护和 stdio 运行时强化均已实现和测试。HTTP 目标套接字 IP 固定和完整的远程 HTTP MCP 端到端仍是明确的后续工作。
>
> PlayMCP 公共注册是 P1 主机服务目标。它仍然需要一个新的入站流式 HTTP 服务器模式(`public-preflight`)和严格的公共工具允许列表。参见 `docs/playmcp-public-hosted-preflight.md` 和 `docs/adr/ADR-018.md`。
## PlayMCP 主机预检方向
对于 PlayMCP/Kakao,预期的列表是一个名为 `MCP 策略网关预检` 的托管远程 MCP。
```text
PlayMCP / Toolbox / Claude / ChatGPT
-> https://<public-host>/mcp
-> public-preflight 工具
-> PlayMCP 库存评估
```
这不是最终用户的本地 PC 安装流程。操作员首先部署公共 HTTPS MCP 端点,在 PlayMCP 开发人员控制台中注册该端点,测试它作为临时注册,然后请求审查。
公共 PlayMCP 工具表面:
- `gateway_search_playmcp` — 在连接目标 MCP 之前搜索 PlayMCP 库存候选者。
- `gateway_preflight_mcp` — PlayMCP MCP 的静态预使用决策辅助:决策、风险标签、代表性风险工具、网关策略建议和下一步行动。
- `gateway_explain_mcp_risk` — 风险标签和决策理由的纯语言解释。
在公共 PlayMCP 模式下,不要暴露 `gateway_call_tool`、`gateway_request_approval`、`gateway_list_exposed_tools`、目标别名、审计、重新扫描、差异或注册工具。`gateway_health` 应该是一个 HTTP `/healthz` 端点,而不是 LLM 可见的 MCP 工具。
当前 P1 实现差距:存储库当前通过 stdio 启动上游服务器。PlayMCP 注册需要入站流式 HTTP `/mcp` 端点、流式 HTTP 头/会话/版本处理、`/healthz`、托管滥用/隐私控制和 `public-preflight` 允许列表。
## 本地运行时工具(当前)
默认客户端表面(`GATEWAY_TOOL_SURFACE_MODE=client`):
- `gateway_health` — 生存/身份。
- `gateway_search_playmcp` — 在连接目标 MCP 之前搜索 PlayMCP 库存候选者。
- `gateway_preflight_mcp` — PlayMCP MCP 的静态预使用决策辅助:决策、风险标签、代表性风险工具、网关策略建议和下一步行动。
- `gateway_explain_mcp_risk` — 风险标签和决策理由的纯语言解释。
- `gateway_call_tool` — 策略评估路由器;在调用时通过真实工具名称强制执行允许/批准/拒绝。 在批准门控工具上,它消耗匹配的批准(原子,一次性)或返回待处理的一个。
- `gateway_list_exposed_tools` — 当前策略过滤的目标别名暴露上游。
- `gateway_request_approval` — 创建与确切调用(JCS 参数哈希 + 策略/观察/模式/重写哈希)绑定的待处理批准,TTL 很短。
受信任的操作员表面(`GATEWAY_TOOL_SURFACE_MODE=operator`)另外暴露:
- `gateway_list_targets` — 已注册的目标(租户范围)。
- `gateway_rescan_target` — 观察目标的 `tools/list`(所有页面)并存储新鲜快照。
- `gateway_inspect_target` — 最新快照:工具、完整性、可调用性(如果不完整/过时则关闭)。
- `gateway_diff_target` — 两个快照之间的差异(添加/删除/模式更改的工具)。
- `gateway_get_audit_event` — 一个审计事件的最小、租户范围的、经过编辑的元数据:决策/规则 ID、HMAC 哈希、策略版本可用性和没有原始参数/结果(记录 `audit_event_read`)。
操作员在带外授予/拒绝待处理批准:
```bash
GATEWAY_DB_PATH=... npm run approve -- <approvalId> # 授予
GATEWAY_DB_PATH=... npm run approve -- reject <approvalId> # 拒绝
```
## 保证和限制
- 允许的工具结果在返回之前通过 **输出策略**:已知秘密模式的最佳努力删除、资源链接方案允许列表和嵌入式资源阻止。这 **不是** 完整的 DLP 保证(ADR-010)。
- 更改工具模式的重新扫描将其标记为 **待重新审查** — 它不会被暴露或调用,直到重新审查(关闭,ADR-008)。
- 审计事件仅存储哈希 + 编辑后的元数据;原始参数/结果永远不会被默认持久化。审计读取是租户范围的,并返回 HMAC 证据和编辑后的元数据,而不是原始有效载荷(ADR-005,ADR-014)。
以及 **策略过滤的目标别名**,作为真实的 MCP 工具暴露(`<target_slug>__<tool_slug>`),例如允许的只读工具或限制别名,强制预览/干燥运行模式。只有允许的工具和别名出现在 `tools/list` 中;隐藏/拒绝的工具永远不会被暴露,并且在调用时即使通过真实名称调用也会被阻止(列表过滤不是控制)。
在受信任的操作员模式下试用:`GATEWAY_TOOL_SURFACE_MODE=operator GATEWAY_POLICY_PATH=examples/policies/local-dev.yaml`,使用 `risky-actions-mcp` 示例目标(`npm run start:target:risky`)。
目标注册是一种特权操作;为 stdio 目标设置 `GATEWAY_EXEC_ALLOWLIST`(逗号分隔的可执行文件)(ADR-012)。空允许列表关闭,除非设置 `GATEWAY_DEV_MODE=true` 或 `GATEWAY_ALLOW_UNLISTED_EXECUTABLES=true` 用于本地开发。MVP 注册拒绝原始 stdio `env` 值,目标进程仅继承 `GATEWAY_STDIO_ENV_KEYS`(默认值:`PATH,SystemRoot,WINDIR,ComSpec`)加上显式适配器提供的 env。使用后期的 env-profile/secret-store 集成进行生产目标凭证。
## 主机用户流程
对于 PlayMCP/Kakao 路径,用户在托管的 MCP 已被操作员注册后开始:
1. 用户打开 PlayMCP、Toolbox、Claude、ChatGPT 或通过 PlayMCP 连接的另一个客户端。
2. 用户选择 `MCP 策略网关预检`。
3. 用户询问:
```text
카카오맵 MCP 연결해도 돼?
```
4. 响应返回静态决策辅助、风险标签、代表性风险工具、推荐策略和下一步行动。
5. 用户决定是否连接目标 MCP、请求批准或将移交给操作员。
此主机预检流程不会安装目标 MCP、调用目标 MCP 工具或提供运行时强制执行。运行时强制执行仅在目标位于托管网关后面时开始。
## 本地提示窗口预检
对于本地开发,起点仍然可以是 Claude、Codex CLI 或桌面 MCP 客户端中的提示窗口。首先在客户端注册 **此网关 MCP**,然后在直接添加目标之前询问目标 MCP。
### 前 5 分钟
1. 安装依赖项:
```bash
npm install
```
2. 为您的应用生成 MCP 客户端配置:
```bash
npm run config:client -- claude-desktop
npm run config:client -- codex-cli
npm run config:client -- generic-json
```
3. 仅将 `mcp-policy-gateway` 添加到 MCP 客户端配置中。不要将目标 MCP 直接添加到客户端。
4. 在使用前验证活动的 MCP 客户端配置:
```bash
npm run config:validate -- claude-desktop /path/to/claude_desktop_config.json
npm run config:validate -- codex-cli /path/to/codex-config.toml
npm run config:validate -- generic-json /path/to/mcp-config.json
```
这是本地偏移检查:它确认配置当前仅列出 `mcp-policy-gateway`。它不是 OS/MDM 级别的锁定。
5. 重新启动 MCP 客户端并从提示窗口询问:
```text
카카오맵 MCP 연결해도 돼?
```
6. 将返回的 `operatorHandoffStructured` 发送给操作员或在目标注册之前使用它作为自我审查清单。
本地协议烟雾:
```bash
npm run smoke:mcp-client-preflight
```
这将网关启动为 MCP stdio 服务器,通过 MCP SDK 客户端列出工具,并通过真实协议路径调用 `gateway_preflight_mcp`。
### 配置示例
Claude Desktop 样式:
```json
{
"mcpServers": {
"mcp-policy-gateway": {
"command": "/path/to/mcp-policy-gateway/node_modules/.bin/tsx",
"args": ["/path/to/mcp-policy-gateway/src/index.ts"],
"env": {
"GATEWAY_TOOL_SURFACE_MODE": "client",
"GATEWAY_HMAC_SECRET": "REPLACE_WITH_LOCAL_HMAC_SECRET",
"PLAYMCP_INVENTORY_CSV": "/path/to/playmcp_inventory_20260625.csv"
}
}
}
}
```
Codex CLI 样式:
```toml
[mcp_servers.mcp-policy-gateway]
command = "/path/to/mcp-policy-gateway/node_modules/.bin/tsx"
args = ["/path/to/mcp-policy-gateway/src/index.ts"]
[mcp_servers.mcp-policy-gateway.env]
GATEWAY_TOOL_SURFACE_MODE = "client"
GATEWAY_HMAC_SECRET = "REPLACE_WITH_LOCAL_HMAC_SECRET"
PLAYMCP_INVENTORY_CSV = "/path/to/playmcp_inventory_20260625.csv"
```
通用 stdio JSON 客户端可以使用 `generic-json` 生成的相同 JSON 形状。
示例提示:
- `카카오맵 MCP 연결해도 돼?`
- `카카오톡 선물하기 MCP는 어떤 승인이 필요해?`
- `컴퓨터 사용 MCP는 왜 차단 후보야?`
客户端可以调用:
- 当 MCP 名称模糊时,调用 `gateway_search_playmcp`。
- 当用户想要一个决策辅助工具时,调用 `gateway_preflight_mcp`。
- 当用户询问为什么分配了标签或决策时,调用 `gateway_explain_mcp_risk`。
答案是基于 PlayMCP 库存的静态预使用评估。它包括决策,如 `usable`、`usable_with_approval`、`manual_review`、`not_recommended` 或 `blocked`,以及风险标签、代表性风险工具、网关策略建议和操作员移交字符串。结构化结果还包括 `operatorHandoffStructured`,用于操作员审查队列或自我审查清单。
如果您想要网关保护,请不要在客户端直接注册目标 MCP。目标应由操作员或托管配置注册在网关后面,然后通过过滤别名和调用时策略公开。
### 下游 HTTP(流式 HTTP)目标
`kind: "http"` 目标是受保护的后 MVP 接口,位于 SSRF 出口保护后面(ADR-017):默认情况下仅限 HTTPS,跳转目的地在跟随之前会重新验证,并且每个连接 + 请求都会被阻止,以解析为环路/私有/本地/元数据 IP(DNS 重新绑定会重新验证每个请求)。通过以下方式配置:
```bash
GATEWAY_EGRESS_SCHEMES=https # 允许的 URL 方案(默认:https)
GATEWAY_EGRESS_HOSTS=mcp.example.com # 可选的主机允许列表(默认:任何公共主机)
GATEWAY_EGRESS_ALLOW_PRIVATE=true # 仅限本地开发 — 允许私有/环路出口
```
剩余的 HTTP 风险被明确跟踪:真正的套接字 IP 固定需要一个后期的传输钩子(例如 undici 代理查找钩子),而完整的远程 HTTP MCP 端到端测试需要一个真正的远程 MCP 服务器。
## 为什么
LLM 客户端不应默认看到或调用每个目标 MCP 工具。网关减少了暴露的工具表面,在调用时强制执行策略(不仅在工具列表中),并保留了审计谁决定了什么以及为什么的审计轨迹。
## 快速入门(开发)
```bash
npm install
npm run typecheck
npm test # 单元 + 集成测试
npm run --silent config:client -- claude-desktop > /tmp/mcp-policy-gateway-claude.json
npm run config:validate -- claude-desktop /tmp/mcp-policy-gateway-claude.json
npm run smoke:preuse # 本地预使用网关 -> 目标 MCP 烟雾测试
npm run smoke:mcp-client-preflight # MCP SDK 客户端 -> 网关预检烟雾测试
npm run assessment:playmcp # PlayMCP 库存静态评估 + HTML/JSON 报告
npm run demo:mvp # 本地 MVP 烟雾行走(注册 -> 过滤 -> 阻止 -> 批准 -> 删除 -> 差异 -> 审计)
npm run verify:mvp # 完整的本地 MVP 验证门控
npm run migrate # 应用 SQLite 迁移
GATEWAY_POLICY_PATH=examples/policies/default-deny.yaml npm start # 在 stdio 上启动客户端表面网关
```
示例目标:`npm run start:target:safe`(只读),`npm run start:target:risky`(突变/破坏)。在运行 `npm run demo:mvp` 之前 — 它会逐步打印每个网关决策步骤。
## 验证和发行说明
发行卫生命令:
```bash
npm run verify:mvp
```
等效的扩展门控:
```bash
npm run typecheck
npm test
npm run smoke:preuse
npm run smoke:mcp-client-preflight
npm run assessment:playmcp
npm run demo:mvp
npm audit --omit=dev
```
使用 `.env.example` 作为非秘密配置模板。当前没有项目许可证文件;在公共重新分发之前添加显式许可证。已知后续风险仍在 ADR-017 中跟踪:HTTP 目标的真正套接字 IP 固定和与真正的远程服务器的完整远程 HTTP MCP 端到端测试。
## MVP 身份
stdio MVP 使用一个配置的incipal进行租户/客户端/参与者身份验证(`GATEWAY_TENANT_ID` 等)。每个用户批准和团队强制执行需要身份验证的传输或托管部署 — 参见 `docs/adr/ADR-011.md`。
## 部署规则
仅注册网关到 MCP 客户端,并将目标 MCP 端点/进程/凭证保留在网关后面。强执行声明仅在客户端无法直接注册目标时有效(托管配置 / MDM / 工作区策略)。目标注册是特权操作员操作(`docs/adr/ADR-012.md`)。
部署模式:
| 模式 | 含义 | 声明边界 |
|---|---|---|
| `self-managed` | 用户手动遵循文档和配置示例。 | 直接目标注册仍可绕过网关。 |
| `validated-local` | `npm run config:validate -- <target> <config-path>` 检查当前配置仅限网关注册。 | 检测配置偏移;不锁定 OS 或客户端。 |
| `managed-enforced` | 组织通过 MDM、GPO、 工作区策略或等效控制部署只读/托管客户端配置。 | 强大的网关唯一强制执行声明范围限定在此部署条件下。 |
参见 `docs/deployment-managed-client.md` 获取操作员检查清单。
## 非目标
- 不是全局 MCP 防火墙。
- 不是完整的恶意 MCP 检测。
- 不是沙盒。
- 不是付费墙 / API / 速率限制 / 反机器人绕过工具。
输出删除是尽力而为的,永远不是完整的 DLP 保证。
## 工具名称
所有暴露的工具名称仅使用 `[a-z0-9_]`(无点):管理员工具为 `gateway_*`(例如 `gateway_list_targets`);目标别名为 `<target_slug>__<tool_slug>`。这使名称在客户端将它们命名为 `mcp__<server>__<tool>` 后仍然有效(`docs/adr/ADR-016.md`)。
## 布局
```
src/ 网关源代码(配置、存储、策略、目标、上游)
sample-targets/ 内存假目标 MCP(安全笔记 MCP)
examples/ 示例策略 YAML
test/ vitest 单元测试
docs/adr/ 架构决策记录
handoff/ 设计移交包(权威)
```