The Tool Description Is the Context A new analysis from an unnamed source argues that MCP tool schema design, not model capability, is the primary driver of agent performance, citing benchmarks against BigQuery, Google Ads, and HubSpot where task-specific servers reduced tokens, calls, and turns. The post contends that rich, data-generated schemas—with enums, units, and guardrails—prevent costly trial-and-error, and that hand-written schemas are impractical at enterprise scale. Schema Drift Reaches the Tool Definition Context layer series Everyone who sells a context layer talks about freshness. Fresh rows, streaming… MCP tool schema design is the practice of writing a tool’s name, description, parameters, and return schema so an agent picks the right tool and calls it correctly on the first try. The schema is the one artifact the model reads on every single call, which makes it the most direct output of a context layer. A grounded schema carries real enums, disambiguated identifiers, units, scope, and guardrails, generated from your data rather than written by hand. This post shows where each piece comes from and what changes when you fix only the schema. The evidence first. We have published three benchmarks this summer comparing our task-specific MCP servers with vendor-native ones for BigQuery, Google Ads and HubSpot. The pattern was the same each time. Fewer tokens, fewer calls, fewer turns, same or better answers. People read those numbers and take away “task-specific beats general-purpose.” That is true. But it hides the mechanism. Task-specific servers win because the tool definitions carry the meaning the agent would otherwise have to discover by trial and error. The 39,000-token schema the HubSpot server resent on every turn was not context. It was a catalog. The agent still had to figure out which object held deals, which property was the close date, and what “closedwon” meant, and it did that with four discovery calls and 13 SQL queries. The point of a context layer is not a knowledge graph diagram on an architecture slide. Its most direct output is a tool definition that is right. This post is about how that definition gets built, where each piece of it comes from, and what changes when you fix only the schema and nothing else. Look at any trace where an agent calls a data tool badly and you find the failure in the tool definition before the first call. "id": { "type": "string" } Which id? The CRM account ID, the billing account number, and the canonical customer key are all “id” somewhere. "status": { "type": "string" } The real values are six enums. One is deprecated. One triggers a workflow. "amt": 13756172 No unit, no currency, no note that it is net of discounts. "description": "Gets deals." The description restates the function name and tells the agent nothing about scope or when to use it. "close date": { "type": "string" } No format, so the agent tries ISO 8601, then epoch, then “last quarter.” returns first 100 rows Pagination is not mentioned, so the agent treats the first page as all rows. None of these are model failures. The model did what the definition allowed. The definition allowed too much. The general advice for fixing this exists and it is fine: write rich descriptions, use enums, add constraints, keep tool count low. AWS, Anthropic and half a dozen others have written that post. We are not going to repeat it. The question we care about is different. Where does the content of a good description come from at enterprise scale, and how do you keep it right when you have hundreds of sources and nobody is going to hand write 4,000 tool schemas? The name says what business question the tool answers, not which table it hits. The description states the purpose, the scope time window, filters already applied , the units of anything numeric, and any consequence. Parameters use business names, carry enums drawn from real values, mark deprecated values, disambiguate identifiers, and state formats. The return schema names every field and says what it means and what unit it is in. Guardrails read only, row limits, allowed filters are declared, not implied. Compare a typical general-purpose CRM tool with one of the tools from our HubSpot benchmark server. { "name": "search crm objects", "description": "Search CRM objects by type with optional filters and properties.", "inputSchema": { "type": "object", "properties": { "objectType": { "type": "string" }, "filterGroups": { "type": "array" }, "properties": { "type": "array", "items": { "type": "string" } }, "limit": { "type": "integer" }, "after": { "type": "string" } }, "required": "objectType" } } { "name": "win loss summary 90d", "description": "Summarize closed deals over the trailing 90 days: count, total and average amount, and win rate, split by won and lost. Amounts are in USD, net of discounts, from the deal amount property at close. Excludes deals in open stages. Use this first for any win/loss question, then drill in with close reason breakdown 90d or deal size trend 90d.", "inputSchema": { "type": "object", "properties": { "pipeline": { "type": "string", "enum": "default", "enterprise", "partner" , "description": "HubSpot pipeline. Omit for all pipelines." }, "as of": { "type": "string", "format": "date", "description": "End of the 90 day window, YYYY-MM-DD. Defaults to today in UTC." } } }, "outputSchema": { "type": "object", "properties": { "closed deals": { "type": "integer", "description": "Deals with a close date inside the window and stage in {closedwon, closedlost}." }, "won": { "type": "integer" }, "lost": { "type": "integer" }, "win rate": { "type": "number", "description": "won / closed deals, 0 to 1." }, "won amount usd": { "type": "number" }, "lost amount usd": { "type": "number" }, "avg won amount usd": { "type": "number" }, "avg lost amount usd": { "type": "number" } } }, "annotations": { "readOnlyHint": true } } The first tool is a search API with a JSON wrapper. The second is an answer with a contract. The agent calling the second one does not discover anything. It already knows what “closed” means, what currency the amounts are in, and where to go next. Nobody wrote it. It was assembled. Every element of a grounded tool schema has a source inside the enterprise, and a context layer’s job is to pull from each source and put the result in the right slot. This is what Helix does behind MCP Studio, and it is the part we have described least, so here it is concretely. acct st in {A, I, P, C} into account status in {ACTIVE, INACTIVE, PENDING, CLOSED}.The connector reads the source schema for types and nullability. That is the easy part. The useful part comes from sampling. Sample 10,000 rows of the deals object and you learn that dealstage has exactly nine distinct values, that two of them have not appeared in eighteen months, that closedate is always midnight UTC, and that amount is never negative. The enum in the tool comes from observed values, not from the API docs, because the API docs say “string.” Every enterprise has four customer IDs. Lineage across the connected systems shows which one the others map to. The same graph shows that arr usd on the CRM account is a nightly copy from billing, so the tool marks it read only and points at the source of truth. Without lineage the tool definition cannot say either of those things and the agent guesses. “Win rate” means something specific at your company. Those definitions live in a glossary, a metrics doc, a Confluence page, or a slide deck. Helix ingests them, ties each definition to the fields and tools it governs, and the definition lands in the tool description. It does not wait for someone to notice a change and hand edit forty servers. For the long tail of SaaS sources, the OpenAPI spec is often the only documentation. It carries the parameter constraints, the pagination model, the rate limits, and sometimes the enums. It also tells you which endpoints mutate state, which is how a read-only server knows what to exclude. This is the source most people skip. Every tool call an agent makes, with its parameters and its outcome, is data. Over a few hundred runs you learn that filtering by close date and stage together returns in 200ms while filtering by owner alone times out, that agents keep passing the CRM ID where the canonical ID is expected, and that a particular parameter combination always returns zero rows. Each of those becomes a line in a description, a tighter enum, or a renamed parameter. The tool gets better because it was used, and the improvement is written back into the definition, not left as tribal knowledge in a Slack thread. Who is allowed to call the tool, which rows they see, which columns are masked, whether the tool is read only, and what happens on a write. These come from the platform’s access policies and land in the tool as annotations and in the gateway as enforcement. Put those six together and you get the second schema above. Pick any slot in the schema below and it tells you which source filled it. Source: schema plus samples The connector sampled the pipeline property across 10,000 deals and found exactly three values in use. The API docs say “string.” The enum comes from what is actually there, and a value that stops appearing gets marked deprecated on the next regeneration. Source: lineage Four systems have a customer identifier. Lineage shows the CRM account ID and the billing account number both map to cust id v2. The tool names that one and warns the agent away from the other two in the parameter description. Source: business docs and glossary The sales ops glossary defines “closed” as stage in {closedwon, closedlost} with a close date inside the window. That sentence is tied to the dealstage and closedate fields, so it lands in the description and in the output field definition. Change the glossary and the description changes. Source: API spec The OpenAPI spec says closedate is an ISO date, results page with a cursor, and a page holds at most 200 rows. Those become the format, the pagination note, and the row limit in guardrails. The spec also marks which endpoints write, which is how a read-only server knows what to leave out. Source: execution logs Across a few hundred runs, agents that called the summary first answered in two turns. Agents that started with the raw rows tool took six. That pattern becomes one sentence in the description, and the sentence is checked again the next time the logs are reviewed. Source: policy The platform’s access policy marks this tool read only and scopes rows by the caller’s team. The annotation tells the model. The gateway enforces it. The model cannot talk its way past a policy it can only read. The generation runs in one direction and the feedback runs in the other. Watch the flow. Sources Schemas, samples, lineage, docs, specs, logs, policy Helix Ties each fact to a field and a tool slot Tool definition Name, description, params, output, guardrails, version Agent Reads it on every call. Its calls become the next logs. Purple dot: generation, sources to agent. Green dot: execution history flowing back into Helix. There is a tension here. If every tool description carries the full data dictionary, you are back to the 39,000-token schema. The description has to carry meaning, not the whole catalog. The way we handle it is progressive disclosure. The tool description carries what the agent needs to choose the tool and call it correctly: purpose, scope, units, the enums that matter, the identifier disambiguation, the guardrails. Anything deeper the full column list, the sample values, the lineage graph is available through the Agentic Probe when the agent asks for it. Same information reachable. Only the relevant part sent by default. A good test: read the description without seeing the backend and ask whether you, a human analyst, would know what you were about to get. If yes, it is grounded. If you would need to run it once to find out, it is not. The claim in this post is that the schema carries the win, not the backend. So we tested it that way. We took TASK FROM THE HUBSPOT OR GOOGLE ADS BENCHMARK and held the backend fixed. Same connector, same query macros, same data. The only variable was the tool definitions. In one run the agent saw the bare definitions: minimal descriptions, no enums, no units, no output schema. In the other it saw the grounded ones generated as described above. | Metric | Bare schema | Grounded schema | |---|---|---| | Tool calls | fill in | fill in | | Agent turns | fill in | fill in | | Total tokens | fill in | fill in | | Correct answer | fill in | fill in | | Clarifying questions asked | fill in | fill in | | Wrong parameter attempts | fill in | fill in | Two to three sentences on the result. Expected direction: fewer calls, fewer wrong-parameter attempts, fewer clarifying questions, same or better accuracy, on the same backend. Report honestly if any metric did not move. The backend did not get smarter. The agent did not get smarter. The tool told the truth about itself and the agent stopped guessing. Generating definitions from sources beats hand writing them. It also fails in predictable ways. We have made every one of these. 1 Enums from the docs instead of the data The API doc lists eleven status values. Nine of them have never appeared in your tenant. Ship all eleven and the agent spends turns filtering on values that return nothing. Build enums from observed values, keep the doc list as a fallback for new values. 2 Over describing If the description is the data dictionary, you are back to the 39,000-token schema, just formatted nicely. A description is for choosing the tool and calling it correctly. Put the rest behind the probe. 3 Generating once A definition generated in March and never touched is a hand written definition with extra steps. Regeneration has to be tied to source change, which means schema fingerprints, glossary hooks, and a version on every tool. That is the next post. 4 Business names that nobody uses Renaming acct st to account status is right. Renaming it to customer lifecycle state indicator because the glossary says so is wrong. Use the name people say out loud in meetings. Execution logs tell you which names agents and users actually reach for. 5 No output schema Teams spend all their effort on parameters and return an untyped blob. The agent then has to guess what won amount usd means, which is the same failure in the other direction. Every output field gets a name, a type, a unit, and one sentence. Everything above is about read tools, because that is what the benchmarks measured. The same six sources produce write tools, with two additions: application validation rules become preconditions, and consequence metadata what downstream systems react to this field becomes part of the description and the dry run response. The first post in this series, Read Context Is Solved. Write Context Is Not. https://nexla.com/blog/write-context-for-ai-agents/ , walks through one write tool built both ways. The short version: a write tool whose schema does not carry the enum, the precondition, and the consequence is a bug waiting for a retry. You do not need a context layer to start. You need an afternoon and your tool list. Most teams that do this find the same thing we did. The fix was never the model. It was a JSON schema that told the truth. Copy this. Fill it from your sources, not from your head. { "name": "