news 2026/10/11 10:23:59

用 Java 5 分钟写一个 MCP Server:基于开源 MCP Java SDK 接入 TaoToken 统一 Key

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用 Java 5 分钟写一个 MCP Server:基于开源 MCP Java SDK 接入 TaoToken 统一 Key

1. Java 开发者为什么需要一个能跑起来的 MCP Server

MCP 全称 Model Context Protocol,你可以把它理解成 AI Agent 和外部世界之间的一根标准数据线。大模型本身只会生成文本,它不知道你数据库里有哪些表、不知道你 Redis 里缓存了什么、更没法直接调用你 Spring Boot 里那个写了三年的订单查询接口。MCP 就是来解决这件事的:它定义了一套统一的协议,让 Agent 通过标准方式发现并调用你暴露出来的工具、资源和提示词。

我身边不少 Java 同学第一次接触 MCP 时,看的都是 Node.js 或 Python 的示例。照着敲一遍能跑,但一旦想把自己项目里的业务能力接进去,就卡住了——总不能为了一个工具调用再学一套 JS 生态吧。Java 开发者需要的是一个符合自己习惯的入口:Maven 依赖、注解、Spring Boot 自动装配,最好五分钟内能看到一个能响应请求的 Server。

这篇要做的就是这件事。我会用一个开源的 MCP Java SDK,带你从零搭一个 MCP Server,把工具注册进去,本地启动,然后用 curl 验证工具列表能不能正常返回。同时把服务端点的鉴权配置统一改到 TaoToken 的 Key/API 通道上,这样你后面接 Claude Code、Cline 或者自己的 Agent 时,不用每个客户端都单独配一遍密钥。

适合谁看:有 Java 基础、用过 Maven、写过 Spring Boot 的开发者;想把自己的内部系统暴露给 AI Agent 但不想碰 Node/Python 的人;以及已经在用 MCP 客户端、想自己写 Server 的折腾党。

先说清楚 MCP Server 到底在干什么。它本质上是一个进程,通过 stdio 或者 SSE 两种传输方式和客户端通信。客户端发过来的是 JSON-RPC 格式的请求,比如tools/list、tools/call,Server 解析后执行对应逻辑,再把结果按协议格式返回。你不需要手写 JSON-RPC 的序列化反序列化,SDK 会帮你处理。你要做的只有两件事:定义工具方法,启动传输层。

工具方法就是普通的 Java 方法,加上注解描述它的名字、参数、用途。SDK 在启动时扫描这些注解,把它们注册成 MCP 协议里的 tool。Agent 看到的工具列表,就是你这些方法的元数据。

这里有个容易混淆的点:MCP Server 不是 Web 服务,至少 stdio 模式下不是。它不监听端口,不处理 HTTP 请求,而是通过标准输入输出和父进程通信。所以你不能用浏览器直接访问它,得用 MCP 客户端或者专门的调试工具。这也是为什么后面验证环节我会用 curl 配合 SSE 模式,而不是直接 curl 一个 stdio 进程。

理解了这些,再看代码就不会觉得是在念咒语了。下面进入实操。

2. TaoToken 统一 Key 的前置准备与 MCP Java SDK 依赖引入

在写代码之前,先把 Key 的事情理清楚。MCP Server 本身如果只是本地 stdio 跑,其实不涉及外部鉴权。但一旦你的工具需要调用大模型能力,或者你要把 Server 以 SSE 方式暴露出去给远程 Agent 用,就需要一个统一的 API 通道来管理鉴权和计费。TaoToken 在这里扮演的就是这个角色:一个统一的 Key 和 API 入口,兼容主流模型调用格式,你不需要为每个客户端单独申请密钥。

先去官网注册并拿到 Key:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。登录后在控制台创建 API Key,复制出来保存好,后面配置里要用。API 的基础地址是 https://taotoken.net/api ,注意这个地址不带任何查询参数,配置时直接填这个。

拿到 Key 之后,回到 Java 工程。我用的是 Maven 多模块结构,核心依赖是开源的 MCP Java SDK。在pom.xml里加入以下依赖:

