Skip to main content

ADR-023: Which of the 75 tools should still exist

The Decision section is deliberately unfilled. This ADR measures, researches and argues. The disposition is the owner's, as with ADR-021.

Context and Problem Statement​

This repository was first committed 2025-07-02 against @modelcontextprotocol/sdk ^1.0.0. It is now thirteen months old and the SDK is at ^1.30.0. It was designed around mid-2025 assumptions about what an MCP host could do for itself.

Several of those assumptions no longer hold. The question this ADR asks is narrow and answerable: of the 75 tools this server exposes, which should still exist?

It is asked now because two pieces of queued work are both sized in tools β€” #1416 collapses four registries into one, and ADR-021 option B migrates tools off the legacy AI layer. Both do less work if fewer tools survive, and both would otherwise spend effort on tools that should not exist.

Measured, on main at v2.7.0​

Every figure below is reproducible by the command beside it. Nothing is carried over from the v3.0 PRD or ADR-021 β€” both have already been found wrong once each.

measurementvaluehow
tools exposed over the wire75MCP tools/list against the built server
tools/list payload94,571 bytes (~24K tokens)same
tools declaring outputSchema0same
tools with a real CE-MCP directive12grep -cE "^\s+case '" src/tools/ce-mcp-tools.ts
tool modules importing the AI layer10grep -rl 'prompt-execution|ai-executor' src/tools/
tools absent from TOOL_CATALOG4wire list minus catalog keys

The four uncatalogued tools are get_gaps, search_codebase, set_project_path and update_knowledge. They are dispatchable but invisible to search_tools. The v3.0 PRD identified them in June; they are still uncatalogued.

Decision Drivers​

  • The MCP spec now assigns to the host what this server built for itself.
  • The server is several times over the spec's own context threshold.
  • Market evidence says generation is commodity and drift is the unmet need.
  • Fewer surviving tools makes #1416 and ADR-021 option B smaller.

The spec moved​

Client Best Practices (2026-07-28) describes progressive discovery as a host responsibility: the host exposes a search_tools meta-tool, defers injecting definitions, and fetches full schemas on demand. It states plainly:

"Some model providers already offer built-in tool search. For example, OpenAI and Anthropic support this natively… When available, you may prefer the platform's tool search over a custom implementation."

This server ships its own search_tools and load_prompt as CE-MCP meta-tools. That was a reasonable thing to build in 2025. It is now a reimplementation of a host feature.

The server is over the spec's own threshold​

The same document recommends switching to progressive discovery once tool definitions consume 1–5% of the context window. At ~24K tokens this server is:

11.8% of a 200K context window
2.4% of a 1M context window

Three to twelve times the recommended threshold. This server does not merely benefit from progressive discovery β€” it forces the mitigation on every host that connects.

Zero output schemas​

The same document describes programmatic tool calling ("code mode"), where the host generates a typed API from tool schemas and the model writes code against it. Precise types require outputSchema, and it says "the real fix is for server authors to provide outputSchema."

This server declares none, on any of 75 tools. It opts out of that path entirely.

What the market actually wants​

Current ADR guidance (adr.github.io, Fowler, 2026 practitioner guides) is consistent on two points:

  • Plain Markdown in docs/adr/ is enough for most teams; tools get adopted only when a specific pain appears that Markdown cannot solve.
  • The dominant pain is staleness: "Decisions don't stay in the file β€” they change in Slack, GitHub and Jira, and nobody updates the ADR." And "there's no dominant tool, which tells you the field is young."

Writing ADRs is not the hard part. Noticing that an ADR stopped being true is.

This repository already proved that internally. scripts/check-adr-drift.sh, built in a few hours, found that ADR-001 claims SSE transport while the code is stdio-only, that ADR-006 claims grammars ADR-017 removed, and that ADR-018a forbids a pattern twelve files use. Twelve ADR-generation tools found none of it.

Considered Options​

  1. Keep all 75. Do #1416 and option B at full scope.
  2. Remove only what the host now provides, keep everything else.
  3. Remove host-native duplicates and consolidate redundant clusters, and record where evidence is insufficient to decide.
  4. Narrow to drift detection, removing most generation tooling.

Proposed classification​

REMOVE β€” the host now does this (11 tools)​

