SpendGate

Servidor MCP/API que evalúa un gasto planificado como aprobar, consultar_humano o bloquear para que los agentes no quemen dinero por error. Gratis para probar, luego USDC vía x402.

Servidor MCP alojado

npx add-mcp 'https://spendgate.toolweather.workers.dev/mcp'

Se instala en Claude Code, Codex, Cursor y más

Documentación

SpendGate

SpendGate ayuda a un agente autónomo a decidir si aprobar un gasto planificado antes de que pague.

Devuelve una puntuación de 0 a 100, un veredicto de approve, ask_human o block, razones breves, un recommended_action y checked_at. La puntuación es determinista. Reformular el propósito no servirá para obtener un mejor veredicto.

SpendGate no envía el pago al comercio y no guarda una clave de wallet. Las tarifas por exceso, cuando están configuradas, son pagos x402 USDC en Base a la dirección PAY_TO que establezcas.

Cuándo debe usarlo un agente

Llama a score_spend o POST /v1/score antes de que una herramienta transfiera criptomonedas, compre una tarjeta de regalo, inicie una suscripción o pague a una API o comercio.

  • Pasa el monto en USD, la categoría, el comercio o la URL del endpoint, y cualquier presupuesto diario o mensual que aún tengas. Si el monto supera un presupuesto que proporcionas, el veredicto es block.
  • approve significa pagar. ask_human significa detenerse y preguntar a una persona. block significa no pagar.
  • Los montos estrictamente por debajo del umbral micro gratuito (por defecto $1) devuelven approve, a menos que superen un presupuesto que hayas pasado.
  • Omítelo para lecturas puras que no gastan. Si verificas una sonda de salud, usa la categoría status_check.
  • Mantén tu propio límite máximo. SpendGate es una segunda revisión, no la única cerradura.
  • No reintentes con un propósito más suave para cambiar la puntuación. v1 no almacena el cuerpo, y el cálculo no depende de la prosa.

Las alertas de gasto gratuitas no están en v1.

Puntuación v1

Comienza en 100 y luego resta. Versión de política 1. Constantes completas: GET /v1/policy.

EntradaEfecto
Montomin(75, round(25 * log10(1 + usd))). Un gasto mayor reduce la puntuación.
ComercioHost conocido o pista known_merchant: 0. Desconocido: 18. Faltante: 24. No-https, IP o URL local: +12. PayCheck suspicious: 30. PayCheck block: 42, veredicto block, puntuación máxima 24.
CategoríaAlta 30: crypto_transfer, irreversible, gift_card. Media 12: api_call, saas, infra. Baja 0: status_check. Cualquier otra: 18.
MonedaUSD y USDC: 0. USDT, DAI, USDP, PYUSD: 6. Otras: 12. Por defecto USDC.
Pistas de riesgoLas pistas que aumentan suman 6 cada una, tope 18. Las pistas que disminuyen restan 6 cada una, tope 12. Las pistas son autoinformadas.

Umbrales después de penalizaciones: 70 aprobar, 40 preguntar a un humano, por debajo bloquear.

Anulaciones estrictas, aplicadas después de la puntuación bruta:

  1. Monto mayor que daily_budget_remaining o monthly_budget_remaining fuerza block y limita la puntuación publicada a 24. Los presupuestos omitidos no se verifican. Igual al presupuesto restante está permitido.
  2. De lo contrario, un monto estrictamente por debajo del umbral micro fuerza approve y eleva la puntuación publicada a al menos 80.

agent_id se repite y no se puntúa.

PayCheck

Cuando el comercio es un dominio, una URL http(s) o una wallet 0x, y no es ya un host https público en la lista local de hosts conocidos, SpendGate llama a PayCheck POST /v1/check con { "target": "..." }. El Worker desplegado usa el enlace de servicio PAYCHECK (service = "paycheck"). Una búsqueda *.workers.dev pública desde otro worker en la misma cuenta falla, por lo que esa URL es solo el respaldo para pruebas y para un proceso sin enlace.

