Tener una tool gigante con un action enum se siente eficiente. Una sola tool, mil casos de uso. Forge lo intentó. Hex tardó cuatro minutos en mostrarle por qué no.
El problema no es estético. es estructural. Cuando juntás cuatro niveles de poder en una sola tool, el modelo (o un atacante vía inyección) elige cuál ejercer al momento de la llamada. Tu capa de autorización ahora tiene que:
action está pidiendo el modelo.Cada uno de esos tres pasos puede fallar. Y "puede fallar" es lo que Hex persigue para ganarse la firma de Atlas.
El patrón correcto:
read_user(user_id) // solo lectura
update_user_fields(user_id, fields) // escritura controlada
suspend_user(user_id) // acción de moderación
delete_user(user_id) // acción destructiva, con dry-runCuatro tools, cuatro niveles de poder. Cuando armás un asistente de soporte de nivel 1, le cableás read_user y update_user_fields y nada más. El modelo literalmente no puede intentar delete_user. la tool no existe en su set.
La autorización ya no es una pregunta sobre el dato ("¿el
actionque el modelo eligió está permitido?"). es una pregunta sobre el cableado ("¿esta tool está en el set del asistente?"). El cableado lo hacés vos, en código, en deploy, bajo code review. mucho más auditable que la decisión del modelo en runtime.
Cuando dos operaciones son isomorfas en poder y en superficie de error. list_users(filter) y count_users(filter) son básicamente la misma cosa con distinto formato de output. agruparlas como query_users(filter, format) puede tener sentido.
Cuando difieren en poder (read vs delete) o en superficie de error (leer mi cuenta vs leer cualquier cuenta), no agrupes.
A la derecha: una tool con cinco supuestos problemas. Cuatro son cierto-pero-no-fatal. Uno es el que volaría la nave. Encontralo.