Remote MCP Functions (远程MCP功能)
一个基于云的自定义 MCP 服务器,使用 Azure Functions,支持通过密钥、HTTPS、OAuth 和网络隔离选项进行安全通信来保存和检索代码片段。
服务介绍
使用 Azure Functions (Node.js/TypeScript) 入门远程 MCP 服务器
这是一个快速启动模板,可以轻松地使用 Azure 函数构建并将自定义远程 MCP 服务器部署到云端。您可以克隆/恢复/在本地机器上调试运行,并通过 azd up 命令在几分钟内将其部署到云端。MCP 服务器默认使用密钥和 HTTPS 进行安全保护,并允许使用 EasyAuth 和/或 API 管理进行 OAuth 的更多选项,以及使用 VNET 进行网络隔离。
观看视频概述
如果您希望查看其他语言的示例,请参阅 .NET/C# 和 Python 版本。
以下是使用 Azure Functions 的远程 MCP 服务器的架构图:

先决条件
- Node.js 版本 18 或更高
- Azure Functions Core Tools >=
4.0.7030 - Azure 开发者 CLI
- 若要使用 Visual Studio Code 在本地运行和调试:
- Docker 用于运行 Azurite,即 Azure 存储模拟器(可选)
准备您的本地环境
此特定示例需要一个 Azure 存储模拟器,因为我们将会从 blob 存储中保存和获取代码片段。
-
启动 Azurite
docker run -p 10000:10000 -p 10001:10001 -p 10002:10002 \ mcr.microsoft.com/azure-storage/azurite
注意 如果您使用的是来自 VS Code 扩展的 Azurite,则需要现在运行
Azurite: Start,否则会看到错误。
从终端本地运行您的 MCP 服务器
-
安装依赖项
npm install -
构建项目
npm run build -
本地启动 Functions 主机:
func start
注意 默认情况下,这将使用 webhooks 路由:
/runtime/webhooks/mcp/sse。稍后我们将在 Azure 中使用它来设置客户端/主机调用的密钥:/runtime/webhooks/mcp/sse?code=<system_key>
从客户端/主机内部使用 本地 MCP 服务器
VS Code - Copilot 编辑
-
添加 MCP 服务器 从命令面板中,并添加 URL 到你正在运行的 Function 应用的 SSE 端点:
http://0.0.0.0:7071/runtime/webhooks/mcp/sse -
选择 HTTP (Server-Sent-Events) 作为要添加的 MCP 服务器类型。
-
输入你正在运行的函数应用的 SSE 端点 URL。
-
输入服务器 ID。(这可以是你想要的任何名称)
-
选择是否要在用户设置中运行此设置(对你所有的应用程序都可用)还是在工作区设置中运行(仅对此应用程序可用)。
-
列出 MCP 服务器 从命令面板并启动服务器。上一步可能已经启动了本地服务器。如果是这样,你可以跳过这一步。
-
在 Copilot 聊天代理模式下输入一个提示来触发工具,例如,选择一些代码并输入以下提示
说你好将此代码片段保存为 snippet1检索 snippet1 并应用于 newFile.ts -
当被提示运行工具时,点击 继续 表示同意。
-
完成后,在终端窗口中按 Ctrl+C 停止
func.exe主机进程,并从命令面板中列出 MCP 服务器以停止本地服务器。
MCP 检查器
-
在新的终端窗口中,安装并运行 MCP 检查器
npx @modelcontextprotocol/inspector node build/index.js -
如果你之前停止了函数应用,请在本地启动 Functions 主机:
func start -
通过 CTRL 单击加载由应用程序显示的 URL 中的 MCP 检查器 Web 应用程序(例如 http://0.0.0.0:5173/#resources)
-
将传输类型设置为
SSE -
设置为你正在运行的 Function 应用的 SSE 端点 URL 并连接:
http://0.0.0.0:7071/runtime/webhooks/mcp/sse -
列出工具。单击一个工具并运行工具。
-
完成后,在终端窗口中按 Ctrl+C 停止
func.exe主机进程,并在同一终端窗口中按 Ctrl+C 停止@modelcontextprotocol/inspector主机进程。
部署到 Azure 以实现远程 MCP
可选地,你可以选择在示例中使用 VNet。(如果你选择这样做,请在执行 azd up 之前完成)
azd env set VNET_ENABLED true
运行此 azd 命令来配置函数应用及其所需的 Azure 资源,并部署你的代码:
azd up
注意 API Management 可用于提高 MCP 服务器的安全性和策略,而 App Service 内置身份验证 可用于设置你喜爱的 OAuth 提供商,包括 Entra。
从客户端连接到远程 MCP 服务器函数应用
您的客户将需要一个密钥来调用新的托管 SSE 端点,该端点的形式为 https://<funcappname>.azurewebsites.net/runtime/webhooks/mcp/sse。托管函数默认需要一个系统密钥,可以从 portal 或 CLI (az functionapp keys list --resource-group <resource_group> --name <function_app_name>) 获取。获取名为 mcp_extension 的系统密钥。
在 MCP Inspector 中连接到远程 MCP 服务器
对于 MCP Inspector,您可以在 URL 中包含密钥:
https://<funcappname>.azurewebsites.net/runtime/webhooks/mcp/sse?code=<your-mcp-extension-system-key>
在 VS Code - GitHub Copilot 中连接到远程 MCP 服务器
对于 VS Code 中的 GitHub Copilot,您应该在 mcp.json 中将密钥设置为 x-functions-key 头,并使用 https://<funcappname>.azurewebsites.net/runtime/webhooks/mcp/sse 作为 URL。以下示例来自此仓库中包含的 mcp.json 文件,并使用输入提示您在从 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-name",
"description": "Azure Functions App Name"
}
],
"servers": {
"remote-mcp-function": {
"type": "sse",
"url": "https://${input:functionapp-name}.azurewebsites.net/runtime/webhooks/mcp/sse",
"headers": {
"x-functions-key": "${input:functions-mcp-extension-system-key}"
}
},
"local-mcp-function": {
"type": "sse",
"url": "http://0.0.0.0:7071/runtime/webhooks/mcp/sse"
}
}
}
-
单击
mcp.json文件中的服务器remote-mcp-function上的“启动”按钮: -
当 VS Code 提示时,输入您在 Azure 门户中创建的功能应用的名称。
-
将
Azure Functions MCP 扩展系统密钥输入到提示中。您可以通过进入功能菜单项,然后选择 App Keys,并复制mcp_extension密钥来从 Azure 门户中复制此密钥。 -
在 Copilot 聊天代理模式下输入一个提示以触发工具,例如,选择一些代码并输入此提示
说你好将此代码片段保存为 snippet1检索 snippet1 并应用于 newFile.ts
重新部署您的代码
您可以根据需要多次运行 azd up 命令,以同时配置您的 Azure 资源并将代码更新部署到您的功能应用。
[!NOTE]
已部署的代码文件总是被最新的部署包覆盖。
清理资源
当您完成对功能应用及其相关资源的操作后,可以使用此命令删除 Azure 中的功能应用及其相关资源,以避免产生任何进一步的成本:
azd down
源代码
getSnippet 和 saveSnippet 端点的函数代码定义在 src 目录中的 TypeScript 文件中。MCP 函数注释将这些函数暴露为 MCP 服务器工具。
这展示了几个 MCP 服务器示例(获取字符串、获取对象、保存对象)的代码:
// Hello function - responds with hello message
export async function mcpToolHello(context: InvocationContext): Promise<string> {
return "Hello I am MCP Tool!";
}
// Register the hello tool
app.mcpTool('hello', {
toolName: 'hello',
description: 'Simple hello world MCP Tool that responses with a hello message.',
handler: mcpToolHello
});
// GetSnippet function - retrieves a snippet by name
export async function getSnippet(_message: unknown, context: InvocationContext): Promise<string> {
console.info('Getting snippet');
// Get snippet name from the tool arguments
const mcptoolargs = context.triggerMetadata.mcptoolargs as { snippetname?: string };
const snippetName = mcptoolargs?.snippetname;
console.info(`Snippet name: ${snippetName}`);
if (!snippetName) {
return "No snippet name provided";
}
// Get the content from blob binding - properly retrieving from extraInputs
const snippetContent = context.extraInputs.get(blobInputBinding);
if (!snippetContent) {
return `Snippet '${snippetName}' not found`;
}
console.info(`Retrieved snippet: ${snippetName}`);
return snippetContent as string;
}
// Register the GetSnippet tool
app.mcpTool('getsnippet', {
toolName: GET_SNIPPET_TOOL_NAME,
description: GET_SNIPPET_TOOL_DESCRIPTION,
toolProperties: [
{
propertyName: SNIPPET_NAME_PROPERTY_NAME,
propertyValue: PROPERTY_TYPE,
description: SNIPPET_NAME_PROPERTY_DESCRIPTION,
}
],
extraInputs: [blobInputBinding],
handler: getSnippet
});
// SaveSnippet function - saves a snippet with a name
export async function saveSnippet(_message: unknown, context: InvocationContext): Promise<string> {
console.info('Saving snippet');
// Get snippet name and content from the tool arguments
const mcptoolargs = context.triggerMetadata.mcptoolargs as {
snippetname?: string;
snippet?: string;
};
const snippetName = mcptoolargs?.snippetname;
const snippet = mcptoolargs?.snippet;
if (!snippetName) {
return "No snippet name provided";
}
if (!snippet) {
return "No snippet content provided";
}
// Save the snippet to blob storage using the output binding
context.extraOutputs.set(blobOutputBinding, snippet);
console.info(`Saved snippet: ${snippetName}`);
return snippet;
}
// Register the SaveSnippet tool
app.mcpTool('savesnippet', {
toolName: SAVE_SNIPPET_TOOL_NAME,
description: SAVE_SNIPPET_TOOL_DESCRIPTION,
toolProperties: [
{
propertyName: SNIPPET_NAME_PROPERTY_NAME,
propertyValue: PROPERTY_TYPE,
description: SNIPPET_NAME_PROPERTY_DESCRIPTION,
},
{
propertyName: SNIPPET_PROPERTY_NAME,
propertyValue: PROPERTY_TYPE,
description: SNIPPET_PROPERTY_DESCRIPTION,
}
],
extraOutputs: [blobOutputBinding],
handler: saveSnippet
});
请注意,host.json 文件还包含对实验性捆绑包的引用,这对于使用此功能的应用程序是必需的:
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle.Experimental",
"version": "[4.*, 5.0.0)"
}
下一步
- 为您的 MCP 服务器添加 API Management
- 为您的 MCP 服务器添加 EasyAuth
- 使用 VNET_ENABLED=true 标志启用 VNET
- 了解有关 Microsoft 的相关 MCP 工作 的更多信息