一个轻量级适配器,用于使用Express.js创建MCP(模型上下文协议)服务器。
由 https://tixaeagents.ai 赞助,可在几秒钟内创建文本/语音AI代理,并与MCP服务器兼容。
检查清单:
npm install mcp-express-adapter@latest
# 或
yarn add mcp-express-adapter@latest
# 或
pnpm add mcp-express-adapter@latest
注意:如果您从私有仓库安装,则需要对GitHub Packages进行身份验证。更多详情请参阅GitHub Packages文档。
在Express服务器上创建一个MCP客户端(示例)
// examples/with-express/src/super-simple.ts
import express from 'express'
import cors from 'cors'
import { MCPClient, mcpTool } from 'mcp-express-adapter'
import { z } from 'zod'
// 创建Express应用
const app = express()
app.use(cors())
// 定义一个超级简单的天气工具
const weatherTool = mcpTool({
name: 'get_weather',
description: '获取某个地点的天气',
// 定义输入模式
schema: z.object({
location: z.string().describe('要获取天气的城市'),
}),
// 简单字符串响应不需要输出模式
handler: async (args) => {
// 只返回一个字符串 - mcpTool处理格式化
return `Weather for ${args.location}: ☀️ 晴朗,72°F`
},
})
// 创建MCP客户端
const mcpClient = new MCPClient({
endpoint: '/mcp',
tools: [weatherTool],
serverName: 'demo-server',
serverVersion: '1.0.0',
})
// 挂载MCP路由器
app.use('/mcp', mcpClient.middleware())
// 应用其他路由的JSON解析器
app.use(express.json())
// 启动服务器
const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
console.log(`✨ 超级简单的MCP服务器正在运行!`)
console.log(`🔗 连接到:http://localhost:${PORT}/mcp/sse`)
})
已创建具有以下配置的MCP客户端:
- 端点:/mcp
- 服务器:my-mcp-server v1.0.0
- 工具:get_weather, calculator, generate_list, greeting
MCP服务器正在端口3000上运行
连接到:http://localhost:3000/mcp/sse
调试模式:启用将显示调试日志,要禁用设置NODE_ENV=production
{
"mcpServers": {
"localMcpServer": {
"command": "npx",
"args": [
"-y",
"mcp-express-adapter",
"--host",
"http://localhost:3000/mcp/sse"
]
}
}
}
然后重启Claude桌面,您将在几秒钟内看到MCP工具

