Mengapa server MCP hasil generate otomatis Anda mendadak crash saat dideploy ke Cloudflare Workers? Sebagai developer, mendapatkan kode siap saji—misalnya dari Postman—seringkali terasa seperti mendapat durian runtuh. Kita mendapatkan struktur server Model Context Protocol (MCP) yang lengkap, lengkap dengan penanganan JSON-RPC, manajemen tools, hingga Dockerfile yang siap saji. Di komputer lokal (localhost), semuanya berjalan mulus tanpa interupsi.

Namun, petaka dimulai saat Anda mencoba mengejar performa edge yang masif dan latensi super rendah menggunakan Cloudflare Workers.

Bukannya berjalan lancar, Anda justru disambut oleh rentetan error fatal saat proses build atau runtime. Mengapa hal ini terjadi? Di ruangproses.id kali ini, kita akan membedah secara jujur akar masalah arsitektur yang membuat kode Node.js konvensional Anda membentur dinding ekosistem serverless ini, serta bagaimana menyelesaikannya tanpa pusing.

Ilustrasi masalah kompatibilitas arsitektur antara Node.js runtime dan Cloudflare Workers V8 Isolates.

Akar Masalah: Benturan Dua Arsitektur Runtime

Masalah utama dari kegagalan ini bukanlah karena kode Anda salah secara logika, melainkan karena perbedaan fundamental antara lingkungan eksekusi (runtime).

Kode hasil generate Postman umumnya dirancang untuk runtime Node.js tradisional (seperti yang berjalan di VPS, PC lokal, atau platform kontainer). Di sisi lain, platform serverless dari Cloudflare ini berjalan di atas V8 Isolates milik Google.

Penting untuk dicatat: platform ini bukan Node.js. Ia tidak memiliki sistem operasi virtual (VM) utuh di belakangnya demi mengejar cold start yang mendekati nol milidetik. Untuk memahami mengapa teknologi ini jauh lebih ringan daripada VM tradisional, Anda bisa membaca dokumentasi resmi tentang Cloudflare Workers Architecture Reference.

Ada tiga batasan arsitektur utama yang membuat kode Node.js konvensional Anda langsung crash:

  • Ketergantungan pada Express.js dan Modul HTTP Internal: Kerangka kerja seperti Express sangat bergantung pada modul internal Node.js seperti http, net, dan stream. Lingkungan kerja serverless ini tidak mengenal entitas ini dan menggunakan standar Web APIs modern seperti fetch(), Request, dan Response.
  • Absennya File System (Hard Drive): Di server tradisional, fungsi seperti fs.readdirSync() atau modul path digunakan secara dinamis untuk memindai folder (misalnya folder ./tools pada server MCP). Di sini, tidak ada hard drive saat runtime. Semua kode harus dibundel menjadi satu file skrip tunggal sebelum dieksekusi.
  • Mekanisme Environment Variables (dotenv): Membaca file .env dari disk menggunakan pustaka dotenv akan langsung gagal karena absennya sistem file. Platform membutuhkan injeksi variabel langsung ke dalam objek env global saat request masuk.

Kegagalan Migrasi mcpServer.js

Mari kita bedah sebuah kasus nyata. Katakanlah kita memiliki file mcpServer.js hasil generate yang menggunakan Express untuk melayani protokol MCP, lengkap dengan pemindaian tools otomatis dari sebuah direktori.

Kode Asli (Berjalan Sempurna di Node.js / Localhost):

JavaScript

// mcpServer.js - Versi Node.js Tradisional
import express from 'express';
import dotenv from 'dotenv';
import fs from 'fs';
import path from 'path';

dotenv.config();
const app = express();
app.use(express.json());

// Masalah 1: Membaca file system secara dinamis
function discoverTools(dir) {
    const fullPath = path.resolve(dir);
    return fs.readdirSync(fullPath).map(file => require(path.join(fullPath, file)));
}

const tools = discoverTools('./tools');

app.post('/mcp/rpc', (req, res) => {
    // Memproses protokol MCP JSON-RPC
    const apiKey = process.env.POSTMAN_API_KEY; // Masalah 2: process.env tidak ada
    res.json({ jsonrpc: "2.0", result: "Tools executed with key: " + apiKey });
});

app.listen(3000, () => console.log('Server running on port 3000')); // Masalah 3: app.listen() ilegal

Jika kode di atas langsung Anda dorong menggunakan Wrangler, Anda akan langsung mendapatkan pesan error fatal seperti: Uncaught ReferenceError: require is not defined atau Module not found: Error: Can't resolve 'fs'.

Dua Jalur yang Bisa Anda Tempuh

Untuk mengatasi masalah ini, Anda memiliki dua pilihan rasional tergantung pada ketersediaan waktu dan kebutuhan infrastruktur Anda.

Jalur 1: Pindah ke Platform Berbasis Kontainer (Rekomendasi Tercepat)