<dependencies> <dependency> <groupId>io.github.6000fish</groupId> <artifactId>mcp-sdk</artifactId> <version>0.1.1</version> </dependency> <dependency> <groupId>io.github.6000fish</groupId> <artifactId>mcp-spring-boot-starter</artifactId> <version>0.1.1</version> </dependency> </dependencies>

如果你只是想要核心 SDK,不加 Spring Boot Starter 也能跑,用DefaultMcpServer.builder()手动构建即可。但既然场景里提到了 Spring Boot 工程结构,我建议两个都加上,后面用 starter 会自动扫描注解并装配 Server,省掉手写启动类的麻烦。

依赖拉下来之后,确认一下版本。0.1.1 是当前稳定版,已经发布到 Maven Central,不需要额外配仓库地址。如果你公司内网有私服,记得把io.github.6000fish这个 groupId 加进镜像白名单,否则可能拉不到。

接下来配置 Key。在src/main/resources/application.yml里加上:

taotoken: api-key: ${TAOTOKEN_API_KEY:sk-your-key-here} base-url: https://taotoken.net/api model: claude-sonnet-4-20250514

这里用环境变量优先、配置文件兜底的方式,避免把 Key 硬编码进代码提交到 Git。本地开发时在 IDE 的运行配置里加一个TAOTOKEN_API_KEY环境变量就行。生产环境用配置中心或者容器 secret 注入。

注意base-url填的是https://taotoken.net/api,不要在后面加/v1或者别的路径,SDK 内部会自己拼接。模型 ID 按你实际要用的填,这里只是示例。

依赖和配置都齐了,下一步写 Server 启动类和工具注册。

3. 可复制的 Server 启动类与工具注册配置

先写一个最简的启动类。如果你用了 Spring Boot Starter,其实可以更省事,但为了让你看清楚整个链路,我先给出手动构建的版本,再给 Spring Boot 版本。

手动构建的启动类长这样:

import io.github.mcpjava.sdk.DefaultMcpServer; import io.github.mcpjava.sdk.McpServer; import io.github.mcpjava.sdk.transport.StdioTransport; import io.github.mcpjava.sdk.annotation.McpAnnotationScanner; public class MyMcpServer { public static void main(String[] args) { McpServer server = DefaultMcpServer.builder() .name("my-java-server") .version("1.0.0") .build(); McpAnnotationScanner.scan(server, new MyTools()); server.start(new StdioTransport()); } }

DefaultMcpServer.builder()构建 Server 实例,name和version是给客户端看的元数据。McpAnnotationScanner.scan()扫描你传入的对象,把带注解的方法注册成工具。最后server.start(new StdioTransport())启动 stdio 传输,进程会阻塞在这里等待客户端请求。

工具类这样写:

import io.github.mcpjava.sdk.annotation.McpTool; import io.github.mcpjava.sdk.annotation.Param; public class MyTools { @McpTool(name = "greet", description = "根据名字返回问候语") public String greet(@Param(name = "name") String name) { return "Hello, " + name + "!"; } @McpTool(name = "current_time", description = "返回服务器当前时间") public String currentTime() { return java.time.LocalDateTime.now().toString(); } @McpTool(name = "calculate", description = "计算两个整数之和") public int calculate( @Param(name = "a") int a, @Param(name = "b") int b) { return a + b; } }

每个@McpTool方法就是一个工具。name是 Agent 调用时用的标识,description会展示给模型看,帮它判断什么时候该调这个工具。参数用@Param标注名字,SDK 会自动做类型转换。

如果你用 Spring Boot Starter,可以省掉手动扫描。在启动类上加@SpringBootApplication,然后定义一个@Bean:

@Configuration public class McpConfig { @Bean public McpServer mcpServer(MyTools myTools) { McpServer server = DefaultMcpServer.builder() .name("spring-boot-mcp-server") .version("1.0.0") .build(); McpAnnotationScanner.scan(server, myTools); return server; } }

然后在application.yml里指定传输方式:

mcp: transport: stdio server: name: spring-boot-mcp-server version: 1.0.0

Starter 会在应用启动时自动拉起 Server。如果你要改成 SSE 模式,把transport改成sse,再加一个端口配置:

