Skip to main content
Glama

使用 Azure Functions (Node.js/TypeScript) 开始使用远程 MCP 服务器

这是一个快速入门模板,可帮助您使用 Azure Functions 轻松构建自定义远程 MCP 服务器并将其部署到云端。您可以在本地计算机上进行克隆/恢复/运行并进行调试,然后几分钟内azd up其部署到云端。MCP 服务器采用密钥和 HTTPS 协议进行安全保护,并支持使用 EasyAuth 和/或 API Management 进行 OAuth 授权,以及使用 VNET 进行网络隔离。

观看视频概述

如果您正在寻找更多语言的示例,请查看.NET/C#Python版本。

在 GitHub Codespaces 中打开

以下是使用 Azure Functions 的远程 MCP 服务器的架构图:

架构图

先决条件

Related MCP server: Azure MCP Server

准备本地环境

此特定示例需要 Azure 存储模拟器,因为我们将从 Blob 存储中保存和获取片段。

  1. 启动 Azurite

    docker run -p 10000:10000 -p 10001:10001 -p 10002:10002 \ mcr.microsoft.com/azure-storage/azurite

**请注意,**如果您使用来自 VS Code 扩展的 Azurite,则需要运行Azurite: Start ,否则您将看到错误。

从终端本地运行 MCP 服务器

  1. 安装依赖项

    npm install
  2. 构建项目

    npm run build
  3. 在本地启动 Functions 主机:

    func start

**注意:**默认情况下,这将使用 webhooks 路由: /runtime/webhooks/mcp/sse 。稍后我们将在 Azure 中使用它来设置客户端/主机调用的密钥: /runtime/webhooks/mcp/sse?code=<system_key>

在客户端/主机中使用本地MCP 服务器

VS Code - Copilot 编辑

  1. 从命令面板添加 MCP 服务器并将 URL 添加到正在运行的函数应用程序的 SSE 端点:

    http://0.0.0.0:7071/runtime/webhooks/mcp/sse
  2. 选择**HTTP(服务器发送事件)**作为要添加的 MCP 服务器类型。

  3. 输入正在运行的函数应用的 SSE 终结点的 URL

  4. 输入服务器 ID。(可以是任何您想要的名称)

  5. 选择是否要在用户设置(适用于所有应用程序)或工作区设置(仅适用于此应用程序)中运行此功能

  6. 从命令面板列出 MCP 服务器并启动服务器。上一步可能已经启动了您的本地服务器。如果是这样,您可以跳过此步骤。

  7. 在 Copilot 聊天代理模式下输入提示来触发该工具,例如,选择一些代码并输入此提示

    Say Hello
    Save this snippet as snippet1
    Retrieve snippet1 and apply to newFile.ts
  8. 当系统提示运行该工具时,单击**“继续”**表示同意。

  9. 完成后,在终端窗口中按 Ctrl+C 停止func.exe主机进程,然后从命令面板列出 MCP 服务器并停止本地服务器。

MCP 检查器

  1. 新的终端窗口中,安装并运行 MCP Inspector

    npx @modelcontextprotocol/inspector node build/index.js
  2. 如果先前已停止函数应用,请在本地启动 Functions 主机:

    func start
  3. 按住 CTRL 键并单击,即可从应用程序显示的 URL 加载 MCP Inspector Web 应用程序(例如http://0.0.0.0:5173/#resources

  4. 将传输类型设置为SSE

  5. 将 URL 设置为正在运行的 Function App 的 SSE 端点并进行连接

    http://0.0.0.0:7071/runtime/webhooks/mcp/sse
  6. 列出工具。单击某个工具并运行工具

  7. 完成后,在终端窗口中按 Ctrl+C 停止func.exe主机进程,在终端窗口中按 Ctrl+C 停止@modelcontextprotocol/inspector主机进程。

部署到 Azure 以进行远程 MCP

或者,您可以选择加入示例中使用的 VNet。(如果选择此选项,请在azd up之前执行此操作)

azd env set VNET_ENABLED true

运行此azd命令来配置函数应用程序及其所需的 Azure 资源,并部署代码:

azd up

注意API 管理可用于改进 MCP 服务器的安全性和策略,而应用服务内置身份验证可用于设置您最喜欢的 OAuth 提供程序(包括 Entra)。

从客户端连接到远程MCP 服务器函数应用程序

客户端需要一个密钥才能调用新的托管 SSE 终结点,其格式为https://<funcappname>.azurewebsites.net/runtime/webhooks/mcp/sse 。 默认情况下,托管函数需要一个系统密钥,该密钥可从门户或 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 中连接到远程 MCP 服务器 - GitHub Copilot

对于 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" } } }
  1. 在服务器remote-mcp-function中,单击mcp.json文件内的 Start :

  2. 当 VS Code 提示时,输入您在 Azure 门户中创建的函数应用程序的名称。

  3. 在提示符中输入Azure Functions MCP Extension System Key 。您可以从 Azure 门户为您的函数应用复制此密钥,方法是转到“函数”菜单项,然后选择“应用密钥”,然后从“系统密钥”中复制mcp_extension密钥。

  4. 在 Copilot 聊天代理模式下输入提示来触发该工具,例如,选择一些代码并输入此提示

    Say Hello
    Save this snippet as snippet1
    Retrieve snippet1 and apply to newFile.ts

重新部署代码

您可以根据需要多次运行azd up命令,以配置 Azure 资源并将代码更新部署到函数应用程序。

NOTE

已部署的代码文件始终会被最新的部署包覆盖。

清理资源

使用完函数应用和相关资源后,可以使用此命令从 Azure 中删除函数应用及其相关资源,并避免产生任何进一步的费用:

azd down

源代码

getSnippetsaveSnippet端点的函数代码定义在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)" }

后续步骤

-
security - not tested
A
license - permissive license
-
quality - not tested

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Azure-Samples/remote-mcp-functions-typescript'

If you have feedback or need assistance with the MCP directory API, please join our Discord server