Skip to content

MatheusHenriquePrates/mcp-multi-target-http

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

PT-BR Β  EN

PT-BR

matheus@devops:~$ cat sobre.txt

Servidor HTTP pequeno que expΓ΅e N backends de database/REST atrΓ‘s de um ΓΊnico endpoint protegido por auth, com cache LRU, allow-list de SQL, health check por target, rate limit, e audit log JSONL.

Pensado pro caso onde um LLM agent (ou qualquer client thin) precisa de acesso de leitura em vΓ‘rias fontes de dados heterogΓͺneas e vocΓͺ nΓ£o quer rodar um servidor separado por fonte.

matheus@devops:~$ ls stack/

Node.js PostgreSQL MySQL MCP Claude Docker

matheus@devops:~$ cat por-que-existe.txt

O conselho padrΓ£o pra "deixa meu AI agent consultar banco" Γ© expor um MCP server por banco. Funciona atΓ© vocΓͺ ter trΓͺs.

TrΓͺs vira problema porque:

  • TrΓͺs deployments. TrΓͺs healthchecks. TrΓͺs conjuntos de credencial pra rotacionar.
  • TrΓͺs lugares pra adicionar caching, rate-limit, audit log β€” e eles divergem.
  • TrΓͺs "shapes" diferentes de SQL safety (ou, mais provΓ‘vel, sΓ³ um deles faz certo).
  • O agent precisa saber qual servidor perguntar pra quΓͺ. LΓ³gica de roteamento sai da data layer e vai pros prompts.

Isso aqui Γ© um servidor HTTP com um token de auth. Adiciona target editando config/targets.json β€” sem processo novo, sem imagem nova, sem regra de ingress nova. O servidor descobre targets no startup, expΓ΅e cada um em POST /query/<target>, e reporta health por-target em GET /health.

matheus@devops:~$ cat o-que-faz.txt
  • Auth. Bearer token ΓΊnico (env var). Rejeita qualquer outra coisa.
  • SQL allow-list. SΓ³ SELECT / SHOW / DESCRIBE / EXPLAIN / WITH. ProΓ­be INSERT/UPDATE/DELETE/DROP/TRUNCATE/..., statement stacking (;), e comentΓ‘rio SQL. Defesa em profundidade β€” tambΓ©m usa um user de DB com sΓ³ SELECT.
  • LRU cache. Keyed por (target, sql-hash). TTL configurΓ‘vel por request (nocache: true no body bypassa).
  • Auto-summarize. Query que retorna mais que 200 linhas vira agregados (sum/avg/min/max/top-N/by-month) + preview de 50 linhas, mantendo a resposta dentro do orΓ§amento de contexto tΓ­pico de LLM. nocache: true se vocΓͺ de fato precisa das linhas inteiras.
  • Rate limit. Por-source-IP, janela deslizante de 60s, cap configurΓ‘vel.
  • Health per-target. GET /health percorre todo adapter e reporta up | down. Sem auth (pra watchdog poder bater).
  • Audit log. JSONL append-only de toda request: timestamp, IP, target, status, row count, elapsed ms, primeiros 500 chars do SQL. AUDIT_LOG_FILE= vazio desabilita.
matheus@devops:~$ cat arquitetura.txt
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                β”‚   mcp-multi-target-http (1 processo) β”‚
                β”‚                                      β”‚
client ──Bearer──> β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
                β”‚   β”‚ auth + rate limit + SQL      β”‚   β”‚
                β”‚   β”‚ allow-list + LRU cache       β”‚   β”‚
                β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
                β”‚              β”‚                       β”‚
                β”‚       β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”                β”‚
                β”‚       β”‚ dispatcher  β”‚                β”‚
                β”‚       β””β”€β”€β”¬β”€β”€β”€β”¬β”€β”€β”€β”¬β”€β”€β”˜                β”‚
                β”‚          β”‚   β”‚   β”‚                   β”‚
                β”‚   β”Œβ”€β”€β”€β”€β”€β”€β–Όβ” β”Œβ–Όβ”€β”€β” β”Œβ–Όβ”€β”€β”€β”€β”€β”€β” adapters β”‚
                β”‚   β”‚  pg   β”‚ β”‚mysβ”‚ β”‚ rest  β”‚          β”‚
                β”‚   β””β”€β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”¬β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”˜          β”‚
                β””β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚       β”‚       β”‚
                   β”Œβ”€β”€β”€β”€β–Όβ” β”Œβ”€β”€β”€β”€β–Όβ”€β”€β” β”Œβ”€β”€β–Όβ”€β”€β”€β”€β”€β”
                   β”‚ PG  β”‚ β”‚ MySQL β”‚ β”‚ REST   β”‚
                   β”‚pool β”‚ β”‚ pool  β”‚ β”‚ host   β”‚
                   β””β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Cada backend Γ© embrulhado num adapter pequeno com o mesmo contrato β€” { healthcheck(), query(sql, opts), close() }. Adicionar tipo novo de backend Γ© escrever um arquivo em lib/adapters/ e registrar em lib/loader.mjs.

