Command Palette

Search for a command to run...

UnylyUnyly
Browse all

Multi Target Http

FreeNot checked

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 he

GitHubEmbed

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.

README

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.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:~$ _

from github.com/MatheusHenriquePrates/mcp-multi-target-http

Installing Multi Target Http

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/MatheusHenriquePrates/mcp-multi-target-http

FAQ

Is Multi Target Http MCP free?

Yes, Multi Target Http MCP is free — one-click install via Unyly at no cost.

Does Multi Target Http need an API key?

No, Multi Target Http runs without API keys or environment variables.

Is Multi Target Http hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install Multi Target Http in Claude Desktop, Claude Code or Cursor?

Open Multi Target Http on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare Multi Target Http with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All data MCPs