Input schema, output schema y Zod: contratos claros para servidores MCP

Aprende a diseñar contratos útiles para tools MCP con `inputSchema`, `outputSchema`, Zod y anotaciones que ayuden al host sin convertir tu servidor en una caja negra difícil de usar.

Cuando un servidor MCP falla en uso real, muchas veces el problema no está en el transporte ni en el SDK, sino en el contrato. La tool existe, pero sus parámetros son ambiguos, su salida es imprevisible o sus anotaciones no ayudan a tomar decisiones.

En MCP, un schema no es burocracia. Es la forma de decirle al host y al modelo qué espera la operación, qué devuelve y qué comportamiento cabe esperar de ella.

Zod encaja especialmente bien en TypeScript porque permite definir validación y derivar la forma estructural del contrato en el mismo sitio donde desarrollas la lógica.

Un `inputSchema` útil no intenta cubrir todos los casos imaginables. Intenta reducir ambigüedad y evitar parámetros inventados, opcionales sin propósito o estructuras imposibles de explicar al modelo.

  • Una buena tool se entiende antes de ejecutarse.
  • Cuando el host descubre una tool, necesita algo más que un nombre. Necesita una descripción clara, un schema de entrada razonable y, si aplica, señales sobre si la operación es de solo lectura, destructiva o idempotente.
  • Si el contrato es borroso, el modelo tendrá más margen para usar mal la herramienta: argumentos inventados, combinaciones absurdas o llamadas innecesarias.
  • Por eso conviene diseñar el contrato antes de meterte en detalles de implementación.
  • Cada parámetro debe tener una razón de existir.

Contrato primero

Una buena tool se entiende antes de ejecutarse.

Cuando el host descubre una tool, necesita algo más que un nombre. Necesita una descripción clara, un schema de entrada razonable y, si aplica, señales sobre si la operación es de solo lectura, destructiva o idempotente.

Si el contrato es borroso, el modelo tendrá más margen para usar mal la herramienta: argumentos inventados, combinaciones absurdas o llamadas innecesarias.

Por eso conviene diseñar el contrato antes de meterte en detalles de implementación.

Input schema

Cada parámetro debe tener una razón de existir.

En el SDK de TypeScript, una tool puede registrar `inputSchema` usando Zod. Ese paso no solo valida: también ayuda a describir la interfaz de la tool al cliente.

Un buen `inputSchema` suele ser pequeño, explícito y estable. Si una tool pide demasiados campos, muchas veces no está modelada al nivel correcto.

También conviene usar descripciones claras y evitar opcionales innecesarios. Si el parámetro no cambia la operación de forma real, probablemente sobra.

Output y respuesta

La salida debe ser útil para leer, validar y combinar.

Muchas tools principiantes devuelven solo texto libre. A veces basta, pero cuando el resultado va a reutilizarse, un `outputSchema` y una respuesta estructurada ahorran mucho dolor.

La idea no es volver rígida cada tool, sino hacer predecible lo importante: identificadores, estados, campos clave y errores comprensibles.

Una salida estructurada mejora tanto la depuración humana como la orquestación entre varias tools.

Annotations útiles

Las anotaciones no son seguridad, pero sí mejoran mucho la experiencia del cliente.

La especificación actual define hints como `readOnlyHint`, `destructiveHint`, `idempotentHint` y `openWorldHint`. Son pistas para el cliente sobre el comportamiento de la tool.

No deben tratarse como controles de seguridad, pero sí como una capa importante de claridad operativa y UX.

Una tool de consulta puede marcar `readOnlyHint: true`; una tool que crea registros quizá necesite `destructiveHint: false` y `idempotentHint: false` según su comportamiento.

Caso guiado

Imagina una tool llamada `create_note` que recibe diez campos opcionales y devuelve un texto genérico como 'hecho'. Ese contrato obliga al modelo a adivinar demasiado y al equipo a depurar a ciegas.

Ahora compárala con una versión que recibe `title` y `body`, devuelve `noteId`, `title` y `createdAt`, y además marca bien si es destructiva o no. La segunda no es solo más elegante: es más usable, más verificable y más mantenible.

Ese es el cambio de nivel que buscamos en MCP: contratos que ayudan a que el servidor trabaje con el modelo, no contra él.

Errores frecuentes

Práctica

Diseña una tool de ejemplo con `inputSchema` breve, una salida estructurada mínima y una anotación coherente. La evidencia de logro observable es que otra persona pueda explicar la tool sin leer tu implementación interna.

El criterio de corrección es claro: nombre específico, inputs justificables, salida útil y ausencia de parámetros decorativos.

MCP
05

Input schema, output schema y Zod: contratos claros para servidores MCP

Aprende a diseñar contratos útiles para tools MCP con `inputSchema`, `outputSchema`, Zod y anotaciones que ayuden al host sin convertir tu servidor en una caja negra difícil de usar.

Código del tema: inputSchema + outputSchema + zod + annotations

📘 Teoría

Contrato primero

Una buena tool se entiende antes de ejecutarse.

1

Cuando el host descubre una tool, necesita algo más que un nombre. Necesita una descripción clara, un schema de entrada razonable y, si aplica, señales sobre si la operación es de solo lectura, destructiva o idempotente.

2

Si el contrato es borroso, el modelo tendrá más margen para usar mal la herramienta: argumentos inventados, combinaciones absurdas o llamadas innecesarias.

