271 lines
6.2 KiB
Markdown
271 lines
6.2 KiB
Markdown
# 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.
|