MCP
O que é?
O MCP é um protocolo de comunicação open-source que padroniza a forma pela qual aplicações provém contexto e disponibilizam ferramentas as LLMs. As aplicações por sua vez podem se conectar a N servidores, permitindo uma maior variedade de contexto e de capacidades.
Sua arquitetura segue o modelo client-server, sendo seus principais componentes:
- MCP Hosts -> Aplicações que irão consumir o conteúdo e utilizar as ferramentas disponbilizadas via MCP (Ex: Claude Desktop, IDEs ou AI Tools);
- MCP Clients -> Protocolo responsável pela manutenção e conexão 1:1 com o servidor (1 aplicação = N clients:N servers);
- MCP Servers -> Programa leve que irá expor os recursos e capacidades do MCP previamente configurados;
Porque usar?
De acordo com a própria documentação: "O MCP ajuda a construir agentes e worflows complexos integrados com IA, uma vez que LLMs frequentemente precisam de uma forma de se conectar a diferentes fontes de dados para contexto e ferramentas para executar ações. E o que o MCP prove?"
- Uma crescente lista de integrações "pré-construídas" que podem ser ligadas diretamente a sua aplicação;
- Flexibilidade para alterar entre diferentes provedores de LLMs;
- As melhores práticas para proteger seus dados, dentro da sua infraestrutura;
Como usar?
É possível construir clients
e servers
a partir de SDKs disponíveis para diferentes linguagens.
Comunicação Interna
Como explicado anteriormente, o MCP é constituído de 3 partes principais Hosts
, Clients
e Servers
. Essas partes se comunicam através de diferentes tipos de mensagens, que por sua vez são enviadas e recebidas através do Protocol Layer
e do Transport Layer
.
Protocol layer
Camada responsável pelo recebimento e tratamento das mensagens trocadas entre client
e server
. Funciona de forma similar ao modelo request/response
do HTTP.
Transport layer
Camada responsável pela real comunicação entre o client
e o server
. Utiliza JSON-RPC 2.0 para comunicação e segue seguinte especificação.
O MCP suporta 2 formas de comunicação nativamente, stdio
que consiste em termos simples na leitura e execução do arquivo contendo a configuração do server
pelo próprio client
, útil para processamento local e para operações que exijam leitura e escrita. E também suporta nativamente a comunicação via HTTP/SSE
para os casos onde o server
ficará disponível de forma remota.
Além disso o MCP também permite a implementação de transports
customizados.
Message types
O MCP possui 4 tipos de mensagens:
Request - Mensagem que espera uma resposta;
interface Request {
method: string;
params?: { ... };
}
Result - Mensagem de resposta a solicitação de Request
;
interface Result {
[key: string]: unknown;
}
Error - Indica que algo falhou;
interface Error {
code: number;
message: string;
data?: unknown;
}
Notification - Mensagens de sentido único, não esperam resposta;
interface Notification {
method: string;
params?: { ... };
}
Resources
Os resources
são um componente primitivo no MCP, sendo responsáveis pela exposição de dados ao client
. Dados esses que podem ser textos, ou binários (que possam ser convertidos em base64) e serão lidos pelo usuário e/ou usados de contexto para LLMs.
Os resources
foram criados para serem controlados pela aplicação (host
), dessa forma diferentes aplicações lidam com os dados de formas diferentes, sendo assim é essencial que o server
esteja apito a fornecer acesso a recursos de diferentes maneiras. Por exemplo (com a própria documentação):
- O Claude Desktop exige que o usuário selecione explicitamente os recursos que serão consumidos pela LLM;
- Outras aplicaçãoes podem escolher de forma pré-definida alguns recursos;
- E por fim, pode ser que a própria LLM escolha quais recursos irá usar;
Entre as maneiras de acesso estão a forma direta, acessando um resource
através de sua URI, e a forma dinâmica através de templates de URI. A URI por sua vez é a forma de identificação de um recurso, sendo estruturada da seguinte forma [protocol]://[host]/[path]
.
Exemplo (da própria documentação)
const server = new Server({
name: "example-server",
version: "1.0.0"
}, {
capabilities: {
resources: {}
}
});
// List available resources
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: "file:///logs/app.log",
name: "Application Logs",
mimeType: "text/plain"
}
]
};
});
// Read resource contents
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const uri = request.params.uri;
if (uri === "file:///logs/app.log") {
const logContents = await readLogFile();
return {
contents: [
{
uri,
mimeType: "text/plain",
text: logContents
}
]
};
}
throw new Error("Resource not found");
});
Prompts
Os prompts
permitem definir no server
templates de prompts reutilizáveis, bem como workflows que podem ser acionados pelo usuário, ou pela LLM, através do client
. Um prompt
pode aceitar argumentos dinâmicos, incluir contexto de resources
, encadear múltiplas interações e guiar o usuário/LLM através de workflows.
Exemplo (da própria documentação)
import { Server } from "@modelcontextprotocol/sdk/server";
import {
ListPromptsRequestSchema,
GetPromptRequestSchema
} from "@modelcontextprotocol/sdk/types";
const PROMPTS = {
"git-commit": {
name: "git-commit",
description: "Generate a Git commit message",
arguments: [
{
name: "changes",
description: "Git diff or description of changes",
required: true
}
]
},
"explain-code": {
name: "explain-code",
description: "Explain how code works",
arguments: [
{
name: "code",
description: "Code to explain",
required: true
},
{
name: "language",
description: "Programming language",
required: false
}
]
}
};
const server = new Server({
name: "example-prompts-server",
version: "1.0.0"
}, {
capabilities: {
prompts: {}
}
});
// List available prompts
server.setRequestHandler(ListPromptsRequestSchema, async () => {
return {
prompts: Object.values(PROMPTS)
};
});
// Get specific prompt
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
const prompt = PROMPTS[request.params.name];
if (!prompt) {
throw new Error(`Prompt not found: ${request.params.name}`);
}
if (request.params.name === "git-commit") {
return {
messages: [
{
role: "user",
content: {
type: "text",
text: `Generate a concise but descriptive commit message for these changes:\n\n${request.params.arguments?.changes}`
}
}
]
};
}
if (request.params.name === "explain-code") {
const language = request.params.arguments?.language || "Unknown";
return {
messages: [
{
role: "user",
content: {
type: "text",
text: `Explain how this ${language} code works:\n\n${request.params.arguments?.code}`
}
}
]
};
}
throw new Error("Prompt implementation not found");
});
Tools
As tools
permitem que as LLMs executem ações de forma quase 100% autonoma, apenas com a necessidade de um humano no fluxo por questão de design. Através das tools
é possível realizar qualquer ação desde que ela retorne uma resposta.
Exemplo (da própria documentação)
const server = new Server({
name: "example-server",
version: "1.0.0"
}, {
capabilities: {
tools: {}
}
});
// Define available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [{
name: "calculate_sum",
description: "Add two numbers together",
inputSchema: {
type: "object",
properties: {
a: { type: "number" },
b: { type: "number" }
},
required: ["a", "b"]
}
}]
};
});
// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "calculate_sum") {
const { a, b } = request.params.arguments;
return {
content: [
{
type: "text",
text: String(a + b)
}
]
};
}
throw new Error("Tool not found");
});