Cách Xây Dựng và Triển Khai Máy Chủ MCP Tùy Chỉnh Trong 10 Phút

💡Bạn muốn tạo quy trình làm việc AI Agentic của riêng mình mà không cần mã lập trình? Bạn có thể dễ dàng tạo quy trình AI với Anakin AI mà không cần kiến thức lập trình. Kết nối với các API LLM như: GPT-4, Claude 3.5 Sonnet, Uncensored

Build APIs Faster & Together in Apidog

Cách Xây Dựng và Triển Khai Máy Chủ MCP Tùy Chỉnh Trong 10 Phút

Start for free
Inhalte
💡
Bạn muốn tạo quy trình làm việc AI Agentic của riêng mình mà không cần mã lập trình?

Bạn có thể dễ dàng tạo quy trình AI với Anakin AI mà không cần kiến thức lập trình. Kết nối với các API LLM như: GPT-4, Claude 3.5 Sonnet, Uncensored Dolphin-Mixtral, Stable Diffusion, DALLE, Web Scraping.... thành Một Quy trình!

Quên đi việc lập trình phức tạp, tự động hóa công việc hàng ngày của bạn với Anakin AI!

Trong một thời gian giới hạn, bạn cũng có thể sử dụng Google Gemini 1.5 và Stable Diffusion miễn phí!
Dễ dàng xây dựng quy trình AI Agentic với Anakin AI!
Dễ dàng xây dựng quy trình AI Agentic với Anakin AI

Giới thiệu

Giao thức Ngữ cảnh Mô hình (MCP) đại diện cho một bước tiến lớn trong hệ sinh thái AI, cung cấp một cách giao tiếp chuẩn hóa với các mô hình ngôn ngữ lớn. Thay vì mỗi nền tảng AI thực hiện định dạng riêng cho tin nhắn của mình, MCP nhằm mục đích cung cấp một giao diện đồng nhất cho các yêu cầu, phản hồi và gọi hàm qua nhiều mô hình và nền tảng khác nhau.

Khi giao thức tự phát triển, việc xây dựng một máy chủ tương thích với MCP cơ bản có thể khá đơn giản. Trong hướng dẫn này, tôi sẽ hướng dẫn bạn cách tạo một máy chủ đơn giản nhưng hoạt động tốt tuân theo các nguyên tắc cốt lõi của việc xử lý tin nhắn trong các hệ thống AI hiện đại. Triển khai của chúng tôi sẽ tập trung vào việc tạo nền tảng mà bạn có thể mở rộng thêm những tính năng nâng cao hơn.

10 phút có đủ thời gian không? Đối với một hệ thống sẵn sàng sản xuất, chắc chắn là không. Nhưng đối với một nguyên mẫu hoạt động cho thấy các khái niệm chính? Tuyệt đối. Hãy bắt đầu nào!

Yêu cầu

Trước khi bắt đầu, bạn cần:

  • Node.js (v16+) đã được cài đặt trên hệ thống của bạn
  • Kiến thức cơ bản về JavaScript/TypeScript
  • Quen thuộc với Express.js hoặc các framework web tương tự
  • Một trình soạn thảo mã (khuyên dùng VS Code)
  • Truy cập terminal/dòng lệnh
  • Quản lý gói npm hoặc yarn

Bước 1: Thiết lập Dự án của Bạn (2 phút)

Đầu tiên, hãy tạo một thư mục mới và khởi tạo dự án của chúng tôi:

mkdir mcp-server
cd mcp-server
npm init -y

Bây giờ, cài đặt các phụ thuộc cần thiết:

npm install express cors typescript ts-node @types/node @types/express @types/cors
npm install --save-dev nodemon

Tạo một tệp cấu hình TypeScript:

npx tsc --init

Chỉnh sửa tệp tsconfig.json được tạo ra để bao gồm các thiết lập quan trọng này:

{
  "compilerOptions": {
    "target": "es2018",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true
  },
  "include": ["src/**/*"]
}

