Skip to main content
Glama

approval-gate-mcp

Un servidor MCP que coloca una puerta de aprobación humana frente a un proceso autónomo de larga duración, además de las herramientas operativas que necesitas para convivir con él: estado, registros, reinicio, parámetros de recarga en caliente y un resumen de lo que ha hecho.

Construido con FastMCP. Funciona con cualquier cliente MCP.

El problema

Un proceso que actúa por sí solo es útil hasta que una de sus acciones tiene consecuencias. Las respuestas habituales son malas en ambos casos. Apágalo y pierdes todo lo que estaba haciendo bien. Déjalo correr y te enteras después.

La puerta es una tercera opción. El proceso sigue ejecutándose sin supervisión, pero cuando quiere realizar una acción que has marcado como trascendente, escribe una propuesta y continúa en lugar de actuar. Revisas las propuestas cuando puedas, en el cliente MCP que ya tengas abierto, y las apruebas o rechazas. El proceso recoge tu decisión en su siguiente ciclo.

Ninguno de los dos lados bloquea al otro. No hay ningún servicio intermedio. El contrato son dos archivos JSON.

  process                            you
     |                                |
     |-- writes proposal ----> pending.json
     |                                |
     |                          list_pending_approvals
     |                          approve(0) / reject(1)
     |                                |
     |<-- reads on next cycle -- approved.json
     |
     |-- acts, appends to records.json

Herramientas

Puerta de aprobación

Herramienta

Qué hace

list_pending_approvals

Cada propuesta pendiente de una decisión, con todos sus campos

approve(index)

Mueve una propuesta al archivo de aprobadas

reject(index)

Descarta una propuesta

Control del proceso

Herramienta

Qué hace

process_status

En ejecución o no, PID, tiempo de actividad, CPU, memoria, disco, última línea de registro

process_logs(lines)

Muestra las últimas líneas del archivo de registro

restart_process

Reinicia mediante tu script de inicio, desacoplado para que sobreviva a la sesión SSH

run_command(cmd)

Vía de escape para comprobaciones puntuales

Registros

Herramienta

Qué hace

record_summary(n)

Totales, resueltos frente a abiertos, desglose por tipo, entradas recientes

record_dashboard

Vista de tabla más amplia, además de los registros abiertos actualmente y su exposición

Parámetros — registrados solo si PARAMS_SCHEMA apunta a tu manifiesto

Herramienta

Qué hace

list_params

Lo que este despliegue declara ajustable y lo que está sobrescrito en este momento

set_param(name, value)

Escribe una sobrescritura, validada contra tu manifiesto

clear_param(name)

Elimina una sobrescritura, o todas ellas

Cadena — registrados solo si EVM_RPC_URL, WALLET_ADDRESS y TOKEN_CONTRACT están todos configurados

Herramienta

Qué hace

wallet_balance

Saldo ERC-20 y nativo de solo lectura para una dirección vigilada. No se firma nada.

Instalación

git clone https://github.com/WillyV347/approval-gate-mcp
cd approval-gate-mcp
pip install -r requirements.txt
cp .env.example .env      # then edit it

Pruébalo en el inspector antes de conectarlo a un cliente:

fastmcp dev server.py

Luego regístralo. Para un cliente que lee una configuración JSON:

{
  "mcpServers": {
    "approval-gate": {
      "command": "python3",
      "args": ["/absolute/path/to/approval-gate-mcp/server.py"],
      "env": {
        "VPS_HOST": "your.host.or.ip",
        "VPS_USER": "your-ssh-user",
        "PROCESS_DIR": "/srv/my-process",
        "PROCESS_MATCH": "my_process.py",
        "PARAMS_SCHEMA": "/absolute/path/to/params.schema.json"
      }
    }
  }
}

No hay valores predeterminados para VPS_HOST, VPS_USER ni PROCESS_DIR. El servidor se cierra con un mensaje claro si faltan. Una herramienta de supervisión que silenciosamente recurre a algún host fijado por su autor es un error, así que esta se niega a adivinar.

La autenticación es tu configuración SSH existente. El servidor invoca al binario ssh del sistema, por lo que los agentes, las claves de hardware y los administradores de contraseñas que exponen un agente funcionan sin cambios. Nunca lee una clave privada por sí mismo.

El contrato que implementa tu proceso

Necesitas tres cosas en el lado del proceso. Ninguna de ellas requiere una biblioteca.

1. Registros. Añade a un array JSON. Cuatro claves se interpretan si están presentes, y todo lo demás se transmite sin cambios:

[
  {
    "id": "job-1041",
    "record_type": "reindex",
    "resolved": true,
    "outcome": "win",
    "cost": 4.25,
    "result": 11.80,
    "label": "nightly reindex, shard 3"
  }
]

