Tutorial Membuat MCP Server dengan TypeScript dan Python - Nayaka Yoga Pradipta

Tutorial Membuat MCP Server dengan TypeScript dan Python

Senin, 23 Des 2024

Model Context Protocol (MCP) telah menjadi standar baru dalam menghubungkan AI models dengan external tools dan data sources. Kalau kamu developer yang ingin membangun MCP server, pasti bertanya-tanya: lebih baik pakai TypeScript atau Python?

Dalam tutorial ini, kita akan membangun MCP server yang identik secara fungsional menggunakan kedua bahasa. Dengan begitu, kamu bisa membandingkan secara langsung dan memilih stack yang paling cocok untuk kebutuhanmu.

Apa Itu MCP Server?

MCP (Model Context Protocol) adalah protokol open-source yang dikembangkan oleh Anthropic untuk memungkinkan AI assistants seperti Claude berkomunikasi dengan external systems secara standardized. MCP server adalah aplikasi yang menyediakan tools, resources, dan prompts yang bisa diakses oleh AI.

Bayangkan MCP seperti USB untuk AI — satu protokol universal yang memungkinkan berbagai AI models terhubung ke berbagai tools tanpa perlu custom integration untuk setiap kombinasi.

Komponen Utama MCP Server

Sebelum kita mulai coding, pahami dulu tiga komponen utama:

  1. Tools — Fungsi yang bisa dipanggil AI untuk melakukan aksi (seperti menghitung, fetch data, dll)
  2. Resources — Data statis atau dinamis yang bisa dibaca AI
  3. Prompts — Template prompt yang bisa di-reuse

Dalam tutorial ini, kita fokus pada Tools karena ini yang paling sering digunakan.

Perbandingan TypeScript vs Python untuk MCP

Sebelum masuk ke code, mari kita lihat perbandingan kedua ecosystem:

AspekTypeScriptPython
SDK@modelcontextprotocol/sdkmcp (fastmcp)
Type SafetyNative, sangat kuatOptional via type hints
EcosystemNPM, banyak async librariesPyPI, rich data science libs
Learning CurveMedium (perlu paham JS + types)Lower (syntax lebih simpel)
PerformanceLebih cepat untuk I/O intensiveLebih lambat, tapi cukup untuk MCP
DeploymentNode.js requiredPython runtime required

Kedua bahasa sama-sama fully supported oleh MCP dan bisa interoperate — artinya TypeScript server bisa dikonsumsi Python client, dan sebaliknya.

Part 1: MCP Server dengan TypeScript

Mari kita mulai dengan TypeScript. Kita akan membuat MCP server sederhana dengan dua tools:

  • calculator — Tool untuk operasi matematika dasar
  • get_weather — Tool untuk mendapatkan info cuaca (mock data)

Step 1: Setup Project

Buat folder baru dan inisialisasi project:

mkdir mcp-server-typescript
cd mcp-server-typescript
npm init -y

Install dependencies yang diperlukan:

npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node tsx

Penjelasan packages:

  • @modelcontextprotocol/sdk — Official MCP SDK untuk TypeScript
  • zod — Library untuk schema validation (digunakan untuk mendefinisikan input tools)
  • typescript — TypeScript compiler
  • tsx — Runner untuk menjalankan TypeScript langsung

Step 2: Konfigurasi TypeScript

Buat file tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}

Update package.json untuk menambahkan type module dan scripts:

{
  "name": "mcp-server-typescript",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "tsx src/index.ts",
    "build": "tsc"
  }
}

Step 3: Membuat MCP Server

Buat folder src dan file src/index.ts:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Inisialisasi MCP Server
const server = new McpServer({
  name: "demo-server",
  version: "1.0.0",
});

// Tool 1: Calculator
// Mendefinisikan schema input menggunakan Zod
const CalculatorInputSchema = z.object({
  operation: z.enum(["add", "subtract", "multiply", "divide"]),
  a: z.number(),
  b: z.number(),
});

