Darle acceso a un agente
La API está construida para que un agente pueda operarla sin que eso implique darle la cuenta entera. Esta página es lo que hay que saber antes de conectar uno.
Una credencial por agente, acotada
Sección titulada «Una credencial por agente, acotada»Nunca reuses la key de tu CI ni la de tu Terraform.
truo auth token create \ --name "agente-soporte" \ --scopes services:read,vps:read,vps:power,operations:read \ --service-allowlist svc_10432,svc_10433Dos límites independientes:
- Scopes — qué puede hacer. Empezá solo con
:ready agregá lo demás cuando haga falta de verdad. - Allowlist de servicios — sobre qué. Una key restringida a tres servicios no enumera el resto de la cuenta: los endpoints de colección filtran, no solo bloquean.
Y hay un límite que no se puede desactivar: apikeys:* y users:* no son
otorgables a una key. Un agente con una API key no puede mintear otra
credencial ni crear un usuario, por más que se lo pidan. Ver
autenticación.
Lo que el contrato le dice al agente
Sección titulada «Lo que el contrato le dice al agente»Cada operación del spec declara cuánto duele equivocarse:
x-truo-scope: vps:writex-truo-danger: destructive # none | reversible | destructivex-truo-long-running: truex-truo-idempotent: truex-truo-mcp: { toolset: vps, action: reinstall, readonly: false }x-truo-danger es lo que hay que usar para decidir dónde pedir confirmación
humana. El CLI ya lo hace: todo lo marcado destructive pregunta antes, y la
misma clasificación va a gobernar el servidor MCP. Una sola taxonomía para las
dos superficies.
Aplicá el gate en tu código, no en el prompt. Un prompt que dice “pedí
confirmación antes de borrar” es una sugerencia; un if (danger === "destructive")
es un control.
Para que no invente parámetros
Sección titulada «Para que no invente parámetros»llms.txt— el catálogo completo: cadaoperationIdcon su método, su ruta, su scope y si es destructiva.llms-full.txt— lo mismo más los parámetros y el body de cada operación. Es lo que evita que un modelo se invente nombres de campo.openapi.json— el contrato entero, si tu agente sabe leerlo.
Dárselos al agente recorta las alucinaciones de parámetros más que cualquier instrucción en el prompt.
Cinco cosas que hay que manejar en el código
Sección titulada «Cinco cosas que hay que manejar en el código»- Todo lo que tarda devuelve
202+ una operación. El agente tiene que consultar/v1/operations/{id}hastasucceededofailed, no asumir que la llamada terminó el trabajo. - Mandá
Idempotency-Keyen toda mutación. Un agente reintenta más que una persona, y sin la key un reintento crea dos backups. - 404 no significa “no existe” — significa “no existe para esta credencial”. Si el agente esperaba ver algo, el problema puede estar en la allowlist, no en la infraestructura.
- Los datos que devuelve la API pueden venir de un atacante. Logs de contenedor, listados de objetos y registros WHOIS son escribibles por terceros. Envolvelos como datos, no como instrucciones, antes de dárselos al modelo.
- Ninguna respuesta debería llevarle credenciales al modelo. Los endpoints
que revelan secretos están detrás de scopes propios (
dbaas:credentials,objectstorage:keys) justamente para que puedas no otorgarlos.
Un ejemplo mínimo
Sección titulada «Un ejemplo mínimo»import { TruoClient, OPERATIONS } from "@truocloud/sdk";
const truo = new TruoClient({ token: process.env.AGENT_TOKEN });
async function ejecutar(operationId: string, args: Record<string, unknown>) { const meta = OPERATIONS[operationId as keyof typeof OPERATIONS]; if (!meta) throw new Error(`operación desconocida: ${operationId}`);
// El gate vive acá, no en el prompt. if (meta.danger === "destructive" && !(await pedirConfirmacionHumana(meta, args))) { return { status: "cancelado" }; }
const res = await truo.request(operationId, { params: args }); // Si es asíncrona, esperarla es parte de "ejecutar", no del prompt. return meta.longRunning ? await truo.operations.wait((res.data as { id: string }).id) : res.data;}