Repository navigation
feat: Agent Plugins v1.0.0 support - #214
Conversation
Adds root plugin.json and mcp.json, moves aws-core hooks under the com.anthropic.claude-code namespace, fixes skill frontmatter at the canonical sources, and adds a conformance validator wired into mise run validate. Frontmatter edits are made in skills/ and propagated to plugin copies with tools/sync-plugin-skills.py, so lint:skill-parity stays green.
1535f67 to
ed08619
Compare
| { | ||
| "type": "command", | ||
| "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/secret-safety.py\"", | ||
| "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/com.anthropic.claude-code/hooks/secret-safety.py\"", |
There was a problem hiding this comment.
Does this work? Claude Code is not on the compatible clients list https://agent-plugins.org/compatible-clients
We also need an equivalent for Codex because this used to work with it before (they also support CLAUDE_PLUGIN_ROOT)
There was a problem hiding this comment.
Tested this live rather than reasoning about it. You're right on both counts, and the Codex half was a regression I introduced — fixed in dc96b8f.
Setup. One Lambda MicroVM per cell in us-east-1, all in parallel, models served by Bedrock. Each cell installs a minimal plugin (one skill, one PreToolUse hook on Bash/shell) into the client under test, triggers a tool call, and passes only if the hook's side-effect file appears — so a hook that is parsed but never executed counts as a failure.
Claude Code declares engines.node >= 22, and Node 18/20 are EOL (2025-04-30, 2026-04-30), so the matrix covers the three supported lines:
| Node 22.23.2 (LTS Jod) | Node 24.19.0 (LTS Krypton) | Node 26.6.0 (current) | |
|---|---|---|---|
Claude Code, hooks in com.anthropic.claude-code/ |
PASS | PASS | PASS |
| Claude Code, hooks at plugin root (pre-PR control) | PASS | PASS | PASS |
Codex, ${CODEX_PLUGIN_ROOT} in the hook command |
FAIL | FAIL | FAIL |
Codex, hooks at plugin root, ${CODEX_PLUGIN_ROOT} (control) |
FAIL | FAIL | FAIL |
Codex, ${CLAUDE_PLUGIN_ROOT} in the hook command |
PASS | PASS | PASS |
Codex, after the fix in dc96b8f |
PASS | PASS | PASS |
Claude Code 2.1.223 and Codex CLI 0.146.1 throughout. Results were identical on all three Node versions, so nothing here is runtime-dependent.
Does the namespaced location work? Yes for Claude Code — the hook fires from com.anthropic.claude-code/hooks/, and the pre-PR control passes too, so the directory move alone changes nothing for that client. Agreed it isn't on the compatible-clients list; hooks aren't a portable component type in v1 either, which is why they belong in a client extension namespace under §8.2 rather than the portable core. That made this a question only a live install could answer.
The Codex equivalent. Confirmed needed. Codex reads hooks from .codex-plugin/plugin.json, and my change only added the pointer to the Claude manifest, so Codex had no hooks at all. Its error was explicit:
Command could not run because a configured pre-tool hook is missing:
`/com.anthropic.claude-code/hooks/probe.py`
The leading slash is ${CODEX_PLUGIN_ROOT} expanding to empty. Your parenthetical was right: Codex expands ${CLAUDE_PLUGIN_ROOT}, not a variable of its own name — that's the only difference between the failing and passing Codex rows.
Two things worth noting beyond the fix. The plugin-root control row failing means this was already broken on main before this PR, since main has no hooks key in the Codex manifest either. And a hook whose path doesn't resolve blocks the tool call rather than degrading quietly, so on main today a Codex user with aws-core installed gets PreToolUse failing closed on every shell call.
The fix is one line — "hooks": "./com.anthropic.claude-code/hooks/hooks.json" in .codex-plugin/plugin.json, same hooks.json, same namespace directory, ${CLAUDE_PLUGIN_ROOT} retained in the command since that's what Codex expands.
There was a problem hiding this comment.
Added Kiro CLI to the live matrix, since it's the client this most needs to work in. Same MicroVM harness, us-east-1, authenticated with a KIRO_API_KEY. Kiro CLI 2.16.1, Node 22.23.2, alongside Claude Code 2.1.223 and Codex 0.146.1.
Headline: Kiro CLI 2.16.1 has no Agent Plugins loader, so nothing in this PR is installable there yet — but the components it is listed for both work when placed where Kiro looks for them.
| Scenario | Result |
|---|---|
SKILL.md in .kiro/skills/ |
PASS — skill activates and is named back |
Same SKILL.md at plugins/<name>/skills/ (portable layout) |
FAIL — not discovered |
MCP server in .kiro/settings/mcp.json (via kiro-cli mcp add) |
PASS — tool called, real result returned |
MCP server in .kiro/mcp.json (workspace path) |
FAIL — not loaded |
PreToolUse hook via agent config |
PASS — hook script ran |
Evidence the passes are real rather than the model being agreeable: the skill cell returned the skill's own token KIRO-SKILL-LOADED and then named kiro-canary-skill; the MCP cell shows [tool] Running: @awsknowledge/aws___search_documentation and answered with a document title only the server could produce. In the plugin-layout cell the model listed its ten built-in tools and no skill, which is what a client with no plugin loader looks like.
What the binary says. kiro-cli exposes chat, agent, mcp, settings, whoami, doctor — no plugin subcommand. String search across kiro-cli-chat finds zero occurrences of plugin.json, agent-plugins, hooks.json, or any *_PLUGIN_ROOT variable, while PreToolUse, "hooks", "skills", and "mcpServers" are all present as agent-config keys. Skills resolve from .kiro/skills/*/SKILL.md. That lines up with Kiro being listed on agent-plugins.org for Agent Skills and MCP only, and it means hooks in a client extension namespace are simply invisible to Kiro rather than broken by this PR.
Two caveats on what I could not measure. Kiro CLI 3.0 is the early-release engine (--v3 / --agent-engine v3) and is where the docs put standalone .kiro/hooks/*.json and permissions.yaml. I could not test it headless: --trust-all-tools is rejected under --agent-engine=v3, and without it the invocation hangs past a 100s timeout with stdin closed, on both --v3 and --agent-engine v3 — consistent with the documented note that v3 needs the TUI. Everything above is therefore the v2 engine, which answers headless in seconds. Someone with an interactive terminal should confirm the v3 numbers.
Practical consequence for this PR: nothing needs to change for Kiro today. Worth knowing that a Kiro user gets our skills only by copying them into .kiro/skills/, and our MCP server only via kiro-cli mcp add, which is roughly what the user guide already says about Kiro connecting to the AWS MCP Server directly. If Kiro adds a plugin loader in 3.x, the portable plugin.json and mcp.json this PR adds are what it would consume.
|
Hi Laith, i tested with claude/codex and seems to work, thank you for that! Could you try the changes with kiro and confirm it works? If so the PR LGTM |
Yep, stand by! Testing with Kiro CLI now |
Description
Adds Agent Plugins v1.0.0 support to all four plugins, so they load in any client that implements the spec. Existing Claude Code, Codex, and Cursor packaging is untouched.
The change is additive:
plugin.jsonfor each plugin (§5.1) and rootmcp.jsonfor the three plugins with MCP servers (§7.2.1), alongside the existing client manifests and.mcp.json. MCP transports are unchanged.plugins/*/skills/per the Agent Skills spec:versionandargument-hintmoved undermetadata, oneallowed-toolssequence rewritten as the space-separated string form, two descriptions brought under the 1024-character limit. Without these a conformant client silently skips 23 of the 47 plugin skills, including all 8 inaws-data-analytics.plugins/aws-core/hooks/→plugins/aws-core/com.anthropic.claude-code/hooks/per §8.2, with ahookspointer added to the Claude Code manifest. Hooks still fire from the new location.tools/validate_spec.pyplus vendored canonical schemas, wired intobuild.ymlandmise run validate. It validates the manifests against the schemas and enforces the rules the schemas cannot express: path containment against the resolved plugin root (§4.1), placeholder restrictions (§9.2), and MCP url/header rules (§7.2.1).aws-agents-for-devsecopsships as a skills-only portable plugin. §6.2 makes an absentmcp.jsona non-error, and its current MCP config uses env-var expansion inurlandheaders, which §7.2.1 does not permit. Its client-native.mcp.jsonis unchanged, so behavior there is the same as today. A portable MCP config for it can follow separately.Notes for reviewers
The validator is written to be safe against hostile plugin content, since it runs in CI on pull requests:
plugins/are rejected outright.extensionsis checked at any nesting depth, including inside arrays.command,args, andcwdare containment-checked, including after${PLUGIN_ROOT}/${PLUGIN_DATA}substitution, because those values become a subprocess path.jsonschemaorpyyamlis a hard failure rather than a skipped check, andmise.tomlpinspythonanduvso the dependencies are present in CI.Verification
mise run lint:md(1182 files),python3 tools/validate.py,mise run lint:spec, andgitleaks detectall pass.skills-refreference validator, up from 24.../traversal, nested and array-valued extension pointers, symlinked plugin andskillsdirectories, traversal in MCPcommand/args/cwd, and loopback-prefix URL spoofing are all rejected.aws-corehooks fire from the namespaced path.Not in scope, possible follow-ups: frontmatter in the repo-root
skills/tree, and the OAuth-based MCP config in #183.Type of Change
Checklist
python3 tools/validate.pyBy submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.