mcp: transport: sse sse: port: 8081 path: /mcp/sse

SSE 模式下 Server 会监听 HTTP 端口,这时候就可以用 curl 来验证了。stdio 模式没法直接 curl,得用 MCP 客户端连。

关于鉴权配置改到 TaoToken 统一 Key 这件事,分两种情况。如果你的工具方法内部要调用大模型,比如做一个「总结文本」的工具,那就在工具类里注入一个 HTTP 客户端,请求时带上 TaoToken 的 Key:

@McpTool(name = "summarize", description = "调用大模型总结文本") public String summarize(@Param(name = "text") String text) { // 从配置读取 apiKey 和 baseUrl String apiKey = System.getenv("TAOTOKEN_API_KEY"); String baseUrl = "https://taotoken.net/api"; // 用 HttpClient 发请求,Header 里带 Authorization: Bearer {apiKey} // 具体请求体按模型接口格式构造 return callModel(baseUrl, apiKey, text); }

如果你的 Server 是以 SSE 方式暴露给远程 Agent,那鉴权应该在传输层做。可以在 SSE 的路径上加一个拦截器,校验请求头里的 Key 是否匹配 TaoToken 下发的凭证。这样所有连过来的 Agent 都走同一个 Key 通道,不用每个客户端单独配。

配置片段汇总一下,方便你直接复制。application.yml:

server: port: 8080 taotoken: api-key: ${TAOTOKEN_API_KEY} base-url: https://taotoken.net/api model: claude-sonnet-4-20250514 mcp: transport: sse server: name: my-java-server version: 1.0.0 sse: port: 8081 path: /mcp/sse

pom.xml依赖片段前面已经给过,这里不重复。注意 Spring Boot Starter 的版本要和 mcp-sdk 保持一致,都是 0.1.1,混用版本可能出现注解扫描不到的问题。

代码写完,mvn package打包,然后java -jar target/my-server-1.0.0.jar启动。看到日志里输出MCP Server started on SSE port 8081就说明起来了。

4. 验证请求:用 curl 检查 MCP 工具列表与调用结果

Server 起来之后,第一件事是确认工具列表能正常返回。SSE 模式下,MCP 的交互分两步:先建立 SSE 连接拿到一个 session 端点,再往那个端点发 JSON-RPC 请求。

先开一个终端,发起 SSE 连接:

curl -N http://localhost:8081/mcp/sse

-N是关闭缓冲,让你能实时看到服务端推过来的事件。正常的话会返回类似这样的内容:

event: endpoint data: /mcp/message?sessionId=abc123-def456

这个sessionId就是本次会话的标识,后面的请求都要带上它。记下这个路径。

再开一个终端,发tools/list请求:

curl -X POST "http://localhost:8081/mcp/message?sessionId=abc123-def456" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {} }'

如果一切正常,你会收到工具列表的 JSON 响应,里面包含greet、current_time、calculate三个工具的元数据,每个都有 name、description 和 inputSchema。inputSchema 是 SDK 根据方法参数自动生成的 JSON Schema,Agent 靠它知道该传什么参数。

接着验证工具调用:

curl -X POST "http://localhost:8081/mcp/message?sessionId=abc123-def456" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "greet", "arguments": { "name": "Java" } } }'

预期返回:

{ "jsonrpc": "2.0", "id": 2, "result": { "content": [ { "type": "text", "text": "Hello, Java!" } ] } }

看到这个就说明整条链路通了:curl 发请求 → SSE 传输 → SDK 解析 JSON-RPC → 调用你的 Java 方法 → 结果按协议格式返回。

再测一下calculate:

curl -X POST "http://localhost:8081/mcp/message?sessionId=abc123-def456" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "calculate", "arguments": { "a": 3, "b": 5 } } }'

返回的 text 应该是8。注意 SDK 会把返回值转成字符串放进 content 里,即使你方法返回的是 int。

如果你用的是 stdio 模式,没法直接 curl,可以用 MCP 官方提供的 inspector 工具,或者直接在你的 Agent 客户端里配置。以 Claude Code 为例,在配置文件里加:

