Módulo 7: Testing, Debugging e Integración

Troubleshooting y Errores Comunes

Troubleshooting y Errores Comunes

Descripción de la cápsula

Esta cápsula es tu guía de referencia. Cuando algo falle — y va a fallar — vienes aquí. Cada error tiene el mismo formato: síntomas (qué ves), diagnóstico (cómo investigar), causa (por qué pasa), y solución (cómo arreglarlo). Sin ambigüedad, sin rodeos.

Los errores están organizados por categoría: conexión, transport, tools, resources, permisos, y configuración. Al final, hay un mini-proyecto que integra todo lo aprendido en este módulo: construir un test suite completo para uno de tus MCP servers.


Errores de conexión

Error 1: "Server failed to start"

Síntomas: Claude Code muestra "disconnected" en /mcp. MCP Inspector no conecta.

Diagnóstico:

# Ejecuta el server directamente
node dist/index.js < /dev/null 2>&1
echo $?  # exit code distinto de 0 = error

# Para Python
python server.py < /dev/null 2>&1

Causas y soluciones:

CausaSolución
Build no actualizadonpm run build
Dependencia faltantenpm install
Path incorrecto en settingsVerifica el path absoluto en ~/.claude/settings.json
Puerto en uso (HTTP transport)Cambia el puerto o mata el proceso existente
Error de sintaxis en el códigoRevisa la salida de tsc --noEmit

Error 2: "Connection refused"

Síntomas: El client intenta conectar pero recibe "connection refused."

Diagnóstico:

# Verifica que el server está ejecutándose
ps aux | grep "node dist/index.js"

# Verifica el puerto (para HTTP transport)
lsof -i :3000

Causa: El server no está escuchando o está escuchando en un puerto/interface diferente.

Solución:

// Verifica que el transport se inicia correctamente
const transport = new StdioServerTransport();
await server.connect(transport);
// Para stdio, no hay puerto — el problema es que el server no arranca

Error 3: "ENOENT: no such file or directory"

Síntomas: Claude Code no puede encontrar el ejecutable del server.

Diagnóstico:

# Verifica que el archivo existe
ls -la ~/path/to/dist/index.js

# Verifica que el comando existe
which node
which python

Solución: Usa paths absolutos en la configuración:

{
  "mcpServers": {
    "my-server": {
      "command": "/usr/local/bin/node",
      "args": ["/Users/tu-usuario/proyectos/my-server/dist/index.js"]
    }
  }
}

Errores de transport

Error 4: "Unexpected token in JSON"

Síntomas: El server arranca pero los requests fallan con "parse error."

Diagnóstico: Busca output no-JSON en stdout:

node dist/index.js < /dev/null 2>/dev/null
# Si ves texto que NO es JSON → ese es el problema

Causa: console.log() en un server con stdio transport. Cualquier texto en stdout que no sea JSON-RPC rompe el parser del client.

Solución:

// ❌ Rompe stdio
console.log("Server iniciado");

// ✅ Usa stderr para logs
console.error("[INFO] Server iniciado");

Revisa todo tu código buscando console.log y cámbialo a console.error. Incluye dependencias que podrían usar console.log internamente.

Error 5: "Timeout waiting for response"

Síntomas: El request se envía pero nunca llega la respuesta.

Diagnóstico:

// Agrega timeout explícito en tu tool
async ({ filePath }) => {
  console.error(`[DEBUG] read_file inicio: ${filePath}`);
  const start = Date.now();

  try {
    const content = await fs.readFile(filePath, "utf-8");
    console.error(`[DEBUG] read_file completado en ${Date.now() - start}ms`);
    return { content: [{ type: "text" as const, text: content }] };
  } catch (error) {
    console.error(`[ERROR] read_file falló después de ${Date.now() - start}ms`);
    throw error;
  }
}

Causas comunes:

CausaSolución
Operación I/O que nunca resuelveAgrega timeout con Promise.race
Deadlock en código asyncRevisa que no hay awaits circulares
Server procesando request anteriorVerifica que no hay operaciones bloqueantes
Network call a servicio caídoAgrega timeout a requests HTTP

Error 6: "Protocol versión mismatch"

Síntomas: "Unsupported protocol versión" en la respuesta de inicialización.

Causa: Versiones incompatibles del SDK entre client y server.

Solución:

# Actualiza el SDK
npm install @modelcontextprotocol/sdk@latest

# Verifica la versión instalada
npm list @modelcontextprotocol/sdk

Errores de Tools

Error 7: "Tool not found"

Síntomas: Claude Code intenta usar un tool pero recibe "tool not found."

Diagnóstico:

# Verifica con MCP Inspector
npx @modelcontextprotocol/inspector node dist/index.js
# ¿El tool aparece en la lista?

Causas y soluciones:

CausaSolución
Tool no registradoVerifica que server.tool(...) se ejecuta
Nombre del tool con typoCompara el nombre en el código vs el que Claude Code usa
Registro condicional que fallaRevisa que la condición se cumple
Build desactualizadonpm run build

Error 8: "Invalid arguments"

Síntomas: "Validation error" o "Invalid arguments" al invocar un tool.

Diagnóstico: Revisa qué argumentos envía Claude Code vs qué espera tu schema:

En MCP Inspector → Panel de mensajes:
Request: { "name": "read_file", "arguments": { "path": "/tmp/test.txt" } }
                                                ^^^^
Tu schema espera "filePath", no "path"

Causa: Mismatch entre lo que Claude Code envía y lo que tu schema Zod espera.

Solución:

// Verifica que tu descripción guía a Claude correctamente
server.tool(
  "read_file",
  "Lee un archivo. El parámetro filePath debe ser la ruta completa al archivo.",
  { filePath: z.string().describe("Ruta completa al archivo (e.g., /home/user/file.txt)") },
  // ...
);

Error 9: "Tool execution failed"

Síntomas: El tool se invoca pero retorna un error genérico.

Diagnóstico: Agrega logging detallado en el handler:

async ({ filePath }) => {
  try {
    // ... tu lógica
  } catch (error) {
    console.error("[ERROR] Tool failed:", error);
    return {
      content: [{ type: "text" as const, text: `Error: ${(error as Error).message}\nStack: ${(error as Error).stack}` }],
      isError: true,
    };
  }
}

Causa: Excepción no capturada en el handler del tool. Sin try/catch, el error se propaga como un error genérico del protocolo.


Errores de Resources

Error 10: "Resource not found"

Síntomas: Intentar leer un resource retorna "resource not found."

Diagnóstico:

# Verifica que el resource está registrado
# En MCP Inspector, panel de Resources → ¿aparece el URI?

Causa: El URI del request no coincide exactamente con el URI registrado.

Solución:

// Verifica la coincidencia exacta
server.resource(
  "project-status",
  "status://project",     // ← Este URI exacto
  { description: "..." },
  async () => { ... }
);

// El client debe usar exactamente "status://project"
// No "status://project/" (trailing slash)
// No "Status://project" (case sensitive)

Error 11: "Resource returned invalid data"

Síntomas: El resource retorna datos pero el client no puede procesarlos.

Causa: El formato de la respuesta no cumple el schema de MCP.

Solución: Asegúrate de retornar el formato correcto:

// ✅ Formato correcto
async () => ({
  contents: [{
    uri: "status://project",
    text: JSON.stringify({ status: "ok" }),
    mimeType: "application/json",
  }],
})

// ❌ Formato incorrecto (falta uri o mimeType)
async () => ({
  contents: [{
    text: "algo",
  }],
})

Errores de permisos y configuración

Error 12: "Permission denied"

Síntomas: Claude Code se niega a invocar un tool.

Causa: Los permisos de MCP no están configurados o fueron denegados.

Solución:

# Verifica permisos en Claude Code
/mcp

# Si el tool aparece pero no se puede usar, revisa los permisos del filesystem
ls -la /ruta/que/el/tool/accede

Error 13: "JSON parse error en settings"

Síntomas: Claude Code no reconoce ningún MCP server.

Diagnóstico:

# Valida el JSON del settings file
python3 -c "import json; json.load(open('$HOME/.claude/settings.json'))"

Causas comunes: Comma trailing, comillas simples en vez de dobles, o comments (JSON no soporta comments).

