简简单单 Online zuozuo :本心、输入输出、结果

在 Azure 上增强 AI 工作流:远程 MCP 工具触发器 + 你的第一个 TypeScript MCP 服务器


编辑 | 简简单单 Online zuozuo
地址 | https://blog.csdn.net/qq_15071263


如果觉得本文对你有帮助,欢迎关注、点赞、收藏、评论,谢谢

前言

在当今的 AI 应用开发中,智能代理(Agent)应用的工作流程通常从用户通过聊天界面或表单提交提示开始。代理接收提示后,会分析用户意图和需求,使用大语言模型(LLM)来获取任务、澄清细节,并将整体任务分解为子任务。

一旦代理清楚理解了目标,它就会选择最合适的专业工具或服务来实现目标。这些工具可能包括 API、数据库、生成式 AI(用于写作、图像生成等)或其他合作伙伴系统。根据任务的复杂程度,代理可能会安排或组合多个工具操作。

代理会持续评估和调整,重复调用工具,如果输出不足或不清楚,还会请求进一步澄清。在获得有形的或高质量的结果后,代理会合并输出,格式化响应,并将其返回给用户进行审查。这种自适应过程通过充分利用集成的工具和智能规划,为用户提供相关且有时甚至更优的答案。

然而,当前的架构缺乏灵活性,无法实现所有工具,这意味着工具只是特定代理应用程序的一部分。没有通用的方法来发布此工具供其他系统或应用程序使用。这就是模型上下文协议(Model Context Protocol,MCP)发挥作用的地方。

#Azure Functions #MCP #AI工作流 #TypeScript #无服务器计算 #模型上下文协议 #AI助手 #云原生

1

1、为什么需要标准协议?

2

当前的架构缺乏灵活性,无法实现所有工具,这意味着工具只是特定代理应用程序的一部分。没有通用的方法来发布此工具供其他系统或应用程序使用。

模型上下文协议(MCP) 正是解决这个问题的方案。MCP 是由 Anthropic 创建的系统,它不仅使工具易于插入单个应用程序,还允许许多不同的应用程序通过共享的、标准的方式来描述和公开功能,从而连接到这些工具。MCP 采用客户端-服务器架构建模。

其结构如下:服务器是通过标准 MCP 接口传达其特定功能的应用程序。客户端是使用这些功能来创建智能、深度集成体验的其他应用程序。下面给出了经典 MCP 架构的概述。

2、Microsoft Azure Functions 的帮助

3

代理缺乏灵活性的问题由 Microsoft 的解决方案解决,该解决方案包括最近在 Azure Functions 中发布的远程 MCP 功能。这是在开发智能 AI 工作流和工具方面向前迈出的一步,可以快速准确地连接云服务并与之协作。

通过直接在 Azure Functions 中使用 MCP,开发人员可以首次创建强大的、可扩展的 AI 工具,这些工具可以利用备受推崇的 Azure 平台的完整功能,同时由于无服务器计算,运行起来既轻松又经济。

3、什么是远程 MCP?

4

远程 MCP 是一项功能,允许将 Azure Functions 配置为 MCP 服务器,这些服务器公开自定义工具,GitHub Copilot、Claude 或其他 MCP 兼容客户端等 AI 助手可以发现和使用这些工具。

MCP 允许将 AI 逻辑连接到云原生操作,从而使 AI 代理能够:

  • 从 Azure 存储读取和写入数据
  • 查询数据库
  • 处理文件和文档
  • 与第三方 API 集成
  • 在云中执行复杂的业务逻辑

4、实际用例:智能代码片段管理

5

让我们探索一个实际场景,开发人员需要一个 AI 驱动的代码片段管理系统,该系统可以:

  • 保存和检索可重用的代码片段
  • 按名称和内容组织代码片段
  • 分析和总结多个代码片段

该系统演示了使用 MCP 工具触发器和 Azure Functions 输入和输出绑定的远程 MCP,这是它们最简单的形式。

实现
1. 代码片段存储和检索
import { app, InvocationContext, input, output, arg } from "@azure/functions";