server.tool(
  "calculator",
  "Melakukan operasi matematika dasar: penjumlahan, pengurangan, perkalian, pembagian",
  CalculatorInputSchema.shape,
  async ({ operation, a, b }) => {
    let result: number;

    switch (operation) {
      case "add":
        result = a + b;
        break;
      case "subtract":
        result = a - b;
        break;
      case "multiply":
        result = a * b;
        break;
      case "divide":
        if (b === 0) {
          return {
            content: [
              {
                type: "text" as const,
                text: "Error: Tidak bisa membagi dengan nol",
              },
            ],
          };
        }
        result = a / b;
        break;
    }

    return {
      content: [
        {
          type: "text" as const,
          text: `Hasil ${operation}(${a}, ${b}) = ${result}`,
        },
      ],
    };
  }
);

// Tool 2: Get Weather (Mock)
const WeatherInputSchema = z.object({
  city: z.string().describe("Nama kota untuk mendapatkan info cuaca"),
});

server.tool(
  "get_weather",
  "Mendapatkan informasi cuaca untuk kota tertentu",
  WeatherInputSchema.shape,
  async ({ city }) => {
    // Mock weather data
    const weatherData: Record<string, { temp: number; condition: string }> = {
      jakarta: { temp: 32, condition: "Cerah berawan" },
      bandung: { temp: 24, condition: "Hujan ringan" },
      surabaya: { temp: 34, condition: "Cerah" },
      yogyakarta: { temp: 28, condition: "Berawan" },
      bali: { temp: 30, condition: "Cerah" },
    };

    const cityLower = city.toLowerCase();
    const weather = weatherData[cityLower];

    if (!weather) {
      return {
        content: [
          {
            type: "text" as const,
            text: `Maaf, data cuaca untuk kota "${city}" tidak tersedia. Kota yang tersedia: Jakarta, Bandung, Surabaya, Yogyakarta, Bali`,
          },
        ],
      };
    }

    return {
      content: [
        {
          type: "text" as const,
          text: `Cuaca di ${city}: ${weather.temp}°C, ${weather.condition}`,
        },
      ],
    };
  }
);

// Menjalankan server dengan STDIO transport
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("MCP Server (TypeScript) berjalan...");
}

main().catch(console.error);

Step 4: Menjalankan Server TypeScript

Jalankan server:

npm start

Server akan berjalan dan menunggu koneksi via STDIO. Untuk testing dengan Claude Desktop, kamu perlu menambahkan konfigurasi di file claude_desktop_config.json.

Konfigurasi Claude Desktop (TypeScript)

Tambahkan ke claude_desktop_config.json:

{
  "mcpServers": {
    "demo-typescript": {
      "command": "npx",
      "args": ["tsx", "/path/to/mcp-server-typescript/src/index.ts"]
    }
  }
}

Part 2: MCP Server dengan Python

Sekarang mari kita buat server yang identik menggunakan Python. Kita akan menggunakan package mcp yang juga dikenal sebagai FastMCP.

Step 1: Setup Project

Buat folder baru dan setup virtual environment:

mkdir mcp-server-python
cd mcp-server-python
python -m venv venv
source venv/bin/activate  # Linux/Mac
# atau: venv\Scripts\activate  # Windows

Install dependencies:

pip install mcp

Atau jika kamu menggunakan uv (recommended untuk project baru):

uv init mcp-server-python
cd mcp-server-python
uv add mcp

Step 2: Membuat MCP Server

Buat file server.py:

from mcp.server.fastmcp import FastMCP
from typing import Literal

# Inisialisasi MCP Server
mcp = FastMCP("demo-server")

# Tool 1: Calculator
@mcp.tool()
def calculator(
    operation: Literal["add", "subtract", "multiply", "divide"],
    a: float,
    b: float
) -> str:
    """Melakukan operasi matematika dasar: penjumlahan, pengurangan, perkalian, pembagian"""
    
    if operation == "add":
        result = a + b
    elif operation == "subtract":
        result = a - b
    elif operation == "multiply":
        result = a * b
    elif operation == "divide":
        if b == 0:
            return "Error: Tidak bisa membagi dengan nol"
        result = a / b
    else:
        return f"Error: Operasi '{operation}' tidak dikenal"
    
    return f"Hasil {operation}({a}, {b}) = {result}"


