MCP 的流程 和MCP Client的搭建和使用

MCP Client 是你项目里与 MCP Server 通信的客户端组件,用来帮助大模型(LLM)和你的本地工具进行交互。它是 Model Context Protocol(MCP)协议的一部分,用于"让大模型调用本地或远程的工具",比如函数、脚本、API、数据库等。

项目的构建

初始化 npm 项目

npm init -y

package.json文件替换成

json 复制代码
{
  "name": "mcpclient",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "tsc && chmod 755 build/index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.13.3",
    "axios": "^1.10.0",
    "dotenv": "^17.0.1", 
  },
  "devDependencies": {
    "@types/node": "^24.0.10",
    "typescript": "^5.8.3"
  }
}

创建一个tsconfig.json文件

json 复制代码
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./build",
    "rootDir": "./",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["index.ts"],
  "exclude": ["node_modules"]
}

根目录创建 .env 和.gitignore 文件

.env文件内容

QIHOOGPT_KEY=Your keys // 模型的key

https://ai.360.com/open. key的申请地址

.gitignore 文件内容

.env

创建客户端

首先,在 index.ts 中设置我们的导入并创建基本的客户端类:

javascript 复制代码
mport axios from "axios"; // 引入axios用于发送HTTP请求
import readline from "readline/promises"; // 获取用户的输入命令
import dotenv from "dotenv"; // 用于加载环境变量配置文件(.env)
import { Client } from "@modelcontextprotocol/sdk/client/index.js"; // MCP Client 的核心类
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; // StdioClientTransport 是 MCP Client 的传输层,

dotenv.config();

const QIHOOGPT_KEY = process.env.QIHOOGPT_KEY;

if (!QIHOOGPT_KEY) throw new Error("请在 .env 中配置 QIHOOGPT_KEY");

interface ToolCall {
  id: string;
  function: {
    name: string;
    arguments: string;
  };
}

interface ToolDefinition {
  name: string;
  description: string;
  parameters: Record<string, any>;
  execute: (args: any) => Promise<string>;
}

class MCPClient {
  private model = "360gpt-pro";
  private tools: ToolDefinition[] = [];
  private transport: StdioClientTransport | null = null;
  private transport1: StdioClientTransport | null = null;
  private mcp = new Client({ name: "mcp-client-cli", version: "1.0.0" });//初始化Client
  private mcp1 = new Client({ name: "mcp-client-cli", version: "1.0.0" });
}

服务器连接管理

javascript 复制代码
// 初始化mcp server连接
  async connectToServer(serverScriptPath?: string) {
    if (!serverScriptPath) return;
    // 判断文件类型
    const command = serverScriptPath.endsWith(".py")
      ? process.platform === "win32"
        ? "python"
        : "python3"
      : process.execPath;

    //  StdioClientTransport 设置 MCP Client 的传输层,
    this.transport = new StdioClientTransport({
      command,
      args: ["/Users/v-yangziqi1/Desktop/work/mcpserver/mcp/build/index.js"],
    });
    this.transport1 = new StdioClientTransport({
      command,
      args: ["/Users/v-yangziqi1/Desktop/work/mcpserver/mcp1/build/index.js"],
    });
    // connect 方法用于连接到 MCP Server。
    this.mcp.connect(this.transport);
    // connect 方法用于连接到 MCP Server。
    this.mcp1.connect(this.transport1);

    // 获取 MCP Server 上的所有工具名称
    const { tools: remoteTools } = await this.mcp.listTools();
    // 获取 MCP Server1 上的所有工具名称
    const { tools: remoteTools1 } = await this.mcp1.listTools();
    const tools = [...remoteTools, ...remoteTools1];
    // 生成工具数组
    this.tools = tools.map((t) => ({
      name: t.name, // 工具名称
      description: t.description || "", // 工具描述
      parameters: t.inputSchema, // 工具参数定义Schema的格式
      execute: async (args: any) => {
        // 执行工具的逻辑
        const result = await this.mcp.callTool({
          name: t.name,
          arguments: args,
        });
        return result.content as string;
      },
    }));

    console.log(
      "已连接 MCP Server,工具:",
      this.tools.map((t) => t.name).join(", ")
    );
  }

查询处理逻辑

使用Client 对于 模型 和 MCP Server的交互

javascript 复制代码
 // 将工具定义转换为模型可以理解的格式
  private convertTool(tool: ToolDefinition) {
    return {
      type: "function",
      function: {
        name: tool.name,
        description: tool.description,
        parameters: tool.parameters,
      },
    };
  }

  // 360模型的方法
  private async call360GPT(payload: any) {
    const { data } = await axios.post(
      "https://api.360.cn/v1/chat/completions",
      { ...payload, stream: false },
      {
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${QIHOOGPT_KEY}`,
        },
      }
    );
    return data;
  }

// 处理用户查询
  // 该方法会将用户的查询发送到360GPT模型,并处理返回的
  async processQuery(query: string) {
    const messages: any[] = [{ role: "user", content: query }];
    // 发送用户消息和mcp server的tools到360GPT模型
    const res = await this.call360GPT({
      model: this.model, // 模型名称
      messages, // 用户消息
      tools: this.tools.map((t) => this.convertTool(t)), // 工具列表
      tool_choice: "auto", // 自动选择工具
    });

    const choice = res.choices[0]; // 获取模型返回的第一个选择
    const toolCalls = choice.message.tool_calls as ToolCall[] | undefined; // 获取工具调用列表
   
    // 如果有工具调用,则执行工具
    if (toolCalls?.length) {
      console.log("工具调用列表:", toolCalls);
      for (const call of toolCalls) {
        // 在 tools 数组中查找与调用名称匹配的工具
        const tool = this.tools.find((t) => t.name === call.function.name);
        if (!tool) continue; // 如果没有找到对应的工具,跳过
        const args = JSON.parse(call.function.arguments || "{}"); // 解析工具调用的参数
        const result = await tool.execute(args); // 执行工具并获取结果
        messages.push({ role: "user", content: result }); // 将工具结果添加到消息中
        // 再次调用360GPT模型,传入更新后的消息进行润色
        console.log("🤖 正在思考...");
        const final = await this.call360GPT({ model: this.model, messages });
        // 如果有工具调用结果,则返回最终的内容
        return final.choices[0].message.content;
      }
    }
    return choice.message.content;
  }

交互式聊天界面

接收参数进行查询

javascript 复制代码
// 聊天循环方法
  async chatLoop() {
    // 创建 readline 接口,用于读取用户输入
    const rl = readline.createInterface({
      input: process.stdin, //标准输入输出
      output: process.stdout,
    });
    console.log("输入内容,输入 quit 退出:");
    while (true) {
      const query = await rl.question("请输入: "); // 提示用户输入查询内容
      if (query.toLowerCase() === "quit") break;
      console.log("🤖 正在思考...");
      const ans = await this.processQuery(query); // 处理用户查询
      console.log("\nAI:", ans, "\n");
    }
    rl.close();
  }

主入口点

最后,我们将添加主执行逻辑:

javascript 复制代码
(async () => {
  const mcpClient = new MCPClient(); 
  const scriptArg = process.argv[2]; // 获取的是MPC Server的地址
  if (scriptArg) await mcpClient.connectToServer(scriptArg);

  await mcpClient.chatLoop(); // 开启输入流
  process.exit(0);
})();

运行客户端

要与任何 MCP 服务器一起运行你的客户端:

复制代码
Client  和  MCP Server 要同时运行

构建 TypeScript

npm run build

运行启动 MCP Server

node server/build/index.js

启动 Client

node build/index.js server/build/index.js

输入内容

获取到模型重新润色过的返回值