From b020cbaaa233262072a4c579b3a6e22febccf107 Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Tue, 30 Jun 2026 18:23:48 +0200 Subject: [PATCH 1/2] docs(adr): decide cross-tool MCP/agent bundling design (ADR 0001) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Records the cross-tool capability matrix (Claude Code / Copilot CLI / VS Code) and the manifest/CI plan for shipping the first non-skill plugin resource — AC#1 + AC#2 of #39. Only Claude Code bundles MCP servers/custom agents in a plugin; Copilot CLI and VS Code consume them as per-user/per-workspace config, so a non-skill plugin has two delivery paths (bundled + documented) from one canonical definition. The validator generalizes (no check weakened) and the concrete flux-operator-mcp bundle lands as child #42. Part of #38. --- AGENTS.md | 4 +- ...-bundling-mcp-servers-and-custom-agents.md | 192 ++++++++++++++++++ 2 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 docs/adr/0001-bundling-mcp-servers-and-custom-agents.md diff --git a/AGENTS.md b/AGENTS.md index 9187505..84c425f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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`). diff --git a/docs/adr/0001-bundling-mcp-servers-and-custom-agents.md b/docs/adr/0001-bundling-mcp-servers-and-custom-agents.md new file mode 100644 index 0000000..8fcec1d --- /dev/null +++ b/docs/adr/0001-bundling-mcp-servers-and-custom-agents.md @@ -0,0 +1,192 @@ +# 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), Copilot +[MCP](https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers) / +[custom agents](https://docs.github.com/en/copilot/reference/custom-agents-configuration), VS Code +[MCP config](https://code.visualstudio.com/docs/agents/reference/mcp-configuration) / +[custom agents](https://code.visualstudio.com/docs/agent-customization/custom-agents)). + +| Capability | Claude Code | GitHub Copilot CLI | VS Code (Copilot) | +|---|---|---|---| +| **Plugin / marketplace bundle** | ✅ `marketplace.json` + a plugin dir that bundles skills, `agents/`, `.mcp.json`, hooks, commands — installed as one unit | ❌ No bundle concept; MCP from a registry or hand-edited config | ❌ No bundle concept; extensions are the closest mechanism but are VS-Code-specific | +| **Agent Skills** | ✅ `skills//SKILL.md` ([agentskills.io](https://agentskills.io) open spec) | ✅ Agent Skills | ✅ Agent Skills | +| **MCP servers** | ✅ **bundled** — `.mcp.json` at plugin root (or inline `mcpServers` in `plugin.json`), auto-discovered | ✅ consumed, **not bundled** — `~/.copilot/mcp-config.json`, 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` | ✅ consumed, **not bundled** — `.github/agents/*.agent.md` | ✅ consumed, **not bundled** — `.github/agents/*.agent.md` (renamed from `.chatmode.md`) | + +### What is tool-neutral vs tool-specific + +- **Tool-neutral (the marketplace's contract):** + - **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 (cannot be made neutral today):** + - **The bundle mechanism itself.** Only **Claude Code** ships MCP servers and custom agents *inside an + installable plugin*. Copilot CLI and VS Code consume the same resources, but **only as per-user / + per-workspace configuration** — there is **no cross-tool plugin-bundle standard** for MCP or agents. + - **The MCP wrapper key:** `mcpServers` (Claude Code, Copilot CLI) vs `servers` (VS Code); the `type` + enum (`stdio`/`local`); VS Code's `inputs`/`oauth`/`sandbox`; Copilot's `tools` allow-list. + - **Custom-agent packaging:** file extension (`.md` vs `.agent.md`), directory (`agents/` in a plugin + vs `.github/agents/`), and Copilot-only frontmatter (`target`, `mcp-servers`, `user-invocable`). + +**The load-bearing consequence:** bundling an MCP server or a custom agent is **fully bundled only on +Claude Code**. For Copilot CLI and VS Code the *same* resource must be delivered as documented +configuration the user adds to their own environment. A non-skill plugin therefore has two delivery +paths from **one canonical definition** — bundled (Claude Code) and documented (Copilot CLI + VS Code) — +and the marketplace must be honest about that asymmetry rather than implying byte-identical bundling +everywhere. + +## 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 (matching Claude Code auto-discovery, so no Claude-specific invention): + +| Resource | On-disk | `plugin.json` | +|---|---|---| +| Skills | `skills//SKILL.md` (+ provenance frontmatter) | `"skills": "skills/"` | +| MCP servers | `.mcp.json` at plugin root (`{ "mcpServers": { … } }`) | none required (auto-discovered); may add `"mcpServers": ".mcp.json"` | +| Custom agents | `agents/.md` (md + YAML frontmatter) | none required (auto-discovered); may add `"agents": ["agents/"]` | + +`plugin.json` keeps `name` / `description` / `version` / `author`. The `"skills": "skills/"` field +becomes **conditional** — present only when the plugin actually ships skills (Claude Code ignores +unknown/absent component fields and auto-discovers `.mcp.json` / `agents/` by convention). + +### D2 — Canonical MCP model → bundled (Claude Code) + documented (Copilot CLI, VS Code) + +A bundled MCP server is authored **once** as the plugin's `.mcp.json` (`mcpServers` map). That file is +the canonical model. The two non-bundling tools are served from the same model: + +- **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:** the plugin README documents the equivalent `~/.copilot/mcp-config.json` `mcpServers` + entry (same `command`/`args`/`env`). +- **VS Code:** 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 snippets are the parity mechanism for the non-bundling tools — generated from the +one canonical definition, never independently authored, so they cannot drift in substance. + +### 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 — the entry + is intentionally minimal (`name`/`description`/`version`/`source`) and Claude Code resolves a plugin's + resources from the plugin dir, 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. +- 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:** MCP/agent bundles are auto-installed only on Claude Code; Copilot CLI and VS + Code require a documented manual config step. This must be stated plainly in each such plugin's README + so cross-tool expectations stay honest. +- **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. From ca50c5cb371fe87282f5c7d9f15a24b61b9a7f39 Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Tue, 30 Jun 2026 19:19:42 +0200 Subject: [PATCH 2/2] =?UTF-8?q?docs(adr):=20correct=20ADR=200001=20?= =?UTF-8?q?=E2=80=94=20Copilot=20CLI=20also=20bundles=20MCP/agents?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first draft claimed only Claude Code bundles MCP servers / custom agents and that no cross-tool plugin-bundle standard exists. That is wrong: GitHub Copilot CLI (and the cloud agent) ship a first-class plugin system (plugin.json + marketplace.json) whose schema overlaps Claude Code's almost exactly — its manifest search order even includes .claude-plugin/plugin.json, so this repo's existing marketplace is already a valid Copilot CLI marketplace. The real consume-via-config surface is VS Code alone. - Rewrite the capability matrix + tool-neutral/specific analysis: the plugin-bundle format is shared (Claude Code + Copilot CLI); VS Code is the only non-bundling surface. - D1/D2: ground the plugin.json component fields in BOTH schemas; the canonical .mcp.json is bundled natively on both, documented for VS Code. - Note the marketplace already doubles its native reach for free. Grounded in the maintainer-linked official docs: - https://docs.github.com/en/copilot/concepts/agents/about-plugins - https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-plugin-reference Co-Authored-By: Claude Opus 4.8 (1M context) --- ...-bundling-mcp-servers-and-custom-agents.md | 124 +++++++++++------- 1 file changed, 78 insertions(+), 46 deletions(-) diff --git a/docs/adr/0001-bundling-mcp-servers-and-custom-agents.md b/docs/adr/0001-bundling-mcp-servers-and-custom-agents.md index 8fcec1d..0b6b264 100644 --- a/docs/adr/0001-bundling-mcp-servers-and-custom-agents.md +++ b/docs/adr/0001-bundling-mcp-servers-and-custom-agents.md @@ -32,22 +32,39 @@ 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), Copilot -[MCP](https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers) / -[custom agents](https://docs.github.com/en/copilot/reference/custom-agents-configuration), VS Code -[MCP config](https://code.visualstudio.com/docs/agents/reference/mcp-configuration) / +[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)). -| Capability | Claude Code | GitHub Copilot CLI | VS Code (Copilot) | +> **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** | ✅ `marketplace.json` + a plugin dir that bundles skills, `agents/`, `.mcp.json`, hooks, commands — installed as one unit | ❌ No bundle concept; MCP from a registry or hand-edited config | ❌ No bundle concept; extensions are the closest mechanism but are VS-Code-specific | -| **Agent Skills** | ✅ `skills//SKILL.md` ([agentskills.io](https://agentskills.io) open spec) | ✅ Agent Skills | ✅ Agent Skills | -| **MCP servers** | ✅ **bundled** — `.mcp.json` at plugin root (or inline `mcpServers` in `plugin.json`), auto-discovered | ✅ consumed, **not bundled** — `~/.copilot/mcp-config.json`, 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` | ✅ consumed, **not bundled** — `.github/agents/*.agent.md` | ✅ consumed, **not bundled** — `.github/agents/*.agent.md` (renamed from `.chatmode.md`) | +| **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.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):** +- **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 = @@ -55,53 +72,60 @@ How each supported tool consumes the three resource types. Grounded in the offic 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 (cannot be made neutral today):** - - **The bundle mechanism itself.** Only **Claude Code** ships MCP servers and custom agents *inside an - installable plugin*. Copilot CLI and VS Code consume the same resources, but **only as per-user / - per-workspace configuration** — there is **no cross-tool plugin-bundle standard** for MCP or agents. - - **The MCP wrapper key:** `mcpServers` (Claude Code, Copilot CLI) vs `servers` (VS Code); the `type` - enum (`stdio`/`local`); VS Code's `inputs`/`oauth`/`sandbox`; Copilot's `tools` allow-list. - - **Custom-agent packaging:** file extension (`.md` vs `.agent.md`), directory (`agents/` in a plugin - vs `.github/agents/`), and Copilot-only frontmatter (`target`, `mcp-servers`, `user-invocable`). - -**The load-bearing consequence:** bundling an MCP server or a custom agent is **fully bundled only on -Claude Code**. For Copilot CLI and VS Code the *same* resource must be delivered as documented -configuration the user adds to their own environment. A non-skill plugin therefore has two delivery -paths from **one canonical definition** — bundled (Claude Code) and documented (Copilot CLI + VS Code) — -and the marketplace must be honest about that asymmetry rather than implying byte-identical bundling -everywhere. +- **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 (matching Claude Code auto-discovery, so no Claude-specific invention): +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.md` (+ provenance frontmatter) | `"skills": "skills/"` | -| MCP servers | `.mcp.json` at plugin root (`{ "mcpServers": { … } }`) | none required (auto-discovered); may add `"mcpServers": ".mcp.json"` | -| Custom agents | `agents/.md` (md + YAML frontmatter) | none required (auto-discovered); may add `"agents": ["agents/"]` | +| Skills | `skills//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/.md` (md + YAML frontmatter; `.agent.md` for Copilot) | optional `"agents": "agents/"` (default `agents/` in both schemas) | `plugin.json` keeps `name` / `description` / `version` / `author`. The `"skills": "skills/"` field -becomes **conditional** — present only when the plugin actually ships skills (Claude Code ignores -unknown/absent component fields and auto-discovers `.mcp.json` / `agents/` by convention). +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) + documented (Copilot CLI, VS Code) +### 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. The two non-bundling tools are served from the same model: +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:** the plugin README documents the equivalent `~/.copilot/mcp-config.json` `mcpServers` - entry (same `command`/`args`/`env`). -- **VS Code:** the plugin README documents the equivalent `.vscode/mcp.json` entry under the **`servers`** - key (key-renamed from the canonical `mcpServers`; same per-server shape). +- **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 snippets are the parity mechanism for the non-bundling tools — generated from the -one canonical definition, never independently authored, so they cannot drift in substance. +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) @@ -147,10 +171,12 @@ named in #39. ## Considered alternatives -- **Add a new `mcpServers` / `agents` field to the marketplace.json plugin entry.** Rejected — the entry - is intentionally minimal (`name`/`description`/`version`/`source`) and Claude Code resolves a plugin's - resources from the plugin dir, 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. +- **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. @@ -167,6 +193,11 @@ named in #39. **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. @@ -174,9 +205,10 @@ named in #39. **Negative / risks** -- **Asymmetric delivery:** MCP/agent bundles are auto-installed only on Claude Code; Copilot CLI and VS - Code require a documented manual config step. This must be stated plainly in each such plugin's README - so cross-tool expectations stay honest. +- **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