# Playbook: MCP na Prática — Conecte um Agente a uma Ferramenta Interna

O Model Context Protocol elimina o adaptador artesanal entre agente e ferramenta: um servidor MCP expõe tools com nome, descrição e schema; o cliente descobre e chama sem acoplamento direto. Este playbook cobre os três passos concretos para subir um servidor MCP, descrever bem uma tool e conectar o agente — com atenção especial à segurança, que é onde a maioria erra.

- URL: https://fernando.moretes.com/studies/playbook-mcp-na-pratica-conecte-um-agente-a-uma-ferramenta

- Markdown: https://fernando.moretes.com/studies/playbook-mcp-na-pratica-conecte-um-agente-a-uma-ferramenta/study.md?lang=pt

- Type: Playbook

- Domain: IA / MCP

- Date: 2025-06-12

- Tags: MCP, AI Agents, GenAI, AWS Bedrock, Tool Use, Security, Event-Driven, Architecture

- Reading time: 10 min

---

Cada nova ferramenta que seu agente precisa chamar virava um adaptador na mão: código acoplado ao modelo, schema hardcoded, autenticação resolvida na gambiarra. O Model Context Protocol resolve isso com um contrato padrão — o servidor anuncia o que sabe fazer, o agente descobre e chama. Um protocolo, N ferramentas, zero reescrita quando você troca de modelo.

## O que você vai conseguir decidir e fazer

- Entender o modelo mental do MCP: cliente ↔ servidor ↔ ferramenta, e onde cada peça vive
- Subir um servidor MCP expondo uma ferramenta interna em 3 passos concretos e verificáveis
- Escrever descrições de tool que o agente realmente usa — trate como parte do prompt, não como documentação
- Escolher entre integração ad-hoc e MCP com critérios objetivos de acoplamento, segurança e manutenção
- Aplicar o mínimo privilégio antes de colocar qualquer tool em produção
- Reconhecer os três anti-padrões que destroem integrações MCP em produção

## Referência Rápida — MCP

- **Criador / Origem:** Anthropic — anunciado em novembro de 2024, especificação aberta
- **Transporte suportado:** stdio (local/subprocesso), HTTP + SSE (remoto), WebSocket (em revisão na spec)
- **SDKs oficiais:** Python, TypeScript/Node, Java, Kotlin, C# (todos open-source, Apache 2.0)
- **Integração AWS:** Bedrock AgentCore Gateway — gerencia servidores MCP como endpoints de tool para agentes Bedrock
- **Primitivos do protocolo:** Tools (chamadas), Resources (dados/contexto), Prompts (templates), Sampling (o servidor pede ao modelo)
- **Formato de mensagem:** JSON-RPC 2.0 sobre o transporte escolhido
- **Autenticação (spec):** OAuth 2.1 para servidores remotos; stdio herda o contexto do processo pai

## O modelo mental que desbloqueia tudo

Antes do MCP, integrar uma ferramenta a um agente era um problema de três camadas resolvido de forma artesanal em cada projeto: você precisava (1) serializar a chamada no formato que o modelo esperava, (2) executar a lógica da ferramenta, e (3) devolver o resultado em um formato que o modelo conseguia interpretar. Cada LLM tinha seu próprio dialeto de function calling. Trocar de modelo significava reescrever os adaptadores. Adicionar uma ferramenta nova significava tocar no código do agente.

O MCP inverte a dependência. Em vez de o agente saber como chamar cada ferramenta, o **servidor MCP** anuncia o que ele sabe fazer — e o agente descobre em tempo de execução via `tools/list`. O contrato é simples: cada tool tem um `name`, uma `description` (texto livre, lido pelo modelo), e um `inputSchema` (JSON Schema). O agente chama `tools/call` com o nome e os argumentos; o servidor executa e devolve o resultado. JSON-RPC 2.0 sobre stdio ou HTTP+SSE.