Jika proyek Anda sudah memiliki Dockerfile bawaan dari Postman, jangan buang waktu Anda untuk memaksa masuk ke ekosistem serverless ini. Keberadaan Dockerfile adalah indikasi kuat bahwa proyek Anda siap di-host di platform yang mendukung Docker-native container.

Anda bisa memanfaatkan layanan seperti Railway.app, Render.com, atau Fly.io. Anda cukup menghubungkan repositori Git, dan platform tersebut akan membaca Dockerfile Anda secara otomatis. Semua fungsionalitas Express, File System scanning, dan process.env akan bekerja 100% tanpa mengubah satu baris kode pun.

Jika Anda memilih opsi ini dan ingin mengotomatiskan prosesnya, silakan baca artikel kami tentang cara setup CI/CD deployment dengan Docker agar proses rilis Anda berjalan otomatis.

Jalur 2: Refaktor Total ke Standar Web API

Namun, jika kebijakan infrastruktur mewajibkan Anda menggunakan Cloudflare Workers, kita harus membuang Express dan menulis ulang transport layer-nya menggunakan pendekatan Web Standard.

Berikut adalah hasil transformasi kode mcpServer.js agar kompatibel dengan V8 Isolates:

JavaScript

// mcpServer.js - Versi Cloudflare Workers Moderen

// SOLUSI MASALAH 1: Hilangkan fs.readdir, gunakan static import secara eksplisit
import { getPostmanCollections } from './tools/collections.js';
import { runEnvironmentScanner } from './tools/environments.js';

const toolsMap = {
  getPostmanCollections,
  runEnvironmentScanner
};

// Ekspor default objek fetch untuk menangani request masuk
export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);

    // Proteksi Route dan Method
    if (request.method !== "POST" || url.pathname !== "/mcp/rpc") {
      return new Response(JSON.stringify({ error: "Method or Path Not Allowed" }), { 
        status: 405,
        headers: { "Content-Type": "application/json" }
      });
    }

    try {
      // Mengambil data JSON dari request stream bawaan Web API
      const body = await request.json();
      
      // SOLUSI MASALAH 2: Mengambil API Key langsung dari objek env yang disuntikkan
      const apiKey = env.POSTMAN_API_KEY; 

      if (!apiKey) {
        return new Response(JSON.stringify({ error: "Unauthorized: Missing Postman API Key" }), { status: 401 });
      }

      // Contoh eksekusi tools berdasarkan data JSON-RPC yang masuk
      const requestedMethod = body.method;
      let executionResult = "Method not found";

      if (toolsMap[requestedMethod]) {
         executionResult = await toolsMap[requestedMethod](body.params, apiKey);
      }

      // Mengembalikan response standar Web API
      return new Response(JSON.stringify({
        jsonrpc: "2.0",
        id: body.id || null,
        result: executionResult
      }), {
        status: 200,
        headers: { "Content-Type": "application/json" }
      });

    } catch (error) {
      return new Response(JSON.stringify({ jsonrpc: "2.0", error: { code: -32603, message: error.message } }), {
        status: 500,
        headers: { "Content-Type": "application/json" }
      });
    }
  }
};

Apa saja yang berubah dari transformasi di atas?

  1. Tidak ada app.listen(): Kode bersifat event-driven. Server hanya bangun ketika ada HTTP request masuk yang memicu fungsi fetch().
  2. Import Manual Terbuka: Kita membuang modul fs dan memetakan fungsi-fungsi tool secara manual di dalam objek toolsMap.
  3. Injeksi env: Variabel rahasia seperti POSTMAN_API_KEY tidak lagi dibaca dari file .env. Anda cukup mengaturnya secara aman via Cloudflare Environment Variables Guide pada menu Settings -> Variables atau file konfigurasi Wrangler Anda.

Kesimpulan

Sebagai penutup proses hari ini, mari kita buat aturan praktis yang jujur untuk arsitektur Anda:

  • Pilihlah Jalur Kontainer (Render/Railway) jika server MCP Anda membutuhkan banyak tools dinamis yang filenya sering berubah, membutuhkan protokol SSE (Server-Sent Events) yang kompleks, atau jika Anda dikejar deadline rilis cepat.
  • Pilihlah Jalur Cloudflare Workers jika Anda bersedia meluangkan waktu merombak kode demi mendapatkan infrastruktur tanpa biaya perawatan server (zero maintenance), biaya operasional super murah, dan latensi eksekusi global yang instan.

Kenali runtime tempat kode Anda berpijak, karena performa terbaik selalu lahir dari keselarasan antara kode dan infrastrukturnya. Jika Anda baru pertama kali bermain dengan ekosistem ini, pastikan Anda juga sudah memahami panduan dasar setup Model Context Protocol (MCP) agar tidak salah mengonfigurasi komponen core server Anda. Sampai jumpa di bedah proses berikutnya!

Leave a Reply

Your email address will not be published. Required fields are marked *