{ "mcpServers": { "my-java-server": { "type": "stdio", "command": "java", "args": [ "-jar", "/absolute/path/to/target/my-server-1.0.0.jar" ], "env": { "TAOTOKEN_API_KEY": "sk-your-key-here" } } } }

重启客户端后,Agent 就能看到你注册的工具了。这里 env 里传的TAOTOKEN_API_KEY会被 Server 进程读取,用于内部调用大模型时的鉴权。

验证环节的关键是:先确认tools/list能返回,再确认tools/call能执行。两步都过了,说明 Server 本身没问题,剩下的就是往工具方法里填业务逻辑。

5. 常见报错排查:401、local proxy failed 与 reading choices

实际跑的时候大概率不会一次成功,我把踩过的坑列一下,对照着排查。

401 Unauthorized。这个最常见,通常是 TaoToken 的 Key 没配对。检查三个地方:环境变量TAOTOKEN_API_KEY是否真的注入到了进程里(可以在启动类里打印一下System.getenv("TAOTOKEN_API_KEY")确认);base-url是否写成了https://taotoken.net/api,有没有多写/v1或者结尾斜杠;请求头里的Authorization格式是否是Bearer sk-xxx,注意 Bearer 后面有一个空格。如果 Key 是从配置文件读的,确认 YAML 缩进没写错,api-key和base-url在同一层级。

local proxy failed。这个报错一般出现在你通过某个客户端连 Server 时,客户端尝试走本地代理但没连上。先确认你的 Server 进程本身是活的,ps -ef | grep java能看到。然后确认端口没被占用,lsof -i:8081检查一下。如果是 stdio 模式,检查客户端配置里的command路径是不是绝对路径,args里的 jar 包路径是否存在。相对路径在客户端启动时的工作目录可能和你预期的不一样,统一用绝对路径最稳。

reading choices 相关报错。这个通常出现在工具方法内部调用大模型接口时,响应体解析失败。原因可能是模型返回的 JSON 结构和你的解析代码不匹配,或者model参数填的模型 ID 不存在。先确认application.yml里的model值是你账号下有权限调用的模型。然后在调用大模型的地方把原始响应打出来看看,别直接反序列化。如果响应里是错误信息而不是正常结构,解析自然会失败。

工具列表为空。tools/list返回空数组,说明注解扫描没生效。检查工具类是否被McpAnnotationScanner.scan()传进去了;@McpTool注解的包路径是否和依赖里的类一致;方法是否是 public 的。Spring Boot Starter 模式下,确认工具类被 Spring 管理(加了@Component或者@Bean声明)。

SSE 连接建立后收不到 endpoint 事件。检查mcp.sse.path配置和 curl 的 URL 是否一致。有些客户端会在路径后面自动加斜杠,导致匹配不上。另外确认没有防火墙或者安全组拦截 8081 端口。

JSON-RPC 返回 method not found。说明请求的 method 名字拼错了,MCP 协议里是tools/list和tools/call,注意是复数 tools,不是 tool。id 字段也要带上,否则某些客户端会认为是通知而不是请求。

中文乱码。工具返回的中文在客户端显示成问号,检查启动参数里有没有加-Dfile.encoding=UTF-8。Java 默认编码在有些系统上不是 UTF-8,显式指定一下。

排查的基本思路是:先确认进程活着,再确认传输层通,再确认协议层通,最后确认业务逻辑对。一层一层往下查,别跳步。

6. 把 MCP Server 接进你的日常开发流

Server 跑通之后,接下来就是把它用起来。几个实际场景你可以直接套。

第一个场景是把内部接口暴露给 Agent。你有一个查询订单状态的 HTTP 接口,写一个@McpTool方法包一层,Agent 就能在对话里直接查订单。方法内部用 HttpClient 调你的接口,返回结果转成字符串。这样不用改现有系统,加一个 MCP Server 模块就行。

第二个场景是数据库查询。开源项目里已经带了 MySQL 和 Redis 的 ready-to-use Server,你直接构建就能用。MySQL Server 支持query、list_tables、describe_table等工具,而且做了安全限制,query只允许单条 SELECT,execute只允许 INSERT 和 UPDATE,DELETE 和 DROP 会被拒绝。这对让 Agent 辅助排查数据问题很有用,又不用担心它误删数据。

