Pre-submission checklist
What existing feature or behavior does this improve?
Install-time agent tool policy. applyAgentFrontmatterExtensions (src/runtime-artifact-conversion.cts:3572-3599) already injects per-agent tool policy into agent artifacts as they are written — an effort: key, and a disallowedTools: key sourced from the hardcoded READONLY_AGENT_DISALLOWED_TOOLS map (:3540-3548).
The mechanism exists. It is hardcoded, subtractive-only, and reaches one runtime. This proposes generalizing it, not introducing it.
Current behavior
There is no supported way to grant a GSD agent an MCP server.
The tools: frontmatter in agents/*.md is the only additive mechanism available. Host config cannot substitute: Claude Code's settings.json can only subtract subagent tools (permissions.deny), and mcpServers: frontmatter is ignored outright for plugin subagents. So a user running a code-intelligence MCP server cannot let gsd-executor or gsd-code-reviewer use it — those agents fall back to Grep/Glob and re-derive structure an index already holds.
Every workaround decays:
- Editing installed
agents/*.md by hand — src/install-engine.cts:569 writes agent files as-is, so the next gsd install reverts it, and a plugin update lands a new version directory that never had the edit.
- Patching from a post-install or host hook — per-machine and per-host, reverted by the same two paths, and a Claude hook does nothing for codex, zcode, or antigravity.
- Forking — disproportionate for a two-entry allowlist.
The result is per-machine drift in files that are supposed to be derived artifacts.
Proposed behavior
A config key, resolved at install time, that appends tools to each agent's existing allowlist.
Reuse the model_overrides resolver pattern verbatim (src/install-model-override-resolver.cts:71, :100, :253) — global ~/.gsd/defaults.json merged with project .planning/config.json, project winning on conflict:
Additive only — entries append, never replace, so each agent's deliberate least-privilege posture is preserved. "*" applies to all GSD agents; a per-agent key adds on top.
Apply it between steps 2 and 3 of the per-file pipeline, not at step 4. The pipeline is documented at src/install-profiles.cts:936-942 (ADR-1235 §1):
1. applyAgentPathRewrites
2. processAttribution
<- HERE
3. converter <- derives per-runtime permissions FROM the `tools:` line
4. applyAgentFrontmatterExtensions <- "no-op for a runtime that declares nothing,
e.g. every non-Claude runtime today"
5. normalizeAgentBodyForRuntime
Step 4 is the wrong seam for two reasons, both load-bearing:
- It is Claude-only by construction. It early-returns unless the runtime descriptor declares
hostBehaviors.agentFrontmatterExtensions, and Claude is the sole declared consumer (capabilities/claude/capability.json:108 → ["effort"]). Extending it there reproduces the per-host problem this proposal exists to remove.
- It runs after the converter. Each runtime has already translated
tools: into its own permission model by then, so a late edit is invisible in every non-Claude artifact.
Applying before conversion means all 18 runtimes in runtimeTierDefaults inherit the grant with no per-runtime work and no new frontmatter key — which matters, because the same docstring notes OpenCode/Qwen/Hermes reject unknown frontmatter keys. Extending an existing value is safe where adding a key is not.
Reason and benefit
Concrete and measurable: a GSD agent that can query a code-intelligence MCP server answers structural questions in one call instead of a grep/read loop across many files. On an indexed repo that is the difference between one tool call and dozens, per agent, per phase — GSD dispatches agents constantly, so it compounds.
The second benefit is correctness of a promise already made. gsd-core ships agents whose tools: lines already carry MCP wildcards — agents/gsd-project-researcher.md:4 lists mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*, mcp__perplexity__*. So MCP grants in agent frontmatter are established, shipped idiom. There is simply no way for a user to add one.
Scope of changes
src/ — one resolver mirroring install-model-override-resolver.cts, plus one call site in the staging pipeline before the converter.
tests/ — both tools: YAML forms round-trip; global/project merge precedence; additive (non-replacing) contract; idempotency across repeated installs.
- Docs — the config key.
Explicitly out of scope: any change to the shipped agents/*.md allowlists. The grant is a user's, not a shipped default.
Breaking changes
None. Absent the config key, behavior is byte-identical — the resolver returns nothing and the tools: line is untouched. It is opt-in and additive; no agent can lose a tool it has today, and read-only agents keep their disallowedTools.
One behavior worth stating: an entry resolving to no tool is harmless, because a subagent fails to launch only when no entry in its list resolves, and built-ins always do. The six MCP wildcards already shipping on gsd-project-researcher demonstrate this in practice.
Alternatives considered
Ranked on performance, then simplicity/LOC, then ecosystem support, then maintenance.
| Mechanism |
Perf |
Simplicity / LOC |
Ecosystem |
Maintenance |
Verdict |
1. Config-driven augmentation of tools: before the converter |
0 — install-time only |
~1 resolver reusing the model_overrides shape + 1 call site |
All 18 runtimes, no per-runtime work |
One seam; new runtimes inherit it automatically |
CHOSEN |
2. Add a 'tools' flag to agentFrontmatterExtensions, inject at step 4 |
0 |
Smaller — the injection helper exists |
Claude only unless all 18 descriptors opt in; runs after conversion so non-Claude artifacts never see it |
18 descriptors to touch and the ordering bug remains |
Rejected — reproduces the problem it is meant to solve |
3. Hardcode the MCP servers into agents/*.md upstream |
0 |
Smallest diff |
All runtimes |
Forces every user to carry specific servers; unmaintainable as the list grows |
Rejected — opinionated defaults, not a mechanism |
| 4. Post-install or host hook that rewrites agent files |
0 |
~100 LOC of frontmatter parsing across two YAML forms |
Per-host; a Claude hook does nothing for codex or zcode |
Silently reverted by the next install or plugin update; per-machine |
Rejected — this is the status quo workaround, restated |
5. Omit tools: so agents inherit every subagent tool |
0 |
-1 line per agent |
All runtimes |
Destroys per-agent least privilege; read-only auditors gain Write |
Rejected — the allowlists are deliberate |
Area affected
Installer (primary), agents, config.
Additional context
Agents whose tools: is a YAML block list rather than an inline CSV — gsd-security-auditor, gsd-nyquist-auditor — must keep that form; a correct implementation round-trips both.
Acceptance criteria I would expect:
agent_tools in ~/.gsd/defaults.json grants the listed tools to every GSD agent after gsd install, on at least Claude, codex, and one kilo-family runtime.
.planning/config.json overrides the global value per project.
- Grants are additive — no agent loses a tool, and read-only agents keep their
disallowedTools.
- Both
tools: YAML forms round-trip.
- Re-running
gsd install is idempotent — no duplicate entries.
Companion issue: adding crush as a supported runtime. The two are independent by design — because this proposal acts before per-runtime conversion, a new runtime inherits the feature the moment its descriptor exists, with no second change and no ordering dependency.
Pre-submission checklist
approved-enhancementbefore writing any codeWhat existing feature or behavior does this improve?
Install-time agent tool policy.
applyAgentFrontmatterExtensions(src/runtime-artifact-conversion.cts:3572-3599) already injects per-agent tool policy into agent artifacts as they are written — aneffort:key, and adisallowedTools:key sourced from the hardcodedREADONLY_AGENT_DISALLOWED_TOOLSmap (:3540-3548).The mechanism exists. It is hardcoded, subtractive-only, and reaches one runtime. This proposes generalizing it, not introducing it.
Current behavior
There is no supported way to grant a GSD agent an MCP server.
The
tools:frontmatter inagents/*.mdis the only additive mechanism available. Host config cannot substitute: Claude Code'ssettings.jsoncan only subtract subagent tools (permissions.deny), andmcpServers:frontmatter is ignored outright for plugin subagents. So a user running a code-intelligence MCP server cannot letgsd-executororgsd-code-revieweruse it — those agents fall back toGrep/Globand re-derive structure an index already holds.Every workaround decays:
agents/*.mdby hand —src/install-engine.cts:569writes agent files as-is, so the nextgsd installreverts it, and a plugin update lands a new version directory that never had the edit.The result is per-machine drift in files that are supposed to be derived artifacts.
Proposed behavior
A config key, resolved at install time, that appends tools to each agent's existing allowlist.
Reuse the
model_overridesresolver pattern verbatim (src/install-model-override-resolver.cts:71,:100,:253) — global~/.gsd/defaults.jsonmerged with project.planning/config.json, project winning on conflict:{ "agent_tools": { "*": ["mcp__codegraph__*", "mcp__serena__*"], "gsd-executor": ["mcp__headroom__*"] } }Additive only — entries append, never replace, so each agent's deliberate least-privilege posture is preserved.
"*"applies to all GSD agents; a per-agent key adds on top.Apply it between steps 2 and 3 of the per-file pipeline, not at step 4. The pipeline is documented at
src/install-profiles.cts:936-942(ADR-1235 §1):Step 4 is the wrong seam for two reasons, both load-bearing:
hostBehaviors.agentFrontmatterExtensions, and Claude is the sole declared consumer (capabilities/claude/capability.json:108→["effort"]). Extending it there reproduces the per-host problem this proposal exists to remove.tools:into its own permission model by then, so a late edit is invisible in every non-Claude artifact.Applying before conversion means all 18 runtimes in
runtimeTierDefaultsinherit the grant with no per-runtime work and no new frontmatter key — which matters, because the same docstring notes OpenCode/Qwen/Hermes reject unknown frontmatter keys. Extending an existing value is safe where adding a key is not.Reason and benefit
Concrete and measurable: a GSD agent that can query a code-intelligence MCP server answers structural questions in one call instead of a grep/read loop across many files. On an indexed repo that is the difference between one tool call and dozens, per agent, per phase — GSD dispatches agents constantly, so it compounds.
The second benefit is correctness of a promise already made. gsd-core ships agents whose
tools:lines already carry MCP wildcards —agents/gsd-project-researcher.md:4listsmcp__firecrawl__*,mcp__exa__*,mcp__tavily__*,mcp__ref__*,mcp__jina__*,mcp__perplexity__*. So MCP grants in agent frontmatter are established, shipped idiom. There is simply no way for a user to add one.Scope of changes
src/— one resolver mirroringinstall-model-override-resolver.cts, plus one call site in the staging pipeline before the converter.tests/— bothtools:YAML forms round-trip; global/project merge precedence; additive (non-replacing) contract; idempotency across repeated installs.Explicitly out of scope: any change to the shipped
agents/*.mdallowlists. The grant is a user's, not a shipped default.Breaking changes
None. Absent the config key, behavior is byte-identical — the resolver returns nothing and the
tools:line is untouched. It is opt-in and additive; no agent can lose a tool it has today, and read-only agents keep theirdisallowedTools.One behavior worth stating: an entry resolving to no tool is harmless, because a subagent fails to launch only when no entry in its list resolves, and built-ins always do. The six MCP wildcards already shipping on
gsd-project-researcherdemonstrate this in practice.Alternatives considered
Ranked on performance, then simplicity/LOC, then ecosystem support, then maintenance.
tools:before the convertermodel_overridesshape + 1 call site'tools'flag toagentFrontmatterExtensions, inject at step 4agents/*.mdupstreamtools:so agents inherit every subagent toolWriteArea affected
Installer (primary), agents, config.
Additional context
Agents whose
tools:is a YAML block list rather than an inline CSV —gsd-security-auditor,gsd-nyquist-auditor— must keep that form; a correct implementation round-trips both.Acceptance criteria I would expect:
agent_toolsin~/.gsd/defaults.jsongrants the listed tools to every GSD agent aftergsd install, on at least Claude, codex, and one kilo-family runtime..planning/config.jsonoverrides the global value per project.disallowedTools.tools:YAML forms round-trip.gsd installis idempotent — no duplicate entries.Companion issue: adding
crushas a supported runtime. The two are independent by design — because this proposal acts before per-runtime conversion, a new runtime inherits the feature the moment its descriptor exists, with no second change and no ordering dependency.