MCP untuk Developer Web 2026: Panduan Lengkap Membangun Server Model Context Protocol dengan TypeScript
Pelajari cara membangun MCP server production-minded dengan TypeScript: dari konsep arsitektur, implementasi tool yang aman, error handling, sampai best practices agar agent AI kamu bisa terhubung ke data dan aksi nyata.
MCP untuk Developer Web 2026: Panduan Lengkap Membangun Server Model Context Protocol dengan TypeScript
Estimasi baca: 15 menit
Level: Menengah
Stack: Node.js + TypeScript + Model Context Protocol SDK
1) Introduction — What and Why
Di 2026, pertanyaan buat developer bukan lagi "gimana bikin chatbot?" tapi "gimana bikin AI yang benar-benar bisa kerja?".
Kerja di sini artinya: baca data bisnis, jalanin aksi yang aman, dan tetap bisa diaudit.
Di sinilah Model Context Protocol (MCP) jadi penting. MCP bisa kamu bayangkan sebagai USB-C untuk AI: satu standar koneksi untuk banyak sumber data dan tools. Daripada bikin integrasi custom ke tiap model/client, kamu cukup expose capability lewat MCP, lalu client yang support MCP (editor, AI app, CLI, dsb.) bisa pakai server kamu.
Kenapa ini relevan buat web developer?
- Kamu sudah terbiasa bikin API, auth, dan business logic.
- MCP server pada dasarnya adalah "API for AI agents" dengan format yang distandarkan.
- Reusable: satu server bisa dipakai lintas host/client.
Contoh real-world:
- Tim support: AI agent membaca knowledge base + memanggil tool ticketing.
- Tim engineering: agent bisa cek status CI/CD, baca log, dan bantu triage issue.
- Tim ops: agent query metrik + trigger workflow insiden dengan guardrail.
Intinya: MCP bikin AI dari "sekadar ngobrol" jadi "bisa eksekusi".
2) Prerequisites — Sebelum Mulai, Kamu Perlu Apa?
Minimal yang perlu kamu siapin:
- Node.js 20+ (disarankan 22+)
- TypeScript basic (interface, async/await, typing)
- Paham konsep HTTP API dan JSON
- Editor favorit (VS Code/Cursor/dll)
- Optional tapi sangat membantu: MCP Inspector untuk testing
Install dependency inti:
mkdir mcp-tutorial && cd mcp-tutorial npm init -y npm install @modelcontextprotocol/server zod npm install -D typescript tsx @types/node npx tsc --init
Tambahkan script di package.json:
{ "scripts": { "dev": "tsx src/server.ts" } }
3) Core Concepts — Konsep Dasar (Pakai Analogi Biar Nempel)
Bayangin kamu bikin restoran otomatis:
- Host = ruang makan (tempat user berinteraksi dengan AI)
- Client MCP = pelayan (penghubung host ke dapur)
- Server MCP = dapur (menyediakan kemampuan nyata)
Di MCP server, ada 3 primitive utama:
- Tools → aksi yang bisa dijalankan (mis. create ticket, cari dokumen)
- Resources → data kontekstual (mis. config, daftar produk, schema)
- Prompts → template instruksi reusable
Transport umum:
- STDIO: local process, cepat dan simpel untuk local integration.
- Streamable HTTP: untuk remote server, multi client, dan auth HTTP/OAuth.
Analogi sederhana:
- Tools = tombol aksi di dashboard
- Resources = panel data read-only
- Prompts = template SOP kerja
4) Architecture / Diagram
Arsitektur minimal MCP server production-minded:
+---------------------------+ | MCP Host (AI App / IDE) | +-------------+-------------+ | | JSON-RPC over STDIO / HTTP v +-------------+-------------+ | MCP Server (TypeScript) | |---------------------------| | Tool: search_articles | | Tool: get_article_detail | | Resource: app://health | | Prompt: write_summary | +-------------+-------------+ | | Internal Service Layer v +-------------+-------------+ | Business Logic + Storage | | (DB/API/Files/Vector DB) | +---------------------------+
Flow eksekusi:
- Host minta daftar tools (
tools/list). - Model memilih tool sesuai intent user.
- Host kirim
tools/callke MCP server. - Server validasi input (Zod), jalankan logic, return hasil terstruktur.
- Host menggabungkan hasil ke jawaban AI.
5) Step-by-Step Implementation (Kode Lengkap & Runnable)
Di bagian ini kita bikin server sederhana bertema content knowledge dengan 2 tools:
search_articles(query)get_article_detail(id)
5.1 Buat file src/server.ts
import { McpServer } from "@modelcontextprotocol/server/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/server/server/stdio.js"; import { z } from "zod"; /** * Data dummy (simulasi knowledge base). * Dalam production, ganti dengan DB/API. */ const ARTICLES = [ { id: "art-001", title: "Mengenal MCP untuk Integrasi AI", tags: ["mcp", "ai", "integration"], content: "MCP adalah protokol standar untuk menghubungkan AI ke tools dan data secara konsisten.", url: "https://example.local/articles/art-001", }, { id: "art-002", title: "Best Practices Error Handling di TypeScript", tags: ["typescript", "backend", "best-practice"], content: "Gunakan typed error, validasi input ketat, dan log terstruktur untuk reliability.", url: "https://example.local/articles/art-002", }, { id: "art-003", title: "Arsitektur Agentic App 2026", tags: ["agent", "architecture", "mcp"], content: "Pisahkan planning, tool execution, dan observability agar sistem mudah di-maintain.", url: "https://example.local/articles/art-003", }, ]; /** Utility: simulasi async delay */ const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); /** Sanitasi query sederhana untuk mencegah input aneh */ function normalizeQuery(q: string): string { return q.trim().toLowerCase().replace(/\s+/g, " "); } /** * Buat MCP server dengan metadata + instructions. * Instructions membantu host/model memahami urutan pemakaian tool. */ const server = new McpServer( { name: "content-knowledge-server", version: "1.0.0", }, { instructions: "Gunakan search_articles dulu sebelum get_article_detail agar ID valid dan hasil lebih relevan.", capabilities: { logging: {}, }, } ); /** Resource sederhana untuk health check */ server.registerResource( "health", "app://health", { title: "Service Health", description: "Health status MCP server", mimeType: "application/json", }, async (uri) => { const payload = { status: "ok", service: "content-knowledge-server", timestamp: new Date().toISOString(), }; return { contents: [ { uri: uri.href, text: JSON.stringify(payload, null, 2), mimeType: "application/json", }, ], }; } ); /** * Tool #1: cari artikel * - input tervalidasi Zod * - error handling jelas * - output konsisten */ server.registerTool( "search_articles", { title: "Search Articles", description: "Cari artikel berdasarkan query teks", inputSchema: z.object({ query: z.string().min(2, "Query minimal 2 karakter").max(100), limit: z.number().int().min(1).max(20).default(5), }), }, async ({ query, limit }, ctx) => { try { await ctx.mcpReq.log("info", `search_articles dipanggil: query='${query}'`); const normalized = normalizeQuery(query); await sleep(80); // simulasi I/O const results = ARTICLES.filter((a) => { const haystack = `${a.title} ${a.tags.join(" ")} ${a.content}`.toLowerCase(); return haystack.includes(normalized); }) .slice(0, limit) .map((a) => ({ id: a.id, title: a.title, url: a.url, snippet: a.content.slice(0, 120), })); const payload = { results, total: results.length }; return { content: [{ type: "text", text: JSON.stringify(payload) }], structuredContent: payload, }; } catch (error) { const message = error instanceof Error ? error.message : "Unknown error"; await ctx.mcpReq.log("error", `search_articles gagal: ${message}`); return { isError: true, content: [ { type: "text", text: JSON.stringify({ error: "Gagal mencari artikel", detail: message }), }, ], }; } } ); /** * Tool #2: ambil detail artikel by id */ server.registerTool( "get_article_detail", { title: "Get Article Detail", description: "Ambil detail artikel berdasarkan ID", inputSchema: z.object({ id: z.string().regex(/^art-\d{3}$/, "Format ID harus art-xxx"), }), }, async ({ id }, ctx) => { try { await ctx.mcpReq.log("info", `get_article_detail dipanggil: id='${id}'`); await sleep(50); const article = ARTICLES.find((a) => a.id === id); if (!article) { return { isError: true, content: [ { type: "text", text: JSON.stringify({ error: "Artikel tidak ditemukan", id }), }, ], }; } const payload = { id: article.id, title: article.title, tags: article.tags, content: article.content, url: article.url, }; return { content: [{ type: "text", text: JSON.stringify(payload) }], structuredContent: payload, }; } catch (error) { const message = error instanceof Error ? error.message : "Unknown error"; await ctx.mcpReq.log("error", `get_article_detail gagal: ${message}`); return { isError: true, content: [ { type: "text", text: JSON.stringify({ error: "Gagal mengambil detail", detail: message }), }, ], }; } } ); /** Prompt template reusable */ server.registerPrompt( "write_summary", { title: "Write Summary", description: "Template untuk merangkum artikel teknis", argsSchema: z.object({ audience: z.string().default("developer menengah"), style: z.string().default("ringkas dan actionable"), }), }, ({ audience, style }) => ({ messages: [ { role: "user", content: { type: "text", text: `Ringkas artikel untuk ${audience} dengan gaya ${style}. Sertakan 3 poin implementasi.`, }, }, ], }) ); async function main() { try { const transport = new StdioServerTransport(); await server.connect(transport); // Penting: untuk STDIO, hindari console.log ke stdout. // Jika perlu logging manual, arahkan ke stderr. process.stderr.write("MCP server berjalan via STDIO... "); } catch (error) { const message = error instanceof Error ? error.message : String(error); process.stderr.write(`Fatal error: ${message} `); process.exit(1); } } main();
5.2 Jalankan server
npm run dev
5.3 Testing cepat dengan MCP Inspector
npx @modelcontextprotocol/inspector npm run dev
Di UI inspector, cek:
tools/listmunculsearch_articlesdanget_article_detail- panggil
search_articlesdengan querymcp - ambil salah satu id, lalu panggil
get_article_detail
Jika dua tool ini jalan stabil, fondasi server kamu sudah benar ✅
6) Best Practices — Tips dari Praktik Industri
-
Validasi input seketat mungkin
Jangan percaya input dari model mentah-mentah. Gunakan Zod schema untuk semua tool. -
Pisahkan layer protocol vs business logic
Handler MCP cukup jadi adapter. Logic inti tetap di service layer biar testable. -
Gunakan logging terstruktur
Log level (info,warn,error) + request context untuk memudahkan observability. -
Fail safely
Saat error, returnisError: truedengan pesan aman. Jangan leak secret atau stack trace sensitif. -
Gunakan principle of least privilege
Tool yang sifatnya destructive (delete/update) harus punya guardrail tambahan (approval, role check, audit log). -
Rate limiting & timeout
Khusus server HTTP, batasi request size, timeout, dan concurrency untuk tahan abuse. -
Security hardening untuk remote transport
Kalau expose via HTTP, wajib pikirkan auth token/OAuth, CORS allowlist, dan redaksi log sensitif.
7) Common Mistakes — Kesalahan Umum yang Sering Terjadi
-
Nulis log ke stdout saat pakai STDIO
Ini bisa merusak framing JSON-RPC. Pakai stderr atau logging API dari context. -
Tool terlalu "gemuk"
Satu tool melakukan terlalu banyak hal. Pecah jadi beberapa tool kecil biar predictable. -
Schema longgar
Input tanpa batas panjang/format bikin rentan prompt abuse dan output tidak konsisten. -
Error message terlalu generik atau terlalu detail
Terlalu generik susah debug, terlalu detail bisa bocorin info sensitif. -
Tidak ada instruksi lintas-tool
Tanpainstructions, model bisa salah urutan panggil tool. -
Langsung production tanpa inspection
Minimal test di inspector + integration test sebelum dipasang ke workflow penting.
8) Advanced Tips — Kalau Mau Naik Kelas
A. Tambahkan metadata untuk ranking hasil
Simpan score, source, updatedAt di output search supaya host/model bisa pilih context lebih tepat.
B. Implement caching
Untuk query populer, cache layer (mis. Redis/in-memory TTL) bisa turunkan latency signifikan.
C. Gunakan transport HTTP untuk skala tim
Kalau kebutuhan multi-user dan remote access, pertimbangkan streamable HTTP + session management.
D. Buat contract test per tool
Setiap tool punya test untuk:
- valid input
- invalid input
- downstream error
- timeout
E. Tambahkan audit trail
Untuk tool dengan side-effect, simpan jejak: siapa (host/user), kapan, aksi apa, status sukses/gagal.
F. Versioning capability
Gunakan naming/version policy yang konsisten agar perubahan schema tidak mematahkan client lama.
9) Summary & Next Steps
Kita sudah bahas dari nol sampai implementasi MCP server TypeScript yang runnable:
- paham kenapa MCP jadi penting di era agentic software,
- mengerti arsitektur host-client-server,
- implement tools/resources/prompts dengan validasi dan error handling,
- tahu best practices security dan reliability.
Next steps yang saya rekomendasikan:
- Ganti data dummy dengan DB/API real.
- Tambahkan auth + rate limit kalau pakai remote HTTP.
- Integrasikan observability (log aggregation + metrics).
- Tambahkan test otomatis di CI.
- Rilis iteratif: mulai read-only tools dulu, baru tools yang punya side effect.
Kalau kamu ngembangin produk web berbasis AI di 2026, MCP bukan lagi "nice to have" — ini fondasi interoperabilitas yang bikin sistemmu scalable dan tidak terkunci ke satu vendor.
10) References
- MCP Introduction (official): https://modelcontextprotocol.io/introduction
- MCP Architecture (official): https://modelcontextprotocol.io/docs/learn/architecture
- Build MCP Server (official): https://modelcontextprotocol.io/docs/develop/build-server
- TypeScript SDK (official repo): https://github.com/modelcontextprotocol/typescript-sdk
- MCP Reference Servers: https://github.com/modelcontextprotocol/servers
- MCP Inspector: https://github.com/modelcontextprotocol/inspector
- Quickstart resources: https://github.com/modelcontextprotocol/quickstart-resources
- OpenAI MCP guide: https://developers.openai.com/api/docs/mcp
- DuckDuckGo trend query ("MCP tutorial 2026"): https://duckduckgo.com/html/?q=Model+Context+Protocol+tutorial+2026
Catatan riset tren (ringkas)
Topik ini dipilih karena konsisten muncul di:
- GitHub Trending: banyak repo terkait AI agents dan tool-augmented workflows.
- Medium/dev community: artikel tentang agentic engineering dan integrasi AI meningkat.
- Hasil pencarian 2026: lonjakan konten "MCP tutorial/guide" dan roadmap resmi MCP 2026.