AGENTS.md•16.6 kB
# AI Agent Guidelines for FitSlot MCP Server
Este documento fornece diretrizes, padrões e boas práticas para agentes de IA trabalhando neste projeto. Siga estas instruções para manter consistência, qualidade e segurança no código.
## 🎯 Visão Geral do Projeto
**FitSlot MCP Server** é um servidor MCP (Model Context Protocol) para integração com a API FitSlot, fornecendo:
- Gerenciamento de tickets de suporte
- Chatbot com FAQs
- Análise de documentos PDF de bioimpedância
**Stack Tecnológica:**
- TypeScript 5.x
- Node.js 18+
- MCP SDK (@modelcontextprotocol/sdk)
- Arquitetura em camadas (Services, Tools, Utils)
## 📐 Padrões Arquiteturais
### Arquitetura em Camadas
```
┌─────────────────────────────────────┐
│ index.ts (MCP Server) │ ← Entry point
├─────────────────────────────────────┤
│ tools/ (MCP Tools) │ ← Interface MCP
├─────────────────────────────────────┤
│ services/ (Business Logic) │ ← Lógica de negócio
├─────────────────────────────────────┤
│ utils/ (Utilities) │ ← Funções auxiliares
├─────────────────────────────────────┤
│ types/ (Type Definitions) │ ← Tipos TypeScript
└─────────────────────────────────────┘
```
**Princípios:**
1. **Separação de Responsabilidades**: Cada camada tem uma responsabilidade específica
2. **Dependência Unidirecional**: Camadas superiores dependem das inferiores, nunca o contrário
3. **Injeção de Dependências**: Services são injetados em Tools no `index.ts`
### Estrutura de Arquivos
```
src/
├── index.ts # MCP Server principal
├── services/ # Camada de serviços
│ ├── fitslot-api.service.ts # Integração com API externa
│ ├── chatbot.service.ts # Lógica do chatbot
│ └── pdf-analysis.service.ts # Análise de PDFs
├── tools/ # Ferramentas MCP
│ ├── ticket.tools.ts # Tools de tickets
│ ├── chatbot.tools.ts # Tools de chatbot
│ └── pdf.tools.ts # Tools de PDF
├── types/ # Definições de tipos
│ └── index.ts # Tipos centralizados
└── utils/ # Utilitários
├── logger.ts # Sistema de logging
└── validation.ts # Validações
dist/ # Código compilado (não commitar)
node_modules/ # Dependências (não commitar)
```
## 🔧 Padrões de Código
### 1. Nomenclatura
#### Classes e Interfaces
```typescript
// ✅ Correto - PascalCase
export class FitSlotAPIService { }
export interface Ticket { }
export interface BioimpedanceData { }
// ❌ Incorreto
export class fitslotAPIService { }
export interface ticket { }
```
#### Funções e Métodos
```typescript
// ✅ Correto - camelCase, verbos descritivos
async createTicket(request: CreateTicketRequest): Promise<Ticket> { }
async analyzeBioimpedancePDF(filePath: string): Promise<BioimpedanceData> { }
// ❌ Incorreto
async CreateTicket() { } // PascalCase
async ticket() { } // Substantivo
```
#### Variáveis e Constantes
```typescript
// ✅ Correto
const ticketId = 'ticket_123';
const MAX_RETRIES = 3;
const API_TIMEOUT = 30000;
// ❌ Incorreto
const TicketId = 'ticket_123';
const maxRetries = 3; // Constante deve ser UPPER_SNAKE_CASE
```
#### Arquivos
```typescript
// ✅ Correto - kebab-case
fitslot-api.service.ts
chatbot.service.ts
pdf-analysis.service.ts
// ❌ Incorreto
FitSlotApiService.ts
chatbot_service.ts
```
### 2. Estrutura de Serviços
**Template de Service:**
```typescript
/**
* [Nome do Serviço]
* [Descrição do que o serviço faz]
*/
import { logger } from '../utils/logger.js';
import { /* tipos necessários */ } from '../types/index.js';
export class [Nome]Service {
private config: [ConfigType];
constructor(config: [ConfigType]) {
this.config = config;
// Inicialização
}
/**
* [Descrição do método]
* @param param - Descrição do parâmetro
* @returns Descrição do retorno
* @throws Condições de erro
*/
async [nomeMetodo](param: Type): Promise<ReturnType> {
try {
logger.info('[Ação iniciada]', { param });
// Lógica do método
logger.info('[Ação concluída]');
return result;
} catch (error) {
logger.error('[Ação falhou]', error);
throw error;
}
}
}
```
**Exemplo Real:**
```typescript
export class PDFAnalysisService {
async analyzeBioimpedancePDF(filePath: string): Promise<BioimpedanceData> {
try {
logger.info('Starting PDF analysis', { filePath });
const dataBuffer = await fs.readFile(filePath);
const pdfParser = new PDFParse({ data: dataBuffer });
const textResult = await pdfParser.getText();
const extractedData = this.extractBioimpedanceData(textResult.text);
const analysis = this.generateAnalysis(extractedData);
const recommendations = this.generateRecommendations(extractedData);
logger.info('PDF analysis completed successfully');
return { ...extractedData, analysis, recommendations };
} catch (error) {
logger.error('Failed to analyze PDF', error);
throw new Error(`PDF analysis failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
}
```
### 3. Estrutura de Tools
**Template de Tool:**
```typescript
/**
* [Nome das Tools] para MCP Server
*/
import { z } from 'zod';
import { logger } from '../utils/logger.js';
import { validateNotEmpty, validateEnum } from '../utils/validation.js';
export function create[Nome]Tools(service: [ServiceType]) {
return {
tool_name: {
description: 'Descrição clara do que a ferramenta faz',
parameters: z.object({
param1: z.string().describe('Descrição do parâmetro'),
param2: z.enum(['option1', 'option2']).optional().describe('Parâmetro opcional')
}),
execute: async (args: { param1: string; param2?: string }) => {
try {
logger.info('Executing tool', args);
// Validações
validateNotEmpty(args.param1, 'Param1');
// Executar lógica usando o service
const result = await service.method(args.param1);
// Retornar sucesso
return {
content: [{
type: 'text',
text: JSON.stringify({ success: true, result }, null, 2)
}]
};
} catch (error) {
logger.error('Tool execution failed', error);
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
}, null, 2)
}],
isError: true
};
}
}
}
};
}
```
### 4. Sistema de Logging
**Sempre use o logger estruturado:**
```typescript
import { logger } from '../utils/logger.js';
// ✅ Correto
logger.debug('Detailed info', { data: complexObject });
logger.info('Action completed', { userId, ticketId });
logger.warn('Unexpected condition', { condition });
logger.error('Operation failed', error);
// ❌ Incorreto
console.log('Something happened'); // Não use console.log
console.error(error); // Não use console.error
```
**Níveis de Log:**
- `DEBUG`: Informações detalhadas para debugging
- `INFO`: Eventos importantes da aplicação
- `WARN`: Situações inesperadas mas recuperáveis
- `ERROR`: Erros que precisam atenção
### 5. Validação de Entrada
**SEMPRE valide inputs do usuário:**
```typescript
import { validateNotEmpty, validateEnum, validateRange } from '../utils/validation.js';
// ✅ Correto
validateNotEmpty(args.title, 'Title');
validateEnum(args.priority, TicketPriority, 'Priority');
validateRange(args.age, 0, 120, 'Age');
// Validação customizada
if (!args.email.includes('@')) {
throw new Error('Email must be valid');
}
// ❌ Incorreto - sem validação
const ticket = await service.createTicket(args); // Perigoso!
```
### 6. Tratamento de Erros
**Padrão de Error Handling:**
```typescript
// ✅ Correto
try {
logger.info('Starting operation', { params });
const result = await riskyOperation();
logger.info('Operation completed successfully');
return result;
} catch (error) {
logger.error('Operation failed', error);
throw new Error('User-friendly error message');
}
// ❌ Incorreto
try {
const result = await riskyOperation();
return result;
} catch (error) {
throw error; // Não adiciona contexto
}
```
**Erros em Tools MCP:**
```typescript
// ✅ Sempre retorne estrutura consistente
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: 'Clear error message',
details: additionalInfo // Opcional
}, null, 2)
}],
isError: true
};
```
## 🛡️ Guardrails de Segurança
### 1. Nunca Commite Credenciais
```typescript
// ✅ Correto - use variáveis de ambiente
const apiKey = process.env.FITSLOT_API_KEY;
const apiUrl = process.env.FITSLOT_API_URL || 'https://api.fitslot.com';
// ❌ NUNCA faça isso
const apiKey = 'sk-123456789abcdef'; // PERIGOSO!
```
### 2. Sanitize User Input
```typescript
import { sanitizeText } from '../utils/validation.js';
// ✅ Correto
const cleanTitle = sanitizeText(userInput.title);
// ❌ Incorreto - usar input direto
const query = `SELECT * FROM tickets WHERE title = '${userInput.title}'`; // SQL Injection!
```
### 3. Limite Tamanho de Inputs
```typescript
// ✅ Correto
if (args.description.length > 5000) {
throw new Error('Description must be less than 5000 characters');
}
// Ou use Zod
parameters: z.object({
description: z.string().max(5000).describe('Ticket description')
})
```
### 4. Valide File Paths
```typescript
import * as path from 'path';
import * as fs from 'fs/promises';
// ✅ Correto
async function readFile(filePath: string) {
// Normalize path
const normalizedPath = path.normalize(filePath);
// Check if file exists
try {
await fs.access(normalizedPath);
} catch {
throw new Error(`File not found: ${filePath}`);
}
// Prevent directory traversal
if (normalizedPath.includes('..')) {
throw new Error('Invalid file path');
}
return await fs.readFile(normalizedPath);
}
// ❌ Incorreto
async function readFile(filePath: string) {
return await fs.readFile(filePath); // Vulnerável a path traversal
}
```
## 📝 Documentação
### JSDoc para Funções Públicas
```typescript
/**
* Create a new support ticket in the FitSlot system
*
* @param request - Ticket creation parameters including title, description, and priority
* @returns Created ticket with generated ID and timestamps
* @throws {Error} If validation fails or API is unavailable
*
* @example
* ```typescript
* const ticket = await service.createTicket({
* title: 'Login issue',
* description: 'Cannot login',
* priority: TicketPriority.HIGH,
* userId: 'user123'
* });
* ```
*/
async createTicket(request: CreateTicketRequest): Promise<Ticket> {
// Implementation
}
```
### Comentários no Código
```typescript
// ✅ Bons comentários - explicam o "porquê"
// Calculate BMI if not found in PDF
// BMI formula: weight(kg) / (height(m))^2
const heightInMeters = data.height / 100;
data.bmi = parseFloat((data.weight / (heightInMeters * heightInMeters)).toFixed(2));
// ❌ Comentários ruins - explicam o "como" (óbvio)
// Loop through array
for (const item of array) {
// Add to sum
sum += item;
}
```
## 🧪 Testes (Futuro)
### Estrutura de Testes
```typescript
describe('FitSlotAPIService', () => {
let service: FitSlotAPIService;
beforeEach(() => {
service = new FitSlotAPIService({
apiUrl: 'https://test.api.com',
apiKey: 'test-key'
});
});
describe('createTicket', () => {
it('should create ticket with valid data', async () => {
const request: CreateTicketRequest = {
title: 'Test',
description: 'Test description',
priority: TicketPriority.MEDIUM,
userId: 'user123'
};
const result = await service.createTicket(request);
expect(result.id).toBeDefined();
expect(result.status).toBe(TicketStatus.OPEN);
});
it('should throw error with empty title', async () => {
const request: CreateTicketRequest = {
title: '',
description: 'Test',
priority: TicketPriority.MEDIUM,
userId: 'user123'
};
await expect(service.createTicket(request)).rejects.toThrow();
});
});
});
```
## 🚀 Workflow de Desenvolvimento
### 1. Antes de Adicionar Nova Funcionalidade
```bash
# 1. Criar branch
git checkout -b feature/nova-funcionalidade
# 2. Verificar build atual
npm run build
# 3. Instalar dependências se necessário
npm install
```
### 2. Durante o Desenvolvimento
```bash
# Desenvolvimento com hot reload
npm run dev
# Build incremental
npm run build
# Limpar build anterior
npm run clean
```
### 3. Antes de Commitar
```bash
# Build final
npm run build
# Verificar tipos
npx tsc --noEmit
# Verificar se não há erros
npm run build
```
## 📦 Adicionando Novas Dependências
### Checklist
- [ ] A dependência é realmente necessária?
- [ ] Não há alternativa nativa ou já incluída?
- [ ] A licença é compatível (ISC, MIT, Apache 2.0)?
- [ ] Não há vulnerabilidades conhecidas?
- [ ] A dependência é mantida ativamente?
### Instalação
```bash
# Produção
npm install --save nome-da-dependencia
# Desenvolvimento
npm install --save-dev nome-da-dependencia
# Verificar vulnerabilidades
npm audit
```
## 🎨 Padrões de Resposta
### Sucesso
```json
{
"success": true,
"message": "Operation completed successfully",
"data": {
// Dados relevantes
}
}
```
### Erro
```json
{
"success": false,
"error": "User-friendly error message",
"details": "Additional context (optional)"
}
```
### Lista
```json
{
"success": true,
"count": 10,
"items": [
// Array de itens
]
}
```
## ⚠️ Anti-Patterns a Evitar
### 1. God Objects
```typescript
// ❌ Incorreto - classe faz tudo
class FitSlotService {
createTicket() { }
updateTicket() { }
answerFAQ() { }
analyzePDF() { }
sendEmail() { }
generateReport() { }
}
// ✅ Correto - responsabilidades separadas
class TicketService { }
class ChatbotService { }
class PDFAnalysisService { }
```
### 2. Magic Numbers
```typescript
// ❌ Incorreto
if (priority > 3) { }
setTimeout(() => { }, 30000);
// ✅ Correto
const MAX_PRIORITY = 3;
const API_TIMEOUT = 30000;
if (priority > MAX_PRIORITY) { }
setTimeout(() => { }, API_TIMEOUT);
```
### 3. Callback Hell
```typescript
// ❌ Incorreto
getData((data) => {
processData(data, (result) => {
saveData(result, (saved) => {
notify(saved, () => {
// ...
});
});
});
});
// ✅ Correto - use async/await
const data = await getData();
const result = await processData(data);
const saved = await saveData(result);
await notify(saved);
```
## 📚 Recursos
- [TypeScript Handbook](https://www.typescriptlang.org/docs/)
- [MCP Documentation](https://modelcontextprotocol.io/)
- [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices)
- [Clean Code JavaScript](https://github.com/ryanmcdermott/clean-code-javascript)
## 🤝 Quando Modificar Este Documento
Este documento deve ser atualizado quando:
- Novos padrões são estabelecidos
- Mudanças arquiteturais são feitas
- Novos anti-patterns são identificados
- Ferramentas ou dependências principais mudam
---
**Lembre-se:** Estes padrões existem para manter qualidade, consistência e segurança. Quando em dúvida, consulte este documento ou revise o código existente para exemplos.