Keamanan

Bikin MCP Server Sendiri pakai Python: Tutorial 2026

Bikin MCP Server Sendiri pakai Python: Tutorial 2026

Bikin MCP server di Python itu urusannya satu file dan satu dekorator per kemampuan. Per 2 Oktober 2026, paket mcp sudah di versi 2.3.0, dan server pertama yang bisa dibuka di MCP Inspector cukup 15 baris: satu objek MCPServer, satu fungsi @mcp.tool(), satu @mcp.resource(). Dokumentasi resmi menyebut perintah jalannya cuma uv run mcp dev server.py, tanpa port dan tanpa JSON Schema manual, karena schema dibaca langsung dari type hint.

TL;DR

  • SDK Python resmi sekarang v2 (2.3.0, rilis 2 Oktober 2026), class-nya MCPServer, bukan FastMCP lagi.
  • Tool, resource, dan prompt cukup didaftarkan pakai dekorator, schema dibangun dari type hint dan docstring.
  • Uji lokal pakai mcp dev (stdio), produksi pakai Streamable HTTP di port sendiri.
  • Deploy ke VPS tanpa transport_security akan balas 421 Misdirected Request untuk semua request, bukan bug kode.
  • Protokol 2026-07-28 menghapus handshake dan session, jadi load balancer tidak perlu sticky session lagi.

Apa Itu MCP Server dan Kenapa Bikin Sendiri?

MCP server adalah program yang membuka data atau aksi ke aplikasi LLM lewat protokol standar, jadi kamu tidak perlu menulis integrasi khusus untuk tiap host. Host (Claude Desktop, IDE, agent runtime) menjalankan client, client bicara ke server kamu, dan server tidak pernah memanggil model secara langsung. Pemisahan ini yang bikin satu server bisa dipakai di banyak aplikasi sekaligus.

Alasan paling praktis: data kamu ada di tempat yang tidak dijangkau tool publik, entah database internal, folder catatan, atau API legacy. MCP server mengubah copy paste manual jadi satu tool dengan argumen terstruktur.

Banyak tutorial lama masih memakai nama class yang sudah dihapus. Di v2 import yang benar from mcp.server import MCPServer, sementara from mcp.server.fastmcp import FastMCP sudah tidak ada, bukan sekadar deprecated.

Berapa Lama Waktu yang Dibutuhkan Sampai Server Pertama Jalan?

Untuk satu tool sederhana, waktu realistisnya 10 sampai 20 menit, sudah termasuk install dan buka Inspector. Bagian yang lama bukan menulis kodenya, tapi memastikan environment Python dan uv siap, karena mcp dev memanggil Inspector berbasis Node.js yang butuh npx di PATH.

Langkah 0: Siapkan environment

Dokumentasi v2 minta Python 3.11 atau lebih baru. Install SDK beserta CLI-nya:

pip install "mcp[cli]"
# atau, kalau pakai uv:
uv add "mcp[cli]"

Extra cli itu yang menambahkan perintah mcp dev, mcp run, dan mcp install. Kalau kamu hanya butuh library-nya di dalam aplikasi lain, pip install mcp saja cukup.

Langkah 1: Server 15 baris

Buat file server.py:

from mcp.server import MCPServer

mcp = MCPServer("Demo")


@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b


@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
    """Greet someone by name."""
    return f"Hello, {name}!"


@mcp.prompt()
def summarize(text: str) -> str:
    """Summarize a piece of text in one sentence."""
    return f"Summarize the following text in one sentence:

{text}"

Yang tidak kamu tulis: JSON Schema, parsing request, validasi argumen, dan handling protokol. a: int, b: int sudah jadi schema, docstring jadi deskripsi tool, dan nama fungsi jadi nama tool.

Langkah 2: Uji di Inspector

uv run mcp dev server.py

Perintah ini menjalankan server sebagai subprocess lewat stdio, lalu membuka MCP Inspector. Di tab Tools, panggil add dengan a=1 dan b=2, hasilnya 3. Di tab Resource Templates, baca greeting://World dan kamu dapat Hello, World!. Inspector membangun form input dari type hint, dan host lain melakukan hal yang sama.

Langkah 3: Sambungkan ke host

Untuk Claude Desktop, ada perintah yang menulis konfigurasinya langsung:

uv run mcp install server.py

Yang ditulis ke config pada dasarnya adalah perintah untuk menjalankan file kamu, dengan path absolut ke uv. Kalau host tidak menemukan uv, ganti dengan hasil which uv, karena host menjalankan server dari working directory miliknya sendiri dengan PATH yang minim.

Untuk host lain, polanya sama:

