SpendGate

MCP/API que avalia um gasto planejado como aprovar, perguntar_humano ou bloquear, para que agentes não queimem dinheiro por engano. Grátis para testar, depois USDC via x402.

Servidor MCP hospedado

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

Instala no Claude Code, Codex, Cursor e outros

Documentação

SpendGate

O SpendGate ajuda um agente autônomo a decidir se deve aprovar um gasto planejado antes de efetuar o pagamento.

Ele retorna uma pontuação de 0 a 100, um veredito de approve, ask_human ou block, motivos resumidos, um recommended_action e checked_at. A pontuação é determinística. Reformular a finalidade não vai buscar um veredito melhor.

O SpendGate não envia o pagamento ao comerciante e não guarda uma chave de carteira. Taxas de excedente, quando configuradas, são pagamentos USDC x402 na Base para o endereço PAY_TO que você definir.

Quando um agente deve usá-lo

Chame score_spend ou POST /v1/score antes de uma ferramenta transferir cripto, comprar um cartão-presente, iniciar uma assinatura ou pagar uma API ou comerciante.

  • Informe o valor em USD, a categoria, o comerciante ou URL do endpoint e qualquer orçamento diário ou mensal que ainda restar. Se o valor ultrapassar um orçamento que você informou, o veredito é block.
  • approve significa pagar. ask_human significa parar e perguntar a uma pessoa. block significa não pagar.
  • Valores estritamente abaixo do limite micro gratuito (padrão $1) retornam approve, a menos que excedam um orçamento que você passou.
  • Pule para leituras puras que não gastam. Se você verificar uma sonda de saúde, use a categoria status_check.
  • Mantenha seu próprio limite rígido. O SpendGate é uma segunda verificação, não a única trava.
  • Não tente novamente com uma finalidade mais suave para mudar a pontuação. A v1 não armazena o corpo, e a matemática não depende de prosa.

Observações gratuitas de gastos não estão na v1.

Pontuação v1

Comece em 100 e depois subtraia. Versão da política 1. Constantes completas: GET /v1/policy.

EntradaEfeito
Valormin(75, round(25 * log10(1 + usd))). Gastos maiores reduzem a pontuação.
ComercianteHost conhecido ou dica de known_merchant: 0. Desconhecido: 18. Ausente: 24. URL não-https, IP ou local: +12. PayCheck suspicious: 30. PayCheck block: 42, veredito block, pontuação no máximo 24.
CategoriaAlta 30: crypto_transfer, irreversible, gift_card. Média 12: api_call, saas, infra. Baixa 0: status_check. Qualquer outra: 18.
MoedaUSD e USDC: 0. USDT, DAI, USDP, PYUSD: 6. Outra: 12. Padrão USDC.
Dicas de riscoDicas de aumento somam 6 cada, limite 18. Dicas de redução subtraem 6 cada, limite 12. Dicas são autorrelatadas.

Limiares após penalidades: 70 aprovar, 40 perguntar a um humano, abaixo disso bloquear.

Substituições rígidas, aplicadas após a pontuação bruta:

  1. Valor maior que daily_budget_remaining ou monthly_budget_remaining força block e limita a pontuação publicada a 24. Orçamentos omitidos não são verificados. Igual ao orçamento restante é permitido.
  2. Caso contrário, valor estritamente abaixo do limite micro força approve e eleva a pontuação publicada para pelo menos 80.

agent_id é ecoado e não pontuado.

PayCheck

Quando o comerciante é um domínio, uma URL http(s) ou uma carteira 0x, e não é um host https público na lista local de hosts conhecidos, o SpendGate chama PayCheck POST /v1/check com { "target": "..." }. O Worker implantado usa o binding de serviço PAYCHECK (service = "paycheck"). Uma busca *.workers.dev pública de outro worker na mesma conta falha, então essa URL é apenas o fallback para testes e para um processo sem binding.

Veredito PayCheckSpendGate
trustedComerciante conhecido, igual à allowlist ou known_merchant.
unknownTratamento atual de comerciante desconhecido. Não é confiado automaticamente.
suspiciousPenalidade de comerciante 30 (mais 12 se a URL for insegura). Não conhecido.
blockPenalidade de comerciante 42 (mais 12 se insegura). Veredito block, pontuação publicada no máximo 24, incluindo abaixo do limite micro de $1.

openai.com e os outros KNOWN_HOST_SUFFIXES permanecem conhecidos sem chamada de rede, então uma interrupção do PayCheck não os altera. Qualquer outra falha do PayCheck (timeout, não-OK, JSON inválido) cai para as regras locais de comerciante e score_spend ainda retorna uma pontuação. detail.paycheck_error é timeout, http_status:<code>, parse ou network nesse fallback, e o Worker registra paycheck lookup failed: …. Um IP bruto ou URL http mantém a penalidade de insegurança nesse fallback.

A consulta é uma chamada interna do SpendGate, não uma segunda cobrança ao chamador. Ela não encaminha a chave sg_live_ ou x-tw-bundle-client do chamador. Autenticação, em ordem: PAYCHECK_API_KEY opcional; senão BUNDLE_PROXY_SECRET com o cliente de pacote próprio do SpendGate 7370656e6467617465 (hex para spendgate, 1.000 verificações PayCheck por dia UTC); senão o nível gratuito anônimo do PayCheck (100 por dia UTC). Nenhum novo segredo é necessário. PAYCHECK_URL tem como padrão https://paycheck.toolweather.workers.dev.