Cập nhật phần scripts trong tệp package.json của bạn:

"scripts": {
  "start": "node dist/index.js",
  "dev": "nodemon --exec ts-node src/index.ts",
  "build": "tsc"
}

Bước 2: Tạo Máy chủ Cốt lõi (3 phút)

Tạo thư mục nguồn và tệp máy chủ chính của bạn:

mkdir -p src/handlers
touch src/index.ts
touch src/handlers/messageHandler.ts
touch src/types.ts

Hãy định nghĩa các loại của chúng ta trước trong src/types.ts:

// Cấu trúc tin nhắn cơ bản
export interface Content {
  type: string;
  text?: string;
}

export interface Message {
  role: "user" | "assistant" | "system";
  content: Content[] | string;
}

// Cấu trúc yêu cầu và phản hồi
export interface ModelRequest {
  messages: Message[];
  max_tokens?: number;
  temperature?: number;
  stream?: boolean;
}

export interface ModelResponse {
  message: Message;
}

// Giao diện gọi công cụ
export interface Tool {
  name: string;
  description: string;
  input_schema: {
    type: string;
    properties: Record;
    required?: string[];
  };
}

export interface ToolCall {
  type: "tool_call";
  id: string;
  name: string;
  input: Record;
}

export interface ToolResult {
  type: "tool_result";
  tool_call_id: string;
  content: string;
}

Bây giờ, triển khai máy chủ cơ bản trong src/index.ts:

import express from 'express';
import cors from 'cors';
import { handleMessageRequest } from './handlers/messageHandler';

const app = express();
const PORT = process.env.PORT || 3000;

// Middleware
app.use(cors());
app.use(express.json({ limit: '10mb' }));

// Điểm cuối chính để xử lý tin nhắn
app.post('/v1/messages', handleMessageRequest);

// Điểm cuối kiểm tra sức khỏe
app.get('/health', (req, res) => {
  res.status(200).json({ status: 'ok' });
});

// Bắt đầu máy chủ
app.listen(PORT, () => {
  console.log(`Máy chủ đang chạy trên cổng ${PORT}`);
  console.log(`Kiểm tra sức khỏe: http://localhost:${PORT}/health`);
  console.log(`Điểm cuối tin nhắn: http://localhost:${PORT}/v1/messages`);
});

Kế tiếp, triển khai bộ xử lý tin nhắn trong src/handlers/messageHandler.ts:

import { Request, Response } from 'express';
import { ModelRequest, ModelResponse, Message, Content } from '../types';

export async function handleMessageRequest(req: Request, res: Response) {
  try {
    const request = req.body as ModelRequest;
    
    // Kiểm tra cơ bản
    if (!request.messages || !Array.isArray(request.messages) || request.messages.length === 0) {
      return res.status(400).json({ error: 'Định dạng yêu cầu không hợp lệ. Mảng tin nhắn là bắt buộc.' });
    }
    
    // Ghi lại yêu cầu đến (để gỡ lỗi)
    console.log('Nhận yêu cầu với', request.messages.length, 'tin nhắn');
    
    // Xử lý các tin nhắn
    const response = processMessages(request.messages);
    
    // Trả lại phản hồi
    return res.status(200).json(response);
  } catch (error) {
    console.error('Lỗi khi xử lý yêu cầu:', error);
    return res.status(500).json({ error: 'Lỗi máy chủ nội bộ' });
  }
}

function processMessages(messages: Message[]): ModelResponse {
  // Trích xuất tin nhắn người dùng cuối cùng
  const lastUserMessage = findLastUserMessage(messages);
  
  if (!lastUserMessage) {
    return createErrorResponse("Không tìm thấy tin nhắn người dùng trong cuộc trò chuyện");
  }
  
  const userQuery = extractTextContent(lastUserMessage);
  
  // Logic tạo phản hồi đơn giản
  let responseText = "";
  
  if (userQuery.toLowerCase().includes('hello') || userQuery.toLowerCase().includes('hi')) {
    responseText = "Xin chào! Tôi có thể giúp gì cho bạn hôm nay?";
  } else if (userQuery.toLowerCase().includes('weather')) {
    responseText = "Tôi không có quyền truy cập vào dữ liệu thời tiết theo thời gian thực, nhưng tôi có thể giúp bạn hiểu các mẫu thời tiết nói chung.";
  } else if (userQuery.toLowerCase().includes('time')) {
    responseText = `Thời gian máy chủ hiện tại là ${new Date().toLocaleTimeString()}.`;
  } else {
    responseText = "Tôi đã nhận được tin nhắn của bạn. Đây là phản hồi đơn giản từ máy chủ mô hình.";
  }
  
  // Xây dựng và trả lại phản hồi
  return {
    message: {
      role: "assistant",
      content: [{ 
        type: "text", 
        text: responseText 
      }]
    }
  };
}

function findLastUserMessage(messages: Message[]): Message | undefined {
  // Tìm tin nhắn cuối cùng với vai trò 'user'
  for (let i = messages.length - 1; i >= 0; i--) {
    if (messages[i].role === 'user') {
      return messages[i];
    }
  }
  return undefined;
}

function extractTextContent(message: Message): string {
  if (typeof message.content === 'string') {
    return message.content;
  } else if (Array.isArray(message.content)) {
    return message.content
      .filter(item => item.type === 'text' && item.text)
      .map(item => item.text)
      .join(' ');
  }
  return '';
}

function createErrorResponse(errorMessage: string): ModelResponse {
  return {
    message: {
      role: "assistant",
      content: [{ 
        type: "text", 
        text: `Lỗi: ${errorMessage}` 
      }]
    }
  };
}

Bước 3: Thêm Tính năng Gọi Công cụ (3 phút)

Tạo một tệp mới cho các định nghĩa và triển khai công cụ:

touch src/tools.ts

src/tools.ts:

import { Tool } from './types';

// Các định nghĩa công cụ
export const availableTools: Tool[] = [
  {
    name: "get_current_time",
    description: "Lấy thời gian máy chủ hiện tại",
    input_schema: {
      type: "object",
      properties: {
        timezone: {
          type: "string",
          description: "Múi giờ tuỳ chọn (mặc định là múi giờ của máy chủ)"
        }
      }
    }
  },
  {
    name: "calculate",
    description: "Thực hiện một phép toán toán học",
    input_schema: {
      type: "object",
      properties: {
        expression: {
          type: "string",
          description: "Biểu thức toán học để đánh giá"
        }
      },
      required: ["expression"]
    }
  }
];

// Các triển khai công cụ
export function executeToolCall(name: string, params: Record): string {
  switch (name) {
    case "get_current_time":
      return getTime(params.timezone);
    case "calculate":
      return calculate(params.expression);
    default:
      throw new Error(`Công cụ không xác định: ${name}`);
  }
}

function getTime(timezone?: string): string {
  const options: Intl.DateTimeFormatOptions = { 
    hour: '2-digit', 
    minute: '2-digit', 
    second: '2-digit',
    timeZoneName: 'short' 
  };
  
  try {
    if (timezone) {
      options.timeZone = timezone;
    }
    return new Date().toLocaleTimeString('en-US', options);
  } catch (error) {
    return `${new Date().toLocaleTimeString()} (Thời gian máy chủ)`;
  }
}

function calculate(expression: string): string {
  try {
    // CẢNH BÁO: Trong một ứng dụng thực tế, bạn nên sử dụng phương pháp đánh giá an toàn hơn
    // Đây là một ví dụ đơn giản chỉ để minh hoạ
    const sanitizedExpression = expression.replace(/[^0-9+\-*/().\s]/g, '');
    const result = eval(sanitizedExpression);
    return `${expression} = ${result}`;
  } catch (error) {
    return `Lỗi khi tính toán ${expression}: ${error instanceof Error ? error.message : String(error)}`;
  }
}

Bây giờ, cập nhật bộ xử lý tin nhắn để hỗ trợ gọi công cụ bằng cách chỉnh sửa src/handlers/messageHandler.ts:

// Thêm các import này ở đầu
import { availableTools, executeToolCall } from '../tools';
import { ToolCall, ToolResult } from '../types';

// Cập nhật hàm handleMessageRequest
export async function handleMessageRequest(req: Request, res: Response) {
  try {
    const request = req.body as ModelRequest;
    
    // Kiểm tra cơ bản
    if (!request.messages || !Array.isArray(request.messages) || request.messages.length === 0) {
      return res.status(400).json({ error: 'Định dạng yêu cầu không hợp lệ. Mảng tin nhắn là bắt buộc.' });
    }
    
    // Kiểm tra xem đây có phải là yêu cầu với kết quả công cụ không
    const lastMessage = request.messages[request.messages.length - 1];
    if (lastMessage.role === 'assistant' && Array.isArray(lastMessage.content)) {
      const toolCalls = lastMessage.content.filter(item => 
        item.type === 'tool_call') as ToolCall[];
      
      if (toolCalls.length > 0) {
        // Đây là một yêu cầu tiếp theo với kết quả công cụ
        return handleToolResultsResponse(request, res);
      }
    }
    
    // Xử lý như một tin nhắn thông thường
    const response = processMessages(request.messages);
    
    // Trả lại phản hồi
    return res.status(200).json(response);
  } catch (error) {
    console.error('Lỗi khi xử lý yêu cầu:', error);
    return res.status(500).json({ error: 'Lỗi máy chủ nội bộ' });
  }
}

// Thêm hàm này để xử lý gọi công cụ
function handleToolResultsResponse(request: ModelRequest, res: Response) {
  const messages = request.messages;
  const lastAssistantMessage = messages[messages.length - 1];
  
  if (lastAssistantMessage.role !== 'assistant' || !Array.isArray(lastAssistantMessage.content)) {
    return res.status(400).json({ error: 'Định dạng kết quả công cụ không hợp lệ' });
  }
  
  // Tìm các cuộc gọi công cụ và kết quả
  const toolCalls = lastAssistantMessage.content.filter(
    item => item.type === 'tool_call'
  ) as ToolCall[];
  
  const toolResults = lastAssistantMessage.content.filter(
    item => item.type === 'tool_result'
  ) as ToolResult[];
  
  // Xử lý các kết quả
  let finalResponse = "Tôi đã xử lý thông tin sau đây:\n\n";
  
  toolResults.forEach(result => {
    const relatedCall = toolCalls.find(call => call.id === result.tool_call_id);
    if (relatedCall) {
      finalResponse += `- ${relatedCall.name}: ${result.content}\n`;
    }
  });
  
  return res.status(200).json({
    message: {
      role: "assistant",
      content: [{ type: "text", text: finalResponse }]
    }
  });
}

// Chỉnh sửa hàm processMessages để xử lý các cuộc gọi công cụ tiềm năng
function processMessages(messages: Message[]): ModelResponse {
  const lastUserMessage = findLastUserMessage(messages);
  
  if (!lastUserMessage) {
    return createErrorResponse("Không tìm thấy tin nhắn người dùng trong cuộc trò chuyện");
  }
  
  const userQuery = extractTextContent(lastUserMessage);
  
  // Tìm kiếm các từ khóa có thể kích hoạt việc sử dụng công cụ
  if (userQuery.toLowerCase().includes('time')) {
    return {
      message: {
        role: "assistant",
        content: [
          { type: "tool_call", id: "call_001", name: "get_current_time", input: {} },
          { 
            type: "text", 
            text: "Tôi sẽ kiểm tra thời gian hiện tại cho bạn." 
          }
        ]
      }
    };
  } else if (userQuery.toLowerCase().match(/calculate|compute|what is \d+[\+\-\*\/]/)) {
    // Trích xuất một phép tính tiềm năng
    const expression = userQuery.match(/(\d+[\+\-\*\/\(\)\.]*\d+)/)?.[0] || "1+1";
    
    return {
      message: {
        role: "assistant",
        content: [
          { 
            type: "tool_call", 
            id: "call_002", 
            name: "calculate", 
            input: { expression } 
          },
          { 
            type: "text", 
            text: `Tôi sẽ tính ${expression} cho bạn.` 
          }
        ]
      }
    };
  }
  
  // Phản hồi mặc định cho các truy vấn khác
  return {
    message: {
      role: "assistant",
      content: [{ 
        type: "text", 
        text: `Tôi đã nhận được tin nhắn của bạn: "${userQuery}". Tôi có thể giúp bạn thêm gì nữa?` 
      }]
    }
  };
}

Bước 4: Kiểm tra và Triển khai (2 phút)

Hãy tạo một kịch bản kiểm tra đơn giản để kiểm tra máy chủ của chúng tôi. Tạo một tệp test.js trong thư mục gốc:

const fetch = require('node-fetch');

async function testServer() {
  const url = 'http://localhost:3000/v1/messages';
  
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      messages: [
        {
          role: "user",
          content: [
            {
              type: "text",
              text: "Bây giờ là mấy giờ?"
            }
          ]
        }
      ]
    })
  });
  
  const data = await response.json();
  console.log(JSON.stringify(data, null, 2));
}

testServer().catch(console.error);

Để kiểm tra máy chủ của bạn:

# Bắt đầu máy chủ
npm run dev

# Trong một terminal khác, chạy bài kiểm tra
node test.js

Để triển khai nhanh, hãy tạo một Dockerfile đơn giản:

FROM node:16-alpine

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .
RUN npm run build

EXPOSE 3000

CMD ["npm", "start"]

Xây dựng và chạy container:

docker build -t mcp-server .
docker run -p 3000:3000 mcp-server

Kết luận

Chỉ trong 10 phút, chúng tôi đã xây dựng một máy chủ cơ bản thực hiện các khái niệm chính của các giao thức tin nhắn AI hiện đại. Máy chủ của chúng tôi có thể:

  1. Xử lý các yêu cầu tin nhắn có cấu trúc
  2. Phản hồi theo định dạng tiêu chuẩn
  3. Giải quyết chức năng gọi công cụ cơ bản
  4. Xử lý và phản hồi kết quả công cụ

Mặc dù triển khai này được đơn giản hóa, nhưng nó cung cấp một nền tảng vững chắc cho việc phát triển thêm. Để mở rộng máy chủ này cho việc sử dụng trong sản xuất, hãy xem xét:

  • Thêm xác thực và giới hạn tỷ lệ
  • Triển khai xử lý lỗi và kiểm tra đúng cách
  • Kết nối với các mô hình AI thực tế để xử lý
  • Thêm nhiều công cụ tinh vi hơn
  • Triển khai phản hồi theo từng dòng
  • Thêm ghi nhật ký và giám sát toàn diện

Hãy nhớ rằng mặc dù triển khai của chúng tôi tuân theo các nguyên tắc chung của các giao thức tin nhắn AI hiện đại, các triển khai cụ thể như API của OpenAI hoặc API của Claude Anthropic có thể có yêu cầu bổ sung hoặc những biến thể nhỏ trong định dạng mong đợi của chúng. Luôn tham khảo tài liệu chính thức của dịch vụ cụ thể mà bạn đang tích hợp.

Lĩnh vực giao thức tin nhắn AI đang phát triển nhanh chóng, vì vậy hãy cập nhật với những phát triển mới nhất và sẵn sàng thích ứng triển khai của bạn khi các tiêu chuẩn phát triển. Bằng cách hiểu các khái niệm cốt lõi được trình bày trong hướng dẫn này, bạn sẽ được trang bị tốt để làm việc với mọi tiến bộ mới xuất hiện trong lĩnh vực thú vị này.