A analogia que uso: pense no servidor MCP como um **microserviço com contrato autodescritivo**. O cliente não precisa de documentação externa — o próprio servidor declara suas capacidades. Isso é o que permite um agente genérico (Claude, GPT-4, um modelo no Bedrock) consumir qualquer ferramenta sem recompilação. O protocolo faz o trabalho de tradução.

Um detalhe que muda tudo na prática: **o agente escolhe qual tool chamar baseado na descrição**, não no nome. O modelo lê o campo `description` como parte do contexto e decide se aquela tool resolve o que o usuário pediu. Isso significa que uma descrição ruim — vaga, técnica demais, sem exemplos de quando usar — vai fazer o agente ignorar a tool ou chamá-la no momento errado. Trate a descrição como um fragmento de prompt de sistema, não como um docstring.

## Integração Ad-hoc vs. MCP
| Critério | Critério | Integração Ad-hoc (na mão) | MCP |
| --- | --- | --- | --- |
| Acoplamento ao modelo | Alto — schema de function call específico por LLM | Baixo — protocolo agnóstico de modelo | — |
| Troca de modelo | Reescrita dos adaptadores | Zero mudança no servidor MCP | — |
| Descoberta de tools | Hardcoded no código do agente | Dinâmica via tools/list em runtime | — |
| Adicionar nova ferramenta | Toca no código do agente + redeploy | Novo servidor MCP ou nova tool no servidor existente — agente não muda | — |
| Segurança / controle de acesso | Implementado por fora, inconsistente entre tools | Camada de autenticação na borda do servidor MCP (OAuth 2.1 ou IAM no Bedrock Gateway) | — |
| Validação de argumentos | Manual ou ausente | JSON Schema declarado no contrato — validável antes de executar | — |
| Manutenção a longo prazo | Cresce linearmente com o número de tools × modelos | Cresce linearmente com tools, independente de modelos | — |
| Quando preferir | Uma tool, um modelo, prazo curto, sem plano de escalar | Múltiplas tools, múltiplos agentes/modelos, ou qualquer requisito de auditoria | — |

## Por que a descrição da tool é parte do prompt — e não documentação

Quando o agente recebe a lista de tools disponíveis via `tools/list`, o modelo vê algo assim no contexto:

```json
{
  "name": "search_internal_knowledge_base",
  "description": "Busca documentos na base de conhecimento interna da empresa. Use quando o usuário fizer perguntas sobre políticas internas, processos de RH, documentação técnica de produtos ou histórico de projetos. NÃO use para perguntas sobre dados em tempo real ou informações externas à empresa.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "query": { "type": "string", "description": "Texto da busca em linguagem natural" },
      "department": { "type": "string", "enum": ["engineering", "hr", "finance", "all"], "default": "all" }
    },
    "required": ["query"]
  }
}
```

O modelo lê o campo `description` como instrução. Três elementos fazem uma descrição eficaz:

**1. Quando usar (positivo):** descreva o caso de uso em linguagem natural, próxima à forma como o usuário vai perguntar. "Use quando o usuário fizer perguntas sobre políticas internas" é melhor que "busca na KB".

**2. Quando NÃO usar (negativo):** isso reduz falsos positivos — o agente chamando a tool em contextos errados. Explicitar o escopo negativo é tão importante quanto o positivo.

**3. Descrições dos parâmetros no inputSchema:** cada campo `description` dentro do schema também é lido pelo modelo. "Texto da busca em linguagem natural" instrui o modelo a não mandar um ID técnico onde deveria mandar uma frase.

Um erro comum: copiar o nome da função como descrição. `"description": "search_kb"` é inútil — o modelo já tem o nome. A descrição precisa adicionar contexto semântico que o nome não carrega.

Outro erro: descrições genéricas demais. Se você tem três tools de busca e todas dizem "busca informações", o agente vai escolher aleatoriamente ou sempre usar a primeira. Diferencie com precisão.

