Skip to content

Plane MCP untuk self-host, tanpa OAuth, cukup header auth

Adityo Guni Waluyo

Mode http plane-mcp-server selalu membangun OAuth dan crash tanpa env. Wrapper 16 baris membuka header auth, token tetap divalidasi ke Plane.

Ringkasan

Container Plane MCP versi 033 langsung crash karena mode http maksa bikin OAuth app padahal env kosong. Ternyata kode header auth udah ada via get_header_mcp cuma nggak diekspos jadi gue bikin wrapper 16 baris biar validasi token langsung ke Plane. Enak mode stateless tanpa sesi jalan di loopback 8211 cuma butuh header Authorization tiap request.

Log container langsung jerket pas deploy pertama: proses MCP-nya mati seketika. Yang saya jalankan itu makeplane/plane-mcp-server versi 0.3.3, server resmi buat nyambungin agen AI ke Plane. Error-nya konsisten: dia mencoba membangun aplikasi OAuth, padahal environment variable OAuth provider nggak pernah saya isi. Deployment ini murni internal, nggak ada niatnya buat jadi hosted service.

Dugaan pertama saya meleset. Saya kira mode http di paket self-hosted bakal narima opsi API key, mirip layanan hosted-nya yang punya endpoint PAT terpisah dengan header Authorization dan X-Workspace-slug [5]. Saya coba cari flag buat skip OAuth. Nggak ketemu.

Setelah bongkar kode sumbernya, baru kelihatan: mode http itu hardcoded untuk selalu membangun OAuth app [5]. Env kosong berarti crash. Transport header-auth-nya sebenernya udah ada di dalam kode, tinggal panggil lewat fungsi get_header_mcp() [7]. Cuma nggak diekspos sebagai opsi CLI.

Jadi saya bikin wrapper Python setinggi 16 baris, run_header.py, yang manggil fungsi itu langsung. Nggak ada satu baris kode upstream pun yang diubah:

import uvicorn

from plane_mcp.server import get_header_mcp

mcp = get_header_mcp()
app = mcp.http_app(stateless_http=True)

uvicorn.run(app, host=HOST, port=8211, log_level="info")  # HOST: semua interface di dalam kontainer

Di compose, service ini jalan di profile mcp dan portnya cuma dipublish ke loopback host di 8211. Dari dalam kontainer, dia nyapa Plane lewat jaringan internal, bukan lewat internet. Saya pilih mode stateless, jadi tiap request berdiri sendiri tanpa sesi yang perlu dijaga. Pola panggilan kliennya nggak teratur, jadi sesi panjang cuma nambah beban pikiran. Nggak ada egress ke luar, nggak ada rahasia yang disimpan di kontainer, token yang beneran memberi akses tetap di tangan klien.

Resource server, bukan mesin token

Cara ngecek ini beneran jalan gampang: kirim POST ke endpoint initialize tanpa token, terus ulangi pakai token ngasal. Dua-duanya balik 401 invalid_token. Justru dari error ini saya yakin setup-nya benar, karena itu tandanya server benar-benar memvalidasi token langsung ke instance Plane yang hidup, bukan sekadar nerima apa pun.

Perilaku ini memang desain spesifikasinya. MCP mendefinisikan otorisasi sebagai urusan level transport, dan server yang diproteksi berperan sebagai resource server OAuth 2.1 [6]. Di HTTP, server kayak gini nggak pernah login-in siapa pun dan nggak ngeluarin token. Tugasnya satu: nerima header Authorization di tiap request dan memutuskan token-nya valid apa nggak [7]. Sisanya, mekanisme verifikatornya dibebaskan ke implementer.

Buat apa semua ini

Server MCP-nya sendiri bukan mainan kecil. Ada 30 tools yang mencakup 204 operasi: baca dan kelola proyek, work item, cycle, sampai module [5]. Begitu nyambung, agen AI lokal bisa langsung kerja di atas data Plane yang self-hosted, tanpa satu pun port standar kebuka ke internet.

Satu catatan buat yang mau tiru: kalo nggak butuh HTTP sama sekali, README server resminya nyediain jalur stdio lewat uvx yang jauh lebih simple, tinggal lempar API key dan workspace slug ke environment [5]. Saya tetap milih jalur HTTP karena kliennya butuh URL yang bisa diarahkan dari mana saja di jaringan lokal, dan profil compose bikin service ini gampang dihidupkan atau dimatiin terpisah dari stack utama.

Pelajaran yang saya bawa: buat deployment internal, TokenVerifier tipis yang ngecek ke API aplikasi yang sudah ada itu cukup. Nggak perlu gerbang OAuth lengkap cuma buat agen di jaringan sendiri. Dan satu lagi, jangan langsung nerima bahwa mode bawaan adalah satu-satunya jalan. Kadang jarak antara gagal jalan dan produksi cuma selebar 16 baris wrapper.

Sumber

  1. Repository resmi makeplane/plane-mcp-server
  2. Spesifikasi MCP: authorization
  3. MCP Python SDK: authorization (resource server)

Artikel terkait