mcp-client-server-sample

elfiservice/mcp-client-server-sample

3.1

If you are the rightful owner of mcp-client-server-sample and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.

This project demonstrates how to implement a functional Model Context Protocol (MCP) server using Node.js, Express, and integration with OpenAI.

Tools
2
Resources
0
Prompts
0

MCP Client Service Sample

Descrição

Este projeto demonstra como implementar um servidor MCP (Model Context Protocol) funcional usando Node.js, Express e integração com OpenAI. O servidor fornece tools customizados que podem ser chamados por LLMs para executar tarefas específicas.

Funcionalidades

Servidor MCP

  • Server MCP padrão com suporte completo ao protocolo
  • Tools customizados para busca de clima e notícias
  • Prompts pré-definidos para assistente inteligente
  • Handlers adequados para todos os request schemas do MCP

Integração HTTP/Express

  • Endpoint REST simplificado (/ask) para frontends
  • Integração com OpenAI para decisão automática de uso de tools
  • Tratamento de erros robusto e mensagens explicativas

Arquitetura

Importações e Dependências

import 'dotenv/config';
import express from "express";
import OpenAI from "openai";
import { Server } from "@modelcontextprotocol/sdk/server";
import { z } from "zod";
import { 
  CallToolRequestSchema, 
  ListToolsRequestSchema,
  GetPromptRequestSchema,
  ListPromptsRequestSchema 
} from "@modelcontextprotocol/sdk/types.js";

Estrutura do Servidor MCP

O servidor utiliza a API moderna do SDK MCP v1.17.3+ com:

  • Maps para gerenciamento de tools e prompts
  • Request handlers específicos para cada tipo de operação
  • Schemas tipados para validação de requests

Como Executar

1. Instalar Dependências

npm install

2. Configurar Variáveis de Ambiente (Opcional)

Para usar o endpoint /ask com OpenAI:

export OPENAI_API_KEY="sua-chave-aqui"

Ou criar arquivo .env:

OPENAI_API_KEY=sua-chave-aqui

3. Executar o Servidor

npm start
# ou
node server-mcp.js

4. Testar os Endpoints

Testando sem OpenAI (servidor MCP puro):

O servidor MCP estará disponível para clientes MCP nativos.

Testando com OpenAI (endpoint HTTP):
# Testar busca de clima
curl -X POST http://localhost:3000/ask \
  -H "Content-Type: application/json" \
  -d '{"text": "Qual o clima em São Paulo?"}'

# Testar busca de notícias  
curl -X POST http://localhost:3000/ask \
  -H "Content-Type: application/json" \
  -d '{"text": "Me dê notícias sobre tecnologia"}'

# Testar sem tool (conversa normal)
curl -X POST http://localhost:3000/ask \
  -H "Content-Type: application/json" \
  -d '{"text": "Oi, como você está?"}'

Características Técnicas

Compatibilidade MCP

  • SDK Version: @modelcontextprotocol/sdk v1.17.3+
  • Protocol: Model Context Protocol
  • Transport: HTTP/REST (via Express) + MCP nativo
  • Capabilities: Tools e Prompts

Integração OpenAI

  • Modelo: gpt-4o-mini
  • Function Calling: Automático com tools MCP
  • Fallback: Resposta direta quando não há tool call

Tratamento de Erros

  • Validação de tools inexistentes
  • Mensagens de erro explicativas
  • Fallback quando OpenAI key não está configurada

Estrutura de Arquivos

├── server-mcp.js          # Servidor MCP principal
├── client-mcp.html        # Cliente HTML (se existir)
├── package.json           # Dependências e configurações
├── .env                   # Variáveis de ambiente (criar)
├── README.md              # Documentação
└── logs/                  # Logs do sistema

Dependências Principais

{
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.17.3",
    "express": "^4.18.2",
    "openai": "^4.20.1",
    "dotenv": "^16.3.1",
    "zod": "^3.22.4"
  }
}

Observações Importantes

  • O servidor MCP funciona independentemente da configuração OpenAI
  • A chave OpenAI é necessária apenas para o endpoint simplificado /ask
  • Os tools são simulados (retornam dados mock)
  • A arquitetura permite fácil extensão com novos tools e prompts
  • Compatível com qualquer cliente MCP padrão

Endpoint HTTP para Frontend

POST /ask

Interface simplificada que integra MCP com OpenAI:

app.post("/ask", async (req, res) => {
  try {
    if (!client) {
      return res.status(500).json({ 
        error: "OpenAI API key não configurada. Defina OPENAI_API_KEY como variável de ambiente." 
      });
    }

    const userText = req.body.text;

    // Converter tools MCP para formato OpenAI
    const openaiTools = Array.from(tools.values()).map(tool => ({
      type: "function",
      function: {
        name: tool.name,
        description: tool.description,
        parameters: tool.inputSchema
      }
    }));

    // Chamada para OpenAI com tools
    const decision = await client.chat.completions.create({
      model: "gpt-4o-mini",
      messages: [
        { role: "system", content: "Use tools do MCP quando necessário." },
        { role: "user", content: userText },
      ],
      tools: openaiTools,
    });

    // Verificar se LLM solicitou uso de tool
    const toolCall = decision.choices[0].message.tool_calls?.[0];
    let finalReply = decision.choices[0].message.content || "Não entendi sua solicitação.";

    if (toolCall) {
      const tool = tools.get(toolCall.function.name);
      if (tool) {
        const args = JSON.parse(toolCall.function.arguments);
        const result = await tool.handler(args);
        finalReply = result.content[0].text;
      }
    }

    res.json({ reply: finalReply });
  } catch (error) {
    console.error("Erro no endpoint /ask:", error);
    res.status(500).json({ error: "Erro interno do servidor" });
  }
});

Entrada:

{
  "text": "Qual o clima em São Paulo?"
}

Saída:

{
  "reply": "O clima em São Paulo hoje está ensolarado ☀️"
}

Tools Implementados

1. getWeather

tools.set("getWeather", {
  name: "getWeather",
  description: "Busca o clima de uma cidade",
  inputSchema: {
    type: "object",
    properties: {
      city: { type: "string", description: "Nome da cidade" }
    },
    required: ["city"]
  },
  handler: async ({ city }) => {
    return {
      content: [{ type: "text", text: `O clima em ${city} hoje está ensolarado ☀️` }],
    };
  }
});

2. getNews

tools.set("getNews", {
  name: "getNews", 
  description: "Busca notícias sobre um tópico",
  inputSchema: {
    type: "object",
    properties: {
      topic: { type: "string", description: "Tópico das notícias" }
    },
    required: ["topic"]
  },
  handler: async ({ topic }) => {
    return {
      content: [{ type: "text", text: `Últimas notícias sobre ${topic}: ...` }],
    };
  }
});

Prompts Implementados

assistant

prompts.set("assistant", {
  name: "assistant",
  description: "Você é um assistente simpático que decide quando chamar Tools para responder perguntas.",
  handler: async () => {
    return {
      messages: [
        {
          role: "system",
          content: {
            type: "text",
            text: "Você é um assistente simpático que decide quando chamar Tools para responder perguntas."
          }
        }
      ]
    };
  }
});

Request Handlers MCP

ListToolsRequestSchema

server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: Array.from(tools.values()).map(tool => ({
      name: tool.name,
      description: tool.description,
      inputSchema: tool.inputSchema
    }))
  };
});

CallToolRequestSchema

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;
  const tool = tools.get(name);
  
  if (!tool) {
    throw new Error(`Tool ${name} not found`);
  }
  
  return await tool.handler(args);
});

ListPromptsRequestSchema e GetPromptRequestSchema

Handlers similares para gerenciamento de prompts.

Observações