AFPS, el formato portable que les faltaba a los agentes IA
MCP mueve tool calls. A2A mueve mensajes. Agent Skills distribuye capacidades. Nada estandarizaba el package en sí. De ahí AFPS. Aquí va el hueco que llena y las decisiones detrás de la spec.
🧱 La capa que le faltaba al stack de agentes de 2026
En 2026, la estantería de estándares abiertos para agentes IA terminó de llenarse. MCP define cómo los agentes llaman a herramientas en runtime. A2A define cómo se hablan entre sí cruzando fronteras de proceso. Anthropic Agent Skills define una capacidad reutilizable como un archivo SKILL.md con frontmatter YAML. Tres specs, tres capas distintas: transporte, comunicación, capacidad.
Ninguna de esas tres estandariza lo que la mayoría de los equipos quiere enviar de verdad: un package portable que declare qué debe lograr el agente y que empaquete todo lo que necesita (prompt, dependencias, schemas de entrada y salida, metadatos de auth de providers) en un único artefacto que puedas mover entre runtimes.
Ese es el hueco que cubre AFPS, el Agent Format Packaging Standard. Lo escribimos, lo publicamos bajo CC-BY, y este post recorre por qué tenía que existir y cómo se tomaron las decisiones que lo componen.
Antes de nada, la divulgación obligada. Appstrate es la autora de AFPS y se beneficia si lo adoptas. Todo lo que viene abajo es nuestra visión de por qué el stack de 2026 tiene un hueco con forma de package, y de por qué pensamos que AFPS es la forma correcta de llenarlo. Donde las decisiones son discutibles, las señalamos. Nuestro runtime resulta ser uno de los primeros que ejecuta packages AFPS de forma nativa, pero el formato en sí es intencionalmente agnóstico al runtime.
🎯 Capacidad vs objetivo: el giro que importa
Cada estándar de agentes existente describe capacidades: qué puede hacer un agente, cómo se invoca una herramienta, cómo se ve un skill en el filesystem. Primitivas útiles. Ninguna responde a la pregunta ¿qué intenta lograr este agente y qué necesita para lograrlo?
AFPS invierte el encuadre. El manifest arranca desde el objetivo (un prompt.md que dice "procesa el inbox y resume las solicitudes de soporte") y declara, alrededor de ese prompt, cada capacidad (skills, tools) y cada conexión (providers con metadatos de auth) de las que el agente depende. Versionado. Con dependencias resueltas. En un solo ZIP.
┌───────────────────────────────┐
Goal │ AFPS Agent │ "Summarize support requests"
│ prompt.md + manifest.json │
├───────────────────────────────┤
Capability │ Skills (SKILL.md) │ "Rewrite in professional tone"
│ Tools (source + manifest) │ "Fetch JSON from a URL"
├───────────────────────────────┤
Connection │ Providers (OAuth2, API key) │ "Gmail, OpenAI, Slack"
├───────────────────────────────┤
Transport │ MCP / A2A │ Runtime protocols
└───────────────────────────────┘
AFPS ocupa las dos capas de arriba. Las dos de abajo ya están estandarizadas. Nada en AFPS compite con MCP ni con A2A: resuelven un problema distinto.
📦 Qué hay de verdad dentro de un archivo .afps
El package mínimo viable son dos archivos dentro de un ZIP:
// manifest.json
{
"name": "@my-org/hello-world",
"version": "1.0.0",
"type": "agent",
"schemaVersion": "1.0",
"displayName": "Hello World",
"author": "My Org",
"dependencies": {}
}
<!-- prompt.md -->
Summarize the latest unread emails and list any action items.
Comprime esos dos, dale la extensión .afps y ya has producido un agent package válido que cualquier runtime compatible con AFPS puede ejecutar. Los packages reales declaran dependencias y configuración de providers; el ejemplo completo en el repo de la spec va bastante más allá. La spec define cuatro tipos de package (agent, skill, tool, provider), cada uno con sus archivos compañeros obligatorios, de forma que un consumidor pueda despachar según manifest.type y rechazar archivos malformados a bajo coste.
La extensión es una convención, no un requisito duro. La spec §2.5 es explícita: los consumidores DEBEN aceptar ZIPs independientemente de la extensión. Elegimos .afps porque tanto los humanos como los sistemas operativos agradecen una señal estable que diga "esto es un agent package, no un archivo cualquiera".
🔐 Las partes difíciles: identidad, versionado, resolución de dependencias
Un skill escrito como SKILL.md es útil, pero por sí solo está más cerca del copiar-y-pegar que de una dependencia. Para compartir skills entre tenants, equipos u organizaciones sin colisiones ni rupturas silenciosas, hay tres cosas que hay que clavar. El manifest de AFPS las clava porque no veíamos cómo un runtime multi-tenant podría saltárselas.
1. Identidad con scope. Cada package AFPS lleva un nombre con scope que encaja en un regex estricto (^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$). Sin mayúsculas, sin underscores, y debe empezar y terminar en alfanumérico. Es la misma forma que los scopes de npm por la misma razón: los registries pueden aplicar la propiedad del scope y rechazar typosquats.
2. Versionado semántico. Agentes, skills, tools y providers se entregan todos como MAJOR.MINOR.PATCH. El campo schemaVersion va aparte y usa solo MAJOR.MINOR: pinea la forma del manifest, no el comportamiento. Seguirlos de forma independiente significa que el runtime puede evolucionar su validador y que el ecosistema puede evolucionar el contenido de los packages sin pelearse entre sí.
3. Dependencias tipadas. dependencies.{providers, skills, tools}: tres mapas, cada uno de nombre con scope a rango semver:
"dependencies": {
"skills": { "@example/rewrite-tone": "^1.0.0" },
"tools": { "@example/fetch-json": "^1.0.0" },
"providers": { "@example/gmail": "^1.0.0" }
},
"providersConfiguration": {
"@example/gmail": { "scopes": ["gmail.readonly", "gmail.send"] }
}
La división entre dependencies.providers y providersConfiguration es donde se nota la experiencia operacional. Un package de provider declara lo que puede hacer (endpoints, modo de auth, scopes soportados); un agente consumidor declara lo que quiere (el subconjunto de scopes que de verdad necesita). Dos agentes pueden depender del mismo @example/gmail en la misma versión y pedir envelopes de scopes completamente distintos, sin forkear el package del provider. Ese es exactamente el tipo de división que necesita un runtime multi-tenant si algún día quiere correr agentes de distintos equipos lado a lado.
🤝 Compatible con lo que ya existe
La forma fácil de construir una spec nueva es redefinirlo todo. La difícil, y la que toma AFPS, es componer con los estándares existentes en lugar de reemplazarlos.
- MCP. AFPS no dice nada sobre el transporte de tool calls. Según la spec §1.2.1, un runtime puede elegir exponer sus tools AFPS por MCP. Nada impide a un consumidor correr packages AFPS y hablar MCP con clientes externos.
- Agent Skills. Los skills de AFPS son un superset estricto. Un directorio Agent Skills válido se convierte en un skill AFPS en el momento en que añades un
manifest.jsonjunto alSKILL.md. El vocabulario del frontmatter (name,description,license,allowed-toolsy el resto) se preserva sin cambios. AFPS añade identidad, versionado y un grafo de dependencias: las tres cosas que Agent Skills deja explícitamente fuera. - A2A. Los protocolos inter-agente operan por debajo de la capa de package. AFPS reserva campos con prefijo
x-para que los metadatos específicos de A2A puedan vivir dentro de un manifest sin esperar a que la spec los ratifique.
Si un package adopta AFPS y el ecosistema de alrededor se estandariza aún más, los packages AFPS no tienen por qué romperse. Los puntos de extensión están ahí por diseño.
🔓 Por qué lo publicamos en lugar de guardárnoslo
Esta es la decisión donde vale la pena ser explícitos sobre el trade-off.
Podríamos haber mantenido AFPS como una convención privada dentro del runtime de Appstrate. Más rápido de iterar, sin coste de gobernanza, sin riesgo de que un competidor entregara una implementación mejor. La contra salta a la vista: cada agente escrito para nuestro runtime quedaría atrapado, cada cliente metería ese lock-in en la decisión de adoptarnos y cada partner de integración tendría que hacer reverse-engineering de nuestros manifests.
La alternativa es lo que hicimos: publicamos la spec bajo CC-BY 4.0, montamos una gobernanza independiente con un proceso de cambio y dejamos que cualquier runtime la implemente. La apuesta es directa. Preferimos perder un cliente frente a un competidor que también hable AFPS, antes que retenerlo atrapado en un formato del que no puede salir. Los agentes portables son mejores para el ecosistema, y un ecosistema con agentes portables es mejor sitio para vender un runtime que un mosaico de formatos propietarios.
Esta es también la razón por la que el scope de la spec es deliberadamente estrecho. AFPS no define un protocolo de registry, ni un esquema de firmado, ni una API de runtime. Son specs separadas, esperando a ser escritas, y van a funcionar con AFPS en vez de ser absorbidas por él.
🧭 Lo que esto desbloquea
Con AFPS en su sitio, se vuelven posibles un puñado de patrones que el stack de 2026 hoy no soporta sin lock-in de vendor:
- Portabilidad entre runtimes. Un equipo escribe un agente contra un runtime, exporta un
.afpsy lo corre en otro. Sin reescritura, sin manifest deducido por reverse-engineering, sin "exporta a nuestro formato". - Marketplaces con dependencias resueltas. Un skill o un tool se publica una vez, se referencia por nombre con scope y lo consumen agentes de equipos distintos, igual que funcionan los packages de npm, con rangos semver y checks de integridad estilo lockfile (la spec §8.5 recomienda hashes SRI).
- Agentes auditables. El manifest declara cada capacidad, cada provider y cada scope OAuth requerido en un solo archivo. Los workflows de revisión, los controles de compliance y las herramientas de supply-chain pueden operar sobre ese manifest sin necesidad de correr el agente.
- Composición agente-a-agente en la capa de package. Un agente puede declarar otro agente como dependencia, igual que un skill declara un tool. No es un protocolo A2A completo (es solo reutilización compositiva), pero es el cimiento sobre el que las interacciones estilo A2A pueden construirse.
Que algo de esto pase de verdad depende de la adopción, no de que nosotros lo hayamos escrito. La spec existe. Lo que venga a continuación son otros runtimes, otras herramientas y otros packages. O no, y AFPS se queda en una convención interna cuidadosamente documentada que casualmente vive en una URL pública.
🛠 Qué viene a continuación
Para la spec en sí, las prioridades son estrechas. Un protocolo de registry es el hueco obvio: una forma de publicar y resolver packages por nombre con scope sin tener que correr un mirror a medida. El firmado es otro. CC-BY está bien, pero la procedencia criptográfica es lo que pide de verdad la seguridad de supply-chain. Ambos son trabajo adyacente a la spec que la gobernanza de AFPS está diseñada para absorber sin que la spec principal tiemble por debajo.
Para Appstrate, el trabajo es demostrar que correr packages AFPS de forma nativa (como procesos aislados y multi-tenant detrás de un runtime de verdad, con las credenciales manejadas fuera del agente y con la infraestructura de los agentes en manos del operador) es un mejor default que la alternativa propietaria. Si esa prueba convence, AFPS se vuelve fácil de adoptar. Si no, ninguna cantidad de spec-writing salva la papeleta.
Tres cosas que puedes hacer si algo de esto resuena:
- Lee la spec: son unas 40 páginas de markdown. El primer es la versión corta.
- Prueba Appstrate: auto-alójalo con
curl -fsSL https://get.appstrate.dev | bash, o córrelo sobre nuestra infraestructura. - Abre un issue o una propuesta de cambio de spec: el proceso de gobernanza está diseñado para absorber feedback del mundo real, especialmente de los equipos que ya corren agentes multi-tenant hoy.
Si has construido un runtime de agentes y no estás de acuerdo con algo de esto, preferimos enterarnos.