// ❌ Errores comunes de JSON
{
  "mcpServers": {
    "server": {
      "command": "node",
      "args": ["index.js"],  // ← trailing comma si es el último campo
    }
  }
}

// ✅ JSON válido
{
  "mcpServers": {
    "server": {
      "command": "node",
      "args": ["index.js"]
    }
  }
}

Tabla de referencia rápida

ErrorCausa más probableFix rápido
Server disconnectedServer crashea al iniciarnpm run build && node dist/index.js < /dev/null
Connection refusedServer no está ejecutándoseVerificar proceso y path
ENOENTPath incorrecto en settingsUsar paths absolutos
JSON parse error (transport)console.log en stdio serverCambiar a console.error
TimeoutOperación I/O bloqueanteAgregar timeout con Promise.race
Tool not foundTool no registrado o build viejonpm run build, verificar con Inspector
Invalid argumentsSchema mismatchVerificar nombre de parámetros en Zod
Resource not foundURI mismatch (case, trailing slash)Comparar URI exacto
Permission deniedPermisos no aceptados/mcp para verificar estado
Settings parse errorJSON inválidoValidar con python -c "import json; ..."

Mini-proyecto: Test suite completo para un MCP server

Objetivo

Construye un test suite completo para uno de los MCP servers que creaste en los módulos 4 o 5. El test suite debe cubrir las tres capas de testing y servir como template para el proyecto del Módulo 8.

Requisitos

Tu test suite debe incluir:

  1. Al menos 3 unit tests de tools — happy path, error handling, edge cases
  2. Al menos 2 unit tests de resources — datos válidos, formato correcto
  3. Al menos 3 integration tests — capabilities, protocolo, flujo completo
  4. Al menos 2 tests de error handling — inputs inválidos, servicios caídos
  5. Setup y teardown — archivos temporales, cleanup, fixtures
  6. Logging — el server debe tener logging a stderr

Estructura del proyecto

my-mcp-server/
├── src/
│   ├── index.ts          # Server con createServer() exportado
│   └── logger.ts         # Logger a stderr
├── tests/
│   ├── tools.test.ts     # Unit tests de tools
│   ├── resources.test.ts # Unit tests de resources
│   └── server.test.ts    # Integration tests
├── vitest.config.ts
├── tsconfig.json
└── package.json

Para Python:

my-mcp-server-python/
├── server.py
├── logger.py
├── tests/
│   ├── __init__.py
│   ├── test_tools.py
│   ├── test_resources.py
│   └── test_server.py
└── pyproject.toml

Paso 1: Setup del test framework

TypeScript:

npm install -D vitest
// vitest.config.ts
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    globals: true,
    testTimeout: 10000,
  },
});

Python:

pip install pytest pytest-asyncio
# pyproject.toml
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]

Paso 2: Helper de testing

// tests/helpers.ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
import { createServer } from "../src/index.js";

export async function createTestClient(): Promise<{ client: Client; cleanup: () => Promise<void> }> {
  const server = createServer();
  const client = new Client({ name: "test-client", version: "1.0.0" });
  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
  await server.connect(serverTransport);
  await client.connect(clientTransport);

  return {
    client,
    cleanup: async () => { await client.close(); },
  };
}

Paso 3: Escribir los tests

Usa los patrones de la cápsula 02 como referencia. Aquí va un template mínimo:

// tests/tools.test.ts
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { createTestClient } from "./helpers.js";
import fs from "fs/promises";
import os from "os";
import path from "path";

let client: Client;
let cleanup: () => Promise<void>;
let tmpDir: string;

beforeAll(async () => {
  ({ client, cleanup } = await createTestClient());
  tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "mcp-test-"));
  // Crea archivos de prueba según tus tools
});

afterAll(async () => {
  await fs.rm(tmpDir, { recursive: true, force: true });
  await cleanup();
});

describe("Tool: [nombre de tu tool]", () => {
  it("happy path — retorna resultado correcto", async () => {
    const result = await client.callTool({
      name: "tu_tool",
      arguments: { /* parámetros válidos */ },
    });
    expect(result.isError).toBeUndefined();
    // Verifica el contenido del resultado
  });

  it("error handling — input inválido", async () => {
    const result = await client.callTool({
      name: "tu_tool",
      arguments: { /* parámetros inválidos */ },
    });
    expect(result.isError).toBe(true);
  });

  it("edge case — [describe el edge case]", async () => {
    // Test para un caso límite específico de tu tool
  });
});
// tests/server.test.ts
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { createTestClient } from "./helpers.js";

