Java 从零开始:用 Spring Boot 创建你的第一个 MCP 服务并接入 TaoToken
1. 为什么 Java 后端要自己写一个 MCP 服务MCPModel Context Protocol模型上下文协议说白了就是给 AI 装一套标准插座。以前你想让 Claude、Cursor 这类客户端调用你写的业务方法得自己拼 HTTP 接口、写一堆胶水代码还得处理鉴权和参数校验。MCP 把这层统一了你只要按协议暴露「工具Tool」客户端就能自动发现并调用参数结构、返回格式都由协议约定好。对 Java 后端来说这件事的价值在于复用。你手上已经有一堆 Spring 的 Service、Repository、内部 RPC 客户端与其重写一遍不如用 Spring AI 的 MCP Server Starter 把现有方法直接标注成工具让 AI 客户端通过标准协议调进来。适合谁三类人最合适一是想把内部系统能力开放给 AI 助手的后端二是做智能硬件/Agent 平台、需要统一工具入口的团队三是想学 MCP 协议但不想从零啃 JSON-RPC 的开发者。这篇我会带你从零建一个 Spring Boot 项目定义两个工具取当前时间、整数求和打包成 JAR再把它接到 TaoToken 的统一 Key/API 通道上做连通性验证。全程可复制踩坑点我会在第五节列清楚。核心检索词先记住Spring Boot 创建 MCP 服务、Java MCP Server 接入、TaoToken 统一 Key 通道。2. 前置准备TaoToken 通道与本地环境在写代码之前先把「AI 侧」的通道准备好。MCP 服务本身是工具提供方但你要验证它、或者让上层 Agent 调用模型时需要一个统一的模型入口。TaoToken 在这里扮演的就是统一 Key/API 通道的角色一个 Key 走多家模型Base URL 固定省得你在每个客户端里维护一堆不同的地址和密钥。你需要准备的东西JDK 17 或以上推荐 JDK 21Spring Boot 3.3 对 21 支持很好。Maven 3.8或者用 IDEA 自带的。一个 TaoToken 账号去控制台生成 API Key。地址是 https://taotoken.net/api Key 在 console 里创建路径是 https://taotoken.net/console 。一个能发 HTTP 请求的工具curl 或 Postman 都行用来做连通性检查。关于 Key 的存放我的习惯是绝对不写进代码和 Git。本地用环境变量CI 用 Secret配置文件里只放占位符。你可以这样导出export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/apiTaoToken 的 Base URL 是 https://taotoken.net/api 注意这个地址不带任何查询参数是纯 API 根路径。模型 ID 按你实际要用的填比如 claude 系列或 gpt 系列具体以控制台文档为准。这里有个关键点MCP 服务端和模型调用是两件事MCP 负责「暴露工具」TaoToken 负责「提供模型」。你完全可以让 MCP 服务只做工具模型调用交给上层客户端也可以在自己的服务里同时调模型做增强。这篇两种都会覆盖到验证环节。环境变量设好后先别急着写代码用一条 curl 确认通道是通的curl -s https://taotoken.net/api/v1/models \ -H Authorization: Bearer $TAOTOKEN_API_KEY | head -c 500能返回模型列表 JSON说明 Key 和通道没问题。如果这里就 401先解决鉴权别往下走否则后面报错你会分不清是 MCP 的问题还是 Key 的问题。3. 可复制配置pom、工具类与 MCP 注册这一节是全文的核心所有片段都能直接抄。先建项目用 start.spring.io 生成骨架或者手写 pom.xml。关键是引入 Spring AI 的 MCP Server Starter。project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion groupIdcom.example/groupId artifactIdmy-mcp-server/artifactId version1.0.0/version parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.3.0/version relativePath/ /parent properties java.version21/java.version spring-ai.version1.0.0-M6/spring-ai.version /properties dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-server-spring-boot-starter/artifactId version${spring-ai.version}/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency /dependencies dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /build /project版本号这块要注意Spring AI 的 MCP Starter 在里程碑阶段版本迭代快0.8.x 和 1.0.0-M6 的包名、注解位置有差异。如果你用 0.8.1注解是org.springframework.ai.tool.annotation.Tool用 1.0.0-M6 也基本一致但 BOM 管理更规范。我建议用 BOM 统一版本避免子依赖打架。接下来定义工具类。MCP 的核心就是「工具」一个带Tool注解的 public 方法就是一个可被 AI 调用的工具参数用ToolParam描述描述写得越清楚模型调用时越不容易传错参数。package com.example.mcp; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; Component public class MyTools { Tool(description 获取当前系统时间返回格式 yyyy-MM-dd HH:mm:ss) public String getCurrentTime() { return LocalDateTime.now() .format(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss)); } Tool(description 计算两个整数的和) public int add( ToolParam(description 第一个整数) int a, ToolParam(description 第二个整数) int b) { return a b; } }然后注册到 MCP。Spring AI 用ToolCallbackProvider把工具对象暴露出去package com.example.mcp; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.ai.tool.method.MethodToolCallbackProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class McpConfig { Bean public ToolCallbackProvider myToolCallbackProvider(MyTools myTools) { return MethodToolCallbackProvider.builder() .toolObjects(myTools) .build(); } }主启动类就是标准的 Spring Boot 入口package com.example.mcp; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class McpServerApplication { public static void main(String[] args) { SpringApplication.run(McpServerApplication.class, args); } }配置文件application.properties里声明 MCP 服务元信息spring.application.namemy-mcp-server spring.ai.mcp.server.namemy-tools spring.ai.mcp.server.version1.0.0 spring.ai.mcp.server.typeSYNC如果你想让 MCP 服务同时能调 TaoToken 的模型做增强可以再加一段模型配置。这里用环境变量占位别硬编码spring.ai.openai.base-url${TAOTOKEN_BASE_URL} spring.ai.openai.api-key${TAOTOKEN_API_KEY} spring.ai.openai.chat.options.modelclaude-3-5-sonnet注意spring.ai.openai.base-url填 https://taotoken.net/api TaoToken 兼容 OpenAI 风格的接口路径所以用 openai starter 就能对接。模型 ID 按控制台实际可用的填别照抄我这个示例名。4. 启动验证与接口连通性检查配置写完先本地跑起来看日志。用 Maven 直接启动mvn spring-boot:run默认情况下 MCP Server 以 stdio标准输入输出模式运行适合被 Claude Desktop、Cursor 这类客户端以子进程方式拉起。启动成功的标志是日志里出现 MCP server 初始化信息并且没有端口占用报错。如果你同时引入了 web starter它会额外起一个 HTTP 端口这不冲突但要注意 stdio 模式下别往 stdout 打无关日志否则会污染协议帧。打包成可执行 JARmvn clean package -DskipTests # 产物target/my-mcp-server-1.0.0.jar验证 JAR 能独立跑java -jar target/my-mcp-server-1.0.0.jar接下来做接口连通性检查。分两层第一层是 MCP 协议层第二层是 TaoToken 模型通道层。MCP 协议层最直接的办法是把它接到一个 MCP 客户端里。以 Claude Desktop 为例配置文件在 macOS 是~/Library/Application Support/Claude/claude_desktop_config.jsonWindows 是%APPDATA%\Claude\claude_desktop_config.json。写入{ mcpServers: { my-java-mcp: { command: java, args: [-jar, /absolute/path/to/my-mcp-server-1.0.0.jar] } } }保存后重启客户端在对话里输入「帮我获取一下当前时间」如果工具被正确发现客户端会提示调用getCurrentTime返回类似2025-01-15 14:30:22。再试「计算 12 加 30」应该返回 42。这两个动作跑通说明 MCP 服务端没问题。TaoToken 通道层用 curl 直接打模型接口确认 Key 和 Base URL 正确curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: claude-3-5-sonnet, messages: [{role: user, content: 只回复两个字通了}] }返回体里choices[0].message.content有内容就说明通道 OK。这一步很关键因为很多人把 MCP 报错和模型报错混在一起排查分开验证能省一半时间。如果你在 Spring 服务里也配了模型调用可以写个简单的 CommandLineRunner 在启动时打一条测试请求日志里能看到响应就放心了。5. 本篇常见错误排查这一节按真实报错来都是我或身边人踩过的。401 Unauthorized。出现在 curl 或 Spring 调模型时。原因基本是 Key 没读到或格式不对。检查TAOTOKEN_API_KEY是否真的导出到了当前 shellecho $TAOTOKEN_API_KEY看一眼。Spring 里如果用了${TAOTOKEN_API_KEY}但环境变量没设启动会直接失败或传空串。另外注意 Header 是Authorization: Bearer sk-xxxBearer 后面有空格别漏。local proxy failed / connection refused。这个报错通常出现在客户端拉起 MCP 子进程时command或args路径写错或者 java 不在 PATH 里。解决办法是用绝对路径command: /usr/bin/javaJAR 也用绝对路径。Windows 上路径反斜杠要转义成\\或者直接用正斜杠。reading choices of undefined。这是解析模型响应时拿不到choices字段。常见原因是 Base URL 写成了https://taotoken.net/api/v1又在代码里拼了/v1导致路径变成/v1/v1/chat/completions。记住 Base URL 只到 https://taotoken.net/api 版本段由 SDK 自己拼。另一个原因是模型 ID 写错接口返回了错误对象而不是正常响应。OAuth / token expired。如果你用的是需要 OAuth 的客户端比如某些 IDE 插件报这个说明授权过期重新走一遍授权流程即可。注意这跟 TaoToken 的 API Key 是两套东西别混。MCP 工具没被发现。客户端里看不到你的工具先确认ToolCallbackProviderBean 被扫描到了包路径要在SpringBootApplication同级或子级。再确认Tool方法所在类有Component。还有一个隐蔽点stdio 模式下任何System.out.println都会破坏协议把日志级别调高或改用 stderr。CC Switch / Cline MCP / Codex auth.json 三件套。如果你用这些工具接 MCP配置里必须同时给全三样Base URL、Key、Model ID。少一样就连不上。以 Cline 的 MCP 配置为例结构大致是{ mcpServers: { my-java-mcp: { command: java, args: [-jar, /abs/path/my-mcp-server-1.0.0.jar], env: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_API_KEY: sk-你的key, TAOTOKEN_MODEL: claude-3-5-sonnet } } } }Codex 的auth.json同理Base URL 和 Key 要对上Model ID 不能空。这三件套缺一个表现就是工具列表空或者调用超时。6. 把 MCP 服务接到 TaoToken 统一通道工具跑通之后最后一步是让它和 TaoToken 的通道协同工作。有两种典型用法。第一种MCP 只做工具模型调用交给客户端。你在 Claude Desktop 或 Cline 里配置 TaoToken 作为模型提供方Base URL 填 https://taotoken.net/api Key 填你的Model ID 选好。这样客户端用 TaoToken 的模型来「思考」用你的 Java MCP 服务来「执行」职责清晰。这种模式下你的 Spring 服务不需要配模型最省事。第二种MCP 服务内部也调模型做增强。比如你的工具需要先让模型总结一段文本再返回。这时在 Spring 里配好spring.ai.openai.base-url和api-key注入ChatClient调用即可。好处是工具内部逻辑闭环坏处是模型调用和工具调用耦合排查问题时要多看一层日志。不管哪种Key 的管理都建议走环境变量或配置中心别写死在代码里。TaoToken 的统一 Key 通道优势就在这里一个 Key 覆盖多个模型你换模型只改 Model ID不用换地址和密钥MCP 服务端配置几乎不用动。如果你要长期跑编码类 Agent或者需要多模型切换做对比可以看看 Coding Plan路径是 https://taotoken.net/coding-plan 。接入文档在 https://taotoken.net/doc API Key 管理在 https://taotoken.net/api-keys 。模型对话调试入口是 https://taotoken.net/chat 适合快速验证某个模型 ID 是否可用。最后给个实操建议把 MCP 服务的工具描述写细。Tool(description...)和ToolParam(description...)不是给人看的是给模型看的。描述里写清楚单位、格式、边界条件模型调用准确率会明显提升。我试过把「计算两个整数的和」改成「计算两个 32 位有符号整数的和返回 int」参数传错的概率下降很多。工具描述就是你和模型之间的接口文档值得多花五分钟。