// examples/with-express/src/multiple-mcp-clients.ts
import express from 'express'
import cors from 'cors'
import { MCPClient, mcpTool } from 'mcp-express-adapter'
import { z } from 'zod'
// 创建Express应用
const app = express()
app.use(cors())
// 使用mcpTool辅助定义天气工具
const weatherTool = mcpTool({
name: 'get_weather',
description: '获取某个地点的当前天气',
schema: z.object({
location: z.string().describe('要获取天气的地点'),
}),
// 您可以定义类型安全的输出模式...
outputSchema: z.object({
farenheight: z.number().describe('华氏温度'),
celsius: z.number().describe('摄氏温度'),
}),
handler: async (args) => {
return {
farenheight: 72,
celsius: 22,
}
},
})
// 使用mcpTool辅助定义计算器工具
const calculatorTool = mcpTool({
name: 'calculate',
description: '计算数学表达式的结果',
schema: z.object({
expression: z.string().describe('要评估的数学表达式'),
}),
handler: async (args) => {
return `结果:${eval(args.expression)}`
},
})
// 使用mcpTool辅助定义时间工具
const timeTool = mcpTool({
name: 'get_time',
description: '获取当前时间,可选地为特定时区',
schema: z.object({
timezone: z
.string()
.optional()
.describe('要获取时间的时区(可选)'),
}),
handler: async (args) => {
return `当前时间${args.timezone ? `在${args.timezone}` : ''}:${new Date().toLocaleString()}`
},
})
// 创建带有天气工具的第一个MCP客户端
const weatherClient = new MCPClient({
endpoint: '/weather-mcp',
tools: [weatherTool],
serverName: 'weather-mcp-server',
serverVersion: '1.0.0',
})
// 创建带有计算器工具的第二个MCP客户端
const calculatorClient = new MCPClient({
endpoint: '/calculator-mcp',
tools: [calculatorTool],
serverName: 'calculator-mcp-server',
serverVersion: '1.0.0',
})
// 创建带有时间工具的第三个MCP客户端
const timeClient = new MCPClient({
endpoint: '/time-mcp',
tools: [timeTool],
serverName: 'time-mcp-server',
serverVersion: '1.0.0',
})
// 在全局JSON解析器之前挂载MCP路由器
app.use('/weather-mcp', weatherClient.middleware())
app.use('/calculator-mcp', calculatorClient.middleware())
app.use('/time-mcp', timeClient.middleware())
// 在代理路由之后应用全局JSON解析器
app.use(express.json())
// 启动服务器
const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3000
app.listen(PORT, () => {
console.log(`多个MCP服务器正在端口${PORT}上运行`)
console.log(`天气MCP:http://localhost:${PORT}/weather-mcp/sse`)
console.log(`计算器MCP:http://localhost:${PORT}/calculator-mcp/sse`)
console.log(`时间MCP:http://localhost:${PORT}/time-mcp/sse`)
})
// examples/with-langchain/src/index.ts
import { MultiServerMCPClient } from '@langchain/mcp-adapters'
import { ChatAnthropic } from '@langchain/anthropic'
import { createReactAgent } from '@langchain/langgraph/prebuilt' // 错误
import dotenv from 'dotenv'
dotenv.config()
async function runLangchainMcpExample() {
console.log('正在使用MCP适配器初始化LangChain...')
const model = new ChatAnthropic({
model: 'claude-3-5-sonnet-20240620',
temperature: 0,
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
})
// 仅保留mcpServers映射的构造函数
const mcpClient = new MultiServerMCPClient({
googleMapsServer: {
// 直接映射服务器
transport: 'sse',
url: 'http://localhost:3000/mcp/sse',
useNodeEventSource: true,
reconnect: {
enabled: true,
maxAttempts: 3,
delayMs: 1000,
},
},
})
console.log('正在通过express适配器从MCP服务器加载工具...')
// 保持getTools调用带选项
const tools = (await Promise.race([
mcpClient.getTools(),
new Promise((_, reject) =>
setTimeout(
() =>
reject(new Error('超时:未能在15秒内加载工具')),
15000,
),
),
])) as Awaited<ReturnType<typeof mcpClient.getTools>>
if (tools.length === 0) {
console.error('未加载任何工具...')
await mcpClient.close()
return
}
console.log(
`加载了${tools.length}个工具:`,
tools.map((t) => t.name).join(', '),
)
const agent = await createReactAgent({
llm: model,
tools,
})
const messages = [
{
role: 'system',
content:
'您是一个有用的助手。使用工具来回答用户的问题。',
},
{
role: 'user',
content: `旧金山现在的天气如何?`,
},
]
let inputs = { messages }
// console.log(`现在一切都好,开始流式传输!: `, inputs);
// await new Promise((resolve) => setImmediate(resolve));
const eventStream = await agent.streamEvents(inputs, {
version: 'v2',
// signal: localController.signal, // <--- 必须传递localController!
})
// --- 调用保持不变 ---
for await (const event of eventStream) {
if (event.event === 'on_chat_model_stream') {
console.log('聊天模型流')
console.log(event.data.chunk.content[0]?.text)
} else if (event.event === 'on_tool_start') {
console.log('工具开始')
console.log(JSON.stringify(event, null, 2))
} else if (event.event === 'on_tool_end') {
console.log('工具结束')
console.log(JSON.stringify(event, null, 2))
}
}
// console.log("\n关闭MCP客户端连接...");
// await mcpClient.close();
// console.log("MCP客户端已关闭。");
// throw new Error("测试错误");
}
runLangchainMcpExample()
这里是一个完整的示例,使用mcpTool辅助创建带Zod模式的类型安全MCP工具:
// examples/with-express/src/index.ts
import express from 'express'
import cors from 'cors'
import { MCPClient, mcpTool } from 'mcp-express-adapter'
import { z } from 'zod'
import dotenv from 'dotenv'
// 加载环境变量
dotenv.config()
// 创建Express应用
const app = express()
app.use(cors())
// 使用增强的mcpTool辅助定义天气工具
const weatherTool = mcpTool({
name: 'get_weather',
description: '获取某个地点的当前天气',
schema: z.object({
location: z.string().describe('要获取天气的地点'),
}),
// 定义输出模式
outputSchema: z
.object({
temperature: z.number().describe('当前温度,单位为°F'),
condition: z.string().describe('天气状况(例如,晴朗,下雨)'),
humidity: z.number().describe('湿度百分比'),
location: z.string().describe('此天气信息对应的地点'),
})
.describe('请求地点的天气信息'),
// 简单返回数据 - mcpTool处理MCP格式化
handler: async (args) => {
console.log(`[WeatherTool] 被调用,地点:${args.location}`)
// 返回一个匹配我们输出模式的对象
return {
temperature: 72,
condition: '晴朗',
humidity: 45,
location: args.location,
}
},
})
// 添加一个计算器工具,简单数字输出
const calculatorTool = mcpTool({
name: 'calculator',
description: '计算两个数字之和',
schema: z.object({
a: z.number().describe('第一个数字'),
b: z.number().describe('第二个数字'),
}),
// 输出只是一个数字
outputSchema: z.number().describe('两个输入数字之和'),
// 简单返回总和 - 不需要为MCP格式化
handler: async (args) => {
console.log(`[CalculatorTool] 被调用,参数:${args.a}, ${args.b}`)
return args.a + args.b
},
})
// 添加一个返回数组的工具
const listTool = mcpTool({
name: 'generate_list',
description: '根据类别生成项目列表',
schema: z.object({
category: z
.string()
.describe('要生成项目的类别(例如,水果,颜色)'),
count: z
.number()
.optional()
.describe('要生成的项目数量(默认:3)'),
}),
// 输出是一串字符串
outputSchema: z
.array(z.string())
.describe('生成的类别中的项目列表'),
handler: async (args) => {
const count = args.count || 3
console.log(
`[ListTool] 正在生成${count}个${args.category}类别的项目`,
)
// 根据类别生成样本数据
const items: Record<string, string[]> = {
fruits: ['苹果', '香蕉', '橙子', '葡萄', '草莓'],
colors: ['红色', '蓝色', '绿色', '黄色', '紫色'],
animals: ['狗', '猫', '大象', '老虎', '企鹅'],
}
const categoryItems = items[args.category.toLowerCase()] || [
'项目1',
'项目2',
'项目3',
'项目4',
'项目5',
]
return categoryItems.slice(0, count)
},
})
// 添加一个不指定outputSchema的工具(期望返回字符串)
const greetingTool = mcpTool({
name: 'greeting',
description: '获取个性化的问候语',
schema: z.object({
name: z.string().describe('要问候的名字'),
formal: z.boolean().optional().describe('是否使用正式语言'),
}),
// 不需要outputSchema,只需返回一个字符串
handler: async (args) => {
const greeting = args.formal
? `您好,${args.name}。我如何为您效劳?`
: `嘿,${args.name}!你好吗?`
console.log(
`[GreetingTool] 为${args.name}生成问候语(正式:${args.formal || false})`,
)
return greeting
},
})
// 添加一个检查认证的受保护工具
const protectedTool = mcpTool({
name: 'get_passcode',
description: '获取用户的通行码',
schema: z.object({
name: z.string().describe('用户的姓名'),
}),
// 在处理器中实现认证检查
handler: async (args, context) => {
console.log(`[ProtectedTool] 被调用,姓名:${args.name}`)
// 检查授权头部
const authHeader = context?.headers?.authorization || ''
console.log(context)
console.log(`[ProtectedTool] 认证头部:${authHeader}`)
// 检查匹配“000000”的bearer token
const validToken = 'Bearer 000000'
if (!authHeader || authHeader !== validToken) {
// 返回未经授权访问的错误
throw new Error('未经授权:无效或缺少认证令牌')
}
// 如果已授权,返回受保护的数据
return `受保护的数据ID:${args.name}`
},
})
// 如果为true将显示调试日志,要禁用设置NODE_ENV=production
const debugMode = process.env.NODE_ENV === 'development'
// 创建MCP客户端
const mcpClient = new MCPClient({
endpoint: '/mcp',
tools: [weatherTool, calculatorTool, listTool, greetingTool, protectedTool],
serverName: 'my-mcp-server',
serverVersion: '1.0.0',
debug: debugMode, // 只有当传递--debug标志时才启用调试日志
})
// 显示客户端元数据
const metadata = mcpClient.getMetadata()
console.log('已创建具有以下配置的MCP客户端:')
console.log(`- 端点:${metadata.endpoint}`)
console.log(`- 服务器:${metadata.serverName} v${metadata.serverVersion}`)
console.log(`- 工具:${metadata.tools.map((tool) => tool.name).join(', ')}`)
// 挂载MCP路由器
app.use('/mcp', mcpClient.middleware())
// 应用其他路由的JSON解析器
app.use(express.json