## Os 3 Passos: Do Zero a um Agente com Tool Interna

1. **Passo 1 — Suba o servidor MCP expondo sua ferramenta** — **Escolha o transporte:** stdio para ferramentas locais (CLI, scripts, desenvolvimento), HTTP+SSE para ferramentas remotas (APIs internas, serviços em produção). Para produção na AWS, use HTTP+SSE atrás de um ALB ou via Bedrock AgentCore Gateway.

**Instale o SDK:** `pip install mcp` (Python) ou `npm install @modelcontextprotocol/sdk` (Node).

**Implemente o servidor mínimo (Python):**
```python
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import json

app = Server("internal-kb-server")

@app.list_tools()
async def list_tools():
    return [
        Tool(
            name="search_internal_kb",
            description="Busca documentos na base de conhecimento interna.

2. **Passo 2 — Escreva descrições que o agente realmente usa** — **Checklist de descrição eficaz** — aplique para cada tool antes de publicar:

- [ ] **Caso de uso em linguagem natural:** começa com "Use quando..." ou "Retorna..."
- [ ] **Escopo negativo explícito:** "NÃO use quando..." — pelo menos um caso de exclusão
- [ ] **Diferenciação de tools similares:** se você tem `search_kb` e `search_tickets`, a descrição de cada uma precisa deixar claro a diferença
- [ ] **Parâmetros com descrição semântica:** cada campo do inputSchema tem `description` que explica o que colocar, não só o tipo
- [ ] **Sem jargão técnico desnecessário:** o modelo interpreta a descrição como linguagem natural; IDs internos e siglas sem contexto confundem
- [ ] **Comprimento:** 2-5 frases. Mais que isso dilui o sinal; menos que isso é insuficiente

3. **Passo 3 — Conecte o agente (cliente MCP) com segurança** — **Conecte o cliente ao servidor:**

```python
# Usando o SDK Python como cliente
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

server_params = StdioServerParameters(
    command="python",
    args=["server.py"],
    env={"KB_API_KEY": os.environ["KB_API_KEY"]}  # segredos via env, nunca hardcoded
)

async with stdio_client(server_params) as (read, write):
    async with ClientSession(read, write) as session:
        await session.initialize()
        tools = await session.list_tools()  # descobre as tools
        # passe tools para o LLM como parte do contexto