3

Por eso conviene diseñar el contrato antes de meterte en detalles de implementación.

Input schema

Cada parámetro debe tener una razón de existir.

1

En el SDK de TypeScript, una tool puede registrar `inputSchema` usando Zod. Ese paso no solo valida: también ayuda a describir la interfaz de la tool al cliente.

2

Un buen `inputSchema` suele ser pequeño, explícito y estable. Si una tool pide demasiados campos, muchas veces no está modelada al nivel correcto.

3

También conviene usar descripciones claras y evitar opcionales innecesarios. Si el parámetro no cambia la operación de forma real, probablemente sobra.

Tool con inputSchema claro
server.registerTool(
  'search_ticket',
  {
    title: 'Buscar ticket',
    description: 'Busca un ticket de soporte por identificador.',
    inputSchema: {
      ticketId: z.string().describe('Identificador exacto del ticket')
    }
  },
  async ({ ticketId }) => {
    return {
      content: [
        { type: 'text', text: `resultado para ${ticketId}` }
      ]
    };
  }
);

Output y respuesta

La salida debe ser útil para leer, validar y combinar.

1

Muchas tools principiantes devuelven solo texto libre. A veces basta, pero cuando el resultado va a reutilizarse, un `outputSchema` y una respuesta estructurada ahorran mucho dolor.

2

La idea no es volver rígida cada tool, sino hacer predecible lo importante: identificadores, estados, campos clave y errores comprensibles.

3

Una salida estructurada mejora tanto la depuración humana como la orquestación entre varias tools.

Salida con estructura reconocible
server.registerTool(
  'get_project_status',
  {
    title: 'Estado del proyecto',
    description: 'Devuelve estado actual y responsable.',
    inputSchema: {
      projectId: z.string()
    },
    outputSchema: {
      projectId: z.string(),
      status: z.enum(['todo', 'doing', 'done']),
      owner: z.string()
    }
  },
  async ({ projectId }) => ({
    structuredContent: {
      projectId,
      status: 'doing',
      owner: 'ana'
    },
    content: [
      { type: 'text', text: `Proyecto ${projectId} en curso, responsable ana.` }
    ]
  })
);

Annotations útiles

Las anotaciones no son seguridad, pero sí mejoran mucho la experiencia del cliente.

1

La especificación actual define hints como `readOnlyHint`, `destructiveHint`, `idempotentHint` y `openWorldHint`. Son pistas para el cliente sobre el comportamiento de la tool.

2

No deben tratarse como controles de seguridad, pero sí como una capa importante de claridad operativa y UX.

3

Una tool de consulta puede marcar `readOnlyHint: true`; una tool que crea registros quizá necesite `destructiveHint: false` y `idempotentHint: false` según su comportamiento.

Caso guiado

Imagina una tool llamada `create_note` que recibe diez campos opcionales y devuelve un texto genérico como 'hecho'. Ese contrato obliga al modelo a adivinar demasiado y al equipo a depurar a ciegas.

Ahora compárala con una versión que recibe `title` y `body`, devuelve `noteId`, `title` y `createdAt`, y además marca bien si es destructiva o no. La segunda no es solo más elegante: es más usable, más verificable y más mantenible.

Ese es el cambio de nivel que buscamos en MCP: contratos que ayudan a que el servidor trabaje con el modelo, no contra él.

Errores frecuentes

1

Schemas inflados

Demasiados parámetros suelen indicar una tool mal separada o demasiado ambiciosa.

2

Texto sin estructura

Devuelve algo legible, pero difícil de reutilizar o validar en cadenas de tools.

3

Hints incoherentes

Marcar una tool como read-only cuando realmente modifica datos genera fricción y confianza rota.

Práctica

Diseña una tool de ejemplo con `inputSchema` breve, una salida estructurada mínima y una anotación coherente. La evidencia de logro observable es que otra persona pueda explicar la tool sin leer tu implementación interna.

El criterio de corrección es claro: nombre específico, inputs justificables, salida útil y ausencia de parámetros decorativos.

🧭 Visuales clave

Mapa de contrato para una tool MCP

Esquema visual de las piezas que componen un contrato claro para una tool MCP.

Diagrama del contrato de una tool MCP con nombre, descripción, input schema, output schema y annotations.

🧪 Aprende probando

Ejemplo Ejemplo guiado: contrato pequeño y claro Observa cómo una tool mejora cuando su contrato reduce ambigüedad.

🏁 Retos

Reto Reto: diseña un inputSchema útil Escribe el bloque `inputSchema` de una tool que consulta un documento por `docId` y filtro opcional de versión.

¿Qué es esto?

Soy Cristian Eslava y a veces hago webs para procrastinar yo y vosotros 😉.

Esta la hice en febrero de 2026 para facilitar el aprendizaje de mis alumnxs. Aprender desarrollo web practicando. La idea es que crezca semanalmente con nuevos temas, tests y retos.

Inspirado en MDN, en W3Schools, en Codepen, en el crack de Manz y en mil sitios de documentación sobre desarrollo web. Quería aportar además de bloques teóricos con ejemplos, la gamificación de los retos y el sistema de test que ya tenía en culTest .

Si te gustó, si no te gustó, si quieres saludarme, o invitarme a 🍻 no dudes en escribirme en cristianeslava@gmail.com .