相关新闻

轻量级日志采集系统从零搭建实践指南

轻量级日志采集系统从零搭建实践指南

无法基于当前输入生成博文。项目标题仅为数字串“12312132123123”,项目正文、关键词、摘要描述均为空,相关热搜词和网络热词也缺失,没有可围绕的核心主题、技术点、应用场景或读者需求线索。请提供完整的输入信息,格式如下&#…

2026/10/10 20:25:12 阅读更多 →
Cursor还能不能用!!!把Base URL改到TaoToken的实测排查

Cursor还能不能用!!!把Base URL改到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/10 20:25:12 阅读更多 →
为Ubuntu终端接入大模型Codex:把auth.json改到TaoToken的一行指令

为Ubuntu终端接入大模型Codex:把auth.json改到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/10 20:25:12 阅读更多 →

最新新闻

Kubernetes Python 客户端 V1QueuingConfiguration 模型详解:API 优先级与公平调度(APF)排队参数实战指南

Kubernetes Python 客户端 V1QueuingConfiguration 模型详解:API 优先级与公平调度(APF)排队参数实战指南

后端云原生容器编排 【免费下载链接】python Official Python client library for kubernetes 项目地址: https://gitcode.com/gh_mirrors/python1/python 点击查看 免费下载 本指南围绕 Kubernetes 官方 Python 客户端(kubernetes)中由 Ope…

2026/10/10 21:13:01 阅读更多 →
AI时代新范式:企业如何应用BI系统结合大模型实现智能问数

AI时代新范式:企业如何应用BI系统结合大模型实现智能问数

一、数据“看得到”却“用不上”:企业面临的真实困境企业在数据基础设施上的投入持续增长,但数据转化为业务价值的效率并未同步提升。据《全国数据资源调查报告(2025年)》显示,2025年全国年度数据生产总值达52.26泽字节…

2026/10/10 21:13:01 阅读更多 →
新能源汽车电子元器件采购:车规级AEC-Q100检测看什么?

新能源汽车电子元器件采购:车规级AEC-Q100检测看什么?

新能源汽车元器件单价高、可靠性要求严、批次追溯要求严——一辆新能源车上的电子元器件超过3000颗,任何一颗失效都可能导致召回。车规级元器件采购的核心是AEC-Q100检测,看懂AEC-Q100报告才能判断一颗芯片能不能上车。一、AEC-Q100 是什么AEC-Q100 是汽…

2026/10/10 21:13:01 阅读更多 →
6天3.1k星、一小时涨50颗:SemIf 凭什么让程序员重新审视 if 语句

6天3.1k星、一小时涨50颗:SemIf 凭什么让程序员重新审视 if 语句

