ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

mcp sdk——demo(1)自定义mcp server(http模式stdio模式)TaoToken 统一 Key 通道实践

mcp sdk——demo(1)自定义mcp server(http模式stdio模式)TaoToken 统一 Key 通道实践 1. 从零搭一个自定义 MCP Server为什么我建议先跑通 http 与 stdio 两种模式mcp sdk 自定义 mcp server 这件事真正卡人的地方从来不是写工具函数而是「传输模式选错、鉴权没打通、客户端连不上」这三件事。MCPModel Context Protocol本质上是给大模型装的一根「外接数据线」模型本身不会查你的数据库但通过 MCP Server 暴露出来的 tool它就能按标准 JSON-RPC 协议去调用你已有的业务接口。适合谁适合手里已经有一套 Spring Boot 业务服务用户、角色、学校这类 CRUD 接口想让 Cursor、Claude Code 这类客户端直接操作业务数据的后端同学。这篇聚焦 mcp sdk 从零搭建自定义 mcp server把 http 模式和 stdio 模式各跑一遍并且统一走 TaoToken 的 Key/API 通道完成鉴权。为什么要统一 Key因为一旦你同时接多个客户端、多个模型Key 散落在各处非常难管统一通道之后Base URL、Key、Model ID 三件套集中配置换模型只改一处。两种模式的核心差异先讲清楚后面配置才不会晕维度http 模式stdio 模式传输方式Streamable-HTTP监听端口标准输入输出子进程启动形态常驻 Web 服务客户端拉起 jar 进程鉴权位置URL 参数 / Headerarguments / 环境变量适合场景多人共享、远程调用本地单机、IDE 集成调试方式curl 直接打手动喂 JSON-RPChttp 模式像开了一家店谁都能按地址来stdio 模式像你随身带的工具包客户端启动时才打开。理解这一点后面的配置就顺了。2. TaoToken 统一 Key 通道前置准备Base URL、Key 与 Model ID 三件套在写 MCP Server 之前先把模型侧的通道准备好。TaoToken 在这里扮演的角色是「统一入口」你的 MCP Server 负责暴露业务工具而模型推理走 TaoToken 的 API 通道两边通过统一的 Key 管理避免每个客户端各配一套。第一步去控制台创建 API Key。打开 https://taotoken.net/api-keys 登录后新建一个 Key复制出来先存好后面配置里会用到。注意这个 Key 只显示一次丢了只能重建。第二步确认 Base URL。TaoToken 的 API 入口是 https://taotoken.net/api 所有兼容 OpenAI 协议的客户端都填这个地址。注意这里不要带任何多余路径客户端一般会自动拼/v1/chat/completions。第三步选 Model ID。在模型对话页面可以先试一下哪个模型符合你的需求https://taotoken.net/models 。选好之后把 Model ID 记下来比如常见的对话模型 ID配置时原样填入。三件套整理成一张表方便你对照配置项值用途Base URLhttps://taotoken.net/api所有请求的根地址API Key控制台生成鉴权凭证Model ID模型对话页选择指定推理模型如果你打算长期做编码类 Agent建议直接看 Coding Planhttps://taotoken.net/coding-plan 它把编码场景的额度和模型打包好了比单次调用省心。接入文档在 https://taotoken.net/doc 遇到协议细节可以对照。这里有个容易踩的坑很多人把 Base URL 写成带/v1的完整路径结果客户端又拼一次变成/v1/v1/...直接 404。记住根地址就是https://taotoken.net/api剩下的交给客户端。准备好这三件套MCP Server 侧的 appKey 鉴权就可以和模型通道解耦MCP 用 appKey 保护你的业务接口模型用 TaoToken Key 走推理各管各的互不干扰。3. 可复制配置http 模式与 stdio 模式的 server 配置片段这一节给可直接复制的配置。先看 http 模式的 pom 依赖核心是mcp-bom和mcp-spring-webmvcdependencyManagement dependencies dependency groupIdio.modelcontextprotocol.sdk/groupId artifactIdmcp-bom/artifactId version0.12.1/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdio.modelcontextprotocol.sdk/groupId artifactIdmcp/artifactId /dependency dependency groupIdio.modelcontextprotocol.sdk/groupId artifactIdmcp-spring-webmvc/artifactId /dependency /dependencieshttp 模式的application.properties注意端口和 appKeyserver.port7778 backend.base-urlhttp://localhost:6666/securityAIDemo backend.user-accountadmin backend.password123 mcp.app-keydemo-key-001http 模式的关键配置类是McpServerConfig它把 MCP 挂在/mcp路径上并用一个 Filter 做鉴权Configuration public class McpServerConfig implements WebMvcConfigurer { public static final String MCP_ENDPOINT /mcp; Bean(name mcpObjectMapper) public ObjectMapper mcpObjectMapper() { ObjectMapper mapper new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); return mapper; } Bean public HttpServletStatelessServerTransport httpServletStatelessServerTransport( Qualifier(mcpObjectMapper) ObjectMapper mcpObjectMapper) { return HttpServletStatelessServerTransport.builder() .objectMapper(mcpObjectMapper) .messageEndpoint(MCP_ENDPOINT) .build(); } }注意FAIL_ON_UNKNOWN_PROPERTIES一定要关掉。Cursor 这类客户端会发 2025-11-25 规范里的capabilities.elicitation.form字段0.12.x 的 SDK 反序列化时会报Unrecognized field form关掉这个开关就绕过去了。再看 stdio 模式。pom 里去掉mcp-spring-webmvc只留mcpdependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdio.modelcontextprotocol.sdk/groupId artifactIdmcp/artifactId /dependency /dependenciesstdio 模式的application.properties要关掉 Web 容器否则 Spring 会往 stdout 打日志污染 JSON-RPCspring.main.web-application-typenone spring.main.banner-modeoff backend.base-urlhttp://localhost:6666/securityAIDemo backend.user-accountadmin backend.password123 mcp.app-keydemo-key-001stdio 的传输配置用StdioServerTransportProviderConfiguration public class StdioMcpConfig { Bean public StdioServerTransportProvider stdioServerTransportProvider( Qualifier(mcpObjectMapper) ObjectMapper mcpObjectMapper) { return new StdioServerTransportProvider(mcpObjectMapper); } Bean public McpSyncServer stdioMcpSyncServer( StdioServerTransportProvider stdioProvider, AppKeyService appKeyService, BackendApiClient backend) { McpSyncServer server McpServer.sync(stdioProvider) .serverInfo(security-ai-mcp, 1.0.0) .capabilities(McpSchema.ServerCapabilities.builder().tools(true).build()) .build(); addUserTools(server, appKeyService, backend); return server; } }stdio 模式必须配logback-spring.xml把日志全部打到 stderrconfiguration appender nameCONSOLE classch.qos.logback.core.ConsoleAppender targetSystem.err/target encoder pattern%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n/pattern charsetUTF-8/charset /encoder /appender root levelINFO appender-ref refCONSOLE/ /root /configuration客户端侧的配置也要给全。Cursor 的mcp.json里http 模式这样写{ mcpServers: { security-ai-mcp: { url: http://localhost:7778/mcp?appKeydemo-key-001 } } }stdio 模式这样写注意env里传 appKey{ mcpServers: { security-ai-mcp: { command: java, args: [-jar, c:/mydemo/security-ai-mcp-demo/target/security-ai-mcp-demo-1.0-SNAPSHOT.jar], env: { MCP_APP_KEY: demo-key-001 } } } }如果你用的是 Claude Code配置在~/.claude/settings.json或项目级 settings 里Base URL 填https://taotoken.net/apiKey 填控制台生成的Model ID 填你选的模型。三件套齐全Claude Code 才能正常走 TaoToken 通道。4. 验证请求一次 curl 与一次 stdio JSON-RPC 的成功结果配置写完先验证 http 模式。启动服务cd security-ai-mcp-demo mvn spring-boot:run看到MCP: transport at /mcp日志就说明起来了。然后用 curl 打一次tools/callcurl -X POST http://localhost:7778/mcp?appKeydemo-key-001 \ -H Content-Type: application/json \ -H Accept: application/json, text/event-stream \ -d {\jsonrpc\:\2.0\,\id\:1,\method\:\tools/call\,\params\:{\name\:\user_list\,\arguments\:{\account\:\zhangsan\,\pageNum\:1,\pageSize\:10}}}注意Accept头必须同时包含application/json和text/event-stream否则 Streamable-HTTP 会拒绝。成功的话你会拿到类似这样的返回{ jsonrpc: 2.0, id: 1, result: { content: [ { type: text, text: {\data\:{\records\:[{\id\:1,\account\:\zhangsan\}],\total\:1},\code\:200} } ], isError: false } }isError为 false说明工具调用成功业务数据也透传回来了。再验证 stdio 模式。先打包再启动cd security-ai-mcp-demo mvn clean package -DskipTests java -jar target/security-ai-mcp-demo-1.0-SNAPSHOT.jar启动后你会看到Server is ready. Waiting for JSON-RPC requests on stdin...。stdio 模式必须按顺序喂三条消息顺序错了没反应。第一条初始化{jsonrpc:2.0,id:1,method:initialize,params:{protocolVersion:2024-11-05,capabilities:{},clientInfo:{name:test,version:1.0}}}第二条发已初始化通知少了这条后面的 tools/call 会被挂起{jsonrpc:2.0,method:notifications/initialized}第三条调用工具{jsonrpc:2.0,id:2,method:tools/call,params:{name:school_list,arguments:{appKey:demo-key-001,pageNum:1,pageSize:5}}}成功返回{jsonrpc:2.0,id:2,result:{content:[{type:text,text:{\data\:{\records\:[{\id\:1,\name\:\第一中学\}],\total\:11},\code\:200}}],isError:false}}如果你故意去掉appKey再调一次会拿到{jsonrpc:2.0,id:2,result:{content:[{type:text,text:Invalid or missing appKey}],isError:true}}这说明鉴权生效了。http 和 stdio 两种模式到这里都跑通了业务接口的返回值也正确透传。5. 本篇常见错排查401、local proxy failed、reading choices 与 OAuth 报错跑 demo 时最容易撞上的几类报错我按现象、原因、解法整理出来。第一类401 Unauthorized或Invalid or missing appKey。http 模式下检查 URL 里的?appKeydemo-key-001是否和application.properties里的mcp.app-key完全一致大小写、空格都算。stdio 模式下检查mcp.json的env.MCP_APP_KEY是否传进去了或者arguments里有没有带appKey。如果两个都没传getAppKeyFromArgs返回 null直接判失败。第二类local proxy failed或连接被拒。这种多半是端口没起来或者被占用。http 模式确认server.port7778没被别的进程占用netstat -ano | findstr 7778查一下。stdio 模式确认 jar 路径写对了Windows 下路径用正斜杠或双反斜杠c:/mydemo/...这种写法最稳。第三类reading choices或Unrecognized field form。这是 SDK 版本和客户端规范不匹配。解法就是前面说的ObjectMapper关掉FAIL_ON_UNKNOWN_PROPERTIES并且在 Filter 里把capabilities.elicitation.form和url字段 strip 掉。McpElicitationStripRequestWrapper就是干这个的它读 body、删字段、再包回去。第四类OAuth 相关报错。如果你在客户端里配了 OAuth 流程但没配好会一直卡在授权。MCP Server 这边其实用的是 appKey 简单鉴权不需要 OAuth。检查客户端配置里有没有多余的auth字段删掉只留url或command。第五类stdio 模式发了tools/call没反应。九成是漏了notifications/initialized这条通知。MCP 服务会等这条再处理后续请求顺序必须是 initialize → initialized → tools/call。第六类日志污染导致 JSON-RPC 解析失败。stdio 模式下如果看到 stdout 里混进了 Spring 的 banner 或 INFO 日志客户端会解析失败。确认spring.main.banner-modeoff和logback-spring.xml的System.err都配了。第七类Backend returned 403。这是你的 MCP Server 调后端业务接口时 token 过期或权限不足。BackendApiClient里已经做了 401/403 清 token 重试一次的逻辑如果还报检查backend.user-account和backend.password是否正确。排查时建议开 debug 日志把logging.level.io.modelcontextprotocolDEBUG加上能看到完整的 JSON-RPC 收发过程定位快很多。6. 把 MCP Server 接到 TaoToken 通道Key 管理与后续扩展两种模式跑通之后最后一步是把模型侧也统一到 TaoToken 通道。MCP Server 负责暴露工具模型负责决策调用哪个工具两边通过客户端串起来。客户端里配置 TaoToken 的三件套{ baseUrl: https://taotoken.net/api, apiKey: 你的 TaoToken Key, model: 你选的 Model ID }这样 Cursor 或 Claude Code 在推理时走 TaoToken调用工具时走你的 MCP Server职责清晰。Key 管理上建议 MCP 的 appKey 和 TaoToken 的 API Key 分开存前者保护业务接口后者管模型额度泄露一个不影响另一个。后续扩展方向有几个。一是把 appKey 从写死改成动态下发和权限系统关联不同用户拿不同 appKey工具调用时按 scope 过滤。二是工具数量多了之后用tools/list做分组客户端按需加载避免一次暴露几十个工具让模型选花眼。三是 http 模式可以加限流stdio 模式可以加超时防止单个客户端把服务打满。如果你要长期跑编码类 AgentCoding Plan 比按次调用更划算额度打包、模型固定省去每次选模型的纠结。接入文档里有完整的协议说明和示例遇到字段对不上可以对照查。实测下来http 模式适合团队共享一个 MCP 服务stdio 模式适合个人本地开发两者配置差异主要在传输层和鉴权位置工具注册逻辑几乎可以复用。把这篇的配置片段复制过去改一下端口和 appKey十分钟就能跑通自己的第一个自定义 MCP Server。
返回列表