96SEO 2026-08-08 05:18 2
第一次看到 MCP 客户端代码时很容易把它理解成“让大模型调用一个接口”。但真正写起来几个问题会立刻冒出来:
这些正是开发者在实际项目中最常碰到的痛点:

这篇文章用一个可运行的 Node.js 示例完成一条最小但完整的链路:连接一个远程 MCP Server。读取它暴露的工具,把工具绑定给聊天模型,执行模型发起的工具调用,再把结果交回模型生成最终回答。
示例使用 LangChain 负责模型与消息编排,使用 @langchain/mcp-adapters 把 MCP 工具转换成 LangChain 能识别的工具。读完之后你不仅能运行代码,也能说清每一轮数据究竟流向了哪里。
大模型本身只负责使用外部能力。应用程序至少要完成三件事:
MCP为工具的发现、参数描述和调用方式提供了一套统一协议。地图服务、浏览器控制器和文件程序服务可以分别实现 MCP Server;应用只要实现 MCP Client,就能用相似的方式接入它们。
使用者痛点:在没有统一协议之前。每接入一个新能力都得手写一次发现、序列化、反序列化逻辑,代码重复且易出错。MCP 把这些重复工作抽离出来让团队专注业务本身。
MCP 常见的传输方式包括 stdio 和 Streamable HTTP。不过,stdio 通常由客户端启动本地子进程。再通过标准输入输出通信,HTTP 则连接已经运行在某个 URL 上的服务,更适合跨机器部署。这篇文章聚焦远程 HTTP 服务,同时会说明如何切换到 stdio.
建议使用 Node.js v18 或更高版本,并创建一个新项目:
mkdir remote-mcp-demo
cd remote-mcp-demo
npm init -y
npm install dotenv @langchain/core @langchain/openai @langchain/mcp-adapters
mkdir src
示例文件使用 .mjs 后缀,所以 Node.js 会按 ES Module 处理。可以直接使用 和顶层 ,不必额外修改 .
在项目根目录创建 .env:
MODEL_API_KEY=your_api_key
MODEL_BASE_URL=https://your-model-provider.example/v1
MODEL_NAME=your-tool-calling-model
MCP_SERVER_URL=https://your-mcp-server.example/mcp
四个值分别表示模型密钥、OpenAI 兼容接口地址、支持工具调用的模型名,还有远程 MCP 端点。说起来,不同供应商名称和地址不同,应以实际服务为准。不要把真实 .env 提交到 Git 仓库,可在 .gitignore 中加入:
.env
Create
Pain point: 很多团队在“获取工具列表”这一步直接硬编码或手动复制 swagger,这导致维护成本爆炸。一旦后端更新,只需要重新跑一次 getTools 即可自动同步。
User pain point: 开发者经常误以为 bindTools 就会自动执行。这导致调试时看到“模型说要用 X 工具,却没有实际请求”。bindTools 仅是把“菜单”交给 LLM 看,看完后仍需自行循环调度。
A single question may need multiple tools,or repeated calls to same tool . Therefore we must loop until model stops requesting tools.
Pain point:
MCP 并不是某个神奇函数,而是一套"发现‑描述‑调用‑回流" 的闭环机制。从在这套机制里来看,
` 会在程序启动时读取 .env。并把值放入 . 环境变量不存在时 JavaScript 通常只会得到
先连接 MCP Server 并发现工具
,write connection and tool‑loading part first:
import "dotenv/config";import { MultiServerMCPClient } from "@langchain/mcp-adapters";不过,function requireEnv {
const value = process.env;不过,if {
throw new Error;老实说,}
return value;}
const mcpClient = new MultiServerMCPClient({
useStandardContentBlocks: true,mcpServers: {
remoteService: {
transport: "http"。url: requireEnv,},// 如需本地 stdio,只需改成下面形式:
// localService: {
// transport: "stdio",// command: "node",// args:,// },},});const tools = await mcpClient.getTools;console.log);
把工具交给模型,不等于已经执行工具
import { ChatOpenAI } from "@langchain/openai";const model = new ChatOpenAI({
model的观点是,requireEnv,apiKey: requireEnv。temperature: 0,configuration: { baseURL: requireEnv },});const modelWithTools = model.bindTools;
用消息列表完成工具调用闭环
import { HumanMessage } from "@langchain/core/messages";async function runAgent {
const messages =;for {
console.log;const response = await modelWithTools.invoke;其实,messages.push;const toolCalls = response.tool_calls?,;if {
// 没有 tool_calls 表示得到最终答案
return response.content;怎么说呢,}
for {
const tool = tools.find;if {
throw new Error;}
console.log})`);// 必须传入完整对象,以保留 call_id 等元信息
const toolMessage = await tool.invoke;messages.push;}
}
throw new Error;}
。you lose call ID and model cannot associate results with its request,causing “我没有收到结果” 的错误。 back into ,subsequent rounds will not see tool’s output.
完整可运行代码
import "dotenv/config";import { MultiServerMCPClient } from "@langchain/mcp-adapters";import { ChatOpenAI } from "@langchain/openai";import { HumanMessage } from "@langchain/core/messages";function requireEnv {
const v = process.env;if throw new Error;return v,}
// ---------- 初始化 MCP 客户端 ----------
const mcpClient = new MultiServerMCPClient({
useStandardContentBlocks: true。mcpServers: {
remoteService: {
transport: "http",url: requireEnv,},},});async function run {
// ---- 步骤1:发现并转换工具 ----
const tools = await mcpClient.getTools;console.log,// ---- 步骤2:创建支持 Tool Calling 的语言模型 ----
const model = new ChatOpenAI({
再看model。requireEnv,apiKey: requireEnv,temperature: 0,configuration: { baseURL: requireEnv },});
const modelWithTools = model.bindTools;// ---- 步骤3:进入 Agent 循环 ----
const answer = await runAgent(
"请告诉我这个 MCP 服务提供了哪些能力,并选择合适的工具验证其中一项。",tools,modelWithTools。/* maxIterations */5
);console.log,}
// ---------- Agent Loop ----------
async function runAgent {
const messages =;老实说,for {
console.log;const aiResp = await modelWithTools.invoke;messages.push;const calls = aiResp.tool_calls?,;if return aiResp.content;for {
const tool = tools.find;if throw new Error;console.log);const resultMsg = await tool.invoke;// 保留 call_id 等元信息
messages.push;console.log);}
}
throw new Error 已达,但仍未产生最终答案`);}
// ---------- 执行入口 ----------
run
.catch)
.finally => mcpClient.close);
程序从启动到结束发生了什么
常见错误与排查方法
1️⃣ 工具结果为空或模型提示“没有收到结果” 🚩
await tool.invoke`;若自行实现自定义适配器,请确保返回对象包含 `{content。tool_call_id}` 字段。
2️⃣ Remote 地址浏览器可访问。但 MCP 连不上 ❌
3️⃣ 模型一直不触发 Tool 调用 🔄
4️⃣ 本地 stdio 服务报 “Cannot find module …老实说,” 📁
5️⃣ 程序异常后进程不退出 ⚙️
从演示走向真实生产环境 🚀
🎯
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback