วิธีสร้างและปรับใช้เซิร์ฟเวอร์ MCP ที่กำหนดเองใน 10 นาที

💡ต้องการสร้าง Workflow AI Agentic ของคุณเองโดยไม่ต้องใช้โค้ดหรือไม่? คุณสามารถสร้าง AI workflows ได้อย่างง่ายดายด้วย Anakin AI โดยไม่ต้องมีความรู้เกี่ยวกับการเขียนโค้ด เชื่อมต่อกับ LLM APIs เช่น: GPT-4, Claude 3.5 Sonnet, Uncensored Dolphin-Mixtral, Stable

Build APIs Faster & Together in Apidog

วิธีสร้างและปรับใช้เซิร์ฟเวอร์ MCP ที่กำหนดเองใน 10 นาที

Start for free
Inhalte
💡
ต้องการสร้าง Workflow AI Agentic ของคุณเองโดยไม่ต้องใช้โค้ดหรือไม่?

คุณสามารถสร้าง AI workflows ได้อย่างง่ายดายด้วย Anakin AI โดยไม่ต้องมีความรู้เกี่ยวกับการเขียนโค้ด เชื่อมต่อกับ LLM APIs เช่น: GPT-4, Claude 3.5 Sonnet, Uncensored Dolphin-Mixtral, Stable Diffusion, DALLE, Web Scraping.... ใน Workflow เดียว!

ลืมเรื่องการเขียนโค้ดที่ซับซ้อน ไปอัตโนมัติงานที่น่าเบื่อของคุณด้วย Anakin AI!

ในช่วงเวลาจำกัด คุณยังสามารถใช้ Google Gemini 1.5 และ Stable Diffusion ฟรีได้!
สร้าง AI Agentic Workflows ได้อย่างง่ายดายด้วย Anakin AI!
สร้าง AI Agentic Workflows ได้อย่างง่ายดายด้วย Anakin AI

บทนำ

Model Context Protocol (MCP) แสดงถึงความก้าวหน้าที่สำคัญในระบบนิเวศ AI โดยเสนอวิธีการมาตรฐานในการสื่อสารกับโมเดลภาษาขนาดใหญ่ แทนที่แต่ละแพลตฟอร์ม AI จะใช้รูปแบบที่ไม่เหมือนกัน MCP มีเป้าหมายเพื่อให้มีอินเทอร์เฟซที่สอดคล้องกันสำหรับการสอบถาม การตอบกลับ และการเรียกฟังก์ชันข้ามโมเดลและแพลตฟอร์มที่แตกต่างกัน

ในขณะที่โปรโตคอลเองกำลังพัฒนา การสร้างเซิร์ฟเวอร์พื้นฐานที่เข้ากันได้กับ MCP อาจทำได้ง่าย ในคู่มือนี้ ฉันจะพาคุณไปสร้างเซิร์ฟเวอร์ที่เรียบง่ายแต่ใช้งานได้ซึ่งปฏิบัติตามหลักการพื้นฐานของการจัดการข้อความในระบบ AI สมัยใหม่ การใช้งานของเราจะมุ่งเน้นไปที่การสร้างพื้นฐานที่คุณสามารถขยายเพิ่มเติมด้วยฟีเจอร์ที่ซับซ้อนมากขึ้น

10 นาทีเพียงพอไหม? สำหรับระบบที่พร้อมใช้งานในเชิงพาณิชย์ แน่นอนว่าไม่ แต่สำหรับโพรโตไทป์ที่ทำงานได้ซึ่งแสดงให้เห็นถึงแนวคิดหลัก? แน่นอน มาลองกันเถอะ!

สิ่งที่ต้องเตรียม