Dois adapters no repo: postgres.mjs (usando pg) e mysql.mjs (usando mysql2/promise). ~30 linhas cada.

matheus@devops:~$ ./quick-start.sh
git clone https://github.com/MatheusHenriquePrates/mcp-multi-target-http
cd mcp-multi-target-http
npm install
cp .env.example .env
cp config/targets.example.json config/targets.json

# Gera bearer token
echo "AUTH_TOKEN=$(openssl rand -hex 32)" >> .env

# Credenciais dos bancos (os targets de exemplo leem essas env vars):
echo "PG_PRIMARY_PASSWORD=..." >> .env
echo "MYSQL_REPORTING_PASSWORD=..." >> .env

node server.mjs

Em outro terminal:

# Health (sem auth)
curl -s http://127.0.0.1:4910/health | jq

# Lista targets configurados
curl -s -H "Authorization: Bearer $AUTH_TOKEN" http://127.0.0.1:4910/targets | jq

# Query (auth + SQL allow-listed)
curl -s -X POST http://127.0.0.1:4910/query/primary-pg \
  -H "Authorization: Bearer $AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sql": "SELECT 1 AS one"}' | jq
matheus@devops:~$ cat targets.json

config/targets.json declara cada backend. Use placeholders ${ENV_VAR} pra qualquer coisa sensΓ­vel β€” resolvidos do environment no startup, entΓ£o secret fica fora do arquivo:

{
  "targets": {
    "primary-pg": {
      "kind": "postgres",
      "config": {
        "host": "127.0.0.1",
        "port": 5432,
        "user": "appuser",
        "password": "${PG_PRIMARY_PASSWORD}",
        "database": "primary",
        "connectionTimeoutMillis": 8000,
        "max": 5
      }
    },
    "reporting-mysql": {
      "kind": "mysql",
      "config": {
        "host": "127.0.0.1",
        "port": 3306,
        "user": "ro_reports",
        "password": "${MYSQL_REPORTING_PASSWORD}",
        "database": "reporting",
        "connectionLimit": 5
      }
    }
  }
}

O kind de cada target mapeia pra um adapter; o config Γ© passado direto pro driver (pg.Pool, mysql.createPool). Qualquer coisa que o driver aceita funciona β€” pool, SSL, statement timeout etc.

matheus@devops:~$ ls endpoints/
Endpoint MΓ©todo Auth Body O que faz
/ GET nΓ£o β€” Banner + lista de targets configurados
/health GET nΓ£o β€” Status per-target up/down, stats de cache
/targets GET sim β€” Lista de target names configurados
/query/<target> POST sim {sql, nocache?} Roda SELECT validado contra <target>

Shape da response (resultado pequeno):

{
  "ok": true,
  "target": "primary-pg",
  "rowCount": 3,
  "truncated": false,
  "elapsedMs": 12,
  "cache": "MISS",
  "rows": [ ]
}

Shape da response (resultado grande, > 200 linhas): rows Γ© substituΓ­do por summary + preview + hint. Pra forΓ§ar linhas inteiras, passa { "sql": "...", "nocache": true }.

matheus@devops:~$ cat watchdog.txt

watchdog.sh Γ© um shell script auto-contido que bate em /health a cada 60s, rastreia falhas consecutivas por target, e dispara alerta no Telegram depois de N ciclos. A lista de targets pra vigiar Γ© derivada da prΓ³pria resposta do /health, entΓ£o o watchdog nΓ£o precisa reconfigurar quando target Γ© adicionado ou removido.

* * * * * TELEGRAM_TOKEN=... TELEGRAM_CHAT_ID=... /usr/local/bin/watchdog.sh

Deixa as env vars do Telegram vazias pra desabilitar alerta (continua logando falha localmente).

matheus@devops:~$ ./deploy.sh

systemd

[Unit]
Description=mcp-multi-target-http
After=network.target

[Service]
Type=simple
User=mcp
WorkingDirectory=/opt/mcp-multi-target-http
EnvironmentFile=/etc/mcp-multi-target-http.env
ExecStart=/usr/bin/node server.mjs
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Docker

docker build -t mcp-multi-target-http .
docker run -d --name mcp \
  -p 127.0.0.1:4910:4910 \
  -v "$PWD/config:/app/config:ro" \
  -v "$PWD/logs:/app/logs" \
  --env-file .env \
  mcp-multi-target-http

ExpΓ΅e sΓ³ em 127.0.0.1 e bota seu reverse proxy existente na frente (nginx, Caddy, Traefik). O container nΓ£o faz terminaΓ§Γ£o TLS.

