Skip to content

feat: Agent Plugins v1.0.0 support - #214

Merged
anasstahr merged 3 commits into
aws:mainfrom
laithalsaadoon:minimal/agent-plugins-v1
Aug 6, 2026
Merged

anasstahr merged 3 commits into
aws:mainfrom
laithalsaadoon:minimal/agent-plugins-v1

Conversation

@laithalsaadoon

@laithalsaadoon laithalsaadoon commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

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:

  • Root plugin.json for each plugin (§5.1) and root mcp.json for the three plugins with MCP servers (§7.2.1), alongside the existing client manifests and .mcp.json. MCP transports are unchanged.
  • Skill frontmatter fixes in plugins/*/skills/ per the Agent Skills spec: version and argument-hint moved under metadata, one allowed-tools sequence 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 in aws-data-analytics.
  • plugins/aws-core/hooks/ → plugins/aws-core/com.anthropic.claude-code/hooks/ per §8.2, with a hooks pointer added to the Claude Code manifest. Hooks still fire from the new location.
  • tools/validate_spec.py plus vendored canonical schemas, wired into build.yml and mise 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-devsecops ships as a skills-only portable plugin. §6.2 makes an absent mcp.json a non-error, and its current MCP config uses env-var expansion in url and headers, which §7.2.1 does not permit. Its client-native .mcp.json is 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:

  • Path containment resolves the filesystem before comparing against the plugin root, so a symlink cannot be used to reach outside it. Symlinks anywhere under plugins/ are rejected outright.
  • Every string leaf under extensions is checked at any nesting depth, including inside arrays.
  • MCP command, args, and cwd are containment-checked, including after ${PLUGIN_ROOT} / ${PLUGIN_DATA} substitution, because those values become a subprocess path.
  • Missing jsonschema or pyyaml is a hard failure rather than a skipped check, and mise.toml pins python and uv so the dependencies are present in CI.
  • Malformed input produces an attributable error instead of a traceback, and one bad plugin does not stop the others from being validated.

Verification

  • mise run lint:md (1182 files), python3 tools/validate.py, mise run lint:spec, and gitleaks detect all pass.
  • All 47 plugin skills pass the skills-ref reference validator, up from 24.
  • Hostile fixtures covering absolute paths, ../ traversal, nested and array-valued extension pointers, symlinked plugin and skills directories, traversal in MCP command/args/cwd, and loopback-prefix URL spoofing are all rejected.
  • All four plugins installed from this branch in Claude Code 2.1.219: MCP servers connect, all skills load (19/7/8/13), aws-core hooks 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

  • New plugin
  • New skill
  • Bug fix
  • Documentation update
  • CI/CD change
  • Other

Checklist

  • I have read the CONTRIBUTING guide
  • My changes pass python3 tools/validate.py
  • I have updated relevant documentation

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

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.
{
"type": "command",
"command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/secret-safety.py\"",
"command": "python3 \"${CLAUDE_PLUGIN_ROOT}/com.anthropic.claude-code/hooks/secret-safety.py\"",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@anasstahr

Copy link
Copy Markdown
Contributor

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

@laithalsaadoon

Copy link
Copy Markdown
Contributor Author

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

@anasstahr
anasstahr added this pull request to the merge queue Aug 6, 2026
Merged via the queue into aws:main with commit b33847d Aug 6, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants