Initial commit
This commit is contained in:
270
mcp-server/tools/helpers/ERROR_HANDLING.md
Normal file
270
mcp-server/tools/helpers/ERROR_HANDLING.md
Normal file
@@ -0,0 +1,270 @@
|
||||
# Error Handling System for Tools
|
||||
|
||||
Centralizado error handling para todas las herramientas MCP del servidor.
|
||||
|
||||
## Características
|
||||
|
||||
✅ **Manejo consistente de errores** - Todas las herramientas retornan el mismo formato
|
||||
✅ **Logging automático** - Todos los errores se registran en consola
|
||||
✅ **Validación de parámetros** - Validación requerida y de tipos
|
||||
✅ **Detección de errores API** - Identifica patrones comunes de error en respuestas
|
||||
✅ **Información contextual** - Cada error incluye el contexto de dónde ocurrió
|
||||
|
||||
## Funciones Disponibles
|
||||
|
||||
### `handleToolError(error, context, additionalInfo)`
|
||||
|
||||
Maneja cualquier error y retorna una respuesta formateada.
|
||||
|
||||
```javascript
|
||||
import { handleToolError } from "../helpers/errorHandler.js";
|
||||
|
||||
try {
|
||||
// tu código
|
||||
} catch (error) {
|
||||
return handleToolError(error, 'my_tool', { userId: 123 });
|
||||
}
|
||||
```
|
||||
|
||||
**Retorna:**
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"code": "ECONNREFUSED",
|
||||
"message": "connect ECONNREFUSED 127.0.0.1:3000",
|
||||
"context": "my_tool",
|
||||
"userId": 123
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `handleApiResponse(data, context)`
|
||||
|
||||
Detecta errores en respuestas de API (busca patrones comunes).
|
||||
|
||||
```javascript
|
||||
const response = await axios.post(url, payload);
|
||||
|
||||
// Detecta automáticamente: error, Error, PHPSyntax, success: false, etc.
|
||||
const apiError = handleApiResponse(response.data, 'save_module');
|
||||
if (apiError) return apiError;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `validateRequired(params, requiredFields, context)`
|
||||
|
||||
Valida que los parámetros requeridos estén presentes.
|
||||
|
||||
```javascript
|
||||
const error = validateRequired(
|
||||
{ name: "Juan", email: "" },
|
||||
['name', 'email'],
|
||||
'create_user'
|
||||
);
|
||||
// error porque email está vacío
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `validateTypes(params, schema, context)`
|
||||
|
||||
Valida tipos de datos.
|
||||
|
||||
```javascript
|
||||
const error = validateTypes(
|
||||
{ age: "25", active: true },
|
||||
{ age: 'number', active: 'boolean' },
|
||||
'create_user'
|
||||
);
|
||||
// error porque age es string, no number
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `createValidator(requiredFields, typeSchema)`
|
||||
|
||||
Crea una función validadora reutilizable.
|
||||
|
||||
```javascript
|
||||
const validateUserInput = createValidator(
|
||||
['name', 'email'],
|
||||
{ age: 'number', active: 'boolean' }
|
||||
);
|
||||
|
||||
// Usar en múltiples lugares
|
||||
const error = validateUserInput(params, 'create_user');
|
||||
if (error) return error;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `withErrorHandling(handler, toolName)`
|
||||
|
||||
Envuelve un handler para manejar errores automáticamente.
|
||||
|
||||
```javascript
|
||||
const safeHandler = withErrorHandling(
|
||||
async (params, extra) => {
|
||||
// tu código
|
||||
},
|
||||
'my_tool'
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `safeJsonParse(jsonString, context)`
|
||||
|
||||
Parse JSON seguro con manejo de errores.
|
||||
|
||||
```javascript
|
||||
const result = safeJsonParse(jsonString, 'parse_config');
|
||||
if (!result.success) {
|
||||
// result.error contiene el error formateado
|
||||
return result.error;
|
||||
}
|
||||
const data = result.data;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Patrón Recomendado para Tools
|
||||
|
||||
```javascript
|
||||
import { z } from "zod";
|
||||
import axios from "axios";
|
||||
import { withAuth, getSessionCredentials } from "../../auth/index.js";
|
||||
import {
|
||||
handleToolError,
|
||||
handleApiResponse,
|
||||
validateRequired
|
||||
} from "../helpers/errorHandler.js";
|
||||
|
||||
export function registerMyTool(server) {
|
||||
server.tool(
|
||||
"my_tool",
|
||||
"Descripción de la herramienta",
|
||||
{
|
||||
param1: z.string().describe("Parámetro 1"),
|
||||
param2: z.number().describe("Parámetro 2"),
|
||||
},
|
||||
withAuth(async ({ param1, param2 }, extra) => {
|
||||
try {
|
||||
// 1. Validar parámetros requeridos
|
||||
const validationError = validateRequired(
|
||||
{ param1, param2 },
|
||||
['param1', 'param2'],
|
||||
'my_tool'
|
||||
);
|
||||
if (validationError) return validationError;
|
||||
|
||||
// 2. Obtener credenciales
|
||||
const credentials = getSessionCredentials(extra.sessionId);
|
||||
|
||||
// 3. Hacer llamada API
|
||||
const response = await axios.post(url, payload, {
|
||||
headers: { /* ... */ }
|
||||
});
|
||||
|
||||
// 4. Verificar respuesta de API
|
||||
const apiError = handleApiResponse(response.data, 'my_tool');
|
||||
if (apiError) return apiError;
|
||||
|
||||
// 5. Retornar resultado
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify(response.data, null, 2)
|
||||
}]
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
// Los errores se capturan y formatean automáticamente
|
||||
return handleToolError(error, 'my_tool', { param1, param2 });
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Errores Detectados Automáticamente
|
||||
|
||||
`handleApiResponse()` detecta estos patrones en respuestas:
|
||||
|
||||
- ✅ `data.error` o `data.Error`
|
||||
- ✅ `data.PHPSyntax` - Errores de sintaxis PHP
|
||||
- ✅ `data.success === false` - Campo success explícito
|
||||
- ✅ Strings con palabras clave: "error", "fatal", "undefined", "syntax"
|
||||
- ✅ Respuestas vacías o null
|
||||
|
||||
---
|
||||
|
||||
## Formato de Error Consistente
|
||||
|
||||
Todos los errores retornan este formato:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"code": "ERROR_CODE",
|
||||
"message": "Mensaje descriptivo del error",
|
||||
"context": "nombre_del_tool",
|
||||
"...": "información adicional"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migración de Tools Existentes
|
||||
|
||||
Para actualizar un tool existente:
|
||||
|
||||
1. Importar funciones de error handler
|
||||
2. Reemplazar `try-catch` genérico con `handleToolError()`
|
||||
3. Agregar validación con `validateRequired()`
|
||||
4. Agregar `handleApiResponse()` después de llamadas API
|
||||
5. Pasar información contextual útil a `handleToolError()`
|
||||
|
||||
**Ejemplo antes:**
|
||||
```javascript
|
||||
try {
|
||||
// código
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Error: " + error.message }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Ejemplo después:**
|
||||
```javascript
|
||||
try {
|
||||
// código
|
||||
} catch (error) {
|
||||
return handleToolError(error, 'my_tool', { extraInfo: value });
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Logging
|
||||
|
||||
Todos los errores se registran en stderr con contexto:
|
||||
|
||||
```
|
||||
[Tool Error - save_module] Cannot read property 'website' of undefined
|
||||
Stack: Error: Cannot read property 'website' of undefined
|
||||
at registerSaveModuleTool (/Users/...save.js:45:20)
|
||||
...
|
||||
```
|
||||
|
||||
Esto facilita debug y auditoría de errores en producción.
|
||||
Reference in New Issue
Block a user