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

手把手教你为Cursor撸一个自定义的MCP服务(对接wiki.js)

1 MCP服务开发

1.1 MCP服务如何开发?

MCP协议的核心思想是解耦标准化。它通过定义一套清晰的规范,使LLM能够以统一的方式访问外部工具、数据源和服务,而无需为每个工具编写特定的适配代码。

清晰的规范,到底是什么规范?

规范维度核心内容
核心目标解决AI集成碎片化问题,提供统一、安全的连接标准
架构模式客户端-服务器 (C/S) 架构,包含Host(宿主应用)、Client(客户端)、Server(服务端)
通信协议应用层基于JSON-RPC 2.0;传输层支持Stdio(标准输入输出)​ 和基于HTTP的SSE/Streamable HTTP
核心能力Server提供三大能力:工具(Tools,可执行函数)、资源(Resources,可读数据)、提示模板(Prompts,预定义提示)

了解了规范有什么后,先具体看看细节,这里着重讲一下JSON-RPC 2.0格式,为后面撸代码做些铺垫

JSON-RPC 2.0输入输出示例

输入

一个 JSON-RPC 2.0 请求数据是一个单一的 JSON 对象,可以包含以下成员:

  • jsonrpc:字符串,指定 JSON-RPC 的版本号,对于 2.0 规范来说,这个值必须是 2.0。
  • method:字符串,指定要调用的远程方法的名称。
  • params:结构化值,可以是数组或者对象,传递给远程方法的参数。如果方法不需要参数可以省略。
  • id:唯一标识符,可以是字符串或数字,用于关联请求和响应,服务端必须返回相同的值。如果请求是一个通知类型,则此参数可以被省略。
@Data public class McpRequest { @JsonProperty("jsonrpc") private String jsonrpc = "2.0"; // JSON-RPC 2.0 中 id 可以是 String、Number 或 null private Object id; private String method; private Map<String, Object> params; }
输出

一个 JSON-RPC 2.0 响应数据也是一个单一的 JSON 对象,可以包含以下成员:

