ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于LangChain构造Agent接入由Java搭建的 MCP Server(Streamable HTTP):TaoToken 统一 Key 配置与联调验证

基于LangChain构造Agent接入由Java搭建的 MCP Server(Streamable HTTP):TaoToken 统一 Key 配置与联调验证 1. 为什么 Java 团队总在 MCP 联调这一步卡住如果你正在用 LangChain 搭 Agent同时后端工具服务是 Java 写的并且想通过 Streamable HTTP 把两边接起来那这篇就是给你准备的。核心要解决的问题很具体LangChain Agent 在调用 Java MCP Server 的多个工具时模型 Key、通道地址、请求头这几样东西散落在不同文件里改一处忘一处联调时不是 401 就是工具列表拉不到。MCP 在 2025 年成了大模型连接外部能力的通用协议通信方式主要有三种Stdio、SSE、Streamable HTTP。Stdio 适合本地进程SSE 正在被 Streamable HTTP 取代因为后者支持普通 POST 请求、无状态部署更省心。Java 侧目前 Spring AI 对 Streamable HTTP 的原生支持还不完整很多团队会先用一个轻量 HTTP Server 把initialize、tools/list、tools/call三个方法实现出来先跑通链路。我试过的做法是Java 端暴露一个/mcp端点Python 端用langchain-mcp-adapters的MultiServerMCPClient以streamable_http传输方式接入模型侧统一走 TaoToken 的 Key 和 API 通道。这样 Agent 拿到的工具列表来自 Java推理请求走统一通道配置集中在一个地方排障时不用来回翻文件。下面按“先配通道、再写 Java 端点、最后跑 Agent 验证”的顺序展开每一步都能直接复制。2. TaoToken 前置统一 Key 与 API 通道怎么配TaoToken 在这里的角色是统一模型接入层。你不需要在 Agent 代码里硬编码某家模型的地址和 Key而是把模型对话、编码计划、控制台、API Keys 这些入口统一到一个账号体系下。对 LangChain 来说它兼容 OpenAI 风格的base_url和api_key所以ChatOpenAI可以直接指过去。先拿到 Key进入控制台在 API Keys 页面创建一个新 Key复制出来。这个 Key 后面会写进config.toml或环境变量Agent 和 MCP 客户端都从同一处读取避免多工具调用时出现“这个工具用旧 Key、那个工具用新 Key”的混乱。控制台入口https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewriteAPI Keys 页面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewriteAPI 基地址用https://taotoken.net/api注意这个地址不带 UTM 参数直接作为base_url使用。模型名按你实际开通的填比如gpt-4o-mini或claude-3-5-sonnet这类具体以文档里的模型列表为准。注意Key 不要提交到 Git。本地用.env或config.tomlCI 里用环境变量注入。下面给的骨架都按“从环境变量读”来写方便你替换。3. 可复制配置config.toml 与 settings.json 骨架先建一个项目目录放两个配置文件。config.toml给 Python 侧读settings.json给需要 JSON 配置的工具或 IDE 插件读。两者内容语义一致只是格式不同。# config.toml [llm] provider openai-compatible base_url https://taotoken.net/api api_key ${TAOTOKEN_API_KEY} model gpt-4o-mini temperature 0.7 streaming true [mcp.java_server] url http://localhost:8080/mcp transport streamable_http # 如果 Java 端开了鉴权把 token 放这里 headers { Authorization Bearer ${MCP_SERVER_TOKEN} }{ llm: { provider: openai-compatible, baseUrl: https://taotoken.net/api, apiKey: ${TAOTOKEN_API_KEY}, model: gpt-4o-mini, temperature: 0.7, streaming: true }, mcp: { javaServer: { url: http://localhost:8080/mcp, transport: streamable_http, headers: { Authorization: Bearer ${MCP_SERVER_TOKEN} } } } }Python 侧读取时用python-dotenv加载.env把TAOTOKEN_API_KEY和MCP_SERVER_TOKEN填进去。这样 Agent 初始化ChatOpenAI时只认base_url和api_key两个字段MCP 客户端只认url、transport、headers职责清晰。依赖版本参考下面这组实测能跑通langchain0.3.26 langchain-core0.3.70 langchain-mcp-adapters0.1.9 langchain-openai0.3.28 mcp1.12.0 openai1.97.0 python-dotenv1.1.1安装命令pip install -r requirements.txt4. Java 侧 Streamable HTTP 端点配置Java 端的目标是暴露一个/mcp端点能响应initialize、tools/list、tools/call。Spring AI 原生支持还不完整时可以用一个轻量 Handler 手动注册路由。核心思路是用自定义注解标记 MCP 服务类和工具方法启动时扫描并注册到RequestMappingHandlerMapping。先定义三个注解McpServerEndpoint标在类上McpFunction标在方法上McpParam标在参数上。Retention(RetentionPolicy.RUNTIME) Target({ElementType.METHOD}) Documented public interface McpFunction { String name(); String description(); } Inherited Retention(RetentionPolicy.RUNTIME) Target({ElementType.PARAMETER}) public interface McpParam { String name(); String description(); String[] enums() default {}; boolean required() default false; } Target({ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) Component Documented public interface McpServerEndpoint { String value(); String name() default ; String version() default ; }然后写一个 Handler处理 POST 请求里的 JSON-RPC 消息。initialize返回协议版本和 serverInfotools/list把扫描到的工具转成 MCP 工具描述tools/call反射调用目标方法并把结果包成content数组。public ResponseEntityObjectNode handlePost(RequestBody String body) throws Exception { ObjectNode request objectMapper.readValue(body, ObjectNode.class); if (request null || !request.has(id)) { return ResponseEntity.status(HttpStatus.ACCEPTED).body(null); } String id request.get(id).asText(); String method request.get(method).asText(); switch (method) { case initialize: return handleInitialize(id); case tools/list: return handleListTools(id); case tools/call: return handleCallTool(request); default: return handleUnsupportedMethod(id, method); } }工具类这样写两个方法分别返回天气和特产Component McpServerEndpoint(value /mcp, version 1.0.0, name 天气查询服务) public class McpServerTool { McpFunction(name getWeather, description 获取天气信息) public String getWeather( McpParam(name city, description 城市名称, required true) String city) { return String.format(%s: 晴天温度25℃, city); } McpFunction(name getSpeciality, description 获取城市特产) public String getSpeciality( McpParam(name city, description 城市名称, required true) String city) { return String.format(%s特产是小笼包, city); } }启动类就是标准 Spring BootSpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }启动后默认监听 8080端点路径是http://localhost:8080/mcp。先用 curl 验证tools/listcurl -X POST http://localhost:8080/mcp \ -H Content-Type: application/json \ -d {jsonrpc:2.0,id:1,method:tools/list,params:{}}返回里应该能看到getWeather和getSpeciality两个工具inputSchema里city是 required。这一步通了说明 Java 侧端点没问题。5. LangChain Agent 接入与验证请求Python 侧用MultiServerMCPClient接入 Java 端点传输方式写streamable_http。模型用ChatOpenAIbase_url指向 TaoToken 的 API 地址api_key从环境变量读。import asyncio import os from typing import Sequence from dotenv import load_dotenv from langchain.agents import create_tool_calling_agent, AgentExecutor from langchain.tools import BaseTool from langchain_community.chat_message_histories import ChatMessageHistory from langchain_core.messages import HumanMessage, AIMessage from langchain_core.prompts import ChatPromptTemplate from langchain_mcp_adapters.client import MultiServerMCPClient from langchain_openai import ChatOpenAI load_dotenv() def create_chat_assistant_agent(tools: Sequence[BaseTool], prompt: ChatPromptTemplate): llm ChatOpenAI( model_nameos.getenv(MODEL_NAME, gpt-4o-mini), openai_api_basehttps://taotoken.net/api, openai_api_keyos.getenv(TAOTOKEN_API_KEY), temperature0.7, streamingTrue, ) agent create_tool_calling_agent(llm, tools, prompt) return AgentExecutor( agentagent, verboseTrue, toolstools, max_iterations5, handle_parsing_errorsTrue, ) async def main(): mcp_client MultiServerMCPClient( { java-mcp-server: { url: http://localhost:8080/mcp, transport: streamable_http, headers: { Authorization: fBearer {os.getenv(MCP_SERVER_TOKEN, )} }, } } ) system_prompts ChatPromptTemplate.from_template( 你是一个智能助手 chat_history:{chat_history} Begin! Question: {input} Thought:{agent_scratchpad} ) tools await mcp_client.get_tools() print(f已加载工具: {[t.name for t in tools]}) agent create_chat_assistant_agent(toolstools, promptsystem_prompts) memory ChatMessageHistory() print(助手: 你好请问需要什么帮助) while True: try: user_input input(用户: ).strip() if user_input.lower() in [quit, exit, 退出]: print(AI助手退出) break res await agent.ainvoke( {input: user_input, chat_history: memory.messages} ) print(f助手: {res[output]}) memory.add_message(HumanMessage(contentuser_input)) memory.add_message(AIMessage(contentres[output])) except KeyboardInterrupt: print(AI助手退出) break if __name__ __main__: asyncio.run(main())运行前确认.env里有TAOTOKEN_API_KEYMCP_SERVER_TOKEN如果 Java 端没开鉴权可以留空。启动脚本python mcp_client.py预期输出先打印已加载工具: [getWeather, getSpeciality]然后进入对话。输入“北京天气怎么样”Agent 会调用getWeather返回“北京: 晴天温度25℃”。再输入“北京有什么特产”会调用getSpeciality返回“北京特产是小笼包”。verboseTrue下能看到完整的工具调用链确认请求确实走了 Java 端点。6. 本篇常见错排查工具列表为空先 curl 直接打 Java 端点确认tools/list返回里有工具。如果 curl 通但 Python 拉不到检查transport是否写成streamable_http以及 URL 是否带了多余斜杠。401 或鉴权失败模型侧报 401 说明TAOTOKEN_API_KEY没读到或失效去 API Keys 页面重新生成。MCP 侧报 401 说明 Java 端开了鉴权但headers没带对确认Authorization格式是Bearer token。连接被拒Java 服务没启动或端口不是 8080。先curl http://localhost:8080/mcp看是否有响应注意 GET 请求在实现里返回 405 是正常的用 POST 测。工具调用参数缺失Java 端handleCallTool里如果 required 参数没传会返回-32602。检查 Agent 生成的 arguments 是否包含city字段必要时在 prompt 里明确要求传城市名。模型名不匹配ChatOpenAI的model_name要和你 TaoToken 账号下开通的模型一致写错会返回模型不存在。不确定时先看接入文档里的模型列表。多工具调用时 Key 混乱确保 Agent 和 MCP 客户端都从同一份配置读 Key不要一个写死在代码里、一个读环境变量。统一走config.toml或.env改一处全生效。7. 下一步按场景选入口链路跑通后接下来看你的使用场景。如果只是验证模型对话和工具调用是否正常直接进模型对话页面发几条消息确认返回符合预期https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite如果你要把这套 Agent 用在长期编码或自动化任务上比如让 Agent 持续调用 Java 工具做数据处理建议看 Coding Plan把调用额度和通道规划好https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite接入过程中遇到报错优先查接入文档里的错误码说明再对照 API Keys 页面确认 Key 状态https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewritehttps://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewriteJava 端如果后续要换成 Spring AI 原生 Streamable HTTP 支持Handler 里的initialize、tools/list、tools/call三个分支逻辑可以保留只替换路由注册部分。Python 侧不用动因为 MCP 协议层已经屏蔽了服务端实现差异。
返回列表