简介

这篇文章将带你了解如何使用 TypeScript 构建一个 Model Context Protocol (MCP) 服务器,以连接 Azure AI Agent 和 Claude Desktop 或其他支持 MCP 的客户端。通过本文,你将学习搭建服务器、配置连接以及以编程方式处理 AI Agent 的交互。


为什么需要 MCP 服务器?

Azure AI Agent 是 Azure AI Foundry 生态系统的一部分,提供强大的对话式 AI 功能。然而,要将这些 Agent 集成到桌面应用程序中,往往需要定制化的解决方案。MCP 提供了一个标准化协议,可以无缝连接 Azure AI Agent 和支持 MCP 的客户端(如 Claude Desktop)。


准备工作

在开始之前,请确保你已准备以下条件:

  1. 安装 Node.js 16 或更高版本
  2. 配置 TypeScript 开发环境
  3. 拥有 Claude Desktop 或其他 MCP 兼容客户端
  4. 配置 Azure CLI 并拥有权限
  5. Azure AI Foundry 中配置了现有 Azure AI Agent

搭建 TypeScript MCP 服务器

第一步:创建项目

首先,创建一个新的项目目录并安装必要的依赖:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 创建项目目录
mkdir azure-agent-mcp
cd azure-agent-mcp

# 初始化 npm 项目
npm init -y

# 安装依赖
npm install @modelcontextprotocol/sdk zod dotenv @azure/ai-projects @azure/identity

# 安装开发依赖
npm install -D typescript @types/node

第二步:配置 TypeScript

创建 tsconfig.json 文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "outDir": "./dist",
    "strict": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "ts-node": {
    "esm": true
  }
}

第三步:环境变量设置

创建 .env 文件以存储 Azure 凭据:

1
2
PROJECT_CONNECTION_STRING=your-project-connection-string
DEFAULT_AGENT_ID=your-default-agent-id  # 可选

第四步:编写 MCP 服务器代码

创建 src/index.ts 文件并开始编写服务器代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import * as dotenv from "dotenv";
import { AIProjectsClient } from "@azure/ai-projects";
import { DefaultAzureCredential } from "@azure/identity";

dotenv.config();

const PROJECT_CONNECTION_STRING = process.env.PROJECT_CONNECTION_STRING;
const DEFAULT_AGENT_ID = process.env.DEFAULT_AGENT_ID || "";

let aiClient: AIProjectsClient | null = null;

第五步:实现核心功能

添加初始化和查询 Agent 的功能:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
function initializeServer(): boolean {
  if (!PROJECT_CONNECTION_STRING) {
    console.error("ERROR: 缺少环境变量 PROJECT_CONNECTION_STRING");
    return false;
  }

  try {
    const credential = new DefaultAzureCredential();
    aiClient = AIProjectsClient.fromConnectionString(PROJECT_CONNECTION_STRING, credential);
    return true;
  } catch (error) {
    console.error(`ERROR: 初始化 AIProjectClient 失败: ${error instanceof Error ? error.message : String(error)}`);
    return false;
  }
}

async function queryAgent(agentId: string, userQuery: string, existingThreadId?: string): Promise<{ response: string; threadId: string }> {
  if (!aiClient) {
    throw new Error("AI 客户端未初始化");
  }

  try {
    await aiClient.agents.getAgent(agentId);

    let threadId = existingThreadId;
    if (!threadId) {
      const thread = await aiClient.agents.createThread();
      threadId = thread.id;
    }

    await aiClient.agents.createMessage(threadId, {
      role: "user" as MessageRole,
      content: userQuery,
    });

    let run = await aiClient.agents.createRun(threadId, agentId);

    while (["queued", "in_progress", "requires_action"].includes(run.status)) {
      await new Promise((resolve) => setTimeout(resolve, 1000));
      run = await aiClient.agents.getRun(threadId, run.id);
    }

    if (run.status === "failed") {
      return {
        response: `错误: Agent 运行失败: ${run.lastError?.message || "未知错误"}`,
        threadId,
      };
    }

    const messages = await aiClient.agents.listMessages(threadId);
    const assistantMessages = messages.data.filter((m) => m.role === "assistant");
    const lastMessage = assistantMessages[assistantMessages.length - 1];

    let responseText = "";
    if (lastMessage) {
      for (const content of lastMessage.content) {
        if (content.type === "text" && content.text?.value) {
          responseText += content.text.value + "\n";
        }
      }
    }

    return { response: responseText.trim(), threadId };
  } catch (error) {
    throw new Error(`Agent 查询失败: ${error instanceof Error ? error.message : String(error)}`);
  }
}

第六步:注册 MCP 工具

创建 MCP 服务器并注册工具:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const serverInitialized = initializeServer();

const mcp = new McpServer({
  name: "azure-agent",
  version: "1.0.0",
  description: "用于集成 Azure AI Agent 服务的 MCP 服务器",
});

mcp.tool(
  "query_agent",
  "查询指定的 Azure AI Agent",
  {
    agent_id: z.string().describe("Azure AI Agent 的 ID"),
    query: z.string().describe("发送给 Agent 的问题或请求"),
    thread_id: z.string().optional().describe("用于继续对话的线程 ID"),
  },
  async ({ agent_id, query, thread_id }) => {
    try {
      const { response, threadId } = await queryAgent(agent_id, query, thread_id);
      return {
        content: [
          {
            type: "text",
            text: `## Azure AI Agent 的响应\n\n${response}\n\n(thread_id: ${threadId})`,
          },
        ],
      };
    } catch (error) {
      return {
        content: [
          {
            type: "text",
            text: `查询 Agent 时出错: ${error instanceof Error ? error.message : String(error)}`,
          },
        ],
      };
    }
  }
);

第七步:运行服务器

添加项目构建和运行脚本:

1
2
3
4
5
6
7
{
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js"
  }
}

运行以下命令:

1
2
npm run build
npm start

与 Claude Desktop 集成

为了将 MCP 服务器与 Claude Desktop 集成,需要更新其配置文件 claude_desktop_config.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "mcpServers": {
    "azure-agent": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/azure-agent-mcp/dist/index.js"],
      "env": {
        "PROJECT_CONNECTION_STRING": "your-project-connection-string",
        "DEFAULT_AGENT_ID": "your-default-agent-id"
      }
    }
  }
}

实际使用示例

  1. 查询指定 Agent
    在 Claude Desktop 中询问:能否使用 azure-agent 检查西雅图是否有天气警报?
    Claude 会调用 query_agent 工具并使用指定 Agent ID。

  2. 使用默认 Agent
    在 Claude Desktop 中询问:使用默认 Azure Agent 总结最新的 NBA 新闻。

  3. 发现可用的 Agent
    在 Claude Desktop 中询问:我有哪些可用的 Azure AI Agent?


总结

通过使用 TypeScript 构建 Azure AI Agent 的 MCP 服务器,你能够在 Azure AI Foundry 服务 和 MCP 客户端之间创建桥梁。这种集成方式不仅增强了服务的能力,还通过 MCP 协议明确职责分工,让开发过程更高效。


原文链接:Azure AI Foundry: Create an MCP Server with Azure AI Agent Service (TypeScript Edition)