feat: subagents — filtered MCP endpoints + per-provider agent files#35
Merged
Conversation
Introduces named sub-agent configurations in `capabilities.yaml`. Each
sub-agent gets a filtered MCP endpoint that exposes only its declared tools,
plus provider-native agent definition files for Claude Code and Cursor.
## What's new
**capabilities.yaml `sub_agents` section**
- New `SubAgent` interface: `id`, `description`, `skills`, `tools`, `instructions`
- Example: an `infra-agent` that only sees CDK/DynamoDB tools, and an
`api-agent` that only sees Lambda tools
**Filtered MCP endpoints (server)**
- New route `/{projectId}/agents/{id}/mcp` served by the same `CapaMCPServer`
class, instantiated with an `agentId`
- `getAgentAllowedToolIds()` resolves declared tool IDs → qualified names
(e.g. `search_cdk_docs` → `aws-iac.search_cdk_docs`)
- Filtering applied in both `handleMessage()` (HTTP JSON-RPC path) and
`setupHandlers()` (SDK transport path) for `tools/list` and `tools/call`
- Unauthorized `tools/call` returns a clear JSON-RPC error, not a silent drop
- Server cache key is `projectId:agentId` so sub-agent servers are independent
**Per-provider agent definition files (install)**
- `claude-code`: writes `.claude/agents/{id}.md` (Claude Code sub-agent
format with `name`/`description`/`model: inherit` frontmatter) + upserts
a `CLAUDE.md` context block
- `cursor`: writes `.cursor/agents/{id}.md` (Cursor subagent format with
`name`/`description`/`model`/`readonly`/`is_background` frontmatter);
Cursor auto-delegates based on the `description` field — no separate MCP
entry needed
- Both providers share the same body: MCP server key, skills list, qualified
tool names, and optional custom `instructions`
**Migration / cleanup**
- `purgeCursorSubAgentMCPEntries()` removes stale `capa-{id}` entries from
`.cursor/mcp.json` and stale `.cursor/rules/*.mdc` files from previous
incorrect implementations
- `capa clean` iterates DB-tracked sub-agents and removes all generated files
and MCP registrations
**DB tracking**
- New `sub_agents` table (`project_id`, `agent_id`, UNIQUE, CASCADE DELETE)
- `upsertSubAgent` / `getSubAgents` / `removeSubAgent` helpers
**Tests (39 new)**
- `sub-agent-filter.test.ts` — `getQualifiedToolName` + filtering logic (8)
- `sub-agent-instructions.test.ts` — agent file writing/removal for both
providers, upsert idempotency, multi-agent isolation (11)
- `sub-agent-mcp.test.ts` — MCP registration, cursor skip, unregister,
purge migration (8)
- `sub-agents.test.ts` (DB) — CRUD, project isolation, cascade delete (8)
- All 335 tests pass (0 failures)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Renames the `sub_agents` YAML key and TypeScript interface field to `subAgents` to align with camelCase naming used by Claude Code and Cursor in their config files. The internal SQLite table name (`sub_agents`) is unchanged as snake_case is conventional for SQL. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Aligns with the lowercase naming convention used by Claude Code and Cursor in their own config keys (e.g. `mcpServers` vs camelCase mixing). All YAML, TypeScript interface, and test references updated. Internal SQLite table name (`sub_agents`) is unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Windows file I/O under coverage instrumentation is slower than the default 5s Bun hook timeout, causing intermittent `afterEach` timeouts when `rmSync` cleans up SQLite temp dirs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Minitour
approved these changes
Apr 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces named sub-agent configurations in
capabilities.yamlunder thesubagentskey. Each sub-agent gets a filtered MCP endpoint that exposes only its declared tools, plus provider-native agent definition files for Claude Code and Cursor.What's new
subagentssection in capabilities.yamlFiltered MCP endpoints
/{projectId}/agents/{id}/mcp— sameCapaMCPServerclass, instantiated with anagentIdtools/listreturns only this agent's declared tools (resolved to qualified names, e.g.aws-iac.search_cdk_docs)tools/callrejects unauthorized tool calls with a clear JSON-RPC errorhandleMessage()(HTTP path) andsetupHandlers()(SDK transport path)projectId:agentIdkey so sub-agent instances are independentPer-provider agent files (on
capa install)claude-code.claude/agents/{id}.md+CLAUDE.mdblockname/description/model: inheritfrontmattercursor.cursor/agents/{id}.mdname/description/model/readonly/is_backgroundfrontmatterCursor reads
.cursor/agents/*.mdto auto-delegate based on thedescriptionfield — no separate MCP server entry per sub-agent is needed.Migration / cleanup
purgeCursorSubAgentMCPEntries()removes stalecapa-{id}entries from.cursor/mcp.jsonand stale.cursor/rules/*.mdcfiles from old implementationscapa cleanremoves all generated agent files and MCP registrations by reading DB-tracked sub-agentsDB tracking
sub_agentstable with CASCADE DELETE on project removalupsertSubAgent/getSubAgents/removeSubAgenthelpersTest plan
src/server/__tests__/sub-agent-filter.test.ts—getQualifiedToolName+ tool filtering logic (8 tests)src/cli/utils/__tests__/sub-agent-instructions.test.ts— agent file writing/removal for both providers, upsert idempotency, multi-agent isolation (11 tests)src/cli/utils/__tests__/sub-agent-mcp.test.ts— MCP registration, cursor skip, unregister, purge migration (8 tests)src/db/__tests__/sub-agents.test.ts— CRUD, project isolation, cascade delete (8 tests)🤖 Generated with Claude Code