O que é MCP (Model Context Protocol)? Explicado para devs Java

1. Visão geral

MCP (Model Context Protocol) é um padrão aberto que permite aos LLMs (modelos de linguagem grandes) se conectarem com dados, ferramentas e sistemas externos de forma unificada, em vez de cada aplicação de IA ter que escrever integrações separadas para cada fonte de dados. Este artigo explica o que é MCP, como é sua arquitetura e mostra como escrever um servidor MCP simples com Spring Boot (através do Spring AI) para expor uma ferramenta que o LLM possa chamar. Após ler, você entenderá quando usar MCP e quando é apenas over-engineering.

2. O problema que MCP resolve

Antes do MCP, cada aplicação de IA (chatbot, agente) que quisesse acessar database, sistema de arquivos, APIs internas… tinha que escrever um adaptador próprio. Se você tivesse M aplicações de IA e N fontes de dados, precisaria de até M × N códigos de integração diferentes.

MCP padroniza essa camada de comunicação: uma aplicação de IA (chamada de Host) só precisa falar o padrão MCP uma vez, e qualquer sistema que exponha um servidor MCP consegue “conversar” com ela. É como USB-C para dispositivos eletrônicos — um padrão de conector único no lugar de dezenas de tipos de porta diferentes.

Nota: MCP não é um framework para substituir REST ou gRPC para comunicação service-to-service comum. Ele foi criado para LLMs/agentes chamarem ferramentas, não para dois microserviços backend se comunicarem entre si.

3. Arquitetura MCP

MCP tem 3 papéis principais:

  • Host: aplicação que contém o LLM (por exemplo, Claude Desktop, um agente que você escreve).
  • Client: componente dentro do Host que mantém uma conexão 1-1 com um servidor MCP.
  • Server: processo que expõe capabilities (ferramentas, recursos, prompts) de acordo com o padrão MCP.
┌──────────────┐        ┌──────────────┐        ┌──────────────────┐
│     Host     │  1-1   │  MCP Client  │  JSON-RPC  │   MCP Server   │
│  (chứa LLM)  │◄──────►│              │◄──────────►│ (DB, API, file) │
└──────────────┘        └──────────────┘        └──────────────────┘

A comunicação entre cliente e servidor usa JSON-RPC 2.0, transmitida por um dos dois transportes mais comuns:

  • stdio: o servidor roda como um processo filho, cliente/servidor se comunicam via stdin/stdout. Apropriado quando o servidor roda localmente.
  • SSE/HTTP (Streamable HTTP): usado quando o servidor roda remotamente, pela rede.

3.1. Três tipos de capability que um servidor MCP pode expor

Capability Significado Exemplo
Tools Funções que o LLM pode chamar para executar ações getWeather(city), createOrder(...)
Resources Dados que o LLM pode ler (como GET) Arquivo de log, registro de database
Prompts Template de prompt predefinido pelo servidor “Resumir pedido de acordo com o padrão X”

A maioria dos casos de uso práticos giram em torno de Tools, então os exemplos abaixo se concentram neles.

4. Escrevendo um servidor MCP com Spring AI

Spring AI (a partir da versão 1.0.0-M6) suporta MCP através de um starter dedicado, permitindo que você exponha um bean Java como uma ferramenta MCP usando annotations, sem precisar implementar JSON-RPC manualmente.

4.1. Adicione a dependência

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
    <version>1.0.0-M6</version>
</dependency>

4.2. Defina a ferramenta

@Service
public class WeatherService {

    @Tool(description = "Obtenha a temperatura atual de uma cidade")
    public String getTemperature(String city) {
        return switch (city.toLowerCase()) {
            case "ha noi" -> "28°C, nublado";
            case "sai gon" -> "32°C, ensolarado";
            default -> "Sem dados para a cidade: " + city;
        };
    }
}
  • @Tool marca este método como uma ferramenta MCP. O atributo description é muito importante — o LLM usa esta descrição para decidir se deve chamar a ferramenta e com quais parâmetros.
  • getTemperature aceita uma String city e retorna uma string descrevendo o tempo. Os parâmetros e o tipo de retorno têm seu JSON schema gerado automaticamente pelo servidor MCP para que o LLM entenda.

4.3. Registre a ferramenta no servidor MCP

@Configuration
public class McpServerConfig {

    @Bean
    public ToolCallbackProvider weatherTools(WeatherService weatherService) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(weatherService)
                .build();
    }
}

MethodToolCallbackProvider verifica os métodos com @Tool em weatherService e os registra automaticamente no servidor MCP quando o contexto da aplicação inicia.

4.4. Configure o servidor

spring.ai.mcp.server.name=weather-mcp-server
spring.ai.mcp.server.version=1.0.0
spring.ai.mcp.server.stdio=true

Com stdio=true, o servidor roda como um processo independente, comunicando-se via stdin/stdout — apropriado para teste rápido com Claude Desktop ou um cliente MCP que você escreve. Se quiser fazer deploy do servidor como um serviço independente rodando remotamente, você troca para o transporte SSE e o expõe via um endpoint HTTP em vez de stdio.

Inicie o servidor:

