LangChain集成MCP
本文最后更新于 2026年8月11日
概述
关于MCP以及npx/uv安装本地MCP服务的前置知识,已经在Spring AI集成MCP Client一文提到,LangChain实现集成MCP也是大同小异,还是以modelscope社区上12306-mcp为例介绍LangChain实现集成MCP
12306-mcp:https://modelscope.cn/mcp/servers/A15570082312/12306,是一款TypeScript编写的从铁路12306查询数据的MCP工具
LangChain实现集成MCP,需要安装这个LangChain的MCP的模块
pip install langchain-mcp-adapters1.Stdio
此处以modelscope上面的12306-mcp为例,并将其源码下载后先用npm安装到本地,同时也要安装好npx,Stdio模式下该MCP服务运行在本地,使用npx运行
先定义一个mcp-server.json文件,使用LangChain调用MCP一定要写明transport字段在这个json文件中,亲测其无法自动识别MCP服务的类型
以下是一个win系统的配置文件,由cmd驱动npx启动本地MCP,mac/linux可直接用npx(uv)做command
{
"mcpServers": {
"12306-mcp": {
"transport": "stdio",
"command": "cmd",
"args": [
"/c",
"npx",
"-y",
"12306-mcp"
]
}
}
}使用MCP加载工具MultiServerMCPClient加载MCP为tools工具,供Agent调用,就如同function calling一样,只不过client.get_tools()需要异步,智能体调用大模型也需要ainvoke()异步
import asyncio
import json
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent
from loguru import logger
load_dotenv(encoding='utf-8')
DS_KEY = os.getenv('DSKEY')
llm = init_chat_model(
model='deepseek-chat',
model_provider='openai',
api_key=DS_KEY,
base_url='https://api.deepseek.com'
)
def load_server(file_path: str = 'mcp-server.json') -> dict:
with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
return data.get('mcpServers', {})
async def main():
cfg = load_server()
client = MultiServerMCPClient(cfg)
tools = await client.get_tools()
logger.info(f'已加载{len(tools)}个工具 {[t.name for t in tools]}')
agent = create_agent(
model=llm,
tools=tools,
system_prompt='你是一个智能助手,能够调用工具回答问题'
)
resp = await agent.ainvoke({"messages":
[{"role": "user", "content": "帮我查查明天上午北京朝阳到承德的高铁"}]
})
print(resp)
if __name__ == "__main__":
asyncio.run(main())12306 MCP Server running on stdio @Joooook
2026-08-11 23:17:11.314 | INFO | __main__:main:36 - 已加载8个工具 ['get-current-date', 'get-stations-code-in-city', 'get-station-code-of-citys', 'get-station-code-by-names', 'get-station-by-telecode', 'get-tickets', 'get-interline-tickets', 'get-train-route-stations']
12306 MCP Server running on stdio @Joooook
12306 MCP Server running on stdio @Joooook
12306 MCP Server running on stdio @Joooook
12306 MCP Server running on stdio @Joooook
{'messages': [HumanMessage(content='帮我查查明天上午北京朝阳到承德的高铁', additional_kwargs={}, response_metadata={}, id='11a12ada-32d2-4057-9d9a-47c7b52036e1'), AIMessage(content='我来帮您查询明天上午从北京朝阳到承德的高铁。首先让我获取当前日期,并查询相关车站信息。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 95, 'prompt_tokens': 2484, 'total_tokens': 2579, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 2432}, 'prompt_cache_hit_tokens': 2432, 'prompt_cache_miss_tokens': 52}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_a18b46594c_prod0820_fp8_kvcache_20260402', 'id': '62a201c1-5fc6-43df-96f3-6e888854dfe9', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019ff166-2622-7500-ab09-4f4866e24a26-0', tool_calls=[{'name': 'get-current-date', 'args': {}, 'id': 'call_00_TgnI66aWqmLNgjrr8KRf8048', 'type': 'tool_call'}, {'name': 'get-station-code-of-citys', 'args': {'citys': '北京朝阳|承德'}, 'id': 'call_01_qkVf8ejXnEerMFhDstE58968', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 2484, 'output_tokens': 95, 'total_tokens': 2579, 'input_token_details': {'cache_read': 2432}, 'output_token_details': {}}), ToolMessage(content=[{'type': 'text', 'text': '2026-08-11', 'id': 'lc_c9dffa37-3b93-4580-bd72-100e0726498b'}], name='get-current-date', id='77398fcf-8131-47b3-9635-8d96c2083e27', tool_call_id='call_00_TgnI66aWqmLNgjrr8KRf8048'), ToolMessage(content=[{'type': 'text', 'text': '{"北京朝阳":{"error":"未检索到城市。"},"承德":{"station_code":"CDP","station_name":"承德"}}', 'id': 'lc_38e1b952-6409-4725-b2f2-f4e6939d5efd'}], name='get-station-code-of-citys', id='a645070c-9b14-4cf0-af39-3e44a1a453d6', tool_call_id='call_01_qkVf8ejXnEerMFhDstE58968'), AIMessage(content='让我查询"北京朝阳"作为具体车站的代码。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 64, 'prompt_tokens': 2632, 'total_tokens': 2696, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 2432}, 'prompt_cache_hit_tokens': 2432, 'prompt_cache_miss_tokens': 200}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_a18b46594c_prod0820_fp8_kvcache_20260402', 'id': '22ce7e30-eafd-4291-af13-2a2ff597547c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019ff166-3c45-7050-ba74-7538689a0ed6-0', tool_calls=[{'name': 'get-station-code-by-names', 'args': {'stationNames': '北京朝阳|承德'}, 'id': 'call_00_wjR33RssraQtCGMJfPnZ1928', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 2632, 'output_tokens': 64, 'total_tokens': 2696, 'input_token_details': {'cache_read': 2432}, 'output_token_details': {}}), ToolMessage(content=[{'type': 'text', 'text': '{"北京朝阳":{"station_code":"IFP","station_name":"北京朝阳"},"承德":{"station_code":"CDP","station_name":"承德"}}', 'id': 'lc_4e03325e-fb18-42ae-893f-920a71eeb46c'}], name='get-station-code-by-names', id='9aa168e5-00e6-4bc5-a26d-b33b353f0f06', tool_call_id='call_00_wjR33RssraQtCGMJfPnZ1928'), AIMessage(content='好的,我已经获取到了日期和车站信息。今天是2026年8月11日,所以明天是**2026年8月12日**。北京朝阳站代码为IFP,承德站代码为CDP。\n\n现在我来查询明天上午的高铁票。上午我理解为0点到12点之间,筛选高铁(G)。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 210, 'prompt_tokens': 2740, 'total_tokens': 2950, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 2688}, 'prompt_cache_hit_tokens': 2688, 'prompt_cache_miss_tokens': 52}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_a18b46594c_prod0820_fp8_kvcache_20260402', 'id': '3930ac1e-1150-4ce2-b55e-c160188de4d5', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019ff166-4db9-7360-a52f-2737b0d4fb95-0', tool_calls=[{'name': 'get-tickets', 'args': {'date': '2026-08-12', 'fromStation': 'IFP', 'toStation': 'CDP', 'trainFilterFlags': 'G', 'earliestStartTime': 0, 'latestStartTime': 12}, 'id': 'call_00_6PuaLXTMUI8b58uV0eBG6785', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 2740, 'output_tokens': 210, 'total_tokens': 2950, 'input_token_details': {'cache_read': 2688}, 'output_token_details': {}}), ToolMessage(content=[{'type': 'text', 'text': '车次|出发站 -> 到达站|出发时间 -> 到达时间|历时\nG7851 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 06:08 -> 07:07 历时:00:59\n- 商务座: 有票 204元\n- 一等座: 有票 93元\n- 二等座: 有票 58元\n- 无座: 有票 58元\nG3661 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 06:23 -> 07:22 历时:00:59\n- 商务座: 剩余7张票 296元\n- 一等座: 剩余7张票 135元\n- 二等座: 有票 85元\n- 无座: 有票 85元\nG3501 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 06:35 -> 07:34 历时:00:59\n- 商务座: 剩余8张票 370元\n- 一等座: 剩余18张票 169元\n- 二等座: 有票 104元\n- 无座: 有票 104元\nG3557 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 06:50 -> 07:42 历时:00:52\n- 商务座: 剩余4张票 370元\n- 一等座: 剩余16张票 169元\n- 二等座: 有票 106元\n- 无座: 有票 106元\nG3663 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 07:00 -> 07:59 历时:00:59\n- 商务座: 剩余4张票 370元\n- 一等座: 剩余8张票 169元\n- 二等座: 有票 106元\n- 无座: 有票 106元\nG3601 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 07:20 -> 08:19 历时:00:59\n- 商务座: 剩余6张票 333元\n- 一等座: 有票 152元\n- 二等座: 有票 94元\n- 无座: 有票 94元\nG3651 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 07:30 -> 08:29 历时:00:59\n- 商务座: 剩余7张票 370元\n- 一等座: 剩余11张票 169元\n- 二等座: 有票 106元\n- 无座: 有票 106元\nG3533 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 07:49 -> 08:41 历时:00:52\n- 商务座: 剩余8张票 333元\n- 一等座: 有票 152元\n- 二等座: 有票 92元\n- 无座: 剩余19张票 92元\nG7857 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 08:15 -> 09:14 历时:00:59\n- 商务座: 剩余11张票 296元\n- 一等座: 有票 135元\n- 二等座: 有票 85元\n- 无座: 有票 85元\nG3623 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 08:20 -> 09:19 历时:00:59\n- 商务座: 剩余18张票 370元\n- 一等座: 剩余19张票 169元\n- 二等座: 有票 106元\n- 无座: 有票 106元\nG3503 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 08:25 -> 09:24 历时:00:59\n- 商务座: 剩余5张票 370元\n- 一等座: 剩余8张票 169元\n- 二等座: 有票 106元\n- 无座: 有票 106元\nG107 北京西(telecode:BXP) -> 承德南(telecode:IVP) 08:56 -> 10:26 历时:01:30\n- 商务座: 剩余5张票 412元\n- 一等座: 剩余2张票 188元\n- 二等座: 无票 118元\n- 无座: 有票 118元\nG3535 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 09:05 -> 10:04 历时:00:59\n- 商务座: 剩余10张票 370元\n- 一等座: 剩余11张票 169元\n- 二等座: 有票 106元\n- 无座: 有票 106元\nG107 北京(telecode:BJP) -> 承德南(telecode:IVP) 09:15 -> 10:26 历时:01:11\n- 商务座: 剩余5张票 395元\n- 一等座: 剩余1张票 180元\n- 二等座: 无票 113元\n- 无座: 有票 113元\nG3627 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 09:17 -> 10:16 历时:00:59\n- 商务座: 剩余7张票 370元\n- 一等座: 剩余1张票 169元\n- 二等座: 有票 106元\n- 无座: 有票 106元\nG107 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 09:37 -> 10:26 历时:00:49\n- 商务座: 剩余5张票 370元\n- 一等座: 剩余3张票 169元\n- 二等座: 无票 106元\n- 无座: 有票 106元\nG3641 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 10:00 -> 10:59 历时:00:59\n- 商务座: 剩余1张票 370元\n- 一等座: 剩余8张票 169元\n- 二等座: 有票 106元\n- 无座: 有票 106元\nG3537 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 10:05 -> 11:04 历时:00:59\n- 商务座: 剩余9张票 333元\n- 一等座: 剩余17张票 152元\n- 二等座: 无票 95元\n- 无座: 有票 95元\nG3507 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 10:11 -> 11:10 历时:00:59\n- 商务座: 剩余13张票 370元\n- 一等座: 有票 169元\n- 二等座: 有票 106元\n- 无座: 有票 106元\nG3509 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 10:21 -> 11:20 历时:00:59\n- 商务座: 剩余11张票 370元\n- 一等座: 剩余13张票 169元\n- 二等座: 有票 106元\n- 无座: 有票 106元\nG3667 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 10:36 -> 11:28 历时:00:52\n- 商务座: 剩余7张票 370元\n- 一等座: 剩余8张票 169元\n- 二等座: 有票 106元\n- 无座: 有票 106元\nG3511 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 10:42 -> 11:34 历时:00:52\n- 商务座: 剩余11张票 370元\n- 一等座: 有票 169元\n- 二等座: 有票 106元\n- 无座: 有票 106元\nG7818 北京西(telecode:BXP) -> 承德南(telecode:IVP) 11:14 -> 12:56 历时:01:42\n- 商务座: 有票 430元\n- 一等座: 有票 198元\n- 二等座: 无票 124元\n- 无座: 剩余20张票 124元\nG3631 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 11:14 -> 12:13 历时:00:59\n- 商务座: 无票 370元\n- 一等座: 剩余9张票 169元\n- 二等座: 剩余18张票 106元\n- 无座: 有票 106元\nG3513 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 11:20 -> 12:26 历时:01:06\n- 商务座: 剩余6张票 370元\n- 一等座: 有票 169元\n- 二等座: 有票 106元\n- 无座: 有票 106元\nG3653 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 11:32 -> 12:31 历时:00:59\n- 商务座: 剩余7张票 370元\n- 一等座: 剩余5张票 169元\n- 二等座: 剩余2张票 106元\n- 无座: 有票 106元\nG7815 北京(telecode:BJP) -> 承德南(telecode:IVP) 11:35 -> 12:56 历时:01:21\n- 商务座: 有票 395元\n- 一等座: 有票 180元\n- 二等座: 无票 113元\n- 无座: 剩余20张票 113元\nG3539 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 11:43 -> 12:45 历时:01:02\n- 商务座: 剩余8张票 370元\n- 一等座: 有票 169元\n- 二等座: 有票 106元\n- 无座: 有票 106元\nG4783 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 11:49 -> 12:41 历时:00:52\n- 商务座: 剩余8张票 370元\n- 一等座: 剩余6张票 169元\n- 二等座: 有票 106元\n- 无座: 有票 106元\nG7815 北京朝阳(telecode:IFP) -> 承德南(telecode:IVP) 11:57 -> 12:56 历时:00:59\n- 商务座: 有票 370元\n- 一等座: 有票 169元\n- 二等座: 有票 106元\n- 无座: 剩余20张票 106元\n', 'id': 'lc_fd1f1ac2-ad11-4649-9ce6-44ae452d1bef'}], name='get-tickets', id='ff6e6ab1-4f1e-44b6-9f62-643ee14a2a9c', tool_call_id='call_00_6PuaLXTMUI8b58uV0eBG6785'), AIMessage(content='我已经为您查询到了明天(2026年8月12日)上午从北京朝阳到承德的高铁车次信息。由于查询到的车次大部分到达的是**承德南**站,我为您整理如下:\n\n## 🚄 明天上午北京朝阳→承德 高铁车次\n\n目前查到的大部分高铁都到达**承德南站**,其中从**北京朝阳站**出发的车次较多。为您列出部分车次供参考:\n\n| 车次 | 出发→到达 | 出发时间→到达时间 | 历时 | 二等座 | 一等座 | 商务座 |\n|------|-----------|------------------|------|--------|--------|--------|\n| **G7851** | 北京朝阳→承德南 | 06:08→07:07 | 59分 | 58元✅ | 93元✅ | 204元✅ |\n| **G3661** | 北京朝阳→承德南 | 06:23→07:22 | 59分 | 85元✅ | 135元(7张) | 296元(7张) |\n| **G3501** | 北京朝阳→承德南 | 06:35→07:34 | 59分 | 104元✅ | 169元(18张) | 370元(8张) |\n| **G3557** | 北京朝阳→承德南 | 06:50→07:42 | 52分 | 106元✅ | 169元(16张) | 370元(4张) |\n| **G3663** | 北京朝阳→承德南 | 07:00→07:59 | 59分 | 106元✅ | 169元(8张) | 370元(4张) |\n| **G3601** | 北京朝阳→承德南 | 07:20→08:19 | 59分 | 94元✅ | 152元✅ | 333元(6张) |\n| **G3651** | 北京朝阳→承德南 | 07:30→08:29 | 59分 | 106元✅ | 169元(11张) | 370元(7张) |\n| **G3533** | 北京朝阳→承德南 | 07:49→08:41 | 52分 | 92元✅ | 152元✅ | 333元(8张) |\n| **G7857** | 北京朝阳→承德南 | 08:15→09:14 | 59分 | 85元✅ | 135元✅ | 296元(11张) |\n| **G107** | 北京朝阳→承德南 | 09:37→10:26 | 49分 | ❌无票 | 169元(3张) | 370元(5张) |\n\n📌 **说明:**\n- ✅ 表示有票,括号内为剩余票数\n- 大部分车次为北京朝阳→**承德南**(承德主要高铁站)\n- 二等座价格大约在 **58元~106元** 之间,性价比很高\n\n请问您需要我重点关注某个具体时间段的班次,或者需要查看某趟车的具体经停信息吗?如果有偏好的出发时间,我可以进一步帮您筛选。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 716, 'prompt_tokens': 5742, 'total_tokens': 6458, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 2944}, 'prompt_cache_hit_tokens': 2944, 'prompt_cache_miss_tokens': 2798}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_a18b46594c_prod0820_fp8_kvcache_20260402', 'id': 'd855f02b-86cf-4285-b4a4-d5173d350b39', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--019ff166-696a-7663-946b-ee68c4997815-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 5742, 'output_tokens': 716, 'total_tokens': 6458, 'input_token_details': {'cache_read': 2944}, 'output_token_details': {}})]}2.Streamable HTTP
modelscope上面可以部署http形式的12306-mcp服务,这种远程MCP就通过Streamable HTTP模式调用,不需本地安装,mcp-server.json文件同步改为Streamable HTTP协议
{
"mcpServers": {
"12306-mcp-http": {
"transport": "http",
"url": "https://mcp.api-inference.modelscope.net/************/mcp"
}
}
}使用http模式的用法和stdio模式一样,修改了mcp-server.json即可,加载调用代码不变。
"如果文章对您有帮助,可以请作者喝杯咖啡吗?"
微信支付
支付宝