掘金 人工智能 07月22日 10:21
AI探索 | 基于 Node.js 开发 MCP 客户端+服务端及优秀项目分享
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

MCP(模型上下文协议)是一个开放协议,旨在为应用程序与大型语言模型(LLM)的交互提供标准化接口,如同AI领域的USB-C端口。它解决了传统AI应用集成复杂、可扩展性受限的问题,通过统一的协议实现AI模型与数据源、工具的无缝连接。MCP架构包含主机、客户端和服务器三个核心组件,确保了安全高效的通信。文章还介绍了MCP的实际应用,包括微软的playwright-mcp、GitHub的github-mcp-server等项目,并提供了使用TypeScript SDK进行开发的示例,以及解决开发过程中可能遇到的错误。

🌟 **MCP协议的核心作用**:MCP是一个开放协议,它规范了应用程序如何向大型语言模型(LLM)提供上下文信息,旨在解决AI应用与外部工具、数据源集成时的复杂性和可扩展性问题。它为AI模型与不同数据源和工具的连接提供了一种标准化方式,类比于AI领域的USB-C端口。

🚀 **MCP的架构与组件**:MCP的架构由三个核心组件构成:MCP主机、MCP客户端和MCP服务器。这三个组件协同工作,实现了AI应用、外部工具和数据源之间的无缝通信,并且保证了操作的安全性和有效管理。

🛠️ **MCP的开发与实践**:文章提供了使用TypeScript SDK进行MCP客户端和服务器开发的示例。服务器端利用Node.js的http模块处理请求,注册工具(如数值相加)和资源(如问候语生成)。客户端则演示了如何连接MCP服务器,列出资源和工具,并调用工具进行操作。

💡 **MCP开发中的常见问题与解决方案**:在开发过程中可能遇到如`zod`版本不兼容(建议使用3.x版本)或服务器未初始化等问题。文中提到了`MCP error -32603: keyValidator._parse is not a function`的错误,并指出是由于`zod`版本过高导致,将其降级至3.x版本即可解决。此外,还强调了确保服务器已正确初始化的重要性。

📚 **MCP相关资源与推荐**:文章列举了多个优秀的MCP开源项目,如`awesome-mcp-servers`、`playwright-mcp`、`github-mcp-server`等,并推荐了中文社区资源`Awesome-MCP-ZH`和`MCP Server资源站`。同时,还推荐了清华大学出版社出版的《MCP协议与AI Agent开发:标准、应用与实现》一书,该书系统阐述了MCP的技术原理和应用实践。

🤔什么是 MCP?

官网原文:MCP is an open protocol that standardizes how applications provide context to LLMs. Think of MCP like a USB-C port for AI applications. Just as USB-C provides a standardized way to connect your devices to various peripherals and accessories, MCP provides a standardized way to connect AI models to different data sources and tools.

MCP是一个开放协议,它规范了应用程序如何向大型语言模型(LLM)提供上下文。你可以将MCP想象成AI应用程序的USB-C端口。就像USB-C为设备与各种外围设备和配件的连接提供了标准化方式一样,MCP也为AI模型与不同数据源和工具的连接提供了标准化方式。

MCP的意义

在 MCP 推出之前,AI 应用依赖于各种方法与外部工具交互,例如手动 API 连接、基于插件的接口以及代理框架。如图 1 所示,这些方法需要将每个外部服务与特定的 API 集成,这增加了复杂性并限制了可扩展性。MCP 通过提供标准化协议来应对这些挑战,该协议支持与多种工具进行无缝且灵活的交互。

MCP 架构

MCP 架构由三个核心组件组成:MCP 主机、MCP 客户端和 MCP 服务器。这些组件协同工作,实现 AI 应用、外部工具和数据源之间的无缝通信,确保操作安全且得到妥善管理。以上两张图片来自:MCP Research Paper

🥇优秀项目

awesome-mcp-servers

开源地址:github.com/punkpeye/aw…

Github 上必须出现的,又必然很火的 awesome-xx 系列🤭,收藏精品 MCP 服务端。

playwright-mcp

开源地址:github.com/microsoft/p…

微软出品,使用 Playwright 提供浏览器自动化功能,能够通过结构化可访问性快照与网页进行交互,无需截图或视觉调优模型。

github-mcp-server

开源地址:github.com/github/gith…

Github 官方出品的 MCP 服务,提供与GitHub API的无缝集成,为开发人员和工具赋予高级自动化和交互能力。

Awesome-MCP-ZH

开源地址:github.com/yzfly/Aweso…

一个专为中文用户打造的 MCP(模型上下文协议)资源合集! 这里有 MCP 的基础介绍、玩法、客户端、服务器和社区资源,帮你快速上手这个 AI 界的“万能插头”。

MCP Server资源站

👨‍💻一套简单的MCP交互程序

其实对程序员来说,MCP 交互不是什么新玩意,都可以简单拿 HTTP 协议套过来进行理解😄。一个支持发送 MCP 请求的客户端,向 MCP 服务器发起调用。

MCP 官方代码库为 github.com/modelcontex…,里面有文档以及主流语言的 SDK,这里我们选择 typescript-sdk 进行客户端/服务端的开发。里面有一个inspector的库,是用于测试 MCP Server 的。

注意:MCP 需要 node.js v18+

开始做 MCP 开发前,有些术语需要了解下。

术语概要

参考资料

🖥️ 服务端

使用 node.js 自带的 http 模块处理请求

