使用说明
🧩 MCP演示(模型+工具+上下文)
使用OpenAI函数调用的MCP的轻量级Node.js演示,展示LLM如何使用具有适当上下文管理的外部工具。
📦 项目结构
mcp-demo/
├── package.json
├── src/
│ ├── app.js # Entry point
│ ├── llm/
│ │ └── openai.js # OpenAI Chat Completions API
│ ├── mcp/
│ │ ├── context.js # Context builder + tool result enrichment
│ │ ├── orchestrator.js # MCP main workflow (runMCP)
│ │ ├── executor.js # Executes validated tool calls
│ │ └── toolRegistry.js # Tool list → OpenAI tool schemas
│ ├── tools/
│ │ ├── revenueTool.js # get_revenue function
│ │ └── timeTool.js # get_time function
│ └── utils/
│ └── logger.js # Logging utilities
└── README.md
🚀 快速开始
1.安装依赖项
npm install
2.设置OpenAI API密钥
export OPENAI_API_KEY=your_api_key_here
3.运行演示
演示1
node src/app.js "What is the revenue for 02/2025?"
预期产量:
LLM回复:
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_",
"type": "function",
"function": {
"name": "get_revenue",
"arguments": {
"month": "02/2025"
}
}
}
],
"refusal": null,
"annotations": []
},
"logprobs": null,
"finish_reason": "tool_calls"
}
工具调用后的LLM响应:
{
"index": 0,
"message": {
"role": "assistant",
"content": "The revenue for February 2025 is 150,000.",
"finish_reason": "stop"
},
...
}
最终答案:
The revenue for February 2025 is 150,000.
🧠 核心组件
src/mcp/context.js
管理上下文信息和工具结果丰富:
-
buildInitialContext({ userId, locale, appState })- 使用用户元数据、业务规则和工具使用示例构建系统消息
- 返回LLM指令的区域设置感知上下文
-
enrichConversationWithToolResult(conversation, { toolName, result, toolCallId })- 将工具响应附加到对话历史记录中
- 为多回转工具的使用保持适当的信息链
-
resolveLocaleText(locale, key)- 检索特定于区域设置的消息(越南语、英语)
- 支持新语言的可扩展性
src/mcp/orchestrator.js
主MCP工作流程(runMCP):
- 构建上下文 -使用用户区域设置和业务规则初始化系统消息
- 致电LLM -使用可用工具发送用户查询
- 检查工具调用 -如果LLM想要使用工具:
- 提取工具名称和参数
- 执行工具
- 将工具结果附加到对话中
- 再次调用LLM并返回结果
- 返回答案 -LLM的最终文本回复
src/mcp/executor.js
工具执行管道:
- 从注册表中检索工具定义
- 验证Zod模式的参数
- 执行工具处理程序
- 返回结果或错误
src/mcp/toolRegistry.js
工具管理:
- 维护所有可用工具的列表
- 将工具定义转换为与OpenAI兼容的模式
- 按名称提供工具查找
src/tools/revenueTool.js
收入数据工具示例:
{
name: "get_revenue",
description: "Get revenue for a specific month in MM/YYYY format",
schema: z.object({
month: z.string().describe("Month in MM/YYYY format (e.g., '02/2025')")
}),
handler: async ({ month }) => { /* returns revenue data */ }
}
src/llm/openai.js
OpenAI集成:
- 配置GPT-4o-mini型号
- 处理工具架构格式
- 管理函数调用流
🧪 演示使用示例
示例1:按月查询收入
node src/app.js "What is the revenue for 02/2025?"
流量:
- LLM标识月份
02/2025查询 - 呼叫
get_revenue和{"month":"02/2025"} - 返回格式化收入
示例2:当前时间查询
node src/app.js "What is today's date?"
流量:
- LLM识别日期查询并建议呼叫
get_time工具 - 呼叫
get_time工具 - 返回当前日期响应
示例3:当月收入查询(多个工具调用)
node src/app.js "What is the revenue for this month?"
预期:
- LLM建议致电
get_time获取当前月份的工具 - 呼叫
get_time工具,返回当月。LLM建议致电get_revenue带有当月参数的工具 - 返回当月收入
🛠️ MCP的工作原理:流程
User Input
↓
[1] buildInitialContext
↓
[2] callLLM with tools
↓
└─→ LLM response: tool_calls?
├─ YES: [3] executeTool
│ ↓
│ [4] enrichConversationWithToolResult
│ ↓
│ [2] callLLM again (with tool result)
│
└─ NO: Return final_response
🛡️ 主要特点
✅ 上下文注入
系统上下文包括:
- 用户区域设置和首选项
- 业务规则(例如,月格式要求)
- 工具使用示例
- 元数据(用户ID、环境)
✅ 模式验证
- Zod模式强制类型安全
- 执行前拒绝了无效参数
- 清除错误消息
✅ 工具结果反馈循环
- 工具输出自动附加到对话中
- LLM可以对工具结果进行推理
- 支持多回转刀具使用
✅ 多语言支持
- 越南语和英语提示
- 可扩展的区域设置解析器
- 按区域设置执行业务规则
🔧 扩展演示
添加新工具
- 创建
src/tools/myTool.js:
import { z } from "zod";
export const myTool = {
name: "my_function",
description: "What this tool does",
schema: z.object({
param1: z.string().describe("Parameter description"),
}),
handler: async ({ param1 }) => {
return { result: "data" };
},
};
- 注册于
src/mcp/toolRegistry.js:
import { myTool } from "../tools/myTool.js";
export const tools = [revenueTool, timeTool, myTool];
改进月度验证
在 revenueTool.js,添加正则表达式验证:
schema: z.object({
month: z
.string()
.regex(/^(0[1-9]|1[0-2])\/\d{4}$/, "Format must be MM/YYYY")
.describe("Month in MM/YYYY format"),
})
连接真实数据库
修改 revenueTool.handler:
handler: async ({ month }) => {
const data = await db.query("SELECT * FROM revenue WHERE month = ?", [month]);
return { month, revenue: data[0].revenue };
}
添加身份验证
在 orchestrator.js:
const context = buildInitialContext({
userId: req.user.id,
locale: req.user.locale,
appState: { environment: "prod" }
});
📊 架构图
┌─────────────────────────────────────────────────────┐
│ User Query (Vietnamese/English) │
└──────────────────┬──────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ orchestrator.js: runMCP() │
│ - buildInitialContext() │
│ - Add system message + user query │
└──────────────────┬──────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ openai.js: callLLM() │
│ - Send to GPT-4o-mini with tool schemas │
└──────────────────┬──────────────────────────────────┘
↓
┌─────────┴─────────┐
↓ ↓
[Tool Calls] [Direct Answer]
↓ ↓
executor.js RETURN
- Resolve tool
- Validate args
- Execute handler
↓
enrichConversationWithToolResult()
↓
callLLM() again
↓
Return final answer
🧾 故障排除
错误: Invalid input expected string, received undefined
原因: LLM未提供所需参数
解决方案: 用清晰的例子提高系统提示
错误: Invalid parameter: messages with role 'tool' must be a response to preceeding message with tool_calls
原因: 消息链断开,工具消息没有匹配的助手消息
解决方案: 确保 enrichConversationWithToolResult 被调用时具有完整的对话历史记录,包括助理消息
错误: 401 Unauthorized 调用OpenAI时
原因: API密钥无效或丢失
解决方案:
export OPENAI_API_KEY=sk-...
echo $OPENAI_API_KEY # verify it's set
LLM不使用工具
原因: 工具架构格式不正确或示例不清楚
解决方案:
- 检查中的Zod模式转换
toolRegistry.js - 在中添加明确的工具使用示例
context.js系统消息 - 验证OpenAI请求中是否包含工具名称
📝 建议的后续步骤
- []添加持久对话历史记录(数据库)
- []为API调用实现速率限制
- []添加工具执行的监控/日志记录
- []为MCP流创建集成测试
- []添加TypeScript以确保类型安全
- []记录每次工具调用的成本估算
- []为失败的工具调用实现重试逻辑
- []添加对流式响应的支持
🏗️ 技术栈
- 运行时间: Node.js v24+
- LLM OpenAI GPT-4o-mini
- 架构验证: 萨德
- 环境: Dotenv。
📄 许可证
麻省理工学院
🤝 贡献
欢迎投稿!遵循以下模式:
- 使用Zod进行模式定义
- 保持工具处理程序的纯净和无副作用
- 测试工具输出与预期模式匹配
- 用新的工具示例更新README
制作❤️ 用于MCP演示