第三个场景是配合 Coding Plan 做长期编码任务。如果你在用 Claude Code 或者类似的编码 Agent,可以把 MCP Server 配进去,让 Agent 在写代码时能调用你的工具。比如一个「查接口文档」的工具,Agent 写完代码后自己调一下确认参数对不对。这种场景下建议用 TaoToken 的 Coding Plan,统一管理调用额度和鉴权,不用每个工具单独配 Key。具体可以看 https://taotoken.net/coding-plan 。

配置的时候记住三件套:Base URL 填https://taotoken.net/api,Key 填你控制台创建的 API Key,Model ID 填你要用的模型标识。这三个在客户端配置、环境变量、代码里都要保持一致,任何一处写错都会导致 401 或者模型调用失败。

如果你用的是 Cline 或者带 MCP 支持的编辑器插件,配置方式类似,在 MCP 配置里加一个 server 条目,type 选 stdio 或 sse,command 和 args 指向你的 jar 包。CC Switch 这类工具切换配置时,注意把 TaoToken 的 Key 一起带过去,别切完发现鉴权丢了。

调试工具方面,模型对话页面可以帮你快速验证 Key 和模型是否可用:https://taotoken.net/model-conversation 。接入文档在 https://taotoken.net/doc ,里面有各客户端的详细配置步骤。API Keys 管理在 https://taotoken.net/api-keys ,Key 泄露了可以在这里吊销重发。

最后说一个实用技巧:把 MCP Server 的启动脚本写成 shell,把环境变量注入和 java 命令放一起,这样换机器或者换客户端时直接跑脚本,不用每次手动配。脚本里记得用exec java -jar ...而不是直接java -jar ...,这样信号能正确传递给 JVM 进程,客户端关闭时 Server 能干净退出。

代码写到这里,一个能跑、能验证、能接进日常流程的 Java MCP Server 就完整了。剩下的就是往工具方法里填你自己的业务逻辑,把内部能力一个个暴露出去。

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

AI测试工具ROI评估方法论:从成本拆解到收益量化实战指南

测了三个月AI测试工具&#xff0c;我总结了一套ROI评估方法先说结论&#xff1a;绝大多数测试团队在引入AI工具时&#xff0c;根本没搞清这笔账怎么算。问起来就是"感觉效率提升了""用例生成快了不少"&#xff0c;但你要是追问一句&#xff1a;具体快了多少…

作者头像 李华
网站建设 2026/10/11 10:21:32

AI生成代码逻辑幻觉全解析:从原理到检测与防御的TaoToken实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/11 10:17:57

正则表达式调试难?REA可视化工具核心实现全复盘

做开发这几年&#xff0c;最常听到的一句话就是“正则写对了吗”。正则表达式这东西&#xff0c;语法本身不难&#xff0c;难的是你不知道它匹配到哪一步了&#xff0c;为什么这个文本没命中&#xff0c;为什么在某个引擎里好使换到另一个就挂。REA&#xff08;Regular Express…

作者头像 李华
网站建设 2026/10/11 10:15:21

LLM模型生产部署:vLLM调优、AWQ量化与热更新实战

简介&#xff1a;本资源是面向大模型工程实践者的权威技术手册《LLM Engineers Handbook》&#xff0c;由领域专家Paul Iusztin与Maxime Labonne联合撰写&#xff0c;系统覆盖从LLM原理、模型选型、数据准备、训练调优、评估测试到生产部署的全链路工程方法&#xff0c;特别聚焦…

作者头像 李华
网站建设 2026/10/11 10:13:03

给AI助手装长期记忆:claude-mem 架构与实操详解

1. 项目概述&#xff1a;给聊天机器人装上“长期记忆”做 AI 应用开发的朋友&#xff0c;大概率都遇到过这样一个痛点&#xff1a;明明在对话里告诉过助手某些固定偏好&#xff0c;比如“代码注释一律用中文”“错误处理统一返回特定格式”&#xff0c;下次新开一个对话窗口&am…

作者头像 李华