MCP服务器正在使用。网络和微软Entra ID
A.NET样板,用于构建与Microsoft Entra ID集成的安全MCP服务器。
此项目为任何想要快速设置使用Microsoft Entra ID(Azure AD)保护的模型上下文协议(MCP)服务器的人提供了一个即用型基础。 它包括对组织中的用户进行身份验证、安全管理用户身份以及在受信任的企业环境中公开MCP工具所需的所有核心组件。
开箱即用,它经过测试并与Claude AI和ChatGPT兼容,使其成为在几分钟内让您自己的Entra保护的MCP服务器运行的最快方法。
该项目演示了如何弥合MCP客户端(Claude AI、ChatGPT)和Microsoft Entra ID之间的身份验证差距,确保这些系统能够安全无缝地协同工作。
 
A.NET 8.0示例实现,展示了如何构建OAuth 2.1代理,使Claude AI能够使用Microsoft Entra ID对模型上下文协议(MCP)服务器进行身份验证。
______________________________________________________________________
概述
该项目演示了如何弥合Claude AI和Microsoft Entra ID(Azure AD)之间的身份验证差距。克劳德要求 RFC 7591动态客户端注册,Entra ID不支持。此代理在维护企业安全的同时,在这些不兼容的系统之间进行转换。
与官方合作建造 模型上下文协议C#SDK -此SDK是在中实现MCP服务器的游戏规则改变者。NET,提供强类型接口和自动协议处理。
┌──────────┐ OAuth 2.1 ┌──────────────────────┐ OAuth 2.0 ┌────────────┐
│ │ (with Dynamic Reg) │ │ (Pre-registered) │ │
│ Claude ├─────────────────────►│ MCP (OAuth) Server ├────────────────────►│ Entra ID │
│ AI │◄─────────────────────┤ (this project) │◄────────────────────┤ │
└──────────┘ └──────────────────────┘ └────────────┘
主要特点
- ✅ 自定义登录UI -Entra ID认证前的品牌同意页面
- ✅ WhoAmI工具 -显示经过身份验证的用户信息的示例MCP工具
- ✅ 动态客户端注册 -实现RFC 7591以实现克劳德兼容性
- ✅ 双PKCE流 -所有组件之间的安全身份验证(RFC 7636)
- ✅ 令牌映射 -不透明的外部令牌,带有用户声明的JWT内部令牌
- ✅ 用户索赔提取 -Entra ID令牌中的姓名、电子邮件、OID、UPN
- ✅ 生产就绪 -CORS、HTTPS、错误处理、结构化日志
截图
OAuth login flow with custom branding before Entra ID authentication
*OAuth login flow with custom branding before Entra ID authentication*
测试平台
✅ 克劳德·艾 -经过全面测试,可使用Claude Desktop和web界面
✅ ChatGPT -经过测试并正常工作;有关动态客户端注册和持久客户端存储要求的说明,请参阅故障排除。
⚠️ 其他MCP客户端 -此代理实现了MCP OAuth规范。虽然它可能与其他MCP客户端一起工作,但它主要经过了Claude AI和ChatGPT的测试。VS Code的MCP集成通常使用带有预配置客户端凭据的直接OAuth,可能不需要此代理
______________________________________________________________________
包含内容
带OAuth的MCP服务器
该项目包括一个完整的MCP服务器实现,包括:
- WhoAmI工具 -显示经过身份验证的用户信息
✅ Authentication Status: Authenticated via Entra ID OAuth
📋 User Information:
• Name: John Doe
• Email: john.doe@company.com
• User ID (OID): b9b8d416-d882-47f9-bb74-445d22ddd735
• UPN: john.doe@company.com- OAuth保护的端点 -所有MCP端点都需要有效的承载令牌
- 用户上下文 -工具可以访问经过身份验证的用户声明以进行个性化
OAuth代理组件
- 动态客户端注册 -接受Claude的注册请求
- 授权流程 -自定义登录页面+Entra ID重定向
- 代币交换 -将Entra ID令牌映射到具有正确受众的代理令牌
- 发现端点 -符合RFC 9728和RFC 8414的元数据
______________________________________________________________________
快速开始
先决条件
- ✅ .已安装NET 8.0 SDK
- ✅ 带有Entra ID租户的Azure订阅
- ✅ 创建应用程序注册的管理员权限
- ✅ Visual Studio 2022、VS Code或Rider
备注:本项目使用官方 MCP C#SDK 这大大简化了MCP服务器的实现。
1.克隆和构建
git clone
cd MCP
dotnet build2.Azure应用程序注册设置
- 首选 Azure 门户 > 参赛者ID > 应用注册 > 新注册
- 配置应用程序:
- 名字: MCP OAuth Proxy - 支持的帐户类型: Accounts in this organizational directory only - 重定向URI: Web - https://YOUR-DOMAIN/oauth/callback - 对于生产:使用您部署的URL(例如。, https://your-app.azurewebsites.net/oauth/callback) - 对于DevTunnels:使用您的隧道URL(例如。, https://abc123-5248.euw.devtunnels.ms/oauth/callback)
- 创建后,请注意以下值:
- 应用程序(客户端)ID - 目录(租户)ID
- 创建客户端密码:
- 首选 证书和秘密 > 新客户机密 - 描述: MCP Proxy Secret - 过期:选择合适的持续时间 - 复制机密值 (仅显示一次)
- 配置API权限:
- 首选 API权限 > 添加权限 - 选择 Microsoft Graph > 委托权限 - 添加: openid, profile, email, User.Read - 点击 授予管理员同意
备注: User.Read 基本身份验证并不严格要求,但通常使用,并支持未来可能需要调用Microsoft Graph API的场景(例如,获取用户照片、日历数据等)。
- 公开API(用于令牌受众):
- 首选 公开API > 添加作用域 - 应用程序ID URI: api://YOUR-CLIENT-ID (默认值很好) - 作用域名称: MCP.Access - 谁可以同意: Admins and users - 管理员同意显示名称: Access MCP Server - 管理员同意说明: Allows the application to access MCP tools on your behalf
3.生成安全密钥
运行密钥生成脚本:
.\GenerateKeys.ps1将生成的值复制到配置中。
4.配置应用程序设置
{
"MCP": {
"ServerUrl": "https://YOUR-DOMAIN"
},
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "YOUR-TENANT-ID",
"ClientId": "YOUR-CLIENT-ID",
"ClientSecret": "YOUR-CLIENT-SECRET",
"Scope": "api://YOUR-CLIENT-ID/MCP.Access"
},
"Jwt": {
"SigningKey": "GENERATED-SIGNING-KEY",
"EncryptionKey": "GENERATED-ENCRYPTION-KEY",
"ExpirationMinutes": "60"
},
"TokenStore": {
"Provider": "InMemory",
"AzureTableStorage": {
"ConnectionString": "DefaultEndpointsProtocol=https;AccountName=YOUR_ACCOUNT;AccountKey=YOUR_KEY;EndpointSuffix=core.windows.net",
"TableName": "TokenMappings"
}
},
"ClientStore": {
"Provider": "InMemory",
"AzureTableStorage": {
"ConnectionString": "DefaultEndpointsProtocol=https;AccountName=YOUR_ACCOUNT;AccountKey=YOUR_KEY;EndpointSuffix=core.windows.net",
"TableName": "ClientRegistrations"
}
}
}重要:
- 替换
YOUR-DOMAIN使用您的实际域名(例如。,https://your-app.azurewebsites.net) - 对于DevTunnels的本地开发,请使用您的隧道URL(例如。,
https://abc123-5248.euw.devtunnels.ms)-见下面的步骤6 - 替换
YOUR-TENANT-ID,YOUR-CLIENT-ID,YOUR-CLIENT-SECRET使用Azure应用程序注册中的值 - 使用由生成的密钥
GenerateKeys.ps1为了SigningKey和EncryptionKey
令牌存储选项:
InMemory(默认)-用于内存存储,适用于开发和单实例部署AzureTableStorage-使用Azure表存储跨多个实例进行持久、可扩展的令牌存储
- 配置 ConnectionString 使用您的Azure存储帐户连接字符串 - TableName 默认为 TokenMappings 如果未指定 - 启动时自动创建表 - 每次应用程序重启时,过期的令牌(>90天)都会被清理
客户端存储选项:
InMemory(默认)-使用确定性客户端ID(SHA-256哈希),适合开发AzureTableStorage-使用Azure表存储进行持久客户端注册
- 配置 ConnectionString 使用您的Azure存储帐户连接字符串 - TableName 默认为 ClientRegistrations 如果未指定 - 对客户端ID使用随机GUID - ChatGPT需要 -ChatGPT只注册一次,需要持久的客户端ID
5.运行服务器
dotnet run服务器将在以下时间启动 https://localhost:5248 (或您配置的端口)。
6.利用DevTunnels进行当地开发(可选)
对于Claude AI的本地测试,您可以使用Microsoft DevTunnels来公开您的本地服务器:
# 1. Install Dev Tunnels CLI (one-time setup)
winget install Microsoft.devtunnel
# 2. Verify installation
devtunnel --version
# 3. Log in (one-time setup for tunnel management)
devtunnel user login
# 4. Create a persistent tunnel with anonymous access
devtunnel create -a
# 5. Map your local HTTPS app on port 5248 to the tunnel
devtunnel port create -p 5248 --protocol https
# 6. Start the tunnel
devtunnel host隧道启动后:
- 使用 “通过浏览器连接:” URL作为您的
MCP:ServerUrl在appsettings.json - 将此URL作为重定向URI添加到您的Entra ID应用程序注册中(
https://YOUR-TUNNEL-URL.devtunnels.ms/oauth/callback) - 配置Claude AI时使用此URL
- 这 “检查网络活动:” URL可用于监控Claude和MCP服务器之间的流量
DevTunnel URL示例: https://abc123-5248.euw.devtunnels.ms
7.配置Claude AI
- 打开 克劳德桌面 或前往 claude.ai
- 引导到 设置 > 连接 (或同等)
- 点击 添加集成 或 添加自定义连接器
- 请输入您的服务器URL:
https://YOUR-DOMAIN(或用于本地测试的DevTunnel URL) - 点击 连接
- 您将被重定向到登录页面,然后是Entra ID
- 身份验证后,您将看到✅ 连接 在克劳德
8.测试连接
在Claude中,尝试:
Use the WhoAmI tool to show my information您应该从Entra ID中看到您的姓名、电子邮件和其他声明。
______________________________________________________________________
项目结构
MCP/
├── Controllers/
│ ├── OAuthController.cs # OAuth endpoints (authorize, continue, cancel, callback, token, register)
│ └── WellKnownController.cs # Discovery endpoints
├── Services/
│ ├── PkceStateManager.cs # Encrypted state for PKCE flows
│ ├── InMemoryLoginTokenStore.cs # Short-lived login page tokens
│ ├── BrandingProvider.cs # Branding configuration provider
│ ├── ConfigurationHelper.cs # Configuration helper utilities
│ ├── ClientStore/
│ │ ├── AzureTableClientStore.cs # Azure Table Storage client store
│ │ ├── InMemoryClientStore.cs # In-memory client store for development
│ │ └── IClientStore.cs # Client store interface
│ ├── Jwt/
│ │ ├── DefaultClaimProvider.cs # Default JWT claim provider
│ │ ├── IClaimProvider.cs # Interface for claim providers
│ │ ├── JwtBuilder.cs # JWT token building utilities
│ │ └── SampleClaimProvider.cs # Sample implementation of claim provider
│ └── TokenStore/
│ ├── AzureTableTokenStore.cs # Azure Table Storage token store
│ ├── InMemoryTokenStore.cs # In-memory token store for development
│ └── ITokenStore.cs # Token store interface
├── Models/
│ ├── PkceStateData.cs # PKCE state data
│ ├── TokenData.cs # Unified token model (authorization_code + refresh_token)
│ ├── LoginTokenData.cs # Login page data
│ ├── ClientMapping.cs # Client registration data
│ └── LoginPageModel.cs # Login page view model
├── Tools/
│ └── WhoAmITool.cs # Example MCP tool
├── Views/
│ ├── _ViewImports.cshtml # Razor view imports
│ └── Login/
│ └── Index.cshtml # Custom login UI
├── wwwroot/css/
│ └── login.css # Login page styling
├── Properties/
│ ├── launchSettings.json # Launch settings for development
│ └── PublishProfiles/ # Publish profiles
├── Program.cs # ASP.NET Core configuration
├── appsettings.json # Configuration
├── appsettings.Development.json # Development configuration
├── MCP.csproj # Project file
├── GenerateKeys.ps1 # Key generation script
├── README.md # This file
└── README-Architecture.md # Technical architecture details______________________________________________________________________
身份验证流程
1. Claude → GET /.well-known/oauth-protected-resource
Discovers authorization server
2. Claude → POST /oauth/register
Dynamic client registration
← Returns client_id
3. Claude → GET /oauth/authorize?client_id=...&code_challenge=...
Starts authorization flow
← Shows custom login page with user consent
[User clicks "Continue"]
4. Proxy → POST /oauth/continue
Redirects to https://login.microsoftonline.com/.../authorize
User authenticates with Entra ID
5. Entra → Redirects to /oauth/callback?code=...
Authorization code returned
6. Proxy → Exchanges code with Entra ID
← Receives access_token + id_token
7. Proxy → Generates proxy authorization code
Redirects to https://claude.ai/api/mcp/auth_callback?code=...
8. Claude → POST /oauth/token (with code_verifier)
Exchanges code for token
← Receives opaque access_token
9. Claude → GET /mcp/v1/tools (with Authorization: Bearer ...)
Proxy validates token, forwards with JWT
← MCP server processes request看 README-架构.md 获取详细的技术文档。
______________________________________________________________________
定制
品牌化
登录页面可以通过以下方式定制您自己的品牌 appsettings.json:
"Branding": {
"CompanyName": "Profility",
"ProductName": "Profility MCP Server",
"PrimaryColor": "#6B46C1",
"PrimaryHoverColor": "#553C9A"
}这使您能够:
- 公司名称:您的组织名称(以文本显示)
- 产品名称:您的产品/服务名称(页面标题和描述)
- 主色调:十六进制格式的主要品牌颜色(徽标、按钮)
- 主悬停颜色:按钮悬停状态的阴影较深
品牌是通过以下方式配置的 appsettings.json -无需更改代码!
定制索赔
发放给Claude的JWT令牌可以通过索赔提供者系统扩展自定义索赔。这允许您向令牌添加特定于组织或特定于应用程序的声明。
它是如何工作的:
- 实施
IClaimProvider接口在MCP/Services/Jwt/ - 在中注册您的提供商
Program.cs与默认提供者一起 - 声明按注册顺序添加,实现了提供者之间的链接
有关更多信息,请参阅README-Architecture.md
______________________________________________________________________
故障排除
“客户端id无效”错误
- 验证
AzureAd:ClientId匹配Azure应用程序注册 - 检查Azure中是否配置了重定向URI
“无效代码_验证器”错误
- 表示PKCE验证失败
- 检查数据保护密钥在重新启动时是否一致
- 验证
Jwt:EncryptionKey已配置
“无效受众”错误
- 确保JWT
aud声明与MCP服务器URL匹配 - 验证
Jwt:Audience配置
令牌立即过期
- 检查系统时钟同步
- 验证令牌生命周期配置
用户声明缺失
- 确保
openid profile email作用域位于令牌请求中 - 检查授予的入口ID API权限
- 验证是否提供了管理员同意
ChatGTP问题: invalid_client 重新启动MCP服务后
当MCP服务重新启动时,ChatGPT可能会在OAuth流程中显示以下错误:
{
"error": "invalid_client",
"error_description": "Client not found"
}
原因: 这是因为 动态客户端注册(DCR) 客户端之间的行为不同:
- 克劳德·艾 每当应用程序重新启动时,都会自动重新注册。
- ChatGPT 仅执行DCR 一次,此时添加了连接器。\
之后,ChatGPT预计 client_id 无限期有效。
如果MCP服务器存储已注册的客户端 在存储器中,重新启动会清除客户端注册表,ChatGPT会继续发送旧的 client_id 不再存在,导致 invalid_client.
解决方案: 使用 持久客户端存储 在使用ChatGPT进行测试时,不使用内存存储:
"ClientStore": {
"Provider": "AzureTableStorage",
"AzureTableStorage": {
"ConnectionString": "DefaultEndpointsProtocol=https;...",
"TableName": "ClientRegistrations"
}
}这确保了注册 client_id 在应用程序重启后幸存下来,并防止OAuth握手失败。
ChatGTP问题:“连接器不安全”错误
问题描述: 在ChatGPT中连接自定义MCP服务器时,连接最初显示为成功。\ 连接器显示为 连接但是 没有可用的工具.\ 刷新工具(开发人员工具→ Network)披露了API响应:
{ "detail": "Connector is not safe" }
这个问题是 不 由MCP服务器、身份验证流、devtunnels或Entra ID引起。相反,ChatGPT在其 安全评估阶段,它扫描工具元数据(名称+描述)以查找任何建议访问的内容 个人数据 (PII)。
尽管服务器是完全安全的,但最初的工具描述明确提到\ 姓名、电子邮件、ID,这触发了ChatGPT的安全启发式,并导致它阻塞了连接器。
原因: 如果任何工具描述暗示连接器检索,ChatGPT会将其标记为“不安全” 用户身份, 电子邮件, 个人信息,或其他敏感数据——无论连接器是私有的还是受信任的。 安全检查仅在以下情况下运行 工具元数据,而不是你的实际实施。
解决方案: 重写工具描述,避免明确提及个人信息。\ 该工具仍可能在内部返回完整的身份详细信息——扫描仅评估元数据文本。
例子: 原始不安全描述:
Get information about the currently authenticated user (name, email, ID, etc.)新的安全说明:
Returns basic operational context about the authenticated session.安全考虑
⚠️ 生产部署检查表:
- \[\]使用带有有效TLS证书的HTTPS
- \[\]将机密存储在Azure密钥库或类似库中
- \[\]对OAuth端点实施速率限制
- \[\]仅对受信任的来源启用CORS
- \[\]使用持久数据保护密钥存储(不在内存中)
- \[ \] 使用Azure表存储进行令牌持久化 (包括在内,请参阅TokenStore配置)
- \[\]实现令牌撤销
- \[\]监视可疑的身份验证模式
- \[\]定期安全更新和依赖关系扫描
- \[\]实施适当的错误处理(错误中没有敏感数据)
- \[\]使用短令牌寿命(建议1小时)
⚠️ 当前限制
此参考实现使用 内存存储 为简单起见,默认情况下:
- PKCE 国家 -存储在静态中
ConcurrentDictionary(记忆中) - 令牌映射 -可配置:
InMemory(默认)或AzureTableStorage - 客户注册 -可配置:
InMemory(默认)或AzureTableStorage - 登录令牌 -存储在静态中
ConcurrentDictionary(记忆中)
用于生产部署,使用Azure表存储进行持久化:
- ✅ Azure表存储令牌 -包括生产就绪令牌存储
- 集 TokenStore:Provider 到 AzureTableStorage 在应用程序设置中 - 启动时自动创建表 - 应用程序重启时过期令牌清理(>90天)
- ✅ 客户端的Azure表存储 -包括生产就绪的客户端存储
- 集 ClientStore:Provider 到 AzureTableStorage 在应用程序设置中 - ChatGPT需要 兼容性(持久客户端ID) - 使用随机GUID而不是确定性哈希
- ✅ 瑞迪斯 -分布式缓存的替代方案(不包括在内,请参阅架构文档)
- ✅ SQL Server -审计跟踪要求的替代方案(不包括在内,请参阅架构文档)
______________________________________________________________________
文档
- 建筑 -技术细节、OAuth流程、PKCE实现
______________________________________________________________________
贡献
欢迎投稿!此项目可作为MCP OAuth与企业身份提供者集成的参考实现。
需要改进的地方
- 其他OAuth提供者实现(谷歌、Okta等)
- 其他工具,如ChatGTP
- 用于生产规模的Redis/SQL令牌存储
- 令牌刷新实现
- 更多MCP工具示例
- 自动化测试
______________________________________________________________________
许可证
MIT许可证-有关详细信息,请参阅许可证文件
______________________________________________________________________
致谢
- Anthropic -关于MCP规范和Claude AI
- 模型上下文协议C#SDK -官方。NET实现使这个项目成为可能
- 微软 -用于Entra ID和优秀的OAuth文档
- 社区 -适用于OAuth 2.1、PKCE和相关RFC
______________________________________________________________________
维护者和贡献者
- 罗尼·范德斯尼克(https://profility.be)
有兴趣贡献吗?请参阅上面的贡献部分或打开拉取请求。
______________________________________________________________________
支持
这是一个参考实现。对于问题:
- 检查上面的故障排除部分
- 审查 README-架构.md 了解技术细节
- 打开日志和配置问题(编辑机密!)
______________________________________________________________________
内置于❤️ 作为企业MCP OAuth集成的示例
