Agent Evidence API
Evidência web citada e verificação de alegações para agentes de IA: envie uma pergunta ou alegação, além de URLs públicas, e receba trechos vinculados às fontes com horários de recuperação e hashes de conteúdo. A descoberta é gratuita; cada chamada é paga em USDC na Base via x402.
Servidor MCP hospedado
npx add-mcp 'https://agent-evidence-api.thx93.workers.dev/mcp'Instala no Claude Code, Codex, Cursor e outros
Documentação
API de Evidências para Agentes (Agent Evidence API)
Evidências web citadas e fundamentadas em fontes, e verificação de alegações para agentes de IA, tanto via HTTP quanto via MCP. Pagamento por chamada em USDC na Base via x402 — US$ 0,003 por chamada, e nada quando nada é recuperado.
| O quê | Onde |
|---|---|
| Endpoint de evidências HTTP | POST https://agent-evidence-api.thx93.workers.dev/v1/evidence |
Serviço MCP (research_evidence) | POST https://agent-evidence-api.thx93.workers.dev/mcp |
Serviço MCP (get-alerts, get-forecast) | POST https://agent-evidence-api.thx93.workers.dev/weather/mcp |
| Amostra gratuita de saída real | GET https://agent-evidence-api.thx93.workers.dev/demo — uma resposta não editada de uma chamada paga real, sem necessidade de carteira |
| CLI de compra sem instalação | curl -fsSL https://agent-evidence-api.thx93.workers.dev/buy.mjs -o buy.mjs |
| Manifesto de capacidade x402 | https://agent-evidence-api.thx93.workers.dev/.well-known/x402 |
A descoberta é gratuita em todas as rotas MCP (tools/list, initialize, ping e uma ferramenta health), para que um agente possa ler os esquemas e testar a disponibilidade antes de pagar por qualquer coisa.
Opcional: ranqueamento semântico
O ranqueamento é, por padrão, correspondência lexical determinística, e tem um teto medido. Perguntado "Qual é a população de Tóquio?", ele retorna passagens que mencionam "população" e "Tóquio" — 1603 história, declínios em tempos de guerra, impostos municipais — e perde a frase que responde à pergunta, porque essa frase diz "a cidade propriamente dita" em vez de repetir "Tóquio". Duas heurísticas lexicais foram testadas e revertidas; nenhum truque de palavras-chave resolve isso.
Um Laya sidecar opcional resolve isso. Laya é um modelo de decisão tipada de pesos abertos (Apache-2.0): ele pontua uma passagem com noul, P(esta passagem responde diretamente à pergunta). Sem fornecedor, sem chave de API, sem taxa por chamada, e seu conteúdo nunca sai do host.
python services/laya/server.py # ~2 GB resident, ~35 s cold load
REASONING_PROVIDER=laya REASONING_URL=http://127.0.0.1:8077 ...
Medido no caso acima, fonte única:
| Fontes | none | laya | O Top 3 contém a resposta? |
|---|---|---|---|
| 1 | ~1,4 s | ~11,4 s | ❌ → ✅ |
| 5 | ~1,6 s | ~1,8 s (recorre ao lexical) | ❌ → ❌ |
O modelo é acionado somente quando o orçamento pode arcar com o pool completo. O custo é de ~800 ms por candidato (uma única chamada mede ~420 ms, mas o sidecar serializa, então ~700 ms sob concorrência real). O orçamento é dividido entre as fontes, então 1–2 fontes podem arcar com um pool de 12 e solicitações maiores não podem. Um pool mais estreito foi medido como inútil — ele só pode reorganizar passagens que já excluíam a resposta — então, em vez de gastar ~20 s nisso, essas solicitações recorrem ao lexical e permanecem rápidas. Reduza REASONING_POOL_SIZE para engajar em solicitações maiores, trocando recall.
O orçamento é um teto rígido, não um alvo: ele é reservado antes de cada chamada, para que fontes concorrentes não possam, coletivamente, ultrapassá-lo. (Uma versão anterior verificava e depois aguardava, o que permitia que cinco fontes passassem na verificação antes que qualquer uma o decrementasse — um orçamento de 8 s produziu uma solicitação de 50 s.)
A troca é latência, não precisão: ~420 ms por candidato, então um pool de 12 candidatos custa ~5 s de inferência por fonte. É por isso que está desativado por padrão. Ativá-lo muda apenas a ORDEM das evidências — cada trecho, fonte, URL e hash é idêntico — e se o sidecar estiver ausente, lento ou retornar qualquer coisa inesperada, o serviço mantém silenciosamente seu resultado lexical. A probabilidade bruta do modelo nunca é exposta (SPEC §21).
Observe que o pool importa: a primeira versão disso ranqueava apenas os 5 candidatos que o lexical já havia escolhido, e a resposta havia sido ranqueada em 8º–20º, então nunca estava disponível para promoção. O pool mais amplo é o que faz funcionar.
Executando o backend no Docker? O sidecar deve ser alcançável a partir do contêiner: --add-host=host.docker.internal:host-gateway e aponte REASONING_URL para http://host.docker.internal:8077, ou coloque ambos em uma única rede Docker.
Você só é cobrado quando o serviço entrega
O middleware x402 liquida um pagamento somente quando o handler retorna um status abaixo de 400, então o serviço é construído para que todas as formas de falha sejam 4xx ou 5xx:
- Se nenhuma fonte puder ser recuperada — todos 404s, todos bloqueados por SSRF, todos os tipos de conteúdo não suportados — a solicitação falha com
NO_SOURCES_RETRIEVED(502) e nenhum pagamento é feito. Uma versão anterior retornava200 inconclusiveaqui, o que teria cobrado de um comprador US$ 0,03 por um resultado vazio. - Sucesso parcial ainda liquida. Se uma de cinco fontes foi recuperada, evidências reais foram entregues e os avisos por fonte explicam o restante.
- No transporte MCP, uma chamada de ferramenta com falha é relatada como
isError: truedentro de um HTTP 200 — comportamento MCP correto, mas invisível para o middleware de pagamento. O Worker reescreve isso para 502 para que o pagamento não seja feito; o corpo JSON-RPC é passado adiante inalterado.
Verificado contra a implantação ativa: uma solicitação cujas fontes falham todas retorna 502 NO_SOURCES_RETRIEVED, e uma solicitação com uma fonte boa e uma morta retorna 200.
Análise de mercado
docs/market-analysis.md — o que a economia x402 realmente paga, medido a partir do único catálogo que publica dados de uso reais (chamadas de 30 dias e pagadores únicos por serviço). É uma leitura desconfortável: a categoria em que este serviço se encontra é 1% da busca por volume, e sua faixa de preço é a mais fraca das cinco.
Relatório de engenharia
docs/ENGINEERING-REPORT.md — o relatório exigido por SPEC.md §44: o que foi construído, a arquitetura, os comandos de implantação e teste, o status x402 e MCP, a prontidão para registro, a configuração manual restante e as limitações conhecidas. Ele declara claramente o que é demonstrado e o que não é — incluindo que nenhuma liquidação foi exercitada e que um critério de aceitação do §42 não foi atendido. Ele não afirma que o projeto está pronto para produção.
Ações que somente você pode tomar
docs/CHECKLIST.md — cada etapa restante que precisa de um humano: o primeiro pagamento, a confirmação da listagem no catálogo, a publicação e as decisões que deixei deliberadamente para você. Todo o resto está feito.
Fazendo a primeira venda
Veja docs/FIRST-SALE.md — todo o caminho de uma carteira vazia a um pagamento liquidado e uma entrada no catálogo, em uma página.
Como ver a receita
Cada solicitação que o backend atende em uma rota paga já passou pelo próprio gate de pagamento x402 do backend (o Worker apenas faz proxy). O log é somente de acréscimo no volume de dados, então ele sobrevive à recriação de contêineres e reimplantações — ao contrário dos logs do Docker.
Uma linha significa que uma prova de pagamento foi verificada neste processo. Não é prova de liquidação. A ordem x402 é verificar → handler → liquidar, então o backend escreve sua linha antes de o facilitador liquidar, e uma liquidação que falhar posteriormente ainda aparecerá aqui. O campo é nomeado payment_provided por esse motivo; anteriormente era chamado settled, o que estava errado.
docker exec aee-live cat /app/data/usage.jsonl # the raw record
# Buyer traffic: requests that carried an x402 payment proof, so operator and
# test calls are excluded. This is NOT a revenue figure - see below.
docker exec aee-live sh -c "grep -c '"payment_provided":true' /app/data/usage.jsonl"
Para dinheiro, use o registro on-chain. O cliente do comprador imprime a transação de liquidação, e o livro-razão autoritativo é o endereço do destinatário:
https://basescan.org/address/0x9c0e2B44180439294Fa30Ae2B2a94f8655455FD0
Cada linha carrega o timestamp, o ID da solicitação, um hash salgado da pergunta (nunca o texto), quantas fontes foram recuperadas, quantos itens de evidência retornaram, a avaliação e a duração no lado do servidor.
{"ts":"2026-09-21T07:53:04.341Z","request_id":"req_2fcc…","question_hash":"725f50fca9fbe049",
"question_chars":32,"sources_requested":1,"sources_retrieved":1,"evidence_items":5,
"assessment":"supported","processing_ms":6,"outcome":"ok"}
Para o lado on-chain, a resposta de pagamento carrega um hash de transação de liquidação, e o destinatário é 0x9c0e2B44180439294Fa30Ae2B2a94f8655455FD0 — verifique-o no basescan.
Implantação ativa
| Endpoint | https://agent-evidence-api.thx93.workers.dev |
| MCP | https://agent-evidence-api.thx93.workers.dev/mcp |
| Saúde | /health · profunda: /health?deep=1 |
| Registro | io.github.Thx93/agent-evidence-api — publicado, status ativo |
| Pagamento | x402, exact, 0,03 USDC na mainnet da Base (eip155:8453) |
| Compre | sem instalação: curl -fsSL .../buy.mjs -o buy.mjs && node buy.mjs "question" https://source.example |
Compre algo sem conta npm e sem nada para instalar — o CLI de compra é um único arquivo autocontido servido pelo próprio serviço:
curl -fsSL https://agent-evidence-api.thx93.workers.dev/buy.mjs -o buy.mjs
X402_PRIVATE_KEY=0x... node buy.mjs "Is Rotamech a manufacturer of centrifugal pumps?" https://example.com
Ou apenas veja o paywall primeiro — isso retorna um 402 e não cobra nada:
curl -X POST https://agent-evidence-api.thx93.workers.dev/v1/evidence \
-H 'content-type: application/json' \
-d '{"question":"Is Rotamech a manufacturer of centrifugal pumps?","urls":["https://example.com"]}'
Infraestrutura de evidências para agentes de IA. Dê a ele uma pergunta ou alegação, mais as páginas da web públicas que você deseja verificar, e ele retorna evidências estruturadas e citadas: URLs de origem, timestamps de recuperação, trechos curtos de evidência, metadados e uma avaliação explícita de supported / contradicted / mixed / inconclusive.
Ele é alcançável por agentes tanto via HTTP quanto via MCP, e é pago por solicitação com x402 + USDC na Base. Não há contas, senhas ou chaves de API — o pagamento é a autenticação.
- Especificação do produto:
SPEC.md— a autoridade em requisitos. - Regras de engenharia:
AGENTS.md— o único livro de regras do projeto. - Arquitetura:
docs/architecture.md
Ele fornece evidências, não verdade. A avaliação é produzida por correspondência lexical determinística, e o serviço diz
inconclusiveem vez de adivinhar. Não trate um statussupportedcomo prova.
Status de implementação — leia isto primeiro. Todos os pacotes e ambos os aplicativos estão implementados, e
pnpm typecheckpassa em todo o workspace.pnpm testcobre ~147 casos em seis suítes, incluindo um teste de fluxo ponta a ponta entre pacotes.O que não está feito: nenhum pagamento x402 on-chain foi demonstrado, não há teste automatizado de pagamento x402,
ROBOTS_POLICYestá configurado, mas inerte, e dois endereços de carteira de teste literais confirmados emapps/worker/wrangler.jsoncprecisam ser substituídos antes de qualquer implantação. Veja Status de implementação.
1. O que o serviço faz
Um agente envia:
- uma pergunta ou alegação, e
- uma ou mais URLs públicas para usar como fontes.
O serviço então:
- valida a URL e rejeita qualquer coisa insegura (defesas SSRF),
- busca a página com timeouts limitados, redirecionamentos e tamanho de resposta,
- detecta o tipo de conteúdo e analisa o HTML,
- extrai metadados (título, descrição, URL canônica, Open Graph, JSON-LD, idioma, datas de publicação e modificação, cabeçalhos, contagem de palavras),
- normaliza o conteúdo principal e produz um hash de conteúdo SHA-256,
- extrai trechos curtos de evidência que correspondem à pergunta, com sua posição no documento,
- compara as fontes e chega a uma avaliação,
- retorna uma resposta estruturada com proveniência completa.
Ele não é um raspador da web, uma ferramenta de SEO, um rastreador genérico, um proxy, um serviço de automação de navegador ou um mecanismo de busca. É infraestrutura de evidências: a coisa que um agente chama quando precisa mostrar seu trabalho.
O que retorna
{
"request_id": "req_3f9c1a7b2e4d4a6f8b0c1d2e",
"version": "1",
"question": "Is Rotamech Industries a manufacturer of centrifugal pumps?",
"assessment": {
"status": "supported",
"basis": "1 source(s) contained passages matching the question (example.invalid) with no contradicting passages found."
},
"sources": [
{
"requested_url": "https://example.invalid/products",
"final_url": "https://example.invalid/products",
"status": 200,
"content_type": "text/html; charset=utf-8",
"title": "Rotamech Industries — Products",
"canonical_url": "https://example.invalid/products",
"description": "Rotamech Industries manufactures centrifugal pumps for industrial use.",
"publisher": "Rotamech Industries",
"language": "en",
"published_at": "2024-03-01T09:00:00.000Z",
"modified_at": null,
"retrieved_at": "2026-09-20T21:16:07.412Z",
"word_count": 63,
"content_hash_sha256": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b",
"evidence": [
{
"excerpt": "Rotamech Industries is a manufacturer of centrifugal pumps.",
"context": "h2:Products > p[1]",
"relevance": "direct"
}
],
"structured_data": { "json_ld": [], "open_graph": {} },
"warnings": [],
"from_cache": false,
"redirect_chain": []
}
],
"limitations": [
"Assessment is derived from deterministic lexical matching, not semantic reasoning. A configured reasoning provider may refine it, but never replaces source evidence."
],
"processing_ms": 412
}
Cada campo é documentado em docs/api.md.
2. Por que um agente o usaria
Porque um agente que cita fontes é mais útil do que um que as afirma.
- Fundamentação. Em vez de gerar uma frase plausível, o agente pode anexar uma URL real, um timestamp de recuperação, um hash de conteúdo e a passagem na qual a alegação se baseia.
- Verificação. Um agente pode verificar o que uma página específica realmente diz, em vez de confiar em um resumo.
- Comparação. Múltiplas fontes são comparadas, e a discordância é relatada como
mixedem vez de ser diluída em uma média. - Honestidade por construção. Quando nada corresponde de perto o suficiente, a resposta é
inconclusive. O serviço nunca inventa um fato, nunca finge que uma fonte diz algo que não diz e nunca emite uma pontuação de confiança. - Sem integração. Sem cadastro, sem chave, sem plano. Descubra a ferramenta, chame-a, pague por solicitação, obtenha evidências.
3. Exemplo de caso de uso
Um agente foi questionado: "A Rotamech Industries é realmente uma fabricante de bombas centrífugas, ou apenas um revendedor?"
O conhecimento do próprio modelo é desatualizado e não verificável, então ele reúne URLs candidatas e chama este serviço:
{
"question": "Is Rotamech Industries a manufacturer of centrifugal pumps?",
"urls": [
"https://example.invalid/products",
"https://example.invalid/about",
"https://example.invalid/registry-record"
],
"max_sources": 5,
"language": "auto",
"mode": "evidence"
}
O site da empresa apoia a alegação; a página do registro a nega. O serviço retorna:
{
"assessment": {
"status": "mixed",
"basis": "Sources disagreed: 2 leaning to support (example.invalid) and 1 leaning against (example.invalid)."
}
}
com trechos curtos citados de cada lado, seus timestamps de recuperação e seus hashes de conteúdo. O agente agora tem algo que não tinha antes: uma resposta defensável que nomeia ambos os lados e mostra de onde cada um veio, além de limitações honestas. Ele pode dizer ao humano "a empresa afirma isso; um registro contesta; aqui estão ambas as fontes", em vez de escolher um lado e parecer confiante.
4. Uso do MCP
Transporte: HTTP streamable. Endpoint: /mcp no mesmo host da API.
${MCP_PUBLIC_URL} e.g. https://${PUBLIC_API_DOMAIN}/mcp
Ferramentas
| Ferramenta | Pagamento | Propósito |
|---|---|---|
research_evidence | pago (x402) | Buscar fontes públicas e retornar evidências estruturadas e citadas. |
health | gratuito | Verificação de status. |
Exemplo de configuração do cliente
{
"mcpServers": {
"agent-evidence-api": {
"type": "http",
"url": "${MCP_PUBLIC_URL}",
"headers": {
"Accept": "application/json, text/event-stream"
}
}
}
}
Não há nada para colocar em headers para autenticação. Não há chave.
initialize e tools/list são gratuitos
Isso importa para agentes autônomos, e é deliberado:
| Método JSON-RPC | Pago? |
|---|---|
initialize | gratuito |
tools/list | gratuito |
tools/call → health | gratuito |
tools/call → research_evidence | pago |
| todo o resto | gratuito |
Um agente deve ser capaz de descobrir as ferramentas antes de decidir pagar por uma. Apenas uma invocação real da ferramenta paga exige pagamento. Um lote JSON-RPC é cobrado se qualquer elemento for uma chamada paga.
Exemplo de chamada de ferramenta
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "research_evidence",
"arguments": {
"question": "Is Rotamech Industries a manufacturer of centrifugal pumps?",
"urls": ["https://example.invalid/products"],
"max_sources": 5,
"language": "auto",
"mode": "evidence"
}
}
}
Esquema de entrada da ferramenta (idêntico ao corpo da requisição HTTP):
| Campo | Tipo | Obrigatório | Observações |
|---|---|---|---|
question | string | sim | 1–2000 caracteres. |
urls | string[] | não | No máximo 25 no esquema, 5 por padrão em tempo de execução. |
max_sources | integer | não | Positivo, ≤ 25. |
language | string | não | "auto" ou uma tag BCP-47. |
mode | "evidence" | não | O único valor na v0.1.0. |
Detalhes completos, incluindo como uma resposta de pagamento exigido se parece para um
cliente MCP: docs/mcp.md.
5. Uso do HTTP
GET /health free liveness
GET / free capabilities
POST /v1/evidence paid the evidence resource
GET /health
curl -s https://${PUBLIC_API_DOMAIN}/health
{ "status": "ok", "service": "agent-evidence-api", "version": "0.1.0" }
GET /
Uma descrição de capacidade gratuita, útil para descoberta antes de pagar.
curl -s https://${PUBLIC_API_DOMAIN}/
{
"service": "agent-evidence-api",
"version": "0.1.0",
"description": "Evidence infrastructure for AI agents. Returns structured, cited, freshly-retrieved evidence from public web pages so an agent can verify claims and ground answers in sources.",
"endpoints": {
"health": "GET /health (free)",
"evidence": "POST /v1/evidence (paid, x402)",
"mcp": "POST /mcp (MCP streamable HTTP; initialize and tools/list are free)"
},
"mcp": { "transport": "streamable-http", "tools": { "research_evidence": "paid", "health": "free" } },
"payment": {
"protocol": "x402",
"scheme": "exact",
"network": "eip155:84532",
"asset": "USDC",
"price_usd": "0.03",
"recipient": "0x…",
"facilitator": "https://x402.org/facilitator"
}
}
POST /v1/evidence
POST /v1/evidence HTTP/1.1
Host: ${PUBLIC_API_DOMAIN}
Content-Type: application/json
payment-signature: <x402 payment proof>
{
"question": "Is Rotamech Industries a manufacturer of centrifugal pumps?",
"urls": ["https://example.invalid/products", "https://example.invalid/about"],
"max_sources": 5,
"language": "auto",
"mode": "evidence"
}
Retorna o documento EvidenceResponse mostrado na §1.
Sem pagamento válido, você recebe HTTP 402 com um envelope de erro PAYMENT_REQUIRED
e os requisitos de pagamento x402:
{
"error": {
"code": "PAYMENT_REQUIRED",
"message": "Payment is required to access this resource.",
"request_id": "req_5c4b3a291807f6e5d4c3b2a1"
}
}
Erros
Toda falha usa um envelope. Os códigos são estáveis — use error.code para decidir.
{
"error": {
"code": "INVALID_URL",
"message": "The supplied URL is invalid or unsupported.",
"request_id": "req_3f9c1a7b2e4d4a6f8b0c1d2e"
}
}
| Código | HTTP |
|---|---|
INVALID_REQUEST | 400 |
INVALID_URL | 400 |
BLOCKED_URL | 400 |
SSRF_ATTEMPT | 400 |
PAYMENT_REQUIRED / PAYMENT_INVALID / PAYMENT_EXPIRED | 402 |
UNAUTHORIZED | 401 |
UNSUPPORTED_CONTENT | 415 |
RATE_LIMIT | 429 |
EXTRACTION_FAILURE / INTERNAL_ERROR | 500 |
TIMEOUT | 504 |
REDIRECT_LIMIT / RESPONSE_TOO_LARGE / UPSTREAM_HTTP_FAILURE / BACKEND_UNREACHABLE | 502 |
NOT_CONFIGURED | 503 |
Tabela completa com significados: docs/api.md §5.
Uma fonte que falha não falha a requisição
Se uma de cinco URLs estiver inacessível, você ainda recebe as outras quatro. A fonte
que falhou aparece com status: null, evidence vazio e um aviso com o
motivo:
{
"requested_url": "https://example.invalid/gone",
"status": null,
"evidence": [],
"warnings": [
{ "code": "UPSTREAM_HTTP_FAILURE", "message": "The source returned an error status." }
]
}
e limitations ganha: "1 de 2 fonte(s) não pôde ser recuperada; veja
avisos por fonte."
6. O fluxo de pagamento x402
Agent
│ 1. POST /v1/evidence (or tools/call research_evidence)
▼
Worker ──▶ 402 Payment Required + payment requirements
│
Agent
│ 2. pays USDC on Base / Base Sepolia to the recipient address
│ 3. retries the same request with payment-signature: <proof>
▼
Worker ──▶ 4. verifies via the x402 facilitator
│ invalid → 402 PAYMENT_INVALID
│ expired → 402 PAYMENT_EXPIRED
▼
Backend ──▶ 5. runs the evidence pipeline
▼
Worker ──▶ 6. 200 + EvidenceResponse
- Sem chaves de API. Sem contas de usuário. Por design (SPEC §11, §38). Um agente nunca deve precisar se cadastrar, solicitar uma chave ou assinar.
- O destinatário precisa apenas de um endereço público. Receber USDC não exige chave privada, porque o serviço nunca assina nada — o cliente pagante é quem assina.
- Nunca comprometa uma chave privada. Não em
wrangler.jsonc, não em.env, não em lugar nenhum.X402_TEST_PRIVATE_KEYé uma chave descartável da Base Sepolia para testes locais apenas, nunca deve conter fundos da mainnet e nunca deve ser comprometida. - O desvio de desenvolvimento é duplamente protegido. Localmente,
DEV_BYPASS_PAYMENT=truepula o bloqueio — mas apenas quando a rede não é a Base mainnet. Emeip155:8453, o pagamento nunca pode ser ignorado, mesmo que a flag vaze para a configuração de produção.
Detalhe completo: docs/x402.md.
7. Base e Base Sepolia
| Ambiente | Rede | Identificador CAIP-2 |
|---|---|---|
| Teste | Base Sepolia | eip155:84532 |
| Produção | Base | eip155:8453 |
O token de liquidação é USDC em ambos os casos.
As configurações de testnet e produção são mantidas em ambientes Worker separados e
nunca devem ser misturadas. O padrão seguro em packages/core/src/config.ts é Base
Sepolia, então uma implantação não configurada fica na testnet em vez de acidentalmente na
mainnet.
apps/worker/wrangler.jsonc fornece três ambientes:
| Ambiente | Ativação | Rede | Pagamento |
|---|---|---|---|
| produção | wrangler deploy | eip155:8453 (Base) | obrigatório |
| dev | wrangler dev --env dev | eip155:84532 (Sepolia) | ignorado |
| teste | wrangler dev --env test | eip155:84532 (Sepolia) | obrigatório |
8. Configuração de preços
O preço é orientado por ambiente, nunca codificado.
| Variável | Padrão | Significado |
|---|---|---|
X402_PRICE_USD | 0.003 | Preço por requisição de evidência, em USD. |
X402_NETWORK | eip155:84532 | Rede de liquidação. |
X402_RECIPIENT | (vazio) | Endereço público de recebimento. |
X402_FACILITATOR_URL | https://x402.org/facilitator | Facilitador usado para verificação. |
O padrão do MVP é aproximadamente US$ 0,03 por requisição de evidência. Esse é um ponto de partida, não um preço comercial fixo.
A string de preço é normalizada antes do uso: um $ inicial é opcional, e o
valor é renderizado com duas casas decimais. Observe que um valor não analisável não
interrompe o Worker — ele silenciosamente volta para $0.03. Verifique o valor após qualquer
alteração.
9. Desenvolvimento local
Requer Node.js ≥ 22.6 (o cache usa o módulo integrado node:sqlite, então
não há dependência nativa para compilar) e pnpm.
pnpm install
cp .env.example .env # placeholders only; fill in locally, never commit
openssl rand -hex 32 # → BACKEND_AUTH_SECRET
pnpm typecheck
pnpm test
| Script | Propósito |
|---|---|
pnpm typecheck | Verificação de tipos de cada workspace a partir do código-fonte. |
pnpm test | Todos os arquivos *.test.ts. |
pnpm test:security | Suíte de SSRF / validação de URL. |
pnpm test:e2e | Fluxo de aceitação ponta a ponta. |
pnpm fixtures | Servidor de fixtures local determinístico. |
pnpm dev:backend | Backend em :8080. |
pnpm dev:worker | Worker em :8787. |
pnpm validate | typecheck && test. |
O servidor de fixtures (tests/fixtures/server.ts) é offline e determinístico — sem
dependência de sites ao vivo. Ele serve páginas de conteúdo, cadeias de redirecionamento,
armadilhas de redirecionamento para privado e para metadados, uma rota que nunca responde, uma
rota de streaming de 8 MiB e respostas 404/500, para que os controles de SSRF e limites possam
ser exercitados contra um socket real.
pnpm fixtures
# [fixtures] listening on http://127.0.0.1:<port>
Ambos os comandos de desenvolvimento funcionam, com uma ressalva.
pnpm dev:backendinicia o backend Fastify em:8080;pnpm dev:workerinicia o Worker. Apontar o Worker para o backend e chamar/v1/evidencetambém exige que a etapa x402 seja liquidada, ouDEV_BYPASS_PAYMENT=truecom uma rede que não seja a mainnet. Veja Status da implementação.
10. Implantação
Cloudflare Worker (public edge) → protected origin → Docker container → Fastify backend → SQLite
-
Backend no VPS. Um serviço Node.js + Fastify construído a partir de
docker/e executado comdocker compose, comrestart: unless-stoppede um volume persistente montado em/app/datapara o cache SQLite. -
Cloudflare Worker.
bash scripts/deploy-live.shenvia o ambiente de produção e se recusa a relatar sucesso a menos que o build saia limpo, o ID da versão realmente mude e o serviço ao vivo verifique de fora. (wrangler deploysozinho canaliza mal:wrangler deploy | tailsai com 0 mesmo quando o build falha, o que escondeu uma implantação quebrada duas vezes.) (Base mainnet). -
O segredo compartilhado.
BACKEND_AUTH_SECRETestá deliberadamente ausente dewrangler.jsoncpara que nunca possa ser comprometido:wrangler secret put BACKEND_AUTH_SECRETEle deve corresponder ao próprio
BACKEND_AUTH_SECRETdo backend. Gere-o comopenssl rand -hex 32. -
Proteção de origem — a opção preferida é um Cloudflare Tunnel, para que o backend não tenha listener público nem registro DNS público. A alternativa é uma origem autenticada atrás de um firewall restrito às faixas de saída da Cloudflare. Em ambos os casos, o segredo compartilhado é validado em cada requisição interna, porque a restrição de rede e a autenticação de requisição são camadas independentes.
Use placeholders — PUBLIC_API_DOMAIN, BACKEND_ORIGIN_URL, MCP_PUBLIC_URL
— e nunca invente um domínio real.
Instruções completas, tabelas de variáveis de ambiente, verificações de saúde e a lista
de verificação manual de credenciais: docs/deployment.md.
Implantável em princípio, ainda não implantado ou exercitado ponta a ponta. Os artefatos Docker, o backend e o Worker existem. Nada foi implantado a partir deste repositório, e dois defeitos de configuração devem ser corrigidos primeiro (veja Status da implementação). Veja
docs/deployment.md.
11. Segurança
O buscador trata cada URL como entrada hostil. Defesas obrigatórias (SPEC §18), todas
aplicadas no pacote @aee/fetcher:
- Lista de permissão de esquemas — apenas
httpehttps;file://e todo o resto rejeitados. - Rejeição de endereços — localhost e seus aliases, loopback, IPv4 RFC1918/privado,
link-local, multicast, não especificado, loopback IPv6, link-local IPv6,
faixas IPv6 privadas e endpoints de metadados de nuvem (
169.254.169.254e amigos). - Evasão codificada — representações decimal, octal, hexadecimal, forma curta e IPv4-mapeado-para-IPv6 são verificadas por faixa após a análise, nunca correspondidas como strings.
- DNS — resolva explicitamente, valide cada endereço resolvido e revalide no momento da conexão para fechar a janela de rebinding de DNS.
- Redirecionamentos — valide cada salto, não apenas a primeira URL, e aplique
MAX_REDIRECTS. - Portas — apenas lista de permissão.
- Limites — timeout de conexão, timeout total de requisição, bytes máximos de resposta aplicados durante o streaming, defesa contra bombas de descompactação, limites de concorrência.
- Nenhuma execução de shell de URLs, nunca. Sem
child_processno caminho de busca. - Logs — JSON estruturado com redação de chaves secretas. Nunca registre segredos de pagamento ou de origem, chaves privadas ou segredos de carteira.
Separadamente, a proteção de origem garante que um chamador não possa pular o Worker e
chamar o backend diretamente para evitar pagar: um Cloudflare Tunnel (preferido) ou uma
origem com firewall, além de autenticação servidor a servidor em cada requisição
interna. BACKEND_AUTH_SECRET não tem padrão justamente para que um backend
mal configurado falhe fechado em vez de rodar sem autenticação.
E as regras de acesso da SPEC §19: sem ignorar controles de acesso, sem derrotar proteções
anti-bot, sem automação de login, sem contornar paywalls, sem resolver CAPTCHA.
Um User-Agent claramente identificável é usado, o comportamento de robots.txt é
configurável via ROBOTS_POLICY, e apenas recursos publicamente acessíveis estão no
escopo. Envie apenas URLs que você tem permissão para acessar.
Esses controles estão implementados em
packages/fetchere testados por unidade emtests/security/, que passa localmente (92 casos de segurança). Nenhuma auditoria independente foi realizada.ROBOTS_POLICYé aplicado. Vejadocs/security.mdpara a tabela de status por controle, o que é aplicado onde, e a lista honesta de lacunas.
12. Limitações
Ditas claramente, porque você deve conhecê-las antes de confiar na saída.
| Limitação | Detalhe |
|---|---|
| Apenas correspondência lexical determinística | A avaliação vem da correspondência de termos mais uma lista fixa de pistas de negação. Não há compreensão semântica. Uma contradição parafraseada que não compartilha vocabulário não será detectada; uma passagem negada que compartilha muitos termos será. |
| Sem raciocínio semântico, a menos que um provedor seja configurado | O ponto de extensão ReasoningProvider na SPEC §37 existe no design e não está implementado. Mesmo quando estiver, ele pode decompor, classificar, comparar e sintetizar — mas nunca deve substituir evidências de origem por prosa sem suporte. |
| Apenas URLs fornecidas pelo agente na v0.1.0 | Você deve fornecer as URLs. Não há provedor de busca, então o serviço não pode descobrir fontes por você. urls é necessário na prática. |
| Excertos são curtos por design | O MAX_EXCERPT_CHARS padrão é 600 (limite do esquema: 4000). Este é um serviço de evidências citadas, não um serviço de despejo de conteúdo, e não reproduz documentos inteiros. |
inconclusive é comum e correto | Se a pergunta não compartilha termos significativos com o texto recuperado, a resposta honesta é inconclusive. Isso é o serviço funcionando, não falhando. |
| Tokens com menos de 3 caracteres são ignorados | Eles são descartados dos termos da pergunta, então perguntas muito curtas ou apenas com siglas podem ter correspondência ruim. |
| Stemming é rudimentar | Um ing/ed/es/s final é removido. Sem biblioteca de stemming, sem formas irregulares. |
| Stopwords são uma lista fixa em inglês | Perguntas não-inglesas perdem pouco (a lista é específica do inglês e majoritariamente inerte em outros idiomas), mas a qualidade da classificação para perguntas não-inglesas não é ajustada. |
| Sem evidências em PDF, imagem ou renderizadas por JavaScript | DocumentProvider e BrowserProvider são pontos de extensão futuros e não estão implementados. Apenas HTML estático. |
| Sem detecção de mudança de fonte ou pontuação de confiança | SourceTrustProvider não está implementado; o serviço relata o que uma página diz, não o quanto confiar no editor. |
| Acertos de cache são sinalizados, não ocultados | from_cache: true mais um aviso de SERVED_FROM_CACHE, e retrieved_at é sempre o tempo real de recuperação. Evidências com até CACHE_TTL_SECONDS (padrão 24 h) podem ser servidas. |
| Uma fonte ruim não falha a solicitação | Falhas são relatadas por fonte em warnings, e uma contagem de falhas é adicionada a limitations. Leia esses arrays. |
| Limitação de taxa é por processo de backend | Um token bucket baseado em CF-Connecting-IP aplica RATE_LIMIT (429) com um mapa de bucket limitado. É em processo, então múltiplas réplicas de backend têm cada uma seu próprio orçamento; limitação no nível de borda exigiria Durable Objects. |
| Conteúdo não-HTML não é processado | Um tipo de conteúdo não suportado gera uma fonte com corpo null e um aviso UNSUPPORTED_CONTENT_TYPE em vez de um erro. HTML, texto simples, XML e JSON são os tipos processados. |
| Correspondência de robots é prefixo/curinga, não RFC 9309 completo | ROBOTS_POLICY=ignore|warn|enforce é aplicado, com um cache limitado por origem. Regras ambíguas resolvem para permitir e geram aviso em vez de recusa silenciosa. Um User-Agent claramente identificável é enviado em cada solicitação. |
| Nenhum pagamento on-chain demonstrado | O gate x402 está implementado; um settlement financiado na Base Sepolia ou mainnet não foi executado contra uma implantação. |
| Nenhuma revisão de segurança independente | Os controles de SSRF estão implementados e testados por unidade no código-fonte; eles não foram auditados adversarialmente. |
E, agora
Implementado e com verificação de tipos: todos os seis pacotes (schemas, core, fetcher,
extraction, cache, mcp), ambos os apps (backend, worker), os artefatos Docker
e server.json. pnpm typecheck passa em todo o workspace, e
seis suítes de teste cobrem aproximadamente 147 casos, incluindo um teste de fluxo de ponta a ponta que
aciona o backend real via HTTP contra o servidor de fixture.
Ainda em aberto:
| Lacuna | Detalhe |
|---|---|
| Endereços de carteira de teste literais | apps/worker/wrangler.jsonc tem o mesmo endereço de destinatário literal em env.dev e env.test. Substitua por placeholders antes de implantar. |
| Correspondência de robots simplificada | ROBOTS_POLICY é aplicado; a correspondência é prefixo/curinga em vez de RFC 9309 completo, e ambiguidade resolve para permitir. |
| Sem verificação de pagamento on-chain | O gate x402 está implementado e revisado; nenhum settlement financiado na Base Sepolia ou mainnet foi executado. |
| Sem teste automatizado de pagamento x402 | A SPEC §26 pede um teste de 402 e um teste de pagamento válido em testnet. Nenhum existe. |
| Limitação de taxa em processo | Aplicada por réplica de backend, não globalmente na borda. |
URL remota server.json é um placeholder | O namespace está confirmado (io.github.Thx93/), mas remotes[].url deve ser substituído, e repository.url referencia um repositório que ainda não existe. |
| Nenhuma revisão de segurança independente | Os controles de SSRF estão implementados e testados por unidade no código-fonte; ninguém os auditou adversarialmente. |
Verificado localmente: pnpm typecheck sai com código 0, pnpm test relata 199 testes
passando, e tanto scripts/smoke.sh quanto scripts/worker-smoke.sh passam contra
sockets ativos. Execute-os novamente e relate a saída real em vez de assumir uma
aprovação — esses resultados são de uma única máquina.
13. Publicação no registro
A SPEC §28 exige que o projeto esteja pronto para registro, não publicado automaticamente. Isso significa:
- um arquivo de metadados
server.jsonusando o esquema oficial atual, - validação desse arquivo,
- etapas exatas de publicação documentadas,
- uma lista de verificação de propriedade de namespace e autenticação,
- publicação manual com o fluxo de trabalho oficial
mcp-publisher, e somente quando credenciais e autorização estiverem explicitamente disponíveis.
server.json agora existe e declara isso como um servidor remoto
streamable-HTTP (sem entrada packages, porque não há pacote npm para
instalar). Duas coisas permanecem antes de ser publicável:
remotes[].urlainda éhttps://mcp.example.invalid/mcp; substitua-o pelo seuMCP_PUBLIC_URLreal.nameerepository.urlafirmam um namespace do GitHub — confirme se são genuinamente seus, porque a propriedade do namespace é validada e um identificador fabricado falha.
Nada foi publicado. O procedimento exato de publicação, etapas de validação e
lista de verificação de namespace estão em
docs/registry-publication.md.
A estratégia de descoberta — o Registro MCP, o repositório GitHub, metadados npm
quando apropriado, metadados legíveis por máquina, documentação e descrições de ferramentas
orientadas a agentes — está em docs/discovery.md. Ela também
lista as frases descritivas verdadeiras que valem a pena usar (evidência web, verificação de
fonte, verificação de alegação, extração de evidência, pesquisa fundamentada em fontes,
comparar fontes, evidência citada, evidência web fresca) e afirma
claramente que as descrições devem ser verdadeiras e não devem ser recheadas de palavras-chave.
Não publique até que o serviço realmente funcione. Uma entrada de registro listando uma
ferramenta que não pode ser chamada é pior do que nenhuma entrada. Dois bloqueadores concretos permanecem:
o placeholder remotes[].url deve ser substituído por um endpoint ativo, e o
namespace name/repository.url deve ser confirmado como genuinamente seu.
Estrutura do projeto
agent-evidence-api/
├── AGENTS.md # sole project rules file
├── SPEC.md # product specification (authority on requirements)
├── README.md
├── server.json # MCP Registry metadata (remote streamable-HTTP server)
├── .env.example # environment template, placeholders only
├── pnpm-workspace.yaml
├── package.json
├── docs/
│ ├── architecture.md # architecture, pipeline, layering rule
│ ├── api.md # HTTP endpoints, exact schemas, error codes, limits
│ ├── mcp.md # MCP transport, tools, free vs paid, client config
│ ├── x402.md # payment flow and configuration
│ ├── security.md # SSRF controls, origin protection, secrets
│ ├── deployment.md # local, Docker, Worker, origin protection
│ ├── discovery.md # agent discoverability strategy
│ └── registry-publication.md # exact MCP Registry publish steps
├── apps/
│ ├── backend/ # Fastify service — the VPS workload
│ │ └── src/ # app.ts, auth.ts, server.ts
│ └── worker/ # Cloudflare Worker — the public edge
│ ├── src/index.ts
│ ├── wrangler.jsonc
│ └── .dev.vars.example
├── packages/
│ ├── schemas/ # zod schemas + types (the shared wire contract)
│ ├── core/ # EvidenceService, config, logger, assessment
│ ├── fetcher/ # SSRF-hardened HTTP fetching (ip.ts, fetch.ts)
│ ├── extraction/ # HTML parsing, metadata, main content, lexical ranking
│ ├── cache/ # SQLite-backed bounded cache (node:sqlite)
│ └── mcp/ # MCP server + tool definitions (streamable HTTP)
├── tests/
│ ├── fixtures/ # deterministic local fixture server
│ ├── security/ # ssrf.test.ts, limits.test.ts
│ └── e2e/ # evidence-flow.test.ts (cross-package)
├── scripts/
│ ├── run-tests.mjs # test runner (node --test + tsx)
│ └── smoke.sh # live socket smoke test against the built backend
├── .dockerignore
└── docker/
├── Dockerfile
├── docker-compose.yml
├── env.production.example # Base mainnet
└── env.development.example # Base Sepolia
Tanto o adaptador HTTP quanto o adaptador MCP chamam o mesmo EvidenceService —
não há lógica de negócios duplicada entre eles (SPEC §39):
HTTP adapter ─────┐
├──> EvidenceService ──> Fetch / Extract / Cache
MCP adapter ──────┘
x402 fica no limite de pagamento no Worker, nunca dentro do mecanismo principal.
Status da implementação
Instantâneo tirado enquanto escrevia a documentação.
Implementado
packages/schemas— o contrato de fio completo (solicitações, respostas, fontes, itens de evidência, avisos, erros, limites) com saída construída.packages/fetcher— recuperação endurecida contra SSRF: allowlist de esquemas, classificação de faixas IPv4/IPv6 (incluindo formas decimal/octal/hex/127.1e IPv6 mapeado por IPv4), regras de hostname, allowlist de portas, validação de IP resolvido em todos os registros DNS, validação de redirecionamento por salto, revalidação na conexão contra rebinding de DNS, timeouts de conexão e total, limite de bytes em streaming e descompressão manual.packages/extraction— parsing de HTML, metadados, extração de conteúdo principal, normalização, hash de conteúdo, classificação lexical determinística, pistas de negação.packages/cache— normalização de chaves,CacheProvider, adaptador SQLite limitado.packages/core— configuração, logger estruturado com redação de segredos, avaliação entre fontes,EvidenceService,ServiceErrortipado.packages/mcp— o adaptador MCP remoto: HTTP streamable no modo stateless (sessionIdGenerator: undefined,enableJsonResponse: true), um novo parServer+ transporte por solicitação HTTP,tools/listetools/calldelegando para o mesmoEvidenceServiceque a camada HTTP usa. Esquemas de entrada de ferramentas são gerados a partir de zod viaz.toJSONSchema.apps/backend— serviço Fastify: inicialização com falha fechada em segredo ausente, verificação de segredo compartilhado em tempo constante em tudo, exceto/health,GET /health,POST /internal/v1/evidencee POST/GET/DELETE em/mcp.apps/worker— a borda completa: gate x402,/healthe/gratuitos, encaminhamentoPOST /v1/evidence, inspeção e encaminhamento de corpo MCP, bypass de desenvolvimento protegido, envelope de erro canônico.docker/—Dockerfilemulti-estágio,docker-compose.yml, modelos de ambiente separados para mainnet/testnet, além de um.dockerignoreraiz.server.json,apps/worker/.dev.vars.example,scripts/smoke.sh.
Verificado neste ambiente
pnpm typecheck— passa em todo o workspace (tsc --noEmit, código de saída 0).- Seis suítes de teste existem totalizando ~147 casos:
tests/security/ssrf.test.ts(30),tests/security/limits.test.ts(16),tests/e2e/evidence-flow.test.ts(18),packages/mcp/src/mcp.test.ts(15),packages/extraction/src/extraction.test.ts(46),packages/cache/src/cache.test.ts(22).
Verificado localmente, não reproduzido de forma independente
pnpm typecheck→ código de saída 0;pnpm test→ 199 passando;scripts/smoke.shescripts/worker-smoke.sh→ todas as verificações passaram. Nenhum terceiro reproduziu esses resultados.- Nenhum pagamento x402 on-chain foi demonstrado (o gate 402 está verificado; o caminho de settlement precisa de uma carteira de testnet financiada).
Defeitos em aberto
| Defeito | Detalhe |
|---|---|
| Endereços de carteira de teste literais | apps/worker/wrangler.jsonc contém o mesmo endereço de destinatário literal em env.dev e env.test. A SPEC §33 e AGENTS.md §6 proíbem commitar configuração de carteira. |
| Correspondência de robots simplificada | ROBOTS_POLICY é aplicado em EvidenceService; a correspondência é prefixo/curinga, não RFC 9309 completo. |
| — (corrigido) | UNSUPPORTED_CONTENT (415) agora é emitido por fonte. |
| Sem teste de settlement on-chain | O gate 402 e a divisão MCP gratuito/pago são cobertos por scripts/worker-smoke.sh; a limitação de taxa é testada por unidade e integração. O settlement real de USDC precisa de uma carteira financiada na Base Sepolia, que é uma credencial externa. |
server.json ainda não publicável | O namespace está confirmado, mas remotes[].url é um placeholder e o repositório referenciado não existe. |
| Allowlist de portas inclui 8080/8443 | Conveniente para ferramentas, mas também portas comuns de interfaces internas. Restrinja se desnecessário. |
Documentação
| Documento | Conteúdo |
|---|---|
docs/architecture.md | Mapa de componentes, pipeline de requisições, divisão edge/backend, fronteira de autenticação, regra de camadas, propriedade de pacotes, pontos de extensão. |
docs/api.md | GET /health, GET /, POST /v1/evidence; esquemas exatos de requisição e resposta; status de avaliação; exemplos práticos; todos os códigos de erro; limites de recursos e suas variáveis de ambiente. |
docs/mcp.md | Transporte HTTP streamable, /mcp, ambas as ferramentas, esquema de entrada exato, descoberta gratuita vs paga, configuração do cliente, exemplo de chamada, resposta de pagamento obrigatório. |
docs/x402.md | A sequência 402 → pagar → tentar novamente → verificar → executar, variáveis de configuração, IDs de rede CAIP-2, liquidação USDC, separação teste/produção, o bypass de desenvolvimento e as regras de chave privada. |
docs/security.md | O conjunto de controles SSRF exigido pela SPEC §18, proteção de origem, o esquema de segredo compartilhado, tratamento de segredos, comportamento de robots/acesso e o que é aplicado onde. |
docs/deployment.md | Desenvolvimento local, Docker no VPS, o Cloudflare Worker, wrangler secret put BACKEND_AUTH_SECRET, verificações de saúde, política de reinício, o volume SQLite e opções de proteção de origem. |
docs/discovery.md | Registro, repositório, npm, metadados legíveis por máquina, documentação e descrições de ferramentas orientadas a agentes. |
docs/registry-publication.md | As etapas exatas de publicação no MCP Registry, validação e a lista de verificação de propriedade de namespace. |
Regras de contribuição
AGENTS.md é o único livro de regras do projeto. Ele define as
restrições rígidas, as regras de segurança, os padrões de codificação, o nível de teste e a
definição de pronto. Não crie CLAUDE.md, .cursorrules, GEMINI.md ou qualquer
arquivo de instruções concorrente.
Os requisitos de produto estão em SPEC.md. Onde este README e a
especificação discordarem, a especificação é autoritativa — e qualquer contradição entre o
código-fonte e a especificação deve ser relatada em vez de resolvida silenciosamente.
Licença
MIT. Consulte package.json.