Engineering11 min read

Tool search solves a problem our trading agent doesn't have — we measured it

A well-argued post making the rounds says tool bloat is killing agents: typical multi-MCP setups burn 50-70k tokens on tool definitions before the agent does anything, and models degrade past 30-50 similar tools. The prescription is tool search — index your tools, load them on demand. All of that is correct for horizontal agents. We measured our vertical one and got a different picture: 2,213 tokens of tool definitions, never more than 10 tools in context, and a context budget dominated by market data. Here's the measurement, and the one idea from that post we're stealing outright.

By

agentsReActtool-usecontext-engineeringRAGdeterminismbuild-in-publicPython

In brief

  • Measured on our entry agent: the tool-definition payload actually sent to the model is 1,948-2,213 tokens depending on analysis stage, and the model never sees more than 10 tools at once — roughly 25x smaller than the 50-70k tool bloat the tool-search argument is built to solve.
  • Context pressure sits on data, not on schemas: 12 real tool calls returned 4,379 tokens of derived market context against 2,213 tokens of tool definitions, and only the data term grows as the analysis deepens.
  • The agent already does progressive disclosure — but over deterministic stages, not a searchable index. It declares which analysis stage it needs and receives a known, curated tool set, so the same system state yields the same available operations.
  • Reproducibility beats a few thousand saved tokens when a reasoning chain is published and can move real orders. Tool search makes tool availability a function of index state and query phrasing; staged disclosure makes it a pure function of system state.
  • The best idea in the tool-search argument — embed the example queries a tool answers, not its description — transfers cleanly to our documentation RAG, where ~140 lines of hand-written query-to-topic rules currently do that job.

A post making the rounds argues that tool bloat is the quiet killer of agent quality. The claims are specific and, as far as they go, correct: typical multi-MCP setups (GitHub + Slack + Jira + Sentry) burn 50-70k tokens on tool definitions before the agent does anything useful, models degrade past 30-50 similar tools through worse routing and more hallucinated calls, and the fix is to keep a small set of core tools plus a meta-tool that searches an index of everything else, loading definitions on demand.

None of that is wrong. It is also, for our system, an answer to a question nobody asked. So rather than argue from intuition, the actual payloads were measured.

The numbers, first

Every figure below comes from serializing the exact structures the backend sends to the model and counting tokens with cl100k_base. Inference runs on DeepSeek, which tokenizes slightly differently — treat these as accurate to within a few percent, not to the token.

The trading tool registry holds 32 tool definitions, three of which are fallback/debug-only and unconditionally filtered out of the model-facing payload. Serialized whole, the registry is 6,465 tokens — about 5% of a 128k window. But the whole registry is never sent. Tools are grouped into analysis stages, and the model receives one stage at a time:

Stage Tools Tokens
core 9 1,948
detailed 10 2,213
market_data 5 836
auxiliary 6 1,582
position_management 7 864

The maximum number of tools visible to the model in a single call is 10. The threshold where the post says routing quality falls apart is 30-50 similar tools. We sit three to five times below it even counting the whole registry — which is never sent.

A first turn of the entry agent, starting in core, looks like this:

system prompt        3,974 tok   63.2%
user context block     367 tok    5.8%
tool definitions     1,948 tok   31.0%
                    ────────────
                     6,289 tok

Tool definitions are not free — 31% of the opening turn is real. But that number is a ceiling that never rises. What rises is everything the agent learns.

Twelve tool calls were then executed live against real BTCUSDT market data and their payloads measured: multi-timeframe market structure (404 tokens), momentum/moneyflow summary (615), aggregated open interest, funding, order flow and volume anomaly (1,433), previous-session liquidity sweep status (672), plus eight smaller ones. Total: 4,379 tokens of derived market context — a floor, not a ceiling, since six tools needing a live POI or stored bias could not be measured offline, and the reasoning content the model accumulates between iterations was not measured at all. A run in our test fixtures used 9 iterations and 14 distinct tools, so twelve calls is a conservative sample.

