All posts

Why we wrote AFPS: a portable format for AI agents

MCP moves tool calls. A2A moves messages. Agent Skills ships capabilities. Nothing standardized the package itself, so we wrote AFPS. Here's the gap it fills and the decisions behind it.

Appstrate
standardafpsecosystemarchitecture

🧱 The missing layer in the 2026 agent stack

In 2026, the open-standards shelf for AI agents finally filled up. MCP defines how agents call tools at runtime. A2A defines how agents talk to each other across process boundaries. Anthropic Agent Skills defines a reusable capability as a SKILL.md file with YAML frontmatter. Three specs, three different layers: transport, communication, capability.

Nothing in that list standardizes the thing most teams actually want to ship: a portable package that declares what the agent should accomplish and bundles everything it needs (prompt, dependencies, input/output schemas, provider auth metadata) into a single artifact you can move between runtimes.

That gap is what AFPS, the Agent Format Packaging Standard, is for. We wrote it, we published it under CC-BY, and this post walks through why it had to exist and how the decisions inside it were made.

Full disclosure before anything else. Appstrate authored AFPS and benefits if you adopt it. Everything below is our view on why the 2026 stack has a package-shaped hole, and why we think AFPS is the right shape to fill it. Where choices are debatable, we call them out. Our runtime happens to be one of the first to execute AFPS packages natively, but the format itself is intentionally runtime-agnostic.

🎯 Capability vs goal: the move that matters

Every existing agent standard describes capabilities: what an agent can do, how a tool should be invoked, what a skill looks like in the filesystem. Useful primitives. None of them answer the question what is this agent trying to accomplish, and what does it need to accomplish it?

AFPS inverts the frame. The manifest starts from the goal, a prompt.md that says "process the inbox and summarize support requests", and declares, around that prompt, every capability (skills, tools) and every connection (providers with auth metadata) the agent depends on. Versioned. Dependency-resolved. In a single 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 sits at the top two layers. The bottom two are already standardized. Nothing in AFPS competes with MCP or A2A. They solve a different problem.

📦 What's actually in a .afps file

The minimum viable package is two files in a 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.

Zip those together, give it a .afps extension, and you've produced a valid agent package any AFPS-compliant runtime can execute. Real packages declare dependencies and provider configuration; the full example in the spec repo goes further. The spec defines four package types (agent, skill, tool, provider), each with its own required companion files, so a consumer can dispatch on manifest.type and reject malformed archives cheaply.

The extension is a convention, not a hard requirement. The spec §2.5 is explicit: consumers MUST accept ZIPs regardless of extension. We picked .afps because humans and operating systems benefit from a stable signal that says "this is an agent package, not a random archive."

🔐 The hard parts: identity, versioning, dependency resolution

A skill written as SKILL.md is useful, but by itself it's closer to a copy-paste than a dependency. To share skills across tenants, teams, or organizations without collisions or silent breakage, three things have to be nailed down. The AFPS manifest nails them because we couldn't see how a multi-tenant runtime could skip them:

1. Scoped identity. Every AFPS package has a scoped name matching a strict regex (^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\/[a-z0-9]([a-z0-9-]*[a-z0-9])?$). No uppercase, no underscores, must start and end alphanumeric. It's the same shape as npm scopes for the same reason: registries can enforce scope ownership and reject typosquats.

2. Semantic versioning. Agents, skills, tools, and providers all ship as MAJOR.MINOR.PATCH. The schemaVersion field is separate and uses only MAJOR.MINOR. It pins the manifest shape, not the behavior. Tracking these independently means the runtime can evolve its validator and the ecosystem can evolve package contents without fighting each other.

3. Typed dependencies. dependencies.{providers, skills, tools}: three maps, each from scoped name to semver range:

"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"] }
}

The split between dependencies.providers and providersConfiguration is where operational experience shows. A provider package declares what it can do (endpoints, auth mode, supported scopes); a consuming agent declares what it wants (the subset of scopes it actually needs). Two agents can depend on the same @example/gmail at the same version and ask for completely different scope envelopes, without forking the provider package. That's the kind of split a multi-tenant runtime needs if it ever wants to run agents written by different teams side by side.

🤝 Compatible with what's already there

The easy way to build a new spec is to redefine everything. The harder way, and the one AFPS takes, is to compose with existing standards instead of replacing them.

  • MCP. AFPS is silent on tool-call transport. Per spec §1.2.1, a runtime may choose to expose AFPS tools over MCP. Nothing stops a consumer from running AFPS packages and speaking MCP to external clients.
  • Agent Skills. AFPS skills are a strict superset. A valid Agent Skills directory becomes an AFPS skill the moment you add a manifest.json next to SKILL.md. Frontmatter vocabulary (name, description, license, allowed-tools, and the rest) is preserved unchanged. AFPS adds identity, versioning, and a dependency graph: the three things Agent Skills explicitly leaves out.
  • A2A. Inter-agent protocols operate below the package layer. AFPS reserves x- prefixed fields so A2A-specific metadata can live inside a manifest without waiting for the spec to ratify it.

If a package adopts AFPS and the surrounding ecosystem standardizes further, AFPS packages don't have to break. The extension points are there by design.

🔓 Why we published it instead of keeping it internal

This is the choice where the trade-off is worth being explicit.

We could have kept AFPS as a private convention inside the Appstrate runtime. Faster to iterate, no governance cost, no risk of a competitor shipping a better implementation. The downside is immediate: every agent written for our runtime would be locked in, every customer would factor that lock-in into the decision to adopt us, and every integration partner would have to reverse-engineer our manifests.

The alternative is what we did: publish the spec under CC-BY 4.0, set up independent governance with a change process, and let any runtime implement it. The bet is straightforward. We'd rather lose a customer to a competitor who also speaks AFPS than keep them trapped in a format they can't export. Portable agents are better for the ecosystem, and an ecosystem with portable agents is a better place to sell a runtime than a patchwork of proprietary formats.

This is also why the spec's scope is deliberately narrow. AFPS does not define a registry protocol, a signing scheme, or a runtime API. Those are separate specs waiting to be written, and they'll work with AFPS rather than being absorbed by it.

🧭 What this unlocks

With AFPS in place, a handful of patterns become possible that the 2026 stack doesn't currently support without vendor lock-in:

  • Cross-runtime portability. A team writes an agent against one runtime, exports a .afps, and runs it on another. No rewrite, no reverse-engineered manifest, no "export to our format."
  • Dependency-resolved marketplaces. A skill or tool can be published once, referenced by scoped name, and consumed by agents from different teams, the same way npm packages work, with semver ranges and lockfile-style integrity checks (spec §8.5 recommends SRI hashes).
  • Auditable agents. The manifest declares every capability, every provider, and every required OAuth scope in a single file. Review workflows, compliance checks, and supply-chain tooling can operate on that manifest without running the agent.
  • Agent-to-agent composition at the package layer. An agent can declare another agent as a dependency the same way a skill declares a tool. That's not a full A2A protocol, just compositional reuse, but it's the foundation A2A-style interactions can build on.

Whether any of this actually happens depends on adoption, not on us writing it down. The spec exists. What comes next is other runtimes, other tools, and other packages. Or it doesn't, and AFPS remains a carefully-documented internal convention that just happens to live at a public URL.

🛠 What's next

For the spec itself, the priorities are narrow. A registry protocol is the obvious gap: a way to publish and resolve packages by scoped name without running a custom mirror. Signing is another one. CC-BY is nice, but cryptographic provenance is what supply-chain security actually asks for. Both are spec-adjacent work that AFPS governance is designed to accommodate without the main spec churning underneath.

For Appstrate, the job is to prove that running AFPS packages natively, as isolated, multi-tenant processes behind a real runtime, with credentials handled outside the agent and agents' infrastructure controlled by the operator, is a better default than the proprietary alternative. If that proof is convincing, AFPS becomes easy to adopt. If it isn't, no amount of spec-writing helps.

Three things you can do if any of this resonates:

  1. Read the spec: it's ~40 pages of markdown. The primer is the short version.
  2. Try Appstrate: self-host with curl -fsSL https://get.appstrate.dev | bash, or run it on our infrastructure.
  3. Open an issue or a spec-change proposal: the governance process is designed to absorb real-world feedback, especially from teams running multi-tenant agents today.

If you've built an agent runtime and disagree with any of this, we'd rather know.