ก่อนที่เราจะเริ่ม คุณจะต้อง:

  • ติดตั้ง Node.js (v16+) บนระบบของคุณ
  • มีความรู้พื้นฐานเกี่ยวกับ JavaScript/TypeScript
  • คุ้นเคยกับ Express.js หรือเฟรมเวิร์กเว็บที่คล้ายกัน
  • มีโปรแกรมแก้ไขโค้ด (แนะนำ VS Code)
  • เข้าถึงเทอร์มินัล/บรรทัดคำสั่ง
  • มี npm หรือ yarn package manager

ขั้นตอนที่ 1: ตั้งค่าโปรเจกต์ของคุณ (2 นาที)

เริ่มต้นโดยการสร้างไดเรกทอรีใหม่และเริ่มต้นโปรเจกต์ของเรา:

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

ตอนนี้ ติดตั้ง Dependencies ที่จำเป็น:

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

สร้างไฟล์กำหนดค่าของ TypeScript:

npx tsc --init

แก้ไขไฟล์ tsconfig.json ที่ถูกสร้างขึ้นเพื่อลงทะเบียนการตั้งค่าที่สำคัญเหล่านี้:

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

อัปเดตส่วน scripts ใน package.json ของคุณ:

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

ขั้นตอนที่ 2: สร้างเซิร์ฟเวอร์หลัก (3 นาที)

สร้างไดเรกทอรีที่ใช้และไฟล์เซิร์ฟเวอร์หลัก:

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

เรามากำหนดประเภทของเราก่อนใน src/types.ts:

// โครงสร้างข้อความพื้นฐาน
export interface Content {
  type: string;
  text?: string;
}

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

// โครงสร้างคำขอและการตอบสนอง
export interface ModelRequest {
  messages: Message[];
  max_tokens?: number;
  temperature?: number;
  stream?: boolean;
}

export interface ModelResponse {
  message: Message;
}

// อินเตอร์เฟสการเรียกเครื่องมือ
export interface Tool {
  name: string;
  description: string;
  input_schema: {
    type: string;
    properties: Record<string, any>;
    required?: string[];
  };
}

export interface ToolCall {
  type: "tool_call";
  id: string;
  name: string;
  input: Record<string, any>;
}

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

ตอนนี้ให้ดำเนินการเซิร์ฟเวอร์พื้นฐานใน 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;

// มิดเดิลแวร์
app.use(cors());
app.use(express.json({ limit: '10mb' }));

// จุดเชื่อมหลักสำหรับการประมวลผลข้อความ
app.post('/v1/messages', handleMessageRequest);

// จุดเช็คสุขภาพ
app.get('/health', (req, res) => {
  res.status(200).json({ status: 'ok' });
});

// เริ่มเซิร์ฟเวอร์
app.listen(PORT, () => {
  console.log(`เซิร์ฟเวอร์กำลังทำงานที่พอร์ต ${PORT}`);
  console.log(`เช็คสุขภาพ: http://localhost:${PORT}/health`);
  console.log(`จุดเชื่อมข้อความ: http://localhost:${PORT}/v1/messages`);
});

ถัดไป ดำเนินการจัดการข้อความใน 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;
    
    // การตรวจสอบพื้นฐาน
    if (!request.messages || !Array.isArray(request.messages) || request.messages.length === 0) {
      return res.status(400).json({ error: 'รูปแบบคำขอไม่ถูกต้อง อาร์เรย์ข้อความจำเป็นต้องมี' });
    }
    
    // บันทึกคำขอที่เข้ามา (สำหรับการดีบัก)
    console.log('ได้รับคำขอที่มี', request.messages.length, 'ข้อความ');
    
    // ประมวลผลข้อความ
    const response = processMessages(request.messages);
    
    // ส่งคืนการตอบสนอง
    return res.status(200).json(response);
  } catch (error) {
    console.error('เกิดข้อผิดพลาดในการประมวลผลคำขอ:', error);
    return res.status(500).json({ error: 'ข้อผิดพลาดของเซิร์ฟเวอร์' });
  }
}

