Ở phần 2 này chúng ta hãy cùng nhau tạo một máy khách đơn giản có thể tương tác qua công cụ dòng lệnh (cli), tức là viết một ứng dụng dòng lệnh. Đầu vào là yêu cầu của người dùng và kết quả đầu ra là ứng dụng gọi đúng hàm có trong MCP Server rồi phản hồi kết quả. Để cho đơn giản, hãy tận dụng máy chủ MCP 2coffee.dev mà chúng ta đã tạo ở các bài viết trường, các bạn có thể tham khảo mã nguồn tại Tạo một MCP Server đơn giản cho 2coffee.dev.
Trên tài liệu của Anthropic có bài viết hướng dẫn cách tạo MCP client tận dụng thư viện @anthropic-ai/sdk của họ. Tuy nhiên thư viện này chỉ tương thích với các model của Anthropic, cho nên chúng ta không làm theo cách đó mà tận dụng các mô hình LLMs cục bộ thông qua công cụ LM Studio. LM Studio hỗ trợ một số API tuân theo chuẩn của OpenAI cho nên hoàn toàn có thể sử dụng thư viện openai thay thế cho @anthropic-ai/sdk.
Đầu tiên khởi động LM Studio lên, vào Developer, chọn một model bạn thích và khởi động server. Ở đây mình chọn mô hình google/gemma-3n-e4b.

Tiếp theo cần khởi tạo kết nối từ máy khách đến máy chủ MCP.
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
const transport = new StdioClientTransport({
command: 'node',
args: ['build/index.js']
});
const client = new Client({
name: 'mcp-2coffee-client',
version: '1.0.0'
});
await client.connect(transport);
Khởi tạo kết nối đến máy chủ LLMs.
import OpenAI from 'openai';
const MODEL = 'google/gemma-3n-e4b';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY ?? 'lmstudio',
baseURL: process.env.OPENAI_BASE_URL ?? 'http://127.0.0.1:1234/v1',
});
Hãy nhớ lại các bước giao tiếp giữa client và server trong bài viết trước. Ngay sau khi người dùng ra lệnh, gửi yêu cầu đó đến máy chủ LLMs kèm theo danh sách tools mà máy chủ MCP cung cấp. Chờ mô hình phản hồi lại kết quả mà có quyết định gọi tools hay là không.
Tạo ra một hàm có tên là chatOne, bên trong chứa logic chính của ứng dụng.
async function chatOnce(userInput: string) {
const first = await openai.chat.completions.create({
model: MODEL,
messages: [{ role: 'user', content: userInput }],
tools: openaiTools,
tool_choice: 'auto',
});
}
Với openaiTools là danh sách tools của MCP. Lưu ý rằng giá trị của tools phải tuân thủ theo API của OpenAI, vì vậy sau khi lấy được danh sách tools từ máy chủ MCP về thì phải qua bước định dạng lại dữ liệu.
function toOpenAITools(tools: Awaited<ReturnType<typeof client.listTools>>['tools']) {
return tools.map(t => ({
type: 'function' as const,
function: {
name: t.name,
description: t.description ?? t.title ?? '',
parameters: (t.inputSchema as any) ?? { type: 'object', properties: {} },
},
}));
}
const { tools: mcpTools } = await client.listTools();
const openaiTools = toOpenAITools(mcpTools);
Sau khi mô hình phản hồi, giả sử yêu cầu phù hợp với tools thì tiến hành gọi lên MCP server bằng cách gọi hàm callTool từ client.
async function executeToolCall(call: any) {
const name = call.function.name;
const args = call.function.arguments ? JSON.parse(call.function.arguments) : {};
const result = await client.callTool({ name, arguments: args });
const content = result.content as any;
return { tool_call_id: call.id, content };
}
const msg = first.choices[0].message;
// Nếu không có tool call -> trả lời luôn
if (!msg.tool_calls || msg.tool_calls.length === 0) {
console.log('[assistant]', msg.content);
return;
}
const toolResults = await executeToolCall(msg.tool_calls[0]);
toolResults lúc này chứa kết quả mà máy chủ MCP trả về. Lúc này bạn có thể trả về kết quả đó cho người dùng hoặc làm thêm một bước nữa là nhờ LLMs trả lời một cách tự nhiên hơn bằng kỹ thuật prompts.
const second = await openai.chat.completions.create({
model: MODEL,
messages: [
{ role: 'user', content: userInput },
{ role: 'tool', tool_call_id: toolResults.tool_call_id, content: toolResults.content },
{ role: 'user', content: 'Trả lời vắn tắt gồm title, url, summary của mỗi bài viết.'}
],
});
Cuối cùng thêm một vài dòng lệnh để tạo tương tác với cli.
import readline from 'readline';
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('> Bạn muốn tìm kiếm gì? ', (input) => {
chatOnce(input);
rl.close();
});
Build ứng dụng.
$ npm run build
Chạy nó.
$ node build/client.js
Gõ vào điều gì đó và xem kết quả.

Vậy là xong, rất đơn giản phải không nào? Hy vọng qua bài viết này bạn đọc hình dung ra được các bước cần thiết để tạo một máy khách MCP. Bạn đọc tham khảo mã nguồn đầy đủ tại Github.