1. 从一次真实踩坑说起:多工具接入为什么让人头大
如果你正在用 Spring Boot 写 AI 应用,大概率会遇到这样一个场景:项目里既要接高德地图查路线,又要接别的工具查天气、查数据库,每个工具背后都是一套独立的 Key、独立的通道、独立的配置。刚开始只有一两个还好,等到第五六个工具接进来,application.yml里全是各种api-key、base-url,改一个环境变量要翻半天,本地跑通了换台机器又挂。
Spring AI 的 MCP(Model Context Protocol)客户端启动器,本质上就是来解决「工具接入标准化」这件事的。它让你用统一的协议去连接不同的 MCP Server,把工具执行交给 Spring AI 的ChatClient框架。但 MCP 只解决了「怎么连」,没解决「Key 和通道怎么统一管」。这就是我这篇要讲的重点:用 TaoToken 做统一 Key 入口,配合 Spring AI MCP 客户端,把高德地图这类工具接进来,让配置收敛到一处。
这篇适合谁?有 Java 17 基础、写过 Spring Boot、想快速跑通 MCP 客户端链路的同学。我会给出application.yml和mcp-servers-config.json的骨架,演示连接高德地图 MCP 服务后的调用验证动作,最后把常见的报错一个个拆开讲。全程可跟做,不需要你提前理解 MCP 协议的全部细节。
2. TaoToken 前置:把分散的 Key 收成一个入口
在讲配置之前,先说清楚 TaoToken 在这里扮演什么角色。你可以把它理解成一个统一的模型与工具调用入口:原本你要为每个模型供应商、每个工具单独申请 Key、单独配base-url,现在通过 TaoToken 拿一个 Key,就能走同一个 API 地址去调用不同的模型和工具通道。
官网入口在这里:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
API 地址是:https://taotoken.net/api (注意这个不加 UTM 参数,配置里直接填这个)。
具体到 Spring AI 项目里,你需要做两件事:
第一,去控制台创建一个 API Key。地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,创建完复制出来,后面填到环境变量里。
第二,如果你要长期跑编码类或 Agent 类任务,可以看下 Coding Plan,地址是 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,它更适合高频调用的场景。
Key 的管理页面在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。建议先把文档扫一遍,里面把模型对话、工具调用的参数都列清楚了。
注意:TaoToken 是合规的 API 聚合入口,配置时只填官方给的地址,不要自己拼别的域名。
拿到 Key 之后,你的 Spring AI 项目里模型这一侧的配置就统一了:spring.ai.openai.api-key填 TaoToken 的 Key,spring.ai.openai.base-url填https://taotoken.net/api。工具那一侧(比如高德地图 MCP Server)的 Key 仍然由高德自己管,但模型通道已经收敛了。
3. 可复制配置:application.yml 与 mcp-servers-config.json 骨架
先看依赖。pom.xml里至少要有这两个:
<dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-client</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-openai</artifactId> </dependency> </dependencies>然后是application.yml。这里我把模型通道指向 TaoToken,工具通道用 STDIO 方式连高德地图 MCP Server:
spring: application: name: mcp-amap-demo main: web-application-type: none ai: openai: api-key: ${TAOTOKEN_API_KEY} base-url: https://taotoken.net/api chat: options: model: gpt-4o-mini mcp: client: stdio: connections: amap-maps-mcp-server: command: npx args: - "-y" - "@amap/amap-maps-mcp-server" env: AMAP_MAPS_API_KEY: ${AMAP_MAPS_API_KEY}几个关键点解释一下。web-application-type: none是因为这是个命令行应用,不需要起 Web 容器。stdio.connections下面每一个命名节点就是一个 MCP Server 连接,amap-maps-mcp-server是连接名,你可以随便起,但后面引用要对得上。command和args是启动这个 MCP Server 的方式,高德官方提供的是 npm 包,所以用npx -y直接拉起来。
如果你不想把连接写在application.yml里,也可以用 Claude Desktop 格式的外部 JSON。在application.yml里加一行:
spring: ai: mcp: client: stdio: servers-configuration: classpath:/mcp-servers-config.json然后src/main/resources/mcp-servers-config.json内容如下:
{ "mcpServers": { "amap-maps": { "command": "npx", "args": ["-y", "@amap/amap-maps-mcp-server"], "env": { "AMAP_MAPS_API_KEY": "${AMAP_MAPS_API_KEY}" } } } }两种方式选一种就行,别同时配,否则可能出现连接重复。我个人更推荐 JSON 方式,因为换工具的时候只改一个文件,application.yml保持干净。
环境变量在启动前导出:
export TAOTOKEN_API_KEY=你的TaoToken密钥 export AMAP_MAPS_API_KEY=你的高德地图密钥高德地图的 Key 去高德开放平台的 MCP Server 创建页申请,这里不展开。
4. 验证请求:跑通一次真实调用
配置写完,先构建:
./mvnw clean install然后运行,通过-Dai.user.input传入问题:
java -Dai.user.input='从北京西站到首都机场怎么走?' \ -jar target/mcp-amap-demo-0.0.1-SNAPSHOT.jar应用启动后,Spring AI 会做这几件事:读取stdio.connections配置,为每个连接创建一个 MCP 客户端;通过npx拉起高德地图 MCP Server 进程;把 MCP Server 暴露的工具注册到ChatClient的工具执行框架里;把你的问题发给模型,模型决定调用哪个工具,Spring AI 负责执行并把结果回传。
如果一切正常,你会在控制台看到类似这样的输出:
User: 从北京西站到首都机场怎么走? Assistant: 根据高德地图的路线规划,从北京西站到首都机场……这里有个细节值得注意:模型本身并不知道怎么调高德地图,它只是看到了一组工具描述(tool schema),然后决定「这个问题应该用 maps_direction_driving 这个工具」。真正执行调用的是 Spring AI 的 MCP 客户端,它把参数传给高德 MCP Server,拿到结果再交回模型组织语言。这就是 MCP 的价值——工具和模型解耦。
如果你想单独验证模型通道是否通,可以用模型对话页面直接测:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite ,发一句话看有没有正常返回。这一步能帮你快速区分是模型通道的问题还是 MCP 工具的问题。
5. 本篇常见错排查
5.1 npx 找不到或 MCP Server 起不来
报错长这样:
Cannot run program "npx": error=2, No such file or directory这是环境里没装 Node.js,或者npx不在 PATH 里。先确认:
node -v npx -v两个都有版本号才行。Windows 上如果用的是npx.cmd,在application.yml里要把command改成npx.cmd,JSON 配置同理。我试过在 Mac 上直接写npx没问题,换到 Windows 就得多加.cmd后缀。
5.2 高德 Key 没生效,工具调用返回鉴权失败
现象是模型能回复,但一涉及地图就报错,类似INVALID_USER_KEY。检查两点:一是AMAP_MAPS_API_KEY环境变量有没有真的导出,可以用echo $AMAP_MAPS_API_KEY确认;二是高德开放平台里这个 Key 有没有绑定 MCP Server 服务,Key 是按服务类型授权的,不是随便一个 Key 都能调 MCP。
5.3 模型通道 401 或 base-url 配错
如果你看到401 Unauthorized,先确认TAOTOKEN_API_KEY有没有填对,以及base-url是不是https://taotoken.net/api。注意不要写成带/v1的路径,Spring AI 的 OpenAI starter 会自己拼。如果还是不通,去 API Keys 页面重新生成一个 Key 试试:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。
5.4 工具注册了但模型不调用
有时候配置都对,模型就是不调工具,直接凭记忆回答。这通常是因为模型能力不够,或者工具描述没被正确加载。换一个支持 function calling 的模型,比如gpt-4o-mini或更强的。另外确认spring-ai-starter-mcp-client的版本和 Spring AI 主版本一致,版本错配会导致工具注册静默失败。
5.5 应用启动后立刻退出
因为web-application-type: none且没有常驻线程,命令行应用执行完就退。这是预期行为。如果你想让它交互式循环,需要自己写一个CommandLineRunner读取输入。参考文档里有完整示例:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。
6. 把链路固定下来:后续怎么扩展
跑通之后,你会发现这套结构的扩展性很好。想加一个新工具,比如天气 MCP Server,只需要在mcp-servers-config.json里加一个节点:
{ "mcpServers": { "amap-maps": { "command": "npx", "args": ["-y", "@amap/amap-maps-mcp-server"], "env": { "AMAP_MAPS_API_KEY": "${AMAP_MAPS_API_KEY}" } }, "weather": { "command": "npx", "args": ["-y", "some-weather-mcp-server"], "env": { "WEATHER_API_KEY": "${WEATHER_API_KEY}" } } } }模型通道那边完全不用动,TaoToken 的 Key 和 base-url 保持一份。这就是统一 Key 入口的好处:工具在变,模型通道不变。
如果你后面要做更复杂的 Agent 编排,或者需要长时间跑编码任务,可以了解下 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。它针对高频调用场景做了优化,比按量计费更适合持续跑的任务。
最后留一个我自己的习惯:每次改完 MCP 配置,先用一个最简单的问题验证,比如「北京今天天气怎么样」,确认工具链路通了,再去调复杂的业务逻辑。这样出问题的时候,你能快速定位是配置层还是业务层。