Repository navigation
Conversation
…n them fully Two related bugs in how capa manages AGENTS.md / CLAUDE.md: 1. `getTargetFilenames` seeded `AGENTS.md` unconditionally as a "universal baseline", so a claude-code-only install produced both `CLAUDE.md` (which Claude reads) and a stray `AGENTS.md` (which Claude never reads). The doc in `docs/providers/claude-code.md` already promised that `AGENTS.md` is "only written if another provider is active" — the code did not honor that. Drop the unconditional seed; fall back to `AGENTS.md` only when the resolved provider set has no declared instructions filename. 2. `agents.base` content was written raw, without capa markers wrapping it. `cleanAgentsFile` calls `removeAllCapaSnippets`, which only strips marker blocks, so any file installed with a base was never fully removed by `capa clean` — the raw base content lingered forever. Wrap the base in a reserved `__base__` marker block (the id was already documented on `AgentFileConfig.base` for exactly this purpose) so the file becomes fully capa-managed. As a bonus, re-running install now refreshes the base in place via upsert instead of clobbering user content outside markers, matching the documented contract. Adds end-to-end tests in `agents-file.test.ts` covering `getTargetFilenames` for representative providers and the full install→clean lifecycle with and without `agents.base`. Updates the existing `clean.test.ts` case to use both `claude-code` and `codex` so it still exercises the multi-file cleanup path under the new behavior. Co-authored-by: Cursor <cursoragent@cursor.com>
…add 'none' mode
Two improvements to how capa exposes tools to MCP-aware agents.
### 1. `setup_tools` returns signatures, not full schemas
`setup_tools` accumulates the active skill set across calls, and each call
previously returned the full MCP `inputSchema` for *every* accumulated tool.
That payload grew quadratically with the number of activations and quickly
bloated the agent's context window.
The new contract: `setup_tools` returns a compact function-style signature
list — `["github.create_issue(title, body, labels?, assignees?)", …]` —
plus the requested-this-call and merged-active skill sets and a one-line
hint. The full input schema is only returned in the `call_tool` error
response when the agent calls invalidly (the natural moment it actually
needs the schema to self-correct).
`call_tool` error paths now follow the MCP-spec pattern for tool execution
failures: content-wrapped result with `isError: true` (previously some
errors were JSON-RPC errors, others were content-wrapped without isError,
inconsistently across the SDK and HTTP paths). The two paths are now
consistent and the schema is attached on:
- executor exceptions (any tool, throws)
- command-tool `{success: false, error: "Missing required argument: …"}`
results — the canonical "agent omitted a required arg" case which
previously returned plain text with no schema and no isError flag
Tool-not-found and tool-not-activated branches deliberately omit the
schema (we don't have one for unknown tools; for not-activated tools the
next-step is `setup_tools`, not a schema-corrected retry).
### 2. New `toolExposure: 'none'` mode
`ToolExposureMode` gains a third value, `'none'`, which makes capa skip
**all** project-local MCP config writes at install time:
- `.mcp.json`, `.cursor/mcp.json`, `.codex/config.toml`
`mcp_servers.capa`, etc. (the main `capa` MCP entry)
- per-sub-agent `capa-<id>` entries (also skipped, and any stale entries
from prior installs are removed for both the main + sub-agent paths so
switching mode is clean)
The capa server still runs and the project endpoints stay live, but the
MCP handler defensively returns an empty `tools/list` and rejects every
`tools/call` with a hint pointing at the `capa sh` CLI fallback. Useful
for users who want capa-managed skills/tools without per-project MCP
file mutations (policy reasons, repo hygiene, etc.).
Sub-agent instruction files are still installed under `'none'` since they
remain informative documentation; the MCP server keys they reference
simply won't resolve — by design.
### Tests
- Pure-function tests in `mcp-handler.test.ts` for `buildToolSignature`,
`buildSetupToolsPayload`, `buildCallToolErrorPayload` (10 new cases,
covering optional `?` marking, required-order preservation, ghost-arg
defense, and the regression-guard that signature output stays string-
only).
- New `mcp-handler.integration.test.ts` exercising `handleMessage` end to
end against a real `SessionManager` + DB:
- `setup_tools` returns signature strings (not schemas)
- skill accumulation surfaces both requested + merged active sets
- `call_tool` attaches the full schema on missing-required-arg
- `call_tool` returns isError (not JSON-RPC error) for not-activated
and not-found cases — and deliberately omits the schema for those
- `toolExposure: 'none'` returns an empty `tools/list` and rejects
every `tools/call` with a `capa sh` hint
- Docs updated in `skills/capabilities-manager/references/` —
`capabilities-schema.md` gains a dedicated "Tool Exposure" section
describing all three modes; `workflows-and-examples.md` clarifies the
new on-demand round trip and mentions the `'none'` fallback path.
Full suite: 1000 pass, 0 fail.
Co-authored-by: Cursor <cursoragent@cursor.com>
fix(agents): only write provider-declared instructions files and clean them fully
Three Copilot review catches:
1. SDK `handleSetupTools` error path (src/server/mcp-handler.ts) now
returns `isError: true` on the result, consistent with the new
`call_tool` error contract and the MCP spec for tool execution
failures. Without this, clients can't distinguish a `setup_tools`
failure (e.g. unknown skill) from a successful activation.
2. HTTP `setup_tools` failure path now returns
`result: { content: [...], isError: true }` instead of a JSON-RPC
`error: { code, message }`. JSON-RPC errors are eaten by most MCP
hosts before they reach the LLM, so the "Available skills: …"
recovery hint we surface was effectively invisible. Brings the HTTP
path in line with the SDK path and `call_tool`.
3. `installSubagentsTask` (src/cli/commands/install-tasks/install-subagents.ts)
now runs `purgeCursorSubAgentMCPEntries` under `toolExposure: 'none'`
too. Cursor doesn't model per-sub-agent MCP entries — its
`capa-<id>` entries can only be cleaned by the purge sweep, and the
per-agent `unregisterSubAgentMCPServer` loop is a no-op for that
provider. The previous `!skipMcpWrites && ...` guard meant
switching to `'none'` left stale `capa-<id>` entries in
`.cursor/mcp.json` indefinitely, contradicting the "no .mcp writes"
contract this mode is supposed to enforce.
Tests:
- New integration case pinning the `setup_tools` unknown-skill response
shape (`isError: true` + `Available skills: …` text).
- New predicate test pinning the `installSubagentsTask` purge gating —
Cursor-style providers must trigger the purge regardless of
`skipMcpWrites`.
Full suite: 1002 pass, 0 fail.
Co-authored-by: Cursor <cursoragent@cursor.com>
feat(tool-exposure): slim setup_tools, schema-on-error in call_tool, add 'none' mode
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
Promotes everything currently on
developtomainfor the v1.9.3 release. Two focused PRs, both already merged intodevelopafter review:setup_tools, schema-on-error incall_tool, add'none'modeNo dependency bumps, no installer/CI changes — pure feature + bugfix release.
Agent instruction file handling (#79)
Two related bugs in how capa manages
AGENTS.md/CLAUDE.md/.github/copilot-instructions.md/replit.md:AGENTS.mdwas written even when no active provider reads it. A claude-code-only install produced bothCLAUDE.md(correct) and a strayAGENTS.md(Claude doesn't read it, just pollution).getTargetFilenamesnow computes the file set purely from active providers'instructions.filename;AGENTS.mdsurvives only as a last-resort fallback when no provider context is known (socapa cleanstill has something to scan). Matches what the docs already promised.agents.basecontent survivedcapa clean. Base content was written raw without capa markers, soremoveAllCapaSnippetscouldn't strip it and the file lingered forever. Now wrapped in a reserved__base__marker block — the idAgentFileConfig.basewas already documented for exactly this purpose, the implementation just wasn't following through. Re-running install also now refreshes the base in place viaupsertSnippetinstead of overwriting user content outside the markers.Migration note for users who already installed under the old behavior with a base configured: re-run
capa install(adds the marker around the new base), then manually delete the residual raw content (or delete the file and re-install). After that, install→clean cycles are fully idempotent.Tool exposure improvements (#80)
setup_toolsreturns signatures, not full schemassetup_toolsaccumulates the active skill set across calls, and each call previously returned the full MCPinputSchemafor every accumulated tool — the payload grew with the number of activations and bloated the agent's context window.The new contract:
{ "success": true, "message": "Activated 1 skill(s); 2 skill(s) and 3 tool(s) now available.", "skills": ["fs-skill"], "activeSkills": ["gh-skill", "fs-skill"], "tools": [ "github.create_issue(title, body, labels?, assignees?)", "github.list_repos(org, type?)", "read_file(path)" ], "hint": "Tools are listed as `name(required, optional?)`. Invoke with `call_tool`; if you pass wrong/missing args, the full input schema is returned in the error." }call_tool(andsetup_tools) errors follow the MCP specBoth meta-tool error paths now consistently return
result: { content, isError: true }instead of mixing content-wrapped responses with JSON-RPC errors (JSON-RPC errors get eaten by most MCP hosts before reaching the LLM). The full input schema is attached on the canonical "agent called wrong" cases:{success: false, error: "Missing required argument: …"}resultsTool-not-found and tool-not-activated branches deliberately omit the schema (we don't have one for unknown tools; for not-activated tools the next step is
setup_tools, not a schema-corrected retry).New
toolExposure: 'none'modeToolExposureModegains a third value,'none', which makes capa skip all project-local MCP config writes at install time:.mcp.json,.cursor/mcp.json,.codex/config.tomlmcp_servers.capa, and per-sub-agentcapa-<id>entriespurgeCursorSubAgentMCPEntriesruns even under'none'so stale entries don't lingerThe capa server still runs and the project endpoints stay live, but the MCP handler defensively returns an empty
tools/listand rejects everytools/callwith a hint pointing at thecapa shCLI fallback. Useful for users who want capa-managed skills/tools without per-project MCP file mutations (policy reasons, repo hygiene, etc.).Sub-agent instruction files are still installed under
'none'since they remain informative documentation; the MCP server keys they reference simply won't resolve — by design.Test plan
bun test— 1002 pass, 0 fail3f1ca26)v1.9.3after mergetoolExposuremodes; verify.mcp.jsoncontent matches the contractMade with Cursor