Repository navigation
Description
Context
Parity pass against DeusData/codebase-memory-mcp (HEAD 97ce23f, v0.9.0), tracked by cdeust/enterprise-backlog#26. Companion to #170 (tabular response encoding), which reduces bytes per response; this issue reduces bytes per session.
CBM ships three tool profiles (src/mcp/mcp.h:97-103, src/mcp/mcp.c:730-757), selected by CLI flag:
| Profile | Tools | Set |
|---|---|---|
ALL |
15 | everything |
ANALYSIS |
11 | search_graph, query_graph, trace_path, get_code_snippet, get_graph_schema, get_architecture, search_code, list_projects, index_status, check_index_coverage, detect_changes |
SCOUT |
7 | search_graph, trace_path, get_code_snippet, get_architecture, list_projects, index_status, check_index_coverage |
Three things follow from the profile, not just the tool list:
initializereturns differentinstructionsper profile (mcp.c:1192-1199) — the server describes itself in the shape it was started in.tools/listis cursor-paginated over the allowed set (mcp.c:821-866,nextCursor,MCP_TOOLS_PAGE_SIZE), with the cursor clamped to the profile's count.- Write and HTTP surfaces are gated by profile (
cbm_mcp_tool_profile_allows_http), so a read-only profile is enforced, not merely advertised.
The gap
Cortex registers ~60 MCP tools, all of them, to every client, on every session — the largest fixed token cost in the ecosystem, paid before the user types anything. There is no profile mechanism (grep -r "tool_profile\|TOOL_PROFILE" over Cortex/ outside .venv returns nothing), no tools/list pagination, and one set of server instructions regardless of how the server was started.
Most sessions use a small fraction of that surface. A session doing recall/onboarding never calls wiki_migrate, wiki_purge, rebuild_profiles, import_sessions, backfill_memories or forget — but pays for their schemas anyway, and the model must discriminate the one it wants from sixty.
For comparison, cdeust/automatised-pipeline already ships ToolProfile::{Core, Full} (src/tool_profile.rs:44). Cortex has four times AP's tool count and none of the mechanism.
Acceptance criteria
- A profile enum with named, documented sets. Derive the membership from measured session usage (the telemetry already exists —
get_telemetry), not from intuition; the derivation is quoted in the PR. - Profile selected at server start (CLI flag and/or plugin config), defaulting to the profile that serves the common session. The default is a behaviour change: it goes in the CHANGELOG (§13 H4) and the plugin manifest.
initializereturns profile-specificinstructions.tools/listsupports cursor pagination withnextCursor, clamped to the profile's allowed count. Absent cursor returns the full allowed set — CBM's behaviour, and the one that cannot break existing clients (mcp.c:924-932).- Destructive tools are gated, not merely hidden.
forget,wiki_purge,wiki_migrate, anddelete-class handlers must be rejected when the active profile excludes them, with a test asserting the rejection — hiding a tool fromtools/listwhile still executing it on call is a security hole, not a token optimization. - Measured before/after: tokens in the
initialize+tools/listexchange per profile, committed as a benchmark (§3.2 / §8 — no unsourced numbers). - Every arm asserted (§13 A3): tool allowed, tool excluded-and-called, unknown profile name, cursor beyond the end, malformed cursor.
Non-goals
Removing tools. Every current tool stays reachable under full. §1.2 (Open/Closed): adding a tool must not require editing profile membership in more than one place.
Definition of Done
Completion Ledger (§13.2), one row per profile × per arm in criterion 7, plus the measurement from criterion 6. A profile whose membership is asserted by no test is an unmet clause.
Activity
Merge pull request #189 from cdeust/feat/mcp-protocol-176-177
feat(mcp): tool profiles + prompts + per-profile instructions (cdeust…