Software · Arquitetura & Engenharia de Dados

Model Context Protocol (MCP) por Dentro: Host, Client, Server e JSON-RPC

Como o protocolo aberto da Anthropic padroniza a exposição de ferramentas, recursos e prompts para modelos de linguagem: ciclo de vida de conexões, transporte, negociação de capacidades e segurança de execução.

Diagrama de arquitetura do Model Context Protocol exibindo o fluxo de mensagens JSON-RPC entre aplicação host, cliente MCP e servidores locais ou remotos
Neste artigo

Antes do surgimento de padrões de comunicação dedicados, conectar modelos de linguagem a bancos de dados, terminais e APIs corporativas dependia de integrações proprietárias. Cada fornecedor de agentes definia seu próprio formato de function calling, obrigando equipes de engenharia a reescrever código de integração sempre que alteravam a ferramenta de execução ou o modelo subjacente.

O Model Context Protocol (MCP), anunciado pela Anthropic e adotado rapidamente por ecossistemas abertos de agentes, resolve essa fragmentação através de um contrato cliente-servidor padronizado. Inspirado na simplicidade que o Language Server Protocol (LSP) trouxe para compiladores e editores de código, o MCP separa a responsabilidade de quem orquestra o raciocínio da responsabilidade de quem fornece o acesso físico aos recursos do ambiente.

flowchart LR
    subgraph HostApp["Aplicação Host (Claude Desktop, IDE, CLI)"]
        LLM["Modelo / Agente"]
        Client["Cliente MCP"]
        LLM <--> Client
    end

    subgraph Transports["Camada de Transporte"]
        Stdio["Stdio (Processo Local)"]
        SSE["Streamable HTTP / SSE (Remoto)"]
    end

    subgraph MCPServers["Servidores MCP"]
        SrvGit["MCP Git / Filesystem"]
        SrvDB["MCP Postgres / SQLite"]
        SrvAPI["MCP REST / GraphQL"]
    end

    Client <--> Stdio <--> SrvGit
    Client <--> Stdio <--> SrvDB
    Client <--> SSE <--> SrvAPI

A Topologia de Três Camadas

A arquitetura do MCP divide o ecossistema em três participantes com fronteiras de isolamento bem delimitadas:

  1. Host: A aplicação de interface direta com o usuário final (como uma IDE, um terminal interativo ou o Claude Desktop). O Host gerencia o ciclo de vida do modelo de IA, define políticas de segurança e autoriza ou bloqueia cada ação solicitada.
  2. Client: A biblioteca dentro do Host que mantém a conexão 1:1 com cada servidor. O cliente descobre capacidades, formata as requisições em conformidade com a especificação e injeta o contexto recebido no prompt do modelo.
  3. Server: Um processo leve (executado localmente via subprocesso ou remotamente via HTTP) que expõe ferramentas executáveis, recursos de leitura ou instruções prontas de prompt.
Primitiva MCP Direção Finalidade Principal Exemplo Prático
Tools Modelo → Servidor Funções executáveis com efeitos colaterais controlados Rodar um comando git, executar uma query SQL, disparar um deploy
Resources Servidor → Modelo Dados contextuais passivos, lidos sob demanda como arquivos Ler o conteúdo de um arquivo .env, inspecionar o schema de tabelas
Prompts Servidor → Modelo Modelos reutilizáveis de instrução pré-empacotados Template de revisão de código, roteiro de depuração de logs
Sampling Servidor → Host Servidor requisita que o Host faça uma inferência via LLM Servidor de banco pedindo resumo de telemetria sem expor credenciais

O Protocolo no Fio: JSON-RPC 2.0

Toda a troca de mensagens ocorre em JSON-RPC 2.0. Esse formato garante que chamadas de métodos, retornos síncronos e notificações assíncronas trafeguem sem a necessidade de parsing binário proprietário.

1. Inicialização e Negociação de Capacidades

Quando o Host inicia um servidor, o primeiro passo é o aperto de mão (handshake) para registrar versões e recursos suportados:

// Requisição enviada pelo Cliente
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "roots": { "listChanged": true },
      "sampling": {}
    },
    "clientInfo": {
      "name": "DATAXSTUDIOS-AgentRunner",
      "version": "1.0.0"
    }
  }
}

O servidor responde confirmando a versão aceita e listando quais primitivas ele oferece:

// Resposta do Servidor
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "tools": { "listChanged": true },
      "resources": { "subscribe": true }
    },
    "serverInfo": {
      "name": "dxs-database-inspector",
      "version": "0.4.2"
    }
  }
}

Após o handshake, o cliente envia uma notificação notifications/initialized, liberando o fluxo de dados.

2. Descoberta e Execução de Ferramentas

O modelo precisa conhecer os schemas de validação antes de invocar qualquer ação. O cliente envia tools/list e recebe um array de definições no padrão JSON Schema:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "execute_query",
    "arguments": {
      "sql": "SELECT id, status, total_amount FROM orders WHERE created_at >= NOW() - INTERVAL '24 hours'",
      "readOnly": true
    }
  }
}

Caso o Host tenha políticas de aprovação humana, a interface pausa a execução neste momento e solicita consentimento antes de entregar o payload ao subprocesso do servidor.

Mecanismos de Transporte: Stdio vs Streamable HTTP

A especificação contempla dois modos principais de transporte físico:

sequenceDiagram
    participant H as Host / Cliente
    participant S as Servidor MCP (Processo Filho)
    
    Note over H,S: Transporte via Standard Streams (stdio)
    H->>S: Spawna processo filho stdin/stdout
    H->>S: Envia initialize via stdin
    S-->>H: Responde resultado via stdout
    H->>S: Envia tools/call com argumentos
    S-->>H: Retorna conteúdo formatado em stdout
  • Stdio (Standard Input / Output): Ideal para servidores locais. O Host inicia o processo executável como filho e comunica-se enviando strings JSON terminadas por quebra de linha em stdin e lendo as respostas em stdout. Registros de log devem ser direcionados para stderr para não corromper o canal de dados.
  • Streamable HTTP / SSE: Utilizado quando o servidor MCP reside em outro nó da rede ou dentro de um cluster. As requisições trafegam por POST HTTP padrão, enquanto notificações em tempo real e respostas longas utilizam Server-Sent Events (SSE).

Construindo um Servidor Prático com TypeScript

Com o SDK oficial @modelcontextprotocol/sdk, implementar um servidor que expõe métricas de disco e execução de scripts exige poucas linhas:

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { execSync } from "node:child_process";

const server = new Server(
  { name: "dxs-system-ops", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

// Registra as ferramentas disponíveis
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "get_disk_usage",
      description: "Retorna a porcentagem de espaço ocupado em disco.",
      inputSchema: {
        type: "object",
        properties: {
          path: { type: "string", description: "Ponto de montagem" }
        },
        required: ["path"]
      }
    }
  ]
}));

// Executa a chamada validando os argumentos
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "get_disk_usage") {
    const targetPath = String(request.params.arguments?.path ?? "/");
    const output = execSync(`df -h "${targetPath}"`).toString("utf8");
    
    return {
      content: [
        { type: "text", text: output.trim() }
      ]
    };
  }
  throw new Error(`Ferramenta não reconhecida: ${request.params.name}`);
});

// Conecta ao canal de entrada e saída padrão
const transport = new StdioServerTransport();
await server.connect(transport);

Implicações de Segurança e Fronteiras de Confiança

Como os servidores MCP operam com permissões no sistema operacional, a adoção do protocolo exige três salvaguardas essenciais:

  1. Princípio do Menor Privilégio: Servidores locais devem rodar com credenciais restritas, sem privilégios administrativos. Ferramentas que gravam arquivos devem validar caminhos para prevenir ataques de path traversal.
  2. Confirmação Humana Explícita: Ferramentas com efeitos irreversíveis (como envio de e-mails, mutações em bancos de produção ou destruição de containers) nunca devem ser acionadas de forma 100% autônoma pelo cliente.
  3. Resistência a Prompt Injection Indireto: Ao ler recursos externos via resources/read, dados não confiáveis (como páginas HTML ou repositórios públicos) podem conter instruções maliciosas destinadas a manipular o comportamento do modelo.
Explorar este tema no Grafo de Conhecimento