# Tool 2: Get Weather (Mock)
@mcp.tool()
def get_weather(city: str) -> str:
    """Mendapatkan informasi cuaca untuk kota tertentu
    
    Args:
        city: Nama kota untuk mendapatkan info cuaca
    """
    
    # Mock weather data
    weather_data = {
        "jakarta": {"temp": 32, "condition": "Cerah berawan"},
        "bandung": {"temp": 24, "condition": "Hujan ringan"},
        "surabaya": {"temp": 34, "condition": "Cerah"},
        "yogyakarta": {"temp": 28, "condition": "Berawan"},
        "bali": {"temp": 30, "condition": "Cerah"},
    }
    
    city_lower = city.lower()
    weather = weather_data.get(city_lower)
    
    if not weather:
        available_cities = ", ".join([c.title() for c in weather_data.keys()])
        return f'Maaf, data cuaca untuk kota "{city}" tidak tersedia. Kota yang tersedia: {available_cities}'
    
    return f"Cuaca di {city}: {weather['temp']}°C, {weather['condition']}"


# Entry point
if __name__ == "__main__":
    mcp.run()

Step 3: Menjalankan Server Python

Jalankan server:

python server.py

Atau dengan uv:

uv run server.py

Konfigurasi Claude Desktop (Python)

Tambahkan ke claude_desktop_config.json:

{
  "mcpServers": {
    "demo-python": {
      "command": "python",
      "args": ["/path/to/mcp-server-python/server.py"]
    }
  }
}

Atau dengan uv:

{
  "mcpServers": {
    "demo-python": {
      "command": "uv",
      "args": ["--directory", "/path/to/mcp-server-python", "run", "server.py"]
    }
  }
}

Perbandingan Side-by-Side

Mari kita bandingkan kedua implementasi secara langsung:

Inisialisasi Server

TypeScript:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

const server = new McpServer({
  name: "demo-server",
  version: "1.0.0",
});

Python:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("demo-server")

Winner: Python — Lebih concise, satu baris sudah cukup.

Mendefinisikan Tool

TypeScript:

const CalculatorInputSchema = z.object({
  operation: z.enum(["add", "subtract", "multiply", "divide"]),
  a: z.number(),
  b: z.number(),
});

server.tool(
  "calculator",
  "Deskripsi tool",
  CalculatorInputSchema.shape,
  async ({ operation, a, b }) => {
    // logic
    return {
      content: [{ type: "text" as const, text: "result" }],
    };
  }
);

Python:

@mcp.tool()
def calculator(
    operation: Literal["add", "subtract", "multiply", "divide"],
    a: float,
    b: float
) -> str:
    """Deskripsi tool"""
    # logic
    return "result"

Winner: Python — Decorator pattern jauh lebih clean. Type hints sudah cukup untuk schema, tidak perlu Zod.

Return Value

TypeScript:

return {
  content: [
    {
      type: "text" as const,
      text: `Hasil: ${result}`,
    },
  ],
};

Python:

return f"Hasil: {result}"

Winner: Python — Langsung return string, SDK yang handle wrapping-nya.

Lines of Code Comparison

KomponenTypeScriptPython
Imports3 lines2 lines
Server init4 lines1 line
Calculator tool35 lines18 lines
Weather tool30 lines22 lines
Main/run6 lines2 lines
Total~78 lines~45 lines

Python menghasilkan code yang ~40% lebih pendek untuk fungsionalitas yang sama.

Kapan Memilih TypeScript?

Pilih TypeScript jika:

1. Tim Kamu Sudah Familiar dengan JavaScript/TypeScript

Jika tim development kamu sudah bekerja dengan Node.js ecosystem, tidak perlu belajar bahasa baru.

2. Butuh Strong Type Safety

TypeScript dengan Zod memberikan runtime validation yang sangat kuat. Ini penting untuk:

  • API yang menerima input kompleks
  • Production systems yang butuh reliability tinggi
  • Tim besar dimana type checking mencegah bugs

3. Integrasi dengan Node.js Ecosystem

Kalau MCP server kamu perlu:

  • Real-time features dengan WebSocket
  • Integration dengan frontend frameworks (React, Vue, dll)
  • NPM packages yang tidak ada equivalent di Python

4. Performance untuk I/O Intensive Tasks

Node.js event loop sangat efisien untuk concurrent I/O operations.

Kapan Memilih Python?

Pilih Python jika:

1. Rapid Prototyping

FastMCP membuat development sangat cepat. Dari nol ke working server bisa dalam hitungan menit.

2. Data Science / ML Integration

Jika MCP server kamu perlu:

  • Pandas untuk data processing
  • NumPy untuk numerical computation
  • Integration dengan ML models (PyTorch, TensorFlow, scikit-learn)
  • Data analysis dan visualization

3. Tim yang Lebih Nyaman dengan Python

Python syntax lebih approachable, terutama untuk tim yang background-nya bukan web development.

4. Scripting dan Automation

Python unggul untuk:

  • System administration tasks
  • File processing
  • Web scraping
  • API integrations

Advanced: Menambahkan Resources

Selain tools, MCP juga mendukung Resources. Berikut contoh menambahkan resource di kedua bahasa:

TypeScript:

server.resource(
  "config://app",
  "Application configuration",
  async () => ({
    contents: [
      {
        uri: "config://app",
        mimeType: "application/json",
        text: JSON.stringify({ version: "1.0.0", env: "development" }),
      },
    ],
  })
);

Python:

@mcp.resource("config://app")
def get_config() -> str:
    """Application configuration"""
    import json
    return json.dumps({"version": "1.0.0", "env": "development"})

Tips Deployment

TypeScript Deployment

  1. Build untuk production:
npm run build
node dist/index.js
  1. Docker:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY dist ./dist
CMD ["node", "dist/index.js"]

Python Deployment

  1. Dengan requirements.txt:
pip freeze > requirements.txt
pip install -r requirements.txt
python server.py
  1. Docker:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "server.py"]

Testing MCP Server

Untuk testing, kamu bisa menggunakan MCP Inspector — tool official untuk debugging:

# Install MCP Inspector
npx @modelcontextprotocol/inspector

Ini akan membuka web interface dimana kamu bisa:

  • Connect ke MCP server
  • List available tools
  • Test memanggil tools dengan berbagai input
  • Inspect responses

Error Handling Best Practices

TypeScript

server.tool(
  "risky_operation",
  "A tool that might fail",
  schema,
  async (input) => {
    try {
      const result = await riskyFunction(input);
      return {
        content: [{ type: "text" as const, text: result }],
      };
    } catch (error) {
      return {
        content: [
          {
            type: "text" as const,
            text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`,
          },
        ],
        isError: true,
      };
    }
  }
);

Python

@mcp.tool()
def risky_operation(input: str) -> str:
    """A tool that might fail"""
    try:
        result = risky_function(input)
        return result
    except Exception as e:
        raise McpError(f"Error: {str(e)}")

Kesimpulan

Kedua bahasa sama-sama capable untuk membuat MCP server. Pilihan terbaik tergantung pada:

KriteriaRekomendasi
Development speedPython
Type safetyTypeScript
Data science integrationPython
Web ecosystem integrationTypeScript
SimplicityPython
Production reliabilityTypeScript (marginal)

Rekomendasi saya:

  • Untuk prototyping dan personal projects: Mulai dengan Python
  • Untuk production di perusahaan dengan Node.js stack: TypeScript
  • Untuk data/ML-heavy applications: Python

Yang terpenting, MCP adalah protokol — jadi server TypeScript bisa berkomunikasi dengan client Python dan sebaliknya. Kamu tidak terkunci pada satu ecosystem.

Selamat mencoba! Jika ada pertanyaan, silakan tinggalkan komentar di bawah.

Referensi