当前位置: 首页 > news >正文

AI探索 | 基于 Node.js 开发 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

开源地址:https://github.com/punkpeye/awesome-mcp-servers

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

playwright-mcp

开源地址:https://github.com/microsoft/playwright-mcp

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

github-mcp-server

开源地址:https://github.com/github/github-mcp-server

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

Awesome-MCP-ZH

开源地址:https://github.com/yzfly/Awesome-MCP-ZH

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

MCP Server资源站

  • Mcp.so:收录超多的 MCP Server

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

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

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

注意:MCP 需要 node.js v18+

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

术语概要

  • resource(资源):是向 LLM 公开数据的方式。它们类似于 REST API 中的 GET 端点——它们提供数据,但不应执行大量计算或对服务端产生影响;
  • tool(工具):允许 LLM 通过你的服务器执行操作。与资源不同,工具需要执行计算并产生副作用;
  • prompt(提示):可重复使用的模板,帮助 LLM 有效地与服务器交互。

参考资料

  • pentest-mcp:适用于专业渗透测试人员的 MCP 服务器,包括 STDIO/HTTP/SSE 支持、nmap、go/dirbuster、nikto、JtR、hashcat、单词表构建等。
  • fastify-mcp:用于运行 MCP 服务器的 Fastify 插件

🖥️ 服务端

使用 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 } = reqif(url == '/favicon.ico')returnconst 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)elsethis.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 = 8080
httpServer.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{//列出全部的 resourceconst 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客户端初始化流程

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

📷 运行截图

服务端

客户端执行结果

🛠️ 使用 MCP Inspector

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

❓ 问题集锦

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

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

  3. Invalid Request: Server already initialized
    Server 已经初始化,sessionId 已经重复。

📚 推荐书籍


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

http://www.lryc.cn/news/594954.html

相关文章:

  • Node.js- node管理工具nvm
  • Spring @RequestBody注解详解与实践
  • Dockerfile 完全指南:从入门到精通
  • 西门子 S7-1500 信号模块硬件配置全解析:从选型到实战
  • (10)机器学习小白入门 YOLOv:YOLOv8-cls 模型评估实操
  • 使用 Tailwind CSS 控制元素在移动端不显示
  • 【LuckiBit】macOS/Linux 常用命令大全
  • Jenkins pipeline触发下游流水线
  • 用Java 代码实现一个简单的负载均衡逻辑
  • 2025最新版PyCharm for Mac统一版安装使用指南
  • springcloud -- 微服务02
  • 【Unity优化】Unity多场景加载优化与资源释放完整指南:解决Additive加载卡顿、预热、卸载与内存释放问题
  • 【c++】leetcode438 找到字符串中所有字母异位词
  • Three.js 从零入门:构建你的第一个 Web 3D 世界
  • 小孙学变频学习笔记(十一)关于V/F曲线的讨论
  • 本地部署AI新选择!LocalAI+cpolar轻松实现隐私安全的远程访问
  • 深入解析Hadoop YARN:三层调度模型与资源管理机制
  • 星游路-个人日志-学习积累法
  • 【PTA数据结构 | C语言版】验证六度空间理论
  • Unity VR多人手术系统恢复3:Agora语音通讯系统问题解决全记录
  • Hadoop数据完整性校验机制深度解析:CRC32校验和与后台扫描线程
  • 低空经济展 | 约克科技携小型化测试设备亮相2025深圳eVTOL展
  • Spring Boot 3核心技术面试指南:从迁移升级到云原生实战,9轮技术攻防(含架构解析)
  • 树链剖分-苹果树
  • EMBMS1820芯祥科技18单元电池监控器芯片数据手册
  • 有关Spring的总结
  • 网络编程之 UDP:用户数据报协议详解与实战
  • 19.TaskExecutor与ResourceManager建立连接
  • Openlayers 面试题及答案180道(161-180)
  • 线上问题排查之【CPU飙高100%】