努美纳MCP网关
MCP(模型上下文协议)网关,使AI代理和用户能够通过基于Envoy的路由、使用NPL支持的捆绑包的OPA策略评估、JWT身份验证和凭证注入安全地访问上游MCP服务。
建筑
┌──────────────┐
│ Keycloak │
│ (OIDC) │
└──────┬───────┘
│ public-net
Agents / Users ──────► ┌─────┴──────┐
(JWT auth) │ Envoy │
│ AI Gateway│
└──┬──┬──┬───┘
│ │ │
policy-net │ │ │ backend-net
│ │ │
┌──────────┴┐ │ ├──────────────────────┐
│ OPA │ │ │ │
│ Sidecar │ │ │ supergateway sidecars│
│(ext_authz)│ │ │ ┌─────────┐ ┌───────┐│
└─────┬─────┘ │ │ │DuckDuck │ │GitHub ││
│ │ │ │Go MCP │ │ MCP ││...
┌─────┴─────┐ │ │ └─────────┘ └───────┘│
│ Bundle │ │ └──────────────────────┘
│ Server │ │
└─────┬─────┘ │ secrets-net
│ │
┌─────┴─────┐ ┌──────┴──────┐
│NPL Engine │ │ Credential │
│(PolicyStore)│ │ Proxy │
└───────────┘ └──────┬──────┘
┌──────┴──────┐
│ Vault │
│ (secrets) │
└─────────────┘四层网络隔离:代理只能访问Envoy和Keycloak(公共网络)。OPA和NPL引擎在政策网络上。保险库和凭证代理位于秘密网络上。后端MCP服务(超级网关侧车)位于后端网络上。
组件
| 组件 | 角色 |
|---|---|
| Envoy AI网关 | MCP协议处理、JWT验证(Keycloak JWKS)、通过aigw运行路由到后端、SSE流、Lua响应过滤(工具/列表) |
| OPA 侧车 | 通过Rego进行ext_authz政策评估;从OPA包加载策略数据(内存中,零网络I/O) |
| NPL发动机 | 策略状态管理器:网关存储(目录+访问规则+撤销)+服务治理(每个服务工作流) |
| 捆绑服务器 | 读取不良贷款网关存储+治理数据(约束、收件人、授权),为OPA包提供服务;SSE触发NPL突变重建 |
| 超级网关Sidecar | 将STDIO MCP工具包装为可流式HTTP端点 |
| 模拟日历MCP | 用于双边流测试(SSE通知)的HTTP原生MCP服务器 |
| 钥匙锁 | 具有用户/角色管理的OIDC身份验证提供程序 |
| 仪表盘 | 管理web UI:服务目录、访问规则、治理规则、审批、用户管理、Docker发现、实时指标、自动孤立容器清理 |
| 凭证代理 | 从Vault获取秘密,在启动时注入超级网关容器 |
| 金库 | 秘密存储(API密钥、令牌、密码) |
快速开始
先决条件
- Docker&Docker编写
- Node.js 18+(用于MCP检查器)
1.启动堆栈
cd deployments
docker compose up -d
# Wait for all services to be healthy
docker compose ps2.播种门户
AIGW配置见容器种子AI网关。
这将引导GatewayStore使用服务、工具、访问规则和治理配置。
3.打开仪表板
打开 http://localhost:8888 并使用登录 admin / Welcome123仪表板提供了一个完整的管理界面,用于管理服务目录、访问规则、治理规则、审批和用户。
4.与MCP检查器连接
npx @modelcontextprotocol/inspector- 运输:流式HTTP
- 统一资源定位符:
http://localhost:8000/mcp - 认证:OAuth(通过内置OAuth代理端点自动执行)
- 网关公开RFC 9728/8414 OAuth发现、重定向
/authorize到Keycloak和代理/token--MCP检查器自动处理流程 - 以注册用户身份登录(例如。,
jarvis/Welcome123)
访问UI
| 服务 | URL | 凭据 |
|---|---|---|
| 仪表盘 | http://localhost:8888 | 管理员/欢迎123 |
| 特使管理员 | http://localhost:9901 | (无) |
| OPA API | http://localhost:8181 | (无) |
| Keycloak管理员 | http://localhost:11000 | 管理员/欢迎 |
| 不良贷款检查员 | http://localhost:8080 | 管理员/欢迎123 |
| Vault用户界面 | http://localhost:8200 | 令牌:dev令牌 |
代理连接
| 端点 | 传输 | 身份验证 |
|---|---|---|
POST /mcp | 流式HTTP(SSE响应) | 承载JWT |
GET /mcp | SSE通知流(服务器启动) | 承载JWT |
GET /sse | 苏格兰和南方能源公司(传统) | 不记名智威汤逊 |
所有端点都需要通过Keycloak进行JWT身份验证。Envoy使用Keycloak的JWKS端点验证令牌。
双边流媒体
网关支持 双向流媒体 通过MCP流式HTTP:
- POST/mcp --代理到服务器:JSON-RPC请求(初始化、工具/列表、工具/调用)
- GET/mcp --服务器到代理:服务器发起的通知的长期SSE流
Envoy通过以下方式路由GET/mcp timeout: 0s (永不超时),因此通知流无限期保持打开状态。JWT身份验证和ext_authz检查在流设置时发生一次。
OPA政策授权
每个MCP请求都通过Envoy的 ext_authz 过滤器,通过gRPC调用OPA。OPA评估注册政策(policies/mcp_authz.rego)使用内存中的捆绑数据——请求路径中的网络I/O为零。
运作原理
NPL PolicyStore ──SSE──► Bundle Server ──bundle──► OPA (in-memory data)
Envoy (port 8000)
│ ext_authz (gRPC)
▼
OPA Sidecar (port 9191 gRPC / 8181 HTTP)
└─ Layer 1: Rego rules + in-memory bundle data (~11ms avg E2E)
Zero network I/O. Bundle rebuilt on NPL mutation via SSE push.策略链中的三个组件:OPA+捆绑服务器+NPL。捆绑服务器将NPL状态桥接到OPA捆绑包中。无轮询,无缓存过期——SSE对每个突变都进行推送。
授权流程
1. Agent → Envoy POST /mcp: tools/call "duckduckgo.search"
2. Envoy Validates JWT (Keycloak JWKS)
3. Envoy → OPA ext_authz (gRPC): forwards JSON-RPC body + headers
4. OPA Decodes JWT: userId=jarvis@acme.com
5. OPA Parses JSON-RPC: method=tools/call, tool=duckduckgo.search
6. OPA Three-layer evaluation (in-memory bundle data):
- Layer 1 (Catalog): service enabled? tool registered?
- Layer 2 (Access Rules): caller matches a rule for this service/tool?
- Layer 3 (Governance): if tag=logic, in-memory constraints + recipients (+ NPL approval if needed)
7. OPA → Envoy allow or deny (with X-OPA-Reason header)
8. Envoy → Backend MCP Routes to supergateway sidecar (if allowed)
9. Backend → Envoy → Agent SSE response streamed back政策规则
| 请求类型 | 规则 |
|---|---|
initialize, ping, notifications/* | 始终允许(非工具方法) |
tools/list | 允许;响应已过滤,仅显示已注册目录的工具(Lua过滤器读取 x-visible-tools 来自OPA) |
GET /mcp (流设置) | 如果用户有任何匹配的访问规则,则允许 |
tools/call (acl标签) | 服务已启用,工具在目录中,调用者匹配访问规则 |
tools/call (逻辑标签) | 以上+内存约束/收件人检查通过(+NPL批准,如果需要) |
| 缺少JWT或未知用户 | 拒绝 |
捆绑传播
- NPL突变 (例如,管理员禁用工具)→ SSE活动→ 捆绑服务器重建→ OPA重新加载
- 传播延迟:近乎即时(SSE推送,而非轮询)
- 没有冷电话:OPA在首次加载包后始终在内存中有数据
网关商店+服务治理(v4)
GatewayStore Single protocol, single instance
├── Catalog Services + tools with acl/logic tags
├── Access Rules Claim-based and identity-based authorization
└── Revoked Subjects Emergency kill switch
ServiceGovernance One instance per governed service
├── Tool Configs Per-tool constraints (regex, in/not_in, max_length)
├── Pending Requests Logic tool calls awaiting approval
└── Approval Settings Per-tool approval requirements + deadlines
ApprovedRecipients Workflow governance for sensitive parameters
├── Approved Recipients Per-tool approved email addresses/domains
├── Agent Identity Caller-specific enforcement (e.g., only AI agents)
└── Supervisor Approval Human-in-the-loop recipient approval workflow捆绑服务器在一次调用中读取GatewayStore(getBundleData())以及来自ServiceGovernance、Approved Recipients和ToolAuthorization实例的治理数据(工具配置、收件人绑定、工具授权)。所有数据都包含在OPA包中,用于内存评估。
对于逻辑标记的工具,OPA评估 三阶段治理检查 完全在记忆中:
- 第一阶段——限制:检查捆绑包中工具配置的参数级约束(正则表达式,in/not_in,contains,not_contains,max_length)
- 第2阶段——收件人绑定:检查特定于呼叫者的收件人限制(批准的收件人:域匹配、电子邮件白名单)
- 第3阶段——审批工作流程:如果
requiresApproval=true,直接调用NPL获取审批状态机(罕见路径,仅网络跳转)
凭证注入
机密(API密钥、令牌)存储在Vault中,并在启动时注入到超级网关容器中。
注入流
- 管理员将机密存储在Vault中(通过Vault UI或CLI)
- 超级网关容器启动并调用凭据代理
- 凭证代理从Vault中提取,返回注入的字段
- Supergateway将字段导出为环境变量
- STDIO MCP工具由环境中的凭据生成
两个凭证范围
| 范围 | 保险库路径模式 | 用例 |
|---|---|---|
| 租户级别 | secret/data/tenants/{tenant}/services/SERVICE/... | 共享组织凭据 |
| 用户级别 | secret/data/tenants/{tenant}/users/{user}/SERVICE/... | 个人证件 |
看 docs/CREDENTIAL_注射.md 了解架构细节。
配置
services.yaml(仅限Bootstrap)
定义上游MCP服务及其工具。用作NPL状态的持久缓存,用于批量导入/导出。 运行时不读取 --OPA从捆绑包服务器构建的捆绑包中加载策略数据。
services:
- name: "duckduckgo"
displayName: "DuckDuckGo"
type: "MCP_STDIO"
enabled: true
command: "docker run -i --rm --init mcp/duckduckgo"
tools:
- name: "search"
enabled: true
user_access:
default_template:
enabled: false
users:
- userId: "jarvis@acme.com"
keycloakId: "..."
tools:
duckduckgo:
- "*" # Wildcard: all tools证书.yaml
将凭据名称映射到Vault路径和注入目标:
mode: simple
tenant: acme
credentials:
google_gemini:
vault_path: secret/data/tenants/{tenant}/services/gemini/api
injection:
type: env
mapping:
api_key: GEMINI_API_KEY
service_defaults:
gemini: google_gemini环境变量
捆绑服务器
| 变量 | 默认值 | 描述 |
|---|---|---|
NPL_URL | http://npl-engine:12000 | NPL引擎URL |
KEYCLOAK_URL | http://keycloak:11000 | 密钥斗篷URL |
KEYCLOAK_REALM | mcpgateway | 钥匙斗篷王国 |
GATEWAY_USERNAME | gateway | 不良贷款电话服务账户 |
GATEWAY_PASSWORD | Welcome123 | 服务帐户密码 |
BUNDLE_PORT | 8282 | 捆绑服务器HTTP端口 |
凭证代理
| 变量 | 默认值 | 描述 |
|---|---|---|
PORT | 9002 | HTTP端口 |
CREDENTIAL_MODE | simple | simple 或 expert |
VAULT_ADDR | http://vault:8200 | 保险库地址 |
VAULT_TOKEN | dev-token | 保险库身份验证令牌 |
项目结构
noumena-mcp-gateway/
├── policies/ # OPA Rego policies (ext_authz)
│ ├── mcp_authz.rego # v4 authorization: three-layer catalog/access/governance
│ └── mcp_authz_test.rego # Rego unit tests (opa test policies/ -v)
│
├── bundle-server/ # OPA bundle server (Python)
│ └── server.py # Reads NPL GatewayStore, serves OPA bundles, SSE-triggered rebuild
│
├── dashboard/ # Admin web UI (Python + single-page HTML)
│ ├── server.py # API server: proxies to NPL, Keycloak, OPA, Docker, metrics
│ └── static/index.html # SPA: catalog, rules, approvals, users, discover, metrics
│
├── credential-proxy/ # Credential injection service (Kotlin/Ktor)
│ └── src/main/kotlin/ # VaultClient, CredentialSelector
│
├── npl/ # NPL protocol definitions
│ └── src/main/npl-1.0/
│ ├── store/ # GatewayStore (catalog + access rules + revocation)
│ └── governance/ # ServiceGovernance + ApprovedRecipients protocols
│
├── mock-calendar-mcp/ # HTTP-native MCP server (bilateral streaming tests)
│ └── server.js # Express: POST /mcp + GET /mcp SSE stream
│
├── configs/ # Runtime configuration
│ ├── services.yaml # Service definitions (bootstrap/cache)
│ └── credentials.yaml # Credential mappings
│
├── deployments/ # Docker Compose + Envoy config
│ ├── docker-compose.yml # Full stack with network isolation
│ ├── envoy/
│ │ ├── envoy-config.yaml # Envoy static config (JWT, ext_authz → OPA, Lua filter, routing)
│ │ └── mcp-servers.json # aigw-run backend MCP server registry
│ └── docker/
│ ├── Dockerfile.supergateway # Supergateway sidecar image
│ ├── Dockerfile.dashboard # Admin dashboard image
│ ├── Dockerfile.credential-proxy
│ └── Dockerfile.mock-calendar # HTTP-native MCP server image
│
├── keycloak/ # Custom Keycloak image
├── keycloak-provisioning/ # Terraform for Keycloak setup
├── integration-tests/ # Integration test suite (JUnit5: NPL + E2E)
└── docs/ # Documentation运行OPA测试
# Install OPA (macOS)
brew install opa
# Run Rego unit tests
opa test policies/ -v测试
集成测试(JUnit5)
# Requires Docker stack running (docker compose up -d)
cd integration-tests
./gradlew test测试套件包括:
- NPL测试 (16个测试):PolicyStore CRUD——注册/启用服务、启用工具、授予/撤销访问、挂起/恢复、上下文路由、getPolicyData
- E2E测试 (11次测试):通过Envoy的完整管道→ OPA → 后端——初始化、工具/列表、工具/调用允许/拒绝、动态授权撤销管道、暂停服务拒绝
手动验证
# 1. Get a token
TOKEN=$(curl -sf -X POST 'http://localhost:11000/realms/mcpgateway/protocol/openid-connect/token' \
-d 'grant_type=password&client_id=mcpgateway&username=jarvis&password=Welcome123' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
# 2. Call a tool (should 200 if jarvis has access)
curl -X POST 'http://localhost:8000/mcp' \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_events","arguments":{"date":"2026-02-14"}}}'
# 3. Inspect OPA bundle data
curl http://localhost:8181/v1/data文档
| 文档 | 描述 |
|---|---|
| 架构参考 | 服务拓扑、网络隔离、数据流图 |
| 三层治理模式 | 概念模型:通道、护栏、工作流 |
| v4治理设计 | 三层治理:目录、访问规则、不良贷款工作流 |
| 如何指导 | 分步配置演练 |
| 审批工作流程 | 人在环审批系统深潜 |
| 凭证注入 | 凭证注入系统设计和Vault集成 |
| 安全战略 | 企业安全战略和MCP风险框架 |
许可证
版权所有2025-2026努美纳数字股份公司。保留所有权利。
