Você pode facilmente criar fluxos de trabalho de IA com o Anakin AI sem nenhum conhecimento de programação. Conecte-se a APIs de LLM como: GPT-4, Claude 3.5 Sonnet, Uncensored Dolphin-Mixtral, Stable Diffusion, DALLE, Web Scraping.... em um único Fluxo de Trabalho!
Esqueça a programação complicada, automatize seu trabalho cotidiano com o Anakin AI!
Por um tempo limitado, você também pode usar o Google Gemini 1.5 e o Stable Diffusion gratuitamente!

Introdução
O Protocolo de Contexto do Modelo (MCP) representa um avanço significativo no ecossistema de IA, oferecendo uma maneira padronizada de se comunicar com grandes modelos de linguagem. Em vez de cada plataforma de IA implementar sua própria formatação única para mensagens, o MCP visa fornecer uma interface consistente para prompts, respostas e chamadas de função em vários modelos e plataformas.
Enquanto o protocolo em si está evoluindo, construir um servidor básico compatível com MCP pode ser simples. Neste guia, vou te conduzir na criação de um servidor simples, mas funcional, que adere aos princípios fundamentais de manipulação de mensagens em sistemas de IA modernos. Nossa implementação se concentrará em criar uma base que você poderá expandir posteriormente com recursos mais avançados.
10 minutos são tempo suficiente? Para um sistema pronto para produção, certamente não. Mas para um protótipo funcional que demonstra os conceitos-chave? Absolutamente. Vamos começar!
Requisitos
Antes de começarmos, você precisará de:
- Node.js (v16+) instalado em seu sistema
- Conhecimento básico de JavaScript/TypeScript
- Familiaridade com Express.js ou frameworks web similares
- Um editor de código (VS Code recomendado)
- Acesso a terminal/línea de comando
- Gerenciador de pacotes npm ou yarn
Etapa 1: Configurando Seu Projeto (2 minutos)
Primeiro, vamos criar um novo diretório e inicializar nosso projeto:
mkdir mcp-server
cd mcp-server
npm init -y
Agora, instale as dependências necessárias:
npm install express cors typescript ts-node @types/node @types/express @types/cors
npm install --save-dev nodemon
Crie um arquivo de configuração do TypeScript:
npx tsc --init
Edite o tsconfig.json
gerado para incluir essas configurações essenciais:
{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true
},
"include": ["src/**/*"]
}
Atualize a seção de scripts do seu package.json
:
"scripts": {
"start": "node dist/index.js",
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc"
}
Etapa 2: Criando o Servidor Central (3 minutos)
Crie seu diretório de origem e arquivo principal do servidor:
mkdir -p src/handlers
touch src/index.ts
touch src/handlers/messageHandler.ts
touch src/types.ts
Vamos definir nossos tipos primeiro em src/types.ts
:
// Estrutura básica da mensagem
export interface Content {
type: string;
text?: string;
}
export interface Message {
role: "user" | "assistant" | "system";
content: Content[] | string;
}
// Estruturas de solicitação e resposta
export interface ModelRequest {
messages: Message[];
max_tokens?: number;
temperature?: number;
stream?: boolean;
}
export interface ModelResponse {
message: Message;
}
// Interfaces de chamada de ferramenta
export interface Tool {
name: string;
description: string;
input_schema: {
type: string;
properties: Record;
required?: string[];
};
}
export interface ToolCall {
type: "tool_call";
id: string;
name: string;
input: Record;
}
export interface ToolResult {
type: "tool_result";
tool_call_id: string;
content: string;
}
Agora, implemente o servidor básico em src/index.ts
:
import express from 'express';
import cors from 'cors';
import { handleMessageRequest } from './handlers/messageHandler';
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(cors());
app.use(express.json({ limit: '10mb' }));
// Endpoint principal para processamento de mensagens
app.post('/v1/messages', handleMessageRequest);
// Endpoint de verificação de saúde
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok' });
});
// Iniciar servidor
app.listen(PORT, () => {
console.log(`Servidor funcionando na porta ${PORT}`);
console.log(`Verificação de saúde: http://localhost:${PORT}/health`);
console.log(`Endpoint de mensagens: http://localhost:${PORT}/v1/messages`);
});
Em seguida, implemente o manipulador de mensagens em src/handlers/messageHandler.ts
:
import { Request, Response } from 'express';
import { ModelRequest, ModelResponse, Message, Content } from '../types';
export async function handleMessageRequest(req: Request, res: Response) {
try {
const request = req.body as ModelRequest;
// Validação básica
if (!request.messages || !Array.isArray(request.messages) || request.messages.length === 0) {
return res.status(400).json({ error: 'Formato de solicitação inválido. Array de mensagens é obrigatório.' });
}
// Log do pedido recebido (para depuração)
console.log('Solicitação recebida com', request.messages.length, 'mensagens');
// Processar as mensagens
const response = processMessages(request.messages);
// Retornar a resposta
return res.status(200).json(response);
} catch (error) {
console.error('Erro ao processar a solicitação:', error);
return res.status(500).json({ error: 'Erro interno do servidor' });
}
}
function processMessages(messages: Message[]): ModelResponse {
// Extrair a última mensagem do usuário
const lastUserMessage = findLastUserMessage(messages);
if (!lastUserMessage) {
return createErrorResponse("Nenhuma mensagem do usuário encontrada na conversa");
}
const userQuery = extractTextContent(lastUserMessage);
// Lógica simples de geração de resposta
let responseText = "";
if (userQuery.toLowerCase().includes('olá') || userQuery.toLowerCase().includes('oi')) {
responseText = "Olá! Como posso te ajudar hoje?";
} else if (userQuery.toLowerCase().includes('clima')) {
responseText = "Não tenho acesso a dados climáticos em tempo real, mas posso te ajudar a entender padrões climáticos em geral.";
} else if (userQuery.toLowerCase().includes('hora')) {
responseText = `A hora atual do servidor é ${new Date().toLocaleTimeString()}.`;
} else {
responseText = "Recebi sua mensagem. Esta é uma resposta simples do servidor modelo.";
}
// Construir e retornar a resposta
return {
message: {
role: "assistant",
content: [{
type: "text",
text: responseText
}]
}
};
}
function findLastUserMessage(messages: Message[]): Message | undefined {
// Encontrar a última mensagem com o papel 'user'
for (let i = messages.length - 1; i >= 0; i--) {
if (messages[i].role === 'user') {
return messages[i];
}
}
return undefined;
}
function extractTextContent(message: Message): string {
if (typeof message.content === 'string') {
return message.content;
} else if (Array.isArray(message.content)) {
return message.content
.filter(item => item.type === 'text' && item.text)
.map(item => item.text)
.join(' ');
}
return '';
}
function createErrorResponse(errorMessage: string): ModelResponse {
return {
message: {
role: "assistant",
content: [{
type: "text",
text: `Erro: ${errorMessage}`
}]
}
};
}
Etapa 3: Adicionando Capacidade de Chamada de Ferramentas (3 minutos)
Crie um novo arquivo para definições e implementações de ferramentas:
touch src/tools.ts
Implemente algumas ferramentas básicas em src/tools.ts
:
import { Tool } from './types';
// Definições de ferramentas
export const availableTools: Tool[] = [
{
name: "get_current_time",
description: "Obter a hora atual do servidor",
input_schema: {
type: "object",
properties: {
timezone: {
type: "string",
description: "Fuso horário opcional (padrão é o fuso horário do servidor)"
}
}
}
},
{
name: "calculate",
description: "Realizar um cálculo matemático",
input_schema: {
type: "object",
properties: {
expression: {
type: "string",
description: "Expressão matemática a ser avaliada"
}
},
required: ["expression"]
}
}
];
// Implementações de ferramentas
export function executeToolCall(name: string, params: Record): string {
switch (name) {
case "get_current_time":
return getTime(params.timezone);
case "calculate":
return calculate(params.expression);
default:
throw new Error(`Ferramenta desconhecida: ${name}`);
}
}
function getTime(timezone?: string): string {
const options: Intl.DateTimeFormatOptions = {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'short'
};
try {
if (timezone) {
options.timeZone = timezone;
}
return new Date().toLocaleTimeString('pt-BR', options);
} catch (error) {
return `${new Date().toLocaleTimeString()} (Hora do servidor)`;
}
}
function calculate(expression: string): string {
try {
// CUIDADO: Em uma aplicação real, você deve usar um método de avaliação mais seguro
// Este é um exemplo simplificado apenas para demonstração
const sanitizedExpression = expression.replace(/[^0-9+\-*/().\s]/g, '');
const result = eval(sanitizedExpression);
return `${expression} = ${result}`;
} catch (error) {
return `Erro ao calcular ${expression}: ${error instanceof Error ? error.message : String(error)}`;
}
}
Agora, atualize o manipulador de mensagens para suportar chamadas de ferramentas modificando src/handlers/messageHandler.ts
:
// Adicione essas importações no topo
import { availableTools, executeToolCall } from '../tools';
import { ToolCall, ToolResult } from '../types';
// Atualize a função handleMessageRequest
export async function handleMessageRequest(req: Request, res: Response) {
try {
const request = req.body as ModelRequest;
// Validação básica
if (!request.messages || !Array.isArray(request.messages) || request.messages.length === 0) {
return res.status(400).json({ error: 'Formato de solicitação inválido. Array de mensagens é obrigatório.' });
}
// Verificar se esta é uma solicitação com resultados de ferramentas
const lastMessage = request.messages[request.messages.length - 1];
if (lastMessage.role === 'assistant' && Array.isArray(lastMessage.content)) {
const toolCalls = lastMessage.content.filter(item =>
item.type === 'tool_call') as ToolCall[];
if (toolCalls.length > 0) {
// Esta é um acompanhamento com resultados de ferramentas
return handleToolResultsResponse(request, res);
}
}
// Processar como uma mensagem normal
const response = processMessages(request.messages);
// Retornar a resposta
return res.status(200).json(response);
} catch (error) {
console.error('Erro ao processar solicitação:', error);
return res.status(500).json({ error: 'Erro interno do servidor' });
}
}
// Adicione esta função para lidar com chamadas de ferramentas
function handleToolResultsResponse(request: ModelRequest, res: Response) {
const messages = request.messages;
const lastAssistantMessage = messages[messages.length - 1];
if (lastAssistantMessage.role !== 'assistant' || !Array.isArray(lastAssistantMessage.content)) {
return res.status(400).json({ error: 'Formato de resultados de ferramentas inválido' });
}
// Encontrar chamadas de ferramentas e resultados
const toolCalls = lastAssistantMessage.content.filter(
item => item.type === 'tool_call'
) as ToolCall[];
const toolResults = lastAssistantMessage.content.filter(
item => item.type === 'tool_result'
) as ToolResult[];
// Processar os resultados
let finalResponse = "Eu processei as seguintes informações:\n\n";
toolResults.forEach(result => {
const relatedCall = toolCalls.find(call => call.id === result.tool_call_id);
if (relatedCall) {
finalResponse += `- ${relatedCall.name}: ${result.content}\n`;
}
});
return res.status(200).json({
message: {
role: "assistant",
content: [{ type: "text", text: finalResponse }]
}
});
}
// Modifique a função processMessages para lidar com potenciais chamadas de ferramentas
function processMessages(messages: Message[]): ModelResponse {
const lastUserMessage = findLastUserMessage(messages);
if (!lastUserMessage) {
return createErrorResponse("Nenhuma mensagem do usuário encontrada na conversa");
}
const userQuery = extractTextContent(lastUserMessage);
// Procure palavras-chave que podem acionar o uso de ferramentas
if (userQuery.toLowerCase().includes('hora')) {
return {
message: {
role: "assistant",
content: [
{ type: "tool_call", id: "call_001", name: "get_current_time", input: {} },
{
type: "text",
text: "Vou verificar a hora atual para você."
}
]
}
};
} else if (userQuery.toLowerCase().match(/calcular|computar|qual é \d+[\+\-\*\/]/)) {
// Extrair um possível cálculo
const expression = userQuery.match(/(\d+[\+\-\*\/\(\)\.]*\d+)/)?.[0] || "1+1";
return {
message: {
role: "assistant",
content: [
{
type: "tool_call",
id: "call_002",
name: "calculate",
input: { expression }
},
{
type: "text",
text: `Vou calcular ${expression} para você.`
}
]
}
};
}
// Resposta padrão para outras consultas
return {
message: {
role: "assistant",
content: [{
type: "text",
text: `Recebi sua mensagem: "${userQuery}". Como posso ajudar mais?`
}]
}
};
}
Etapa 4: Testes e Implantação (2 minutos)
Vamos criar um script de teste simples para verificar nosso servidor. Crie um arquivo test.js
no diretório raiz:
const fetch = require('node-fetch');
async function testServer() {
const url = 'http://localhost:3000/v1/messages';
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
messages: [
{
role: "user",
content: [
{
type: "text",
text: "Que horas são agora?"
}
]
}
]
})
});
const data = await response.json();
console.log(JSON.stringify(data, null, 2));
}
testServer().catch(console.error);
Para testar seu servidor:
# Inicie o servidor
npm run dev
# Em outro terminal, execute o teste
node test.js
Para uma implantação rápida, vamos criar um Dockerfile simples:
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
Construa e execute o contêiner:
docker build -t mcp-server .
docker run -p 3000:3000 mcp-server
Conclusão
Em apenas 10 minutos, construímos um servidor básico que implementa os conceitos-chave dos modernos protocolos de mensagens de IA. Nosso servidor pode:
- Processar solicitações de mensagens estruturadas
- Responder em um formato padrão
- Manipular funcionalidade básica de chamadas de ferramentas
- Processar e responder a resultados de ferramentas
Embora esta implementação seja simplificada, ela fornece uma base sólida para desenvolvimento futuro. Para expandir este servidor para uso em produção, considere:
- Adicionar autenticação e limitação de taxa
- Implementar tratamento de erros adequado e validação
- Conectar a modelos de IA reais para processamento
- Adicionar ferramentas mais sofisticadas
- Implementar respostas em streaming
- Adicionar logging e monitoramento abrangentes
Lembre-se de que, embora nossa implementação siga princípios gerais dos protocolos modernos de mensagens de IA, implementações específicas como a API da OpenAI ou a API do Claude da Anthropic podem ter requisitos adicionais ou variações em seus formatos esperados. Sempre consulte a documentação oficial para o serviço específico com o qual você está integrando.
O campo dos protocolos de mensagens de IA está evoluindo rapidamente, então fique atualizado com os últimos desenvolvimentos e esteja preparado para adaptar sua implementação à medida que os padrões evoluem. Ao entender os conceitos principais demonstrados neste guia, você estará bem preparado para trabalhar com quaisquer novas inovações que surgirem neste emocionante campo.