1. Java8 项目接 MCP Server 到底卡在哪
如果你手上还有一堆跑在 JDK8 上的老服务,最近又被 MCP(Model Context Protocol)这个词刷屏,大概率会经历这样一个过程:先搜「Java 开发 MCP Server」,然后发现官方mcp-sdk要求 JDK17+,spring-ai-mcp-server要求 JDK17+,langchain4j-mcp-client也是 JDK17+。一圈看下来,结论好像是「想玩 MCP,先把 JDK 升到 17」。
但现实是,很多后端团队的生产环境还锁在 JDK8:编译链、依赖树、运维脚本、灰度流程全都围绕 JDK8 建好了,为了一个协议层的能力去动整个运行时,成本高得离谱。MCP 本身是一个协议框架,它的价值在于「让模型能调用你的工具」,而不是「逼你换 JDK」。所以真正的问题是:有没有一个能在 JDK8 上直接跑起来的 MCP Server 实现?
我试过 Solon AI MCP 这条路,它把 MCP Server 和 MCP Client 都做成了 JDK8 可用的依赖包,同时兼容 JDK11/17/21。也就是说,你不需要升级运行时,只要在现有 Java8 工程里加一个依赖,就能把工具方法注册成 MCP 端点,让支持 MCP 的客户端(比如 Claude Code、各类 Agent 框架)来调用。这篇就按「能直接复制」的标准,把 pom 依赖、启动类、config.toml骨架、以及通过 TaoToken 统一 Key/API 通道做一次工具调用验证的完整路径写清楚,适合还在维护 Java8 项目的后端同学照着搭。
2. 为什么选 Solon AI MCP 而不是官方 SDK
先把几个主流方案的 JDK 要求摆出来,这样你选型时不用来回翻文档:
| 方案 | JDK 要求 | 说明 |
|---|---|---|
| mcp-sdk | JDK17+ | 官方协议实现,版本跟进快 |
| spring-ai-mcp-server | JDK17+ | 绑定 Spring 生态,配置偏重 |
| spring-ai-mcp-client | JDK17+ | 同上 |
| langchain4j-mcp-client | JDK17+ | 偏客户端集成 |
| solon-ai-mcp-server | JDK8+ | 本篇主角,服务端 |
| solon-ai-mcp-client | JDK8+ | 本篇主角,客户端 |
Solon AI 本身是一个 Java AI 全场景开发框架,覆盖 LLM、Function Call、RAG、Embedding、Reranking、Flow、MCP Server、MCP Client 这些能力,同时支持 Java8/11/17/21。它可以和 Solon 集成,也能嵌到 SpringBoot2、jFinal、Vert.x 里用。对 Java8 团队来说,关键点有两个:一是依赖包本身不要求高版本 JDK,二是它的 MCP Server 支持多端点,一个进程里可以挂多个sseEndpoint,这对老项目做能力拆分很友好。
组件式写法长这样,和写 MVC Controller 几乎一样:
@McpServerEndpoint(name = "mcp-case1", sseEndpoint = "/case1/sse") public class McpServerTool { @ToolMapping(description = "查询天气预报") public String getWeather(@ToolParam(description = "城市位置") String location) { return "晴,14度"; } }如果你不想用注解,也可以用原生 Java 方式构建:
McpServerEndpointProvider serverEndpoint = McpServerEndpointProvider.builder() .name("mcp-case2") .sseEndpoint("/case2/sse") .build(); serverEndpoint.addTool(new MethodToolProvider(new McpServerTool())); serverEndpoint.postStart();客户端调用也很直接:
McpClientToolProvider clientToolProvider = McpClientToolProvider.builder() .apiUrl("http://localhost:8080/case1/sse") .build(); String rst = clientToolProvider.callToolAsText("getWeather", Map.of("location", "杭州"));注意:
Map.of是 JDK9 才有的 API,Java8 下要换成Collections.singletonMap或HashMap,这是后面排障章节会重点讲的一个坑。
3. TaoToken 前置:统一 Key 与 API 通道
MCP Server 本身只负责「暴露工具」,但工具背后往往要调模型或外部能力。如果每个服务各自维护一套 Key、各自拼一套 API 地址,老项目里很快就会变成配置灾难。我的做法是走 TaoToken 的统一通道:一个 Key 覆盖模型对话、编码计划、控制台等入口,API 地址统一成https://taotoken.net/api,这样 Java8 服务里只需要配一个 base url 和一个 token,不用在代码里散落多家厂商的地址。
具体入口按用途分:
- 模型对话验证:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_java8
- 长期编码 / Agent 场景:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_java8
- 控制台:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_java8
- API Keys 管理:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_java8
- 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_java8
- Claude Code / Anthropic 相关:https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_java8
API 地址固定用https://taotoken.net/api,不要加 UTM 参数,避免拼进请求路径导致 404。拿到 Key 之后,把它写进config.toml或环境变量,Java8 代码里只读配置,不硬编码。
4. 可复制配置:pom、启动类与 config.toml 骨架
4.1 pom 依赖片段
Java8 工程里加 Solon AI MCP 相关依赖,注意版本号以你实际拉到的为准,这里给的是结构:
<properties> <java.version>1.8</java.version> <solon.version>2.9.0</solon.version> <solon.ai.version>1.0.0</solon.ai.version> </properties> <dependencies> <!-- Solon 核心 --> <dependency> <groupId>org.noear</groupId> <artifactId>solon.boot.smarthttp</artifactId> <version>${solon.version}</version> </dependency> <!-- Solon AI MCP Server --> <dependency> <groupId>org.noear</groupId> <artifactId>solon-ai-mcp-server</artifactId> <version>${solon.ai.version}</version> </dependency> <!-- Solon AI MCP Client,用于本地验证 --> <dependency> <groupId>org.noear</groupId> <artifactId>solon-ai-mcp-client</artifactId> <version>${solon.ai.version}</version> </dependency> </dependencies>如果你的老工程是 SpringBoot2,也可以只引 MCP 依赖,把 Solon 的启动方式嵌进去,不必整体迁移。
4.2 MCP Server 启动类
下面是一个最小可跑的启动类,注册两个端点:一个注解式,一个原生式,方便你对比:
import org.noear.solon.Solon; import org.noear.solon.ai.mcp.server.McpServerEndpointProvider; import org.noear.solon.ai.mcp.server.tool.MethodToolProvider; public class McpServerApp { public static void main(String[] args) { Solon.start(McpServerApp.class, args, app -> { // 原生方式注册第二个端点 McpServerEndpointProvider serverEndpoint = McpServerEndpointProvider.builder() .name("mcp-case2") .sseEndpoint("/case2/sse") .build(); serverEndpoint.addTool(new MethodToolProvider(new McpServerTool())); serverEndpoint.postStart(); }); } }注解式的McpServerTool保持第 2 节里的写法即可,Solon 启动时会自动扫描@McpServerEndpoint。
4.3 config.toml 骨架
Solon 默认读resources/config.toml,把 TaoToken 的 Key 和 API 地址放这里:
[server] port = 8080 [taotoken] api_url = "https://taotoken.net/api" api_key = "${TAOTOKEN_API_KEY}" [mcp] server_name = "java8-mcp-demo" sse_endpoint = "/case1/sse"${TAOTOKEN_API_KEY}从环境变量注入,别把 Key 提交进仓库。启动前先export TAOTOKEN_API_KEY=你的Key。
5. 验证请求:一次完整的工具调用
5.1 启动服务
export TAOTOKEN_API_KEY=sk-你的Key mvn clean package -DskipTests java -jar target/java8-mcp-demo.jar看到 Solon 启动日志里出现mcp-case1和mcp-case2两个端点,说明注册成功。
5.2 用 MCP Client 调用工具
写一个本地验证类,走 Solon AI MCP Client 调getWeather:
import org.noear.solon.ai.mcp.client.McpClientToolProvider; import java.util.HashMap; import java.util.Map; public class McpClientDemo { public static void main(String[] args) { McpClientToolProvider clientToolProvider = McpClientToolProvider.builder() .apiUrl("http://localhost:8080/case1/sse") .build(); Map<String, Object> params = new HashMap<>(); params.put("location", "杭州"); String rst = clientToolProvider.callToolAsText("getWeather", params); System.out.println("工具返回: " + rst); } }Java8 下这里用HashMap而不是Map.of,跑起来会输出:
工具返回: 晴,14度5.3 通过 TaoToken 通道做模型侧验证
工具能调通只说明 MCP Server 注册没问题,还要确认模型侧能通过统一通道访问。用 curl 打一次 TaoToken 的 API:
curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "messages": [{"role": "user", "content": "用一句话说明 MCP Server 的作用"}] }'返回里能看到正常的choices结构,就说明 Key 和 API 通道都通了。把这一步和上面的工具调用串起来,就是「Java8 MCP Server 注册成功 + 模型通道可用」的完整验证。
6. 本篇常见错排查
6.1Map.of编译报错
Java8 没有Map.of,报cannot find symbol。换成:
Map<String, Object> params = new HashMap<>(); params.put("location", "杭州");6.2 SSE 端点 404
检查sseEndpoint是否以/开头,以及server.port是否和客户端apiUrl里的端口一致。Solon 默认端口是 8080,如果你在config.toml里改了,客户端也要同步改。
6.3 依赖冲突导致启动失败
老工程里可能已经有旧版 Solon 或其它 HTTP 框架,用mvn dependency:tree看下solon-ai-mcp-server拉进来的 Solon 版本,和现有版本对齐。必要时用<exclusions>排掉重复依赖。
6.4 TaoToken 请求 401
先确认环境变量TAOTOKEN_API_KEY真的导出到了当前 shell,echo $TAOTOKEN_API_KEY能看到值。再确认 API 地址是https://taotoken.net/api,没有多拼路径或 UTM 参数。Key 失效的话去 API Keys 页面重新生成。
6.5 工具调用返回空
callToolAsText返回空,通常是工具方法名和@ToolMapping注册名不一致,或者参数名对不上。@ToolParam里的description是给模型看的,参数名要和params的 key 严格一致。
7. 下一步:把通道和端点固定下来
Java8 跑 MCP Server 这件事,卡点从来不是协议本身,而是依赖的 JDK 门槛。Solon AI MCP 把服务端和客户端都压到 JDK8 可用,等于给存量项目开了一条不用升级运行时的路。落地时建议把两件事固定成团队规范:一是 MCP 端点统一走sseEndpoint命名规则,二是模型通道统一走 TaoToken 的 Key 和 API 地址,配置只从环境变量读。
需要长期跑编码类 Agent 的话,可以从 Coding Plan 入口拿更合适的额度;只是验证模型连通性,用模型对话页面就够;接入细节和参数说明在接入文档里。把config.toml里的api_url和api_key换成你自己的,这套骨架就能直接进你的 Java8 工程。