Crie um agente de pesquisa na web com o Claude Agent SDK e um orçamento rígido
Cerca de 60 linhas de Python: Claude Agent SDK mais o servidor MCP do FastAF para busca, leitura de páginas e Google Scholar. Uma execução real custou 11 créditos.

Um agente de pesquisa precisa de três coisas: um jeito de encontrar fontes, um jeito de lê-las e um limite para quanto pode gastar fazendo as duas coisas. O Claude Agent SDK dá o loop. O servidor MCP hospedado do FastAF dá as ferramentas. Este post liga as duas partes em cerca de 60 linhas de Python, roda o agente numa pergunta real e mostra quanto custou.
Do que você precisa antes de começar?
Python 3.10 ou mais recente, o SDK e duas chaves:
python3 -m venv .venv && source .venv/bin/activate
pip install claude-agent-sdk
export ANTHROPIC_API_KEY=... # conforme o quickstart do Agent SDK
export FASTAF_API_KEY=... # do seu dashboard do FastAF
O pacote é o claude-agent-sdk (versão 0.2.168 no PyPI, publicada em 15 de outubro de 2026), e ele já traz o binário do Claude Code, então não há mais nada para instalar. Numa máquina de desenvolvimento ele também aproveita um login existente do Claude Code, que foi como a execução abaixo foi feita. A visão geral do Agent SDK da Anthropic, verificada em 19 de outubro de 2026, diz que produtos construídos sobre o SDK devem usar autenticação por chave de API e não o login do claude.ai, então publique com ANTHROPIC_API_KEY.
O lado do FastAF é uma URL: https://api.fastaf.co/mcp, Streamable HTTP, com a sua chave num header Bearer. Ela expõe 15 ferramentas; este agente usa três. search_web e search_scholar custam 5 créditos cada, scrape_page custa 1 crédito para uma página estática. A documentação do MCP lista as outras.
O agente
import asyncio, json, os, sys
from claude_agent_sdk import (
query, ClaudeAgentOptions, HookMatcher, AssistantMessage, UserMessage,
ResultMessage, ToolUseBlock, ToolResultBlock,
)
MAX_CALLS = 6
SYSTEM = f"""You are a research agent. Answer only from what the tools return.
Use at most {MAX_CALLS} tool calls, one at a time. If a tool returns RATE_LIMITED, retry it.
search_web and search_scholar find sources; scrape_page reads the 1-3 best ones.
Answer in under 250 words. Cite every claim as [n] and list the URLs at the end."""
calls = 0
async def cap_calls(input_data, tool_use_id, context):
global calls
calls += 1
if calls <= MAX_CALLS:
return {}
return {"hookSpecificOutput": {
"hookEventName": input_data["hook_event_name"],
"permissionDecision": "deny",
"permissionDecisionReason": "Tool budget used up. Answer with what you have.",
}}
options = ClaudeAgentOptions(
model="claude-sonnet-5-5",
system_prompt=SYSTEM,
mcp_servers={"fastaf": {
"type": "http",
"url": "https://api.fastaf.co/mcp",
"headers": {"Authorization": f"Bearer {os.environ['FASTAF_API_KEY']}"},
}},
tools=[], # nenhuma ferramenta embutida (Bash, Read, ...)
allowed_tools=[f"mcp__fastaf__{t}" for t in ("search_web", "scrape_page", "search_scholar")],
permission_mode="dontAsk", # tudo o que não é permitido é negado
setting_sources=[], # ignora as configurações locais do Claude Code
env={"ENABLE_CLAUDEAI_MCP_SERVERS": "false"}, # nenhum conector do claude.ai
hooks={"PreToolUse": [HookMatcher(matcher="mcp__fastaf__.*", hooks=[cap_calls])]},
max_turns=10,
max_budget_usd=0.50,
)
async def main(question):
credits = 0
async for msg in query(prompt=question, options=options):
if isinstance(msg, AssistantMessage):
for b in msg.content:
if isinstance(b, ToolUseBlock):
print(f"-> {b.name.removeprefix('mcp__fastaf__')} {b.input}")
elif isinstance(msg, UserMessage) and isinstance(msg.content, list):
for b in msg.content:
if isinstance(b, ToolResultBlock):
text = b.content if isinstance(b.content, str) else b.content[0]["text"]
if b.is_error:
print(f" error: {text}")
else:
data = json.loads(text)
credits += data["credits_used"]
print(f" {data['credits_used']} credits, {data['credits_remaining']} left")
elif isinstance(msg, ResultMessage):
print("\n" + (msg.result or f"stopped: {msg.subtype}"))
print(f"\nturns={msg.num_turns} fastaf_credits={credits} model_usd={msg.total_cost_usd:.4f}")
asyncio.run(main(sys.argv[1]))
As opções que importam, segundo a referência do SDK para Python e o guia de MCP, ambos verificados em 19 de outubro de 2026:
- As ferramentas MCP se chamam
mcp__<server>__<tool>, então a chave"fastaf"geramcp__fastaf__search_web. allowed_toolssó aprova automaticamente; não esconde as outras 12 ferramentas do FastAF. Quem nega tudo o que não está na lista épermission_mode="dontAsk".tools=[]remove as ferramentas embutidas. Na nossa primeira execução, a mensagem de init ainda listava conectores do claude.ai (o Gmail entre eles) vindos do login local, e é por isso queENABLE_CLAUDEAI_MCP_SERVERSestá emfalse.- Todo resultado de ferramenta do FastAF é um JSON que traz
credits_usedecredits_remaining, então o script soma o gasto real em vez de estimá-lo.
O que ele fez de fato?
Rodado em 19 de outubro de 2026 com a pergunta “What changed in the Model Context Protocol (MCP) specification in 2026, and what does recent academic research say about MCP security risks? Cite sources.” Saída resumida:
-> search_web {'query': 'Model Context Protocol specification 2026 changelog new version', 'language': 'en'}
-> search_scholar {'query': 'Model Context Protocol security risks attacks', 'year_from': 2025, 'results': 8}
5 credits, 890 left
5 credits, 885 left
-> scrape_page {'url': 'https://blog.modelcontextprotocol.io/posts/2026-07-28/'}
1 credits, 884 left
The 2026-07-28 revision is the main 2026 release. It followed the November 2025 release [1].
- Stateless core: The initialize/initialized handshake and the Mcp-Session-Id header are gone. [...] [1]
- Routing headers: Mcp-Method and Mcp-Name are now required on Streamable HTTP. [...] [1]
- Authorization hardening: Clients must validate the RFC 9207 iss parameter. [...] [1]
[...]
- A survey in ACM Transactions on Software Engineering and Methodology (TOSEM) identifies 16 threat scenarios. [2]
- An arXiv study ran 847 attack scenarios. [...] This is a preprint, and I only saw its abstract snippet. [3]
[...]
[1] https://blog.modelcontextprotocol.io/posts/2026-07-28/
[2] https://dl.acm.org/doi/abs/10.1145/3796519
[3] https://arxiv.org/abs/2601.17549
turns=4 fastaf_credits=11 model_usd=0.0578
Três chamadas de ferramenta, 11 créditos do FastAF, cerca de 20 segundos e US$ 0,0578 de custo de modelo, segundo a estimativa do SDK. Conferimos as afirmações sobre a spec com a página raspada e os números dos papers com os snippets do Scholar: está tudo lá. O agente também avisou que só viu os resumos, que é exatamente o comportamento que você quer. Para perguntas centradas em papers, a página da Google Scholar API descreve os campos que search_scholar devolve.
Uma observação honesta: a nossa chave de teste divide uma conta com concorrência 1 com outros jobs, e as primeiras tentativas receberam RATE_LIMITED na maioria das chamadas (chamadas que falham nunca são cobradas). Para a execução acima adicionamos um segundo hook de depuração que espera um slot livre. Com a sua própria chave você não vai precisar dele, mas leia a próxima seção antes.
Como limitar o que um agente de pesquisa gasta?
Três camadas, da mais branda à mais rígida:
max_turnslimita o loop. Com 10, o SDK para com o subtypeerror_max_turns, seja lá o que o modelo tenha planejado.max_budget_usdfaz o mesmo para o custo do modelo, embora a documentação avise que a estimativa pode passar do valor.- Um limite de chamadas no prompt é uma preferência. Vimos isso na prática: o prompt dizia “one at a time”, e em várias execuções o modelo ainda mandou duas chamadas no mesmo turno, uma vez três leituras de página de uma só vez. Numa conta Free (concorrência 1) a chamada extra recebe
RATE_LIMITED. O hookPreToolUsetransforma o limite numa regra: comMAX_CALLS = 1e o prompt ainda dizendo 6, o hook negou três scrapes, o agente respondeu a partir da sua única busca e a execução custou 5 créditos. - O saldo da conta é o teto rígido. Cada chamada reserva o seu custo antes de começar, e um saldo vazio devolve
402 insufficient_credits. O saldo pertence à conta e é compartilhado por todas as suas chaves, então uma chave separada não te dá um orçamento separado. Coloque na conta o que você aceita perder para um loop ruim.
O post sobre teto de gastos se aprofunda no lado do wrapper, e grounding com uma SERP API cobre o mesmo padrão de buscar e depois ler, sem um loop de agente.
Resumindo
Aponte mcp_servers para https://api.fastaf.co/mcp com um header Bearer, permita três ferramentas, defina permission_mode="dontAsk" e adicione um hook PreToolUse que conta as chamadas. Uma resposta com citações sobre um tema de 2026 custou 11 créditos, o que dá cerca de 1.800 respostas desse tipo com um pacote Starter de US$ 10. Os preços têm o custo de cada ferramenta, e criando uma conta você ganha 500 créditos grátis para rodar o agente você mesmo.