Guia de Referência

Model Context Protocol
& FastMCP

Tudo que você precisa saber: o que é, o que você pode fazer, como consumir, como deployar e onde estão os limites.

O que é MCP

MCP (Model Context Protocol) é um protocolo aberto criado pela Anthropic que padroniza como LLMs se conectam a ferramentas, dados e serviços externos. Pense como um "USB-C para IA": qualquer cliente MCP (Claude, ChatGPT, Cursor, Gemini, seu próprio agente) consegue falar com qualquer servidor MCP sem integrações customizadas.

Por que importa Sem MCP, cada integração precisa de código específico: um plugin pra Claude, outro pra ChatGPT, outro pra seu agente LangGraph. Com MCP, você escreve uma vez e qualquer cliente compatível consome.
LLM / Agente
⟷
MCP Client
⟷
MCP Server
⟷
Seus sistemas

FastMCP

FastMCP é o framework Python padrão de facto para construir servidores MCP. FastMCP 1.0 foi incorporado diretamente no SDK oficial da Anthropic. A versão standalone atual (pip install fastmcp) é baixada ~1 milhão de vezes por dia e alimenta ~70% dos servidores MCP em todos os idiomas. Atualmente na versão 3.x.

Os 3 Primitivos do MCP

Todo servidor MCP expõe alguma combinação de três tipos de componentes:

⚙️ Tools

Funções invocáveis. O LLM decide quando chamar. Fazem ações, buscam dados ao vivo, chamam APIs.

📄 Resources

Fontes de dados passivas acessadas por URI. O cliente lê quando precisa de contexto.

💬 Prompts

Templates de mensagens reutilizáveis que guiam interações do LLM de forma padronizada.

Transportes

MCP suporta três mecanismos de comunicação:

TransporteQuando usarCaracterísticas
STDIO Ferramentas locais, CLI, Claude Desktop Processo filho, sem rede, padrão para desktop clients
Streamable HTTP Deploy web, múltiplos clientes, produção HTTP/streaming, suporta auth, CORS, load balancers. Padrão atual.
SSE Legado Deprecado, ainda suportado por compatibilidade
python
# STDIO (default) — local
mcp.run()

# HTTP — remoto
mcp.run(transport="http", host="0.0.0.0", port=8000)

# ASGI app (para Uvicorn/Gunicorn)
app = mcp.http_app()
# uvicorn app:app --host 0.0.0.0 --port 8000

Tools Server

Tools são o primitivo mais importante — funções Python decoradas que o LLM pode invocar. FastMCP gera automaticamente o JSON Schema, valida inputs e trata erros.

Definição básica

python
from fastmcp import FastMCP

mcp = FastMCP("MeuServidor")

@mcp.tool
def buscar_cliente(cliente_id: str, incluir_historico: bool = False) -> dict:
    """Busca dados de um cliente pelo ID.

    Args:
        cliente_id: ID único do cliente
        incluir_historico: Se True, inclui histórico de pedidos
    """
    # sua lógica aqui
    return {"id": cliente_id, "nome": "João"}
Dica O docstring vira a descrição da tool para o LLM. Seja descritivo — é o que o modelo usa para decidir quando e como chamar a função.

Tool assíncrona + Context

python
from fastmcp import FastMCP, Context

@mcp.tool
async def processar_lote(itens: list[str], ctx: Context) -> str:
    """Processa uma lista de itens com progresso reportado."""
    for i, item in enumerate(itens):
        await ctx.report_progress(i, len(itens), f"Processando {item}")
        await ctx.info(f"Item {item} processado")
        # ... lógica ...
    return "Concluído"

Tipos suportados

FastMCP suporta qualquer tipo Python com suporte a Pydantic: str, int, float, bool, list, dict, datetime, modelos Pydantic, Literal, Union, Optional, Enum etc.

python
from pydantic import BaseModel
from enum import Enum

class Status(Enum):
    ATIVO = "ativo"
    INATIVO = "inativo"

class FiltroCliente(BaseModel):
    status: Status
    limite: int = 10

@mcp.tool
def listar_clientes(filtro: FiltroCliente) -> list[dict]:
    """Lista clientes com filtro estruturado."""
    pass

Tags e controle de exposição

