news 2026/6/23 1:44:09

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手把手教你为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才可以起飞。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/22 23:53:51

5分钟快速验证:用Docker在Ubuntu搭建Python数据分析环境

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 生成一个即开即用的Docker容器配置&#xff0c;在Ubuntu上快速部署包含以下组件的Python数据分析环境&#xff1a;1.Jupyter Notebook 2.Pandas 1.5 3.Matplotlib 4.Scikit-learn 5…

作者头像 李华
网站建设 2026/6/23 13:03:18

VCU应用层模型:实车量产中独立功能模型的编译支持

vcu应用层模型&#xff0c;实车量产在用。 应用层建模学习&#xff0c;可通过成熟的模型&#xff0c;借鉴逻辑处理和算法&#xff0c;除整体模型外&#xff0c;每个功能有单独的模型&#xff0c;包含接口定义&#xff0c;支持编译。凌晨两点的车间还亮着灯&#xff0c;老王叼着…

作者头像 李华
网站建设 2026/6/23 14:48:55

Sanic框架CLI参数解析异常深度解析与修复指南

Sanic框架CLI参数解析异常深度解析与修复指南 【免费下载链接】sanic Accelerate your web app development | Build fast. Run fast. 项目地址: https://gitcode.com/gh_mirrors/sa/sanic Sanic作为Python生态中备受瞩目的异步Web框架&#xff0c;以其卓越的性能表现和…

作者头像 李华
网站建设 2026/6/23 21:04:07

效率对比:传统vs AI辅助开发Vue-Baidu-Map

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 请生成两份对比代码&#xff1a;1.传统方式手动编写的vue-baidu-map基础功能实现&#xff1b;2.使用AI生成的相同功能代码。功能包括&#xff1a;地图初始化、标记点添加、信息窗口…

作者头像 李华
网站建设 2026/6/23 20:26:26

Bodymovin/Lottie完整教程:从零开始掌握动画导出与渲染

Bodymovin/Lottie完整教程&#xff1a;从零开始掌握动画导出与渲染 【免费下载链接】lottie-web 项目地址: https://gitcode.com/gh_mirrors/lot/lottie-web 还在为精美的After Effects动画无法直接应用到网页而烦恼&#xff1f;设计师与开发者的工作流程脱节导致项目延…

作者头像 李华
网站建设 2026/6/23 2:35:44

Figma汉化插件实战:跨国团队协作的救星

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个详细的Figma汉化插件使用教程项目&#xff0c;包含逐步安装指南、配置说明和常见问题解答。要求演示如何在不同场景下使用插件&#xff0c;如UI设计审查、团队协作和客户演…

作者头像 李华