mvn spring-boot:run
Started WeatherMcpApplication in 1.124 seconds
Registered MCP tool: getTemperature

5. Chamando o servidor MCP do cliente

Suponha que você está escrevendo uma aplicação Host (por exemplo, um agente interno) que precisa chamar a ferramenta getTemperature acima.

5.1. Dependência do cliente

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
    <version>1.0.0-M6</version>
</dependency>

5.2. Chame a ferramenta

@Component
public class WeatherAgent {

    private final McpSyncClient mcpClient;

    public WeatherAgent(McpSyncClient mcpClient) {
        this.mcpClient = mcpClient;
    }

    public String askTemperature(String city) {
        CallToolRequest request = new CallToolRequest(
                "getTemperature",
                Map.of("city", city));

        CallToolResult result = mcpClient.callTool(request);
        return result.content().get(0).text();
    }
}
  • McpSyncClient é um cliente Spring AI que cria automaticamente um bean com base na configuração do transporte (stdio ou SSE) declarada em application.yml.
  • CallToolRequest encapsula o nome da ferramenta (getTemperature) e os parâmetros (city) em uma chamada JSON-RPC.
  • result.content() retorna uma lista de conteúdos da resposta; aqui pegamos a primeira parte text.

Configuração do cliente (por exemplo, conectando ao servidor via stdio executando o jar do servidor como subprocess):

spring:
  ai:
    mcp:
      client:
        stdio:
          connections:
            weather-server:
              command: java
              args: -jar weather-mcp-server.jar

6. Teste a ferramenta com JUnit 5 + AssertJ

Como @Tool é apenas uma annotation em um método Java comum, você testa a lógica de negócio independente do MCP — sem precisar configurar o servidor inteiro:

class WeatherServiceTest {

    private final WeatherService weatherService = new WeatherService();

    @Test
    void shouldReturnTemperatureForKnownCity() {
        String result = weatherService.getTemperature("Ha Noi");

        assertThat(result).contains("28°C");
    }

    @Test
    void shouldReturnFallbackMessageForUnknownCity() {
        String result = weatherService.getTemperature("Tokyo");

        assertThat(result)
                .startsWith("Sem dados para a cidade:")
                .contains("Tokyo");
    }
}

Se você quer testar também a integração MCP (se a ferramenta é registrada com o nome correto e o schema correto), você pode escrever um teste no nível @SpringBootTest e verificar ToolCallbackProvider:

@SpringBootTest
class McpServerConfigTest {

    @Autowired
    private ToolCallbackProvider toolCallbackProvider;

    @Test
    void shouldExposeTemperatureTool() {
        var toolNames = toolCallbackProvider.getToolCallbacks().stream()
                .map(callback -> callback.getToolDefinition().name())
                .toList();

        assertThat(toolNames).contains("getTemperature");
    }
}

Este teste garante que sempre que você refatorar WeatherService, a ferramenta ainda seja exposta com o nome correto — evitando o caso em que você muda o nome do método mas esquece de atualizar os lugares onde o LLM a referencia.

7. Erros comuns e quando não usar MCP

Descrição da ferramenta muito vaga. Se a description apenas disser “obter dados”, o LLM muito provavelmente escolherá a ferramenta errada quando há muitas ferramentas semelhantes. Escreva uma descrição específica, deixando claro o input/output esperado.

Usar MCP para substituir REST/gRPC interno. MCP adiciona uma camada JSON-RPC e um transporte dedicado, otimizado para o contexto em que um LLM chama ferramentas dinamicamente — não é otimizado para dois serviços backend se chamarem mutuamente com alta frequência e baixa latência. Usar MCP aqui é over-engineering.

Não controlar as permissões da ferramenta. Um servidor MCP geralmente roda com as permissões do processo que o hospeda. Se você expuser uma ferramenta capaz de deletar arquivos ou executar comandos shell sem limitar os parâmetros de entrada, o LLM (ou injeção de prompt) pode abusá-la. Sempre valide os parâmetros na ferramenta, não confie cegamente na entrada do LLM.

Executar servidor stdio em ambiente serverless. O transporte stdio requer spawning de processo filho — não funciona bem em ambientes serverless/container de curta duração. Neste caso, você deve usar o transporte SSE/HTTP.

Não sincronizar versões entre cliente e servidor. MCP ainda está em rápido desenvolvimento (muitas mudanças quebradas entre versões da especificação). Ao atualizar o SDK, verifique cuidadosamente o changelog antes de fazer deploy em produção.

8. Conclusão

MCP padroniza a forma como os LLMs chamam ferramentas e leem dados externos, resolvendo o problema de integração M × N entre aplicações de IA e fontes de dados. Com Spring AI, você pode expor um serviço Java como uma ferramenta MCP apenas com a annotation @Tool, sem precisar implementar JSON-RPC manualmente. Use MCP quando estiver construindo agentes/chatbots que precisam chamar ferramentas dinamicamente conforme a decisão do LLM — e evite usá-lo para comunicação service-to-service comum, onde REST ou gRPC ainda são escolhas mais apropriadas.

mcpspring-aillmjavaai-integration