python
# Exponha ou oculte tools por tags
@mcp.tool(tags={"public", "read-only"})
def tool_publica() -> str: ...

@mcp.tool(tags={"admin"})
def tool_admin() -> str: ...

# No servidor: só expõe tags "public"
mcp.enable(tags={"public"}, only=True)

Resources Server

Resources são fontes de dados identificadas por URI. O cliente decide quando ler — eles não são invocados pelo LLM como tools, mas fornecem contexto sob demanda.

Resource estático

python
@mcp.resource("config://app")
def get_config() -> dict:
    """Configuração atual da aplicação."""
    return {"versao": "1.0", "ambiente": "prod"}

Resource Template (URI dinâmico)

python
@mcp.resource("usuario://{user_id}/perfil")
async def get_perfil(user_id: str) -> str:
    """Perfil de um usuário específico."""
    dados = await buscar_usuario(user_id)
    return json.dumps(dados)

# Cliente acessa: usuario://abc123/perfil

Resource com conteúdo binário

python
from fastmcp import Image

@mcp.resource("relatorio://{mes}/grafico")
async def get_grafico(mes: str) -> Image:
    img_bytes = await gerar_grafico(mes)
    return Image(data=img_bytes, format="png")

Prompts Server

Prompts são templates reutilizáveis de mensagens para guiar interações do LLM de forma padronizada.

python
@mcp.prompt
def analisar_dados(pontos: list[float], contexto: str = "") -> str:
    """Template para análise de dados numéricos."""
    dados_fmt = ", ".join(str(p) for p in pontos)
    return f"""Analise estes dados: {dados_fmt}

Contexto adicional: {contexto}

Por favor identifique tendências, outliers e padrões relevantes."""

Context API Server

O parâmetro ctx: Context em qualquer tool ou resource dá acesso a capacidades do servidor durante execução:

MétodoO que faz
ctx.info(msg)Loga mensagem nível info para o cliente
ctx.debug(msg)Loga mensagem de debug
ctx.warning(msg)Loga aviso
ctx.report_progress(n, total, msg)Reporta progresso de operação longa
ctx.read_resource(uri)Lê um resource de dentro de uma tool
ctx.sample(...)Pede ao cliente LLM para gerar texto (sampling)
ctx.client_idIdentifica o cliente conectado
ctx.request_idID único da requisição atual
python
@mcp.tool
async def resumir_doc(uri: str, ctx: Context) -> str:
    # Lê resource
    conteudo = await ctx.read_resource(uri)

    # Pede ao LLM para resumir (sampling)
    resumo = await ctx.sample(
        f"Resuma em 3 bullets:\n{conteudo[0].text}",
        max_tokens=300
    )
    return resumo.text

Lifespan Server

Lifespan gerencia recursos compartilhados que precisam existir durante toda a vida do servidor (conexões DB, pools HTTP, caches):

python
from contextlib import asynccontextmanager
from fastmcp import FastMCP

@asynccontextmanager
async def lifespan(server):
    # Setup: roda quando servidor inicia
    db = await Database.connect("postgresql://...")
    redis = await Redis.create(...)

    yield {"db": db, "redis": redis}  # disponível via ctx.lifespan_context

    # Teardown: roda quando servidor para
    await db.disconnect()
    await redis.close()

mcp = FastMCP("MeuApp", lifespan=lifespan)

@mcp.tool
async def buscar(query: str, ctx: Context) -> list:
    db = ctx.lifespan_context["db"]
    return await db.fetch(query)

Composição de Servidores Advanced

FastMCP permite montar múltiplos servidores como sub-servidores, criando hierarquias e namespacing:

python
# Servidor de banco de dados
db_mcp = FastMCP("Database")

@db_mcp.tool
def query(sql: str) -> list: ...

# Servidor de arquivos
files_mcp = FastMCP("Files")

@files_mcp.tool
def ler_arquivo(path: str) -> str: ...

# Servidor principal que compõe os dois
main = FastMCP("Principal")
main.mount(db_mcp, prefix="db")
main.mount(files_mcp, prefix="files")

# Resultado: tools "db_query" e "files_ler_arquivo"

OpenAPI → MCP Server

python
from fastmcp import FastMCP

# Gera servidor MCP a partir de qualquer OpenAPI spec
mcp = FastMCP.from_openapi(
    openapi_url="https://api.meuservico.com/openapi.json",
    client=httpx.AsyncClient(base_url="https://api.meuservico.com")
)
# Cada endpoint vira uma tool automaticamente!

FastMCP Client Client

O Client é a interface programática para consumir qualquer servidor MCP — ideal para testes, agentes determinísticos e aplicações que precisam controlar chamadas MCP diretamente.

Conectando a servidores

python
import asyncio
from fastmcp import Client

# Em memória (mesmo processo — ideal para testes)
client = Client(meu_servidor)

# HTTP remoto
client = Client("https://mcp.meuservico.com/mcp")

# Script local (lança como subprocess via STDIO)
client = Client("./servidor.py", env={"API_KEY": "..."})

# Multi-servidor via config
client = Client({
    "mcpServers": {
        "db": {"url": "https://db.exemplo.com/mcp"},
        "files": {"command": "python", "args": ["./files.py"]}
    }
})

Operações disponíveis

python
async with Client("https://mcp.exemplo.com/mcp") as client:
    # Listar
    tools     = await client.list_tools()
    resources = await client.list_resources()
    prompts   = await client.list_prompts()

    # Chamar tool
    resultado = await client.call_tool("buscar_cliente", {"cliente_id": "123"})
    print(resultado.data)

    # Ler resource
    conteudo = await client.read_resource("usuario://123/perfil")
    print(conteudo[0].text)

    # Renderizar prompt
    msgs = await client.get_prompt("analisar_dados", {"pontos": [1, 2, 3]})

    # Ping
    await client.ping()

Integração com LangChain/LangGraph

python
from langchain_mcp_adapters.client import MultiServerMCPClient

async with MultiServerMCPClient({
    "agente_especialista": {
        "url": "https://agent.interno/mcp",
        "transport": "streamable_http",
        "headers": {"Authorization": f"Bearer {token}"}
    }
}) as client:
    tools = client.get_tools()  # LangChain BaseTool list
    agent = create_react_agent(llm, tools)

Callbacks do Client Client

python
from fastmcp import Client

async def on_log(msg):
    print(f"[SERVER] {msg.level}: {msg.data}")

async def on_progress(progress, total, msg):
    pct = int(progress / total * 100) if total else "?"
    print(f"[PROGRESSO] {pct}% — {msg}")

client = Client(
    "https://mcp.exemplo.com/mcp",
    log_handler=on_log,
    progress_handler=on_progress,
    timeout=60.0
)
CallbackQuando dispara
log_handlerServer envia mensagem de log via ctx.info/debug/warning
progress_handlerServer chama ctx.report_progress()
sampling_handlerServer pede ao cliente para gerar texto LLM
elicitation_handlerServer pede input interativo do usuário

Sampling Advanced

Sampling permite que o servidor peça ao LLM do cliente para gerar texto — inversão do fluxo normal. Útil para agentes que precisam de raciocínio intermediário sem expor credenciais do LLM no servidor.

python
# No SERVIDOR: pede ao cliente para gerar texto
@mcp.tool
async def classificar(texto: str, ctx: Context) -> str:
    resposta = await ctx.sample(
        f"Classifique este texto em: positivo/negativo/neutro.\nTexto: {texto}",
        max_tokens=50
    )
    return resposta.text

# No CLIENTE: provê o handler de sampling
async def sampling_handler(messages, params, context):
    response = await llm.ainvoke(messages)
    return response.content

client = Client(mcp, sampling_handler=sampling_handler)

Deploy HTTP Produção

Standalone

python — server.py
# server.py
from fastmcp import FastMCP

mcp = FastMCP("MeuServidor")

@mcp.custom_route("/health", methods=["GET"])
async def health(request):
    from starlette.responses import JSONResponse
    return JSONResponse({"status": "ok"})

if __name__ == "__main__":
    mcp.run(transport="http", host="0.0.0.0", port=8000)
# Endpoint MCP: http://host:8000/mcp

ASGI (produção com Uvicorn/ECS)

python — app.py
# app.py
app = mcp.http_app(path="/mcp")

# Dockerfile / ECS task
# uvicorn app:app --host 0.0.0.0 --port 8000 --workers 4