// 与 C# ToolsInformation 类匹配的常量
const GET_SNIPPET_TOOL_NAME = "getsnippets";
const GET_SNIPPET_TOOL_DESCRIPTION = "Gets code snippets from your snippet collection.";
const SAVE_SNIPPET_TOOL_NAME = "savesnippet";
const SAVE_SNIPPET_TOOL_DESCRIPTION = "Saves a code snippet into your snippet collection.";
const SNIPPET_NAME_PROPERTY_NAME = "snippetname";
const SNIPPET_PROPERTY_NAME = "snippet";
const SNIPPET_NAME_PROPERTY_DESCRIPTION = "The name of the snippet.";
const SNIPPET_PROPERTY_DESCRIPTION = "The code snippet.";

// 定义 blob 输入和输出绑定
const blobInputBinding = input.storageBlob({
  connection: "AzureWebJobsStorage",
  path: `snippets/{mcptoolargs.${SNIPPET_NAME_PROPERTY_NAME}}.json`,
});

const blobOutputBinding = output.storageBlob({
  connection: "AzureWebJobsStorage",
  path: `snippets/{mcptoolargs.${SNIPPET_NAME_PROPERTY_NAME}}.json`,
});

// GetSnippet 函数 - 按名称检索代码片段
export async function getSnippet(
  _toolArguments: unknown,
  context: InvocationContext
): Promise<string> {
  console.info("Getting snippet");
  
  // 从工具参数中获取代码片段名称
  const mcptoolargs = context.triggerMetadata.mcptoolargs as { snippetname?: string; };
  const snippetName = mcptoolargs?.snippetname;
  console.info(`Snippet name: ${snippetName}`);
  
  if (!snippetName) {
    return "No snippet name provided";
  }
  
  // 从 blob 绑定获取内容 - 正确地从 extraInputs 检索
  const snippetContent = context.extraInputs.get(blobInputBinding);
  if (!snippetContent) {
    return `Snippet '${snippetName}' not found`;
  }
  
  console.info(`Retrieved snippet: ${snippetName}`);
  return snippetContent as string;
}

// SaveSnippet 函数 - 使用名称保存代码片段
export async function saveSnippet(
  _toolArguments: unknown,
  context: InvocationContext
): Promise<string> {
  console.info("Saving snippet");
  
  // 从工具参数中获取代码片段名称和内容
  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";
  }
  
  // 使用输出绑定将代码片段保存到 blob 存储
  context.extraOutputs.set(blobOutputBinding, snippet);
  console.info(`Saved snippet: ${snippetName}`);
  return snippet;
}

// 注册 GetSnippet 工具
app.mcpTool("getSnippet", {
  toolName: GET_SNIPPET_TOOL_NAME,
  description: GET_SNIPPET_TOOL_DESCRIPTION,
  toolProperties: {
    [SNIPPET_NAME_PROPERTY_NAME]: arg.string().describe(SNIPPET_NAME_PROPERTY_DESCRIPTION)
  },
  extraInputs: [blobInputBinding],
  handler: getSnippet,
});

// 注册 SaveSnippet 工具
app.mcpTool("saveSnippet", {
  toolName: SAVE_SNIPPET_TOOL_NAME,
  description: SAVE_SNIPPET_TOOL_DESCRIPTION,
  toolProperties: {
    [SNIPPET_NAME_PROPERTY_NAME]: arg.string().describe(SNIPPET_NAME_PROPERTY_DESCRIPTION),
    [SNIPPET_PROPERTY_NAME]: arg.string().describe(SNIPPET_PROPERTY_DESCRIPTION)
  },
  extraOutputs: [blobOutputBinding],
  handler: saveSnippet,
});
2. 智能代码片段分析
import { BlobServiceClient } from "@azure/storage-blob";

