使用说明
模拟MCP服务器
一个模拟MCP(模型上下文协议)代理服务器,具有两部分身份验证、SSE通知和基于CIMD的客户端身份。作为一个。NET 8 ASP。NET Core应用程序。模拟一个真实世界的架构,其中MCP代理通过CIMD(AAD应用程序#1)对客户端进行身份验证,并通过单独的AAD应用程序(应用程序#2)对后端MCP服务器进行内部身份验证。
两部分身份验证流程
VS Code (MCP Client)
│
├─ CIMD OAuth ──→ AAD App #1 ──→ proxy token
│
├─ tools/list ──→ [discover_tools only]
│
├─ tools/call discover_tools ──→ elicitation URL
│ │
│ └─ User opens browser → /backend-auth/login
│ → Entra login (AAD App #2)
│ → Backend tokens stored server-side
│ → "Success, close window"
│
├─ tools/list ──→ [full tools list]
│
└─ tools/call echo ──→ proxy uses stored backend token internally
第1部分:代理身份验证(CIMD+AAD应用程序#1)
- 客户在以下位置发现PRM
/.well-known/oauth-protected-resource - 客户端通过CIMD OAuth流进行身份验证(
/oauth/authorize→/oauth/token) - 客户收到 代理令牌 --这是客户端唯一看到的令牌
- 使用代理令牌,
tools/list仅返回discover_tools
第2部分:后端认证(AAD应用程序#2,服务器端)
- 客户电话
discover_tools→ 接收启发URL - 用户在浏览器中打开URL→ 根据AAD应用程序#2进行身份验证
- 存储后端令牌(访问+刷新) 服务器端,由代理令牌键入
- 现在
tools/list返回完整的工具和tools/call作品 - 客户端继续使用相同的代理令牌——后端令牌永远不会暴露
端点
| 方法 | 路线 | 描述 |
|---|---|---|
| 得到 | /.well-known/oauth-protected-resource |
受保护资源元数据(PRM) |
| 得到 | /.well-known/oauth-authorization-server |
OAuth授权服务器元数据 |
| 得到 | /oauth/authorize |
OAuth授权端点(CIMD) |
| 得到 | /oauth/callback |
Entra ID回调(代理身份验证) |
| 职位 | /oauth/token |
OAuth令牌端点(代理身份验证) |
| 获取/发布 | /mcp |
MCP JSON-RPC端点 |
| 得到 | /backend-auth/login |
启动后端身份验证(AAD应用程序#2) |
| 得到 | /backend-auth/callback |
后端身份验证回调 |
| 得到 | /backend-auth/status |
检查后端身份验证状态 |
| 得到 | /cimd-policy |
当前CIMD策略配置 |
| 获取/发布 | /backend/mcp |
后端MCP服务器(JSON-RPC+SSE) |
支持的MCP方法
initialize-返回服务器功能tools/list-返回可用工具(同意后展开)tools/call-执行工具调用(echo、get_weather、compute)resources/list-返回可用资源(空)prompts/list-返回可用提示(空)
流媒体代理架构(SSE Sidecar)
后端身份验证完成后,代理需要从后端MCP服务器接收服务器发起的通知。这 后端服务器管理器 充当进程内SSE sidecar:
- 客户端↔ 代理人:上海证券交易所成立于
initialize(代理令牌) - 后端认证后:sidecar打开SSE
GET /backend/mcp(后端令牌) - 后端发送通知→ sidecar读到了→ 继电器通过
McpServer.NotifyClient()→ 写给客户SSE - 工具调用是无状态的POST转发(代理→ 后端),不涉及SSE
在生产环境中,sidecar将是每个环境的一个单独的Azure容器应用程序,使后端SSE连接远离APIM。
配置
身份验证模式
集 MCP_AUTH_MODE 在 local.settings.json:
| 模式 | 描述 |
|---|---|
mock (默认) |
无外部呼叫。本地生成的身份验证码和令牌。适合离线测试。 |
entra |
真正的Azure AD集成。重定向到Entra ID进行用户身份验证。需要AAD应用程序注册。 |
授权策略
集 MCP_AUTH_STRATEGY 在 local.settings.json:
| 策略 | 描述 |
|---|---|
cimd (默认) |
代理充当自己的OAuth授权服务器。客户端通过CIMD进行身份验证。适用于任何MCP客户端。 |
entra-direct |
PRM直接指向Entra ID。VS Code使用WAM进行无声身份验证(无浏览器弹出窗口)。仅适用于Entra注册客户。 |
环境变量
代理身份验证(AAD应用程序#1)
| 变量 | 默认值 | 描述 |
|---|---|---|
MCP_AUTH_MODE |
mock |
身份验证模式: mock 或 entra |
MCP_AUTH_STRATEGY |
cimd |
授权策略: cimd (代理是OAuth AS)或 entra-direct (参赛者ID为AS) |
MCP_EXPOSED_SCOPE |
Runtime.All |
AAD应用程序上暴露的作用域名称(用于entra直接PRM) |
AZURE_TENANT_ID |
common 登录 ID |
|
AZURE_CLIENT_ID |
-- | AAD应用程序#1客户端ID(代理) |
AZURE_CLIENT_SECRET |
-- | AAD应用程序#1客户端机密(仅CIMD策略需要) |
后端认证(AAD应用程序#2——真正的MCP服务器)
| 变量 | 默认值 | 描述 |
|---|---|---|
BACKEND_CLIENT_ID |
-- | AAD应用程序#2客户端ID(后端) |
BACKEND_CLIENT_SECRET |
-- | AAD应用程序#2客户端机密 |
BACKEND_TENANT_ID |
AZURE_TENANT_ID |
后端租户(默认为代理租户) |
BACKEND_SCOPES |
openid profile offline_access |
应用程序#2的范围 |
其他
| 变量 | 默认值 | 描述 |
|---|---|---|
MCP_VERBOSE_LOGGING |
false |
启用详细的请求/响应日志记录 |
CIMD_POLICY_MODE |
open |
CIMD客户政策: open, allowlist,或 denylist |
CIMD_ALLOWED_CLIENTS |
-- | 允许的CIMD客户端模式的分号分隔列表(在模式下使用=allowlist) |
CIMD_DENIED_CLIENTS |
-- | 被拒绝的CIMD客户端模式的分号分隔列表(在模式下使用=denylist) |
CIMD客户端策略
控制允许哪些MCP客户端通过CIMD进行身份验证。对政策进行评估 之前 获取CIMD文档(防止SSRF不受信任 client_id URL)。
| 模式 | 行为 |
|---|---|
open (默认) |
允许所有CIMD客户端 |
allowlist |
只允许明确列出的客户端/域 |
denylist |
允许所有客户端,但明确阻止的客户端除外 |
图案类型 (以分号分隔 CIMD_ALLOWED_CLIENTS / CIMD_DENIED_CLIENTS):
- 确切URL:
https://vscode.dev/oauth/client-metadata.json - 确切域名:
vscode.dev(匹配该主机上的任何CIMD) - 通配符域:
*.microsoft.com(仅匹配子域)
示例——只允许列出VS代码和GitHub客户端:
{
"Values": {
"CIMD_POLICY_MODE": "allowlist",
"CIMD_ALLOWED_CLIENTS": "https://vscode.dev/oauth/client-metadata.json;*.github.com"
}
}
示例--阻止特定客户端:
{
"Values": {
"CIMD_POLICY_MODE": "denylist",
"CIMD_DENIED_CLIENTS": "*.untrusted.dev"
}
}
查看当前政策: curl http://localhost:7071/cimd-policy
入口模式设置(CIMD策略)
- 应用程序#1(代理): 在Azure门户中注册,添加重定向URI
http://localhost:7071/oauth/callback - 应用程序#2(后端): 单独注册,添加重定向URI
http://localhost:7071/backend-auth/callback - 在中设置环境变量
local.settings.json:{ "Values": { "MCP_AUTH_MODE": "entra", "MCP_AUTH_STRATEGY": "cimd", "AZURE_TENANT_ID": "your-tenant-id", "AZURE_CLIENT_ID": "app1-client-id", "AZURE_CLIENT_SECRET": "app1-secret", "BACKEND_CLIENT_ID": "app2-client-id", "BACKEND_CLIENT_SECRET": "app2-secret", "BACKEND_SCOPES": "api://app2-client-id/.default" } }
Entra直接设置(通过WAM进行静默认证)
在此模式下,VS Code使用用户现有的Microsoft会话进行静默身份验证,不需要浏览器弹出窗口。PRM直接指向Entra ID作为授权服务器。
- 应用程序#1(代理): 在Azure门户中注册
- 公开API 在应用程序#1上:
- 添加范围(例如。,
Runtime.All) - 预授权VS Code的客户端ID:
aebc6443-996d-45c2-90f0-388ff96faa56
- 添加范围(例如。,
- 应用程序#2(后端): 单独注册,添加重定向URI
http://localhost:7071/backend-auth/callback - 在中设置环境变量
local.settings.json:注:{ "Values": { "MCP_AUTH_MODE": "entra", "MCP_AUTH_STRATEGY": "entra-direct", "MCP_EXPOSED_SCOPE": "Runtime.All", "AZURE_TENANT_ID": "your-tenant-id", "AZURE_CLIENT_ID": "app1-client-id", "BACKEND_CLIENT_ID": "app2-client-id", "BACKEND_CLIENT_SECRET": "app2-secret", "BACKEND_SCOPES": "api://app2-client-id/.default" } }AZURE_CLIENT_SECRETentra-direct不需要代理(代理只是一个资源服务器,而不是OAuth AS)。
本地开发
先决条件
本地运行
dotnet run
# Server starts on http://localhost:7071
测试CIMD流量
# 1. Check Protected Resource Metadata
curl http://localhost:7071/.well-known/oauth-protected-resource
# 2. Check OAuth Authorization Server Metadata
curl http://localhost:7071/.well-known/oauth-authorization-server
# 3. Start authorization (opens redirect)
curl -v "http://localhost:7071/oauth/authorize?response_type=code&client_id=https://client.example.dev/oauth/metadata.json&redirect_uri=https://client.example.dev/oauth/callback&scope=mcp.tools.execute&state=test123"
# 4. Exchange code for token
curl -X POST http://localhost:7071/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code&code=<AUTH_CODE>&client_id=https://client.example.dev/oauth/metadata.json"
# 5. Use token for MCP calls
curl -X POST http://localhost:7071/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{},"id":1}'
项目结构
mock-mcp-server/
├── McpServer.cs # Proxy server logic (MCP, OAuth/CIMD, SSE, backend auth)
├── BackendMcpServer.cs # Simulated backend MCP server (tools, SSE notifications)
├── BackendSseManager.cs # SSE sidecar — manages backend SSE connections per session
├── Program.cs # ASP.NET Core host, route mapping, config loading
├── MockMcpServer.csproj # Project file (.NET 8 Web SDK)
├── local.settings.json # Local dev settings (auth mode, credentials)
├── samplecalls.http # HTTP test file for VS Code REST Client
├── APIHUB_MCP_INTEGRATION.md # Architecture, requirements, and demo script
└── README.md
许可证
麻省理工学院