Azure Functions 的 MCP 工具触发器

使用 MCP 工具触发器在 模型内容协议 (MCP) 服务器中定义工具终结点。 客户端语言模型和代理可以使用工具执行特定任务,例如存储或访问代码片段。

有关设置和配置详细信息,请参阅概述

示例:

注释

对于 C#,Azure Functions MCP 扩展仅支持 隔离的辅助角色模型

此代码创建一个终结点,用于公开一个名为将命名 SaveSnippet 代码片段保存到 Blob 存储的工具。

private const string BlobPath = "snippets/{mcptoolargs.snippetname}.json";

[Function(nameof(SaveSnippet))]
[BlobOutput(BlobPath)]
public string SaveSnippet(
    [McpToolTrigger("save_snippet", "Saves a code snippet into your snippet collection.")]
        ToolInvocationContext context,
    [McpToolProperty("snippetname", "The name of the snippet.", isRequired: true)]
        string name,
    [McpToolProperty("snippet", "The code snippet.", isRequired: true)]
        string snippet
)
{
    return snippet;
}

此代码创建一个终结点,用于公开一个名为 GetSnippet 的工具,该工具尝试按名称从 Blob 存储检索代码片段。

private const string BlobPath = "snippets/{mcptoolargs.snippetname}.json";

[Function(nameof(GetSnippet))]
public object GetSnippet(
    [McpToolTrigger("get_snippets", "Gets code snippets from your snippet collection.")]
        ToolInvocationContext context,
    [BlobInput(BlobPath)] string snippetContent
)
{
    return snippetContent;
}

函数 GetSnippet 的工具属性在以下项中 Program.cs配置:

var builder = FunctionsApplication.CreateBuilder(args);

builder.ConfigureFunctionsWebApplication();

builder.Services
    .AddApplicationInsightsTelemetryWorkerService()
    .ConfigureFunctionsApplicationInsights();

builder
    .ConfigureMcpTool("get_snippets")
    .WithProperty("snippetname", "string", "The name of the snippet.", required: true);

builder.Build().Run();

小窍门

上面的示例对两者 Program.cs 中“get_snippets”工具的名称和函数等内容使用了文本字符串。 请考虑改用共享常量字符串来保持项目之间的同步。

有关完整的代码示例,请参阅 SnippetTool.cs

此代码创建一个终结点,用于公开一个名为将命名 SaveSnippets 代码片段保存到 Blob 存储的工具。

@FunctionName("SaveSnippets")
@StorageAccount("AzureWebJobsStorage")
public String saveSnippet(
        @McpToolTrigger(
                name = "saveSnippets",
                description = "Saves a text snippet to your snippets collection."
        )
        String mcpToolInvocationContext,
        @McpToolProperty(
                name = "snippetName",
                propertyType = "string",
                description = "The name of the snippet.",
                required = true
        )
        String snippetName,
        @McpToolProperty(
                name = "snippet",
                propertyType = "string",
                description = "The content of the snippet.",
                required = true
        )
        String snippet,
        @BlobOutput(name = "outputBlob", path = "snippets/{mcptoolargs.snippetName}.json")
        OutputBinding<String> outputBlob,
        final ExecutionContext context
) {
    // Log the entire incoming JSON for debugging
    context.getLogger().info(mcpToolInvocationContext);

    // Log the snippet name and content
    context.getLogger().info("Saving snippet with name: " + snippetName);
    context.getLogger().info("Snippet content:\n" + snippet);

    // Write the snippet content to the output blob
    outputBlob.setValue(snippet);

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

此代码创建一个终结点,用于公开一个名为 GetSnippets 的工具,该工具尝试按名称从 Blob 存储检索代码片段。

@FunctionName("GetSnippets")
@StorageAccount("AzureWebJobsStorage")
public String getSnippet(
        @McpToolTrigger(
                name = "getSnippets",
                description = "Gets a text snippet from your snippets collection."
        )
        String mcpToolInvocationContext,
        @McpToolProperty(
                name = "snippetName",
                propertyType = "string",
                description = "The name of the snippet.",
                required = true
        )
        String snippetName,
        @BlobInput(name = "inputBlob", path = "snippets/{mcptoolargs.snippetName}.json")
        String inputBlob,
        final ExecutionContext context
) {
    // Log the entire incoming JSON for debugging
    context.getLogger().info(mcpToolInvocationContext);

    // Log the snippet name and the fetched snippet content from the blob
    context.getLogger().info("Retrieving snippet with name: " + snippetName);
    context.getLogger().info("Snippet content:");
    context.getLogger().info(inputBlob);

    // Return the snippet content or a not found message
    if (inputBlob != null && !inputBlob.trim().isEmpty()) {
        return inputBlob;
    } else {
        return "Snippet '" + snippetName + "' not found.";
    }
}

有关完整的代码示例,请参阅 Snippets.java

JavaScript 的示例代码当前不可用。 有关使用 Node.js的常规指南,请参阅 TypeScript 示例。

此代码创建一个终结点,用于公开一个名为将命名 savesnippet 代码片段保存到 Blob 存储的工具。

app.mcpTool("saveSnippet", {
  toolName: SAVE_SNIPPET_TOOL_NAME,
  description: SAVE_SNIPPET_TOOL_DESCRIPTION,
  toolProperties: [
    {
      propertyName: SNIPPET_NAME_PROPERTY_NAME,
      propertyType: PROPERTY_TYPE,
      description: SNIPPET_NAME_PROPERTY_DESCRIPTION,
    },
    {
      propertyName: SNIPPET_PROPERTY_NAME,
      propertyType: PROPERTY_TYPE,
      description: SNIPPET_PROPERTY_DESCRIPTION,
    },
  ],
  extraOutputs: [blobOutputBinding],
  handler: saveSnippet,
});

此代码处理 savesnippet 触发器:

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;
}

