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. approvesignifica pagar.ask_humansignifica parar e perguntar a uma pessoa.blocksignifica 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.
| Entrada | Efeito |
|---|---|
| Valor | min(75, round(25 * log10(1 + usd))). Gastos maiores reduzem a pontuação. |
| Comerciante | Host 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. |
| Categoria | Alta 30: crypto_transfer, irreversible, gift_card. Média 12: api_call, saas, infra. Baixa 0: status_check. Qualquer outra: 18. |
| Moeda | USD e USDC: 0. USDT, DAI, USDP, PYUSD: 6. Outra: 12. Padrão USDC. |
| Dicas de risco | Dicas 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:
- Valor maior que
daily_budget_remainingoumonthly_budget_remainingforçablocke limita a pontuação publicada a 24. Orçamentos omitidos não são verificados. Igual ao orçamento restante é permitido. - Caso contrário, valor estritamente abaixo do limite micro força
approvee 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 PayCheck | SpendGate |
|---|---|
trusted | Comerciante conhecido, igual à allowlist ou known_merchant. |
unknown | Tratamento atual de comerciante desconhecido. Não é confiado automaticamente. |
suspicious | Penalidade de comerciante 30 (mais 12 se a URL for insegura). Não conhecido. |
block | Penalidade 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ível | Limite | Como |
|---|---|---|
| Anônimo | 100 pontuações / dia UTC | Sem chave |
| Chave gratuita | 1.000 pontuações / dia UTC | POST /v1/keys |
| Chave Toolweather (cliente hex) | 1.000 pontuações / dia UTC | x-tw-bundle-secret correspondente mais um x-tw-bundle-client hex |
| Chamador anônimo Toolweather | 100 pontuações / dia UTC | Segredo 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ção | x402 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-712USD Coin, versão2). - Facilitador https://facilitator.payai.network (
POST /verify, depoisPOST /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_TOpara o endereço 0x que deve receber USDC. Se não estiver definido, pontuações acima do limite retornam402compayment_not_configured. O servidor não vai inventar uma carteira.
Uma tentativa paga:
POST /v1/scoresem cabeçalho de pagamento.- Leia HTTP 402 e o cabeçalho
PAYMENT-REQUIREDem base64 (o corpo JSON espelha isso). - Assine EIP-3009
TransferWithAuthorizationpara o requisito aceito. - Tente novamente com
PAYMENT-SIGNATUREdefinido para o payload de pagamento x402 v2 em base64. - A pontuação volta com
PAYMENT-RESPONSEapó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 /healthGET /v1/policyGET /openapi.jsonGET /llms.txteGET /llms-full.txtGET /.well-known/mcp(e/.well-known/mcp.json)GET /.well-known/x402POST /mcpHTTP streamable, protocolo2025-06-18(também aceita2025-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].