Nível gratuito e x402

NívelLimiteComo
Anônimo100 pontuações / dia UTCSem chave
Chave gratuita1.000 pontuações / dia UTCPOST /v1/keys
Chave Toolweather (cliente hex)1.000 pontuações / dia UTCx-tw-bundle-secret correspondente mais um x-tw-bundle-client hex
Chamador anônimo Toolweather100 pontuações / dia UTCSegredo correspondente mais um id de cliente anon:. Mesmo limite que anônimo. get_quota ainda reporta assunto e nível bundle.
Excedente$0,001 USDC por pontuaçãox402 na Base após o limite

O Worker Toolweather faz proxy de score_spend, get_quota e spendgate_get_api_key para este servidor. Quando ele envia x-tw-bundle-secret igual ao BUNDLE_PROXY_SECRET deste Worker, a cota é cobrada de x-tw-bundle-client. Um cliente hex (um hash de chave de API Toolweather, 16–128 caracteres hex; um valor mais longo usa um prefixo estável de 128 caracteres) obtém o limite de chave gratuita de 1.000 pontuações por dia UTC. Um chamador anônimo Toolweather (anon: mais esse hash) obtém as mesmas 100 pontuações por dia UTC que um chamador anônimo direto, então um endereço de saída rotativo não cria um novo balde de 1.000 pontuações. get_quota reporta assunto e nível bundle para ambos, com daily_limit definido para o limite aplicável. O id de cliente bruto não é armazenado. Uma chave sg_live_… apresentada ainda vence. Um segredo ausente, não definido ou errado mantém o balde de IP anônimo e não retorna um erro apenas porque os cabeçalhos estão ausentes. A criação de chaves através do proxy usa o mesmo id de cliente para o limite diário de criação.

  • Rede eip155:8453 (mainnet Base).
  • Ativo USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 (nome EIP-712 USD Coin, versão 2).
  • Facilitador https://facilitator.payai.network (POST /verify, depois POST /settle).
  • Preço é 1.000 unidades atômicas (6 decimais).
  • Leituras de cota, criação de chaves, docs e descoberta MCP não são cobradas.
  • Defina PAY_TO para o endereço 0x que deve receber USDC. Se não estiver definido, pontuações acima do limite retornam 402 com payment_not_configured. O servidor não vai inventar uma carteira.

Uma tentativa paga:

  1. POST /v1/score sem cabeçalho de pagamento.
  2. Leia HTTP 402 e o cabeçalho PAYMENT-REQUIRED em base64 (o corpo JSON espelha isso).
  3. Assine EIP-3009 TransferWithAuthorization para o requisito aceito.
  4. Tente novamente com PAYMENT-SIGNATURE definido para o payload de pagamento x402 v2 em base64.
  5. A pontuação volta com PAYMENT-RESPONSE após a liquidação. O mesmo nonce retorna a pontuação original e não liquida novamente.

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_..."

Outras rotas:

  • GET /health
  • GET /v1/policy
  • GET /openapi.json
  • GET /llms.txt e GET /llms-full.txt
  • GET /.well-known/mcp (e /.well-known/mcp.json)
  • GET /.well-known/x402
  • POST /mcp HTTP streamable, protocolo 2025-06-18 (também aceita 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"}}}'

Ferramentas: score_spend, get_quota, get_api_key.

Após o limite gratuito, tools/call em score_spend retorna HTTP 402 com um erro JSON-RPC (-32002) e o cabeçalho PAYMENT-REQUIRED. get_quota e get_api_key permanecem gratuitos.

Executar localmente

Requer Node 20+.

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

O site e a API escutam em http://127.0.0.1:43123.

Carteira receptora opcional, em .dev.vars (ignorado pelo git, copie de .dev.vars.example):

PAY_TO=0xYourAddress
npm test
npm run typecheck

Implantar

Plano gratuito Cloudflare: um Worker, um banco de dados D1, ativos estáticos de public/. Sem SaaS pago e sem segredos no repositório.

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 deve ser o mesmo valor que no Worker Toolweather. Defina-o após o merge e depois reimplante. Não faça commit do valor. Até que esse segredo seja definido, chamadas via proxy sem uma chave sg_live_… continuam compartilhando o balde anônimo diário. O mesmo segredo, quando definido, também atribui as consultas PayCheck do SpendGate ao cliente de pacote interno do SpendGate. PAYCHECK_URL já está em [vars]. PAYCHECK_API_KEY é opcional (npx wrangler secret put PAYCHECK_API_KEY) e não é necessário para o nível anônimo do PayCheck.

npm run dry-run empacota o Worker como uma implantação faria, sem publicar.

Para hospedar o HTML no Pages separadamente: npx wrangler pages deploy public. A API ainda precisa do Worker. Servir ambos do Worker é a configuração que wrangler.toml usa.

database_id em wrangler.toml é um UUID de espaço reservado. Substitua-o antes de uma implantação remota real. Não coloque uma carteira de produção em [vars].