Skip to content

Release v1.9.3: AGENTS.md provider gating + tool-exposure improvements - #81

Merged
Minitour merged 5 commits into
mainfrom
develop
May 29, 2026
Merged

Minitour merged 5 commits into
mainfrom
develop

Conversation

@Minitour

Copy link
Copy Markdown
Member

Summary

Promotes everything currently on develop to main for the v1.9.3 release. Two focused PRs, both already merged into develop after review:

No 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.md was written even when no active provider reads it. A claude-code-only install produced both CLAUDE.md (correct) and a stray AGENTS.md (Claude doesn't read it, just pollution). getTargetFilenames now computes the file set purely from active providers' instructions.filename; AGENTS.md survives only as a last-resort fallback when no provider context is known (so capa clean still has something to scan). Matches what the docs already promised.
  • agents.base content survived capa clean. Base content was written raw without capa markers, so removeAllCapaSnippets couldn't strip it and the file lingered forever. Now wrapped in a reserved __base__ marker block — the id AgentFileConfig.base was already documented for exactly this purpose, the implementation just wasn't following through. Re-running install also now refreshes the base in place via upsertSnippet instead 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_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 — 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 (and setup_tools) errors follow the MCP spec

Both 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:

  • executor exceptions (any tool throws)
  • command-tool {success: false, error: "Missing required argument: …"} results

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).

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, and per-sub-agent capa-<id> entries
  • For Cursor specifically, purgeCursorSubAgentMCPEntries runs even under 'none' so stale entries don't linger
  • Stale entries from prior installs are removed for both paths so switching modes 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.

Test plan

Made with Cursor

Minitour and others added 5 commits May 29, 2026 18:21
…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
@Minitour
Minitour merged commit 85c9eed into main May 29, 2026
17 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant