CommunityProgramación y desarrollogithub.com

Diseño de servidores MCP (Rene-Roscher/mcp-server-design)

Diseña y revisa servidores MCP que los agentes utilizan de forma segura en productos multiinquilino: herramientas como adaptadores en la capa de servicio, un inquilino por conexión vinculado en el token, derechos verificados por llamada, clases de efecto con vista previa y confirmación, protección contra repetición, interruptor de apagado, formas de error, evaluaciones del modelo. Úselo al construir o revisar herramientas o servidores MCP. No apto para usar servidores MCP o diseño REST.

¿Qué es mcp-server-design)?

Esta habilidad proporciona un conjunto completo de reglas y mejores prácticas para diseñar servidores MCP (Model Context Protocol) seguros y robustos, especialmente en entornos multiinquilino. Está dirigida a desarrolladores e ingenieros que construyen o revisan herramientas y servidores MCP. La habilidad detalla dónde deben ubicarse las herramientas en el código (como adaptadores en la capa de servicio), cómo vincular una conexión a un solo inquilino y persona, qué llamadas requieren confirmación humana, y cómo los resultados y errores guían al modelo hacia la siguiente llamada correcta. Incluye invariantes críticos como la protección contra repetición de escritura, la validación de argumentos del lado del servidor, el manejo seguro de texto no confiable de terceros, el control de versiones en actualizaciones, y un interruptor de apagado global. La habilidad se basa en la especificación MCP (revisión 2026-07-28) y en criterios de revisión de conectores, y es aplicable a plataformas como Claude Code, Cursor y otros agentes que usan MCP.

Compatible con✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Rene-Roscher/skills/tree/HEAD/plugins/api/skills/mcp-server-design

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Diseño de servidores MCP

Esta habilidad decide cómo un servidor MCP expone un producto a los agentes: dónde se ubican las herramientas en el código, cómo una conexión se vincula a un inquilino y una persona, qué llamadas necesitan la confirmación de una persona, y cómo los resultados y errores guían al modelo hacia la siguiente llamada correcta. La sintaxis del protocolo sigue la especificación MCP (revisión 2026-07-28 cuando se escribió esto) y la documentación del SDK. El ámbito de las consultas, los catálogos de permisos y los formatos de identificadores provienen de tenant-isolation, authorization-rbac y dual-key-identifiers. El fallo que esto evita: un modelo que elige el inquilino incorrecto, envía un correo a un cliente dos veces o actúa sobre texto que un tercero introdujo, porque el servidor dejó una puerta al modelo, al cliente o a una descripción de herramienta.

Se aplica cuando / no se aplica

  • Se aplica: construir un servidor MCP para un producto, agregar o revisar una herramienta, elegir cómo las credenciales MCP se vinculan a inquilinos y personas, herramientas que notifican a clientes, localizan personas, silencian alertas o eliminan, escrituras duplicadas después de reintentos del cliente, formas de error y resultado, nombres y descripciones de herramientas, conjuntos de herramientas para modelos pequeños, evaluaciones del uso de herramientas.
  • No se aplica: configurar un cliente para usar el servidor MCP de otra persona, diseño de endpoints REST o GraphQL, chatbots y comandos de barra que no son MCP, escritura de indicaciones para el asistente propio de un producto, límites de tasa en rutas que no son MCP.

Reglas

Lo más importante primero. Los ejemplos usan un sistema de reservas: reservas, clientes, técnicos.

  • [invariante] El servidor impone lo que no debe salir mal; las descripciones y anotaciones solo orientan. Cada puerta de abajo se ejecuta en el código del servidor en cada llamada. Por qué: las anotaciones son sugerencias, y existen clientes que auto-aprueban o nunca preguntan. Prueba: llama a cada herramienta protegida desde un cliente JSON-RPC sin formato que ignora las sugerencias; cada protección sigue disparándose. Fuente: Esquema MCP 2026-07-28, ToolAnnotations: "Los clientes nunca deben tomar decisiones de uso de herramientas basadas en ToolAnnotations recibidas de servidores no confiables" (https://modelcontextprotocol.io/specification/2026-07-28/schema, verificado 2026-10-31).
  • [invariante] Las herramientas son un segundo adaptador en la capa de servicio: llaman a los mismos servicios de dominio que la interfaz de usuario y la API, nunca a su propia API HTTP en un bucle y nunca directamente al ORM. Las comprobaciones que protegen los datos (límites, listas blancas de URL contra SSRF, propiedad de los registros referenciados) residen en el servicio, no solo en un controlador o solicitud de formulario. Una ruta de escritura por función. Por qué: un adaptador delgado hereda cada comprobación que solo ejecutaron sus adaptadores hermanos. Prueba: una prueba de arquitectura falla en llamadas de persistencia en clases de herramienta; por herramienta de escritura, una entrada que la API rechaza es rechazada por la herramienta. Fuente: reproducible con la prueba anterior.
  • [invariante] Una conexión, un inquilino: el inquilino está vinculado en la credencial cuando se crea la conexión, ningún esquema de herramienta tiene un parámetro de inquilino, un argumento como workspace_id se rechaza como desconocido, y cada identificador se resuelve dentro del inquilino vinculado. Una credencial que sirve a varios inquilinos lleva el inquilino en el transporte (ruta o cabecera) y lo verifica contra la credencial, nunca en un argumento de herramienta. Por qué: un parámetro de inquilino permite que el modelo elija el inquilino. Prueba: por herramienta con una referencia, pasa el identificador válido de otro inquilino; espera not_found y una fila sin cambios. Fuente: Especificación de herramientas MCP, el conjunto de herramientas "PUEDE variar según la autorización presentada en la solicitud" (https://modelcontextprotocol.io/specification/2026-07-28/server/tools); mejores prácticas de seguridad, estado clave por un ID de usuario "derivado del token verificado en lugar de proporcionado por el cliente" (https://modelcontextprotocol.io/docs/2026-07-28/tutorials/security/security_best_practices); ambos verificados 2026-10-31.
  • [invariante] Los derechos se cortan en vivo: derechos efectivos = derechos del token ∩ derechos actuales de la persona que creó la conexión, calculados en cada tools/list y tools/call. Los derechos del token están limitados en la creación, una persona eliminada o desactivada finaliza la conexión, la revocación se ejecuta a través de un servicio que audita. Por qué: los derechos congelados en un token sobreviven a un cambio de rol. Prueba: elimina el rol del creador, llama a una herramienta de escritura; rechazada en la siguiente llamada. Fuente: Especificación de herramientas MCP, los servidores DEBEN "Implementar controles de acceso adecuados" (verificado 2026-10-31).
  • [invariante] Sin usuario ambiental en solicitudes MCP: el middleware vincula un contexto de conexión (inquilino, creador, derechos en vivo) y deja vacía la ranura de usuario actual del framework; las herramientas leen el actor del contexto, los servicios toman el actor como parámetro. Por qué: con el propietario establecido como usuario actual, una verificación de política errante en una herramienta pasa porque los propietarios pueden hacer todo; sin usuario falla de forma cerrada. Prueba: una prueba de arquitectura prohíbe los ayudantes de puerta, política y usuario actual en el código de la herramienta; una verificación de política agregada deliberadamente hace que la llamada falle. Fuente: reproducible con la prueba anterior.
  • [invariante] Cada herramienta tiene exactamente una clase de efecto: lectura, escritura silenciosa, notificar (llega a los clientes o cambia lo que ven), despertar (localiza o llama a una persona), alertar (silencia, pausa o redirige alertas), eliminar. Deriva la clase de la ruta del código (eventos, oyentes, observadores, trabajos, correos), no del nombre, y divide una herramienta que abarca dos. Por qué: las sugerencias, el cubo de tasa, el deber de vista previa y el cambio de conexión siguen de la clase. Prueba: con cola, correo y simulaciones de eventos, las herramientas silenciosas no envían ninguna notificación. Fuente: Criterios de revisión de conectores, las herramientas de lectura/escritura comodín se rechazan y las escrituras se dividen por tipo de acción (https://claude.com/docs/connectors/building/review-criteria, verificado 2026-10-31).
  • [invariante] Notificar, despertar, alertar y eliminar toman dos pasos. La herramienta solo prepara: almacena una acción pendiente (conexión, herramienta, argumentos normalizados y resumen, hash del contenido que saldrá, caducidad) y devuelve PREVIEW, un confirm_token y un confirm_summary escrito por el servidor. Solo confirm_action se ejecuta, después de verificar conexión, no usado, no caducado, no rechazado, resumen exacto, un bloqueo, derechos y conmutadores en vivo, y una preparación fresca cuyo hash de contenido aún coincide (si no, nueva vista previa, token antiguo muerto). Por qué: ni el modelo ni el cliente preguntan de manera confiable a una persona, y el texto aprobado puede cambiar entre medias. Prueba: la vista previa sola no envía nada; los tokens reutilizados, caducados, extraños y de resumen incorrecto fallan; una edición entre pasos produce changed_since_preview. Fuente: Especificación de herramientas MCP, "SIEMPRE debería haber un humano en el circuito con la capacidad de denegar invocaciones de herramientas"; requisitos del servidor MRTR: el estado repetido por un cliente es "entrada controlada por el atacante", vinculado al principal, caducidad corta y resumen de parámetros, uso único aplicado del lado del servidor (https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/mrtr); ambos verificados 2026-10-31.
  • [invariante] Protección contra repetición en cada escritura silenciosa: huella digital = conexión + herramienta + SHA-256 del JSON canónico de los argumentos normalizados (claves ordenadas en cada nivel); un acierto dentro de la ventana devuelve el primer resultado con replayed: true y no ejecuta nada. Una idempotency_key opcional está vinculada al resumen del argumento. La caché contiene la salida del presentador, nunca secretos de un solo uso. Por qué: una transmisión rota pierde la solicitud en curso y los clientes DEBEN reenviarla con una nueva ID, por lo que una escritura que se ejecutó llega de nuevo. Prueba: misma llamada dos veces con diferentes ID de JSON-RPC; una fila, segundo resultado replayed: true. Fuente: Registro de cambios MCP 2026-07-28, cambio mayor 9 (https://modelcontextprotocol.io/specification/2026-07-28/changelog); Claude Code repite una llamada pendiente a herramientas marcadas idempotentHint o readOnlyHint una vez en una nueva conexión (https://code.claude.com/docs/en/mcp); ambos verificados 2026-10-31.
  • [invariante] Toda herramienta de creación tiene una herramienta de actualización hermana y un guardia de duplicados en una clave de negocio. Un acierto devuelve un error que nombra el identificador existente y la herramienta de actualización; crear de todos modos necesita separate: true y una razón. Los guardias leen columnas síncronas (created_by, created_at), nunca un registro de auditoría en cola. Las herramientas de edición no tienen parámetro de notificar. Por qué: cuando se le pide "mejora el texto", un modelo crea un segundo objeto a menos que se le detenga. Prueba: crea dos veces con la misma clave; una fila, un error que la nombra. Fuente: reproducible con la prueba anterior.
  • [invariante] Las actualizaciones llevan la versión que el llamante leyó: los objetos y mensajes en los resultados tienen una version (hash de contenido), las herramientas de actualización y edición la requieren, una obsoleta devuelve conflict con el estado actual. Por qué: un agente que trabaja a partir de una lectura antigua sobrescribe la edición de un colega. Prueba: lee, cambia la fila en otro lugar, actualiza con la versión antigua; conflict, fila sin cambios. Fuente: reproducible con la prueba anterior.
  • [invariante] Las herramientas por lotes toman identificadores explícitos, o un filtro más un expected_count requerido; cuando el recuento de coincidencias difiere, devuelve las coincidencias y no ejecuta nada. Por qué: un filtro más amplio de lo que el modelo esperaba multiplica un error. Prueba: un filtro que coincide con 12 reservas con expected_count: 3; error enumera 12, nada cambió. Fuente: reproducible con la prueba anterior.
  • [invariante] Valida cada argumento en el servidor con las reglas propias de la herramienta y rechaza campos desconocidos con la lista de permitidos. Por qué: los SDK no siempre verifican los argumentos contra inputSchema, y un campo ignorado permite que el modelo crea que estableció algo. Prueba: un campo adicional devuelve la lista permitida y no escribe nada. Fuente: Especificación de herramientas MCP, los servidores DEBEN "Validar todas las entradas de la herramienta" (verificado 2026-10-31).
  • [invariante] El texto de terceros en los resultados no es confiable: el texto que un cliente, usuario final, sistema externo o carga puede escribir (notas, cuerpos de correo, cargas útiles de webhook, mensajes de error remotos) se trunca, se despoja de caracteres de control y se devuelve bajo untrusted_text con una nota de no seguir instrucciones en él; los cuerpos sin procesar permanecen fuera. Las descripciones describen la herramienta y nunca le dicen al modelo cómo comportarse. Por qué: el texto inyectado que dirige una herramienta con efecto externo es el riesgo principal; el paso de vista previa es la segunda línea. Prueba: un canario de inyección en cada campo de terceros aparece solo dentro del envoltorio. Fuente: Especificación de herramientas MCP, los servidores DEBEN "Sanitizar las salidas de la herramienta"; los criterios de revisión de conectores rechazan patrones de inyección de indicaciones en las descripciones; ambos verificados 2026-10-31.
  • [invariante] Los resultados no llevan secretos y solo los datos personales que la tarea necesita: presentadores con listas de campos explícitas, nunca un serializador de registro completo; los registros contienen nombre de la herramienta, resultado e identificadores públicos, nunca argumentos. Por qué: cada resultado va al proveedor de modelo del usuario. Prueba: llena cada campo secreto de un fixture con un valor señuelo, llama a cada herramienta de lectura, grep los resultados y registros; ningún señuelo. Fuente: reproducible con la prueba anterior; mantenimiento de registros en gdpr-engineering.
  • [invariante] Los fallos de negocio, validación y permiso son errores de ejecución de la herramienta (isError: true) cuyo texto comienza con Error:, nombra la causa, la solución y la herramienta correcta con identificadores reales, y termina con una línea de detalles JSON. "No encontrado" apunta a una herramienta find_, nunca a crear. Una herramienta oculta por derechos, conjunto de herramientas o plan responde con esa razón. Por qué: el modelo se autocorrige solo con errores que ve y entiende. Prueba: cada ruta de error en el conjunto de contratos comienza con Error: y tiene una línea JSON analizable. Fuente: Esquema MCP, CallToolResult.isError: los errores de herramienta "DEBEN informarse dentro del objeto de resultado... De lo contrario, el LLM no podría ver que ocurrió un error y autocorregirse"; los criterios de revisión de conectores fallan en respuestas genéricas de "Error interno del servidor"; ambos verificados 2026-10-31.
  • [invariante] El bloque de texto y structuredContent llevan el mismo JSON, y un outputSchema declarado cubre cada resultado estructurado, incluidos los errores. Por qué: los clientes difieren en qué canal llega al modelo, y DEBERÍAN validar contra un esquema declarado. Prueba: para cada resultado en el conjunto, el texto analizado es igual a structuredContent. Fuente: Especificación de herramientas MCP, una herramienta con contenido estructurado "TAMBIÉN DEBERÍA devolver el JSON serializado en un bloque TextContent" (verificado 2026-10-31).
  • [invariante] La hora y el dinero son trabajo del servidor: las horas llegan como hora local más zona IANA o ISO 8601 con desplazamiento, nunca sin; las horas locales inexistentes y ambiguas se rechazan con las opciones; cada hora en un resultado muestra el día de la semana, la hora local con zona, UTC y la distancia desde ahora. Los totales, impuestos y redondeos se calculan en el servidor a partir de cadenas decimales o unidades menores enteras. Por qué: los errores de un día de diferencia y horario de verano son invisibles sin el día de la semana. Prueba: 2026-03-29 02:30 en Europe/Berlin es rechazado; 2026-10-25 02:30 devuelve ambos desplazamientos. Fuente: reproducible con la prueba anterior.
  • [invariante] Un interruptor de apagado en un solo lugar: una bandera global, establecida por un comando CLI y una palanca de administrador que ambos auditan, hace que cada llamada MCP devuelva 503 con Retry-After mientras las conexiones permanecen; un interruptor por inquilino con su valor predeterminado definido una vez permite que un administrador de inquilino desactive el acceso del agente. Por qué: un incidente necesita un movimiento que detenga todas las escrituras del agente. Prueba: bandera activada, llama a una herramienta de escritura; 503, sin fila, sin trabajo. Fuente: reproducible con la prueba anterior.
  • [invariante] Cada escritura MCP se audita con canal mcp, conexión y nombre del cliente, establecidos en el punto de entrada como columnas, y list_recent_changes muestra lo que esta conexión cambió. Por qué: los inquilinos ven lo que hizo un agente, y un agente que perdió el contexto encuentra su borrador en lugar de crear otro. Prueba: cada herramienta de escritura produce exactamente una fila de auditoría con canal mcp. Fuente: reproducible con la prueba anterior.
  • [invariante] Las credenciales MCP y API no se cruzan: los tokens MCP tienen su propio tipo y prefijo, se almacenan hasheados, se muestran una vez, y son rechazados por cada ruta API; los tokens API y las cookies de sesión son rechazados por el endpoint MCP. Con OAuth, acepta solo tokens emitidos para este servidor y nunca pases uno. Valida Origin. Por qué: los tokens MCP están en texto plano en los archivos de configuración del cliente. Prueba: cada emparejamiento devuelve 401; un Origin incorrecto devuelve 403. Fuente: Especificación de autorización MCP, los servidores "NO DEBEN aceptar ni transitar ningún otro token" (https://modelcontextprotocol.io/specification/2026-07-28/server/authorization, verificado 2026-10-31).

Estrategias de prueba

  • Prueba de herramientas desde un cliente JSON-RPC sin formato para verificar que las protecciones se disparen independientemente de las sugerencias.
  • Prueba de arquitectura para prohibir llamadas de persistencia y ayudantes de usuario actual en clases de herramienta.
  • Prueba con identificadores de inquilino entrecruzados para verificar el ámbito correcto.
  • Prueba de eliminación de roles para verificar cortes de derechos en vivo.
  • Prueba de inyección de texto no confiable con canarios.
  • Prueba de repetición de llamadas para verificar protección contra repetición.
  • Prueba de creación duplicada para verificar guardias de clave de negocio.
  • Prueba de actualización con versión obsoleta para verificar control de concurrencia.
  • Prueba de argumentos desconocidos para verificar validación de esquema.
  • Prueba del interruptor de apagado.
  • Prueba de filtración de secretos.
  • Prueba de formato de error.
  • Prueba de formato de tiempo y dinero.

Skills relacionados