Found during the tool-wide token audit in #887, which deliberately left tools/query.py alone because it was being modified concurrently by #884 and #885.
Measured on a full build of this repository (5,764 nodes, 47,638 edges), serialized as FastMCP does and counted with tiktoken cl100k_base:
| tool |
worst-case tokens |
cause |
get_impact_radius |
3,446,634 |
changed_nodes and edges ignore max_results; only impacted_nodes is capped |
find_large_functions |
736,800 |
limit is neither validated nor capped |
traverse_graph |
385,100 |
token_budget is neither validated nor capped, and the check uses a len/4 heuristic applied after the entry is built |
semantic_search_nodes |
145,395 |
limit is neither validated nor capped |
get_impact_radius has a second problem: max_results exists on the underlying function but is not exposed on the MCP tool signature in main.py, so no client can bound it even if it wanted to. Adding the parameter without fixing the ignored-cap bug would be a misleading half-fix, which is why #887 left both alone.
What a fix needs
Follow the contract #853 and #887 established rather than inventing another: validate the parameter the way query.py already does (reject bools, reject values below 1, same message), cap every list in the response rather than one of them, always report the untruncated total, flag truncated, and say "showing N of M" in the summary. Vary ceilings by detail_level where payload size depends on it.
tests/test_token_budget.py already names all four in its QUERY_OWNED_UNBOUNDED set and asserts their default budgets while skipping their worst case. Removing each entry from that set as it is fixed is the acceptance criterion.
Found during the tool-wide token audit in #887, which deliberately left
tools/query.pyalone because it was being modified concurrently by #884 and #885.Measured on a full build of this repository (5,764 nodes, 47,638 edges), serialized as FastMCP does and counted with tiktoken cl100k_base:
get_impact_radiuschanged_nodesandedgesignoremax_results; onlyimpacted_nodesis cappedfind_large_functionslimitis neither validated nor cappedtraverse_graphtoken_budgetis neither validated nor capped, and the check uses a len/4 heuristic applied after the entry is builtsemantic_search_nodeslimitis neither validated nor cappedget_impact_radiushas a second problem:max_resultsexists on the underlying function but is not exposed on the MCP tool signature inmain.py, so no client can bound it even if it wanted to. Adding the parameter without fixing the ignored-cap bug would be a misleading half-fix, which is why #887 left both alone.What a fix needs
Follow the contract #853 and #887 established rather than inventing another: validate the parameter the way
query.pyalready does (reject bools, reject values below 1, same message), cap every list in the response rather than one of them, always report the untruncatedtotal, flagtruncated, and say "showing N of M" in the summary. Vary ceilings bydetail_levelwhere payload size depends on it.tests/test_token_budget.pyalready names all four in itsQUERY_OWNED_UNBOUNDEDset and asserts their default budgets while skipping their worst case. Removing each entry from that set as it is fixed is the acceptance criterion.