Remote MCP Functions (远程MCP功能)

@Azure-Samples/remote-mcp-functions-typescript
0 Stars 322 次浏览 Azure-Samples 更新于 2026-08-23

一个基于云的自定义 MCP 服务器,使用 Azure Functions,支持通过密钥、HTTPS、OAuth 和网络隔离选项进行安全通信来保存和检索代码片段。

该服务暂未提供标准配置,请参考 README 手动接入

服务介绍

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

这是一个快速启动模板,可以轻松地使用 Azure 函数构建并将自定义远程 MCP 服务器部署到云端。您可以克隆/恢复/在本地机器上调试运行,并通过 azd up 命令在几分钟内将其部署到云端。MCP 服务器默认使用密钥和 HTTPS 进行安全保护,并允许使用 EasyAuth 和/或 API 管理进行 OAuth 的更多选项,以及使用 VNET 进行网络隔离。

观看视频概述

如果您希望查看其他语言的示例,请参阅 .NET/C#Python 版本。

在 GitHub Codespaces 中打开

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

架构图

先决条件

准备您的本地环境

此特定示例需要一个 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 到你正在运行的 Function 应用的 SSE 端点:

    http://0.0.0.0:7071/runtime/webhooks/mcp/sse
    
  2. 选择 HTTP (Server-Sent-Events) 作为要添加的 MCP 服务器类型。

  3. 输入你正在运行的函数应用的 SSE 端点 URL。

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

  5. 选择是否要在用户设置中运行此设置(对你所有的应用程序都可用)还是在工作区设置中运行(仅对此应用程序可用)。

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

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

    说你好
    
    将此代码片段保存为 snippet1 
    
    检索 snippet1 并应用于 newFile.ts
    
  8. 当被提示运行工具时,点击 继续 表示同意。

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

MCP 检查器

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

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

    func start
    
  3. 通过 CTRL 单击加载由应用程序显示的 URL 中的 MCP 检查器 Web 应用程序(例如 http://0.0.0.0:5173/#resources)

  4. 将传输类型设置为 SSE

  5. 设置为你正在运行的 Function 应用的 SSE 端点 URL 并连接

    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 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"
        }
    }
}
  1. 单击 mcp.json 文件中的服务器 remote-mcp-function 上的“启动”按钮:

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

  3. Azure Functions MCP 扩展系统密钥 输入到提示中。您可以通过进入功能菜单项,然后选择 App Keys,并复制 mcp_extension 密钥来从 Azure 门户中复制此密钥。

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

    说你好
    
    将此代码片段保存为 snippet1 
    
    检索 snippet1 并应用于 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)"
}

下一步

  • 为您的 MCP 服务器添加 API Management
  • 为您的 MCP 服务器添加 EasyAuth
  • 使用 VNET_ENABLED=true 标志启用 VNET
  • 了解有关 Microsoft 的相关 MCP 工作 的更多信息

相关 MCP 服务