Skip to content
目录

3.1 mcp

3.1.1 原理

  1. 服务器注册工具并暴露给客户端
  2. 客户端(通常是集成了大模型的应用)通过 MCP 协议发现和调用这些工具
  3. 大模型产生的输出会被客户端解析,然后客户端负责按照 MCP 协议的格式构造请求

3.1.2 mcp 和 tool call的基础概念

然后许多人会把MCP和Tool Call弄混,function call简单来说就是AI调用外部工具的能力,是某些大模型(如 DeepSeek)提供的专有接口特性。它允许模型直接生成结构化的函数调用请求(如查询天气、计算数值),由宿主应用执行并返回结果。本质是模型内部的功能扩展,依赖于特定厂商的实现,无强制标准协议。

传统Tool Call方式(DeepSeek)

javascript
import { OpenAI } from 'openai';
import fs from 'fs';

// 模拟天气API的函数
function get_weather(location, date) {
    console.log(`获取${location}${date}的天气信息`);
    return { location, date, temperature: "25°C", condition: "晴朗", humidity: "60%" };
}
const config = {
    model: "deepseek-chat",
    query: "你好,北京明天的天气怎么样?",
    baseURL: 'https://api.deepseek.com',
    apiKey: "你的apiKey",
}
const openai = new OpenAI({
    baseURL: config.baseURL,
    apiKey: config.apiKey,
});

const main = async () => {
    // Function call 示例
    const functionCallResponse = await openai.chat.completions.create({
        model: config.model,
        messages: [{ role: "user", content: config.query }],
        tools: [
            {
                type: "function",
                function: {
                    name: "get_weather",
                    description: "获取指定位置的天气信息",
                    parameters: {
                        type: "object",
                        properties: {
                            location: {
                                type: "string",
                                description: "城市名称,如'北京'、'上海'等"
                            },
                            date: {
                                type: "string",
                                description: "日期,如'今天'、'明天'等"
                            }
                        },
                        required: ["location", "date"]
                    }
                }
            }
        ]
    });
    fs.writeFileSync("response.json", JSON.stringify(functionCallResponse, null, 2));
    
    // 处理function call的返回值
    if (functionCallResponse.choices && functionCallResponse.choices.length > 0) {
        for (const choice of functionCallResponse.choices) {
            if (choice.message && choice.message.tool_calls && choice.message.tool_calls.length > 0) {
                // 遍历所有工具调用
                for (const toolCall of choice.message.tool_calls) {
                    // 获取函数名称和参数
                    const functionName = toolCall.function.name;
                    const args = JSON.parse(toolCall.function.arguments);
                    console.log(`函数调用: ${functionName},参数:`, args);
                    
                    // 根据函数名称分发处理
                    let toolResponse;
                    switch (functionName) {
                        case "get_weather":
                            toolResponse = get_weather(args.location, args.date);
                            break;
                        default:
                            console.log(`未知的函数调用: ${functionName}`);
                            toolResponse = { error: `未找到函数: ${functionName}` };
                    }
                    
                    console.log(`函数调用结果:`, toolResponse);
                    
                    // 将结果发送回模型
                    const finalResponse = await openai.chat.completions.create({
                        model: config.model,
                        messages: [
                            { role: "user", content: config.query },
                            choice.message,
                            {
                                role: "tool",
                                tool_call_id: toolCall.id,
                                content: JSON.stringify(toolResponse)
                            }
                        ]
                    });
                    fs.writeFileSync(`final_response_${functionName}.json`, JSON.stringify(finalResponse, null, 2));
                }
            }

        }
    }
    
}

main();

3.1.3 最小示例

mcp server

js
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// step1:初始化MCP服务器
const server = new McpServer({
    name: "代码review",
    version: "1.0.0"
});
const anime_suffix = process.env.anime_suffix ?? "";
console.log(anime_suffix);
// step2:定义工具
server.tool(
  "code-review",
  { code: z.string().describe("代码") },
  async ({ code }) => {
    return {
      content: [{ type: "text", text: "注意用二次元风格给我代码审查的结果,每一句话后面需要加上颜文字或者" + anime_suffix }, { type: "text", text: code }]
    };
  }
);
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("MCP code review 服务器已启动");
}
// step3:启动服务器
main().catch((error) => {
  console.error("服务器启动失败:", error);
  process.exit(1);
});

注册

接下来,我们需要在Cursor中注册我们的MCP服务。创建一个.cursor/mcp.json文件:

json
{
  "mcpServers": {
     "mcp-code-review": {
      "command": "cmd",
      "args": [
        "/c",
        "node",
        "D:\\myProject\\ai-explore\\packages\\mcp\\code-review.js"
      ],
      "env": {
        "anime_suffix": "desu"
      }
    }
  }
}

3.1.4 mcp编写详解

3.1.4.1 安装依赖

bash
npm install @modelcontextprotocol/sdk@1.10.1 openai@4.95.1

3.1.4.2 工具类

js
import {Client} from "@modelcontextprotocol/sdk/client/index.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import fs from "fs";
export async function useMcpTool({
    baseUrl,
    command,
    args,
    name,
    version
}){
    let sseTransport
    const mcp = new Client({name: name, version: version});
    try{
        if(baseUrl){
            sseTransport = new SSEClientTransport(baseUrl);
            await mcp.connect(sseTransport);
        }
    } catch (error) {
        console.error("useMcpTool-baseUrl初始化失败", error);
        return
    }
    try{
        if(command){
            const transport = new StdioClientTransport({
                command,
                args,
            });
            await mcp.connect(transport);
        }
    } catch (error) {
        console.error("useMcpTool-command初始化失败", error);
        return
    }

    const getToolsList = async ()=>{
        const toolsResult = await mcp.listTools();
        fs.writeFileSync("response tool.txt", JSON.stringify(toolsResult, null, 2));
        const tools = toolsResult.tools.map((tool) => {
            return {
                type: "function",
                function: {
                    name: tool.name,
                    description: tool.description,
                    parameters: tool.inputSchema,
                }
            };
        });
        return tools;
    }
    const callTool = async (toolName, toolArgs)=>{
        const result = await mcp.callTool({
            name: toolName,
            arguments: toolArgs,
        });
        return result;
    }
    const disconnect = async ()=>{
        sseTransport.close();
    }
    return {
        getToolsList,
        callTool,
        disconnect
    }
}

3.1.4.3 sse(接入)

js
import {OpenAI} from 'openai'
import {useMcpTool} from "./tool.js";
import fs from "fs";

// 配置参数
const ModelConfig = {
    apiKey: "你的model api key",
    baseURL: "https://api.deepseek.com",
    modelName: "deepseek-chat",
    userMessage: [
        {
            role: "user",
            content: "请帮我搜索今天的新闻"
        },
    ]
}
const ToolConfig = {
    // refer: https://sai.baidu.com/mcp?utm_source=ai-bot.cn
    // baidu api: https://console.bce.baidu.com/iam/#/iam/apikey/list
    baiduapiKey: "bce-v3 你的baidu key",
}

const mcpConfig = {
    baseUrl:  new URL("https://appbuilder.baidu.com/v2/ai_search/mcp/sse?api_key=Bearer+" + ToolConfig.baiduapiKey),
    name: "ai-search",
    version: "1.0.0",
}

async function main() {
    const openai = new OpenAI(ModelConfig); 
    const {getToolsList, callTool} =await useMcpTool(mcpConfig);
    try{
        const tools = await getToolsList();
        const messages = ModelConfig.userMessage
        const response = await openai.chat.completions.create({
            model: ModelConfig.modelName,
            messages,
            // tools,
        });
        fs.writeFileSync("response get init response.txt", JSON.stringify(response, null, 2));
        return
        const finalText = [];
        const toolResults = [];
         for (const choice of response.choices) {
            // 如果没有工具调用
            if (!choice.message.tool_calls || choice.message.tool_calls.length === 0) {
                // 将消息内容添加到最终文本数组
                finalText.push(choice.message.content);
            } else {
                // 获取工具名称
                const toolName = choice.message.tool_calls[0].function.name;
                // 获取工具参数
                const toolArgs = JSON.parse(choice.message.tool_calls[0].function.arguments);

                // 调用工具
                const result = await callTool(toolName, toolArgs);

                // 将工具调用结果添加到工具结果数组
                toolResults.push(result);
                // 将工具调用信息添加到最终文本数组
                finalText.push(
                    `[Calling tool ${toolName} with args ${JSON.stringify(toolArgs)}]`
                );

                // 将工具调用结果添加到消息数组
                messages.push({
                    role: "user",
                    content: result.content,
                });

                // 再次调用 聊天完成接口
                const response = await openai.chat.completions.create({
                    model: ModelConfig.modelName,
                    messages,
                });

                // 将响应内容添加到最终文本数组
                finalText.push(
                    response.choices[0].message.content
                );
            }
        }

        // 返回最终文本,用换行符连接
        const finalTextStr = finalText.join("\n");
        fs.writeFileSync("response get final response.txt", finalTextStr);
        return finalTextStr;
    } catch (error) {
        console.error("连接失败");
        console.error(error);
    }
}

main();

3.1.4.4 stdio(接入)

改一下mcpserver就好了

js
const mcpConfig = {
    // baseUrl:  new URL("https://appbuilder.baidu.com/v2/ai_search/mcp/sse?api_key=Bearer+" + ToolConfig.baiduapiKey),
    name: "ai-search",
    version: "1.0.0",
    command: "node",
    args: ["ai-search.js"],
}

3.1.4.5 sse(编写server)

js
// const express = require('express');
import express from 'express';
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
// step1:初始化MCP服务器
const server = new McpServer({
    name: "代码review",
    version: "1.0.0"
});

// step2:定义工具
server.tool(
  "code-review",
  { code: z.string().describe("代码") },
  async ({ code }) => {
    return {
      content: [{ type: "text", text: "注意用二次元风格给我代码审查的结果,每一句话后面需要加上喵或者颜文字" }, { type: "text", text: code }]
    };
  }
);

// Setup routes for the server
const setupServer = async () => {
  await server.connect(transport);
};

const PORT = 3000;

const app = express();
app.use(express.json());

const transport = new StreamableHTTPServerTransport({
  sessionIdGenerator: undefined, // set to undefined for stateless servers
});
setupServer().then(() => {
  app.listen(PORT, () => {
    console.log(`MCP Streamable HTTP Server listening on port ${PORT}`);
  });
}).catch(error => {
  console.error('Failed to set up the server:', error);
  process.exit(1);
});



app.post('/mcp', async (req, res) => {
    console.log('Received MCP request:', req.body);
    try {
        await transport.handleRequest(req, res, req.body);
    } catch (error) {
      console.error('Error handling MCP request:', error);
      if (!res.headersSent) {
        res.status(500).json({
          jsonrpc: '2.0',
          error: {
            code: -32603,
            message: 'Internal server error',
          },
          id: null,
        });
      }
    }
  });

3.1.4.6 stdio(编写server)

js
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// step1:初始化MCP服务器
const server = new McpServer({
    name: "ai-search",
    version: "1.0.0"
});

// step2:定义工具
server.tool(
  "ai-search",
  { content: z.string().describe("搜索内容") },
  async ({ content }) => {
    return {
       // 模拟数据
      content: [{ type: "text", text: `
       今日新闻:
       js是世界上最好的语言(doge)
       专家警告:JS正在引发全球性代码崇拜,
       有人目击新郎用JS写婚礼誓词,新娘感动得当场背诵《JavaScript高级程序设计》目录 
        `  }, { type: "text", text: content }]
    };
  }
);
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("MCP code review 服务器已启动");
}

// step3:启动服务器
main().catch((error) => {
  console.error("服务器启动失败:", error);
  process.exit(1);
});

MIT License.