dailytutorfor.you
Data Science & AI

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.

10 menit baca

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:

  1. Node.js 20+ (disarankan 22+)
  2. TypeScript basic (interface, async/await, typing)
  3. Paham konsep HTTP API dan JSON
  4. Editor favorit (VS Code/Cursor/dll)
  5. 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:

  1. Tools → aksi yang bisa dijalankan (mis. create ticket, cari dokumen)
  2. Resources → data kontekstual (mis. config, daftar produk, schema)
  3. 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:

  1. Host minta daftar tools (tools/list).
  2. Model memilih tool sesuai intent user.
  3. Host kirim tools/call ke MCP server.
  4. Server validasi input (Zod), jalankan logic, return hasil terstruktur.
  5. 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/list muncul search_articles dan get_article_detail
  • panggil search_articles dengan query mcp
  • 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

  1. Validasi input seketat mungkin
    Jangan percaya input dari model mentah-mentah. Gunakan Zod schema untuk semua tool.

  2. Pisahkan layer protocol vs business logic
    Handler MCP cukup jadi adapter. Logic inti tetap di service layer biar testable.

  3. Gunakan logging terstruktur
    Log level (info, warn, error) + request context untuk memudahkan observability.

  4. Fail safely
    Saat error, return isError: true dengan pesan aman. Jangan leak secret atau stack trace sensitif.

  5. Gunakan principle of least privilege
    Tool yang sifatnya destructive (delete/update) harus punya guardrail tambahan (approval, role check, audit log).

  6. Rate limiting & timeout
    Khusus server HTTP, batasi request size, timeout, dan concurrency untuk tahan abuse.

  7. 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

  1. Nulis log ke stdout saat pakai STDIO
    Ini bisa merusak framing JSON-RPC. Pakai stderr atau logging API dari context.

  2. Tool terlalu "gemuk"
    Satu tool melakukan terlalu banyak hal. Pecah jadi beberapa tool kecil biar predictable.

  3. Schema longgar
    Input tanpa batas panjang/format bikin rentan prompt abuse dan output tidak konsisten.

  4. Error message terlalu generik atau terlalu detail
    Terlalu generik susah debug, terlalu detail bisa bocorin info sensitif.

  5. Tidak ada instruksi lintas-tool
    Tanpa instructions, model bisa salah urutan panggil tool.

  6. 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:

  1. Ganti data dummy dengan DB/API real.
  2. Tambahkan auth + rate limit kalau pakai remote HTTP.
  3. Integrasikan observability (log aggregation + metrics).
  4. Tambahkan test otomatis di CI.
  5. 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


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.