Di episode ini kita bangun server MCP nyata dengan dua bahasa: TypeScript lewat McpServer high-level dan Server low-level, serta Python lewat FastMCP dan Server low-level, lengkap dengan contoh tool server sederhana end-to-end.

Di episode 5 kemarin kalian sudah memahami dua primitives penting: Resources untuk menyediakan data kontekstual lewat resources/list dan resources/read, serta Prompts sebagai template reusable lewat prompts/list dan prompts/get. Sekarang saatnya berpindah dari teori ke praktik murni: kita akan membangun server MCP nyata yang siap dijalankan di mesin kalian.
Episode ini fokus ke Server SDK dalam dua ekosistem utama: TypeScript dan Python. Kita bedah dua level abstraksi di masing-masing bahasa — yang high-level untuk produktivitas, dan yang low-level untuk kontrol penuh atas pesan JSON-RPC. Di akhir episode, kalian punya satu server tool lengkap yang bisa langsung dicolokkan ke MCP Inspector maupun aplikasi host.
Sebelum menulis kode, pahami dulu peta SDK. Setiap ekosistem resmi menyediakan dua gaya:
McpServer dari paket @modelcontextprotocol/sdk, Python memakai FastMCP dari paket mcp. Kalian cukup mendaftarkan tool lewat satu fungsi atau satu dekorator; serialisasi JSON-RPC, skema, dan lifecycle diurus SDK.Server — kelas yang memberi akses langsung ke request handler JSON-RPC seperti tools/list dan tools/call. Cocok untuk transport custom, tool dinamis, atau saat kalian butuh menyelipkan logika di setiap pesan.Info
Pilih high-level untuk memulai dan untuk mayoritas kasus produksi. Turun ke low-level hanya jika kalian butuh menangani request di luar tools, resources, dan prompts — misalnya interaksi mid-call yang akan kita bahas di episode 8 soal Multi-Round-Trip Requests.
Pemasangannya satu baris per ekosistem: npm install @modelcontextprotocol/sdk untuk TypeScript, atau pip install mcp untuk Python. Sisanya tinggal menulis kode.
High-level server paling cocok untuk tool-based server. Contoh berikut membuat server mata uang dengan satu tool konversi, lalu terhubung ke transport stdio — protokol standar untuk server lokal yang dipanggil sebagai subprocess oleh host seperti editor atau CLI agent.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new McpServer({
name: "currency-tools",
version: "1.0.0",
});
server.registerTool(
"convert-currency",
{
description: "Konversi nilai antar mata uang",
inputSchema: {
type: "object",
properties: {
from: { type: "string" },
to: { type: "string" },
amount: { type: "number" },
},
required: ["from", "to", "amount"],
},
},
async ({ from, to, amount }) => ({
content: [
{ type: "text", text: `${amount} ${from} = ${amount * 15000} ${to}` },
],
})
);
const transport = new StdioServerTransport();
await server.connect(transport);Perhatikan struktur registerTool: argumen pertama nama tool, kedua deskripsi plus skema input JSON Schema, ketiga fungsi eksekusi yang menerima parameter hasil parsing skema. Nilai baliknya objek content bertipe array — inilah structured output yang kalian kenal dari episode 4. Tanpa dekorasi apa pun, McpServer otomatis mengimplementasikan tools/list dan tools/call di belakang layar. Untuk menjalankannya, build dengan npm run build lalu panggil node dist/server.js. Sekadar catatan, ada juga pustaka fastmcp untuk TypeScript yang meniru gaya dekorator Python — tapi McpServer tetap API utama SDK.
Kalau kalian butuh kontrol penuh — misalnya menambahkan log per request atau mengubah respons sebelum dikirim — turun ke kelas Server dari @modelcontextprotocol/sdk/server/index.js. Di sini setiap method JSON-RPC didaftarkan manual lewat setRequestHandler.
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
const server = new Server({ name: "low-level-server", version: "1.0.0" });
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{ name: "ping", description: "Balas pong", inputSchema: { type: "object", properties: {} } },
],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "ping") {
return { content: [{ type: "text", text: "pong" }] };
}
throw new Error(`Tool tidak dikenal: ${request.params.name}`);
});Skema ListToolsRequestSchema dan CallToolRequestSchema dari @modelcontextprotocol/sdk/types.js adalah definisi JSON-RPC resmi yang di-export SDK. Dengan pola ini kalian juga bisa meregistrasi resources/list, resources/read, prompts/list, dan prompts/get satu per satu — tidak ada yang otomatis.
Di Python, padanan McpServer adalah FastMCP. Registrasi tool cukup lewat dekorator @mcp.tool(), dan type hint pada parameter langsung diterjemahkan menjadi skema JSON Schema — kodenya terasa seperti menulis fungsi biasa.
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("currency-tools")
@mcp.tool()
def convert_currency(from_currency: str, to_currency: str, amount: float) -> str:
result = amount * 15000
return f"{amount} {from_currency} = {result} {to_currency}"
if __name__ == "__main__":
mcp.run(transport="stdio")Jalankan dengan satu baris: python server.py. Kelebihan FastMCP: dukungan asinkron, notifikasi progress, dan helper untuk resources serta prompts dalam satu kelas. Untuk server sederhana, ini jalur tercepat dari nol menuju server yang berfungsi.
Jika FastMCP terlalu "sihir", Python juga menyediakan Server low-level dengan dekorator yang eksplisit terhadap method JSON-RPC:
import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import CallToolRequest
server = Server("low-level-server")
@server.list_tools()
async def list_tools():
return [{"name": "ping", "description": "Balas pong"}]
@server.call_tool()
async def call_tool(request: CallToolRequest):
if request.params.name == "ping":
return {"content": [{"type": "text", "text": "pong"}]}
raise ValueError("Tool tidak dikenal")Dekorator @server.list_tools() dan @server.call_tool() menangkap request JSON-RPC yang masuk. Untuk menjalankannya, bungkus server.run(read, write, server.create_initialization_options()) di dalam async with stdio_server() lalu panggil asyncio.run(main()). Perhatikan create_initialization_options() — di era modern (spesifikasi 2026-07-28) informasi versi dan capability dikirim per-request, jadi server tidak lagi bergantung pada handshake sesi khusus.
Terakhir, rangkai semua menjadi satu server cuaca dengan dua tool, siap dicolokkan ke MCP Inspector dari episode berikutnya:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("weather-server")
@mcp.tool()
def get_weather(city: str) -> str:
if city.lower() == "jakarta":
return "Cerah, 32 derajat Celcius"
return f"Data cuaca untuk {city} belum tersedia"
@mcp.tool()
def get_forecast(city: str, days: int) -> str:
return f"Perkiraan {days} hari ke depan untuk {city}: hujan ringan"
if __name__ == "__main__":
mcp.run(transport="stdio")Server ini bisa diuji cepat lewat npx @modelcontextprotocol/inspector — ia membuka UI interaktif di browser; kita bedah semua fiturnya di episode 7.
Episode 6 memberi kalian keterampilan membangun server MCP di dua bahasa dengan dua level abstraksi: McpServer dan FastMCP untuk produktivitas, serta Server low-level untuk kontrol penuh atas pesan JSON-RPC. Kalian juga sudah punya satu server cuaca end-to-end yang berfungsi nyata.
Inti yang harus dibawa pulang:
registerTool dan @mcp.tool() cukup untuk mayoritas tool server.setRequestHandler dan @server.call_tool() memberi akses langsung ke method JSON-RPC.content berisi blok teks (dan nanti blok lain).Di episode 7 berikutnya kita bangun sisi sebaliknya — Client SDK untuk menghubungi server lewat stdio maupun HTTP, plus MCP Inspector untuk menguji server kalian secara interaktif. Sampai jumpa!