Veredicto PayCheckSpendGate
trustedComercio conocido, igual que la lista de permitidos o known_merchant.
unknownManejo actual de comercio desconocido. No se confía automáticamente.
suspiciousPenalización de comercio 30 (más 12 si la URL es insegura). No conocido.
blockPenalización de comercio 42 (más 12 si es insegura). Veredicto block, puntuación publicada máxima 24, incluso por debajo del umbral micro de $1.

openai.com y los otros KNOWN_HOST_SUFFIXES permanecen conocidos sin llamada de red, por lo que una interrupción de PayCheck no los cambia. Cualquier otro fallo de PayCheck (tiempo de espera, no-OK, JSON malo) vuelve a las reglas locales de comercio y score_spend aún devuelve una puntuación. detail.paycheck_error es timeout, http_status:<code>, parse o network en ese respaldo, y el Worker registra paycheck lookup failed: …. Una IP cruda o URL http mantiene la penalización insegura en ese respaldo.

La búsqueda es una llamada interna de SpendGate, no un segundo cargo al llamante. No reenvía la clave sg_live_ del llamante ni x-tw-bundle-client. Autenticación, en orden: PAYCHECK_API_KEY opcional; si no, BUNDLE_PROXY_SECRET con el cliente de paquete propio de SpendGate 7370656e6467617465 (hex para spendgate, 1,000 verificaciones PayCheck por día UTC); si no, el nivel gratuito anónimo de PayCheck (100 por día UTC). No se requiere ningún secreto nuevo. PAYCHECK_URL por defecto es https://paycheck.toolweather.workers.dev.

Nivel gratuito y x402

NivelTopeCómo
Anónimo100 puntuaciones / día UTCSin clave
Clave gratuita1,000 puntuaciones / día UTCPOST /v1/keys
Clave Toolweather (cliente hex)1,000 puntuaciones / día UTCx-tw-bundle-secret coincidente más un x-tw-bundle-client hex
Llamante anónimo Toolweather100 puntuaciones / día UTCSecreto coincidente más un id de cliente anon:. Mismo tope que anónimo. get_quota aún informa sujeto y nivel bundle.
Exceso$0.001 USDC por puntuaciónx402 en Base después del tope

El Worker de Toolweather hace de proxy de score_spend, get_quota y spendgate_get_api_key a este servidor. Cuando envía x-tw-bundle-secret igual al BUNDLE_PROXY_SECRET de este Worker, la cuota se carga a x-tw-bundle-client. Un cliente hex (un hash de clave API de Toolweather, 16–128 caracteres hex; un valor más largo usa un prefijo estable de 128 caracteres) obtiene el tope de clave gratuita de 1,000 puntuaciones por día UTC. Un llamante anónimo de Toolweather (anon: más ese hash) obtiene las mismas 100 puntuaciones por día UTC que un llamante anónimo directo, por lo que una dirección de salida rotatoria no crea un nuevo depósito de 1,000 puntuaciones. get_quota informa sujeto y nivel bundle para ambos, con daily_limit establecido al tope que aplica. El id de cliente crudo no se almacena. Una clave sg_live_… presentada aún gana. Un secreto faltante, no establecido o incorrecto mantiene el depósito anónimo por IP y no devuelve un error solo porque los encabezados estén ausentes. La acuñación de claves a través del proxy usa el mismo id de cliente para el tope diario de acuñación.

  • Red eip155:8453 (Base mainnet).
  • Activo USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 (nombre EIP-712 USD Coin, versión 2).
  • Facilitador https://facilitator.payai.network (POST /verify, luego POST /settle).
  • Precio es 1000 unidades atómicas (6 decimales).
  • Lecturas de cuota, acuñación de claves, documentos y descubrimiento MCP no se facturan.
  • Establece PAY_TO a la dirección 0x que debe recibir USDC. Si no está establecido, las puntuaciones por encima del tope devuelven 402 con payment_not_configured. El servidor no inventará una wallet.

