Free

Minta Claude Menulis Ulang MCP yang Saya Pasang 6 Hari Lalu

fast-mcp ditolak Smithery. Claude memindahkan seluruh MCP server ke SDK resmi dalam setengah jam — penulisan ulang murah karena lapis pertamanya tipis.


Proyek smarts memasang MCP enam hari yang lalu. Saya minta Claude Code menyalakan tiga tool pakai gem fast-mcp:

  • get_contract_info(chain, address) — nama kontrak, tag adapter protokol, hitungan function/event
  • read_contract_state(chain, address, function_name, args?) — satu panggilan view/pure, cache 60 detik
  • get_uniswap_v3_pool(chain, address) — panel V3 lengkap: harga dua arah, liquidity, tick, TVL dalam USD

Commit waktu itu adalah 27ca82e feat(mcp): serve live contract data over MCP via fast-mcp, 2026-04-21 jam 1:37 dini hari. Sekitar 30 menit. Mulus.

Enam hari kemudian, malam ini — 2026-04-27 jam 20:24 — saya push commit lain: 4df08fa feat(mcp): switch to official mcp gem with Streamable HTTP transport.

Di antara dua commit itu terjadi apa?

Smithery menolak fast-mcp

Saya submit smarts ke Smithery (direktori MCP server) untuk pendaftaran. Scanner mereka langsung balas 405:

POST /mcp/sse → 405 Method Not Allowed

Butuh waktu sebentar untuk paham: fast-mcp 1.6.0 (latest release) masih mengimplementasikan HTTP+SSE transport dari MCP spec 2024-11-05. Sementara klien MCP modern sudah pindah ke Streamable HTTP dari spec 2025-03-26 — endpoint tunggal, POST + DELETE di URL yang sama, tanpa path /sse terpisah.

Artinya: spec sudah maju selangkah, dan gem dominan di komunitas belum nyusul.

Saya tidak mau nunggu fast-mcp. Lapisan MCP server itu ada untuk dikonsumsi klien — kompatibilitas spec lebih penting daripada gem mana yang lebih nyaman ditulis.

Ganti pondasi, sekitar 30 menit

Saya ganti fast-mcp ke gem resmi Anthropic mcp 0.14.0. Putaran ini saya buka thread baru di Amp (di bawahnya tetap Claude Opus, sama persis dengan Claude Code).

Yang mendarat dalam migrasi:

Transport disusun ulang

# Lama: fast-mcp mount otomatis di /mcp dengan /sse
# Baru: mount eksplisit, endpoint tunggal
mount StreamableHTTPTransport.new(server, stateless: true), at: "/mcp"

stateless: true adalah kuncinya. Server tidak menyimpan state per-session di memori, sehingga Puma bisa jalan dengan workers > 0 dan scale horizontal tanpa sticky session.

Restrukturisasi Tool API

Bentuk lama fast-mcp:

class GetContractInfoTool < ApplicationTool
  arguments do
    required(:chain).filled(:string)
    required(:address).filled(:string)
  end

  def call(chain:, address:)
    # logika bisnis
  end
end

Gem resmi mendorong perpipaan SDK ke kelas dasar:

class ApplicationTool < MCP::Tool
  def self.call(**args, server_context: nil)
    hash = payload(**args)
    MCP::Tool::Response.new([{ type: "text", text: hash.to_json }])
  end
end

class GetContractInfoTool < ApplicationTool
  input_schema(
    properties: {
      chain:   { type: "string" },
      address: { type: "string" }
    },
    required: %w[chain address]
  )

  def self.payload(chain:, address:)
    # logika bisnis, return Hash
  end
end

Subkelas hanya mengurusi Hash apa yang dikembalikan payload(**args); kelas dasar membungkusnya jadi text block JSON-encoded di dalam MCP::Tool::Response. Dua untungnya:

  1. Perpipaan SDK berhenti tersebar di tiap kelas tool
  2. Test bisa langsung assert Hash dari Tool.payload(chain:, address:), tanpa membuka envelope protokol

DSL dry-schema saya ganti jadi literal JSON Schema mentah. Kelihatan seperti langkah mundur, tapi input_schema adalah persis yang dibaca klien dari MCP spec — menulisnya sebagai primitif spec lebih jujur.

Test pindah rumah

433 test, dipindah dari Tool.new.call(...) ke Tool.payload(...). Semua hijau.

Sinkronisasi discovery / docs

  • .well-known/mcp.json dengan transport: "streamable-http", protocol_version: "2025-03-26"
  • README, llms.txt, smithery.yaml — semua di-update ke https://smarts.md/mcp
  • Flag claude mcp add ganti dari --transport sse ke --transport http
  • Di CLAUDE.md, seksi tech stack sekarang menandai mcp sebagai primary dan fast-mcp sebagai legacy/deprecated

"Berani menulis ulang" itu leverage yang sebenarnya

Integrasi MCP pertama makan 30 menit. Penulisan ulang sekitar segitu juga. Beda 6 hari.

Yang nyantol di kepala saya: di ekosistem muda seperti MCP, gem ketinggalan spec itu normal. fast-mcp bukan jelek — maintainernya aktif. Masalahnya, protokol itu sendiri iterasinya lebih cepat daripada implementasi pihak ketiga. Gem yang tahun ini kelihatan stabil, setengah tahun lagi bisa jadi "yang spec lama".

Kalau enam bulan lalu saya tulis sepuluh ribu baris logika bisnis di atas fast-mcp, hari ini saya kemungkinan besar terkunci di spec lama — karena biaya menulis ulang akan terlalu tinggi sampai saya tidak akan mau ngerjain.

Tapi kenyataannya: lapisan fast-mcp sejak awal cuma thin wrapper. Logika bisnis tinggal di subkelas ApplicationTool. Jadi biaya nyata penulisan ulang adalah:

  • Ganti kelas dasar (sekali)
  • Tulis ulang DSL schema di tiap dari 4 kelas tool (~10 baris per kelas)
  • Ganti gaya pemanggilan di test (mekanis)
  • Ganti satu baris mount di routes
  • Search-and-replace URL di docs

Yang mahal adalah logika bisnis. Wrapper protokol seharusnya murah dan bisa ditulis ulang. Itu favor terbesar yang Claude Code berikan ke saya enam hari lalu — dia tidak menjejalkan logika bisnis ke callback fast-mcp. Dia bikin abstraksi ApplicationTool yang bersih yang menyerap SDK. Karena itulah enam hari kemudian pertukaran SDK cuma butuh kelas dasar + schema.

Yang membuat saya bisa dalam 6 hari menempuh "integrasi pertama → upgrade spec → tulis ulang" adalah stabilitas Claude sebagai model di bawahnya — agent shell mana yang saya pakai, kurang relevan.

Source: https://github.com/defi-io/smarts