  • jsonrpc:字符串,指定 JSON-RPC 的版本号,对于 2.0 规范来说,这个值必须是2.0。
  • result:当请求成功时,包含由远程方法返回的结果。如果请求失败,则不包含此成员。
  • error:当请求失败时,包含一个错误对象。如果请求成功,则不包含此成员。
  • id:与请求中的 id 相同,用于识别哪个请求对应的响应。
@Data @NoArgsConstructor @AllArgsConstructor @JsonPropertyOrder({"jsonrpc", "id", "result", "error"}) @JsonInclude(JsonInclude.Include.NON_NULL) public class McpResponse { @JsonProperty("jsonrpc") private String jsonrpc = "2.0"; // JSON-RPC 2.0 中 id 可以是 String、Number 或 null,必须与请求中的 id 保持一致 private Object id; private Object result; private McpError error; @Data @NoArgsConstructor @AllArgsConstructor public static class McpError { private int code; private String message; } }
请求与响应示例

下表展示了不同场景下的消息示例。

场景描述

请求 (Client -> Server)

响应 (Server -> Client)

基本调用(有序参数)

{
"jsonrpc": "2.0",
"method": "subtract",
"params": [42, 23],
"id": 1
}

{
"jsonrpc": "2.0",
"result": 19,
"id": 1
}

基本调用(命名参数)

{
"jsonrpc": "2.0",
"method": "subtract",
"params": {"minuend": 42, "subtrahend": 23},
"id": 3
}

{
"jsonrpc": "2.0",
"result": 19,
"id": 3
}

通知(Notification)
(客户端不期待响应)

{
"jsonrpc": "2.0",
"method": "update",
"params": [1,2,3,4,5]
}
注意:没有id字段

服务端不返回任何响应

调用错误
(例如,方法不存在)

{
"jsonrpc": "2.0",
"method": "nonExistentMethod",
"id": "1"
}

{
"jsonrpc": "2.0",
"error": {
"code": -32601,
"message": "Method not found"
},
"id": "1"
}

无效请求
(例如,method不是字符串)

{
"jsonrpc": "2.0",
"method": 1,
"params": "bar"
}

{
"jsonrpc": "2.0",
"error": {
"code": -32600,
"message": "Invalid Request"
},
"id": null
}

批处理调用 (Batch)

客户端可以发送一个请求对象数组来进行批量调用。服务器处理完后,应返回一个响应对象数组,其中的响应顺序不一定与请求顺序一致,客户端需根据id进行匹配

批量请求示例

[
{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
{"jsonrpc": "2.0", "method": "notify_hello", "params": [7]}, // 这是一个通知
{"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"}
]

批量响应示例

[
{"jsonrpc": "2.0", "result": 7, "id": "1"},
{"jsonrpc": "2.0", "result": 19, "id": "2"}
]

1.2 Cursor对MCP服务的要求是什么?

没有去找官方文档,也没去找其他博客,实践出真知,我是直接接口打断点看看Cursor到底怎么请求的

1.2.1 按照JSON-RPC 2.0协议规范,开发了一个接口

@PostMapping public Mono<ResponseEntity<McpResponse>> handleMcpRequest( @RequestBody McpRequest request, HttpServletRequest httpRequest) { return Mono.just(ResponseEntity.ok().build()); }

1.2.2 在Cursor中配置这个接口

"wikijs": { "type": "http", "url": "http://127.0.0.1:8080/mcp" } }

1.2.3 打断点看看Cursor做了哪些调用,报文是什么

第一次请求(initialize), 初始化,带了一些Cursor的参数

第二次请求(notifications/initialized),通知初始化完成

第三次请求(tools/list),请求工具,这个请求距离initialized有一定时间间隔,断点发现tools/list总共请求了2次并且有周期性的请求(间隔时间较长,推断是定时拉取更新)

总结就是

initialize -> notifications/initialized -> tools/list

1.3 撸个真实的MCP服务

有了以上的基础,现在实现一个真实的MCP服务

@PostMapping public Mono<ResponseEntity<McpResponse>> handleMcpRequest( @RequestBody McpRequest request, HttpServletRequest httpRequest) { String token = (String) httpRequest.getAttribute("wikijs_token"); // initialize 和 initialized 方法不需要 token if ("initialize".equals(request.getMethod())) { return handleInitialize(request); } if ("initialized".equals(request.getMethod())) { // initialized 是一个通知(notification),客户端在收到 initialize 响应后发送 // 根据 JSON-RPC 2.0 规范,通知没有 id,不需要响应 // 但为了兼容性,如果客户端发送了带 id 的请求,我们也返回成功响应 if (request.getId() != null) { // 如果客户端发送了带 id 的请求,返回成功响应 return Mono.just(ResponseEntity.ok() .contentType(org.springframework.http.MediaType.APPLICATION_JSON) .body(McpResponse.success(request.getId(), Map.of()))); } else { // 通知不需要响应,返回 200 OK 但无响应体 return Mono.just(ResponseEntity.ok().build()); } } // tools/list 不需要 token(只是获取工具列表) if ("tools/list".equals(request.getMethod())) { return handleToolsList(request); } // 其他方法需要 token(如 tools/call) if (token == null) { return Mono.just(ResponseEntity.ok( McpResponse.error(request.getId(), -32000, "未授权:必须提供Wiki.js API token") )); } return switch (request.getMethod()) { case "tools/call" -> handleToolsCall(request, token); default -> Mono.just(ResponseEntity.ok( McpResponse.error(request.getId(), -32601, "方法不存在: " + request.getMethod()) )); }; }

这是我使用wiki.js搭建私有知识库做的一个MCP服务,如果你感兴趣可以查看全部的源码,地址见:

https://gitee.com/huang_yang/ai-developed-scaffolding

相信有了以上基础,你可以开发任意你想要的MCP服务,有了这些MCP服务的加持,你的Cursor才可以起飞。

http://www.cnnetsun.cn/news/25757.html

相关文章:

  • 5分钟快速验证:用Docker在Ubuntu搭建Python数据分析环境
  • VCU应用层模型:实车量产中独立功能模型的编译支持
  • Sanic框架CLI参数解析异常深度解析与修复指南
  • 效率对比:传统vs AI辅助开发Vue-Baidu-Map
  • Bodymovin/Lottie完整教程:从零开始掌握动画导出与渲染
  • Figma汉化插件实战:跨国团队协作的救星
  • 永磁同步电机控制玩的就是环套环的把戏,今天咱们拆解一套RSMDO+DBCC组合拳。这玩意儿在工业现场对付参数变化和负载扰动特别带劲,不信你看完下面的代码实操
  • 2024年CUPT技术探索:水滴透镜comsol仿真技术与实际应用分析
  • Linux小白也能懂:fcitx5中文输入法安装使用图解
  • 用AI构建导师评价系统:5分钟开发全流程
  • AI图像修复工具深度优化指南:从基础应用到专业调优
  • AI一键生成Oracle安装脚本,告别复杂配置
  • 实用Node-RED编程:从零到精通的完整学习指南
  • OpenAI Whisper Turbo模型实战部署:从零到一构建高效语音转写系统
  • 企业级项目中依赖注入错误的5个真实案例解析
  • MES信息化智能化解决方案,MES实施方案
  • UE5 材质-28-各种节点:三输出的 if 节点,借助于 time 节点和 Frac(只取小数部分)得到纹理的溶解效果。 也可用于测试 TextureCoordinate 节点的输出向量的值是什么
  • UE5 材质-29-各种节点:
  • 有什么好的团队文件管理软件?测评对比14款
  • CV技术的应用现状与CNN模型识别图像中对象的流程
  • 小兔鲜儿微信小程序开发全攻略
  • 项目分享 | Agent Lightning:零代码改动训练与优化你的AI智能体
  • 用户体验设计终极指南:《用户体验的要素》免费PDF下载
  • 如何快速分析C盘里到底是什么东西占用了最多空间?
  • C盘如何清理?
  • C盘清理?
  • 终极指南:5步轻松部署any-listen私有音乐播放器
  • 从零到一:ArcObjects SDK实战开发完全指南
  • sql server 事务日志备份异常恢复案例---惜分飞
  • 蓝牙音箱EMC整改实战——从±8KV静电复位到稳定过检的技术路径