tool(s)now provided by
read_file, write_file, list_directory, read_directoryhost file tools in every major MCP client
list_rootsthe MCP roots capability
llm_web_searchnative web search in every frontier host
llm_cloud_management, llm_database_managementprompt wrappers over host capability
search_tools, load_promptthe spec assigns this to the host; OpenAI and Anthropic ship it natively
get_current_datetimehost

search_tools and load_prompt are the strongest case, because the argument is not "another tool does this too" but "the specification says this is not the server's job."

Removing these eleven costs ~15% of the tool surface and a corresponding share of the 24K token payload, for no capability a modern host lacks.

OUT OF SCOPE β€” the aggregator (10 tools)​

ADR-021 already ruled the aggregator "a separate commercial question". Unchanged here.

DIES WITH THE AI LAYER (1 tool)​

check_ai_execution_status exists solely to explain the legacy execution mode. Under ADR-021 option B it has nothing left to report.

REVIEW β€” insufficient evidence to classify (the remainder)​

This is the honest gap in this ADR: there is no usage data reaching a durable store. Every classification below the line above is reasoning about code shape, not about use.

An earlier revision of this ADR said "nothing in src/ records which tools are actually called." That was wrong, and the correction matters, because it changes the gap from a project into three edits. Two mechanisms already record every call by name, and one already computes exactly the per-tool counts this section needs:

what existswherewhy no usable evidence comes out
complete per-tool count Map, sortedsrc/utils/knowledge-graph-manager.ts:406-416.slice(0, 10) truncates it before persisting β€” the full map is built and discarded
every execution recordedtrackToolExecution, src/index.ts:8219, called at :4201 / :4208persists under os.tmpdir() (knowledge-graph-manager.ts:44-52) β€” wiped by OS temp cleanup, never survives a reboot
distinct tools per sessionsrc/utils/conversation-memory-manager.ts:131,163same os.tmpdir() problem; a dedup'd list, no counts
complete MonitoringManager with MetricCategory.TOOLsrc/utils/monitoring.ts β€” retired, #1487had zero production call sites and no persistence; it could not have served this need. Retired 2026-08-27

And one blind spot that bears directly on this section: the CE-MCP directive path at src/index.ts:3875-3888 returns before the dispatch switch and before trackToolExecution, so all twelve CE_MCP_DIRECTIVE_TOOLS are unrecorded whenever CE-MCP mode is on. perform_research is one of them β€” a tool whose disposition this section defers pending data that this bug guarantees will never exist for it.

So the evidence is computed and thrown away, not absent. Ten entries survive out of 75, into a directory the OS deletes. Every cluster below falls beyond that cut.

Every line reference in the table above was verified against 0d3817b7 on 2026-08-27 and will drift. #1416 collapses four registries and removes roughly 4,000 lines from src/index.ts; the :3875, :4201, :8219 citations will all be wrong afterwards. They are given because they are checkable today and the argument depends on being checkable. When #1416 lands, re-anchor them to symbol names β€” which is #1463's whole point about ADR-014, and the reason this note exists rather than the same mistake.

Clusters that look consolidatable but should not be cut on inference alone:

  • memory (6) β€” expand_memory, memory_loading, expand_analysis_section, get_memory_stats, get_conversation_snapshot, query_conversation_history
  • workflow (5) β€” get_workflow_guidance and get_development_guidance are adjacent enough to question
  • research (4 after removals) β€” create_research_template, generate_research_questions, incorporate_research, perform_research

KEEP β€” the reason the server exists​

The 12 ADR tools, the 4 analysis tools, 6 content-security (masking is not a host capability), 7 deployment, and 3 rules. These are domain work a host cannot do, which is the test the spec's "single responsibility: one clear domain" guidance implies.

Decision​