Montado em FastAPI

python
from fastapi import FastAPI
from fastmcp import FastMCP

api = FastAPI()
mcp = FastMCP("MeuMCP")

# Suas rotas REST normais
@api.get("/usuarios")
async def usuarios(): ...

# MCP montado em /mcp
mcp_app = mcp.http_app()
api.mount("/mcp", mcp_app)
# uvicorn main:api

Autenticação Produção

FastMCP suporta múltiplos mecanismos de autenticação para servidores HTTP. Relevante para sua arquitetura com JWT/Bearer:

Bearer Token (JWT via IdP externo — Okta/Auth0)

python
from fastmcp import FastMCP
from fastmcp.server.auth import BearerAuthProvider

# Valida JWTs emitidos pelo seu IdP via JWKS
auth = BearerAuthProvider(
    jwks_uri="https://seu-idp.com/.well-known/jwks.json",
    audience="https://sua-api.com",
    issuer="https://seu-idp.com/"
)

mcp = FastMCP("SecureServer", auth=auth)

# Acessar claims do token dentro das tools:
@mcp.tool
async def minha_tool(ctx: Context) -> str:
    token = ctx.get_access_token()
    user_id = token.claims.get("sub")
    return f"Olá, {user_id}"
Para sua arquitetura Isso é exatamente o padrão que você já usa no API Gateway com JWT Authorizer. O FastMCP faz a mesma validação via JWKS — você pode usar o mesmo token Bearer que o API Gateway valida.

OAuth 2.1 completo

FastMCP tem integrações prontas para: Auth0, Okta, AWS Cognito, Azure Entra ID, Google, GitHub, Discord, Keycloak, Supabase, e mais. Cada uma tem uma página de docs em gofastmcp.com/integrations/.

Client com Bearer

python
from fastmcp import Client
from fastmcp.client.auth import BearerAuth

client = Client(
    "https://mcp.meuservico.com/mcp",
    auth=BearerAuth(token="eyJ...")
)

Middleware Server

Middleware intercepta toda mensagem MCP (requests, responses, notifications) — ideal para logging, rate limiting, tracing:

python
from fastmcp import FastMCP
from fastmcp.server.middleware import Middleware, MiddlewareContext

class LoggingMiddleware(Middleware):
    async def on_request(self, ctx: MiddlewareContext, call_next):
        print(f"→ {ctx.method} | client={ctx.client_id}")
        response = await call_next(ctx)
        print(f"← {ctx.method} ok")
        return response

mcp = FastMCP("App", middleware=[LoggingMiddleware()])

# CORS para HTTP (necessário apenas para clientes browser)
from starlette.middleware.cors import CORSMiddleware
from starlette.middleware import Middleware as StarletteMiddleware

app = mcp.http_app(middleware=[
    StarletteMiddleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"],
        allow_headers=["mcp-protocol-version", "mcp-session-id", "Authorization"],
        expose_headers=["mcp-session-id"])
])

Background Tasks v2.14+

Operações longas podem rodar assincronamente enquanto o cliente faz polling do resultado — evita timeout de load balancers:

python
# Habilitar no servidor
mcp = FastMCP("App", tasks=True)

@mcp.tool
async def processar_video(url: str, task=True) -> str:
    """Processa vídeo de forma assíncrona."""
    # Retorna imediatamente, roda em background
    resultado = await download_e_processar(url)
    return resultado

# Cliente: chama e observa progresso
async with client:
    task = await client.call_tool("processar_video", {"url": "..."})
    resultado = await client.wait_for_task(task.task_id)

OpenAPI → MCP Integração

FastMCP pode gerar um servidor MCP completo a partir de qualquer OpenAPI spec — cada endpoint vira uma tool automaticamente:

python
import httpx
from fastmcp import FastMCP

# A partir de URL
mcp = FastMCP.from_openapi(
    openapi_url="https://api.exemplo.com/openapi.json",
    client=httpx.AsyncClient(
        base_url="https://api.exemplo.com",
        headers={"Authorization": "Bearer ..."}
    ),
    name="ExemploAPI"
)

# A partir de dict/arquivo local
with open("openapi.json") as f:
    spec = json.load(f)

