1. 从一条微信通知说起Spring Boot 里把公众号消息封装成 MCP 服务你有没有遇到过这种场景CI 跑完了、PR 合并了、定时任务失败了想让系统自动往微信推一条消息但每次都要在业务代码里手写一遍 access_token 获取、模板拼装、HTTP 调用重复且难维护。更麻烦的是现在很多团队开始用 IDE 里的 AI 智能体或 Agent 来驱动工作流希望模型能直接“调用一个工具”把通知发出去而不是让模型去拼 HTTP 请求。这就是把微信发消息通知封装成 MCP 服务的价值所在。MCPModel Context Protocol是一套让模型和外部工具对话的协议你可以把它理解成“给 AI 用的 USB 接口”只要把能力注册成 MCP 工具任何支持 MCP 的客户端IDE、Agent、命令行助手都能按统一方式调用。而 Spring AI 提供了spring-ai-mcp-server-spring-boot-starter让我们用 Spring Boot 的写法就能把普通 Java 方法暴露成 MCP 工具。这篇内容面向三类人需要在业务里实现微信公众号模板消息通知的后端开发想把传统 HTTP 能力封装成 AI 工具、在 IDE/Agent 里一键调用的工程师以及关注分层架构、希望通知能力可复用的团队。核心检索词就是“微信 MCP 服务”和“Spring AI 工具注册”我会从零带你跑通建工程、写工具函数、配 TaoToken 统一 Key、发一条真实模板消息、再排查几个高频报错。实测下来整条链路最容易被卡住的不是微信 API 本身而是 MCP 客户端的鉴权配置和工具注册的注解细节。所以下面每一步我都会给出可复制的配置和代码你照着改参数就能用。先明确整体结构Spring Boot 应用作为 MCP Server内部用领域服务暴露Tool方法基础设施层负责调微信接口MCP 客户端通过 TaoToken 的统一 API 通道完成鉴权再调用这个工具。这样业务语义、外部集成、AI 调用三层是分开的后面扩展企业微信、钉钉、邮件都只是换适配器。2. TaoToken 前置统一 Key 与 MCP 客户端鉴权通道在动手写代码前先把“鉴权通道”这件事理清楚。很多同学第一次做 MCP 接入会以为 MCP Server 自己就能被任意客户端调用结果在 IDE 里配完发现请求 401或者报 local proxy failed。原因通常是MCP 客户端调用模型或工具时需要一个统一的 API 入口和 Key而每个模型厂商的 Key 格式、Base URL 都不一样配置散落各处很容易出错。TaoToken 在这里扮演的角色就是“统一 Key / API 通道”。你可以在官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后拿到一把 Key然后在 MCP 客户端里把 Base URL 指向 https://taotoken.net/api注意 API 地址不带 UTM 参数这样无论是模型对话还是工具调用都走同一条鉴权链路。对 Spring AI 项目来说好处是你不用在代码里硬编码多家厂商的 Key客户端侧只维护一份配置。具体要准备三样东西我把它叫“三件套”后面配置里会反复出现Base URLhttps://taotoken.net/apiAPI Key在控制台创建的 Key形如sk-...Model ID你实际要调用的模型标识比如claude-3-5-sonnet之类按你控制台可用的填如果你用的是 Claude Code 这类客户端配置入口在~/.claude/settings.json或项目级 settings如果用 Cline则在 MCP 配置里填 command/args/env如果用 Codex则对应auth.json。不管哪种核心都是把 Base URL 和 Key 填对。下面给一个通用的 MCP 客户端配置片段你可以按自己客户端的要求适配{ mcpServers: { mcp-server-weixin: { command: java, args: [ -Dspring.ai.mcp.server.stdiotrue, -jar, /opt/app/mcp-server-weixin-1.0.0.jar, --weixin.api.app-idwx_your_appid, --weixin.api.app-secretyour_secret, --weixin.api.template_idyour_template_id, --weixin.api.touseryour_openid ], env: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_API_KEY: sk-your-key } } } }注意上面env里的 Base URL 和 Key 是给 MCP 客户端调用模型/工具时用的和微信的 app-id/app-secret 是两套东西别混在一起。微信那套是 MCP Server 内部去调微信 API 用的。这里有个容易踩的坑有人把 TaoToken 的 Key 填到了微信配置里或者反过来结果一边 401 一边拿不到 access_token。记住分层——客户端鉴权走 TaoToken业务侧调微信走公众号自己的凭证。另外Key 不要写死在代码或提交到仓库用环境变量或启动参数注入。如果你还没有 Key可以去控制台创建入口在 https://taotoken.net/console 创建后复制保存页面只显示一次。3. 可复制配置Spring AI MCP Server 工程与工具注册这一节是重头戏给你能直接抄的配置和代码。先建一个 Spring Boot 工程pom.xml里引入 MCP Server 依赖和 Retrofit用来调微信 HTTP 接口dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-server-spring-boot-starter/artifactId /dependency dependency groupIdcom.squareup.retrofit2/groupId artifactIdretrofit/artifactId version2.11.0/version /dependency dependency groupIdcom.squareup.retrofit2/groupId artifactIdconverter-gson/artifactId version2.11.0/version /dependency dependency groupIdcom.google.guava/groupId artifactIdguava/artifactId version33.2.1-jre/version /dependency然后是application.yml把微信相关参数和 MCP Server 模式配好。注意敏感值用占位符运行时用启动参数覆盖spring: ai: mcp: server: name: mcp-server-weixin version: 1.0.0 stdio: true weixin: api: app-id: ${WEIXIN_APP_ID:wx_placeholder} app-secret: ${WEIXIN_APP_SECRET:secret_placeholder} template-id: ${WEIXIN_TEMPLATE_ID:tpl_placeholder} touser: ${WEIXIN_TOUSER:openid_placeholder}接下来是核心领域服务里用Tool注册工具函数。Spring AI 会扫描带Tool的方法并暴露给 MCP 客户端。方法签名建议用请求/响应对象语义清晰Service public class WeiXinNoticeService { private final IWeiXiPort weiXiPort; public WeiXinNoticeService(IWeiXiPort weiXiPort) { this.weiXiPort weiXiPort; } Tool(description 微信公众号消息通知传入平台、主题、简述和跳转地址) public WeiXinNoticeFunctionResponse weixinNotice(WeiXinNoticeFunctionRequest request) throws IOException { return weiXiPort.weixinNotice(request); } }请求对象定义四个字段和微信模板的键对应public class WeiXinNoticeFunctionRequest { private String platform; private String subject; private String description; private String jumpUrl; // getter/setter 省略 }基础设施层实现端口负责拿 access_token 和发模板消息。access_token 用 Guava Cache 按 appId 缓存避免每次都请求微信Component public class WeiXiPort implements IWeiXiPort { private final CacheString, String tokenCache CacheBuilder.newBuilder() .expireAfterWrite(7000, TimeUnit.SECONDS) .build(); private final IWeixinApiService weixinApiService; private final WeiXinApiProperties properties; public WeiXiPort(IWeixinApiService weixinApiService, WeiXinApiProperties properties) { this.weixinApiService weixinApiService; this.properties properties; } Override public WeiXinNoticeFunctionResponse weixinNotice(WeiXinNoticeFunctionRequest request) throws IOException { String accessToken tokenCache.getIfPresent(properties.getAppId()); if (accessToken null) { WeixinTokenResponseDTO tokenResp weixinApiService .getToken(client_credential, properties.getAppId(), properties.getAppSecret()) .execute().body(); if (tokenResp null || tokenResp.getAccess_token() null) { throw new IOException(获取微信 access_token 失败); } accessToken tokenResp.getAccess_token(); tokenCache.put(properties.getAppId(), accessToken); } MapString, MapString, String data new HashMap(); put(data, platform_name, request.getPlatform()); put(data, article_name, request.getSubject()); put(data, date_name, request.getDescription()); WeixinTemplateMessageDTO message new WeixinTemplateMessageDTO(); message.setTouser(properties.getTouser()); message.setTemplate_id(properties.getTemplateId()); message.setUrl(request.getJumpUrl()); message.setData(data); weixinApiService.sendMessage(accessToken, message).execute(); WeiXinNoticeFunctionResponse response new WeiXinNoticeFunctionResponse(); response.setSuccess(true); return response; } private void put(MapString, MapString, String data, String key, String value) { MapString, String item new HashMap(); item.put(value, value null ? : value); data.put(key, item); } }Retrofit 接口定义两个方法一个拿 token一个发消息public interface IWeixinApiService { GET(cgi-bin/token) CallWeixinTokenResponseDTO getToken( Query(grant_type) String grantType, Query(appid) String appId, Query(secret) String appSecret); POST(cgi-bin/message/template/send) CallVoid sendMessage( Query(access_token) String accessToken, Body WeixinTemplateMessageDTO message); }Retrofit 的 Base URL 指向https://api.weixin.qq.com/用 Gson 转换器。到这里MCP Server 侧就齐了Tool方法暴露工具端口实现调微信配置从环境变量注入。打包命令mvn clean package -DskipTests生成的 jar 就是 MCP 客户端要启动的服务。记住三件套在客户端侧是 Base URL Key Model ID在服务端侧是 app-id app-secret template-id两边别搞混。4. 验证请求一次端到端发消息与成功结果配置写完必须验证。分两步先单独验证微信通知链路再验证 MCP 客户端能调到这个工具。第一步写个单元测试直接调领域服务确认微信能收到消息。测试前你需要一个测试公众号去微信公众平台申请测试号拿到 appID 和 appsecret关注后拿到自己的 openid 作为 touser再新增一个模板模板键用platform_name、article_name、date_nameSpringBootTest class WeiXinNoticeServiceTest { Autowired private WeiXinNoticeService weiXinNoticeService; Test void test_weixinNotice() throws IOException { WeiXinNoticeFunctionRequest request new WeiXinNoticeFunctionRequest(); request.setPlatform(CSDN); request.setSubject(Spring AI MCP 接入实践); request.setDescription(通知链路已跑通); request.setJumpUrl(https://taotoken.net/api); WeiXinNoticeFunctionResponse response weiXinNoticeService.weixinNotice(request); System.out.println(发送结果: response.isSuccess()); } }运行后如果微信里收到模板消息说明服务端链路通了。控制台会打印类似发送结果: true第二步验证 MCP 客户端。把打包好的 jar 配到客户端参考第 2 节的 JSON重启客户端让它加载 MCP Server。然后在对话里让模型调用工具比如输入“帮我用 weixinNotice 工具发一条通知平台 CSDN主题测试简述跑通跳转地址 https://taotoken.net/api”。模型会发起工具调用MCP Server 收到后执行微信收到消息。如果客户端支持查看工具列表你应该能看到weixinNotice这个工具及其描述。调用成功后客户端侧会返回类似{ success: true }这一步的关键是确认“模型 → MCP 工具 → 微信”整条链路都通。我试过在 IDE 里直接让 Agent 调用第一次因为客户端 env 里 Key 没填对报了 401换成控制台新建的 Key 后正常。所以验证时先看客户端日志再看服务端日志定位是哪一段断了。服务端建议开 debug 日志把 access_token 获取和消息发送的请求响应打出来方便排查。5. 本篇常见错排查401、local proxy failed 与工具不出现这一节把高频报错列出来对照着查能省很多时间。报错一401 Unauthorized。出现在 MCP 客户端调用模型或工具时。原因通常是 TaoToken 的 Key 没填、填错或者 Base URL 写成了带路径的地址。检查客户端配置里的TAOTOKEN_API_KEY是否是控制台新建的完整 KeyTAOTOKEN_BASE_URL是否为https://taotoken.net/api。注意 API 地址不要加 UTM 参数加了可能导致路由异常。如果用的是 Claude Code检查settings.json里的 env 段Cline 检查 MCP 配置的 envCodex 检查auth.json的字段名。报错二local proxy failed。一般是客户端本地代理配置和实际网络环境不匹配或者 MCP Server 进程没起来。先确认 jar 路径正确、Java 版本兼容建议 17再手动执行java -jar看能否启动。如果启动报端口或 stdio 相关错误检查spring.ai.mcp.server.stdiotrue是否配置。这个报错和 TaoToken 无关是本地进程通信问题。报错三reading choices 相关解析错误。多出现在模型返回格式和客户端预期不一致时常见于 Model ID 填错或模型不支持工具调用。确认你填的 Model ID 在控制台可用且支持 function calling / tool use。换一个明确支持工具调用的模型再试。报错四工具列表里看不到 weixinNotice。检查三点Tool注解的方法所在类是否被 Spring 扫描到加ServiceMCP Server 依赖版本是否和 Spring AI 匹配方法参数和返回值是否可序列化。另外Tool的 description 要写清楚有些客户端会据此过滤。报错五微信侧 40001 / invalid credential。这是 access_token 问题检查 app-id 和 app-secret 是否配对缓存是否过期。测试号和生产号的凭证不通用别混用。排查顺序建议先看客户端日志确认请求发出再看服务端日志确认工具被调用最后看微信返回码。三段日志对齐问题基本一目了然。6. 语义一致 CTA把通知能力接进你的 AI 工作流到这里你已经有了一个能跑的微信 MCP 服务Spring Boot 工程暴露Tool基础设施层调微信模板消息MCP 客户端通过 TaoToken 统一 Key 完成鉴权。接下来可以按同样模式扩展企业微信、钉钉、邮件把“通知中台”沉淀下来。如果你在接入过程中卡在鉴权或工具注册建议先看接入文档里面有各客户端的配置示例需要创建或管理 Key去 API Keys 页面想先验证模型和工具调用是否正常可以用模型对话快速试一条如果是长期做编码和 Agent 工作流Coding Plan 会更合适。把通知链路跑通后你会发现让 AI 帮你发消息、发告警、发日报其实就差一个 MCP 工具的距离。