次の方法で共有

快速入门:使用 Azure Functions 生成自定义远程 MCP 服务器

在本快速入门中,你将使用 Azure 开发人员 CLI 从模板项目创建自定义远程模型上下文协议 (azdMCP) 服务器。 此 MCP 服务器使用 Azure Functions MCP 服务器扩展为 AI 模型、代理和助手提供工具。 还可以使用 MCP 服务器扩展 创建交互式 MCP 应用

在本地运行项目并使用 GitHub Copilot 验证代码后,可以在 Azure Functions 中将其部署到新的无服务器函数应用,该应用遵循当前安全且可缩放的部署最佳做法。

重要

尽管所有 Functions 语言都支持 创建自定义 MCP 服务器 ,但本快速入门方案目前仅包含 C#、Java、Python 和 TypeScript 的示例。 若要完成本快速入门,请在文章顶部选择其中一种受支持的语言。

本文支持适用于 Azure Functions 的 Node.js 编程模型版本 4。

本文支持适用于 Azure Functions 的 Python 编程模型版本 2。

先决条件

初始化项目

azd init使用命令从模板创建本地 Azure Functions 代码项目。

  1. 在 Visual Studio Code 中,打开要在其中创建项目的文件夹或工作区。
  1. 在终端中,运行以下命令 azd init

    azd init --template remote-mcp-functions-dotnet -e mcpserver-dotnet
    

    此命令从模板存储库中拉取项目文件,并在当前文件夹中初始化项目。 -e 标志设置当前环境的名称。 在 azd,环境维护您应用程序的独特部署上下文,并且您可以定义多个。 它也在 Azure 中你创建的资源组名称中使用。

  1. 在本地终端或命令提示符下运行以下命令 azd init

    azd init --template remote-mcp-functions-java -e mcpserver-java 
    

    此命令从模板存储库中拉取项目文件,并在当前文件夹中初始化项目。 -e 标志设置当前环境的名称。 在 azd,环境维护您应用程序的独特部署上下文,并且您可以定义多个。 它还用于在 Azure 中创建的资源的名称。

  1. 在本地终端或命令提示符下运行以下命令 azd init

    azd init --template remote-mcp-functions-typescript -e mcpserver-ts
    

    此命令从模板存储库中拉取项目文件,并在当前文件夹中初始化项目。 -e 标志设置当前环境的名称。 在 azd,环境维护您应用程序的独特部署上下文,并且您可以定义多个。 它还用于在 Azure 中创建的资源的名称。

  1. 在本地终端或命令提示符下运行以下命令 azd init

    azd init --template remote-mcp-functions-python -e mcpserver-python
    

    此命令从模板存储库中拉取项目文件,并在当前文件夹中初始化项目。 -e 标志设置当前环境的名称。 在 azd,环境维护您应用程序的独特部署上下文,并且您可以定义多个。 它还用于在 Azure 中创建的资源的名称。

启动存储模拟器

在本地运行代码项目时,使用 Azurite 模拟器模拟 Azure 存储帐户连接。

  1. 如果尚未 安装,请安装 Azurite

  2. F1。 在命令面板中,搜索并运行命令 Azurite: Start 以启动本地存储模拟器。

在本地运行 MCP 服务器

在终端窗口中,转到 FunctionsMcpTool 项目文件夹:

cd src/FunctionsMcpTool

Visual Studio Code 与 Azure Functions Core 工具 集成,让你在本地开发计算机上运行此项目。 若要在本地启动 Functions 应用,请按 F5 或在左侧活动栏中选择 “运行和调试 ”图标。

“终端”面板将显示 Core Tools 的输出。 应用在 终端 面板中启动,可以看到在本地运行的函数的名称。

使用 GitHub Copilot 进行验证

项目模板包含一个 .vscode/mcp.json 文件,该文件已经定义了 local-mcp-function 服务器并将其指向本地 MCP 终结点。 使用此配置通过 Visual Studio Code 中的 GitHub Copilot 验证代码:

  1. 打开.vscode/mcp.json文件,然后选择配置上方的“开始”local-mcp-function

  2. 在 Copilot 聊天 窗口中,确保已选择 代理 模式,选择 “配置工具” 图标,并验证是否已 MCP Server:local-mcp-function 在聊天中启用。

  3. 运行此命令:

    Say Hello
    

    当系统提示运行该工具时,选择 “在此工作区中允许” ,这样就不必继续授予权限。 提示运行后返回 Hello World 响应,且函数执行信息会被记录在日志中。

  4. 现在,在项目文件之一中选择一些代码,然后运行以下提示:

    Save this snippet as snippet1
    

    Copilot 存储代码片段并响应请求,并提供有关如何使用该工具检索代码片段 getSnippets 的信息。 再一次,你可以查看日志中的函数执行,并验证saveSnippets函数是否运行。

  5. 在 Copilot 聊天中,运行以下提示:

    Retrieve snippet1 and apply to NewFile
    

    Copilot 检索代码片段,将其添加到一个名为 NewFile 的文件中,并执行任何其他认为必要的操作,以使代码片段在项目中正确运行。 Functions 日志显示调用了getSnippets 端点。

  6. 完成测试后,按 Ctrl+C 停止 Functions 主机。

查看代码(可选)

可以查看定义 MCP 服务器工具的代码:

MCP 服务器工具的函数代码在 src 文件夹中定义。 该 McpToolTrigger 属性将函数公开为 MCP 服务器工具:

    [Function(nameof(SayHello))]
    public string SayHello(
        [McpToolTrigger(HelloToolName, HelloToolDescription)] ToolInvocationContext context
    )
    {
        logger.LogInformation("C# MCP tool trigger function processed a request.");
        return "Hello I am MCP Tool!";
    }
    [Function(nameof(GetSnippet))]
    public object GetSnippet(
        [McpToolTrigger(GetSnippetToolName, GetSnippetToolDescription)]
            ToolInvocationContext context,
        [BlobInput(BlobPath)] string snippetContent
    )
    {
        return snippetContent;
    }

    [Function(nameof(SaveSnippet))]
    [BlobOutput(BlobPath)]
    public string SaveSnippet(
        [McpToolTrigger(SaveSnippetToolName, SaveSnippetToolDescription)]
            ToolInvocationContext context,
        [McpToolProperty(SnippetNamePropertyName, SnippetNamePropertyDescription, true)]
            string name,
        [McpToolProperty(SnippetPropertyName, SnippetPropertyDescription, true)]
            string snippet
    )
    {
        return snippet;
    }
}

可以在 Azure Functions .NET MCP 服务器 GitHub 存储库中查看完整的项目模板。

MCP 服务器工具的函数代码在 src/main/java/com/function/ 文件夹中定义。 批 @McpToolTrigger 注将函数公开为 MCP 服务器工具:

                description = "The messages to be logged.",
                isRequired = true,
                isArray = true)
            String messages,
            final ExecutionContext functionExecutionContext
    ) {
        functionExecutionContext.getLogger().info("Hello, World!");
        functionExecutionContext.getLogger().info("Tool Name: " + mcpToolInvocationContext.getName());
        functionExecutionContext.getLogger().info("Transport Type: " + mcpToolInvocationContext.getTransportType());

        // Handle different transport types
        if (mcpToolInvocationContext.isHttpStreamable()) {
            functionExecutionContext.getLogger().info("Session ID: " + mcpToolInvocationContext.getSessionid());
        } else if (mcpToolInvocationContext.isHttpSse()) {
            if (mcpToolInvocationContext.getClientinfo() != null) {
                functionExecutionContext.getLogger().info("Client: " + 
                    mcpToolInvocationContext.getClientinfo().get("name").getAsString() + " v" +
        // Write the snippet content to the output blob
        outputBlob.setValue(snippet);

        return "Successfully saved snippet '" + snippetName + "' with " + snippet.length() + " characters.";
    }

    /**
     * Azure Function that handles retrieving a text snippet from Azure Blob Storage.
     * <p>
     * The function is triggered by an MCP Tool Trigger. The snippet name is provided
     * as an MCP tool property, and the snippet content is read from the blob at the 
     * path derived from the snippet name.
     *
     * @param mcpToolInvocationContext The JSON input from the MCP tool trigger.
     * @param snippetName   The name of the snippet to retrieve, provided as an MCP tool property.
     * @param inputBlob     The Azure Blob input binding that fetches the snippet content.
     * @param functionExecutionContext       The execution context for logging.
     */
    @FunctionName("GetSnippets")
    @StorageAccount("AzureWebJobsStorage")
    public String getSnippet(
            @McpToolTrigger(
                name = "getSnippets",
                description = "Gets a text snippet from your snippets collection.")
            String mcpToolInvocationContext,
            @McpToolProperty(
                name = SNIPPET_NAME_PROPERTY_NAME,
                propertyType = "string",
                description = "The name of the snippet.",
                isRequired = true)
            String snippetName,
            @BlobInput(name = "inputBlob", path = BLOB_PATH)
            String inputBlob,
            final ExecutionContext functionExecutionContext
    ) {
        // Log the entire incoming JSON for debugging
        functionExecutionContext.getLogger().info(mcpToolInvocationContext);

        // Log the snippet name and the fetched snippet content from the blob

可以在 Azure Functions Java MCP 服务器 GitHub 存储库中查看完整的项目模板。

MCP 服务器工具的函数代码在 src/function_app.py 文件中定义。 MCP 函数注释将这些函数公开为 MCP 服务器工具:


@app.mcp_tool()
def hello_mcp() -> str:
    """Hello world."""

@app.mcp_tool()
@app.mcp_tool_property(arg_name="snippetname", description="The name of the snippet.")
@app.blob_input(arg_name="file", connection="AzureWebJobsStorage", path=_BLOB_PATH)
def get_snippet(file: func.InputStream, snippetname: str) -> str:
    """Retrieve a snippet by name from Azure Blob Storage."""
    snippet_content = file.read().decode("utf-8")
    logging.info(f"Retrieved snippet: {snippet_content}")
    return snippet_content


@app.mcp_tool()
@app.mcp_tool_property(arg_name="snippetname", description="The name of the snippet.")
@app.mcp_tool_property(arg_name="snippet", description="The content of the snippet.")
@app.blob_output(arg_name="file", connection="AzureWebJobsStorage", path=_BLOB_PATH)
def save_snippet(file: func.Out[str], snippetname: str, snippet: str) -> str:
    """Save a snippet with a name to Azure Blob Storage."""
    if not snippetname:
        return "No snippet name provided"

    if not snippet:
        return "No snippet content provided"

    file.set(snippet)
    logging.info(f"Saved snippet: {snippet}")

可以在 Azure Functions Python MCP 服务器 GitHub 存储库中查看完整的项目模板。

MCP 服务器工具的函数代码在 src 文件夹中定义。 MCP 函数注册将这些函数公开为 MCP 服务器工具:

export async function mcpToolHello(_toolArguments:unknown, context: InvocationContext): Promise<string> {
    console.log(_toolArguments);
    // Get name from the tool arguments
    const mcptoolargs = context.triggerMetadata.mcptoolargs as {
        name?: string;
    };
    const name = mcptoolargs?.name;

    console.info(`Hello ${name}, I am MCP Tool!`);

    return `Hello ${name || 'World'}, 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.',
    toolProperties:{
        name: arg.string().describe('Required property to identify the caller.').optional()
    },
    handler: mcpToolHello
});
// SaveSnippet function - saves a snippet with a name
export async function saveSnippet(
  _toolArguments: 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;
}

可以在 Azure Functions TypeScript MCP 服务器 GitHub 存储库中查看完整的项目模板。

在本地验证 MCP 服务器工具后,可以将项目发布到 Azure。

部署到 Azure 云

此项目配置为使用 azd 将其部署到使用 Azure 的弹性消费计划的新函数应用中。 该项目包括一组 Bicep 文件,azd 使用这些文件可以按照最佳做法为 Flex 消耗计划创建安全部署。

  1. 在 Visual Studio Code 中,按 F1 打开命令面板。 搜索并运行命令 Azure Developer CLI (azd): Package, Provision and Deploy (up)。 然后,使用 Azure 帐户登录。

  2. 出现提示时,选择以下所需的部署参数:

    参数 Description
    Azure 订阅 创建资源时使用的订阅。
    Azure 位置 要在其中创建包含新 Azure 资源的资源组的 Azure 区域。 仅显示当前支持 Flex 消耗计划的区域。
    vnetEnabled False 若要跳过创建虚拟网络资源,可简化部署。

    命令成功完成后,你会看到指向所创建资源的链接。

连接到远程 MCP 服务器

MCP 服务器现在在 Azure 中运行。 项目模板在 remote-mcp-function 中包含一个已配置为连接到远程服务器的 .vscode/mcp.json 条目。 启动此服务器时,VS Code 会提示你输入访问远程 MCP 终结点所需的函数应用名称和系统密钥。

  1. 运行该脚本,使用 azd Azure 命令行接口打印输出函数应用名称和访问工具所需的系统密钥(mcp_extension)。

    eval $(azd env get-values --output dotenv)
    MCP_EXTENSION_KEY=$(az functionapp keys list --resource-group $AZURE_RESOURCE_GROUP \
        --name $AZURE_FUNCTION_NAME --query "systemKeys.mcp_extension" -o tsv)
    printf "Function app name: %s\n" "$SERVICE_API_NAME"
    printf "MCP Server key: %s\n" "$MCP_EXTENSION_KEY"
    
  2. .vscode/mcp.json中,在配置上方选择remote-mcp-function

  3. 出现提示时,输入上一步中的函数应用名称和系统键值。

验证部署

现在可以让 GitHub Copilot 像在本地一样使用远程 MCP 工具,但现在代码在 Azure 中安全运行。 为确保一切正常工作,请重复执行您之前使用的相同命令。

清理资源

完成 MCP 服务器和相关资源的操作后,使用此命令从 Azure 中删除函数应用及其相关资源,以避免产生额外费用。

azd down 

后续步骤