// 一次总结多个代码片段
export async function summarizeSnippets(
  _toolArguments: unknown,
  context: InvocationContext
): Promise<string> {
  console.info("Summarizing snippets");
  
  try {
    // 获取存储连接字符串
    const connectionString = process.env.AzureWebJobsStorage;
    if (!connectionString) {
      return "Azure Storage connection string not configured";
    }
    
    // 创建 blob 服务客户端
    const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString);
    const containerClient = blobServiceClient.getContainerClient("snippets");
    
    if (!(await containerClient.exists())) {
      return "No snippets container found";
    }
    
    let summary = `**Snippets Summary**\n\n`;
    let totalWords = 0;
    let totalChars = 0;
    
    // 读取并显示所有代码片段
    for await (const blob of containerClient.listBlobsFlat()) {
      const blobClient = containerClient.getBlobClient(blob.name);
      const response = await blobClient.download();
      
      if (response.readableStreamBody) {
        const chunks = [];
        for await (const chunk of response.readableStreamBody) {
          chunks.push(chunk);
        }
        const content = Buffer.concat(chunks).toString();
        const words = content.trim().split(/\s+/).length;
        const chars = content.length;
        totalWords += words;
        totalChars += chars;
        
        const fileName = blob.name.replace('.json', '');
        summary += `**${fileName}** (${words} words, ${chars} chars)\n`;
        summary += `\`\`\`\n${content}\n\`\`\`\n\n`;
      }
    }
    
    summary = `**Snippets Summary** (${totalWords} total words, ${totalChars} total chars)\n\n` + summary;
    return summary;
  } catch (error) {
    console.error("Error in summarizeSnippets:", error);
    return `Error generating summary: ${error instanceof Error ? error.message : 'Unknown error'}`;
  }
}

// 注册 SummarizeSnippets 工具
app.mcpTool("summarizeSnippets", {
  toolName: "summarize_snippets",
  description: "Analyze and summarize all code snippets in storage with statistics and insights",
  toolProperties: {
    // 不需要参数 - 分析存储中的所有代码片段
  },
  handler: summarizeSnippets,
});
3. 与 AI 助手的集成

部署后,AI 助手可以发现并使用这些工具:

GitHub Copilot Chat 示例:

用户:“存储一个关于 TypeScript 最佳实践的新文档”

AI:我会帮你存储该文档。让我使用文档管理系统。

用户:“查找所有与 Azure 相关的文档”

AI:我会搜索包含 Azure 相关内容的文档。

5、为什么使用 Azure 无服务器平台?

6

无服务器可扩展性: Azure Functions 根据需求自动扩展,可以处理从单个文档请求到数千个并发操作的所有内容。

丰富的集成生态系统: 通过内置绑定,你可以轻松集成:

  • Azure 存储(Blobs、Tables、Queues)
  • Azure SQL 数据库
  • Cosmos DB
  • Service Bus
  • Event Hubs
  • 以及更多…

成本效益: 使用消费计划,只需为执行时间付费,非常适合具有零星使用模式的 AI 助手场景。

企业级安全性: 内置身份验证、网络隔离和托管标识支持,确保你的 AI 工具默认安全。

6、开始使用

7

1. 克隆示例存储库:
git clone https://github.com/Azure-Samples/remote-mcp-functions-typescript
git clone https://github.com/Azure-Samples/remote-mcp-functions-python
git clone https://github.com/Azure-Samples/remote-mcp-functions-java
git clone https://github.com/Azure-Samples/remote-mcp-functions-dotnet
cd remote-mcp-functions-typescript
2. 安装依赖:
npm install
3. 启动本地开发:
func start
4. 部署到 Azure:
azd up

7、结论

8

Azure Functions 中的远程 MCP(也称为 MCP 工具触发器)功能是构建云原生 AI 助手/工作流的绝佳机会。无服务器计算与 Azure 广泛的生态系统无缝集成,帮助开发人员创建强大、可扩展且经济的 AI 工具。

MCP 工具触发器也是文档管理系统、数据处理管道或自定义业务逻辑工作流中 AI 决策的捷径。与 Azure Functions 绑定系统的功能集成让你可以连接到几乎任何服务或数据源,从而增强 AI 助手的功能和实用性。

成为第一个使用指纹文件触发器的人,探索真正自主的云如何改变你的应用程序!

8、资源


生如逆旅,一苇以航
欢迎关注、欢迎联系交流、欢迎沟通想法、欢迎交换意见、欢迎合作咨询

感谢亲的关注、点赞、收藏、评论,一键三连支持,谢谢

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