Pular para o conteúdo

MCP Client — Agent Loop

Visao Geral

O Floopy pode atuar como cliente MCP: ele se conecta a servidores MCP externos em nome dos seus agentes e injeta as ferramentas deles na conversa. Quando o LLM decide chamar uma ferramenta, o Floopy a executa, adiciona o resultado a conversa e volta ao modelo — tudo de forma transparente.

Este e o agent loop: o modelo raciocina, chama ferramentas, observa resultados e raciocina novamente ate chegar a uma resposta final.


O Agent Loop

flowchart TD
    A[Mensagem do usuario] --> B[Chamada LLM]
    B --> C{Tool call solicitado?}
    C -- Nao --> D[Retornar resposta final]
    C -- Sim --> E[Executar tools via servidor MCP]
    E --> F[Pos-processamento dos resultados]
    F --> G[Adicionar resultados as mensagens]
    G --> H{Round < max_rounds?}
    H -- Sim --> B
    H -- Nao --> I[Retornar ultima resposta do modelo]

O loop e limitado por max_rounds para evitar execucao infinita. Quando o limite e atingido, o Floopy retorna a ultima resposta do modelo. Um timeout global (padrao 120s) tambem se aplica a todo o loop.


Schema do Plugin YAML

Configure o agent loop com um plugin YAML vinculado a sua regra de roteamento ou enviado como header de requisicao (floopy-mcp-plugin).

Exemplo Completo

version: "1"
mcp_servers:
- id: pesquisa_web
url: "https://mcp.example.com/search"
auth:
type: bearer
secret_ref: "secret.mcp_search_api_key" # resolvido do Floopy Vault
tools:
- search_web
- fetch_page
timeout_ms: 5000
max_retries: 2
- id: interpretador_codigo
url: "https://mcp.example.com/code"
auth:
type: api_key
header: "X-Api-Key"
secret_ref: "secret.mcp_code_api_key"
tools: "*" # expoe todas as ferramentas deste servidor
timeout_ms: 15000
agent:
max_rounds: 10
stream_mode: final_only # final_only | disabled
tool_call_parallel: true # executa chamadas independentes em paralelo
tool_cache_ttl_seconds: 300 # cache de resultados de ferramentas (0 = desativado)
# prompt_guard_on_tool_output era o scan ONNX pre-migracao dos
# resultados — atualmente no-op enquanto a interface sync→async do
# firewall e refeita. Campo aceito para compatibilidade.

Referencia de Campos

mcp_servers[]

CampoTipoObrigatorioDescricao
idstringsimIdentificador unico deste servidor no plugin
urlstringsimEndpoint HTTP(S) do servidor MCP (deve passar o validador SSRF)
authobjetonaoAutenticacao para usar ao chamar o servidor
toolsstring[] ou "*"naoFerramentas a expor. Padrao: "*" (todas)
timeout_msinteironaoTimeout por requisicao. Padrao: 5000
max_retriesinteironaoTentativas em erros transientes. Padrao: 1

auth

Tipo de authCamposDescricao
bearersecret_refEnvia Authorization: Bearer <segredo>
api_keyheader, secret_refEnvia o segredo em um header customizado
hmacsecret_ref, algorithmAssina o corpo da requisicao (SHA-256 padrao)
noneSem autenticacao

agent

CampoTipoPadraoDescricao
max_roundsinteiro5Iteracoes maximas antes de retornar
stream_modeenumfinal_onlyQuando transmitir: final_only ou disabled
tool_call_parallelbooleanotrueExecuta chamadas de ferramenta independentes em paralelo
tool_cache_ttl_secondsinteiro0Cache de chamadas identicas (por hash de args)
prompt_guard_on_tool_outputbooleanofalseScan ONNX dos resultados (pre-migracao). Atualmente no-op enquanto a interface sync Validator e refeita. Campo aceito para compatibilidade.

Gerenciamento de Segredos

Nunca coloque chaves de API diretamente no YAML. Armazene-as no Floopy Vault e referencie-as pelo nome.

Armazenando um Segredo

  1. Abra o dashboard do Floopy e navegue ate Configuracoes > Segredos
  2. Clique em Adicionar Segredo
  3. Digite o nome (por exemplo, mcp_search_api_key) e o valor
  4. Clique em Salvar

O segredo e criptografado em repouso (AES-256) e injetado em tempo de execucao — ele nunca e registrado em logs nem retornado em respostas da API.

Referenciando um Segredo

Use o prefixo secret. seguido do nome que voce armazenou:

auth:
type: bearer
secret_ref: "secret.mcp_search_api_key"

O formato e sempre secret.<nome> onde <nome> corresponde exatamente ao que voce armazenou no dashboard. Somente caracteres alfanumericos, hifens e underscores sao permitidos (maximo 64 caracteres). Caracteres como :, / e . (alem do prefixo) sao rejeitados por seguranca.

Internamente, cada segredo e isolado por organizacao — seus segredos nunca sao acessiveis por outros tenants.


Modos de Streaming

ModoComportamento
final_onlyTransmite a resposta LLM final apos todas as chamadas de ferramentas serem concluidas. Chamadas intermediarias nao sao transmitidas.
disabledRetorna a resposta completa como um unico objeto JSON quando o loop termina.

As etapas intermediarias sempre ficam disponiveis no log de requisicoes em Observabilidade, independentemente do modo de streaming.


Limites do Loop e Timeouts

Defina max_rounds de acordo com seu caso de uso:

Caso de usomax_rounds recomendado
Consulta a ferramenta unica2–3
Pesquisa em multiplas etapas5–8
Agente autonomo complexo10–15

Cada rodada adiciona latencia do LLM mais tempo de execucao da ferramenta. Mantenha timeout_ms baixo por servidor para evitar travamentos no loop.

Um timeout de gateway rigido de 120 segundos se aplica a todo o agent loop. Requisicoes que excedem esse limite sao encerradas e a resposta parcial e retornada com finish_reason: timeout.


Enviando o Plugin via Header

Em vez de vincular o plugin a uma regra de roteamento, voce pode envia-lo inline por requisicao usando o header floopy-mcp-plugin com o valor YAML codificado em base64:

import { OpenAI } from "openai";
import { Buffer } from "buffer";
const plugin = `
version: "1"
mcp_servers:
- id: pesquisa
url: "https://mcp.example.com/search"
auth:
type: bearer
secret_ref: "secret.mcp_search_api_key"
agent:
max_rounds: 5
`;
const client = new OpenAI({
baseURL: "https://api.floopy.ai/v1",
apiKey: process.env.FLOOPY_API_KEY,
defaultHeaders: {
"floopy-mcp-plugin": Buffer.from(plugin).toString("base64"),
},
});
const response = await client.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Qual e o preco atual do BTC?" }],
});

Exemplo Completo

O exemplo a seguir conecta um servidor MCP de busca na web a um agente GPT-4o que responde perguntas de pesquisa.

Plugin YAML (vinculado a regra de roteamento “Agente de Pesquisa”):

version: "1"
mcp_servers:
- id: brave_search
url: "https://mcp.brave.com/search"
auth:
type: bearer
secret_ref: "secret.brave_api_key"
tools:
- web_search
timeout_ms: 8000
agent:
max_rounds: 6
stream_mode: final_only
tool_call_parallel: false
# prompt_guard_on_tool_output e no-op atualmente (veja a Referencia de Campos)

Requisicao:

const response = await client.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "user",
content: "Quais sao os tres artigos mais citados sobre atencao em transformers publicados em 2024?",
},
],
});
console.log(response.choices[0].message.content);
// O modelo pesquisou na web, leu os resultados e sintetizou uma resposta final.

O que aconteceu internamente:

  1. GPT-4o chamou web_search("transformer attention papers 2024")
  2. O Floopy executou a ferramenta via servidor MCP do Brave
  3. Os resultados foram adicionados a conversa
  4. GPT-4o chamou web_search("citation counts transformer 2024") para aprofundar
  5. O Floopy retornou a resposta final sintetizada apos a rodada 2

Observabilidade

Cada execucao do agent loop e registrada completamente:

  • Chamadas de ferramentas realizadas (nome, argumentos, duracao)
  • Resultados das ferramentas (sanitizados — segredos reduzidos)
  • Numero de rodadas concluidas
  • Total de tokens consumidos em todas as rodadas
  • Se o loop atingiu max_rounds

Veja os logs em Observabilidade > Requisicoes no dashboard. Filtre por has_tool_calls: true para isolar sessoes agenticas.