{
  "mcpServers": {
    "Demo": {
      "command": "/absolute/path/to/uv",
      "args": ["run", "--with", "mcp[cli]", "mcp", "run", "/absolute/path/to/server.py"]
    }
  }
}

Pastikan mcp.run() ada di dalam if __name__ == "__main__":. Semua alat (mcp dev, mcp run, mcp install, dan test) mengimpor file itu, jadi run() tanpa guard akan menyalakan server setiap kali modul dimuat.

Tiga Primitif: Tool, Resource, Prompt

Bedanya bukan soal bentuk data, tapi soal siapa yang memutuskan memakainya.

Primitif    Yang memutuskan   Fungsinya                        Analogi HTTP
tool        model             aksi, boleh punya efek samping   POST
resource    aplikasi          data yang dibaca, read only      GET
prompt      user              template pesan yang dipanggil     tidak ada

Tool untuk aksi seperti simpan catatan atau panggil API. Resource untuk data yang perlu dimuat host ke context, misal isi file atau hasil query. Prompt untuk template yang dipilih user, mirip slash command. Salah menaruh sesuatu di kategori yang salah bikin perilakunya aneh: model tidak akan pernah “memutuskan” membaca resource, dan aplikasi tidak bisa memaksa tool jalan.

Untuk operasi yang bisa gagal secara wajar, lempar ToolError supaya pesannya sampai ke model dan bisa dicoba ulang:

from mcp.server.mcpserver.exceptions import ToolError

@mcp.tool()
def get_author(title: str) -> str:
    """Look up the author of a book in the catalog."""
    if title not in CATALOG:
        raise ToolError(f"No book titled {title!r} in the catalog.")
    return CATALOG[title]

Kapan Harus Pakai stdio dan Kapan Streamable HTTP?

Pakai stdio kalau servernya hanya untuk mesin kamu sendiri dan dipanggil host lokal. Pakai Streamable HTTP kalau server harus diakses dari mesin lain, dipakai beberapa orang, atau perlu URL tetap. Dokumentasi resmi menyebut SSE sebagai transport lama yang sudah digantikan Streamable HTTP sejak revisi protokol 2025-03-26.

Untuk mode HTTP, cukup beri argumen transport:

if __name__ == "__main__":
    mcp.run(transport="streamable-http", host="0.0.0.0", port=3001)

Default-nya 127.0.0.1:8000 dengan endpoint /mcp. Batas body default 4 MiB, dan request yang lebih besar ditolak HTTP 413 sebelum diparsing.

Kalau kamu menjalankan model atau agent yang butuh akses ke banyak provider, satu gateway terpusat biasanya lebih rapi daripada menaruh API key di tiap server. Layanan seperti NgodeAI bisa jadi lapisan itu, sementara server MCP kamu tetap fokus ke satu tugas.

Langkah 4: Deploy ke VPS Tanpa Kena 421

Ini bagian yang paling sering bikin orang mengira kodenya rusak. streamable_http_app() tidak tahu hostname apa yang akan dipakai, jadi default-nya mengaktifkan proteksi DNS rebinding dan hanya menerima Host bernilai localhost atau 127.0.0.1. Di balik domain asli, semua request ditolak dengan 421 Misdirected Request sebelum logika MCP kamu disentuh sama sekali.

Perbaikannya dengan allowlist eksplisit:

from mcp.server import MCPServer
from mcp.server.transport_security import TransportSecuritySettings

mcp = MCPServer("Notes")

security = TransportSecuritySettings(
    allowed_hosts=["mcp.example.com", "mcp.example.com:*"],
    allowed_origins=["https://app.example.com"],
)
app = mcp.streamable_http_app(transport_security=security)

Jalankan dengan uvicorn, dan kalau TLS diterminasi proxy seperti Caddy atau nginx, tambahkan --proxy-headers beserta --forwarded-allow-ips yang tepat. Tanpa itu, redirect /mcp ke /mcp/ akan mengarah ke http://, dan client Python menolak turun dari HTTPS ke HTTP.

uvicorn server:app --host 127.0.0.1 --port 3001 --proxy-headers   --forwarded-allow-ips='127.0.0.1'

Soal worker: untuk client yang bicara protokol 2026-07-28, request bersifat mandiri, tanpa handshake dan tanpa Mcp-Session-Id, jadi --workers 4 aman dan load balancer tidak perlu sticky session. Client lama (2025-11-25 ke bawah) memegang session di memori satu worker, jadi request lanjutan di worker lain dapat 404 Session not found kecuali stateless_http=True.

VPS kecil sudah cukup untuk tahap awal, dan yang menentukan bukan RAM server MCP-nya, tapi proses Python plus dependency-nya. Di sinilah memilih instance yang benar menghemat waktu: Alibaba Cloud ECS generasi Intel ke-9 bisa dipakai untuk menaruh server HTTP ini di belakang reverse proxy, dengan pemisahan yang jelas antara proses aplikasi dan database.

Kesalahan yang Paling Sering Muncul

  • Import FastMCP dari tutorial lama, padahal v2 sudah memakai MCPServer.
  • mcp.run() tidak ditaruh di dalam guard __main__, jadi server menyala saat modul diimpor test.
  • Menulis log pakai print() saat mode stdio, karena stdout adalah jalur protokol. Pakai modul logging yang menulis ke stderr.
  • Debug lewat browser_navigate atau buka port langsung ke internet tanpa reverse proxy dan autentikasi.
  • Mengira host="mcp.example.com" sudah cukup untuk allowlist. Itu hanya mematikan proteksi default, bukan mengizinkan hostname tersebut.

Bagaimana Cara Mengetes Server Sebelum Dipakai?

Pakai class Client dari SDK yang sama. Untuk server HTTP, cukup arahkan ke URL:

import asyncio
from mcp import Client

async def main():
    async with Client("http://localhost:3001/mcp") as client:
        result = await client.call_tool("add", {"a": 1, "b": 2})
        print(result.structured_content)  # {'result': 3}

asyncio.run(main())

Untuk test otomatis, Client juga bisa menerima objek server langsung dan berjalan di memori tanpa proses dan tanpa transport. Itu menggantikan helper create_connected_server_and_client_session() di v1, dan jauh lebih cepat untuk CI.

Rekomendasi Tools & Layanan

  • SDK dan dokumentasi: mulai dari repo python-sdk dan halaman What’s new in v2. Dua halaman itu paling cepat menyamakan persepsi soal nama class dan API baru.
  • Spesifikasi protokol: spesifikasi MCP terbaru, untuk memastikan perilaku transport dan versioning sesuai revisi yang kamu klaim.
  • Server untuk deploy: kalau butuh instance bersih buat server HTTP plus reverse proxy, halaman benefit Alibaba Cloud punya jalur uji yang tidak mengikat.
  • Gateway model: NgodeAI membantu kalau kamu memakai beberapa model sekaligus dan tidak mau menyebar key di banyak server.

FAQ

Apakah MCP server harus pakai Python 3.11?

Dokumentasi v2 menyebut Python 3.11 atau lebih baru. Metadata PyPI untuk 2.3.0 masih mencantumkan 3.10 sebagai batas bawah, tapi ikuti dokumentasi kalau kamu ingin menghindari perbedaan perilaku di versi lama.

Apakah FastMCP masih bisa dipakai di 2026?

Tidak lewat jalur import lama. Di v2 class-nya berganti nama jadi MCPServer, dan modulnya pindah dari mcp.server.fastmcp ke mcp.server.mcpserver. Kode v1 harus di-porting, bukan sekadar dijalankan.

Bisakah satu server dipakai Claude Desktop dan web app sekaligus?

Bisa, dari objek yang sama dengan dua transport: stdio untuk host lokal, Streamable HTTP untuk web. Tool dan resource tidak perlu ditulis dua kali.

Kenapa server saya balas 421 padahal curl ke port-nya nyambung?

Itu proteksi DNS rebinding, bukan error jaringan. Tambahkan hostname kamu ke TransportSecuritySettings(allowed_hosts=[...]) atau matikan proteksi hanya kalau reverse proxy sudah mengunci header Host.

Apakah perlu sticky session kalau pakai lebih dari satu worker?

Untuk protokol 2026-07-28 tidak perlu, karena request tidak membawa session. Untuk client lama yang masih di 2025-11-25 ke bawah, sticky session atau stateless_http=True wajib dipilih salah satu.

Penutup

MCP server bukan proyek besar. Satu file, satu class, satu dekorator per kemampuan, dan batas antara “jalan di laptop” dan “jalan di produksi” cuma soal transport serta allowlist host. Yang bikin gagal biasanya bukan kode tool-nya, tapi environment host, guard __main__, atau setting keamanan transport yang tidak dibaca.

Mulai dari satu tool yang benar-benar kamu pakai tiap hari. Setelah tool pertama jalan, menambah tool kedua dan ketiga jauh lebih cepat karena polanya sama.

💬 Komentar (0)

Belum ada komentar. Jadilah yang pertama! 💬

Komentar yang memuat tautan, bahasa asing, atau aksara non-Latin ditahan dulu buat ditinjau.