← Kembali ke Blog

Bikin MCP Server Pertamamu dengan Python

Asisten AI kamu nggak bisa lihat server. Uptime, pemakaian disk, isi log, mesti dicopy manual ke chat, atau dibikin script sekali pakai yang cuma jalan di satu aplikasi.

MCP (Model Context Protocol) benerin ini lewat standar terbuka. Tool dibangun sekali, lalu host MCP mana pun bisa manggilnya: Claude Desktop, Claude Code, Cursor, atau client yang kamu tulis sendiri. Panduan ini ngajak kamu bikin server system-info kecil dengan Python, jalanin lokal, uji di MCP Inspector, terus sambungkan ke host.

Prasyarat

  • Linux atau macOS (contohnya baca /proc, jadi Linux paling gampang)
  • Python 3.10 ke atas
  • uv terinstall (package manager resmi Python; pip juga bisa)

Catatan soal kode: tool-nya baca /proc yang khusus Linux. Di macOS, ganti panggilan itu dengan ps atau vm_stat. Windows di luar lingkup panduan ini.

Langkah 1: Siapkan project

Bikin project lalu install MCP Python SDK (versi 2.0 ke atas):

uv init system-info
cd system-info
uv add "mcp[cli]"

Extra [cli] nambah perintah mcp yang dipakai pas development. Kalau lebih suka pip: pip install "mcp[cli]".

Langkah 2: Tulis server-nya

Buat file server.py:

import logging
import os
import platform
from mcp.server import MCPServer

logger = logging.getLogger(__name__)
mcp = MCPServer("system-info")


@mcp.tool()
def read_uptime() -> str:
    """Read how long the host has been running."""
    with open("/proc/uptime") as f:
        seconds = float(f.readline().split()[0])
    days, rem = divmod(seconds, 86400)
    hours, rem = divmod(rem, 3600)
    minutes = rem // 60
    return f"{int(days)}d {int(hours)}h {int(minutes)}m"


@mcp.tool()
def disk_usage(path: str = "/") -> str:
    """Report disk usage for a given path."""
    st = os.statvfs(path)
    total = st.f_blocks * st.f_frsize
    free = st.f_bavail * st.f_frsize
    used = total - free
    pct = (used / total) * 100 if total else 0
    return f"{path}: {used / 2**30:.1f} GiB used of {total / 2**30:.1f} GiB ({pct:.0f}%)"


@mcp.tool()
def memory_info() -> str:
    """Report current memory usage from /proc/meminfo."""
    with open("/proc/meminfo") as f:
        data = dict(line.split(":", 1) for line in f)
    total = int(data["MemTotal"].strip().split()[0])
    available = int(data["MemAvailable"].strip().split()[0])
    used = total - available
    return f"{used / 2**20:.0f} MB used of {total / 2**20:.0f} MB total"


@mcp.resource("host://info")
def host_info() -> str:
    """Basic host identifier."""
    return f"hostname={platform.node()} platform={platform.system()} {platform.release()}"


@mcp.prompt()
def summarize_system() -> str:
    """Summarize the state of this host."""
    return "Summarize the current state of this host using the available tools."


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    mcp.run(transport="stdio")

Class MCPServer otomatis ngubah type hints dan docstring jadi schema tool. Kamu cuma nulis fungsi biasa pake @mcp.tool(), sisanya SDK yang bikin JSON Schema, ngatur pesan JSON-RPC, dan ngelola koneksi. Nggak ada parsing request manual.

Ada tiga komponen di sini:

  • @mcp.tool(): aksi yang punya efek samping, kayak baca kondisi sistem
  • @mcp.resource(): data read-only, setara endpoint GET
  • @mcp.prompt(): template prompt yang bisa dipakai ulang

Langkah 3: Uji di MCP Inspector

uv run mcp dev server.py

Perintah ini ngejalanin server sekaligus buka MCP Inspector, UI interaktif (aplikasi Node.js, jadi butuh npx di PATH). Buka tab Tools, pilih disk_usage, terus panggil. Form-nya lahir dari type hints kamu. Bagian yang halus: disk_usage(path="/") punya nilai default, jadi SDK tahu argumen itu opsional.

Langkah 4: Sambungkan ke host MCP

Bisa nyambungin server ke host itu yang bikin alat ini berguna di luar demo. Claude Desktop contoh paling simpel. Edit file config-nya:

~/.config/Claude/claude_desktop_config.json

terus tambah:

{
  "mcpServers": {
    "system-info": {
      "command": "uv",
      "args": ["--directory", "/ABS/PATH/system-info", "run", "server.py"]
    }
  }
}

Pakai path absolut, terus quit penuh Claude Desktop (Cmd+Q atau quit dari tray, bukan cuma nutup jendelanya) dan buka lagi. Prompt kayak "berapa pemakaian disk di /" bakal bikin Claude milih manggil tool kamu.

Langkah 5: Logging ke stderr, jangan print

Server stdio komunikasi lewat stdout. print() yang nyasar bakal merusak aliran JSON-RPC dan bikin koneksi putus. Pakai modul logging yang nulis ke stderr:

logger.info("checking disk usage")

Kalau server perlu memperlihatkan aktivitasnya saat jalan, salurin lewat logger.

Kapan pindah ke transport HTTP

Stdio jadi default dan cukup buat host lokal. Kalau server mau diakses orang lain atau aplikasi remote lewat jaringan, ganti transport:

mcp.run(transport="http", port=8000)

Client langsung konek ke http://host:8000/mcp. Dokumentasi modelcontextprotocol.io menyarankan Streamable HTTP transport buat deployment production.

Troubleshooting

  • Server nggak muncul di Claude Desktop: cek syntax JSON, pastikan path absolut, dan quit app sepenuhnya.
  • Tool call gagal diam-diam: lihat log Claude di ~/.config/Claude/logs/mcp-server-*.log buat output stderr server kamu.
  • uv run server.py nggak nampilin apa-apa: itu wajar, yang jalan cuma protokol stdio. Pakai Inspector atau client buat lihat output tool.

Langkah selanjutnya

  • Jalanin server lewat HTTP dan hubungkan client remote
  • Tambah auth dengan OAuth 2.1 (SDK sudah punya panduan authorization)
  • Mount server di dalam aplikasi FastAPI atau Starlette yang sudah ada
  • Lihat official example servers buat pola yang lebih besar

Loop yang bakal kamu ulang di setiap server yang dikirim itu sama: build, uji di Inspector, lalu sambungkan ke host. Mulai dari tool disk dan memory di sini, terus ganti isi fungsinya sesuai dengan apa yang infrastruktur kamu punya.

Referensi

Butuh Bantuan Implementasi?

Saya membantu tim mendesain dan membangun infrastruktur cloud scalable, pipeline DevOps, dan sistem production-grade.

Konsultasi Gratis