matheus@devops:~$ cat notas.txt
  • Bind interface. Default 0.0.0.0. Pra maioria dos deploys vocΓͺ quer 127.0.0.1 + reverse proxy. O server nΓ£o faz TLS.
  • Grants do user de DB. A allow-list pega modificaΓ§Γ£o Γ³bvia, mas nΓ£o Γ© sandbox. Sempre usa user de banco com sΓ³ SELECT nos schemas que esse server vΓͺ. Se o user pode escrever, o validator Γ© sua ΓΊltima linha β€” nΓ£o seja a ΓΊnica.
  • InvalidaΓ§Γ£o de cache. NΓ£o tem. Entradas expiram pelo TTL. Pra dado fresco numa call, passa nocache: true. Global, abaixa CACHE_DEFAULT_TTL_SEC.
  • Rate limit. SΓ³ por-IP. AtrΓ‘s de reverse proxy, set proxy_set_header X-Forwarded-For ... e ajusta se quer limite por IP real.
  • Audit em containers. Pra log de auditoria sobreviver restart, monta /app/logs.
matheus@devops:~$ cat o-que-NAO-e.txt
  • NΓ£o Γ© um MCP server "de verdade" no sentido estrito do Model Context Protocol. Γ‰ HTTP server que preenche o mesmo nicho (um endpoint LLM-facing expondo tools) mas com shape POST /query/<target> muito mais simples. Se precisa MCP de fato, embrulha esse server num shim MCP.
  • NΓ£o Γ© proxy de escrita. Rejeita tudo exceto SELECT/SHOW/DESCRIBE/EXPLAIN/WITH sem escape hatch.
  • NΓ£o Γ© query planner / federated query engine. Cada call bate em exatamente um target. Pra join cross-target, vocΓͺ faz client-side (ou no LLM).
  • NΓ£o Γ© substituto pra API de dados de verdade. Se o acesso primΓ‘rio do time Γ© "queries parametrizadas, auditadas, schema-aware, com row-level access control", constrΓ³i GraphQL/REST de verdade. Isso aqui Γ© pra camada ad hoc.
matheus@devops:~$ cat LICENSE

MIT. Veja LICENSE.

matheus@devops:~$ contact

LinkedIn Email

matheus@devops:~$ _

EN

matheus@devops:~$ cat about.txt

A small HTTP server that exposes N database/REST backends behind one auth-protected endpoint, with LRU cache, SQL allow-list, per-target health checks, rate limiting, and JSONL audit log.

Designed for the case where an LLM agent (or any thin client) needs read access to several heterogeneous data sources and you don't want to run a separate server per source.

matheus@devops:~$ ls stack/

Node.js PostgreSQL MySQL MCP Claude Docker

matheus@devops:~$ cat what-it-does.txt
  • Auth. Single Bearer token (env var).
  • SQL allow-list. Only SELECT/SHOW/DESCRIBE/EXPLAIN/WITH. Forbids modifications, statement stacking, and SQL comments.
  • LRU cache. Keyed by (target, sql-hash). nocache: true bypasses.
  • Auto-summarize. > 200 rows β†’ aggregates + 50-row preview.
  • Rate limit. Per-source-IP, sliding 60s window.
  • Per-target health. GET /health reports up | down per adapter.
  • Audit log. JSONL append-only of every request.
matheus@devops:~$ ./quick-start.sh
git clone https://github.com/MatheusHenriquePrates/mcp-multi-target-http
cd mcp-multi-target-http
npm install
cp .env.example .env
cp config/targets.example.json config/targets.json

echo "AUTH_TOKEN=$(openssl rand -hex 32)" >> .env

node server.mjs
matheus@devops:~$ ls endpoints/
Endpoint Method Auth Body What
/ GET no β€” Service banner + targets
/health GET no β€” Per-target up/down, cache stats
/targets GET yes β€” Configured target names
/query/<target> POST yes {sql, nocache?} Validated SELECT against <target>
matheus@devops:~$ cat what-this-is-NOT.txt
  • Not a real MCP server in the strict Model Context Protocol sense. Simpler POST /query/<target> shape.
  • Not a write proxy. Only SELECT/SHOW/DESCRIBE/EXPLAIN/WITH.
  • Not a federated query engine. Each call hits exactly one target.
  • Not a replacement for a proper data API. This is the ad hoc layer.
matheus@devops:~$ cat LICENSE

MIT. See LICENSE.

matheus@devops:~$ contact

LinkedIn Email

matheus@devops:~$ _

About

MCP-style HTTP server that multiplexes N database/REST backends behind one auth-protected endpoint - LRU cache, SQL allow-list, rate limiting, and per-target health checks.

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors