news 2026/9/30 20:48:42

Java中使用Spring Boot+Ollama实现本地AI的MCP接入:TaoToken统一Key配置实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java中使用Spring Boot+Ollama实现本地AI的MCP接入:TaoToken统一Key配置实战

1. Spring Boot + Ollama 本地 AI 接入 MCP 的真实痛点

如果你正在用 Java 写一个本地 AI 应用,大概率会遇到这样一个场景:Ollama 跑着 qwen3:4b,Spring Boot 里用spring-ai-ollama-spring-boot-starter把聊天接口调通了,但一旦想让模型去操作本地文件、查数据库、调外部工具,就得引入 MCP(Model Context Protocol)。MCP 是什么?简单说,它是让大模型和外部工具之间说同一种语言的协议,模型不再只是聊天,而是能真正“动手”去读写文件、执行命令。适合谁?适合已经跑通 Ollama 本地对话、想进一步让 AI 具备工具调用能力的 Java 开发者。

问题也随之而来。MCP 客户端要连 MCP 服务端,服务端可能是 filesystem、可能是数据库、可能是各种第三方工具,每个工具都有自己的启动命令、参数、路径。更麻烦的是,如果你同时用多个模型供应商或者多个工具链,Key 和 Base URL 会散落在application.yml、mcp-servers.json、环境变量、甚至 IDE 的配置里。改一个地方,忘一个地方,排查半天发现是某个 Key 没同步。

我试过把 Ollama 的地址、MCP 的 stdio 配置、模型参数全塞进一个 yml,结果流式接口一调就卡住,工具调用被跳过,日志里只有reading choices之类的报错。后来把 Key 管理统一到 TaoToken,用一套 Key 覆盖模型对话和工具链调用,配置才稳定下来。这篇就按“一次配置跑通本地 AI 调用链”的目标,把 Spring Boot + Ollama + MCP 的骨架、TaoToken 统一 Key 的接入步骤、以及连通性验证动作完整写一遍。

核心检索词先明确:Spring Boot 集成 Ollama MCP 本地 AI 工具调用,重点解决多工具 Key 分散和流式工具调用时序问题。下面从依赖开始,一步步来。

2. TaoToken 统一 Key 前置配置与 MCP 依赖骨架

在动手改代码之前,先把 Key 管理这件事理清楚。本地 AI 调用链里通常有两类请求:一类是模型推理请求(Ollama 本地或远程模型),一类是工具链请求(MCP 服务端可能调用的外部 API)。如果每个都单独配 Key,application.yml会越来越臃肿,而且团队协作时容易泄露。

TaoToken 的做法是提供一个统一的 API 入口,模型对话和工具调用都走同一个 Base URL 和同一套 Key。官网地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api 。你需要在控制台创建一个 API Key,然后把它写进 Spring Boot 的配置里。注意,Ollama 本地模型本身不需要 Key,但 MCP 工具链如果涉及远程服务,统一走 TaoToken 的 Key 可以避免多套凭证。

先看 Maven 依赖。Spring AI 的版本要用 milestone,因为 MCP 相关 starter 还在迭代。下面是我实测能加载的pom.xml骨架:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>Qwen3</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.4.1</version> </parent> <properties> <java.version>17</java.version> <spring-ai.version>1.0.0-M6</spring-ai.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-ollama-spring-boot-starter</artifactId> <version>${spring-ai.version}</version> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId> <version>${spring-ai.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <repositories> <repository> <id>spring-milestones</id> <url>https://repo.spring.io/milestone</url> </repository> </repositories> </project>

这里有个坑:spring-ai-mcp-client-spring-boot-starter在 M6 版本里依赖了io.modelcontextprotocol的包,如果你本地 Maven 仓库没有缓存,第一次拉取会比较慢。另外,spring-ai-ollama-spring-boot-starter和 MCP starter 的版本必须一致,否则会出现NoSuchMethodError。

接下来是application.yml的骨架。注意 Ollama 的 base-url 指向本地 11434,MCP 的 stdio 配置指向一个 JSON 文件。TaoToken 的 Key 先放在环境变量里,后面在配置类里读取。

server: port: 8181 spring: ai: ollama: base-url: http://localhost:11434 chat: enabled: true model: qwen3:4b options: temperature: 0.7 top_p: 0.9 num_predict: 5120 embedding: enabled: true model: qwen3:4b options: num-batch: 5120 mcp: client: enabled: true name: mcp-client version: 1.0 type: SYNC request-timeout: 30s stdio: servers-configuration: classpath:/mcp-server-settings.json logging: level: org.springframework.ai.mcp.tool: DEBUG

注意servers-configuration指向的是 classpath 下的mcp-server-settings.json,这个文件要放在src/main/resources目录。如果你放在其他路径,启动时会报FileNotFoundException。另外,type: SYNC表示同步客户端,流式场景下需要额外处理,后面会讲。

TaoToken 的 Key 不要直接写死在 yml 里,建议用环境变量TAOTOKEN_API_KEY,然后在 Java 配置类里通过@Value注入。这样本地开发和 CI 环境可以复用同一套配置。

3. 可复制的 application.yml 与 mcp-servers.json 配置

这一节把配置写全,确保你复制过去就能跑。先看mcp-servers.json,这个文件定义 MCP 服务端的启动方式。以 filesystem 为例,它允许 AI 访问指定目录。

{ "mcpServers": { "filesystem": { "command": "F:\\Environment\\nodejs\\npx.cmd", "args": [ "-y", "@modelcontextprotocol/server-filesystem", "C:\\Users\\lenovo\\Desktop\\temp" ] } } }

几个关键点:command在 Windows 下是npx.cmd,Linux/macOS 下是npx。args里的路径要改成你自己的桌面路径,且必须存在,否则 MCP 服务端启动会失败。@modelcontextprotocol/server-filesystem是官方提供的文件系统服务端,通过 npx 临时下载运行,所以需要 Node.js 和 npm 已安装。你可以先在命令行执行npx -y @modelcontextprotocol/server-filesystem C:\Users\lenovo\Desktop\temp测试一下,如果能进入等待输入的状态,说明环境没问题。

接下来是完整的application.yml,把 TaoToken 的 Base URL 和 Key 也纳入进来。注意,Ollama 本地模型不走 TaoToken,但 MCP 工具链如果调远程 API,统一用 TaoToken 的 Key。

server: port: 8181 taotoken: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY:sk-xxxx} spring: ai: ollama: base-url: http://localhost:11434 chat: enabled: true model: qwen3:4b options: temperature: 0.7 top_p: 0.9 num_predict: 5120 embedding: enabled: true model: qwen3:4b options: num-batch: 5120 mcp: client: enabled: true name: mcp-client version: 1.0 type: SYNC request-timeout: 30s stdio: servers-configuration: classpath:/mcp-server-settings.json logging: level: org.springframework.ai.mcp.tool: DEBUG io.modelcontextprotocol: DEBUG

这里taotoken.api-key用了占位符,实际运行时通过环境变量注入。如果你在 IDE 里跑,可以在 Run Configuration 里加TAOTOKEN_API_KEY=你的Key。注意不要把这个 Key 提交到 Git。

然后是配置类,把 MCP 的同步客户端注入到 Spring 容器,并生成工具回调提供者。

package com.example.qwen3.config; import io.modelcontextprotocol.client.McpSyncClient; import org.springframework.ai.mcp.SyncMcpToolCallbackProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.List; @Configuration public class McpConfig { @Bean public SyncMcpToolCallbackProvider syncMcpToolCallbackProvider( List<McpSyncClient> mcpSyncClients) { return new SyncMcpToolCallbackProvider(mcpSyncClients); } }

这个 Bean 的作用是把所有 MCP 客户端注册的工具收集起来,供 ChatClient 调用。如果你有多个 MCP 服务端,比如 filesystem 和 database,它们都会被注入到这个 List 里。

最后是 Controller,提供三个接口:非流式、先执行再流式、纯流式。先看非流式版本,这是最稳定的。

package com.example.qwen3.controller; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.mcp.SyncMcpToolCallbackProvider; import org.springframework.ai.ollama.OllamaChatModel; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/mcp") @RestController public class McpController { private final OllamaChatModel chatModel; private final SyncMcpToolCallbackProvider toolCallbackProvider; public McpController(OllamaChatModel chatModel, SyncMcpToolCallbackProvider toolCallbackProvider) { this.chatModel = chatModel; this.toolCallbackProvider = toolCallbackProvider; } @GetMapping("/mcpChat") public String generate(@RequestParam("prompt") String prompt) { ChatClient chatClient = ChatClient.builder(chatModel) .defaultTools(toolCallbackProvider.getToolCallbacks()) .build(); return chatClient.prompt().user(prompt).call().content(); } }

启动应用后,访问http://localhost:8181/mcp/mcpChat?prompt=在temp目录下创建文件e1.txt,内容为我是mcpChat创建的文件,如果配置正确,模型会调用 filesystem 工具创建文件。注意,qwen3:4b 在本地跑工具调用会比较慢,实测创建一个小文件大概需要几分钟,这是模型推理速度决定的,不是配置问题。

4. 验证 MCP 连通性与流式接口的成功结果

配置写完,怎么确认 MCP 真的连上了?先看启动日志。应用启动时,spring-ai-mcp-client会尝试启动mcp-servers.json里定义的服务端。如果成功,日志里会出现类似MCP client initialized和Registered tools: [read_file, write_file, list_directory]的信息。如果失败,通常会报Failed to start MCP server或npx.cmd not found。

你可以先用一个简单的测试接口确认 Spring Boot 本身没问题:

@GetMapping("/test") public String test(@RequestParam("prompt") String prompt) { return "input=" + prompt; }

访问http://localhost:8181/mcp/test?prompt=hello,返回input=hello说明 Web 层正常。然后调非流式接口,观察日志里是否有工具调用的记录。如果日志里出现Tool call: write_file,说明 MCP 工具被正确触发。

流式接口是坑最多的地方。先看“先执行再流式”的版本:

@GetMapping(value = "/mcpChatStream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<ChatResponse> generateStream(@RequestParam("prompt") String prompt) { ChatClient chatClient = ChatClient.builder(chatModel) .defaultTools(toolCallbackProvider.getToolCallbacks()) .build(); String result = chatClient.prompt().user(prompt).call().content(); return chatClient.prompt() .user(prompt + "\n\n基于上述操作,请总结执行结果:") .stream() .chatResponse(); }

这个版本的逻辑是:先执行一次完整的非流式调用,确保工具被执行,然后再发起一次流式调用让模型总结结果。实测下来,这样能避免工具调用被跳过的问题。访问http://localhost:8181/mcp/mcpChatStream?prompt=在temp目录下创建文件e2.txt,内容为我是mcpChat创建的文件,你会先等待一段时间(工具执行),然后看到流式输出的总结。

还有一个简化版本,直接把非流式结果分块返回:

@GetMapping(value = "/mcpChatStreamFixed", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> mcpChatStreamFixed(@RequestParam("prompt") String prompt) { ChatClient chatClient = ChatClient.builder(chatModel) .defaultTools(toolCallbackProvider.getToolCallbacks()) .build(); return Mono.fromCallable(() -> chatClient.prompt().user(prompt).call().content()) .flatMapMany(result -> Flux.fromArray(result.split("")) .delayElements(Duration.ofMillis(50))) .subscribeOn(Schedulers.boundedElastic()); }

这个版本没有真正的流式推理,只是把结果逐字返回,适合前端需要打字机效果的场景。注意delayElements的延迟不要设太小,否则前端可能来不及渲染。

验证成功的标志:temp目录下出现了e1.txt和e2.txt,内容与 prompt 一致。同时日志里能看到write_file工具被调用。如果文件没出现,先检查mcp-servers.json里的路径是否有写权限,再检查 npx 是否能正常启动。

关于 TaoToken 的 Key,如果你在 MCP 工具链里调用了远程 API,可以在配置类里读取taotoken.api-key,然后传给对应的工具。比如自定义一个 MCP 服务端,在启动参数里带上 Key。这样模型对话走 Ollama 本地,工具链走 TaoToken 统一入口,Key 只需要维护一份。

5. 本篇常见错误排查:401、local proxy failed、reading choices

配置过程中最容易遇到的几个报错,这里逐个拆解。

401 Unauthorized:如果你在 MCP 工具链里调用了需要鉴权的远程服务,但 Key 没传对,就会返回 401。检查application.yml里的taotoken.api-key是否通过环境变量正确注入。可以在配置类里加一行日志打印 Key 的前几位,确认不是空值。另外,TaoToken 的 Base URL 是https://taotoken.net/api,不要漏掉/api路径。

local proxy failed:这个报错通常出现在 MCP 客户端尝试连接服务端时。原因可能是mcp-servers.json里的command路径不对,或者 npx 没有安装。先在命令行执行npx -y @modelcontextprotocol/server-filesystem C:\Users\lenovo\Desktop\temp,如果报command not found,说明 Node.js 环境变量没配好。Windows 下要用npx.cmd的完整路径,比如F:\Environment\nodejs\npx.cmd。

reading choices 报错:这个通常出现在流式接口里,日志显示Error reading choices或No choices found。原因是流式响应中工具调用的时机和非流式不同,模型可能在工具还没执行完就返回了空结果。解决办法就是前面提到的“先执行再流式”,或者直接用非流式接口。如果你坚持用纯流式,需要在ChatClient里显式设置defaultOptions,并确保request-timeout足够长。

OAuth 相关报错:如果你接入的 MCP 服务端需要 OAuth 鉴权,但本地没配置 token,会报OAuth token missing。这种情况下,要么在mcp-servers.json的env字段里传入 token,要么改用不需要鉴权的本地工具。TaoToken 的 Key 可以作为统一凭证传给需要鉴权的服务端,但具体要看服务端的鉴权方式。

工具调用被跳过:日志里没有Tool call记录,模型直接返回了文本。检查ChatClient.builder里是否调用了.defaultTools(toolCallbackProvider.getToolCallbacks())。另外,SyncMcpToolCallbackProvider注入的 List 是否为空,如果为空,说明 MCP 客户端没启动成功。可以在启动时打印mcpSyncClients.size()。

模型速度太慢:qwen3:4b 在普通笔记本上跑工具调用确实慢,实测创建一个小文件要几分钟。如果追求实用性,可以考虑换更小的模型,或者把工具调用逻辑放到非流式接口里,避免流式带来的额外开销。

排查顺序建议:先确认 Ollama 本地对话能通,再确认 MCP 服务端能独立启动,最后确认 Spring Boot 里工具回调被注册。每一步都用日志验证,不要跳步。

6. 一次配置跑通本地 AI 调用链的 CTA

把上面的配置串起来,你的本地 AI 调用链应该是这样的:Spring Boot 启动时加载application.yml,Ollama 连接本地 11434,MCP 客户端读取mcp-servers.json启动 filesystem 服务端,McpConfig把工具回调注入容器,Controller 里的 ChatClient 带上工具回调发起请求。模型对话走本地 Ollama,工具链如果需要远程鉴权,统一走 TaoToken 的 Key。

如果你还没创建 TaoToken 的 API Key,可以去控制台生成一个,然后把它写进环境变量。接入文档里有详细的 Base URL 和鉴权说明,照着配就行。验证模型是否连通,可以用模型对话页面发一条测试消息,确认 Key 有效。长期做编码或 Agent 场景的话,Coding Plan 更适合,Key 和额度管理会更省心。

最后提醒一句:mcp-servers.json里的路径一定要改成你自己的,npx.cmd的路径也要确认存在。流式接口如果卡住,先用非流式跑通,再逐步加流式逻辑。本地模型速度慢是正常的,别在流式上死磕,先把工具调用跑通再说。

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

如何结合 RAG 和 Fine-tuning 来提升提示词效果?

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:AI大模型原理和应用面试题 文章目录 一、🍀回答重点 二、🍀扩展知识 一、🍀回答重点 RAG(检索增强生成)和 Fine-tuning(微调)是提升提示词效果…

作者头像 李华
网站建设 2026/9/30 20:44:30

离线芯片焊接自动化改造:3个节奏点决定回本周期

跟几位做功率器件和传感器封装的朋友聊天&#xff0c;发现一个共性&#xff1a;离线芯片焊接设备买了好几年&#xff0c;上下料还是靠人守。一人看两台机&#xff0c;夜班人手一紧&#xff0c;稼动率就往下掉&#xff1b;基板划伤、物料混批的索赔单也隔三差五冒出来。自动化改…

作者头像 李华
网站建设 2026/9/30 20:34:20

OpenAI Codex 深度集成 IDE:用 TaoToken 统一 Key 重塑 AI 辅助编程体验

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

作者头像 李华
网站建设 2026/9/30 20:28:38

基于 MCP 的配置管理实战:把 Cline MCP settings 改到 TaoToken

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

作者头像 李华
网站建设 2026/9/30 20:21:44

职臣AI科研绘图:从选图到下载四步法

论文里的图表&#xff0c;不是把数据“做得好看”就够了。它还需要承担展示趋势、比较差异、解释关系和支撑结论等任务。面对不同研究内容&#xff0c;很多人最先卡住的不是配色&#xff0c;而是“不知道该选什么图”。从职臣AI科研绘图工作台的界面来看&#xff0c;整个操作可…

作者头像 李华