Multi Target Http
FreeNot checkedMCP-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
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
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/
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íbeINSERT/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: trueno 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: truese você de fato precisa das linhas inteiras. - Rate limit. Por-source-IP, janela deslizante de 60s, cap configurável.
- Health per-target.
GET /healthpercorre todo adapter e reportaup | 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ê quer127.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ó
SELECTnos 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, abaixaCACHE_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/WITHsem 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
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/
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: truebypasses. - Auto-summarize. > 200 rows → aggregates + 50-row preview.
- Rate limit. Per-source-IP, sliding 60s window.
- Per-target health.
GET /healthreportsup | downper 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
matheus@devops:~$ _
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-httpFAQ
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
wenb1n-dev/SmartDB_MCP
A universal database MCP server supporting simultaneous connections to multiple databases. It provides tools for database operations, health analysis, SQL optim
by wenb1n-devPostgres Server
This server enables interaction with PostgreSQL databases through the Model Context Protocol, optimized for the AWS Bedrock AgentCore Runtime. It provides tools
by madhurprashPostgres
Query your database in natural language
by AnthropicPostgreSQL
Read-only database access with schema inspection.
by modelcontextprotocolCompare 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