let client: Client;
let cleanup: () => Promise<void>;

beforeAll(async () => {
  ({ client, cleanup } = await createTestClient());
});

afterAll(async () => { await cleanup(); });

describe("Server integration", () => {
  it("lista todos los tools registrados", async () => {
    const { tools } = await client.listTools();
    expect(tools.length).toBeGreaterThan(0);
    // Verifica que tus tools específicos aparecen
  });

  it("cada tool tiene descripción y schema", async () => {
    const { tools } = await client.listTools();
    for (const tool of tools) {
      expect(tool.description).toBeTruthy();
      expect(tool.inputSchema).toBeDefined();
    }
  });

  it("resources están accesibles", async () => {
    const { resources } = await client.listResources();
    for (const resource of resources) {
      const result = await client.readResource({ uri: resource.uri });
      expect(result.contents).toHaveLength(1);
    }
  });
});

Paso 4: Ejecutar y verificar

# TypeScript
npm test

# Python
pytest -v

# Output esperado:
# ✓ Tool: [nombre] > happy path
# ✓ Tool: [nombre] > error handling
# ✓ Tool: [nombre] > edge case
# ✓ Resource: [nombre] > datos válidos
# ✓ Resource: [nombre] > formato correcto
# ✓ Server > lista tools
# ✓ Server > tools con descripción
# ✓ Server > resources accesibles
# ✓ Error handling > input inválido
# ✓ Error handling > servicio caído
#
# 10 tests passed

Criterios de éxito del mini-proyecto

□ Al menos 10 tests en total
□ Cubre tools (happy path + error + edge case)
□ Cubre resources (datos + formato)
□ Cubre integration (capabilities + protocolo)
□ Setup/teardown funciona (archivos temporales se limpian)
□ Todos los tests pasan en verde
□ El server tiene logging a stderr
□ npm test / pytest ejecuta todo con un solo comando

Resumen del módulo

A lo largo de las 5 cápsulas de este módulo, aprendiste:

  1. Cápsula 01: La transición de "funciona" a "funciona de forma confiable" — por qué testing es inversión, no burocracia
  2. Cápsula 02: Tests automatizados con Vitest y pytest — unit tests, integration tests, InMemoryTransport
  3. Cápsula 03: Debugging con MCP Inspector, logging a stderr, tracing de performance
  4. Cápsula 04: Configuración de Claude Code — settings, scopes, permisos, verificación paso a paso
  5. Cápsula 05: Guía de troubleshooting para los 13 errores más comunes + mini-proyecto de test suite

Lo que ahora puedes hacer

  • ✅ Escribir tests automatizados para cualquier MCP server
  • ✅ Debuggear problemas con MCP Inspector y logging
  • ✅ Configurar MCP servers en Claude Code con confianza
  • ✅ Diagnosticar y resolver errores comunes sin buscar en Google
  • ✅ Construir un test suite completo como base para proyectos reales

Lo que viene

El Módulo 8 es el proyecto final. Todo converge: construyes un MCP server production-ready conectado a datos reales, con test suite completo, documentación, y demo end-to-end en Claude Code. Los patrones de testing, debugging, y configuración que aprendiste aquí son la base directa del proyecto.

La transición es: "Ya sabes construir, testear, debuggear y conectar MCP servers. Ahora construye uno real de principio a fin."


Recursos adicionales

  1. Vitest Documentation — Framework de testing para TypeScript
  2. pytest Documentation — Framework de testing para Python
  3. MCP Inspector — Herramienta de debugging visual
  4. Claude Code MCP Documentation — Configuración oficial
  5. MCP Specification — Error Handling — Manejo de errores en el protocolo
  6. MCP TypeScript SDK — SDK oficial con ejemplos
  7. MCP Python SDK — SDK oficial para Python
  8. Node.js Debugging Guide — Debugging de aplicaciones Node.js