If you’ve built an Agent Skill and wired it to an MCP server, you already know the annoying part isn’t the skill. It’s everything around it. The instructions travel fine as a markdown file. The MCP server config does not, because every client wants its own directory layout, its own manifest, its own way of saying “run this process and pass it these environment variables.”

Vercel, AWS, GitHub, Microsoft, OpenAI, and the Cursor team shipped Agent Plugins 1.0.0 to fix that: one folder format for packaging Agent Skills and MCP servers together, readable by any client that implements the spec. ChatGPT, Codex, Cursor, GitHub Copilot, Kiro, and VS Code support it at launch, and Google has joined the technical steering committee as a core maintainer.

Anthropic wrote the underlying Agent Skills spec and published it as an open standard last December. It isn’t part of this coalition. Claude Code isn’t on the compatibility list. Make of that what you will — I’ll get back to it.


What a plugin actually is

A plugin is a directory with a manifest. The smallest valid one is two required fields:

1
2
3
4
{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "acme.release-notes"
}

Names are dotted, lowercase, no leading or trailing dashes, no double dashes. acme.release-notes is fine; Acme-ReleaseNotes and -release-notes are not. Restrictive, but it means a plugin name is portable across registries without anyone having to guess a normalization rule.

Your existing skill doesn’t change

This is the part that makes adoption cheap. If you already have a working Agent Skill, you don’t rewrite it, you just give it a home:

1
2
3
4
5
release-notes-plugin/
├── plugin.json
└── skills/
    └── summarize-release/
        └── SKILL.md

SKILL.md keeps its usual frontmatter and instructions, untouched. The plugin’s only job is to declare what the skill depends on so a client doesn’t have to be told about it separately in a README.

mcp.json is where it gets interesting

Add an mcp.json next to the skill and the plugin can now describe the MCP server the skill actually needs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
  "mcpServers": {
    "changelog": {
      "type": "stdio",
      "command": "./bin/changelog-server",
      "args": ["--cache", "${PLUGIN_DATA}/cache"],
      "env": { "TEMPLATES": "${PLUGIN_ROOT}/templates" }
    }
  }
}

Three transports are defined: stdio for a local process, streamable-http for a remote server behind a URL, and sse, which is already marked deprecated in the 1.0.0 spec. Don’t build anything new on SSE.

PLUGIN_ROOT and PLUGIN_DATA look interchangeable until you’ve been burned once. PLUGIN_ROOT is where the plugin itself lives: templates, static assets, anything you shipped. PLUGIN_DATA is writable state the server generates at runtime, like a cache or an index. Write your cache to PLUGIN_ROOT and it vanishes the next time the plugin updates, because that directory gets replaced wholesale. It’s a small distinction and it will cost you a debugging session the first time you get it backwards.

command is not a shell

Easy to miss, worth writing down: command takes exactly one executable, not a shell string.

1
{ "command": "./bin/changelog-server" }

works. "command": "./bin/changelog-server --port 8080" does not. Put the flag in args instead. And placeholders don’t expand inside command at all, so ${PLUGIN_ROOT}/bin/server stays a literal string instead of resolving to a path. Use a plugin-relative path like ./bin/server and put anything that needs ${PLUGIN_ROOT} or ${PLUGIN_DATA} in args or env, where expansion actually happens.

Failure is scoped, not all-or-nothing

The part of the spec I like most: one broken piece doesn’t take the whole plugin down. A malformed plugin.json fails the plugin, sure. But a bad mcp.json just disables MCP support while the skills still load. If one server in mcp.json is unreachable, that server goes dark and the others keep running. Same for skills — one skill failing to parse doesn’t block the rest of the directory. A plugin with two MCP servers and one down endpoint still gives you a working skill and a working server, instead of a hard failure because of a problem in an unrelated part of the manifest.

What still doesn’t travel

Auth. You cannot put a bearer token in mcp.json and expect it to move safely between clients, and 1.0.0 doesn’t define a portable way to reference a credential either. That’s still each client’s problem to solve its own way. Distribution, installation, permission handling, and any client-specific commands or hooks are explicitly out of scope for this version too, same as before the spec existed. So is a validator: there’s no agent-plugins lint you can run and trust yet. Right now the debugging loop is “load it in a real client, see what happened,” not “run the linter.”

Do you actually need one

If you have one skill and no external tool, that’s just a Skill, don’t wrap it. If you have one MCP server for one client, a plain MCP config is less ceremony. The plugin format earns its keep when a skill and its MCP server need to move together across more than one client — that’s the actual use case the format was built for, not a reason to package everything you own.


Back to the missing name in the room. Anthropic authored the skills spec this whole thing sits on top of, and chose not to join the group standardizing what wraps around it. That could mean Claude Code gets a compatibility shim later, or it could mean two formats sit side by side indefinitely, the way RSS and JSON Feed both still exist without anyone losing sleep over it. Either way, if you’re maintaining skills for Claude and for this new coalition of everyone else, the safe move is keeping the actual instructions and MCP wiring in plain files with no platform syntax baked in, and treating plugin.json as a thin adapter on top. Then whichever format wins, you’re repackaging a folder instead of rewriting a library of skills you already got working.