Membangun MCP Server Production-Ready dengan TypeScript (Panduan Lengkap 2026)
Panduan lengkap berbahasa Indonesia untuk membangun MCP Server modern dengan TypeScript: mulai dari konsep dasar, arsitektur, implementasi runnable, best practices, common mistakes, hingga tips advanced untuk production.
Membangun MCP Server Production-Ready dengan TypeScript (Panduan Lengkap 2026)
1) Introduction — Apa itu MCP dan Kenapa Lagi Naik Daun?
Kalau kamu sering mengikuti GitHub Trending atau diskusi developer belakangan ini, kamu pasti melihat pola yang sama: AI agent, tool calling, automation workflow, dan MCP (Model Context Protocol) muncul berulang-ulang.
Kenapa? Karena di 2026, tantangan kita bukan lagi “bisa bikin chatbot atau tidak”, tapi gimana caranya AI benar-benar bisa kerja: akses data, menjalankan aksi, dan tetap aman.
Di sinilah MCP jadi penting.
Bayangkan AI seperti karyawan baru yang sangat pintar, tapi belum punya akses ke alat kantor. Dia bisa berpikir, tapi tidak bisa buka database, baca file project, atau panggil API internal. MCP adalah standar “USB-C”-nya AI tools: satu protokol, banyak integrasi.
Real-world context
Contoh kasus nyata di tim engineering:
- Tim support ingin AI bantu cek status order pelanggan.
- Tim dev ingin AI bisa membaca ticket Jira dan membuat branch otomatis.
- Tim data ingin AI meng-query data warehouse dengan guardrail.
Kalau semua dibuat custom per vendor/model, maintainability cepat hancur. Dengan MCP, kamu bisa:
- menulis server tool sekali,
- dipakai lintas client/agent,
- dan lebih mudah diaudit security-nya.
Tutorial ini akan fokus ke implementasi MCP Server dengan TypeScript yang bisa kamu jalankan lokal hari ini, lalu scale ke production.
2) Prerequisites — Yang Perlu Kamu Siapkan
Sebelum mulai, pastikan environment kamu siap:
Wajib
- Node.js v20+ (disarankan v22 LTS)
- npm atau pnpm
- Pemahaman dasar TypeScript
- Familiar dengan konsep API dan JSON
Nice to have
- Pengalaman dengan schema validation (misalnya Zod)
- Dasar keamanan aplikasi (input validation, rate limit)
Tools yang dipakai
@modelcontextprotocol/sdk(SDK MCP)zoduntuk validasi inputpinountuk structured logging
3) Core Concepts — Fondasi MCP dengan Analogi Sederhana
Sebelum nulis kode, kita samakan mental model.
MCP dalam 1 kalimat
MCP adalah protokol standar agar AI client bisa menemukan dan memanggil tools secara konsisten.
Analogi restoran
- AI client = pelayan
- MCP server = dapur
- Tool = menu
- Tool schema = daftar bahan + aturan pemesanan
Pelanggan (user) pesan “tolong cek cuaca Surabaya”. Pelayan tidak masak sendiri; dia kirim pesanan terstruktur ke dapur. Dapur validasi pesanan, masak, lalu kirim hasil yang formatnya konsisten.
Konsep penting
-
Tool discovery
- Client bisa tahu tool apa saja yang tersedia.
-
Typed input schema
- Setiap tool mendefinisikan input yang valid.
- Mengurangi hallucination dan runtime error.
-
Deterministic output shape
- Output punya struktur jelas agar mudah diproses lanjut.
-
Transport layer
- Umumnya stdio untuk local/dev.
- Bisa diperluas ke transport lain sesuai kebutuhan.
4) Architecture / Diagram
Berikut arsitektur minimal untuk MCP server production-ready:
+-------------------+ MCP Protocol +------------------------+ | AI Client/Agent | <------------------------> | MCP Server (TypeScript)| | (IDE, Chat, CLI) | | - Tool registry | +-------------------+ | - Input validation | | | - Error handling | | user intent | - Logging | v +-----------+------------+ +-------------------+ | | Prompt/Task | | +-------------------+ v +-------------------------------+ | Business Layer | | - call internal API | | - query DB | | - read/write trusted files | +-------------------------------+
Flow ringkas:
- User memberi instruksi ke AI client.
- Client memilih tool dari MCP server.
- MCP server memvalidasi input.
- Business logic dieksekusi.
- Result + error context dikembalikan dalam format aman.
5) Step-by-Step Implementation (Complete Runnable)
Kita akan buat MCP server dengan 2 tools:
generate_slug— membuat URL slug aman.estimate_reading_time— estimasi menit baca artikel.
Kenapa dua tool ini? Karena sederhana, real-world, dan langsung bisa dipakai untuk content workflow.
5.1 Inisialisasi project
mkdir mcp-content-tools && cd mcp-content-tools npm init -y npm install @modelcontextprotocol/sdk zod pino npm install -D typescript tsx @types/node npx tsc --init
5.2 package.json
Ganti isi package.json jadi:
{ "name": "mcp-content-tools", "version": "1.0.0", "private": true, "type": "module", "scripts": { "dev": "tsx src/index.ts", "build": "tsc -p .", "start": "node dist/index.js" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.12.0", "pino": "^9.3.2", "zod": "^3.23.8" }, "devDependencies": { "@types/node": "^22.7.5", "tsx": "^4.19.1", "typescript": "^5.6.2" } }
5.3 tsconfig.json
{ "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "outDir": "dist", "rootDir": "src", "strict": true, "skipLibCheck": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true }, "include": ["src"] }
5.4 Implementasi server src/index.ts
import pino from "pino"; import { z } from "zod"; import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; const logger = pino({ level: process.env.LOG_LEVEL ?? "info" }); // ---------- Schemas ---------- const generateSlugInput = z.object({ title: z.string().min(3).max(180), }); const estimateReadingTimeInput = z.object({ text: z.string().min(20), wordsPerMinute: z.number().int().min(100).max(400).optional().default(200), }); // ---------- Helpers ---------- function toSafeErrorMessage(error: unknown): string { if (error instanceof Error) return error.message; return "Unknown error"; } function createSlug(title: string): string { return title .toLowerCase() .trim() .replace(/[^a-z0-9\s-]/g, "") .replace(/\s+/g, "-") .replace(/-+/g, "-"); } function estimateMinutes(text: string, wpm: number): number { const words = text.trim().split(/\s+/).filter(Boolean).length; return Math.max(1, Math.ceil(words / wpm)); } // ---------- MCP Server ---------- const server = new Server( { name: "mcp-content-tools", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "generate_slug", description: "Generate SEO-friendly slug from article title.", inputSchema: { type: "object", properties: { title: { type: "string", description: "Article title", }, }, required: ["title"], additionalProperties: false, }, }, { name: "estimate_reading_time", description: "Estimate reading time in minutes.", inputSchema: { type: "object", properties: { text: { type: "string", description: "Full article text", }, wordsPerMinute: { type: "number", description: "Reading speed, default 200", }, }, required: ["text"], additionalProperties: false, }, }, ], }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { if (name === "generate_slug") { const input = generateSlugInput.parse(args); const slug = createSlug(input.title); return { content: [ { type: "text", text: JSON.stringify({ slug }, null, 2), }, ], }; } if (name === "estimate_reading_time") { const input = estimateReadingTimeInput.parse(args); const minutes = estimateMinutes(input.text, input.wordsPerMinute); return { content: [ { type: "text", text: JSON.stringify( { minutes, wordsPerMinute: input.wordsPerMinute, }, null, 2 ), }, ], }; } return { isError: true, content: [ { type: "text", text: `Unknown tool: ${name}`, }, ], }; } catch (error) { logger.error({ err: error, tool: name }, "Tool execution failed"); return { isError: true, content: [ { type: "text", text: `Tool failed: ${toSafeErrorMessage(error)}`, }, ], }; } }); async function main() { try { const transport = new StdioServerTransport(); await server.connect(transport); logger.info("MCP Content Tools server started on stdio"); } catch (error) { logger.fatal({ err: error }, "Failed to start MCP server"); process.exit(1); } } void main();
5.5 Jalankan server
npm run dev
Kalau sukses, server siap menerima request dari MCP-compatible client.
5.6 Contoh pengujian cepat
Untuk test logika core (tanpa client penuh), kamu bisa unit-test function createSlug dan estimateMinutes.
Contoh singkat dengan Node REPL/ts-node:
// quick-test.ts import assert from "node:assert/strict"; function createSlug(title: string): string { return title .toLowerCase() .trim() .replace(/[^a-z0-9\s-]/g, "") .replace(/\s+/g, "-") .replace(/-+/g, "-"); } function estimateMinutes(text: string, wpm: number): number { const words = text.trim().split(/\s+/).filter(Boolean).length; return Math.max(1, Math.ceil(words / wpm)); } try { assert.equal(createSlug("Belajar MCP Server dari Nol!"), "belajar-mcp-server-dari-nol"); assert.equal(estimateMinutes("halo ".repeat(450), 200), 3); console.log("All tests passed ✅"); } catch (err) { console.error("Test failed:", err); process.exit(1); }
6) Best Practices — Praktik Terbaik dari Industri
Berikut kebiasaan yang sangat membantu saat MCP server mulai dipakai tim lain:
1. Validasi input ketat, jangan percaya client
Walaupun client “resmi”, tetap validasi semua input. Schema-based validation (Zod/JSON Schema) wajib untuk mencegah crash, behavior aneh, dan potensi abuse.
2. Gunakan output yang stabil
Jangan mengubah format output seenaknya. Jika perlu perubahan, versioning tool (v2) lebih aman daripada breaking change diam-diam.
3. Pisahkan transport vs business logic
Transport (MCP plumbing) harus tipis. Logic utama simpan di service layer agar mudah di-test dan dipakai ulang.
4. Structured logging dari hari pertama
Log plain text cepat bikin pusing saat debugging. Pakai JSON log (pino) supaya mudah dicari di observability stack.
5. Terapkan principle of least privilege
Kalau ada tool yang menyentuh file system atau API sensitif, batasi scope. Contoh: hanya folder tertentu, hanya endpoint tertentu.
6. Timeout dan retry policy jelas
Tool eksternal (HTTP/DB) pasti sesekali lambat atau gagal. Definisikan timeout eksplisit dan retry terbatas (dengan backoff).
7. Document contract
Setiap tool harus punya:
- deskripsi use case,
- input example,
- output example,
- error mode.
Ini menghemat banyak waktu onboarding.
7) Common Mistakes — Kesalahan yang Sering Terjadi
Mistake #1: Tool terlalu “god object”
Satu tool melakukan 10 hal sekaligus. Akibatnya susah validasi, susah audit, dan prompt AI sering salah pakai.
Solusi: pecah jadi tool kecil dengan tanggung jawab tunggal.
Mistake #2: Tidak membedakan user-facing error dan internal error
Semua stack trace dikirim ke client = risiko bocor informasi sensitif.
Solusi: tampilkan error aman ke client, detail lengkap simpan di log.
Mistake #3: Tanpa guardrail pada input string panjang
Input besar bisa bikin memory bengkak atau biaya inference naik.
Solusi: batasi panjang input, sanitasi whitespace, dan tambahkan hard limit.
Mistake #4: Menganggap “local = aman”
Server lokal tetap bisa dipanggil oleh workflow yang tidak kamu duga.
Solusi: tetap desain dengan mindset security-first.
Mistake #5: Tidak punya test untuk contract
Saat refactor, format output berubah dan client downstream rusak.
Solusi: tambah contract tests untuk setiap tool.
8) Advanced Tips — Kalau Kamu Mau Naik Level
A. Tambahkan authentication/authorization layer
Untuk deployment team/shared environment, jangan biarkan semua tool terbuka. Tambahkan token-based policy atau service identity.
B. Instrumentasi metrics
Pantau metrik:
- tool calls per minute,
- success rate,
- P95 latency,
- top failure reasons.
Dengan ini kamu bisa tahu bottleneck sebelum user komplain.
C. Implement caching untuk tool read-heavy
Contoh tool “fetch docs summary” bisa diberi TTL cache 1-5 menit agar respons lebih cepat dan biaya turun.
D. Versioning strategy
Gunakan naming eksplisit seperti:
search_docs_v1search_docs_v2
Saat client sudah migrasi, baru deprecate versi lama.
E. Hybrid pattern: MCP + event-driven backend
Untuk operasi lama (misalnya generate report 2 menit), jangan blok request sync. Kembalikan job ID, proses async di worker, lalu expose tool status checker.
9) Summary & Next Steps
Kita sudah membahas end-to-end cara membangun MCP server modern dengan TypeScript:
- paham konsep dasar MCP,
- setup project,
- implementasi tool dengan validasi + error handling,
- best practices produksi,
- dan strategi scale lanjutan.
Kalau kamu baru mulai, target sederhana dulu:
- Buat 2-3 tool kecil yang jelas fungsinya.
- Tambahkan test contract.
- Integrasikan ke satu AI client.
- Pantau log dan perbaiki reliability.
Setelah stabil, kamu bisa ekspansi ke tool internal perusahaan (ticketing, analytics, CRM) dengan security policy yang ketat.
Intinya: MCP bukan sekadar tren AI 2026, tapi fondasi integrasi agent yang maintainable dan vendor-agnostic.
10) References
- Model Context Protocol — Introduction: https://modelcontextprotocol.io/introduction
- MCP Specification: https://spec.modelcontextprotocol.io/
- MCP TypeScript SDK: https://github.com/modelcontextprotocol/typescript-sdk
- GitHub Trending (monitor topik harian): https://github.com/trending
- DEV Community (artikel developer terbaru): https://dev.to/
- Example MCP Servers (community): https://github.com/modelcontextprotocol/servers
Catatan riset tren (ringkas)
Berdasarkan observasi sumber publik (GitHub Trending dan DEV), topik yang paling menonjol adalah:
- AI coding agents & multi-agent orchestration
- MCP/tool integration untuk agent
- Praktik production readiness (security, observability, reliability)
Karena itu, tutorial ini dipilih agar relevan untuk kebutuhan developer saat ini: bukan hanya “bisa demo”, tapi siap dipakai di workflow nyata.