All articles
October 27, 2026

Tool-call accuracy is a docs problem too

Ubicloud published a careful write-up this week on fixing tool-call accuracy in vLLM, and it hit the Hacker News front page. The headline number: when they served Kimi K2.6 on vLLM 0.28, the model produced a schema-valid tool call only 92% of the time. Two out of every 25 calls were malformed. The fix, now shipping as a server flag in vLLM 0.31, is constrained decoding — the inference engine refuses to sample tokens that would break the tool's JSON schema.

That's good news if you host models. But if you publish an API that agents call through MCP or OpenAI-style tools, the same post is also quietly about you. Half of what makes a tool call succeed lives on your side: the schema, the parameter names, the descriptions, the examples. This post is about why tool-call accuracy is a documentation problem as much as an inference problem, and what to do about it.

#

What "schema accuracy" actually measures

Moonshot AI publishes a benchmark called K2-Vendor-Verifier that any provider serving Kimi can run against their endpoint. It replays 2,000 real requests and scores two things:

  • Trigger similarity — does the service decide to call a tool at the same moments Moonshot's own service does?

  • Schema accuracy — of the tool calls it generates, how many actually conform to the declared JSON schema?

As Ubicloud explains, schema accuracy is the one that bites agents. A single malformed call pollutes the context window with an error, the agent retries, token usage climbs, and the user waits. Over a multi-turn session that compounds fast.

And the failure modes are not random. From the post:

A model may generate a tool or field name that it has seen during the training, even if it does not exist in the current production environment. For example, it may generate search_web when the available tool is web_search, or use city_name when the active schema expects location.

That's a training-data leak into inference. The model has seen a thousand slightly different weather APIs and picks the shape it remembers best, not the shape you declared.

#

How constrained decoding fixes half the problem

An LLM generates one token at a time by sampling from a probability distribution. Constrained decoding narrows that distribution at each step so only tokens that keep the output on a valid path through the schema survive the sample. vLLM wires this up using structural tags from XGrammar, which lets you say "between these two special tokens, the output must match this JSON schema." Turn it on and schema accuracy jumps to 100% for Kimi and DeepSeek models in Ubicloud's tests.

That solves a specific thing: syntactic validity. The arguments will parse. Required fields will be present. Enum values will be from the allowed set. Numbers will be numbers.

It does not solve:

  • The model picking the wrong tool out of ten plausible ones.

  • The model inventing a URL, an ID, or a resource name that happens to be a valid string.

  • The model using a correct field but with a semantically wrong value — passing "markdown" when the user asked for raw HTML, say.

  • The model guessing which tool exists at all, because the one it needs isn't exposed.

For those, the schema and the words next to it are what the model has to go on.

#

The docs side of tool calling

If you've written an OpenAI tool definition or an MCP tool, you've already written mini-documentation: a name, a description, and a JSON Schema for parameters with per-field descriptions. The example in Ubicloud's post is instructive — the webfetch tool description is a short bulleted list of what the tool does, when to use it, and what formats it returns. That text goes straight into the model's context on every turn.

A few things follow from this that are easy to get wrong:

  1. Tool names should match what the model has seen a thousand times. If every other weather API in the training set is called get_weather, naming yours fetchCurrentConditionsV2 is making the model fight its priors. Boring, conventional names reduce the trigger-similarity gap.

  2. Field names should be unambiguous. location beats city_name when you also accept lat/long. query beats q when there are three string fields.

  3. Enums beat free text. Any time you can write "enum": ["text", "markdown", "html"] instead of "a string, one of…", do it. Constrained decoding will enforce it; the model will pick from it.

  4. Required fields should actually be required. Marking everything optional lets the model skip the one field that makes the call meaningful.

  5. Descriptions are prompts. The per-parameter description is the only place to tell the model when to supply a value and what shape it should take. "The URL to fetch content from" is fine. "Full absolute URL including scheme, e.g. https://example.com/path" is better.

None of this is new advice for people writing OpenAPI specs. But it travels differently when the consumer is a model that will hallucinate a plausible-looking payload if your schema is vague.

#

Where this connects to publishing an API

An API's schema and its documentation used to live in two different places — an OpenAPI file for codegen, a prose site for humans. For agent consumers that split doesn't work. The model needs the schema and the surrounding words at the same time, in a form it can read, at the version you're actually running.

This is the problem DocStar sits on. The same source produces the human docs site, an llms.txt and per-page markdown, an OpenAPI file, a hosted MCP server, and a generated CLI, all kept in sync. When an agent calls your API through the MCP server, it's reading the same descriptions, enums and required-field markers that your reference page shows a developer. Constrained decoding on the inference side plus a clean, versioned schema on the publishing side is the pair that gets you close to calls that actually work the first time.

#

A practical checklist for your tool schemas

If you expose an API to agents today — directly, through MCP, or via a tool definition in someone's harness — it's worth a 30-minute audit:

  • Run through every endpoint and ask: would a model that has never seen this API before guess the right tool name from a user's natural-language request?

  • For every string parameter, decide whether it could be an enum. If yes, make it one.

  • Read each parameter description out loud. If it just restates the field name ("url": "The URL"), rewrite it with an example.

  • Check which fields are required. Loosening required-ness to "be nice" tends to make calls worse, not better.

  • Try your API from a fresh agent session with no prior context. Watch which calls it gets wrong and why. Fix the schema, not the prompt.

The Ubicloud post is a reminder that the inference stack is finally closing the "malformed JSON" gap. The remaining accuracy comes from how clearly you describe what your API does.

If you want your API to be one of the ones agents get right on the first try, DocStar publishes your docs, your MCP server and your CLI from the same source. Paste the prompt at docstar.io into your coding agent and it will set the whole thing up.

 

tool-callingmcpagentsapi-docsvllmllms