mcp = FastMCP.from_openapi(openapi=spec, client=httpx.AsyncClient(...))
Caso de uso Expor suas APIs REST internas como tools MCP sem reescrever nada. Útil para o seu BFF MCP Server que chama sistemas externos.

Limites e Considerações

⚠️ Sem estado por padrão

MCP servers HTTP são stateless entre requisições. Use session_state_store ou Redis externo para estado de sessão.

⚠️ Timeout de LB

Tools lentas podem causar timeout em ALBs (padrão 60s). Use Background Tasks ou aumente o idle timeout no ALB.

⚠️ Schema de tools

O LLM vê o JSON Schema das tools. Inputs muito complexos (schemas aninhados profundos) podem confundir o modelo. Prefira tipos simples ou Pydantic models bem documentados.

⚠️ Erro handling

Em produção, use mask_error_details=True para não vazar detalhes internos de erro para o LLM/cliente.

⚠️ CORS

Só necessário se o cliente MCP roda no browser. Claude Desktop, agentes LangGraph e API calls não precisam de CORS.

⚠️ SSE deprecado

Transport SSE está deprecado. Use Streamable HTTP (transport="http") para novos deployments.

Limites de tamanho

ItemLimite / Comportamento
Tamanho de payloadDefinido pelo servidor HTTP (padrão Uvicorn: sem limite hard)
Tools por servidorSem limite técnico, mas muitas tools confundem LLMs. Use list_page_size para paginar.
Timeout de toolConfigurável no Client(timeout=...). Default: 30s
Resources bináriosSuporte nativo a bytes/images. Limite de memória da instância.
Sessões simultâneasLimitado pelo número de workers (Uvicorn/Gunicorn)

Clientes MCP compatíveis

Qualquer servidor FastMCP é automaticamente compatível com:

LLM Hosts

Claude Desktop, Claude Code, ChatGPT, Gemini CLI, Cursor, Goose

SDKs

Anthropic API, OpenAI API, Gemini SDK, Pydantic AI

Agentes

LangChain/LangGraph (via langchain-mcp-adapters), Pydantic AI, qualquer cliente MCP custom

Na sua arquitetura multi-agente AWS

Mapeando o MCP para os três accounts que você opera:

Account / ComponentePapel MCPPadrão recomendado
Supervisor Account
LangGraph + Redis
MCP Client Usa MultiServerMCPClient do LangChain para chamar specialists como tools. O StateGraph gerencia o fluxo.
Agents Account
ECS + Specialist Agents
MCP Server Cada specialist é um FastMCP rodando como ASGI no ECS. Auth via BearerAuthProvider + JWKS do seu IdP. Exposto via ALB interno.
BFFs Account
External Actions
MCP Server FastMCP com from_openapi() wrappando APIs externas, ou tools custom para integrações específicas.

Padrão de auth cross-account

python
# Supervisor → Specialist (via Bearer JWT do IdP)
from langchain_mcp_adapters.client import MultiServerMCPClient

# Token obtido do IdP (Okta/Auth0) com escopo do agente
token = await get_agent_token(scope="specialist:financeiro")

client = MultiServerMCPClient({
    "financeiro": {
        "url": "https://financeiro-agent.interno/mcp",
        "transport": "streamable_http",
        "headers": {"Authorization": f"Bearer {token}"}
    }
})

# No specialist server: valida o JWT
auth = BearerAuthProvider(
    jwks_uri="https://seu-idp.com/.well-known/jwks.json",
    audience="specialist:financeiro"
)
mcp = FastMCP("Financeiro", auth=auth)
Dica para sua stack Use o Lifespan do FastMCP para inicializar a conexão com o OpenSearch Knowledge Base do specialist. O contexto fica disponível em todas as tools via ctx.lifespan_context — evita reconectar a cada chamada.

CLI úteis para dev e debug

bash
# Instalar servidor no Claude Desktop/Code
fastmcp install servidor.py

# Inspecionar tools/resources de um servidor
fastmcp inspect servidor.py

# Chamar tool direto do terminal
fastmcp call servidor.py buscar_cliente --cliente_id=123

# Gerar CLI tipado a partir de um servidor MCP
fastmcp generate-cli servidor.py -o cli_tool.py