The supported tool surface is 54. Recorded 2026-08-27 (#1490).

Read the verb precisely, because it is doing work: these tools are removed from the supported surface, not deleted. Nothing here authorises a deletion, and the Confirmation section below explains why it cannot.

Out of the supported surface β€” the host now does this (11)​

read_file, write_file, list_directory, read_directory, list_roots, llm_web_search, llm_cloud_management, llm_database_management, search_tools, load_prompt, get_current_datetime.

search_tools and load_prompt decide the rest of the list. The argument for them is not "a host tool does this too" β€” it is that the specification assigns progressive discovery to the host, and OpenAI and Anthropic ship it natively. A server implementing it is reimplementing a host feature, and this server is 3–12Γ— over the threshold at which the spec says to stop.

Dies with the AI layer (1)​

check_ai_execution_status exists to explain the legacy execution mode. Under ADR-021 option B it has nothing left to report. It goes when the layer does, not before.

Out of scope (10)​

The aggregator. ADR-021 ruled it "a separate commercial question" and that stands unchanged here.

Deferred β€” memory (6), workflow (5), research (4)​

Not classified. This ADR said plainly that this section "is reasoning about code shape, not about use", and that limit is respected rather than overridden. Deferred to ADR-024, whose precondition is usage data.

That precondition is now satisfiable. The correction in #1486 established that the evidence was being computed and discarded, and #1488 and #1489 fixed all three losses: the .slice(0, 10) before persistence, the os.tmpdir() store the OS clears, and the CE-MCP directive path that recorded nothing for twelve tools. Counts now accumulate in .mcp-adr-cache/ and survive a restart.

So ADR-024 is not blocked on building anything. It is blocked on elapsed use β€” which is an honest thing for an ADR to wait on, and a different thing from the indefinite deferral this section originally implied.

What this does not decide​

Whether any of the 11 is ever deleted. That is retirement.py's question, asked per asset, each with its own admission under the CE-MCP migration milestone.

Correction (2026-08-28). That routing was unworkable as written. The CE-MCP migration milestone's own description says it "stays empty until #1416 lands: the batches are defined BY the registry" β€” so a retirement admitted there would have been blocked behind an unassigned issue. Acting on the sentence above, #1537 and #1538 were filed into that milestone and broke its precondition within the hour.

Retirement admissions now go to the Cleanup milestone, which has no such precondition. The CE-MCP milestone keeps only what ADR-021 requires of it: the per-batch migration, still empty until #1416.

The substance is unchanged β€” per asset, retirement.py first, its own admission, and RETIREMENT_REVIEW is the expected verdict rather than a blocker. Only the container moves.

Consequences​

It resizes queued work. 75 βˆ’ 11 host-native βˆ’ 10 aggregator = 54. #1416 builds a ToolDefinition[] for 54 entries rather than 75, and ADR-021 option B has fewer tools to migrate. Both were explicitly waiting on this number.

It sharpens what the product is. If the market's unmet need is drift detection and this repo has already built a working drift checker, then the ADR-generation surface is the commodity part and the verification surface is the differentiated part. That is a positioning consequence, not only a scope one.

It unblocks five issues that were gated on nothing but this section: #1416, #1477, #759, #750, and #1461's rule half.

It creates a deprecation obligation. Eleven tools are advertised over the wire today and clients may be calling them. Removal from the surface means: excluded from #1416's registry, marked deprecated in TOOL_CATALOG and the docs, and announced in CHANGELOG.md β€” before any of them stops responding. public_contracts is one of the dimensions retirement.py deliberately leaves blind, so this obligation is a human one and is recorded here rather than assumed.

It leaves outputSchema unaddressed. This ADR measured zero output schemas across 75 tools and did not decide anything about them. A 54-tool registry is the moment to add them, and #1416 should not close without a position β€” but that position is not taken here, and pretending otherwise would be the same overreach this ADR declined elsewhere.

Four tools remain uncatalogued β€” get_gaps, search_codebase, set_project_path, update_knowledge. They are dispatchable and absent from TOOL_CATALOG, so this Decision does not cover them: they are not in the 11, not in the aggregator 10, and not in a deferred cluster. #1416 must classify them as it derives the single registry.

Confirmation​

Verifiable now:

  • bash scripts/check-adr-drift.sh stays at its baseline with this ADR added.
  • Every measurement in the table above reproduces via the command beside it.

Deliberately not claimed: that removing these tools is safe. retirement.py governs that, and it advertises dynamic_references, runtime_usage, public_contracts and migration_obligations as false β€” so it will return RETIREMENT_REVIEW, never REMOVAL_READY, from static analysis. Any removal needs its own admission.

And the largest gap is not technical. Without usage data, the REVIEW section is opinion. Converting it to evidence does not require instrumenting anything β€” the instrumentation exists. It requires lifting a .slice(0, 10), moving a store out of os.tmpdir() into the project-local .mcp-adr-cache that src/utils/config.ts:17 already defines, and recording the CE-MCP directive path. Tracked as #1487, #1488 and #1489.

That correction is itself an instance of what this ADR argues. The claim that no usage data was recorded went unchecked into a document meant to decide the fate of 75 tools, and it was wrong in the direction that made the problem look bigger than it is.

More Information​