function processMessages(messages: Message[]): ModelResponse {
  // ดึงข้อความที่ผู้ใช้ส่งมาเป็นล่าสุด
  const lastUserMessage = findLastUserMessage(messages);
  
  if (!lastUserMessage) {
    return createErrorResponse("ไม่พบข้อความจากผู้ใช้ในการสนทนา");
  }
  
  const userQuery = extractTextContent(lastUserMessage);
  
  // ลอจิกการสร้างการตอบสนองพื้นฐาน
  let responseText = "";
  
  if (userQuery.toLowerCase().includes('hello') || userQuery.toLowerCase().includes('hi')) {
    responseText = "สวัสดี! ฉันสามารถช่วยเหลือคุณได้อย่างไรในวันนี้?";
  } else if (userQuery.toLowerCase().includes('weather')) {
    responseText = "ฉันไม่มีข้อมูลสภาพอากาศแบบเรียลไทม์ แต่ฉันสามารถช่วยคุณเข้าใจรูปแบบสภาพอากาศโดยรวมได้";
  } else if (userQuery.toLowerCase().includes('time')) {
    responseText = `เวลาปัจจุบันบนเซิร์ฟเวอร์คือ ${new Date().toLocaleTimeString()}.`;
  } else {
    responseText = "ฉันได้รับข้อความของคุณแล้ว นี่คือการตอบสนองเป็นเซิร์ฟเวอร์เบื้องต้น";
  }
  
  // สร้างและส่งคืนการตอบสนอง
  return {
    message: {
      role: "assistant",
      content: [{ 
        type: "text", 
        text: responseText 
      }]
    }
  };
}

function findLastUserMessage(messages: Message[]): Message | undefined {
  // ค้นหาข้อความล่าสุดที่มีบทบาทเป็น 'ผู้ใช้'
  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: `ข้อผิดพลาด: ${errorMessage}` 
      }]
    }
  };
}

ขั้นตอนที่ 3: เพิ่มความสามารถในการเรียกเครื่องมือ (3 นาที)

สร้างไฟล์ใหม่สำหรับการกำหนดและการใช้งานเครื่องมือ:

touch src/tools.ts

ดำเนินการเครื่องมือพื้นฐานใน src/tools.ts:

import { Tool } from './types';

// การกำหนดเครื่องมือ
export const availableTools: Tool[] = [
  {
    name: "get_current_time",
    description: "รับเวลาปัจจุบันจากเซิร์ฟเวอร์",
    input_schema: {
      type: "object",
      properties: {
        timezone: {
          type: "string",
          description: "เขตเวลา (เป็นตัวเลือก เริ่มต้นที่เขตเวลาเซิร์ฟเวอร์)"
        }
      }
    }
  },
  {
    name: "calculate",
    description: "ทำการคำนวณทางคณิตศาสตร์",
    input_schema: {
      type: "object",
      properties: {
        expression: {
          type: "string",
          description: "นิพจน์ทางคณิตศาสตร์ที่ต้องการประเมิน"
        }
      },
      required: ["expression"]
    }
  }
];

// การติดตั้งเครื่องมือ
export function executeToolCall(name: string, params: Record<string, any>): string {
  switch (name) {
    case "get_current_time":
      return getTime(params.timezone);
    case "calculate":
      return calculate(params.expression);
    default:
      throw new Error(`เครื่องมือที่ไม่รู้จัก: ${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()} (เวลาเซิร์ฟเวอร์)`;
  }
}

function calculate(expression: string): string {
  try {
    // ระวัง: ในแอปพลิเคชันจริง คุณควรใช้วิธีการประเมินที่ปลอดภัยกว่า
    // นี่คือตัวอย่างที่เรียบง่ายเพื่อการสาธิตเท่านั้น
    const sanitizedExpression = expression.replace(/[^0-9+\-*/().\s]/g, '');
    const result = eval(sanitizedExpression);
    return `${expression} = ${result}`;
  } catch (error) {
    return `ข้อผิดพลาดในการคำนวณ ${expression}: ${error instanceof Error ? error.message : String(error)}`;
  }
}

ตอนนี้ อัปเดตการจัดการข้อความเพื่อสนับสนุนการเรียกเครื่องมือโดยการปรับปรุง src/handlers/messageHandler.ts:

// เพิ่มการนำเข้าดังต่อไปนี้ที่ด้านบน
import { availableTools, executeToolCall } from '../tools';
import { ToolCall, ToolResult } from '../types';

// อัปเดตฟังก์ชัน handleMessageRequest
export async function handleMessageRequest(req: Request, res: Response) {
  try {
    const request = req.body as ModelRequest;
    
    // การตรวจสอบพื้นฐาน
    if (!request.messages || !Array.isArray(request.messages) || request.messages.length === 0) {
      return res.status(400).json({ error: 'รูปแบบคำขอไม่ถูกต้อง อาร์เรย์ข้อความจำเป็นต้องมี' });
    }
    
    // ตรวจสอบว่าคำขอนี้มีผลลัพธ์จากเครื่องมือหรือไม่
    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) {
        // นี่เป็นการติดตามผลด้วยผลลัพธ์จากเครื่องมือ
        return handleToolResultsResponse(request, res);
      }
    }
    
    // ประมวลผลเป็นข้อความทั่วไป
    const response = processMessages(request.messages);
    
    // ส่งคืนการตอบสนอง
    return res.status(200).json(response);
  } catch (error) {
    console.error('เกิดข้อผิดพลาดในการประมวลผลคำขอ:', error);
    return res.status(500).json({ error: 'ข้อผิดพลาดของเซิร์ฟเวอร์' });
  }
}

// เพิ่มฟังก์ชันนี้เพื่อตอบสนองต่อการเรียกเครื่องมือ
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: 'รูปแบบผลลัพธ์จากเครื่องมือไม่ถูกต้อง' });
  }
  
  // ค้นหาการเรียกเครื่องมือและผลลัพธ์
  const toolCalls = lastAssistantMessage.content.filter(
    item => item.type === 'tool_call'
  ) as ToolCall[];
  
  const toolResults = lastAssistantMessage.content.filter(
    item => item.type === 'tool_result'
  ) as ToolResult[];
  
  // ประมวลผลผลลัพธ์
  let finalResponse = "ฉันได้ประมวลผลข้อมูลดังต่อไปนี้:\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 }]
    }
  });
}

// ปรับเปลี่ยนฟังก์ชัน processMessages เพื่อจัดการกับการเรียกเครื่องมือที่อาจเกิดขึ้น
function processMessages(messages: Message[]): ModelResponse {
  const lastUserMessage = findLastUserMessage(messages);
  
  if (!lastUserMessage) {
    return createErrorResponse("ไม่พบข้อความจากผู้ใช้ในการสนทนา");
  }
  
  const userQuery = extractTextContent(lastUserMessage);
  
  // ค้นหาคำสำคัญที่อาจกระตุ้นการใช้เครื่องมือ
  if (userQuery.toLowerCase().includes('time')) {
    return {
      message: {
        role: "assistant",
        content: [
          { type: "tool_call", id: "call_001", name: "get_current_time", input: {} },
          { 
            type: "text", 
            text: "ฉันจะตรวจสอบเวลาปัจจุบันให้คุณ." 
          }
        ]
      }
    };
  } else if (userQuery.toLowerCase().match(/calculate|compute|what is \d+[\+\-\*\/]/)) {
    // ดึงนิพจน์การคำนวณที่อาจเกิดขึ้น
    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: `ฉันจะคำนวณ ${expression} ให้คุณ.` 
          }
        ]
      }
    };
  }

  // การตอบสนองเริ่มต้นสำหรับคำถามอื่น ๆ
  return {
    message: {
      role: "assistant",
      content: [{ 
        type: "text", 
        text: `ฉันได้รับข้อความของคุณ: "${userQuery}" ฉันสามารถช่วยคุณได้อย่างไรต่อไป?` 
      }]
    }
  };
}

ขั้นตอนที่ 4: การทดสอบและการนำไปใช้งาน (2 นาที)

มาสร้างสคริปต์การทดสอบง่าย ๆ เพื่อตรวจสอบเซิร์ฟเวอร์ของเรากัน สร้างไฟล์ test.js ในไดเรกทอรีหลัก:

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: "ตอนนี้เวลาเท่าไหร่?"
            }
          ]
        }
      ]
    })
  });
  
  const data = await response.json();
  console.log(JSON.stringify(data, null, 2));
}

testServer().catch(console.error);

เพื่อตรวจสอบเซิร์ฟเวอร์ของคุณ:

# เริ่มเซิร์ฟเวอร์
npm run dev

# ในเทอร์มินัลอื่น เรียกใช้การทดสอบ
node test.js

เพื่อการนำไปใช้งานอย่างรวดเร็ว เรามาสร้างไฟล์ Dockerfile ง่าย ๆ:

FROM node:16-alpine

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .
RUN npm run build

EXPOSE 3000

CMD ["npm", "start"]

สร้างและรันคอนเทนเนอร์:

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

บทสรุป

ในเวลาเพียง 10 นาที เราได้สร้างเซิร์ฟเวอร์พื้นฐานที่นำเสนอแนวคิดหลักของโปรโตคอลข้อความ AI สมัยใหม่ เซิร์ฟเวอร์ของเราสามารถ:

  1. ประมวลผลคำขอข้อความที่มีโครงสร้าง
  2. ตอบสนองในรูปแบบมาตรฐาน
  3. จัดการฟังก์ชันการเรียกเครื่องมือพื้นฐาน
  4. ประมวลผลและตอบสนองต่อผลลัพธ์จากเครื่องมือ

แม้การใช้งานนี้จะถูกทำให้เรียบง่าย แต่มันให้พื้นฐานที่มั่นคงสำหรับการพัฒนาต่อไป เพื่อขยายเซิร์ฟเวอร์นี้ให้ใช้ในงานผลิตจริง ให้นึกถึง:

  • การเพิ่มการตรวจสอบสิทธิ์และการจำกัดอัตรา
  • การดำเนินการจัดการข้อผิดพลาดและการตรวจสอบให้ถูกต้องอย่างเหมาะสม
  • การเชื่อมต่อกับโมเดล AI ที่แท้จริงสำหรับการประมวลผล
  • การเพิ่มเครื่องมือที่ซับซ้อนมากขึ้น
  • การดำเนินการส่งข้อมูลแบบสตรีม
  • การเพิ่มบันทึกและการตรวจสอบอย่างครอบคลุม

จำไว้ว่าขณะที่การใช้งานของเราสอดคล้องกับหลักการทั่วไปของโปรโตคอลข้อความ AI สมัยใหม่ การใช้งานเฉพาะอย่างของ OpenAI's API หรือ Anthropic's Claude API อาจมีข้อกำหนดเพิ่มเติมหรือลักษณะเฉพาะที่แตกต่างกันในรูปแบบที่คาดหวังของพวกเขาเสมอ ตรวจสอบเอกสารทางการสำหรับบริการเฉพาะที่คุณกำลังเชื่อมต่อ

สาขาของโปรโตคอลข้อความ AI กำลังพัฒนาอย่างรวดเร็ว ดังนั้นให้ติดตามความก้าวหน้าล่าสุดและเตรียมตัวปรับเปลี่ยนการใช้งานของคุณเมื่อมาตรฐานพัฒนาไปเรื่อย ๆ โดยการเข้าใจแนวคิดหลักที่แสดงให้เห็นในคู่มือนี้ คุณจะมีความพร้อมในการทำงานกับความก้าวหน้าใหม่ ๆ ที่เกิดขึ้นในสาขาที่น่าตื่นเต้นนี้