示例:使用 Azure Functions 进行 MCP 服务器授权
此示例展示了如何使用应用服务身份验证和授权来为模型上下文协议(MCP)服务器进行授权。MCP服务器是使用Azure Functions MCP扩展实现的。该示例还展示了如何代表已登录用户调用Microsoft Graph。
该示例使用了一个.NET项目。有两种开始方式:
- 您可以使用 Azure 开发者命令行界面 (azd) 将应用程序部署到 Azure - 这是开始使用示例的推荐方式。
- 你可以在本地运行该应用程序 - 此选项不使用任何MCP服务器授权。它不是使用已授权用户的身份,而是使用您的开发者凭据来向Microsoft Graph发起出站调用。提供此选项是为了完整性,并展示如何构建可在本地运行的应用程序,但它不是本示例的核心重点。
您可以在本地运行它,或者使用 Azure 开发者命令行界面 (azd) 快速将其部署到 Azure。
先决条件
- 一个Azure订阅 - 创建一个Azure免费账户
- Azure CLI(Azure 命令行接口) 安装 Azure CLI
- Azure 开发者命令行界面 (azd) - 安装 Azure 开发者 CLI
- Azure Functions 核心工具 - 安装 Azure Functions 核心工具
- .NET SDK 8.0 或更高版本 - 安装.NET SDK
- Visual Studio Code - 安装 Visual Studio Code
- 蓝铜矿 安装Azurite
您还必须拥有一个Entra客户端ID,以便您的MCP客户端可以使用。对于基本测试,您可以使用Visual Studio Code,其客户端ID已在下面的设置说明中给出。
开始使用(部署到 Azure,使用 azd)
- 克隆这个仓库。
- 登录到 Azure 并初始化 azd:
az login
azd auth login- 创建一个新的azd项目环境,并指定一个位置(
westus2(建议)提供您想要使用的订阅ID以及环境的名称:
azd env new --location westus2 --subscription - 此示例使用Visual Studio Code作为主要客户端。将其配置为允许的客户端应用程序:
azd env set PRE_AUTHORIZED_CLIENT_IDS aebc6443-996d-45c2-90f0-388ff96faa56如果您有其他将调用您服务器的MCP客户端,请提供所有客户端ID,用逗号分隔:
azd env set PRE_AUTHORIZED_CLIENT_IDS - 如您的组织有要求,请指定一个服务管理参考。 如果你不是微软员工,且不知道需要进行此设置,可以跳过此步骤。 然而,如果配置过程中因缺少服务管理引用而出现错误,您可能需要重新执行这一步。使用Microsoft租户的Microsoft员工必须提供服务管理引用(即您的服务树ID)。如果没有这个引用,您将无法创建Entra应用程序注册,并且配置将会失败。
azd env set 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客户端能够成功登录。为了测试,您只需在浏览器中登录该应用即可为自己授权。请参阅 同意书撰写 关于在生产环境中你将如何处理这种情况。
1. 导航至 /.auth/login/aad 您已部署的函数应用的终端节点。例如,如果您的函数应用位于 https://my-mcp-function-app.azurewebsites.net导航至 https://my-mcp-function-app.azurewebsites.net/.auth/login/aad。
您的函数应用基础URL应在输出中显示 azd up 命令。如果你在部署输出中错过了它,你可以通过以下方式获取:
azd env get-value SERVICE_MCP_DEFAULT_HOSTNAME或者,在 Azure 门户中,可以在您的 Function App 资源下找到它。
1. 您应该会被重定向到一个登录提示页面。请使用您将在MCP客户端中用于测试的账户进行登录。
1. 登录后,系统会提示您同意该应用程序。请查看所请求的权限,并点击“接受”以授予同意。
1. 您应该会被重定向回函数应用URL所托管的页面,该页面显示“您已成功登录”。此时,您可以关闭该页面。
- 获取您的函数应用的MCP扩展系统密钥,以便连接MCP客户端。除了使用Entra ID进行服务器授权外,您的客户端还需要此密钥来调用您的MCP服务器。
你可以获取名为(系统名称)的系统密钥 mcp_extension 使用 Azure CLI。首先,从您的 azd 部署中获取资源组和函数应用的名称:
# Get resource group name
azd env get-value AZURE_RESOURCE_GROUP_NAME
# Get function app name
azd env get-value SERVICE_MCP_NAME然后使用这些值来检索系统密钥:
az functionapp keys list --resource-group --name --query "systemKeys.mcp_extension" -o tsv或者,您可以从……中检索它 Azure 门户:
1. 在 Azure 门户中导航到您的函数应用 1. 首选 功能 → 应用密钥 1. 复制该值 mcp_extension 系统密钥
- 按照以下说明测试您的MCP服务器 测试您的MCP服务器 以下部分。您将需要您的函数应用URL以及
mcp_extension上一步中的系统密钥。
开始使用(本地运行)
\[!NOTE\](注:此标记通常用于提醒或注意事项) 当在本地运行时,MCP 服务器将使用您的开发人员凭据(来自 Azure CLI 或 Visual Studio Code)来发起对 Microsoft Graph 的出站调用,而不是使用经过授权用户的身份。
- 克隆这个仓库。
- (可选)更新
local.settings.json文件在src/FunctionsMcpAuthorization文件夹中包含您的 Microsoft Entra 租户 ID。这有助于应用程序在您能够登录多个租户的情况下,仍然能够正确访问您的开发者身份。
1. 从 Azure CLI 获取租户 ID:
az account show --query tenantId -o tsv1. 更新 local.settings.json 以包含租户ID:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
"AZURE_TENANT_ID": ""
}
}- 启动Azurite。如果您使用命令行界面(CLI)来启动它,请使用
azurite或者一个docker run例如,可以在一个单独的后台终端中执行此命令。
- 将你的主终端移动到
src/FunctionsMcpAuthorization文件夹。
- 启动项目:
dotnet run- 按照以下说明测试您的MCP服务器 测试您的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服务器(本地或远程)相匹配的服务器。
- (可选) 显示输出日志:
- 在VS Code中,打开命令面板: - 在Windows/Linux上:按下 Ctrl+Shift+P - 在Mac上:按下 Cmd+Shift+P - 输入“MCP: 列出服务器”并按回车键 - 选择您想要启动的服务器(任选其一 remote-mcp-function 或者 local-mcp-function) - 选择“显示输出”——这将打开输出面板。 - 在输出面板中选择齿轮图标,然后选择“调试”或更高的级别。“追踪”提供了最详细的信息,但可能会包含额外的干扰信息。
- 启动MCP服务器:
- 在 VS Code 中,打开命令面板: - 在Windows/Linux上:按下 Ctrl+Shift+P - 在Mac上:按 Cmd+Shift+P - 输入“MCP: 列出服务器”并按回车键 - 选择你想要启动的服务器(任选其一 remote-mcp-function 或者 local-mcp-function) - 选择“启动服务器”。 - 如果你选择了 remote-mcp-function,系统会提示您: - 您的函数应用URL - 你的 mcp_extension 系统密钥
- 如果你正在连接到托管在应用服务身份验证和授权之后的应用程序,VS Code 会提示你允许 GitHub Copilot 访问你的 Microsoft 帐户。请按照提示进行登录。
如果您配置了调试输出日志,您应该能看到MCP客户端和服务器在登录过程中是如何交互的。请参阅 服务器授权协议 了解更多详情。
- 打开GitHub Copilot聊天面板(
Ctrl+Alt+I)并输入提示以使用该工具。确保调用该工具的最简单方法是发送消息#HelloTool。
- GitHub Copilot 应会提示您允许其调用该工具。请确认它正在连接到正确的服务器,然后允许其继续操作。
- 你应该在聊天窗格中看到工具的响应。它应该会以你的名字打招呼,并显示你的电子邮件。这些信息来自Microsoft Graph,由MCP服务器调用。
概念概述
代码结构
MCP服务器的代码在 src/FunctionsMcpAuthorization 项目文件夹。这是一个使用Azure Functions的MCP扩展的Azure Functions项目,其中定义了一个工具 HelloTool.cs这个函数的目标是调用 Microsoft Graph 并返回简单的问候语。当部署在 Azure 上时,它会代表已登录的用户为请求调用 Graph。当在本地运行时,它将使用您的开发者凭据。
为了满足这些要求,函数类使用依赖注入来获取正确用户的令牌 Program.cs 为此,文件注册了一个作用域服务。该服务之所以是作用域的,是因为每个请求可能是针对不同用户,所以我们需要为每个请求创建一个新的实例。
\Program.cs\ 文件还为 MCP(可能是指某种触发机制或框架,如“Machine Communication Protocol”等,具体根据上下文确定)触发器注册了一个函数中间件。这个中间件用于识别用户上下文,并为作用域服务配置适当的(设置或参数) Azure.Core.TokenCredential 例子 在本地环境中,这很简单 ChainedTokenCredential然而,当在Azure中使用应用服务身份验证和授权托管时,中间件会使用MCP工具触发器的 ToolInvocationContext 要从底层HTTP传输中访问请求头。它使用这些请求头来构建一个自定义的(请求/响应) TokenCredential 实现。此实现使用了 OnBehalfOfCredential使用在配置过程中设置的托管身份作为联合身份凭证,以应用程序注册的身份进行身份验证。
使用作用域服务和函数中间件的方法便于配置每个请求的依赖项,如用户上下文。它还使工具实现保持简单并专注于其核心目的,而不会引入任何额外的身份验证代码。此外,如果需要,相同的设置也可以在多个工具中重用。服务、中间件、自定义凭据和支持辅助工具都包含在其中 McpOutboundCredential 文件夹。
值得注意的是,本地开发环境的设置变得简单,因为示例应用程序仅需要 User.Read Microsoft Graph的权限。这是Graph默认请求的权限之一。如果需要额外的特定权限,应用程序需要获取包含正确权限声明的令牌。实现这一目标的正确方法是,在开发期间使用自定义客户端注册,其方式与在完整的Azure部署中使用的应用程序注册相同。在本示例范围之外,但构建您自己的应用程序时需要考虑这一点。
同意书撰写
在这个示例中描述的步骤中,您通过在浏览器中登录应用表示了同意。这使得应用能够请求代表您访问Microsoft Graph的委托权限。处理同意的方式主要有两种:
- 用户同意 - 上述示例中采用了这种方法。每个用户登录应用程序并同意所请求的权限。他们只能为自己这样做,除非他们是租户管理员,有权代表他人同意。在本示例中,用户同意是合适的,因为它允许您快速测试,而不影响其他用户。然而,本示例中用户同意的编写方式并不反映您在生产环境中通常会采用的方式。以下将对此进行更详细的说明。
- 管理员同意 - 租户管理员可以在所有用户登录并查看权限时,代表所有用户同意该应用程序。一旦完成此操作,单个用户无需再次同意即可登录。这种方法更具可扩展性,并确保所有用户都能访问应用程序,而不会遇到同意问题。对于示例目的来说,管理员同意并不合适,但对于生产环境场景来说,这是一个很好的选择。
此示例采用的用户同意方法是独立登录,因为该示例使用Visual Studio Code作为客户端。尽管Visual Studio Code已预先获得我们应用程序的授权,但这仅意味着用户同意调用MCP服务器。它并不表示MCP服务器获得代表用户调用Microsoft Graph的权限。当我们直接登录应用程序时,我们会作为联合同意体验的一部分,请求Microsoft Graph的权限。
主要区别在于,由于Visual Studio Code采用的是单点登录流程,因此它仅请求MCP服务器的令牌。它没有提供用户以交互方式同意MCP服务器所需或所用的任何权限的机会。如果你开发了一个客户端,该客户端使用某种交互式登录方式,那么可以完全由该客户端来处理所有流程。这样,就不需要单独的浏览器登录了。
看见 Microsoft身份平台中的权限与同意概述 如需了解Entra ID如何处理同意的相关信息,请参阅以下内容。
服务器授权协议
在VS Code的调试输出中,您会看到一系列请求和响应,这些是MCP客户端和服务器交互时产生的。当使用MCP服务器授权时,您应该会看到以下事件序列:
- 编辑器向MCP服务器发送初始化请求。
- MCP服务器返回一个错误,指示需要进行授权。响应中包含指向应用程序受保护资源元数据(PRM)的指针。应用程序服务的认证和授权功能为使用此示例构建的应用程序生成PRM。
- 编辑获取PRM(可能是指某种凭证或密钥管理模块)并使用它来识别授权服务器。
- 编辑器尝试从授权服务器上的一个已知端点获取授权服务器元数据(ASM)。
- Microsoft Entra ID 不支持在知名终结点上使用 ASM(应用程序服务管理),因此编辑器回退到使用 OpenID Connect 元数据终结点来获取 ASM。它尝试通过在任何其他路径信息之前插入知名终结点来发现这个终结点。
- OpenID Connect规范实际上将众所周知的终结点定义为位于路径信息之后,而Microsoft Entra ID就是以此格式来托管该终结点的。因此,编辑器再次尝试使用这种格式。
- 编辑器成功检索到ASM(应用程序安全模块/架构)。然后,它可以结合自身的客户端ID使用这些信息进行登录。此时,编辑器会提示您登录并同意该应用程序。
- 假设您成功登录并同意,编辑器将完成登录过程。它会再次向MCP服务器发送初始化请求,这次的请求中包含了授权令牌。此重试操作在调试输出级别下不可见,但在跟踪输出级别下可以看到。
- MCP服务器验证令牌,并对初始化请求返回成功响应。从这一点开始,标准的MCP流程继续进行,最终导致发现本示例中定义的MCP工具。
你可以了解更多关于完整协议的信息在 MCP规范。