2. Propuestas. Cuando se ejecuta en modo de aprobación, escribe aquí en lugar de actuar:

{
  "proposals": [
    {
      "id": "job-1042",
      "label": "reindex shard 7",
      "action": "reindex",
      "cost": 6.00,
      "created_at": "2026-08-18T22:14:07Z"
    }
  ]
}

Cada campo que incluyas se muestra. El servidor no tiene un esquema para las propuestas y no quiere tenerlo.

3. Consulta el archivo de aprobadas. En cada ciclo, lee approved.json, actúa sobre lo que hay y límpialo. A grandes rasgos:

approved = read_json(APPROVED_FILE) or {"proposals": []}
for proposal in approved["proposals"]:
    execute(proposal)
write_json(APPROVED_FILE, {"proposals": []})

El orden es deliberado en el lado del servidor. Una decisión elimina la propuesta de pending.json antes de escribir en approved.json, por lo que el modo de fallo de una decisión a medio completar es una propuesta descartada, nunca una acción duplicada.

Parámetros, y por qué no hay ninguno en este repositorio

La recarga en caliente solo es útil si el servidor sabe qué es ajustable, y codificar los parámetros de un despliegue en una herramienta compartida la hace de un solo propósito. Así que declaras los tuyos en un manifiesto y apuntas PARAMS_SCHEMA a él:

{
  "poll_interval_seconds": { "type": "int",  "description": "Seconds between work cycles" },
  "max_concurrent_jobs":   { "type": "int",  "description": "Jobs in flight at once" },
  "dry_run":               { "type": "bool", "description": "Plan work but do not execute" },
  "approval_mode":         { "type": "bool", "description": "Queue proposals instead of acting" }
}

Los tipos admitidos son int, float, bool, str, dict y list. set_param convierte y valida contra esto y rechaza cualquier cosa que no se pueda convertir. Sin un manifiesto, las herramientas de parámetros simplemente no se registran, lo cual es mejor que exponer una ruta de escritura sin nada contra lo que verificarla.

params.schema.json está en gitignore, porque tu ajuste es tuyo. params.schema.example.json se incluye como punto de partida.

Haz que tu proceso lea el archivo de sobrescrituras al inicio, y en cada ciclo si quieres que los cambios se apliquen sin reiniciar:

overrides = read_json(PARAMS_FILE) or {}
poll_interval = overrides.get("poll_interval_seconds", POLL_INTERVAL_DEFAULT)

Sesión de ejemplo

> is it running?

  Process RUNNING

  PID:      31882
  Uptime:   14h 22m (51720s)
  CPU:      0.6%
  Memory:   84.3 MB
  Disk:     212M

  Last log: 2026-08-18 22:03:11 cycle complete, 2 proposals queued

> anything waiting on me?

  2 proposal(s) awaiting approval

  --- Proposal #0 ------------------------------
    id:           job-1042
    label:        reindex shard 7
    action:       reindex
    cost:         6.00
    created_at:   2026-08-18T22:14:07Z

  --- Proposal #1 ------------------------------
    id:           job-1043
    label:        reindex shard 12
    action:       reindex
    cost:         6.00
    created_at:   2026-08-18T22:14:07Z

  Use approve(index) or reject(index) to decide.

> approve 0, reject 1

  Approved and queued for execution: reindex shard 7
  Rejected and removed: reindex shard 12

La salida anterior es ilustrativa.

Notas sobre seguridad

  • run_command ejecuta comandos de shell arbitrarios en el host remoto. Existe porque la alternativa es abrir una terminal de todos modos, pero es la herramienta más peligrosa aquí. Limita el usuario SSH a lo que realmente necesita en lugar de ejecutar todo como root.

  • wallet_balance es de solo lectura y nunca toca una clave. Si no lo configuras, no existe.

  • Nada en este repositorio almacena una credencial. Las URL RPC llevan claves de proveedor en la ruta, así que mantén EVM_RPC_URL en el entorno y fuera de tus archivos de configuración.

  • StrictHostKeyChecking está configurado en accept-new: los hosts desconocidos se consideran de confianza en la primera conexión, las claves de host cambiadas aún fallan. Configúralo en yes y rellena previamente known_hosts si quieres el comportamiento más estricto.

Licencia

MIT

-
license - not tested
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • Control plane for autonomous software labor. Agents claim objectives over MCP with audit trail.

  • A paid remote MCP for CLI tool MCP, built to return verdicts, receipts, usage logs, and audit-ready

  • A paid remote MCP for hosted MCP server, built to return verdicts, receipts, usage logs, and audit-r

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/WillyV347/approval-gate-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server