此代码创建一个终结点,用于公开一个名为 getsnippet 的工具,该工具尝试按名称从 Blob 存储检索代码片段。

app.mcpTool("getSnippet", {
  toolName: GET_SNIPPET_TOOL_NAME,
  description: GET_SNIPPET_TOOL_DESCRIPTION,
  toolProperties: [
    {
      propertyName: SNIPPET_NAME_PROPERTY_NAME,
      propertyType: PROPERTY_TYPE,
      description: SNIPPET_NAME_PROPERTY_DESCRIPTION,
    },
  ],
  extraInputs: [blobInputBinding],
  handler: getSnippet,
});

此代码处理 getsnippet 触发器:

export async function getSnippet(
  _toolArguments: 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;
}

有关完整的代码示例,请参阅 snippetsMcpTool.ts

此代码使用 mcp_tool_trigger 修饰器创建一个终结点,以公开一个名为 save_snippet 将命名代码片段保存到 Blob 存储的工具。

@app.mcp_tool_trigger(
    arg_name="context",
    tool_name="save_snippet",
    description="Save a snippet with a name.",
    tool_properties=tool_properties_save_snippets_json,
)
@app.blob_output(arg_name="file", 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 "No snippet name provided"

    if not snippet_content_from_args:
        return "No snippet content provided"

    file.set(snippet_content_from_args)
    logging.info(f"Saved snippet: {snippet_content_from_args}")
    return f"Snippet '{snippet_content_from_args}' saved successfully"

此代码使用 mcp_tool_trigger 修饰器创建一个终结点,以公开一个名为 get_snippet 的工具,该工具尝试按名称从 Blob 存储检索代码片段。

@app.mcp_tool_trigger(
    arg_name="context",
    tool_name="get_snippet",
    description="Retrieve a snippet by name.",
    tool_properties=tool_properties_get_snippets_json,
)
@app.blob_input(arg_name="file", connection="AzureWebJobsStorage", path=_BLOB_PATH)
def get_snippet(file: func.InputStream, context) -> str:
    """
    Retrieves a snippet by name from Azure Blob Storage.

    Args:
        file (func.InputStream): The input binding to read the snippet from Azure Blob Storage.
        context: The trigger context containing the input arguments.

    Returns:
        str: The content of the snippet or an error message.
    """
    snippet_content = file.read().decode("utf-8")
    logging.info(f"Retrieved snippet: {snippet_content}")
    return snippet_content

有关完整的代码示例,请参阅 function_app.py

重要

MCP 扩展目前不支持 PowerShell 应用。

特性

C# 库用于 McpToolTriggerAttribute 定义函数触发器。

该特性的构造函数采用以下参数:

参数 DESCRIPTION
ToolName (必需)由 MCP 触发器终结点公开的工具的名称。
说明 (可选)客户端工具终结点的友好说明。

请参阅 “用法 ”,了解如何将终结点的属性定义为输入参数。

批注

@McpToolTrigger 注创建一个函数,用于在远程 MCP 服务器中公开工具终结点。

注释支持以下配置选项:

参数 DESCRIPTION
name (必需)由 MCP 触发器终结点公开的工具的名称。
说明 (可选)客户端工具终结点的友好说明。

@McpToolProperty 注定义工具的各个属性。 函数中的每个属性参数都应用此批注进行批注。

@McpToolProperty 注支持以下配置选项:

参数 DESCRIPTION
name (必需) 向客户端公开的工具属性的名称。
propertyType (必需) 工具属性的类型。 有效类型包括:string、、numberintegerboolean、 。 object
说明 (可选)工具属性的作用说明。
(必需) (可选) 如果设置为 true,则工具属性是必需的作为工具调用的参数。 默认为 false

修饰器

仅适用于 Python v2 编程模型。

mcp_tool_trigger修饰器需要 1.24.0 或更高版本的azure-functions。 支持 mcp_tool_trigger以下 MCP 触发器属性:

资产 DESCRIPTION
arg_name 函数代码中用于访问执行上下文的变量名称(通常 context)。
tool_name (必需)函数终结点公开的 MCP 服务器工具的名称。
说明 函数终结点公开的 MCP 服务器工具的说明。
tool_properties 向客户端公开工具属性的一个或多个属性对象的 JSON 字符串表示形式。

配置

触发器支持以下绑定选项,这些选项在代码中定义:

选项 DESCRIPTION
类型 必须设置为 mcpToolTrigger。 仅用于泛型定义。
toolName (必需)函数终结点公开的 MCP 服务器工具的名称。
说明 函数终结点公开的 MCP 服务器工具的说明。
toolProperties 向客户端公开工具属性的对象数组 toolProperty
extraOutputs 定义后,将函数输出发送到另一个绑定。
处理器 包含实际函数代码的方法。

有关完整示例,请参阅示例部分

用法

MCP 工具触发器可以绑定到以下类型:

类型 DESCRIPTION
ToolInvocationContext 表示工具调用的对象,包括调用的工具名称和参数。
JSON 可序列化类型 函数尝试将工具参数反序列化为普通的 CLR 对象 (POCO) 类型。 此类型还用于 定义工具属性

绑定到 JSON 可序列化类型时,还可以选择包含 ToolInvocationContext 类型的参数来访问工具调用信息。

工具属性

MCP 客户端使用参数调用工具,为工具的作提供数据和上下文。 客户端知道如何根据工具作为协议的一部分播发的属性收集和传递这些参数。 因此,需要在函数代码中定义工具的属性。

定义工具属性时,默认情况下它是可选的,客户端可以在调用该工具时省略它。 如果工具在没有它们的情况下无法运行,则需要将属性显式标记为必需。

注释

默认情况下,MCP 扩展预览版的早期版本使所有工具属性都是必需的。 此行为已更改为版本 1.0.0-preview.7,现在必须显式将属性标记为必需。

在 C# 中,可以通过多种方式定义工具的属性。 使用哪种方法是代码样式首选项。 选项包括:

  • 函数使用 McpToolProperty 特性获取输入参数。
  • 使用属性定义自定义类型,函数绑定到该类型。
  • 可以使用该文件 FunctionsApplicationBuilderProgram.cs 定义属性。

可以通过将 McpToolProperty 属性应用于函数中的输入绑定样式参数来定义一个或多个工具属性。

McpToolPropertyAttribute 类型支持以下属性:

资产 DESCRIPTION
PropertyName 向客户端公开的工具属性的名称。
说明 工具属性的作用说明。
是必需的 (可选)如果设置为 true,则需要工具属性作为工具调用的参数。 默认为 false

属性类型是从应用属性的参数的类型推断出来的。 例如 [McpToolProperty("snippetname", "The name of the snippet.", true)] string name ,定义 MCP 消息中类型命名 snippetnamestring 必需工具属性。

可以在SaveSnippet”中查看工具中使用的这些属性。

在 Java 中,通过使用单个函数参数上的 @McpToolProperty 注释来定义工具属性。 每个表示工具属性的参数都应用此批注进行批注,并指定属性名称、类型、说明以及是否需要。

可以看到 示例中使用的这些批注。

可以在触发器定义的 toolProperties 字段中配置工具属性,这是对象的数组的 ToolProperty 字符串表示形式。

对象 ToolProperty 具有以下结构:

{
    "propertyName": "Name of the property",
    "propertyType": "Type of the property",
    "description": "Optional property description",
    "isRequired": true|false,
    "isArray": true|false
}

对象的字段 ToolProperty 为:

资产 DESCRIPTION
propertyName 向客户端公开的工具属性的名称。
propertyType 工具属性的类型。 有效类型包括:string、、numberintegerboolean、 。 object 请参阅 isArray 数组类型。
说明 工具属性的作用说明。
isRequired (可选)如果设置为 true,则需要工具属性作为工具调用的参数。 默认为 false
isArray (可选)如果设置为 true,则工具属性是指定属性类型的数组。 默认为 false

有关详细信息,请参阅 示例

host.json 设置

host.json 文件包含控制 MCP 触发器行为的设置。 有关可用设置的详细信息,请参阅 host.json 设置部分。