Un reintento pagado:

  1. POST /v1/score sin encabezado de pago.
  2. Lee HTTP 402 y el encabezado PAYMENT-REQUIRED en base64 (el cuerpo JSON lo refleja).
  3. Firma EIP-3009 TransferWithAuthorization para el requisito aceptado.
  4. Reintenta con PAYMENT-SIGNATURE establecido al payload de pago x402 v2 en base64.
  5. La puntuación regresa con PAYMENT-RESPONSE después del settle. El mismo nonce devuelve la puntuación original y no vuelve a hacer settle.

API

curl -s http://127.0.0.1:43123/v1/score \
  -H 'content-type: application/json' \
  -d '{
    "amount": 12,
    "currency": "USDC",
    "category": "api_call",
    "purpose": "embeddings for the planner",
    "merchant": "https://api.openai.com/v1",
    "agent_id": "agent_planner",
    "daily_budget_remaining": 40,
    "risk_hints": []
  }'
curl -s http://127.0.0.1:43123/v1/keys \
  -H 'content-type: application/json' \
  -d '{"label":"planner"}'

curl -s http://127.0.0.1:43123/v1/quota \
  -H "authorization: Bearer sg_live_..."

Otras rutas:

  • GET /health
  • GET /v1/policy
  • GET /openapi.json
  • GET /llms.txt y GET /llms-full.txt
  • GET /.well-known/mcp (y /.well-known/mcp.json)
  • GET /.well-known/x402
  • POST /mcp HTTP streamable, protocolo 2025-06-18 (también acepta 2025-03-26)

MCP

curl -s http://127.0.0.1:43123/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"example","version":"0"}}}'

curl -s http://127.0.0.1:43123/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"score_spend","arguments":{"amount":0.2,"category":"status_check","merchant":"https://www.cloudflare.com","agent_id":"agent_demo"}}}'

Herramientas: score_spend, get_quota, get_api_key.

Después del tope gratuito, tools/call en score_spend devuelve HTTP 402 con un error JSON-RPC (-32002) y el encabezado PAYMENT-REQUIRED. get_quota y get_api_key permanecen gratuitos.

Ejecutar localmente

Requiere Node 20+.

npm install
npx wrangler d1 migrations apply spendgate --local
npm run dev

El sitio y la API escuchan en http://127.0.0.1:43123.

Wallet receptora opcional, en .dev.vars (gitignored, copia desde .dev.vars.example):

PAY_TO=0xYourAddress
npm test
npm run typecheck

Desplegar

Plan gratuito de Cloudflare: un Worker, una base de datos D1, activos estáticos desde public/. Sin SaaS de pago y sin secretos en el repositorio.

npx wrangler d1 create spendgate
# Put the returned database_id in wrangler.toml (the committed id is a placeholder).
npx wrangler d1 migrations apply spendgate --remote
npx wrangler secret put PAY_TO
npx wrangler secret put BUNDLE_PROXY_SECRET
npx wrangler deploy

BUNDLE_PROXY_SECRET debe ser el mismo valor que en el Worker de Toolweather. Establécelo después del merge y luego vuelve a desplegar. No confirmes el valor. Hasta que ese secreto esté establecido, las llamadas proxy sin clave sg_live_… siguen compartiendo el depósito anónimo diario. El mismo secreto, cuando está establecido, también atribuye las búsquedas PayCheck de SpendGate al cliente de paquete interno de SpendGate. PAYCHECK_URL ya está en [vars]. PAYCHECK_API_KEY es opcional (npx wrangler secret put PAYCHECK_API_KEY) y no se requiere para el nivel anónimo de PayCheck.

npm run dry-run empaqueta el Worker como lo haría un despliegue, sin publicar.

Para alojar el HTML en Pages por sí solo: npx wrangler pages deploy public. La API aún necesita el Worker. Servir ambos desde el Worker es la configuración que usa wrangler.toml.

database_id en wrangler.toml es un UUID de marcador de posición. Reemplázalo antes de un despliegue remoto real. No pongas una wallet de producción en [vars].