- +paperclip (pm2, 3168), +easypanel/infisical (systemd fastmcp-proxy, 3206/3207) - contadores corrigidos (30->33 services), proxima porta livre 3185->3208
10 KiB
name, description, context
| name | description | context |
|---|---|---|
| gateway-check | Gestao completa dos MCPs no gateway.descomplicar.pt — health check, restart, troubleshoot, mapa de portas, adicionar MCPs. Usar quando MCP falha, health check, ou gestao gateway. | fork |
/gateway-check v2.0
Gestao e health check dos MCPs no servidor gateway (VM 103, gateway.descomplicar.pt).
Referencia: Memory mcp-gateway.md | PROC-MCP-Desenvolvimento.md
Acesso
- VM: 103 no Proxmox (QEMU)
- IP: 5.9.90.69
- SSH:
mcp__ssh-unified__ssh_execute(server="gateway") - HTTPS:
https://gateway.descomplicar.pt/v1/<nome>/mcp - Nginx whitelist: 188.251.199.30 (IP fixo NOS). Se 403 -> verificar IP com
curl -4 ifconfig.me - Nginx config:
/etc/nginx/sites-enabled/no gateway - NAO confundir com: server (VM 100, 5.9.90.105), easy (VM 101, 5.9.90.70), dev (LXC 102)
Mapa de MCPs (33 services — actualizado 08-08-2026)
pm2 (Node.js — /opt/mcp-gateway/)
| pm2 ID | Nome | Porta | nginx path | Prioridade |
|---|---|---|---|---|
| 0 | mcp-desk-crm | 3150 | /v1/desk-crm/mcp | P1 |
| 1 | mcp-memory | 3151 | /v1/memory/mcp | P1 |
| 2 | mcp-wikijs | 3152 | /v1/wikijs/mcp | P3 |
| 5 | mcp-moloni | 3158 | /v1/moloni/mcp | P2 |
| 6 | mcp-youtube-research | 3157 | /v1/youtube-research/mcp | P3 |
| 3 | mcp-paperclip | 3168 | /v1/paperclip/mcp | P3 |
systemd (26 services)
| Service | Porta | nginx path | Tipo | Prioridade |
|---|---|---|---|---|
| google-workspace-mcp | 3164 | /v1/google-workspace/mcp | Python/FastMCP | P1 |
| mcp-time | 3163 | /v1/mcp-time/mcp | Node | P1 |
| imap-enterprise | 3160 | /v1/imap/mcp | Node | P2 |
| gitea-mcp | 3162 | /v1/gitea/mcp | Go | P2 |
| n8n-mcp | 3161 | /v1/n8n/mcp | Node | P2 |
| gsc-mcp | 3153 | /v1/gsc/mcp | Python/FastMCP | P2 |
| google-analytics-mcp | 3156 | /v1/google-analytics/mcp | Python/FastMCP | P2 |
| context7-mcp | 3159 | /v1/context7/mcp | Node | P2 |
| mcp-reonic | 3170 | /v1/reonic/mcp | Node | P3 |
| cloudflare-dns-mcp | 3171 | /v1/cloudflare-dns/mcp | supergateway | P3 |
| magic-mcp | 3172 | /v1/magic/mcp | supergateway | P3 |
| mcp-echarts-mcp | 3173 | /v1/mcp-echarts/mcp | supergateway | P3 |
| mcp-mermaid-mcp | 3174 | /v1/mcp-mermaid/mcp | supergateway | P3 |
| metabase-mcp | 3175 | /v1/metabase/mcp | supergateway | P3 |
| pixabay-mcp | 3176 | /v1/pixabay/mcp | supergateway | P3 |
| replicate-mcp | 3177 | /v1/replicate/mcp | supergateway | P3 |
| outline-api-mcp | 3178 | /v1/outline-api/mcp | supergateway | P3 |
| pexels-mcp | 3179 | /v1/pexels/mcp | supergateway | P3 |
| penpot-mcp | 3180 | /v1/penpot/mcp | supergateway | P3 |
| vimeo-mcp | 3181 | /v1/vimeo/mcp | supergateway | P3 |
| presenton-mcp | 3182 | /v1/presenton/mcp | supergateway | P3 |
| cwp-mcp | 3183 | /v1/cwp/mcp | supergateway | P3 |
| design-engine-mcp | 3184 | /v1/design-engine/mcp | supergateway | P3 |
| mcp-youtube-router | 3198 | /v1/youtube/mcp | Python/Starlette | P3 |
| mcp-youtube | 3187 | (nenhuma — upstream interno) | Python/FastMCP | P3 |
| mcp-youtube-desc | 3199 | (nenhuma — upstream interno) | Python/FastMCP | P3 |
| ssh-unified | 3192 | /v1/ssh-unified/mcp | Python/FastMCP | P3 |
| easypanel-mcp | 3206 | /v1/easypanel/mcp | fastmcp-proxy (Node stdio wrapped) | P2 |
| infisical-mcp | 3207 | /v1/infisical/mcp | fastmcp-proxy (Node stdio wrapped) | P2 |
Prioridades: P1=critico (bloqueia trabalho) | P2=importante (degrada workflow) | P3=util
Proxima porta livre: 3208. Verificar sempre — este mapa
ficou desactualizado antes (30-07-2026, porta 3188 já ocupada) e voltou a ficar (08-08-2026,
/opt/mcp-gateway/port-map.json tinha 3198 marcado como mcp-easypanel quando na verdade é
mcp-youtube-router; corrigido, easypanel real ficou em 3206):
ss -tlnH cruzado com grep -oE "127.0.0.1:3[0-9]{3}" /etc/nginx/sites-enabled/mcp-gateway.
Protocolo de Health Check
Executar via mcp__ssh-unified__ssh_execute(server="gateway") em 2 chamadas:
Chamada 1 — estado geral
echo "=== PM2 ===" && pm2 list 2>/dev/null && echo "=== SYSTEMD ===" && systemctl list-units --type=service --state=running --no-pager | grep -i mcp && echo "=== FAILED ===" && systemctl list-units --type=service --state=failed --no-pager | grep -i mcp && echo "=== LOAD ===" && uptime && free -h
Chamada 2 — portas + erros
echo "=== PORTAS ===" && for port in 3150 3151 3152 3153 3156 3157 3158 3159 3160 3161 3162 3163 3164 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3187; do if ss -tln | grep -q ":${port} "; then echo "OK :${port}"; else echo "DOWN :${port}"; fi; done && echo "=== RAM MCPs ===" && ps aux | grep -E 'mcp|supergateway' | grep -v grep | awk '{sum+=$6} END {printf "Total: %.0f MB\n", sum/1024}' && echo "=== ERROS (30min) ===" && for svc in $(systemctl list-units --type=service --state=running | grep -i mcp | awk '{print $1}'); do errs=$(journalctl -u $svc --since "30 min ago" -p err --no-pager -q 2>/dev/null | wc -l); if [ "$errs" -gt 0 ]; then echo "--- $svc ($errs) ---"; journalctl -u $svc --since "30 min ago" -p err --no-pager -q 2>/dev/null | tail -2; fi; done && echo "=== PM2 ERROS ===" && pm2 logs --err --lines 3 --nostream 2>/dev/null
Output esperado
Apresentar como tabela resumo com data via mcp-time:
## Gateway Health — [data]
Servidor: gateway.descomplicar.pt | Load: X.XX | RAM: X.XG/XG | MCPs RAM: XXXMB
X/30 operacionais | Alertas: N
Criterios: OK=running+porta escuta | WARN=running com erros ou >5 restarts | DOWN=parado ou porta fechada
Operacoes
Restart de um MCP
# pm2
pm2 restart <nome>
# systemd
systemctl restart <nome>.service
Ver logs de um MCP
# pm2
pm2 logs <nome> --lines 30 --nostream
# systemd
journalctl -u <nome>.service --since "1h ago" --no-pager | tail -30
Testar endpoint especifico
# Internamente no gateway
curl -s http://127.0.0.1:<porta>/mcp -X POST \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0.1"}}}'
# Externamente
curl -s https://gateway.descomplicar.pt/v1/<nome>/mcp -X POST \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0.1"}}}'
Troubleshooting
MCP DOWN
- Verificar service:
systemctl status <nome>oupm2 show <nome> - Ver logs:
journalctl -u <nome> --since "1h ago" --no-pager | tail -30 - Se supergateway: verificar preload
catch-errors.mjs(ver abaixo) - Tentar restart:
systemctl restart <nome> - Re-verificar porta:
ss -tln | grep :<porta>
Supergateway crash (bug conhecido)
- Erro:
No connection established for request ID: 0 - Fix: preload script em
/opt/mcp-gateway/supergateway-catch-errors.mjs - Activacao:
Environment="NODE_OPTIONS=--import /opt/mcp-gateway/supergateway-catch-errors.mjs"no unit file - 15 services patchados: todos os supergateway na tabela acima
- Ao adicionar novo supergateway: OBRIGATORIO adicionar esta linha ao unit file
FastMCP Python + nginx (DNS rebinding)
FastMCP 1.26+ bloqueia Host headers externos. No nginx:
proxy_set_header Host "127.0.0.1:<PORTA>"; # CORRECTO
# proxy_set_header Host $host; # ERRADO — FastMCP bloqueia
Detectar: Se MCP retorna Invalid Host header via HTTPS mas funciona em curl localhost -> e este problema.
403 Forbidden
IP nao esta na whitelist nginx. Verificar: curl -4 ifconfig.me
IP autorizado: 188.251.199.30. Actualizar em /etc/nginx/sites-enabled/ se mudou.
RAM total > 2GB
- Identificar top consumers:
ps aux --sort=-%mem | head -20 | grep -E 'node|python|supergateway' - Processos orphan:
ps aux | grep -c supergateway - Se orphans > 24:
pkill -f supergatewaye restart escalonado
Adicionar novo MCP ao gateway
- Instalar em
/opt/mcp-gateway/<nome>/(Node) ou/opt/mcp-<nome>/(Python) - Porta: proxima livre (actualmente 3188)
- Criar unit file systemd (se supergateway: incluir preload catch-errors)
- Criar bloco nginx (se FastMCP Python: Host header fix obrigatorio)
systemctl daemon-reload && systemctl enable --now <nome>.servicenginx -t && systemctl reload nginx- Testar internamente e externamente (ver comandos curl acima)
- Adicionar a
~/.claude.json:{"type":"http","url":"https://gateway.descomplicar.pt/v1/<nome>/mcp"} - Actualizar esta skill (mapa de portas + proxima porta livre)
- Actualizar memory
mcp-gateway.md
Anti-Patterns
- Nunca restart massivo sem verificar primeiro
- Nunca ignorar MCP P1 em estado DOWN
- Nunca confundir gateway (VM 103) com dev/server/easy
- Sempre reportar estado mesmo que tudo OK
- Sempre testar endpoint apos restart
- Sempre actualizar mapa de portas ao adicionar/remover MCPs
- Nunca guardar backups do nginx dentro de
sites-enabled/— o include e por wildcard, o backup passa a ser carregado enginx -tfalha comduplicate log_format, quebrando todo o gateway. Backups vao para/root/. - Nunca confiar no "proxima porta livre" deste ficheiro sem cruzar
ss -tlnHcom as portas roteadas no nginx. - Contar ~40s de arranque nos MCPs Python/FastMCP no gateway (import lento, I/O de
disco). Um probe imediato apos
systemctl startda falso negativo — aguardar o bind.
Skill v2.0.0 | 28-03-2026 | Descomplicar
Healing Log
Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar.
{"date":"2026-07-30","issue":"Backup do nginx criado em sites-enabled/ -> duplicate log_format mcp_audit -> nginx -t falhou para todo o gateway","fix":"Backups para /root/. Verificar com nginx -t apos remover.","source":"auto"}
{"date":"2026-07-30","issue":"Skill anunciava 3188 como proxima porta livre; estava ocupada -> deploy abortou","fix":"Cruzar ss -tlnH com grep 127.0.0.1:3XXX no nginx antes de escolher porta","source":"auto"}
{"date":"2026-07-30","issue":"Probe tools/list imediatamente apos systemctl start deu falso negativo (FastMCP demora ~40s a bindar)","fix":"Poll da porta com timeout antes do probe HTTP","source":"auto"}
Adicionar nova linha após cada erro corrigido.