ข้ามไปยังเนื้อหา

Setup and Your First Server

เครื่องมือVersionทำไม
Node.js22.19 ขึ้นไปรัน TypeScript SDK และ MCP Inspector (Inspector ต้องการ ≥ 22.19)
Python3.10 ขึ้นไปรัน Python SDK
uvรุ่นใหม่ ๆ ตัวไหนก็ได้ตัวจัดการ project และ dependency ของ Python ที่เร็ว; uvx รัน server ที่เผยแพร่แล้วได้โดยไม่ต้องติดตั้ง

ตรวจด้วย node --version, python3 --version และ uv --version ที่เหลือ package manager จะดึงมาให้เอง

Terminal window
mkdir hello-mcp && cd hello-mcp
npm init -y
npm install @modelcontextprotocol/server zod
npm install -D tsx typescript

@modelcontextprotocol/server คือครึ่งฝั่ง server ของ TypeScript SDK (ครึ่งฝั่ง client เป็น package แยก) ส่วน package mcp ของ Python มีทั้งสองครึ่งอยู่ด้วยกัน

server ด้านล่างเปิด tool ตัวเดียวชื่อ get-forecast และพูด stdio — transport ที่ host ใช้รัน server เป็น subprocess

server.ts
import { McpServer } from '@modelcontextprotocol/server';
import { serveStdio } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';
serveStdio(() => {
const server = new McpServer({ name: 'weather', version: '1.0.0' });
server.registerTool(
'get-forecast',
{
description: 'Get the weather forecast for a city',
inputSchema: z.object({ city: z.string() }),
},
async ({ city }) => ({
content: [{ type: 'text', text: `Sunny in ${city} all week.` }],
}),
);
return server;
});

แต่ละส่วนทำอะไร:

  • McpServer / MCPServer — object ของ server name และ version จะกลายเป็น io.modelcontextprotocol/serverInfo ใน result และใน response ของ server/discover
  • registerTool / @mcp.tool() — ลงทะเบียน tool TypeScript รับ inputSchema แบบ Zod ที่ระบุชัด (import จาก zod/v4) และส่ง argument ที่มี type ให้คุณ Python สร้าง JSON Schema จาก type hint ของ function และเอา description มาจาก docstring
  • ค่าที่ handler return — TypeScript return result object ที่มี array content Python ให้ return string ธรรมดาได้ แล้ว SDK จะห่อเป็น text content block ให้
  • serveStdio / mcp.run() — ต่อ server เข้ากับ stdin/stdout serveStdio รับ factory เพื่อให้ SDK สร้าง server ใหม่ต่อ connection ได้ และ factory รูปร่างเดียวกันนี้จะถูกใช้กับ HTTP ในภายหลัง

บน wire server นี้ตอบ tools/list ด้วย:

{
"resultType": "complete",
"tools": [
{
"name": "get-forecast",
"description": "Get the weather forecast for a city",
"inputSchema": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
]
}

นั่นคือ contract ทั้งหมดที่ host ต้องการ

MCP Inspector คือเครื่องมือนักพัฒนาอ้างอิง: package npx ตัวเดียวที่มี web UI, CLI และ terminal UI ซึ่งใช้โค้ด connection ร่วมกันทั้งหมด ชี้มันไปที่คำสั่งเปิด server ของคุณ:

Terminal window
npx @modelcontextprotocol/inspector npx tsx server.ts

คำสั่งจะพิมพ์ URL ที่มี session token แบบใช้ครั้งเดียว เปิดมันแล้วคุณจะได้แท็บ Tools ที่แสดง get-forecast, ฟอร์มที่สร้างจาก inputSchema ของมัน และ sidebar monitoring ที่แสดง JSON-RPC message ทุกตัวขณะวิ่งผ่าน — ทั้ง server/discover, tools/list และ tools/call ของคุณ การอ่าน pane นั้นเทียบกับบทเรียนก่อนหน้าคือวิธีที่เร็วที่สุดที่จะทำให้ wire format จับต้องได้

sequenceDiagram
  participant You as Browser
  participant I as Inspector
  participant S as server.ts (subprocess)
  You->>I: เปิด session URL
  I->>S: spawn, เขียน JSON-RPC ลง stdin
  I->>S: server/discover
  S-->>I: capabilities (tools)
  I->>S: tools/list
  S-->>I: get-forecast
  You->>I: กรอก city แล้วกด Run
  I->>S: tools/call get-forecast
  S-->>I: content: Sunny in Lisbon all week.
Inspector ทำอะไรเมื่อคุณเชื่อมต่อ

สำหรับ CI, shell pipeline หรือเช็กเร็ว ๆ package เดียวกันมีโหมด CLI ที่รัน method เดียวแล้วออก:

Terminal window
npx @modelcontextprotocol/inspector --cli npx tsx server.ts --method tools/list
npx @modelcontextprotocol/inspector --cli npx tsx server.ts \
--method tools/call --tool-name get-forecast --tool-arg city=Lisbon

เติม --format json แล้ว pipe ไป jq เมื่ออยาก assert กับ output

บน stdio stdout คือช่องทางของ protocol client อ่านมันทีละบรรทัดและ parse แต่ละบรรทัดเป็น JSON-RPC server ห้าม เขียนอย่างอื่นลงไป แค่ console.log("starting…") หรือ print("debug") หลุดมาบรรทัดเดียว client ก็จะเจอบรรทัดที่ไม่ใช่ JSON และ connection จะพังในแบบที่ดูเหมือน server ค้าง

ให้ log ไปที่ stderr แทน transport spec อนุญาตชัดเจนให้ server เขียน UTF-8 ลง stderr สำหรับ output แบบ informational, debug และ error และบอก client ว่าไม่ต้องถือว่า stderr เป็นสัญญาณ error

console.error('starting weather server'); // fine: stderr
// console.log(...) // never in a stdio server

กฎเดียวกันใช้กับ library ทุกตัวที่ server ของคุณ import: ถ้า dependency ตัวไหนพิมพ์ banner ลง stdout ตอน import มันจะทำ server ของคุณพังก่อน message แรกจะมาถึง

MCP server ที่ทำงานได้ วิธีทดลองใช้มัน และ wire traffic ที่พิสูจน์ว่ามันทำอะไรอยู่ จากตรงนี้คอร์สจะขยายออกไป: โมดูล Server Primitives เติม tools, resources และ prompts ลงใน server; โมดูล Transports ย้ายมันจาก subprocess ไปเป็น HTTPS endpoint

ใน stdio server ทำไม print ที่หลุดลง stdout ถึงเป็น bug ร้ายแรง?
stdio server ควรเขียน log ไปที่ไหน?
คำสั่งไหนรัน tools/list ครั้งเดียวกับ TypeScript server แล้วออก?