- 需要已经安装nodejs以及python环境
- 准备好任意一个大模型的api-key和base_url
- 和上节一样,我们先试试用tool来调用当前时间,使用mcp-server-time 来做演示。
- 使用pip安装一下它。
pip install mcp-server-time
- 初始化一个Nodejs项目,安装mcp ts sdk / openai / node types
npm init -y
npm install @modelcontextprotocol/sdk
npm install openai
npm i --save-dev @types/node
- 全局安装ts,初始化ts项目
npm install -g typescript
tsc --init
- 安装完成后,我们可以使用下面的代码来测试一下。
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport, StdioServerParameters} from "@modelcontextprotocol/sdk/client/stdio.js";
const params: StdioServerParameters = {
command: "python", // 建议改成你的python绝对路径
args: [
"-m",
"mcp_server_time",
"--local-timezone=Asia/Shanghai"
]
}
const transport = new StdioClientTransport(params);
const client = new Client(
{
name: "example-client",
version: "1.0.0"
}
);
await client.connect(transport);
// List tool
const tools = await client.listTools();
console.log("===tools===")
console.log(tools)
console.log("==========")
// Call a tool
const result = await client.callTool({
name: "get_current_time",
arguments: {
timezone: "Asia/Shanghai"
}
});
if (Array.isArray(result.content) && result.content.length > 0) {
let data: {type: string, text: string} = result.content[0];
console.log("===result ===")
console.log(data.text)
console.log("===========")
}
await client.close()
- 输出结果,可以看到能够准确获取当前时间
===tools===
{
tools: [
{
name: 'get_current_time',
description: 'Get current time in a specific timezones',
inputSchema: [Object]
},
{
name: 'convert_time',
description: 'Convert time between timezones',
inputSchema: [Object]
}
]
}
==========
===result ===
{
"timezone": "Asia/Shanghai",
"datetime": "2025-06-05T10:22:33+08:00",
"is_dst": false
}
===========
- 不过,上面调用工具这块,不够优雅。只能手动调用固定的tool,这部分应该让AI来完成,就像我们上一节做的那样,我们提供工具和命令,AI执行工具以及获取结果。我们应该将工具和要求传给AI,我们可以回忆一下之前tool_call时,是怎么做的。于是有了下面的代码:
import {Client} from "@modelcontextprotocol/sdk/client/index.js"
import {StdioClientTransport, StdioServerParameters} from "@modelcontextprotocol/sdk/client/stdio.js"
import {OpenAI} from "openai"
import {
ChatCompletionCreateParamsBase,
ChatCompletionMessageParam,
ChatCompletionTool,
ChatCompletion
} from "openai/src/resources/chat/completions.js"
const params: StdioServerParameters = {
command: "python", // 建议改成你的python绝对路径
args: [
"-m",
"mcp_server_time",
"--local-timezone=Asia/Shanghai"
]
};
class MCPClient {
#transport = new StdioClientTransport(params);
#client = new Client({
name: "Demo",
version: "0.1.0"
})
#aiSession = new OpenAI({
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
apiKey: process.env["DASHSCOPE_API_KEY"]
});
#model = "qwen-max" // 你可以换成其他的
async connect() {
await this.#client.connect(this.#transport)
}
async processQuery(query: string, system_prompt: string ="You are a helpful assistant.") {
let messages: Array<ChatCompletionMessageParam> = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": query}
];
let toolResponse = await this.#client.listTools()
// 生成 tool call 的描述信息
let availableTools: ChatCompletionTool[] = toolResponse.tools.map((tool)=>{
return {
type: "function",
function: {
name: tool.name,
description: tool.description ?? "",
parameters: tool.inputSchema
}
}
});
// console.log("await Tools:", availableTools);
// 调用模型
let clientParams: ChatCompletionCreateParamsBase = {
messages: messages,
model: this.#model,
tools: availableTools
}
let response = await this.#aiSession.chat.completions.create(clientParams) as ChatCompletion;
// console.log(response)
if (response.choices.length > 0) {
let choice = response.choices[0];
if (choice.finish_reason == "tool_calls" && choice.message.tool_calls?.length) {
// 如果AI决定执行tool_call
let toolCall = choice.message.tool_calls[0];
let toolName = toolCall.function.name;
let toolArgs = JSON.parse(toolCall.function.arguments);
// 将结果加入message
messages.push({
role: "assistant",
tool_calls: [toolCall]
});
// 执行工具
let toolResult = await this.#client.callTool({
name: toolName,
arguments: toolArgs
});
let toolText: string = "";
if (Array.isArray(toolResult.content) && toolResult.content.length > 0) {
toolText = toolResult.content[0].text;
}
// 将工具执行结果丢给AI处理
messages.push({
role: "tool",
content: toolText,
tool_call_id: toolCall.id,
});
// 将上面的结果再返回给 AI用于生产最终的结果
let aiResponse = await this.#aiSession.chat.completions.create({
messages: messages,
model: this.#model,
});
let content = aiResponse.choices?.[0].message.content?? "";
return content
} else {
let content = response.choices?.[0].message.content?? "";
return content
}
} else {
return "";
}
}
async disconect() {
await this.#client.close();
}
}
let mcpClient = new MCPClient();
await mcpClient.connect();
let response = await mcpClient.processQuery("当前时间是?")
console.log(response)
await mcpClient.disconect();
根据您提供的信息,当前时间是2025年6月5日12点53分28秒,时区为亚洲/上海(+08:00),并且没有实行夏令时。请注意,这个时间是您所提供数据中的时间,并非实时获取的时间。
- 上面输入时写死的,如果还要更加灵活的话,我们可以支持用户自定义输入,并且弄一个循环体,并且利用messages来控制上下文,于是就有了下面的代码。
import {Client} from "@modelcontextprotocol/sdk/client/index.js"
import {StdioClientTransport, StdioServerParameters} from "@modelcontextprotocol/sdk/client/stdio.js"
import {OpenAI} from "openai"
import * as readline from "node:readline/promises";
import {stdin, stdout} from "node:process"
import {
ChatCompletionCreateParamsBase,
ChatCompletionMessageParam,
ChatCompletionTool,
ChatCompletion
} from "openai/src/resources/chat/completions.js"
const params: StdioServerParameters = {
command: "C:\\Users\\37104\\anaconda3\\envs\\tools\\python",
args: [
"-m",
"mcp_server_time",
"--local-timezone=Asia/Shanghai"
]
};
class MCPClient {
#transport = new StdioClientTransport(params);
#client = new Client({
name: "Demo",
version: "0.1.0"
})
#aiSession = new OpenAI({
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
apiKey: process.env["DASHSCOPE_API_KEY"]
});
#model = "qwen-max" // 你可以换成其他的
async connect() {
await this.#client.connect(this.#transport)
}
async processQuery(messages: Array<ChatCompletionMessageParam>) {
let toolResponse = await this.#client.listTools()
// 生成 tool call 的描述信息
let availableTools: ChatCompletionTool[] = toolResponse.tools.map((tool)=>{
return {
type: "function",
function: {
name: tool.name,
description: tool.description ?? "",
parameters: tool.inputSchema
}
}
});
// console.log("await Tools:", availableTools);
// 调用模型
let clientParams: ChatCompletionCreateParamsBase = {
messages: messages,
model: this.#model,
tools: availableTools
}
let response = await this.#aiSession.chat.completions.create(clientParams) as ChatCompletion;
// console.log(response)
if (response.choices.length > 0) {
let choice = response.choices[0];
if (choice.finish_reason == "tool_calls" && choice.message.tool_calls?.length) {
// 如果AI决定执行tool_call
let toolCall = choice.message.tool_calls[0];
let toolName = toolCall.function.name;
let toolArgs = JSON.parse(toolCall.function.arguments);
// 将结果加入message
messages.push({
role: "assistant",
tool_calls: [toolCall]
});
// 执行工具
let toolResult = await this.#client.callTool({
name: toolName,
arguments: toolArgs
});
let toolText: string = "";
if (Array.isArray(toolResult.content) && toolResult.content.length > 0) {
toolText = toolResult.content[0].text;
}
// 将工具执行结果丢给AI处理
messages.push({
role: "tool",
content: toolText,
tool_call_id: toolCall.id,
});
// 将上面的结果再返回给 AI用于生产最终的结果
let aiResponse = await this.#aiSession.chat.completions.create({
messages: messages,
model: this.#model,
});
let content = aiResponse.choices?.[0].message.content?? "";
return content
} else {
let content = response.choices?.[0].message.content?? "";
return content
}
} else {
return "";
}
}
async disconect() {
await this.#client.close();
}
}
async function main() {
const system_prompt: string ="You are a helpful assistant."
let messages: Array<ChatCompletionMessageParam> = [
{role: "system", content: system_prompt},
];
let mcpClient = new MCPClient();
await mcpClient.connect();
console.log("=".repeat(20))
console.log("欢迎使用AI聊天机器人,你可以问我当前时间相关信息,输入exit或者quit退出,输入clear清空历史记录")
console.log("=".repeat(20))
const face = readline.createInterface({input: stdin, output: stdout});
while (true) {
const query = await face.question("input: ")
messages.push({role: "user", content: query});
if (["exit", "exit()"].includes(query)) {
console.log("output: 即将退出");
break;
}
if (["clear", "clear()"].includes(query)) {
messages = messages.slice(0, 1);
console.log("messages length: ", messages.length)
console.log("output: 已清理历史对话信息。")
}
let response = await mcpClient.processQuery(messages)
messages.push({role: "assistant", content: response});
console.log("output:", response);
}
face.close();
await mcpClient.disconect();
}
await main();
====================
欢迎使用AI聊天机器人,你可以问我当前时间相关信息,输入exit或者quit退出,输入clear清空历史记录
====================
input: 你好
output: 你好!有什么我能帮助你的吗?
input: 你叫啥?
output: 您好,我是通义千问,是阿里云开发的一款人工智能助手。有什么我可以帮助您的吗?
input: 现在几点了?
output: 根据您提供的信息,现在是北京时间2025年6月5日18点16分。
input: exit
outptu: 即将退出
- 可以看到,AI能够在需要的时候,正确调用获取当前时间tool。不需要的时候,也可以进入闲聊模式。
- 那么,动动手吧,试试上面的代码,顺便把上节的
playwright
操作浏览器这个功能,也用代码实现一下。