此示例展示了如何使用App Service身份验证和授权来实现Model Context Protocol (MCP)服务器授权。MCP服务器是通过Azure Functions MCP扩展实现的。该示例还展示了如何代表已登录用户调用Microsoft Graph。
示例使用了一个.NET项目。有两种开始方式:
您可以选择在本地运行或使用Azure开发者CLI(azd)快速将其部署到Azure。
您还需要一个Entra客户端ID,可以由您的MCP客户端使用。对于基本测试,您可以使用Visual Studio Code,它有一个已知的客户端ID,将在设置说明中解释。
azd部署到Azure)克隆这个仓库。
登录到Azure并初始化azd:
az login
azd auth login
创建一个新的azd项目环境,指定位置(建议使用westus2),您要使用的订阅ID,以及环境名称:
azd env new <environment-name> --location westus2 --subscription <subscription-id>
此示例使用Visual Studio Code作为主要客户端。配置它为允许的客户端应用:
azd env set PRE_AUTHORIZED_CLIENT_IDS aebc6443-996d-45c2-90f0-388ff96faa56
如果您有其他MCP客户端将调用您的服务器,请提供所有客户端ID作为逗号分隔的列表:
azd env set PRE_AUTHORIZED_CLIENT_IDS <comma-separated-list-of-client-ids>
如果您的组织需要服务管理引用,请指定它。**如果您不是微软员工且不知道是否需要设置此引用,可以跳过这一步。**然而,如果因为缺少服务管理引用而导致配置失败,您可能需要重新考虑这一步。使用微软租户的微软员工必须提供服务管理引用(您的服务树ID)。没有这个,您将无法创建Entra应用注册,配置也会失败。
azd env set SERVICE_MANAGEMENT_REFERENCE <service-management-reference>
如果您不确定应该设置什么,请咨询您的组织租户管理员。
(可选)如果您正在部署到主权云,请设置用于该云的令牌交换受众。
对于Entra ID美国政府版:
azd env set TOKEN_EXCHANGE_AUDIENCE api://AzureADTokenExchangeUSGov
对于由中国21Vianet运营的Entra ID中国版:
azd env set TOKEN_EXCHANGE_AUDIENCE api://AzureADTokenExchangeChina
创建Azure资源并部署应用程序:
azd up
确保所有资源都显示为“成功”状态后再继续。
授权应用程序,以便您的MCP客户端能够成功登录。为了测试,您只需为自己授权即可,方法是在浏览器中登录应用程序。有关生产场景中的授权编写,请参阅授权编写。
导航到您部署的功能应用的/.auth/login/aad端点。例如,如果您的功能应用位于https://my-mcp-function-app.azurewebsites.net,则导航到https://my-mcp-function-app.azurewebsites.net/.auth/login/aad。
您的功能应用基础URL应在azd up命令的输出中显示。如果您错过了部署输出中的URL,可以通过以下命令检索:
azd env get-value SERVICE_MCP_DEFAULT_HOSTNAME
或者在Azure门户中找到您的功能应用资源下的URL。
您应该被重定向到登录提示。使用您将用于测试的MCP客户端的帐户登录。
登录后,您将被提示授权应用程序。查看请求的权限并点击“接受”以授予授权。
您应该被重定向回一个托管在功能应用URL上的页面,上面写着“您已成功登录”。此时您可以关闭页面。
获取您的功能应用的MCP扩展系统密钥,以便连接MCP客户端。您的客户端除了需要与Entra ID进行服务器授权外,还需要这个密钥来调用您的MCP服务器。
您可以使用Azure CLI获取名为mcp_extension的系统密钥。首先,从您的azd部署中获取资源组和功能应用名称:
# 获取资源组名称
azd env get-value AZURE_RESOURCE_GROUP_NAME
# 获取功能应用名称
azd env get-value SERVICE_MCP_NAME
然后使用这些值来检索系统密钥:
az functionapp keys list --resource-group <resource_group> --name <function_app_name> --query "systemKeys.mcp_extension" -o tsv
或者,您也可以从Azure门户中检索它:
mcp_extension系统密钥的值按照下面的测试您的MCP服务器部分中的说明测试您的MCP服务器。您需要功能应用的URL和上一步中的mcp_extension系统密钥。
[!注意] 当本地运行时,MCP服务器将使用您的开发者凭据(来自Azure CLI或Visual Studio Code)对外部调用Microsoft Graph,而不是使用授权用户的标识。
克隆这个仓库。
(可选)更新src/FunctionsMcpAuthorization文件夹中的local.settings.json文件,以包含您的Microsoft Entra租户ID。这有助于应用程序正确访问您的开发者身份,即使您可以登录多个租户。
使用Azure CLI获取租户ID:
az account show --query tenantId -o tsv
更新local.settings.json以包含租户ID:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
"AZURE_TENANT_ID": "<your-tenant-id>"
}
}
启动Azurite。如果您使用CLI启动它,例如使用azurite或docker run命令,请在单独的后台终端中执行。
将主终端移动到src/FunctionsMcpAuthorization文件夹。
启动项目:
dotnet run
按照下面的测试您的MCP服务器部分中的说明测试您的MCP服务器。
完成上述任一“开始”选项后,您可以按照以下说明使用MCP客户端连接到您的MCP服务器进行测试。
以下说明是针对使用Visual Studio Code中的GitHub Copilot进行测试的,它支持MCP协议和Entra ID认证。此设置还提供了日志,使您容易看到客户端和服务器在服务器授权过程中的交互情况。
在VS Code中创建一个新的工作区并安装MCP扩展。
创建或更新您的MCP配置在VS Code中。将以下内容添加到您的mcp.json文件中(或创建一个):
{
"inputs": [
{
"type": "promptString",
"id": "functions-mcp-extension-system-key",
"description": "Azure Functions MCP Extension System Key",
"password": true
},
{
"type": "promptString",
"id": "functionapp-host",
"description": "The host domain of the function app."
}
],
"servers": {
"remote-mcp-function": {
"type": "http",
"url": "https://${input:functionapp-host}/runtime/webhooks/mcp",
"headers": {
"x-functions-key": "${input:functions-mcp-extension-system-key}"
}
},
"local-mcp-function": {
"type": "http",
"url": "http://localhost:7071/runtime/webhooks/mcp"
}
}
}
这创建了两个服务器配置,分别对应上述两种“开始”选项。在后续步骤中,选择与您设置MCP服务器的方式相匹配的服务器,无论是本地还是远程。
(可选)显示输出日志:
Ctrl+Shift+PCmd+Shift+Premote-mcp-function或local-mcp-function)启动MCP服务器:
Ctrl+Shift+PCmd+Shift+Premote-mcp-function或local-mcp-function)remote-mcp-function,您将被提示输入:
mcp_extension系统密钥如果您连接到的是托管在App Service身份验证和授权后面的App,VS Code会提示您允许GitHub Copilot访问您的Microsoft帐户。按照提示登录。
如果您配置了调试输出日志,您应该能看到MCP客户端和服务器在登录过程中是如何交互的。有关更多细节,请参阅服务器授权协议。
打开GitHub Copilot聊天面板(Ctrl+Alt+I)并输入提示以使用工具。确保调用工具的最简单方法是发送消息#HelloTool。
GitHub Copilot应提示您允许其调用工具。确认它指向正确的服务器,然后允许其继续。
您应在聊天面板中看到工具的响应。它应该以您的名字问候您,并显示您的电子邮件。这些信息来自MCP服务器调用的Microsoft Graph。
MCP服务器的代码位于src/FunctionsMcpAuthorization项目文件夹中。这是一个使用Azure Functions MCP扩展的Azure Functions项目,在HelloTool.cs中定义了一个工具。此函数的目标是调用Microsoft Graph并返回简单的问候语。当托管在Azure中时,它将代表已登录用户调用Graph。当本地运行时,它将使用您的开发者凭据。
为了满足这些需求,函数类使用依赖注入来获取正确的用户令牌。Program.cs文件为此目的注册了一个作用域服务。服务的作用域是因为每个请求可能是不同的用户,所以我们需要为每个请求创建一个新的实例。
Program.cs文件还注册了一个用于MCP触发器的功能中间件。此中间件识别用户上下文,并使用适当的Azure.Core.TokenCredential实例配置作用域服务。在本地环境中,这是一个简单的ChainedTokenCredential。然而,当托管在Azure中并使用App Service身份验证和授权时,中间件使用MCP工具触发器的ToolInvocationContext来访问底层HTTP传输的标头。它使用这些标头来构建自定义的TokenCredential实现。此实现使用OnBehalfOfCredential,使用托管身份作为联合身份凭证进行身份验证,该凭证在配置期间设置。
使用作用域服务和功能中间件的方法便于配置每个请求的依赖项,如用户上下文。这也使工具实现保持简单并专注于其核心目的,而不引入任何额外的身份代码。这也允许在需要的情况下跨多个工具使用相同的设置。服务、中间件、自定义凭证和支持助手都位于McpOutboundCredential文件夹内。
值得注意的是,本地开发设置之所以简单,是因为示例应用程序只需要对Microsoft Graph的User.Read权限。这是默认请求的权限之一。如果需要额外的具体权限,应用程序需要获得包含正确权限声明的令牌。正确的方法是利用开发时间的自定义客户端注册,镜像在完整的Azure部署中使用的应用注册。适应这一点超出了此示例的范围,但在构建自己的应用程序时值得考虑。
在本示例中描述的步骤中,您通过在浏览器中登录应用程序来授权它。这允许应用程序请求对Microsoft Graph的委托权限。处理授权主要有两种方式:
本示例中的用户授权方法是一个单独的登录,因为示例使用Visual Studio Code作为客户端。尽管Visual Studio Code预先授权给我们的应用程序,但这仅创建了用户调用MCP服务器的授权。它并没有创建MCP服务器代表用户调用Microsoft Graph的授权。当我们直接登录应用程序时,我们会在组合授权体验中请求Microsoft Graph权限。
主要区别在于,由于Visual Studio Code使用单一登录流程,所以它只请求MCP服务器的令牌。它没有提供机会让用户交互式地同意MCP服务器所需的任何权限。如果您构建了一个使用某种交互式登录的客户端,它可以完全由该客户端处理。在这种情况下,不需要单独的浏览器登录。
有关Entra ID如何处理授权的更多信息,请参阅Microsoft身份平台中的权限和授权概述。
在VS Code的调试输出中,您会看到一系列请求和响应,这是MCP客户端和服务器交互的结果。当使用MCP服务器授权时,您应该看到以下事件序列:
有关完整协议的更多信息,请参阅MCP规范。