怎么分别用 sse 和 stdio 实现大模型 mcp联网的功能

472 阅读3分钟

我们在上一篇文章知道了mcp的基础概念,基础调用和编写,这篇文章我们来实现一个实用的功能。ai联网搜索,并且咱们会分别用sse和stdio的方式进行实现,其中模型用deepseek,mcp服务用baidu的服务,deepseek充个10块就可以了。然后ai搜索 baidu有免费额度(现在2025/5/14 每天免费ai搜索100次)

尽管现在ai模型的选择已经应接不暇,但是模型本身其实是不支持联网搜索,如果强制要他进行搜索,大概率会返回下面的结果

不能联网.png

那么在正文开始之前,我们先做一些前置工作

前置工作

安装依赖

npm install @modelcontextprotocol/sdk@1.10.1 openai@4.95.1

编写util.js工具类,

定义了三个能力,获取工具tool,调用工具,连接和取消mcp的connect


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


申请资源

  1. 拿到 deepseek 或者其他模型的 api key 或者 其实支持function call的大模型
  2. 去到 https://sai.baidu.com/mcp?utm_source=ai-bot.cn 拿到需要的 api key,这里我用的是百度ai 搜索,去https://console.bce.baidu.com/iam/#/iam/apikey/list申请key就可以了

联网实现

sse

很简单,在之前工具调用的基础上面加上我们工具函数返回的tool,和调用tool就可以了,注意把配置的key和url换成你实际用的,

输出结果

sse.png

代码流程

注意把配置的key和url换成你实际用的


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();

stdio

输出结果

api stdio返回.png

代码流程

我们先简单写一个 30行的 server,作为stdio的入口,这里的数据源我们用mock的数据

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

然后我们在上面sse的基础上 改变我们的 mcpConfig 就可以了,commandargs合并就是我们的这次mcp运行的入口,就像这样

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"],
}

最后直接运行就可以得到上面的结果