返回市场
远程-mcp功能-python

远程-mcp功能-python

作者:Azure-Samples102 星标更新:2025-11-06

项目介绍

使用 Azure Functions (Python) 快速入门远程 MCP 服务器

这是一个快速入门模板,用于轻松构建并部署一个自定义的远程 MCP 服务器到云端,使用 Azure Functions 和 Python。您可以在本地机器上进行克隆/恢复/运行调试,并通过 azd up 在几分钟内将其部署到云端。MCP 服务器通过设计使用密钥和 HTTPS 进行安全防护,并允许使用内置身份验证和/或 API 管理 提供更多的 OAuth 选项,以及通过 VNET 实现网络隔离。

如果您需要更多语言的示例,请查看 .NET/C#Node.js/TypeScript 版本。

在 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. 切换到新终端窗口中的 src 文件夹:

    cd src
    
  2. 安装 Python 依赖项:

    pip install -r requirements.txt
    

注意 在执行 pip install 之前创建虚拟环境是一个最佳实践,以避免依赖项问题/冲突,或者如果您正在 CodeSpaces 中运行。有关更多信息,请参阅 VS Code 中的 Python 环境

  1. 本地启动函数主机:

    func start
    

注意 默认情况下,这将使用 webhook 路由:/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. 列出 MCP 服务器 从命令面板中开始服务器

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

    说你好
    
    将此片段保存为 snippet1
    
    检索 snippet1 并应用于 newFile.py
    
  4. 当提示运行工具时,点击 继续 同意

  5. 完成后,在终端窗口中按 Ctrl+C 停止函数主机进程。

MCP Inspector

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

    npx @modelcontextprotocol/inspector
    
  2. CTRL 点击以从应用程序显示的 URL 加载 MCP Inspector Web 应用程序(例如 http://0.0.0.0:5173/#resources)

  3. 设置传输类型为 SSE

  4. 设置 URL 为正在运行的函数应用的 SSE 终结点并 连接

    http://0.0.0.0:7071/runtime/webhooks/mcp/sse
    

注意 此步骤在 CodeSpaces 中不起作用。请继续部署到远程 MCP。

  1. 列出工具。点击一个工具并 运行工具

验证 Azurite 中的本地 blob 存储

在本地测试片段保存功能之后,您可以验证 blob 是否正确存储在本地 Azurite 存储模拟器中。

使用 Azure 存储资源管理器

  1. 打开 Azure 存储资源管理器
  2. 在左侧面板中,展开 模拟器 & 已附加存储帐户(模拟器 - 默认端口) (密钥)
  3. 导航到 Blob 容器snippets
  4. 您应该在此容器中看到任何已保存的片段作为 blob 文件
  5. 双击任何 blob 查看其内容并验证片段数据是否正确保存

使用 Azure CLI(替代方法)

如果您更喜欢使用命令行,也可以使用 Azure CLI 和存储模拟器来验证 blob:

# 列出 snippets 容器中的 blob
az storage blob list --container-name snippets --connection-string "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;"
# 下载特定 blob 以查看其内容
az storage blob download --container-name snippets --name <blob-name> --file <local-file-path> --connection-string "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;"

此验证步骤确保您的 MCP 服务器正确与本地存储模拟器交互,并且在部署到 Azure 之前 blob 存储功能按预期工作。

部署到 Azure 以实现远程 MCP

运行此 azd 命令以提供函数应用及其所需的所有 Azure 资源,并部署您的代码:

azd up

您可以选择使用 VNet。要在 azd up 之前启用 VNet,请执行以下操作:

azd env set VNET_ENABLED true

此外,可以使用 API 管理 来提高安全性并为您的 MCP 服务器制定策略,还可以使用 App Service 内置身份验证来设置您最喜欢的 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 - GitHub Copilot 中连接到远程 MCP 服务器

对于 GitHub Copilot 内部的 VS Code,您应将密钥设置为 mcp.json 中的 x-functions-key 头,并使用 https://<funcappname>.azurewebsites.net/runtime/webhooks/mcp/sse 作为 URL。以下示例使用输入,并在您从 VS Code 启动服务器时提示您提供密钥。请注意,mcp.json 已经包含在此仓库中,并将被 VS Code 捕获。点击服务器上的“开始”以提供包括 functionapp-name(在您的 /.azure/*/.env 文件中)和 functions-mcp-extension-system-key 的值,后者可以通过上面的 CLI 命令或门户中的 API 密钥获得。

{
    "inputs": [
        {
            "type": "promptString",
            "id": "functions-mcp-extension-system-key",
            "description": "Azure Functions MCP 扩展系统密钥",
            "password": true
        },
        {
            "type": "promptString",
            "id": "functionapp-name",
            "description": "Azure Functions 应用名称"
        }
    ],
    "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 Inspector,您可以在 URL 中包含密钥:https://<funcappname>.azurewebsites.net/runtime/webhooks/mcp/sse?code=<your-mcp-extension-system-key>

对于 GitHub Copilot 内部的 VS Code,您应将密钥设置为 mcp.json 中的 x-functions-key 头,并使用 https://<funcappname>.azurewebsites.net/runtime/webhooks/mcp/sse 作为 URL。以下示例使用输入,并在您从 VS Code 启动服务器时提示您提供密钥:

{
    "inputs": [
        {
            "type": "promptString",
            "id": "functions-mcp-extension-system-key",
            "description": "Azure Functions MCP 扩展系统密钥",
            "password": true
        }
    ],
    "servers": {
        "my-mcp-server": {
            "type": "sse",
            "url": "<funcappname>.azurewebsites.net/runtime/webhooks/mcp/sse",
            "headers": {
                "x-functions-key": "${input:functions-mcp-extension-system-key}"
            }
        }
    }
}

重新部署您的代码

您可以多次运行 azd up 命令以提供 Azure 资源并部署代码更新到您的函数应用。

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

清理资源

当您完成对函数应用及相关资源的工作后,可以使用此命令从 Azure 删除函数应用及其相关资源,以避免产生进一步的成本:

azd down

有用的 Azure 命令

一旦您的应用程序部署完毕,您可以使用这些命令来管理和监控您的应用程序:

# 从环境文件获取您的函数应用名称
FUNCTION_APP_NAME=$(cat .azure/$(cat .azure/config.json | jq -r '.defaultEnvironment')/env.json | jq -r '.FUNCTION_APP_NAME')
echo $FUNCTION_APP_NAME

# 获取资源组
RESOURCE_GROUP=$(cat .azure/$(cat .azure/config.json | jq -r '.defaultEnvironment')/env.json | jq -r '.AZURE_RESOURCE_GROUP')
echo $RESOURCE_GROUP

# 查看函数应用日志
az webapp log tail --name $FUNCTION_APP_NAME --resource-group $RESOURCE_GROUP

# 重新部署应用程序而不提供新的资源
azd deploy

源代码

get_snippetsave_snippet 终结点的函数代码定义在 src 目录中的 Python 文件中。MCP 函数注解将这些函数暴露为 MCP 服务器工具。

这是来自 function_app.py 文件的实际代码:


@app.generic_trigger(arg_name="context", type="mcpToolTrigger", toolName="hello", 
                     description="Hello world.", 
                     toolProperties="[]")
def hello_mcp(context) -> None:
    """
    一个简单的函数,返回问候消息。

    参数:
        context: 触发上下文(在此函数中未使用)。

    返回:
        str: 问候消息。
    """
    return "Hello I am MCPTool!"


@app.generic_trigger(
    arg_name="context",
    type="mcpToolTrigger",
    toolName="getsnippet",
    description="通过名称检索片段。",
    toolProperties=tool_properties_get_snippets_json
)
@app.generic_input_binding(
    arg_name="file",
    type="blob",
    connection="AzureWebJobsStorage",
    path=_BLOB_PATH
)
def get_snippet(file: func.InputStream, context) -> str:
    """
    从 Azure Blob 存储中通过名称检索片段。

    参数:
        file (func.InputStream): 读取 Azure Blob 存储中片段的输入绑定。
        context: 包含输入参数的触发上下文。

    返回:
        str: 片段的内容或错误消息。
    """
    snippet_content = file.read().decode("utf-8")
    logging.info(f"检索片段: {snippet_content}")
    return snippet_content


@app.generic_trigger(
    arg_name="context",
    type="mcpToolTrigger",
    toolName="savesnippet",
    description="通过名称保存片段。",
    toolProperties=tool_properties_save_snippets_json
)                   
@app.generic_output_binding(
    arg_name="file",
    type="blob",
    connection="AzureWebJobsStorage",
    path=_BLOB_PATH
)
def save_snippet(file: func.Out[str], context) -> str:
    content = json.loads(context)
    snippet_name_from_args = content["arguments"][_SNIPPET_NAME_PROPERTY_NAME]
    snippet_content_from_args = content["arguments"][_SNIPPET_PROPERTY_NAME]

    if not snippet_name_from_args:
        return "未提供片段名称"

    if not snippet_content_from_args:
        return "未提供片段内容"
 
    file.set(snippet_content_from_args)
    logging.info(f"保存片段: {snippet_content_from_args}")
    return f"片段 '{snippet_content_from_args}' 保存成功"

请注意,host.json 文件还包括对实验性捆绑包的引用,这是使用此功能的应用程序所必需的:

"extensionBundle": {
  "id": "Microsoft.Azure.Functions.ExtensionBundle.Experimental",
  "version": "[4.*, 5.0.0)"
}

下一步