Context
Operator directive during a live cloud-deployment probe: the KB tool get_class_hierarchy description must explicitly state "Neo.mjs code only". The prompt was concrete — that deployment's Knowledge Base has ingested a tenant's own repositories, so a caller reasonably reads "the static class hierarchy from the knowledge base" as covering what that knowledge base actually contains. It does not: the tool answers for Neo.mjs code exclusively, from a generated build artifact rather than the embedded corpus.
I implemented that as a rewritten OpenAPI description (commit f4982b7d2a, unpushed at filing time) and then verified where the text surfaces. It does not surface where it matters.
The Problem
The knowledge-base MCP server emits two description tiers, and they are sourced differently:
| Tier |
Served by |
Source precedence |
Cap |
compact tools/list line |
ToolService.buildToolListDescription (ai/mcp/ToolService.mjs:309-320) |
x-neo-tool-summary → summary → full description |
120 (ai/mcp/server/knowledge-base/toolService.mjs:89, overriding the base 160) |
| full handbook body |
ToolService.buildToolHandbookEntry (ai/mcp/ToolService.mjs:330-333) |
x-neo-tool-handbook → full description → summary |
1024 |
get_class_hierarchy carries no x-neo-tool-summary, so summary: Get Class Hierarchy wins the compact tier. That bare four-word title is the entire scope information an agent gets by default; the rewritten description is only served on an explicit get_mcp_tool_handbook({toolId: 'get_class_hierarchy'}) call.
Empirically confirmed, not inferred — with x-neo-tool-summary withheld, the derived listed description is exactly:
Received string: "Get Class Hierarchy"
The caller who never fetches the handbook is precisely the caller who would misread the scope, so the tier that had the fix was the tier that did not need it.
This capability never existed. No get_class_hierarchy description has ever stated its scope on any tier; 6 of 13 KB operations carry an x-neo-tool-summary at all, so 7 still fall back to summary titles.
The Architectural Reality
Two properties of buildToolListDescription make the failure quiet in both directions:
- Fallback, not error. A missing
x-neo-tool-summary silently degrades to summary. Nothing fails; the tool list just stops saying anything useful.
- Truncation, not rejection. Past the cap it emits
singleLine.slice(0, maxLength - 3) + '...'. An over-long scope line keeps its opening phrase and loses its tail — so a naive guard asserting only Neo.mjs code only would pass a half-sentence that dropped the not-covered clause.
Both are reasonable engine behaviours; together they mean the scope claim can be absent or half-present with every existing test green. McpServerListToolsSmoke.spec.mjs:354-356 caps every KB listed description at 120 but asserts nothing about content, and no spec has ever read get_class_hierarchy's description at all.
Related note on evidence: commit f4982b7d2a's message cites McpServerToolLimits as its green witness. That spec loads ai/mcp/server/**memory-core**/openapi.yaml (McpServerToolLimits.spec.mjs:179) and never observes the knowledge-base file — a green test answering about the wrong subject. The commit message is corrected as part of this work.
The Fix
- Add
x-neo-tool-summary to /knowledge/hierarchy get in ai/mcp/server/knowledge-base/openapi.yaml, carrying the scope and the not-covered clause within the 120-char cap (93 chars as written, 27 headroom).
- Keep the long-form scope paragraph in
description for the handbook tier.
- Add
test/playwright/unit/ai/mcp/server/knowledge-base/classHierarchyScope.spec.mjs asserting the derived listed description via listTools() rather than the YAML text, with a precedence control (a listed description equal to summary means x-neo-tool-summary was dropped) and an anti-truncation assertion.
- Correct
f4982b7d2a's message to cite McpServerListToolsSmoke — the spec that actually loads this file.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
get_class_hierarchy compact tools/list description |
x-neo-tool-summary in ai/mcp/server/knowledge-base/openapi.yaml, read at ai/mcp/ToolService.mjs:311 |
states Neo.mjs-only scope + not-covered clause, ≤120 chars, untruncated |
key absent → summary ("Get Class Hierarchy") — the defect this closes |
the tool description IS the doc surface |
new spec, incl. a mutation control that reproduces "Get Class Hierarchy" |
get_class_hierarchy handbook body |
description in the same operation, read at ai/mcp/ToolService.mjs:330-333 |
full scope paragraph + ingestion-independence note, ≤1024 |
description absent → summary → tool name |
same |
get_mcp_tool_handbook assertion in the new spec |
the other 7 KB operations without x-neo-tool-summary |
same file |
unchanged |
continue falling back to summary titles |
— |
Out of Scope below |
No operationId, schema, parameter, or dispatch change — so the tool contract itself is untouched and no ingestion path is involved.
Decision Record impact
none. Description-and-fixture only; no ADR authority is created, amended, or challenged. Adjacent-but-unaffected: ADR 0019 governs AiConfig provider SSOT and this touches no config leaf.
Acceptance Criteria
Out of Scope
- The other 7 KB operations lacking
x-neo-tool-summary. Real, but a distinct judgement call per tool, and bundling them would make this diff a vocabulary review.
- The four "framework" occurrences across both openapi surfaces — #16063.
- Everything in #16061: the deploy image never building
docs/output/class-hierarchy.json, healthcheck claiming all features operational beside a known-incomplete capability, and a runtime incompleteness signal on the response. A static description cannot discharge a runtime disclosure, so #16061 AC4 stays with #16061.
Avoided Traps
- Fixing the tier that was easy to reach. The long
description is the obvious place to write a scope warning and the wrong one. The two tiers fail independently — when the mutation control ran, the handbook assertion stayed green while the compact assertion went red, which is exactly why a handbook-only fix reads as complete.
- Asserting the YAML text instead of the derived output. A
grep-style guard over openapi.yaml would pass whether or not ToolService precedence actually selected the new key. The guard goes through listTools().
- Trusting the phrase as the witness. Truncation preserves the head of the string, so
toContain('Neo.mjs code only') alone certifies nothing about the clause that carries the actual warning.
- Closing #16061 with this. #16061's first AC is a decision about whether cloud deployments carry the artifact — operator authority, not mine to discharge in a description commit. Splitting on delivery authority is what makes a
Resolves honest at authoring time.
Related
- #16061 — the artifact gap where this scope violation was found; this ticket takes only the description leaf, that one keeps the image-build, healthcheck, and runtime-signal ACs
- #16063 — the framework-vocabulary sweep across both MCP openapi surfaces (Grace)
AGENTS.md §neo_identity_anchor · ai/mcp/ToolService.mjs:309-333 · ai/mcp/server/knowledge-base/toolService.mjs:89
Sizing note
One YAML key, one new spec file, one commit-message correction. Deliberately one-PR-resolvable.
Live latest-open sweep: checked latest 20 open issues at 2026-07-27T20:30:01Z plus a 30-message A2A claim sweep; no equivalent found — #16063 is the framework-vocabulary sweep and #16061 the artifact gap, neither covering the tier-placement defect.
Origin Session ID: c038696f-94a6-4788-82bf-747c5672908c
Retrieval Hint: query_raw_memories("KB tool description two tiers x-neo-tool-summary compact list vs handbook") · commit anchor f4982b7d2a
Context
Operator directive during a live cloud-deployment probe: the KB tool
get_class_hierarchydescription must explicitly state "Neo.mjs code only". The prompt was concrete — that deployment's Knowledge Base has ingested a tenant's own repositories, so a caller reasonably reads "the static class hierarchy from the knowledge base" as covering what that knowledge base actually contains. It does not: the tool answers for Neo.mjs code exclusively, from a generated build artifact rather than the embedded corpus.I implemented that as a rewritten OpenAPI
description(commitf4982b7d2a, unpushed at filing time) and then verified where the text surfaces. It does not surface where it matters.The Problem
The knowledge-base MCP server emits two description tiers, and they are sourced differently:
tools/listlineToolService.buildToolListDescription(ai/mcp/ToolService.mjs:309-320)x-neo-tool-summary→summary→ full descriptionai/mcp/server/knowledge-base/toolService.mjs:89, overriding the base 160)ToolService.buildToolHandbookEntry(ai/mcp/ToolService.mjs:330-333)x-neo-tool-handbook→ full description →summaryget_class_hierarchycarries nox-neo-tool-summary, sosummary: Get Class Hierarchywins the compact tier. That bare four-word title is the entire scope information an agent gets by default; the rewrittendescriptionis only served on an explicitget_mcp_tool_handbook({toolId: 'get_class_hierarchy'})call.Empirically confirmed, not inferred — with
x-neo-tool-summarywithheld, the derived listed description is exactly:The caller who never fetches the handbook is precisely the caller who would misread the scope, so the tier that had the fix was the tier that did not need it.
This capability never existed. No
get_class_hierarchydescription has ever stated its scope on any tier; 6 of 13 KB operations carry anx-neo-tool-summaryat all, so 7 still fall back tosummarytitles.The Architectural Reality
Two properties of
buildToolListDescriptionmake the failure quiet in both directions:x-neo-tool-summarysilently degrades tosummary. Nothing fails; the tool list just stops saying anything useful.singleLine.slice(0, maxLength - 3) + '...'. An over-long scope line keeps its opening phrase and loses its tail — so a naive guard asserting onlyNeo.mjs code onlywould pass a half-sentence that dropped the not-covered clause.Both are reasonable engine behaviours; together they mean the scope claim can be absent or half-present with every existing test green.
McpServerListToolsSmoke.spec.mjs:354-356caps every KB listed description at 120 but asserts nothing about content, and no spec has ever readget_class_hierarchy's description at all.Related note on evidence: commit
f4982b7d2a's message citesMcpServerToolLimitsas its green witness. That spec loadsai/mcp/server/**memory-core**/openapi.yaml(McpServerToolLimits.spec.mjs:179) and never observes the knowledge-base file — a green test answering about the wrong subject. The commit message is corrected as part of this work.The Fix
x-neo-tool-summaryto/knowledge/hierarchygetinai/mcp/server/knowledge-base/openapi.yaml, carrying the scope and the not-covered clause within the 120-char cap (93 chars as written, 27 headroom).descriptionfor the handbook tier.test/playwright/unit/ai/mcp/server/knowledge-base/classHierarchyScope.spec.mjsasserting the derived listed description vialistTools()rather than the YAML text, with a precedence control (a listed description equal tosummarymeansx-neo-tool-summarywas dropped) and an anti-truncation assertion.f4982b7d2a's message to citeMcpServerListToolsSmoke— the spec that actually loads this file.Contract Ledger Matrix
get_class_hierarchycompacttools/listdescriptionx-neo-tool-summaryinai/mcp/server/knowledge-base/openapi.yaml, read atai/mcp/ToolService.mjs:311summary("Get Class Hierarchy") — the defect this closes"Get Class Hierarchy"get_class_hierarchyhandbook bodydescriptionin the same operation, read atai/mcp/ToolService.mjs:330-333descriptionabsent →summary→ tool nameget_mcp_tool_handbookassertion in the new specx-neo-tool-summarysummarytitlesNo
operationId, schema, parameter, or dispatch change — so the tool contract itself is untouched and no ingestion path is involved.Decision Record impact
none. Description-and-fixture only; no ADR authority is created, amended, or challenged. Adjacent-but-unaffected: ADR 0019 governs AiConfig provider SSOT and this touches no config leaf.Acceptance Criteria
get_class_hierarchy's compacttools/listdescription states the Neo.mjs-only scope, verified throughlistTools()rather than the YAML source text.x-neo-tool-summaryis removed, so the scope cannot silently revert to the baresummarytitle. Certified by mutation: the guard was observed RED against"Get Class Hierarchy".query_documents/ask_knowledge_baseredirect.AGENTS.md §neo_identity_anchor).McpServerListToolsSmoke.spec.mjsandOpenApiValidatorCompliance.spec.mjsstay green — the two specs that load this openapi file.f4982b7d2a's commit message cites the spec that actually observes this file.Out of Scope
x-neo-tool-summary. Real, but a distinct judgement call per tool, and bundling them would make this diff a vocabulary review.docs/output/class-hierarchy.json,healthcheckclaiming all features operational beside a known-incomplete capability, and a runtime incompleteness signal on the response. A static description cannot discharge a runtime disclosure, so #16061 AC4 stays with #16061.Avoided Traps
descriptionis the obvious place to write a scope warning and the wrong one. The two tiers fail independently — when the mutation control ran, the handbook assertion stayed green while the compact assertion went red, which is exactly why a handbook-only fix reads as complete.grep-style guard overopenapi.yamlwould pass whether or notToolServiceprecedence actually selected the new key. The guard goes throughlistTools().toContain('Neo.mjs code only')alone certifies nothing about the clause that carries the actual warning.Resolveshonest at authoring time.Related
AGENTS.md §neo_identity_anchor·ai/mcp/ToolService.mjs:309-333·ai/mcp/server/knowledge-base/toolService.mjs:89Sizing note
One YAML key, one new spec file, one commit-message correction. Deliberately one-PR-resolvable.
Live latest-open sweep: checked latest 20 open issues at 2026-07-27T20:30:01Z plus a 30-message A2A claim sweep; no equivalent found — #16063 is the framework-vocabulary sweep and #16061 the artifact gap, neither covering the tier-placement defect.
Origin Session ID: c038696f-94a6-4788-82bf-747c5672908c
Retrieval Hint:
query_raw_memories("KB tool description two tiers x-neo-tool-summary compact list vs handbook")· commit anchorf4982b7d2a