Anotaciones de herramientas MCP y límites de aprobación
Las anotaciones de herramientas MCP pueden describir el comportamiento esperado, pero solo las pruebas de los efectos secundarios reales deberían determinar qué acciones del agente requieren aprobación.

Las anotaciones de herramientas MCP son una documentación útil. También son un lugar muy fácil para dar una apariencia ordenada a un sistema de aprobación inseguro. Si un cliente trata readOnlyHint, destructiveHint o idempotentHint como una autorización, el autor del servidor ha terminado escribiendo la política de aprobación del usuario sin demostrar que la implementación la merece.
Eso está al revés. Una anotación puede ayudar a explicar una solicitud, ordenar una lista de herramientas o sugerir un valor predeterminado razonable a la persona que revisa una acción. La aprobación debe depender de la solicitud que ejecutará el servidor, la credencial que utilizará, el objetivo al que llegará y los efectos secundarios que pueda desencadenar. He visto demasiadas integraciones llamar a un endpoint llamado get, devolver un objeto JSON correcto y aun así crear trabajo en otro lugar.
La diferencia importa especialmente con los agentes, porque reintentan, combinan herramientas y actúan a un ritmo que vuelve costoso cualquier pequeño error de clasificación. Una herramienta segura cuando se usa una vez puede ser insegura dentro de un bucle. Una herramienta de solo lectura frente a una API puede convertirse en un mecanismo de exportación frente a otra. Una herramienta que parece idempotente puede crear trabajo duplicado cuando un tiempo de espera oculta el primer resultado correcto.
Las anotaciones describen el comportamiento, no conceden autoridad
La especificación de herramientas del Model Context Protocol describe las anotaciones como indicaciones sobre el comportamiento de una herramienta. Esa formulación es deliberada: un cliente puede usarlas para mejorar su interfaz, pero no puede utilizarlas de forma segura como decisión de seguridad sin verificar la declaración.
Los tres campos en cuestión describen afirmaciones distintas:
readOnlyHint: trueafirma que la herramienta no modifica su entorno.destructiveHint: trueafirma que la herramienta puede realizar actualizaciones destructivas.idempotentHint: trueafirma que las llamadas repetidas con los mismos argumentos no producen efectos adicionales en el entorno.
Esas afirmaciones no cubren todo el riesgo de una llamada. Una herramienta puede leer una base de datos completa de clientes, enviar el resultado al agente y marcarse honestamente como de solo lectura. Otra puede escribir únicamente una marca de tiempo de acceso, algo que parece menor hasta que esa marca cambia la retención, la facturación o un registro de incidentes. La idempotencia no dice nada sobre si el primer efecto era aceptable.
La especificación también establece valores predeterminados conservadores para estos campos. readOnlyHint tiene false como valor predeterminado. idempotentHint también tiene false. destructiveHint tiene true, y solo tiene un significado útil cuando la herramienta no es de solo lectura. No sustituyas esos valores por una regla casera como «si faltan metadatos, es suficientemente segura». La ausencia de metadatos suele significar que el autor del servidor no reflexionó sobre la clasificación.
Hay otro punto incómodo: un servidor bienintencionado puede equivocarse. Un desarrollador añade readOnlyHint: true porque el controlador ejecuta un SELECT, pero después una capa de biblioteca actualiza un token, escribe una entrada de caché o invoca un hook de solicitud. La anotación sigue siendo true mucho después de que cambie el comportamiento. Nadie pretendía engañar al cliente, pero el cliente toma una mala decisión si aprueba automáticamente la acción.
Una lectura no implica un resultado inofensivo
Una operación de solo lectura puede divulgar datos, consumir un recurso escaso o activar un comportamiento en un servicio remoto. Tratar «no escribe» como «no necesita aprobación» es un error de categoría.
Considera una herramienta llamada get_build_log que acepta el identificador de un trabajo. El servidor lee los datos del sistema de compilación y devuelve el resultado. Puede declarar correctamente readOnlyHint: true. Sin embargo, el registro puede contener código fuente, detalles del entorno, URLs de descarga firmadas o credenciales que otro sistema haya impreso por accidente. Enviar esa respuesta a un agente autónomo cambia quién puede utilizar esa información, aunque la base de datos del sistema de compilación permanezca intacta.
El mismo problema aparece en las API administrativas. get_user podría devolver códigos de recuperación. list_invoices podría exponer datos bancarios. search_documents puede convertirse en una extracción masiva cuando un agente aumenta el tamaño de página o recorre todos los prefijos. El efecto secundario es la divulgación, y las anotaciones no tienen ningún campo para indicar la sensibilidad de esa divulgación.
Las operaciones de lectura también pueden modificar el servicio remoto. Algunas API actualizan last_accessed_at, consumen un token de descarga de un solo uso, registran una vista previa o emiten una consulta con coste por uso. Un fallo de caché puede activar un servicio posterior costoso. Estos efectos no vuelven peligrosa toda lectura, pero hacen indefendible una regla de aprobación de solo lectura sin más matices.
Clasifica la llamada en dos ejes separados: si modifica un sistema y qué puede revelar o provocar fuera de ese sistema. Un sondeo de estado de bajo riesgo y una exportación masiva pueden no modificar nada. No deberían recibir el mismo tratamiento de aprobación.
Un registro práctico de revisión debería nombrar el límite de datos con palabras sencillas. «Lee el estado del despliegue del proyecto A» se puede revisar. «Llama a get_status» no. La segunda frase oculta el objetivo, el alcance, la cuenta y el hecho de que un método con un nombre similar puede significar algo distinto en otro servidor.
Prueba el controlador contra un objetivo desechable
No puedes establecer la seguridad de una anotación leyendo el nombre de una herramienta o su esquema de entrada. Ejecuta el servidor en un entorno donde puedas observar su solicitud, su respuesta y el estado antes y después de la llamada.
Empieza con una cuenta de prueba que contenga registros que puedas perder. Asígnale una credencial API independiente y dirige los webhooks de notificación a un endpoint de captura. Registra las solicitudes salientes del servidor, el estado de la base de datos si tienes control sobre ella, los eventos de auditoría, los correos electrónicos, los trabajos en cola y los contadores de facturación o uso. El cuerpo de la respuesta es una evidencia, pero no constituye todo el registro.
Usa una matriz de pruebas pequeña para cada herramienta que pueda influir en las aprobaciones:
- Llámala una vez con una entrada válida normal y guarda el estado completo antes y después.
- Vuelve a llamarla con una entrada idéntica byte por byte y compara todos los efectos observables.
- Llámala con un recurso inexistente, una operación ya completada y un campo no válido.
- Interrumpe el cliente después de que el servidor reciba la solicitud y vuelve a intentar la misma llamada.
- Ejecuta dos llamadas idénticas al mismo tiempo si los agentes pueden emitirlas de forma concurrente.
El caso del tiempo de espera detecta un fallo frecuente. Supón que create_ticket envía la solicitud para crear un ticket y después se interrumpe la conexión antes de que el servidor responda. El agente ve un error y reintenta. Si el sistema de tickets no tiene un token de idempotencia, la herramienta crea dos tickets. Marcar el controlador como idempotente porque su código acepta dos veces la misma entrada no cambia el resultado remoto.
Registra los resultados con un formato que obligue a inspeccionar los efectos en lugar de confiar en una respuesta correcta:
case: retry after response timeout
request: {"title":"rotate staging certificate","request_id":"test-104"}
first call: transport timeout after request received
second call: 201 {"ticket":"842"}
remote records: ["841", "842"]
result: not idempotent without a remote idempotency mechanism
El request_id de ese ejemplo solo ayuda si la API remota lo almacena y lo aplica. Un identificador generado por el cliente que el servidor ignora es simple decoración. Comprueba que se aplica repitiendo el identificador exacto y verificando si el sistema remoto devuelve la operación original en lugar de crear otra.
Conserva las pruebas junto al servidor. La deriva de las anotaciones suele llegar con un cambio de código, una actualización de dependencia o un endpoint nuevo. Una prueba aprobada que compara la indicación declarada con el comportamiento observable vale más que un comentario junto a la definición de la herramienta.
Las afirmaciones de solo lectura fallan en los límites
El falso readOnlyHint más fácil aparece cuando solo se observa la consulta principal de la base de datos. El límite relevante incluye todos los servicios que llama el controlador y todas las acciones causadas por su respuesta.
Piensa en una herramienta del servidor que obtiene un documento. Su solicitud principal es GET /documents/42, pero el controlador puede intercambiar primero un token de actualización, emitir una URL temporal de descarga, actualizar una caché local y escribir un evento de acceso. Cada operación puede fallar de una manera distinta. Cada una puede estar sujeta a credenciales y requisitos de auditoría diferentes.
No aceptes el argumento de que una escritura es demasiado pequeña para contar. Las escrituras pequeñas crean sus propios modos de fallo. Una marca de última visualización puede influir en la retención. Una caché puede conservar contenido después de que debería haber terminado el acceso. Un evento de acceso puede notificar al propietario. Un contador de uso puede hacer que una cuenta pase a un nivel de pago. Pregunta si la escritura cambia un hecho que otra persona, proceso o factura vaya a observar. Si es así, documéntalo.
El comportamiento activado por la respuesta merece el mismo análisis. Una herramienta que devuelve un enlace firmado puede hacer que el agente lo descargue más tarde. Una herramienta que devuelve un comando ejecutable puede llevar al agente a ejecutarlo en otro canal. La primera herramienta sigue siendo de solo lectura en un sentido estricto, pero una pantalla de aprobación que diga «lectura segura» ofrece a la persona una imagen falsa de la siguiente acción que el agente puede realizar.
Un buen servidor separa las operaciones cuando los riesgos son distintos. get_document_metadata puede seguir siendo una llamada de inspección limitada. create_download_link debería ser su propia herramienta porque crea una capacidad de acceso, aunque los bytes del documento subyacente no cambien. Esa división ayuda a los agentes a elegir correctamente y ofrece a quienes revisan una descripción que realmente pueden aprobar.
Lo destructivo depende de la reversibilidad, no de una lista de verbos
destructiveHint debería reflejar si una llamada puede provocar actualizaciones perjudiciales difíciles de revertir, no si el nombre de la herramienta contiene delete. Los equipos se equivocan en ambas direcciones.
Algunos verbos evidentes son reversibles en un sistema y permanentes en otro. archive puede limitarse a ocultar un registro o iniciar un temporizador de purga. disable_user puede conservar todas las autorizaciones y archivos o revocar el acceso de forma que deje aislado un proceso automatizado. replace_config puede actualizar un borrador o provocar un despliegue inmediato en producción. El controlador necesita información específica del objetivo que un único booleano no puede expresar.
Algunas herramientas con nombres inofensivos son claramente destructivas. sync_members puede eliminar las cuentas que no aparezcan en la lista enviada. apply_labels puede sobrescribir una taxonomía mantenida cuidadosamente. reconcile puede corregir un libro mayor externo con asientos que nadie debería crear a la ligera. El autor de un servidor que marca estas herramientas como no destructivas porque la API puede deshacerlas técnicamente está ocultando el coste operativo de la reparación.
Considera la reversibilidad como una secuencia, no como una casilla. Pregunta quién puede deshacer el resultado, qué pruebas necesita, durante cuánto tiempo está disponible la posibilidad de deshacerlo y si un proceso posterior consume el cambio antes de que nadie pueda revertirlo. Si una persona tiene que reconstruir la intención a partir de los registros después de una actualización masiva, la acción merece una clasificación destructiva aunque la API ofrezca un método inverso.
La mala recomendación más extendida es reservar la aprobación únicamente para la eliminación explícita. Es popular porque mantiene a los agentes en movimiento y hace que una demostración parezca fluida. Falla en producción porque los cambios destructivos suelen ser sustituciones, revocaciones, envíos o conciliaciones. Aprueba el cambio de estado relevante, no el vocabulario utilizado para describirlo.
Para una acción que afecta a una colección, exige que el registro de revisión incluya la regla de selección y la cantidad. «Sincroniza usuarios» es demasiado impreciso. «Elimina 14 contratistas inactivos seleccionados mediante los ID proporcionados» permite juzgar el alcance. Si el servidor no puede informar de ese alcance antes de actuar, no ha proporcionado al cliente información suficiente para una solicitud de aprobación seria.
La idempotencia debe resistir los reintentos y la concurrencia
idempotentHint es una afirmación limitada: los argumentos idénticos deberían producir el mismo efecto después de la primera llamada, sin efectos adicionales. No significa que una llamada sea segura, barata, reversible o adecuada para que un agente la repita sin límite.
Una actualización de estado puede ser idempotente si establecer state=closed dos veces deja el mismo registro cerrado. Sin embargo, el controlador deja de ser idempotente si envía un correo cada vez, añade un comentario de auditoría cada vez o incrementa un contador de versión. Muchas personas inspeccionan la fila de la base de datos y pasan por alto los efectos secundarios que los usuarios perciben primero.
La igualdad de las entradas también necesita una definición precisa. El orden de las propiedades de un objeto JSON no debería importar. Un servidor que trata un note omitido de forma distinta a note: "" puede recibir lo que el agente considera la misma solicitud, pero ejecutar dos actualizaciones distintas. Los valores de tiempo, los valores predeterminados generados y las expresiones relativas como tomorrow debilitan la afirmación porque cambian el comando efectivo aunque los argumentos visibles parezcan estables.
La concurrencia es donde se desmorona la idempotencia superficial. Dos procesos de trabajo pueden comprobar que un objeto no existe y después crearlo los dos. Una restricción única, una operación upsert transaccional o una función de idempotencia remota pueden evitarlo. Una caché en memoria de un proceso del servidor MCP no puede proteger un despliegue que funciona con más de un proceso.
Usa un registro de idempotencia solo después de definir su alcance. Almacena un token proporcionado por quien llama junto con la identidad autenticada, el cuerpo de la solicitud normalizado, el resultado y una caducidad adecuada para la operación. Rechaza un token reutilizado con una entrada normalizada distinta. De lo contrario, un agente puede adjuntar por accidente un token antiguo a una solicitud nueva y recibir el resultado de otra acción.
No reintentes automáticamente solo porque la indicación sea true. Reintenta únicamente los fallos en los que sepas si el servidor recibió la llamada. Si no puedes saberlo, la solución es un mecanismo de idempotencia en el punto real de mutación. La indicación del cliente no lo es.
Construye las aprobaciones a partir de la acción ejecutada
Un sistema de aprobación debería responder a estas preguntas: qué proceso realiza la solicitud, qué credencial se utilizará, qué objetivo externo recibirá la solicitud, qué estado o datos están dentro del alcance y qué ocurrirá si la llamada tiene éxito. Las anotaciones de herramientas pueden hacer más breve esa explicación. No pueden aportar hechos que el servidor no haya expuesto.
Mantén separadas la aprobación de sesión y la aprobación por llamada. La aprobación de sesión es adecuada para un proceso de agente conocido que realiza una ejecución delimitada con capacidades normales. La aprobación por llamada es adecuada para credenciales que pueden transferir dinero, modificar el acceso a producción, enviar mensajes, divulgar registros sensibles o crear un compromiso externo irreversible. La decisión depende de la credencial y del contexto de la acción, no de un readOnlyHint optimista.
Una solicitud útil nombra la operación concreta: «El agente firmado por esta autoridad usará la credencial de despliegue para reiniciar el servicio X en la cuenta Y». Una solicitud débil dice: «¿Permitir la herramienta deploy?». La primera ofrece algo que la persona puede evaluar. La segunda le pide que confíe en un detalle de implementación.
Sallyport mantiene las credenciales API y SSH en su bóveda cifrada, ejecuta la acción HTTP o SSH y devuelve el resultado al agente en lugar de la credencial. Su autorización de sesión identifica el proceso que realiza la solicitud, mientras que una configuración por credencial puede exigir una decisión en cada uso. Es un lugar más adecuado para el control humano que un booleano proporcionado por el servidor.
Incluso con una puerta delante de las credenciales, conserva un registro de actividad que capture el objetivo y el resultado de la solicitud final. La aprobación responde si la acción puede continuar. El registro de auditoría responde qué ocurrió cuando continuó. No mezcles ambas preguntas en un evento impreciso llamado «herramienta utilizada».
Integra las comprobaciones de anotaciones en el mantenimiento del servidor
El uso adecuado de las anotaciones de herramientas MCP consiste en comunicar con honestidad y respaldar esa comunicación con pruebas. El autor del servidor debería establecerlas de forma conservadora, documentar cada caso límite y cambiarlas cuando cambie el comportamiento. El autor del cliente debería usarlas como una entrada más para diseñar la interfaz, nunca como la única entrada para decidir permisos.
Añade pruebas que contradigan deliberadamente la propiedad declarada. Para una declaración de solo lectura, haz que la prueba falle si el entorno de prueba detecta una escritura, una notificación saliente, una actualización de credencial o una capacidad creada para recuperarla más tarde. Para una declaración destructiva, prueba la ruta de fallo y la ruta para deshacer la acción, incluido lo que ocurre después de que un trabajo posterior consuma el cambio. Para la idempotencia, ejecuta la misma solicitud normalizada después de simular un tiempo de espera y con llamadas simultáneas.
No ocultes una discrepancia cambiando el objetivo de la prueba hasta que pase. Limita la herramienta para que la indicación sea cierta, cambia la anotación o muestra el efecto en los detalles de aprobación. Cada opción informa al próximo responsable de mantenimiento sobre lo que realmente hace el código.
Ejecuta sp audit verify como parte de la revisión de incidentes cuando Sallyport sea la puerta de acceso de las acciones. Verifica sin conexión la cadena de hashes cifrada, de modo que puedes comprobar que el historial de acciones registrado sigue intacto sin abrir la bóveda. Eso no demuestra que la anotación del servidor fuera honesta, pero te proporciona un registro resistente a manipulaciones de las llamadas que la siguieron.
El criterio práctico es sencillo: una anotación debería resistir una prueba adversarial contra el efecto que importa a la persona usuaria. Si no puede hacerlo, mantenla en una postura conservadora y conserva la aprobación allí donde ocurre la acción.
FAQ
¿Puedo aprobar automáticamente de forma segura una herramienta MCP con readOnlyHint?
Trátalo como una afirmación que necesita pruebas, no como un permiso. Examina la implementación del servidor, ejecuta la herramienta contra un objetivo desechable y comprueba todos los efectos secundarios que puede provocar.
¿Qué garantiza realmente idempotentHint?
Indica que el autor considera que las llamadas repetidas con los mismos argumentos no producen efectos adicionales en el entorno. Aun así, debes probar los sistemas externos, el comportamiento ante reintentos, las marcas de tiempo, las notificaciones y la normalización de argumentos.
¿Las anotaciones de herramientas MCP son un límite de seguridad?
No. La especificación del Model Context Protocol describe estos campos como indicaciones de comportamiento, no como sustitutos de una decisión de seguridad del cliente. Un servidor malicioso, desactualizado o simplemente equivocado puede publicar metadatos engañosos.
¿Qué herramientas MCP deberían seguir requiriendo aprobación?
Mantén la aprobación cuando una llamada pueda cambiar el estado del negocio, exponer datos sensibles, iniciar un trabajo costoso o llegar a un sistema fuera del objetivo de prueba controlado. Un nombre aparentemente inofensivo y una anotación optimista no eliminan esos riesgos.
¿Cómo compruebo si una herramienta es idempotente?
Usa una cuenta desechable o un entorno local de prueba, registra el estado inicial, llama dos veces a la herramienta con argumentos idénticos y compara el estado resultante y las evidencias externas. Repite la prueba con datos incorrectos y una solicitud interrumpida, porque las rutas de reintento suelen revelar los daños.
¿Puede una herramienta destructiva ser segura cuando no cambia nada?
Una herramienta de eliminación puede no ser destructiva para un registro concreto porque el registro ya no existe, aunque sea destructiva en general. El diseño de la aprobación debe clasificar la capacidad y el contexto del objetivo, no solo el resultado de una llamada.
¿Qué ocurre cuando faltan anotaciones MCP?
Supón que la omisión requiere una postura conservadora y revisa el significado de cada campo antes de automatizarlo. En las anotaciones actuales de herramientas MCP, un destructiveHint omitido tiene el valor predeterminado true, mientras que readOnlyHint e idempotentHint omitidos tienen el valor predeterminado false.
¿Cómo puede una llamada de una API de solo lectura tener efectos secundarios?
Una herramienta puede devolver una respuesta plausible, crear una entrada de auditoría, actualizar un campo de último acceso, activar un webhook o cobrar a una cuenta aunque el recurso evidente no cambie. Comprueba los registros posteriores, las llamadas de red y los logs del sistema, no solo la respuesta de la herramienta.
¿Cómo debo diseñar las aprobaciones para agentes autónomos de programación?
Aprueba la ejecución del agente después de identificar la autoridad de firma de código y el alcance previsto. Reserva la aprobación por llamada para las credenciales o acciones cuyo uso requiera una decisión humana en cada ocasión. Los metadatos pueden ayudar a redactar el texto de aprobación, pero no deberían decidir el resultado.
¿Cómo ayuda Sallyport a controlar las acciones MCP?
Sallyport mantiene las credenciales fuera del agente y permite que una persona apruebe una sesión o exija aprobación cada vez que se use una credencial determinada. Así puedes basar la aprobación en la acción ejecutada, en lugar de hacerlo en una anotación proporcionada por el servidor.