6天3.1k星、一小时涨50颗:SemIf 凭什么让程序员重新审视 if 语句 【免费下载链接】SemIf-OpenJev Semantic ifs from open models, on a 3090 at home. Independent; not affiliated with Jev or TypeSafe. 项目地址: https://gitcode.com/gh_mirrors/op/SemIf-Op…

2026/10/10 21:13:01 阅读更多 →
Selenium显式等待优化实战:回归测试耗时降低41%的改造方案

Selenium显式等待优化实战:回归测试耗时降低41%的改造方案

我曾经接过一个支付类后台的回归测试优化任务。套件里有 60 条用例,跑完要一个小时出头,其中大量时间花在界面上转圈、按钮半天不亮、弹窗迟迟不弹这些"无谓等待"上。把耗时明细打出来以后发现,Selenium 的WebDriverWait占了差不多…

2026/10/10 21:13:00 阅读更多 →
Sentinel系统规则实战:CPU使用率与JVM指标联动限流

Sentinel系统规则实战:CPU使用率与JVM指标联动限流

Sentinel 系统规则实战:让 JVM 的 CPU 使用率直接参与限流决策先扯个实际场景。你有没有遇到过这种诡异情况:线上服务 QPS 明明还在可接受范围,接口 RT 却开始直线飙高,紧接着整个应用像被什么东西掐住脖子一样,卡顿、…

2026/10/10 21:12:00 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/10 11:14:25 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/10 11:14:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/10 10:38:42 阅读更多 →