在 CommonJS 项目中加载 ESM-only 的 @mcp-use/client:动态 import() 实战指南
【免费下载链接】mcp-useThe fullstack MCP framework to develop MCP Apps for ChatGPT / Claude & MCP Servers for AI Agents.项目地址: https://gitcode.com/gh_mirrors/mc/mcp-use
本篇指南围绕 mcp-use 仓库中 CommonJS 宿主加载示例 展开,讲解如何在.cjs/ CommonJS 环境中使用以 ESM 形式发布的@mcp-use/client包。你将掌握动态import()的标准写法、HTTP 与 stdio 两种 MCP Server 连接的启动方式,以及 Node 与浏览器环境下 OAuth 导出的差异,从而在传统 CJS 工程中顺畅接入 mcp-use v2 的 MCP 客户端。
为什么 @mcp-use/client 是 ESM-only
@mcp-use/client是 mcp-use v2 框架的官方 MCP 客户端,负责与 Model Context Protocol 服务器建立连接、管理会话与 OAuth 鉴权。它从设计上就只提供 ESM 构建产物,不提供 CommonJS(CJS)构建,因此不能用require("@mcp-use/client")直接加载。
这一点可以从包配置中得到直接印证。查看 packages/client/package.json:
- 顶层声明
"type": "module",整个包按 ESM 语义解析; exports字段中每个条件的产物类型都只有"import"入口(./dist/index.js、./dist/index-browser.js等),不存在"require"条件,Node 在解析require()时无法命中任何构建产物。
这是许多现代 npm 包的常见发布策略:ESM 便于静态分析、tree-shaking 和顶层 await,代价则是传统 CommonJS 宿主需要借助异步加载手段。Node 官方对 ESM 与 CJS 互操作的支持路径是:CommonJS 文件可以通过动态import()加载 ESM 模块,而require()仅能同步加载 CJS 模块,无法加载纯 ESM 包。
核心模式:用动态 import() 代替 require
在 CommonJS(.cjs或"type": "commonjs"的.js)文件中加载@mcp-use/client的标准做法,是把import调用放在async函数内部,并解构出所需导出:
async function main() { const { MCPClient } = await import("@mcp-use/client"); const client = new MCPClient({ mcpServers: { demo: { url: "http://127.0.0.1:3102/mcp" }, }, }); const connection = await client.connect("demo"); console.log(await connection.listTools()); await client.close(); } main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });这个模式有三个关键点:
await import(...)返回的是模块命名空间对象,从中解构MCPClient构造函数,用法与 ESM 中的静态import { MCPClient } from "@mcp-use/client"完全一致;- 动态 import 是异步的,因此
main()必须声明为async,并在入口处用.catch()兜底,避免未处理的 Promise 拒绝; - 只加载一次:Node 会对 ESM 模块进行缓存,多次调用
import()不会重复执行模块副作用,其./telemetry/configure-node.js等启动逻辑只会运行一次(该副作用在 packages/client/src/index.ts 中可见)。
完整可运行示例:commonjs_example.cjs
仓库在 examples/browser/commonjs/commonjs_example.cjs 提供了开箱即用的完整示例,展示了比最小模式更完整的生命周期:连接、列出工具、调用工具、关闭连接、错误处理。
async function runCommonJSExample() { const { MCPClient } = await import("@mcp-use/client"); console.log("=== CommonJS MCP Example ===\n"); const useStdio = process.env.USE_STDIO_EVERYTHING === "1"; const url = process.env.MCP_SERVER_URL ?? "http://127.0.0.1:3102/mcp"; const client = new MCPClient({ mcpServers: useStdio ? { everything: { command: "npx", args: ["-y", "@modelcontextprotocol/server-everything"], }, } : { demo: { url }, }, }); try { const name = useStdio ? "everything" : "demo"; const connection = await client.connect(name); console.log("✓ Connected", `(era=${connection.protocolEra ?? "?"})`); const tools = await connection.listTools(); console.log(`✓ Found ${tools.length} tools`); for (const tool of tools.slice(0, 5)) { console.log(` - ${tool.name}`); } if (tools.some((t) => t.name === "echo")) { const result = await connection.callTool("echo", { message: "cjs" }); console.log("✓ echo ->", JSON.stringify(result.content)); } console.log("\n=== CommonJS Example Completed Successfully ==="); } catch (error) { console.error("Error:", error.message); process.exitCode = 1; } finally { await client.close(); } } runCommonJSExample().catch((error) => { console.error("Fatal error:", error); process.exit(1); });该示例展示了几个值得复用的工程细节:
- 环境变量驱动的连接配置:
MCP_SERVER_URL指定 HTTP 端点,USE_STDIO_EVERYTHING=1切换到 stdio 服务器,二者互斥且可覆盖默认值; connection.protocolEra:mcp-use 客户端会自动协商传统会话式(legacy)与无会话(sessionless)两种 MCP 服务器时代(SDK 能力在 packages/client/src/index.ts 中描述为"自动协商 legacy sessionful 与 modern sessionless MCP servers"),此处打印出协商结果便于观测;finally中关闭连接:无论成功失败都调用await client.close()释放资源,避免进程悬挂;- 错误码传递:失败时设置
process.exitCode = 1,方便 CI 等自动化场景捕获失败。
运行方式:HTTP 与 stdio 两种服务器
方式一:连接本地 HTTP 演示服务器
仓库在 examples/_demo-servers 提供了两个最小演示服务器,分别模拟两代 Streamable HTTP 协议:
- v1-http.ts:legacy 时代(2025 Streamable HTTP)服务器,
PORT=3101 pnpm v1启动,提供echo与add两个工具; - v2-http.ts:mcp-use v2 无会话 Streamable HTTP 服务器,
PORT=3102 pnpm v2启动,同样提供echo与add工具,且可通过LEGACY=reject强制拒绝旧协议请求。
在examples/browser/commonjs/目录下分别执行:
# 先启动演示服务器:cd ../_demo-servers && PORT=3102 pnpm v2 MCP_SERVER_URL=http://127.0.0.1:3101/mcp node commonjs_example.cjs MCP_SERVER_URL=http://127.0.0.1:3102/mcp node commonjs_example.cjs两条命令分别验证客户端与 v1、v2 两种协议时代的服务器握手——这正是connection.protocolEra发挥作用的地方。运行成功后应依次看到连接成功、工具数量与名称列表,以及echo工具调用返回的结果。
方式二:通过 stdio 连接 server-everything
如果不想先启动 HTTP 服务器,可以直接用 stdio 方式拉起 npm 生态中的@modelcontextprotocol/server-everything:
USE_STDIO_EVERYTHING=1 node commonjs_example.cjs此时示例内部会改用npx -y @modelcontextprotocol/server-everything作为子进程命令(见commonjs_example.cjs中的 stdio 配置分支),客户端通过标准输入输出与该进程通信,无需任何网络端口。这在本地快速验证、或者 CI 无端口环境下非常实用。
方式三:从 packages/client 目录直接运行
如果你已在本地构建过@mcp-use/client(执行过pnpm build),也可以从包目录直接运行示例:
node examples/browser/commonjs/commonjs_example.cjs此时包解析会命中 packages/client/package.json 中exports["."].node.import指向的./dist/index.js,即 Node 专用入口。
关键注意事项
绝不要 require("@mcp-use/client")
如前所述,包没有 CJS 构建,require()会直接抛错(ERR_REQUIRE_ESM或模块解析失败)。判断某个包能否被require的快速方法:检查其package.json的exports是否包含"require"条件——@mcp-use/client只有"import",明确不支持同步加载。
Node 与浏览器的 OAuth 导出不同
OAuth 相关辅助函数分环境导出:
- Node 入口(
exports["."].node,即./dist/index.js)导出NodeOAuthClientProvider,以及createOAuthProvider、completeOAuthFlow、isOAuthInteractionRequired、isUnauthorized等(见 packages/client/src/auth/node.ts 与 packages/client/src/index.ts)。它支持完整的发现、动态客户端注册(DCR)、PKCE 回环回调与令牌持久化,仓库在 examples/node/auth/oauth-flow.ts 提供了无需外部身份提供商的端到端演示; - 浏览器 OAuth 提供方不会从 Node 入口导出:浏览器场景应使用浏览器构建(
exports["."].browser/default指向./dist/index-browser.js)中对应的 OAuth 提供方,Node 构建中不存在该导出。
因此,如果你在 CommonJS 的 Node 宿主中需要 OAuth,应使用NodeOAuthClientProvider;需要浏览器侧 OAuth 时,请切换到浏览器入口并采用对应的浏览器 OAuth 提供方。
版本与运行环境前提
以当前仓库为准:@mcp-use/client版本为 2.3.2(见 packages/client/package.json),声明"engines": { "node": ">=22.22.2" },依赖@modelcontextprotocol/client、@modelcontextprotocol/core与@modelcontextprotocol/ext-apps(均为 2.0.0)。动态import()从 Node 12.17+ 即可使用,但请以包的 engines 声明为准选择运行环境;react、zod、@e2b/code-interpreter为可选 peer 依赖,不使用对应能力时无需安装。
小结
在 CommonJS 宿主中使用 ESM-only 的@mcp-use/client,核心只有一条规则:把require换成 async 函数内的动态import()。配合MCP_SERVER_URL/USE_STDIO_EVERYTHING环境变量即可灵活切换 HTTP 与 stdio 服务器;Node 与浏览器入口在 OAuth 导出上的差异则决定了鉴权代码应该写在哪个构建产物之上。将 commonjs_example.cjs 作为模板,你的 CJS 工程即可无缝接入 mcp-use v2 的完整 MCP 客户端能力。
【免费下载链接】mcp-useThe fullstack MCP framework to develop MCP Apps for ChatGPT / Claude & MCP Servers for AI Agents.项目地址: https://gitcode.com/gh_mirrors/mc/mcp-use
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考