import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"import { z } from "zod"import dayjs from "dayjs"import { randomUUID } from "node:crypto"import { createServer, ServerResponse } from 'node:http'import pc from 'picocolors'const log = (msg, level="INFO")=> console.debug(`${pc.cyan(dayjs().format("MM-DD HH:mm:ss:SSS"))} ${pc.gray(level)} ${msg}`)const buildServer = ()=>{    const mcpServer = new McpServer({ name: "demo-mcp-server", version:"1.0.0" })    mcpServer.registerTool(        "add",        {            title: "数值相加",            description: "将参数累加",            inputSchema: {  a: z.number(), b: z.number() }        },        async ({ a, b })=>{            log(`${pc.magenta("[TOOL]")} add a=${a} b=${b}`)            return {                content: [{ type:'text', text: `${a+b}` }]            }        }    )    mcpServer.registerResource(        "greeting",        new ResourceTemplate("greeting://{name}", { list: null }),        {            title: "Greeting Resource",            description: "Dynamic greeting generator"        },        async (uri, { name })=>{            log(`${pc.magenta("[RESOURCE]")} GREETING ${name}`)            return {                contents:[{ uri: uri.href, text:`Hello ${name}` }]            }        }    )    return mcpServer}/** * @type {Map<String, StreamableHTTPServerTransport>} */const transports = new Map()/** * * @param {ServerResponse} res * @param {String} message - 描述信息 * @param {Number} code - 错误代码,默认-32000 */const sendError = (res, statusCode=400, message, code=-32000)=>{    res.writeHead(statusCode, { 'content-type':'application/json' })    res.end(JSON.stringify({        jsonrpc: "2.0",        error: { code, message },        id: null,    }))}const httpServer = createServer(async (req, res)=>{    const { url, method, headers } = req    if(url == '/favicon.ico')        return    const sessionId = headers['mcp-session-id']    // 请求体(需要手动拼接)    let body = '';    req.on('data', chunk =>  body += chunk)    req.on('end', async ()=>{        log(`${pc.magenta(method.padEnd(4, " "))} | ${req.url} SESSION=${sessionId} TYPE=${headers['content-type']}`)        if(method == 'POST'){            /**@type {StreamableHTTPServerTransport} */            let transport = transports.get(sessionId)            if(transport == null || transport.closed == true){                let server = buildServer()                transport = new StreamableHTTPServerTransport({                    sessionIdGenerator: ()=> randomUUID(),                    onsessioninitialized: id=> {                        log(`MCP Session 已创建,ID=${id}`)                        transports.set(id, transport)                    }                })                transport.onclose = function(){                    log(`transport 关闭 onclose...`)                    if(this.sessionId)                        transports.delete(this.sessionId)                    else                        this.closed = true                }                await server.connect(transport)                log(`StreamableHTTPServerTransport 创建成功...`)            }            const postBody = JSON.parse(body)            await transport.handleRequest(req, res, postBody)            log("处理 POST 请求...")            console.debug(postBody)        }        else {            sendError(res, 405, "Method not allowed.")        }    })})const PORT = 8080httpServer.listen(PORT, ()=>{    log(`SERVER RUNNING ON ${PORT} ...`)})

🤖 客户端

import { Client } from "@modelcontextprotocol/sdk/client/index.js";import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";const client = new Client({    name: "demo-mcp-client",    version: "1.0.0"})let transport = new StreamableHTTPClientTransport("http://localhost:8080")await client.connect(transport)console.debug("MCP Server 连接成功...")try{    //列出全部的 resource    const resources = await client.listResources()    console.debug("资源列表", resources)    console.debug(await client.readResource({uri:"greeting://0604hx"}))    const tools = await client.listTools()    console.debug("工具列表", tools)    console.debug(await client.callTool({        name: 'add',        arguments:{            a: 100,            b: 300        }    }))}catch(e){    console.debug("出错啦", e)}await transport.close()await client.close()

StreamableHTTP客户端初始化流程

    发起初始化
{  method: 'initialize',  params: {    protocolVersion: '2025-06-18',    capabilities: {},    clientInfo: { name: 'demo-mcp-client', version: '1.0.0' }  },  jsonrpc: '2.0',  id: 0}
    第二次请求
{ method: 'notifications/initialized', jsonrpc: '2.0' }

📷 运行截图

🛠️ 使用 MCP Inspector

后续会单独写一篇博文介绍如何使用😄

❓ 问题集锦

    MCP error -32603: keyValidator._parse is not a function经排查,是由于 zod 版本过高(一开始用的是 4.x),改成 3.x 就正常了。参考:McpError: MCP error -32603: keyValidator._parse is not a function

    "Bad Request: Server not initializedServer 并未初始化,请先调用server.connect(transport)

    Invalid Request: Server already initializedServer 已经初始化,sessionId 已经重复。

📚 推荐书籍

《MCP协议与AI Agent开发:标准、应用与实现》清华大学出版社出版的图书,系统地阐述了MCP的技术原理、协议机制与工程应用,提供了从底层协议设计到项目部署的全流程的应用指南。全书从结构上分为基础理论、协议规范、开发工具链、应用构建4部分,共9章。具体内容包括大模型基础、MCP基本原理、MCP标准与规范体系、MCP与LLM的互联机制、MCP开发环境与工具链、MCP与多模态大模型集成,以及MCP的状态流转机制、Prompt构建规范、上下文调度策略及其与模型推理引擎的协同工作,同时涉及流式响应、函数调用、模块化Prompt设计等前沿技术,并结合基于DeepSeek平台的真实应用项目(人格共创AI剧本工坊、自演化智能议程会议系统与深梦编导器),助力读者理解MCP在多元领域的可拓展性与工程实践

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

MCP AI协议 LLM 上下文协议 标准化
相关文章