```

## Arquitetura MCP: Cliente ↔ Servidor ↔ Ferramentas (com camada de autorização)

Fluxo completo de uma chamada de tool via MCP em produção na AWS. O agente (cliente) descobre e chama tools sem conhecer a implementação interna. A autorização acontece na borda do servidor MCP, antes de qualquer execução.

### 👤 User / Application

- User request (user)
- Application (orchestrator) (frontend)

### 🤖 Agent Layer (MCP Client)

- LLM (Claude / Bedrock) (ai)
- MCP Client SDK (compute)

### 🔐 Authorization Edge

- Bedrock AgentCore Gateway / Auth Layer (security)
- IAM Policy + OAuth 2.1 (security)

### ⚙️ MCP Server

- MCP Server (tools/list, tools/call) (compute)
- Arg Validator + Rate Limiter (security)
- Audit Logger (CloudWatch) (data)

### 🛠️ Internal Tools

- Internal KB Search API (external)
- Internal DB (read-only) (data)
- Internal REST API (external)

### Fluxos

- user -> app: pergunta
- app -> llm: prompt + contexto
- llm -> mcp_client: tools/list (discovery)
- mcp_client -> gateway: tools/call + identity
- gateway -> iam: verifica permissão
- iam -> gateway: allow/deny
- gateway -> mcp_server: chamada autorizada
- mcp_server -> validator: valida args
- mcp_server -> logger: registra chamada
- validator -> tool_kb: executa
- validator -> tool_db: executa
- validator -> tool_api: executa
- tool_kb -> mcp_server: resultado
- tool_db -> mcp_server: resultado
- tool_api -> mcp_server: resultado
- mcp_server -> mcp_client: TextContent
- mcp_client -> llm: tool result
- llm -> app: resposta final

## Segurança: tool é poder — e poder precisa de controle

Uma tool MCP não é um endpoint de API qualquer. É uma capacidade que um modelo de linguagem pode acionar autonomamente, baseado em sua interpretação de uma instrução em linguagem natural. Isso muda o modelo de ameaça fundamentalmente.

Em uma API tradicional, o chamador é um sistema determinístico que você controla. Em um agente com tools MCP, o chamador é um modelo que interpreta linguagem natural — e linguagem natural pode ser manipulada. **Prompt injection** é o ataque mais direto: um documento malicioso na base de conhecimento contém instruções para o modelo chamar uma tool com argumentos específicos. Se a tool tem poder demais, o dano é real.

Três princípios que aplico em qualquer integração MCP antes de ir para produção:

**1. Mínimo privilégio no servidor, não no agente.** Não adianta restringir o agente se o servidor MCP tem acesso de escrita ao banco inteiro. A identidade que executa o servidor MCP deve ter apenas as permissões que as tools expostas precisam — e nada mais. Na AWS, isso significa uma role IAM com política inline específica, não `AdministratorAccess`.

**2. Allowlist explícita de tools por agente.** Um servidor MCP pode expor 20 tools. Um agente específico pode precisar de 3. Não exponha todas — configure o cliente para filtrar `tools/list` e apresentar ao modelo apenas as tools relevantes para o contexto. Isso reduz a superfície de ataque e melhora a qualidade das escolhas do modelo.

**3. Validação no servidor é mandatória — não opcional.** O JSON Schema no contrato MCP é uma descrição para o modelo, não uma barreira de segurança. O modelo pode gerar argumentos que passam no schema mas são semanticamente inválidos ou maliciosos (path traversal em um campo de arquivo, SQL injection em um campo de query). Valide no handler, antes de executar qualquer operação com side effects.

O Bedrock AgentCore Gateway adiciona uma camada de controle gerenciada: autenticação OAuth 2.1 ou IAM, logging centralizado, e a capacidade de revogar acesso a uma tool específica sem mudar o código do agente. Para ambientes corporativos com requisitos de auditoria, essa camada vale o custo adicional.

> **Anti-padrões que destroem integrações MCP em produção:** **1. Tool sprawl — um servidor com 30+ tools.**
O modelo recebe todas as tools no contexto. Cada tool consome tokens. Com muitas tools, o modelo fica confuso, a latência aumenta e o custo explode. Regra: se um servidor tem mais de 10-12 tools, divida por domínio. Agentes especializados com poucas tools performam melhor que agentes genéricos com muitas.

**2. Descrição vaga ou ausente.**
`"description": "tool for data"` é um bug silencioso. O agente vai chamar a tool errada, no momento errado, com argumentos errados — e você vai culpar o modelo. A descrição é código. Revise com o mesmo rigor.

**3. Privilégio excessivo no servidor.**
O erro mais perigoso: o servidor MCP roda com credenciais de admin porque "é mais fácil". Uma tool de busca não precisa de permissão de escrita. Uma tool de leitura de dados não precisa de acesso a outras contas AWS. Defina a role mínima antes de escrever a primeira linha de código da tool — não depois.

**4. Sem validação de argumentos antes de executar.**
O schema JSON é documentação para o modelo, não uma barreira de segurança. Sempre valide no handler. Especialmente: campos de texto livre que vão para queries, caminhos de arquivo, ou parâmetros que constroem comandos.

**5. Segredos como argumentos de tool.**
Se o modelo precisa passar uma API key como argumento para uma tool, o design está errado. Segredos ficam no ambiente do servidor MCP (Secrets Manager, variáveis de ambiente injetadas no deploy). O modelo nunca deve ver ou transmitir credenciais.

> **Regra de Bolso:** **"A descrição da tool é um prompt. O schema é uma barreira fraca. A autorização fica no servidor — nunca no agente."**

Se você só lembrar de três coisas do MCP: (1) escreva a descrição como se fosse instrução para o modelo, porque é; (2) valide os argumentos no servidor antes de executar qualquer coisa; (3) a identidade que roda o servidor MCP tem mínimo privilégio — ponto final.

> **Minha perspectiva — o que eu faço na prática:** O MCP resolve um problema real que eu vi se repetir em projetos de agentes: cada integração virava um adaptador artesanal, acoplado ao modelo, impossível de reusar. A ideia do protocolo padrão é sólida — e a execução da Anthropic é boa o suficiente para adotar.

O que eu faço diferente do tutorial básico:

**Começo pela descrição, não pelo código.** Antes de escrever uma linha de servidor MCP, escrevo a descrição da tool em linguagem natural e peço para um colega (ou para o próprio modelo) me dizer em que situação ele chamaria essa tool. Se a resposta não bate com o que eu quero, a descrição está errada — e é mais fácil corrigir texto do que código.

**Trato o servidor MCP como um microserviço de produção desde o primeiro dia.** Logging, health check, timeout, rate limiting, mínimo privilégio. Não como um script de integração que "depois a gente melhora". Tools em produção são superfície de ataque — e em sistemas financeiros, superfície de ataque é risco regulatório.

**Na AWS, uso o Bedrock AgentCore Gateway para qualquer coisa que vai para produção.** Gerenciar autenticação OAuth 2.1 na mão em um servidor MCP remoto é trabalho que o Gateway já faz. O custo de não ter logging centralizado de chamadas de tool em um ambiente auditável é muito maior que o custo do serviço.

**Limito o número de tools por agente agressivamente.** Minha heurística pessoal: se um agente tem mais de 8 tools, eu questiono o design. Provavelmente é um agente fazendo trabalho de dois ou três agentes especializados. Agentes com escopo estreito e tools bem descritas são mais confiáveis, mais baratos e mais fáceis de auditar.

O MCP ainda é jovem — a spec está evoluindo, o ecossistema de servidores está explodindo (bom e ruim). Minha recomendação: adote para ferramentas internas onde você controla o servidor; seja criterioso com servidores MCP de terceiros (você está dando ao modelo acesso a sistemas externos — avalie o risco).

## Veredicto

O MCP não é hype de framework — é um contrato de integração que resolve o problema de acoplamento entre agentes e ferramentas de forma limpa e agnóstica de modelo. O valor real não está no protocolo em si, mas no que ele habilita: você pode trocar o LLM, adicionar uma ferramenta nova ou revogar acesso a uma tool sem tocar no código do agente. Isso é a diferença entre uma integração que escala e uma que vira dívida técnica.

Mas o protocolo não resolve segurança por você. Tool é poder. Poder sem controle em um sistema dirigido por linguagem natural é risco. Mínimo privilégio, validação de argumentos e logging de auditoria não são opcionais — são o preço de colocar um agente em produção.

A regra final: **se a descrição da sua tool não explica claramente quando usá-la E quando não usá-la, ela não está pronta para produção.** O agente vai decidir baseado nesse texto. Trate-o como código.

## Referências

- [Anthropic — Model Context Protocol (Specification & Docs)](https://modelcontextprotocol.io/)
- [Anthropic — Introducing the Model Context Protocol](https://www.anthropic.com/news/model-context-protocol)
- [AWS — Bedrock AgentCore Gateway (Tools via MCP)](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway.html)

## Fontes do caso

- [Anthropic — Model Context Protocol](https://modelcontextprotocol.io/)
- [Anthropic — Introducing the MCP](https://www.anthropic.com/news/model-context-protocol)
- [AWS — Bedrock AgentCore Gateway (tools via MCP)](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway.html)