Mid-run, after the agent has walked core → detailed → market_data:

system prompt        3,974 tok   36.3%
user context           367 tok    3.4%
tool definitions     2,213 tok   20.2%
tool outputs         4,379 tok   40.1%   ← the only term that grows
                    ────────────
                    10,933 tok

Data to tool definitions: 1.98 : 1, and climbing, since only one of those terms is a function of how deep the analysis goes.

Worth stating plainly, because the honest number is less dramatic than the convenient one: tool definitions are not negligible here. They are bounded. At 2,213 tokens they occupy 1.7% of a 128k window against the 50-70k the post describes — call it 25x smaller. Optimizing a bounded 2k term while a growing 4k+ term sits next to it is the wrong order of work.

Horizontal and vertical agents are different problems

Claude Code cannot know in advance what it will be asked to do — a database, a browser, a Kubernetes cluster, a Rust build — so it carries the union of plausible capabilities, and that union is where 50-70k tokens of schemas comes from. Tool search is the correct response to genuine unpredictability.

Our entry agent knows what it will do before it starts: evaluate one point of interest, on one symbol, on a fixed pair of timeframes, against a closed ICT/SMC vocabulary — market structure, POI quality, displacement, liquidity, order flow, path to target, session context. That list has never needed a search index because it has never been long enough to lose track of.

Horizontal agent Vertical agent (ours)
Task predictability Unknown until the user speaks Known at dispatch: assess one POI
Tool count Union of every plausible integration; 50-70k tokens of schemas 32 registered, 29 exposable, max 10 in context
Context pressure Tool definitions, before any work happens Market data returned by tools, growing per iteration
Determinism required Low — a suboptimal tool choice costs a retry High — the reasoning chain is published and can move orders

The one place in the product that genuinely resembles a horizontal agent is the in-app assistant: 53 unique tools spanning journal writes, transaction analytics, reasoning-trace inspection, notification preferences, risk configuration. Flattened into one payload, that is 12,519 tokens. It is not flattened — tools are scoped to the UI surface the user is on, so a real conversation carries between 486 tokens (academy) and 5,756 (transactions). Same problem the post describes, solved with a static scoping table rather than an index, because the mapping from UI surface to relevant tools is known at build time.

We already do progressive disclosure — over stages, not an index

The agent starts in core. When it needs volume profile or displacement, it calls a meta-tool to request a stage transition with a stated reason, and the exposed tool list is swapped in place before the next turn — progressive disclosure, adopted for the reason the post gives, but with a different retrieval mechanism.

# simplified: stage transition swaps the exposed tool set mid-loop
if tool_name == "request_stage_transition" and result.get("success"):
    current_stage = result["target_stage"]
    available_tools = get_available_tools(stage=current_stage)

In tool search, the agent asks a question in natural language and an index answers. Here it names a stage from a fixed enum and a lookup answers. Both narrow the tool surface; only one can return a different answer tomorrow for the same request.

Determinism is the feature, not the constraint

Tool search introduces non-determinism at exactly the layer we want to keep boring. Which tools an agent can see becomes a function of index contents, embedding model version, and how the model happened to phrase its query this time. Re-index the corpus and an agent that reached for the order-flow tool last week may not this week — with nothing in the trace explaining why, because the tool was never proposed.

For a system whose reasoning chain is written to a database and published, that is a bad trade. Every entry decision persists a full execution trace as JSONB: each iteration's assistant message, every tool call with its arguments, every tool result in full, per-step durations, and the model that produced them. When someone asks why a setup was rejected, the answer is not reconstructed — it is read.

Staged disclosure keeps tool availability a pure function of system state: current stage, which venue's executor is attached, the asset class of the symbol, feature flags, agent configuration. Same state, same tools, every time — and that is testable, with tests asserting it, including one asserting entry-memory search can never appear in the position-management flow.

To be precise about what reproducibility means here: the decision chain is reproducible, the market is not. Candles are not snapshotted with the trace, so replaying the same calls today returns today's data. The trace is auditable, not bit-for-bit re-executable, and conflating those would be overselling it.

