mcp-dummy-server
README.md
# mcp-dummy-server
MCP server dengan data dari **PostgreSQL**, transport **Streamable HTTP** mode **stateless** — cocok untuk multi-instance/replica di Fly.io.
## Struktur
```
src/
data/
db.js # koneksi pool PostgreSQL (pg)
hariRepository.js # query ke tabel `hari`
kodeposRepository.js # query ke tabel `kodepos`
apiKeysRepository.js # auth: bcrypt.compare ke tabel `api_keys`
tools/
index.js # definisi & registrasi semua MCP tools
server.js # express + StreamableHTTPServerTransport + auth middleware
db/
migrations/
001_create_tables.sql # schema: hari, kodepos, api_keys
seed.js # isi data awal (jalankan sekali setelah db:up)
docker-compose.yml # Postgres 16-alpine lokal, port 5435
```
**Pindah Provider DB (Dinamis):** Cukup ganti nilai `DATABASE_URL` di `.env` (Docker Lokal ↔ Supabase ↔ Neon ↔ Fly Postgres) tanpa perlu mengubah kode sumber sama sekali.
## Tools yang tersedia
| Tool | Input | Fungsi |
|---|---|---|
| `list_hari` | - | Semua hari Senin-Minggu |
| `get_hari` | `id` (1-7) | Detail 1 hari |
| `list_kodepos` | - | Semua data kodepos |
| `search_kodepos_by_kota` | `kota` (string) | Cari kodepos berdasarkan nama kota |
| `get_kodepos` | `kodepos` (5 digit) | Detail exact match kodepos |
## Jalan lokal (dengan Postgres via Docker)
```bash
npm install
cp .env.example .env # isi MCP_API_KEY, sesuaikan DATABASE_URL dll.
# 1. Jalankan Postgres Docker (port 5435 — hindari conflict 5432/5433/5434)
npm run db:up
# 2. Seed: buat tabel + isi data awal + hash API key ke DB
npm run db:seed
# 3. Jalankan server
npm start
# server listen di http://localhost:8080/mcp
```
Test cepat:
```bash
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "x-api-key: $(grep MCP_API_KEY .env | cut -d= -f2)" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
```
Stop Postgres:
```bash
npm run db:down
```
## Auth
Server dilindungi API key berbasis database (tabel `api_keys`, key disimpan sebagai **bcrypt hash** — tidak pernah plaintext).
- Kalau `DATABASE_URL` **tidak di-set** → auth otomatis nonaktif (dev mode), muncul warning di log. **Jangan biarkan di production.**
- Kalau `DATABASE_URL` di-set → semua request ke `/mcp` wajib bawa header `x-api-key` yang cocok (diverifikasi via bcrypt.compare ke DB).
- `last_used_at` di tabel `api_keys` otomatis diupdate setiap kali key dipakai (fire-and-forget).
Jalankan lokal dengan auth aktif:
```bash
# Pastikan DATABASE_URL sudah di-.env dan db:seed sudah dijalankan
MCP_API_KEY=<your-key> npm start
```
## Deploy ke Fly.io
```bash
# install flyctl kalau belum: https://fly.io/docs/flyctl/install/
fly auth login
# dari folder project ini
fly launch --no-deploy # generate app, JANGAN overwrite fly.toml yang sudah ada
# WAJIB: set secrets (jangan taruh di fly.toml / git)
fly secrets set MCP_API_KEY="ganti-dengan-string-random-yang-panjang"
fly secrets set DATABASE_URL="postgresql://user:pass@host:5432/dbname"
fly deploy
```
> **Catatan:** Untuk production, gunakan Postgres yang dihosting (mis. Fly Postgres, Supabase, Neon). Jalankan `npm run db:seed` sekali saja terhadap DB production untuk inisialisasi tabel dan API key pertama.
Setelah deploy, endpoint MCP ada di:
```
https://<nama-app-kamu>.fly.dev/mcp
```
## Konek dari Antigravity IDE
```json
{
"mcpServers": {
"dummy-data-mcp": {
"url": "https://<nama-app-kamu>.fly.dev/mcp",
"headers": {
"x-api-key": "ganti-dengan-string-random-yang-panjang"
}
}
}
}
```
Kalau Antigravity IDE hanya mendukung MCP via stdio, pakai proxy `mcp-remote`:
```json
{
"mcpServers": {
"dummy-data-mcp": {
"command": "npx",
"args": [
"-y", "mcp-remote", "https://<nama-app-kamu>.fly.dev/mcp",
"--header", "x-api-key:ganti-dengan-string-random-yang-panjang"
]
}
}
}
```
## Manajemen API Key
API key disimpan di tabel `api_keys` sebagai bcrypt hash. Untuk menambah key baru:
```bash
# Contoh: tambah key baru langsung ke DB
node -e "
import('bcrypt').then(async ({default: bcrypt}) => {
const hash = await bcrypt.hash('key-baru-kamu', 12);
console.log('INSERT INTO api_keys (key_hash, label) VALUES (\'' + hash + '\', \'nama-label\');');
});
"
# Salin output INSERT-nya, jalankan di psql atau DB client
```
## Catatan keamanan
- API key tidak pernah disimpan plaintext — hanya bcrypt hash di DB.
- `MCP_API_KEY` di `.env` hanya dipakai saat `db:seed` (untuk generate hash pertama kali). Selebihnya tidak dipakai server.
- Untuk revoke key: `UPDATE api_keys SET is_active = false WHERE id = <id>;`
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues