Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ two parity-checked manifests. Sibling repo to [devantler-tech/agent-skills](http

By design the marketplace is **not scoped to skills-only** β€” a plugin may bundle any agent resource
(agent skills today; [MCP](https://modelcontextprotocol.io) servers and custom agents as they prove out
across the supported tools), which is what keeps it a tool-neutral, industry-standard marketplace.
across the supported tools), which is what keeps it a tool-neutral, industry-standard marketplace. The
cross-tool capability matrix and the manifest/CI plan for the first non-skill resource are recorded in
[ADR 0001](docs/adr/0001-bundling-mcp-servers-and-custom-agents.md).

This file is the single canonical instructions file for the repository. It is read natively by GitHub
Copilot, and by Cursor, Codex, and Claude (via `CLAUDE.md` β†’ `@AGENTS.md`).
Expand Down
224 changes: 224 additions & 0 deletions docs/adr/0001-bundling-mcp-servers-and-custom-agents.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
# ADR 0001 β€” Bundling MCP servers and custom agents (cross-tool prove-out)

- **Status:** Proposed
- **Date:** 2026-06-30
- **Deciders:** devantler-tech maintainers
- **Issue:** [#39](https://github.com/devantler-tech/agent-plugins/issues/39) (Part of roadmap epic [#38](https://github.com/devantler-tech/agent-plugins/issues/38), Theme 1 β€” *realize "not skills-only"*)

> πŸ€– Generated by the Daily AI Assistant

## Context

`AGENTS.md` and the README already commit this marketplace to being **tool-neutral and not
skills-only** β€” *"a plugin may bundle any agent resource (agent skills today; MCP servers and custom
agents as they prove out across the supported tools)."* That direction is settled maintainer policy
([#15](https://github.com/devantler-tech/agent-plugins/issues/15) was closed *"I do not want this
[skills-only scoping]"*; [#7](https://github.com/devantler-tech/agent-plugins/issues/7) rescoped the
repo cross-tool).

Yet **all six published plugins are skills-only**, so the marketplace's defining cross-tool capability
is unrealized, and the CI gate (`scripts/validate-manifests.sh`) actively *assumes* skills-only β€” it
hard-requires `"skills": "skills/"` plus a non-empty `skills/` directory for **every** plugin. Before we
ship the first non-skill resource we need a grounded answer to two questions:

1. **What is genuinely tool-neutral** about bundling an MCP server or a custom agent across the three
supported tools (Claude Code, GitHub Copilot CLI, VS Code), and what is tool-specific?
2. **What must the manifests and CI change** to describe and validate a non-skill plugin without
weakening the parity guarantees that make the marketplace trustworthy?

This ADR answers both (AC#1 + AC#2 of #39). The first concrete bundle (AC#3) is a follow-up child so
this change stays documentation-only and low-blast-radius.

## Cross-tool capability matrix

How each supported tool consumes the three resource types. Grounded in the official docs (Claude Code
[plugins reference](https://code.claude.com/docs/en/plugins-reference); GitHub Copilot
[about plugins](https://docs.github.com/en/copilot/concepts/agents/about-plugins) +
[CLI plugin reference](https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-plugin-reference);
VS Code [MCP config](https://code.visualstudio.com/docs/agents/reference/mcp-configuration) /
[custom agents](https://code.visualstudio.com/docs/agent-customization/custom-agents)).

> **Correction (2026-06-30, on maintainer review):** an earlier draft of this ADR claimed only Claude
> Code bundles MCP servers / custom agents and that *"there is no cross-tool plugin-bundle standard."*
> That is **wrong**. **GitHub Copilot CLI (and the Copilot cloud agent) ship a first-class plugin
> system** β€” a `plugin.json` manifest plus a `marketplace.json` catalog that bundles skills, **agents,
> MCP servers**, hooks, commands and LSP servers as one installable unit β€” and its schema **overlaps
> Claude Code's almost exactly**. Copilot CLI's manifest search order even *includes*
> `.claude-plugin/plugin.json`, so **this repo's existing `.claude-plugin/marketplace.json` is already a
> valid Copilot CLI marketplace.** The real surface that *can't* bundle is **VS Code** (plugins aren't a
> VS Code concept; it consumes MCP/agents as per-workspace/repo config). The matrix and analysis below
> are the corrected version.

| Capability | Claude Code | GitHub Copilot CLI / cloud agent | VS Code (Copilot) |
|---|---|---|---|
| **Plugin / marketplace bundle** | βœ… `.claude-plugin/marketplace.json` + a plugin dir (`plugin.json`) bundling skills, `agents/`, `.mcp.json`, hooks, commands β€” installed as one unit | βœ… **`plugin.json` + `marketplace.json`** bundling `skills`, `agents`, `mcpServers`, `hooks`, `commands`, `lspServers`; installed via `copilot plugin install` or `enabledPlugins` in `~/.copilot/settings.json` / `.github/copilot/settings.json` | ❌ Not a plugin surface; consumes MCP/agents as per-workspace/repo config |
| **Agent Skills** | βœ… `skills/<skill>/SKILL.md` ([agentskills.io](https://agentskills.io) open spec) | βœ… `skills` field (default `skills/`), `SKILL.md` | βœ… Agent Skills |
| **MCP servers** | βœ… **bundled** β€” `.mcp.json` at plugin root (or inline `mcpServers` in `plugin.json`), auto-discovered | βœ… **bundled** β€” `mcpServers` field β†’ `.mcp.json` / `.github/mcp.json` (or inline), key `mcpServers` | βœ… consumed, **not bundled** β€” `.vscode/mcp.json` / user `mcp.json`, key `servers` |
| **Custom agents** | βœ… **bundled** β€” `agents/*.md` (md + YAML frontmatter), auto-discovered, namespaced `plugin:agent` | βœ… **bundled** β€” `agents` field (default `agents/`), `*.agent.md`, ID from filename | βœ… consumed, **not bundled** β€” `.github/agents/*.agent.md` (renamed from `.chatmode.md`) |

### What is tool-neutral vs tool-specific

- **Tool-neutral (the marketplace's contract β€” now spanning *two* native plugin systems):**
- **The plugin-bundle format itself.** Claude Code and Copilot CLI share a near-identical model: a
`plugin.json` with the same optional component-path fields (`skills`, `agents`, `mcpServers`,
`hooks`, `commands`) and the same on-disk defaults (`skills/`, `agents/`, `.mcp.json`), catalogued by
a `marketplace.json` whose plugin entries carry the same `name`/`source`/`description`/`version`.
Copilot CLI even searches `.claude-plugin/plugin.json`, so one plugin dir is consumed by both tools
**without per-tool restructuring**.
- **Skills** β€” a real open standard (Agent Skills, released 2025-12-18, adopted by 26+ tools). Already
how every plugin works today; nothing changes.
- **The MCP *server definition*** β€” the per-server shape converges across all three tools: stdio =
`{ command, args, env }`; remote = `{ type, url, headers }`. One canonical model describes a server
for every tool.
- **The custom-agent *body*** β€” markdown + YAML frontmatter with the neutral core `name` +
`description` + `tools` + `model`. Converged de-facto across all three.
- **Tool-specific (the residual divergence β€” now small):**
- **VS Code is not a plugin surface.** It is the one supported tool with no plugin/marketplace bundle;
it consumes MCP via `.vscode/mcp.json` (key **`servers`**, not `mcpServers`) and agents via
`.github/agents/*.agent.md`. A bundled MCP/agent must be delivered to VS Code as documented config.
- **The MCP wrapper key:** `mcpServers` (Claude Code, Copilot) vs `servers` (VS Code); the `type` enum
(`stdio`/`local`); VS Code's `inputs`/`oauth`/`sandbox`; Copilot's per-agent `tools` allow-list.
- **Custom-agent file convention:** `agents/*.md` (Claude Code) vs `agents/*.agent.md` (Copilot, where
the ID derives from the filename) vs `.github/agents/*.agent.md` (VS Code), plus Copilot-only
frontmatter (`target`, `mcp-servers`, `user-invocable`).

**The load-bearing consequence (corrected):** bundling an MCP server or a custom agent is **natively
supported by both Claude Code *and* Copilot CLI** from a single plugin dir β€” the marketplace's committed
"not skills-only" promise is realizable as a *true cross-tool bundle*, not a Claude-only bundle plus
docs. Only **VS Code** needs the same resource delivered as documented per-workspace config. So a
non-skill plugin has **one bundled path serving both plugin-native tools** (from the canonical
`.mcp.json` / `agents/`) and **one documented path for VS Code** β€” and the marketplace must be honest
that VS Code is the sole consume-via-config surface, not imply that Copilot needs hand-configuration.

## Decision

### D1 β€” Resource model: a plugin may provide skills, MCP servers, and/or custom agents

A plugin is valid if it provides **at least one** recognized resource. The three resource types and
their on-disk conventions use the component-path fields **shared by both the Claude Code and Copilot CLI
plugin schemas** (same field names, same defaults β€” no tool-specific invention):

| Resource | On-disk | `plugin.json` |
|---|---|---|
| Skills | `skills/<skill>/SKILL.md` (+ provenance frontmatter) | `"skills": "skills/"` (default `skills/` in both schemas) |
| MCP servers | `.mcp.json` at plugin root (`{ "mcpServers": { … } }`) | optional `"mcpServers": ".mcp.json"` (Copilot resolves `mcpServers`β†’`.mcp.json`/`.github/mcp.json`; Claude Code auto-discovers `.mcp.json`) |
| Custom agents | `agents/<name>.md` (md + YAML frontmatter; `.agent.md` for Copilot) | optional `"agents": "agents/"` (default `agents/` in both schemas) |
Comment on lines +95 to +105

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

πŸ—„οΈ Data Integrity & Integration | 🟠 Major | ⚑ Quick win

Clarify the lspServers scope.

The matrix above treats Copilot lspServers as a bundled resource, but D1/D3 only recognize skills, MCP servers, and custom agents. If LSPs are intentionally out of scope, say so here; otherwise the generalized validator/README contract is incomplete for a supported bundle class.

Suggested wording
- A plugin is valid if it provides at least one recognized resource.
+ A plugin is valid if it provides at least one recognized resource (skills, MCP servers, custom agents). LSP servers are intentionally out of scope for this ADR.
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### D1 β€” Resource model: a plugin may provide skills, MCP servers, and/or custom agents
A plugin is valid if it provides **at least one** recognized resource. The three resource types and
their on-disk conventions use the component-path fields **shared by both the Claude Code and Copilot CLI
plugin schemas** (same field names, same defaults β€” no tool-specific invention):
| Resource | On-disk | `plugin.json` |
|---|---|---|
| Skills | `skills/<skill>/SKILL.md` (+ provenance frontmatter) | `"skills": "skills/"` (default `skills/` in both schemas) |
| MCP servers | `.mcp.json` at plugin root (`{ "mcpServers": { … } }`) | optional `"mcpServers": ".mcp.json"` (Copilot resolves `mcpServers`β†’`.mcp.json`/`.github/mcp.json`; Claude Code auto-discovers `.mcp.json`) |
| Custom agents | `agents/<name>.md` (md + YAML frontmatter; `.agent.md` for Copilot) | optional `"agents": "agents/"` (default `agents/` in both schemas) |
### D1 β€” Resource model: a plugin may provide skills, MCP servers, and/or custom agents
A plugin is valid if it provides **at least one** recognized resource (skills, MCP servers, custom agents). LSP servers are intentionally out of scope for this ADR. The three resource types and
their on-disk conventions use the component-path fields **shared by both the Claude Code and Copilot CLI
plugin schemas** (same field names, same defaults β€” no tool-specific invention):
| Resource | On-disk | `plugin.json` |
|---|---|---|
| Skills | `skills/<skill>/SKILL.md` (+ provenance frontmatter) | `"skills": "skills/"` (default `skills/` in both schemas) |
| MCP servers | `.mcp.json` at plugin root (`{ "mcpServers": { … } }`) | optional `"mcpServers": ".mcp.json"` (Copilot resolves `mcpServers`β†’`.mcp.json`/`.github/mcp.json`; Claude Code auto-discovers `.mcp.json`) |
| Custom agents | `agents/<name>.md` (md + YAML frontmatter; `.agent.md` for Copilot) | optional `"agents": "agents/"` (default `agents/` in both schemas) |
🧰 Tools
πŸͺ› LanguageTool

[uncategorized] ~104-~104: The official name of this software platform is spelled with a capital β€œH”.
Context: ...pilot resolves mcpServers→.mcp.json/.github/mcp.json; Claude Code auto-discovers `...

(GITHUB)

πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/adr/0001-bundling-mcp-servers-and-custom-agents.md` around lines 95 -
105, Clarify the bundle resource scope in the D1 resource model by explicitly
stating whether Copilot `lspServers` are supported or out of scope; if they are
excluded, update the section that defines valid plugin resources so it only
covers skills, MCP servers, and custom agents, and make the terminology in the
table and surrounding ADR text consistent with the validator/README contract.


`plugin.json` keeps `name` / `description` / `version` / `author`. The `"skills": "skills/"` field
becomes **conditional** β€” present only when the plugin actually ships skills (both tools treat the
component-path fields as optional and fall back to the default dirs, so an absent field is safe). The
component-path fields are explicit here so the manifest is self-describing for the CI gate (D3).

### D2 β€” Canonical MCP model β†’ bundled (Claude Code + Copilot CLI) + documented (VS Code)

A bundled MCP server is authored **once** as the plugin's `.mcp.json` (`mcpServers` map). That file is
the canonical model, consumed natively by both plugin-native tools and documented for the one that isn't:

- **Claude Code:** the `.mcp.json` is bundled and auto-loaded with the plugin. Use `${CLAUDE_PLUGIN_ROOT}`
for any plugin-relative paths so it survives updates.
- **Copilot CLI / cloud agent:** the **same** `.mcp.json` is bundled β€” `plugin.json`'s `mcpServers`
field resolves to it (and the plugin installs via `copilot plugin install` or `enabledPlugins`). No
hand-configuration; the server ships *inside* the plugin exactly as on Claude Code.
- **VS Code:** the only consume-via-config surface β€” the plugin README documents the equivalent
`.vscode/mcp.json` entry under the **`servers`** key (key-renamed from the canonical `mcpServers`;
same per-server shape).

The README install snippet is the parity mechanism for VS Code alone β€” generated from the one canonical
`.mcp.json`, never independently authored, so it cannot drift in substance. Claude Code and Copilot CLI
need no snippet; they bundle the canonical file directly.

### D3 β€” CI / manifest changes (the gate stays the contract)

`scripts/validate-manifests.sh` (and its self-test `validate-manifests.test.sh`) change as follows.
**No check is weakened** β€” each is generalized to the resource the plugin actually ships, and a new
check is added for MCP well-formedness:

1. **`validate_plugin_json`** β€” replace the unconditional `"skills" == "skills/"` + non-empty `skills/`
requirement with: *the plugin must ship β‰₯1 recognized resource.* Per resource present, keep/add:
- `skills/` present β†’ keep all existing skill checks (the `skills/` field equals `"skills/"`, β‰₯1
`SKILL.md`, and the existing per-skill provenance + agentskills.io spec checks). **Unchanged for
every plugin shipping today.**
- `.mcp.json` present β†’ it must be valid JSON with a non-empty `.mcpServers` object, and each server
must have a `command` (stdio) **or** a `url` (remote). (New check.)
- `agents/` present β†’ β‰₯1 `agents/*.md`, each with YAML frontmatter carrying `name` + `description`.
(New check.)
2. **`validate_readme_parity`** β€” generalize the README **Skills** column to a **Resources** column that
lists the plugin's resources (skill dir names, MCP server names, and/or agent names), and match it
against on-disk resources. Skill-only rows are unaffected in substance (the column simply lists the
same skill names under a broader header).
3. **Marketplace manifests** β€” **no entry-schema change**: plugin entries stay `name` / `description` /
`version` / `source` and the byte-for-byte parity diff is untouched. Only the top-level
`metadata.description` prose broadens from *"Curated agent skills bundled by category"* to cover
*agent resources (skills, MCP servers, custom agents)*. Both manifests change together, as always.
4. **README scope + table header** broaden to match (the "Scope" section already says "any agent
resource"; the table header changes Skills β†’ Resources).

All four land **with the first concrete bundle** (the follow-up child), each paired with a new
`validate-manifests.test.sh` fixture (a PASS case for an MCP-only plugin and a FAIL case for an empty /
malformed `.mcp.json`), so the gate's behavior stays pinned by its self-test and the change is provably
non-weakening before any consumer sees it.

### D4 β€” First concrete bundle (prove-out β†’ follow-up child)

Ship the **Flux MCP (`flux-operator-mcp`)** bundled into the **existing `gitops-kubernetes` plugin** as
the first proof. Rationale: the bundled `gitops-cluster-debug` skill already declares
`compatibility: Requires flux-operator-mcp` and, on failure, tells the user *"the `flux-operator-mcp`
server is not running. Provide the install command."* Bundling the server makes that skill
**self-sufficient on Claude Code** and is the highest-utility, lowest-divergence first bundle β€” a skill
and the MCP it already depends on, shipped together. Copilot CLI / VS Code get the documented config per
D2. This is exactly the "MCP paired with an existing plugin where the skills already assume it" candidate
named in #39.

## Considered alternatives

- **Add a new `mcpServers` / `agents` field to the marketplace.json plugin entry.** Rejected β€” although
the Copilot CLI marketplace-entry schema *does* permit `mcpServers`/`agents`/`skills` on the entry, the
entry is kept intentionally minimal (`name`/`description`/`version`/`source`) and both tools resolve a
plugin's resources from the plugin dir (`plugin.json` + on-disk files), not the marketplace entry.
Adding resource fields to the entry would duplicate on-disk truth and create a second drift surface for
no consumer benefit.
- **A new standalone `mcp` plugin (MCP-only, unpaired).** Rejected as the *first* proof β€” less useful and
higher-divergence than pairing with a skill that already requires the server. Viable later for servers
with no skill counterpart.
- **Bundle a custom agent first instead of an MCP server.** Deferred β€” custom-agent packaging diverges
more across tools (extension, dir, Copilot-only frontmatter) and the marketplace has no agent whose
bundling is already implied by an existing plugin, so an MCP bundle is the cleaner first prove-out.
Custom agents follow once the MCP path is proven (the CI model in D3 already covers `agents/`).
- **Generate the Copilot/VS Code MCP config from a build step.** Deferred as premature β€” with one bundled
server, a documented README snippet from the canonical `.mcp.json` is sufficient and reviewable; a
generator is worth revisiting once several servers ship.

## Consequences

**Positive**

- Realizes the committed "not skills-only" direction with a concrete, grounded plan instead of prose.
- **Doubles the marketplace's native reach for free:** because Copilot CLI's plugin schema overlaps
Claude Code's (and its manifest search order includes `.claude-plugin/plugin.json`), the existing
`.claude-plugin/marketplace.json` is *already* a valid Copilot CLI marketplace β€” bundled MCP/agents
ship natively to both plugin-native tools with no per-tool packaging. (A possible follow-up is to
document/test Copilot CLI installation explicitly; the format work is already done.)
- Keeps the manifests' parity contract and every existing skill check **intact** β€” changes are additive
and generalize rather than weaken (pinned by the self-test).
- One canonical MCP definition serves all three tools; cross-tool parity is explicit, not assumed.
- The first bundle removes a real papercut (a skill that today needs a manual MCP install).

**Negative / risks**

- **Asymmetric delivery (VS Code only):** MCP/agent bundles are auto-installed on both Claude Code and
Copilot CLI; **VS Code** is the sole surface that requires a documented manual config step. This must
be stated plainly in each such plugin's README so cross-tool expectations stay honest β€” but the
asymmetry is now one tool, not two.
- **CI surface grows:** the validator gains MCP/agent branches and new self-test fixtures β€” more to
maintain, but bounded and self-tested.
- **Versioning:** a bundled MCP server pins a server version/source; keeping it fresh is new maintenance
(a `dependabot`/`renovate` or pinning convention to decide when the first server lands).

## Follow-up

- **AC#3 child ([#42](https://github.com/devantler-tech/agent-plugins/issues/42), next):** implement D3
(validator + self-test generalization) **and** D4 (the `flux-operator-mcp` bundle in
`gitops-kubernetes`: `.mcp.json`, README bundled + Copilot/VS Code config snippets, manifest/README
prose broadening) in one PR β€” the design here is the contract it implements.
- Custom-agent bundling (the `agents/` path in D1/D3) proves out after the MCP path, as a later child of
epic #38 Theme 1.