The measurement made one more thing obvious: raw OHLCV never enters the context at all. Candles are fetched, consumed inside the tools, and what returns is derived — structure labels, levels, mitigation status, metrics. If progressive disclosure has more room to run here, that is where: staging data, letting the agent pull a summary of levels first and a fuller series for one interval only on request. Today it gets whatever granularity a tool chooses to return. A direction, not a shipped feature.

What we are stealing from the post

The strongest idea in it is almost a footnote: when indexing tools semantically, embed the example queries a tool answers rather than its description. Intent matches intent far better than intent matches documentation.

That transfers one-to-one — not to tools, but to documentation retrieval. The public knowledge base is chunked by markdown heading at roughly 800 tokens per chunk, and each chunk's own content is what gets embedded. Retrieval is hybrid: vector and keyword search in parallel, merged on source and heading path, then a reranker applying lexical and metadata boosts with a cap on how many chunks one document may contribute. No similarity threshold — ranking and top-k only.

The reranker is where the seam shows. Query intent is inferred by a hand-written rule table of roughly 140 lines: query contains "approval mode", boost these categories, tags and slugs; mentions "kill zone", boost those; a synonym list maps model wejścia onto entry model. Every new concept in the docs means another rule, and the rules are invisible to anyone reading the documents.

Embedding the questions each chunk answers moves that knowledge out of a Python dictionary and into the documents themselves — a chunk about approval mode carrying "what happens if I don't approve in time?" as embedded text. The synonym problem largely dissolves: a paraphrase of a question sits close to that question in embedding space in a way it never sits close to a formal definition. Concrete change, not yet made.

When we would change our mind

The condition is exchange count. Venue differences are currently expressed as conditionals rather than tools — around 40 branches on which executor is attached, spread across eight files in the agent and analysis layers, with no shared interface between exchange executors making the contract explicit. The registry stays clean because a tool takes an injected executor instead of existing in per-venue variants.

That has a known ceiling and we are walking toward it: a third venue is in progress on a working branch whose most recent commit is precisely about routing stop-loss management between venues. Add a fourth and a fifth and the conditional branches multiply where the tool list does not.

The answer to that pressure is a venue abstraction — one operation, adapters underneath — not a searchable pile of near-duplicate tools. Tool search would let us skip designing the interface by making the mess retrievable instead of fixing it. Reuse first, not a parallel discovery system layered over avoidable duplication. If tool count ever grows because the domain grew rather than because one operation got copied per venue, the post's advice applies directly.

What we are deliberately not doing

The post's fourth option — treat tools as files or CLI commands and let the agent use grep, glob, and bash to find and invoke them — is the one being declined, and not on aesthetic grounds.

The analytical loop shares a process boundary with the code that holds exchange credentials and places orders. Filesystem and shell offload converts a bounded set of typed function calls into general code execution inside that boundary. The function-calling surface has a property worth keeping: everything the model can cause to happen is enumerable, reviewable, testable. A model that can run shell commands has a capability surface defined by whatever happens to be installed. Materially larger blast radius, for an efficiency gain measured at roughly two thousand tokens.

There is a version of this that is fine — code execution in a sandbox with no credential access, for pure analysis. That is a different system than the one that can move money, and it would have to stay one.


The general lesson is not that tool search is overhyped. It is that context-engineering advice is load-bearing on assumptions about an agent's shape, and those assumptions travel silently. The 50-70k figure is real for agents that cannot predict their own task. Ours can, so the same reasoning produced a 2,213-token answer and pointed the work somewhere else — at the data, where it belonged the whole time. Measure your own payload before adopting a fix for someone else's.

ShareX / TwitterThreads
sc4mp avatar

Public pen name of LiquidMind's founder and builder. Writing first-hand engineering notes and transparent performance reviews from the system's internal ledger.

Stay Liquid

New posts on transparency, engineering, and the LiquidMind thesis — no noise.

Loading discussion…