Model Context Protocol (MCP): how tool schema bloat and ambiguity break LLMs—and how to fix it

One extra field can kaput an LLM. Here’s why, and how to fix it.

Daniel Wells and Raian Osman’s AWS sample repo and write‑up (https://github.com/aws-samples/sample-mcp-tool-design-patterns) demonstrate a simple, reproducible failure mode: the Model Context Protocol (MCP) tools you expose to LLMs can cause failures from design choices, not from the protocol itself. Model Context Protocol (MCP), the interface pattern for exposing tools/endpoints to LLMs, works, but poorly designed tool schemas can bloat the context or confuse model reasoning. The repo wraps one simulated K, 12 search backend with six MCP tool designs (V1, V6) so you can see the tradeoffs in action.

The two mundane bugs behind most failures

Bloat. Verbose tool definitions, long docstrings, or fat response payloads get loaded into the model’s context and eat tokens the model could use for reasoning and user intent. A tool that returns many fields on every call silently steals the LLM’s workspace.

Confusion. Natural language doesn’t always map cleanly to API names, enums, or parameters. As the model’s internal reasoning degrades, it picks the wrong tool, uses incorrect parameter names, or retries with poor error messages, classic signs of ambiguity.

Example docstring from V1: “Performs a global search for educational resources.”

Helpful, but not enough once the schema grows. The repo’s simulated backend exposes 14 filterable fields (subject, grade, format, standards alignment, language, resource class, and others), which is enough to trigger both problems unless you design the tool carefully.

How bad is “bloat” in practice?

As a quick illustration: 14 fields × ~20 tokens per field ≈ 280 tokens. On a 4, 096‑token context window that’s roughly 6-7% of the available space used just by field names and short descriptions, before any user prompt, system instructions, or model reasoning. Trim that to six fields and you reclaim a meaningful chunk of the model’s attention.

The six MCP tool‑design variants (V1, V6)

  • V1, v1_passthrough.py (Raw passthrough)

    What it does: exposes all 14 internal parameters (discipline, media_type, content_bucket, etc.) directly.

    Pros: minimal infra and simplest to implement.

    Cons: maximum token bloat and high chance of confusion across client LLMs.

    When to use: prototyping only. For product managers: fast to ship, slow to scale.

  • V2, v2_better_descriptions.py (Rich descriptions)

    What it does: keeps parameters but enriches docstrings with valid values and synonym mappings (e.g., “‘quiz’/’test’ → Assessment, ‘worksheet’ → Activity”).

    Pros: improves mapping from free text to schema values, reducing some confusion.

    Cons: increases token footprint; tradeoff between clarity and bloat.

    When to use: when you need quick accuracy wins for ambiguous client prompts and can tolerate larger context size.

  • V3, v3_rethought_schema.py (Schema + defaults)

    What it does: renames fields for clarity, uses enums/Literal types, supplies sensible defaults (example defaults: structure=’Asset’, resource_class=’Student Resource’, language=’en’), and extracts heavy drill-downs into a separate get_resource_detail tool.

    Pros: tightens the space the model needs to consider, reduces ambiguity, and keeps token use low.

    Cons: requires domain work to choose good defaults and enums.

    When to use: recommended starting point for production, low cost, high impact.

  • V4, v4_lazy_loading.py (Lazy loading / get_taxonomy)

    What it does: keeps the primary search tool lean and puts large taxonomies behind a separate get_taxonomy tool that is only called on demand.

    Pros: smallest client context footprint; large token savings when taxonomy isn’t needed.

    Cons: extra round trips, cache complexity, and potential stale taxonomy issues on clients.

    When to use: when taxonomies are big and most queries don’t require full vocab lists.

  • V5, v5_llm_introspect.py (Server-side LLM introspection)

    What it does: a server-side LLM (the examples use Bedrock-backed models in the repo) interprets ambiguous client queries and returns recommended parameters, centralizing mapping logic you control and test.

    Pros: consistent mapping across heterogeneous clients and models, central testing and evolution.

    Cons: extra inference calls (cost), higher latency, and you must manage privacy and audit for queries sent to the server LLM. Check model names and availability in your AWS Bedrock console. Names and regions vary over time.

    When to use: when cross-client consistency or complex mapping justifies the additional cost and latency.

  • V6, v6/app/v6/main.py (Agent-as-tool)

    What it does: exposes a single natural‑language interface, agentic_search_content(question: str), backed by a Strands Agent that owns reasoning, orchestration, and internal tools.

    Pros: maximal control, rich orchestration (multi-step flows), and centralized observability.

    Cons: highest infra and ops complexity, highest cost, and added latency if the agent chains many calls.

    When to use: for workflows that require multi-step reasoning, access to many internal services, or strict centralized control over behavior.

Manager one-liners

  • V1: prototype fast, fail loudly in production.
  • V2: better free-text mapping but heavier context.
  • V3: best low-cost lift, use this as your default production pattern.
  • V4: save tokens with on-demand taxonomies; watch caches and latency.
  • V5: centralize ambiguity resolution, pay for consistency.
  • V6: full agent control, use when orchestration or policy enforcement requires it.

Hands-on: run the lab and what to watch for

Repo: https://github.com/aws-samples/sample-mcp-tool-design-patterns. Prerequisites: Python 3.10+, SQLite (ships with Python), and Kiro or another MCP client. Bedrock model access is needed only for V5/V6 if you want to reproduce server-side introspection or agent behavior.

Key Kiro CLI interactions and what to look for:

  • /agent swap v1-passthrough (or v2, v3…), swap the server version. Look for behavioral differences in subsequent calls and note how many extraneous fields the tool returns.
  • /clear, reset chat context between experiments so prior tool metadata doesn’t carry forward.
  • /context show, observe the context-window percentage used. You should see this percentage drop when moving from V1 → V3 or V4.
  • /model, switch the client LLM. Test multiple client models to see which tool designs generalize.

Starter test queries (use across V1, V6 and compare behavior):

  • “Find me a quiz on fractions for my 7th graders.”
  • “Do you have any lessons for teaching Spanish in middle school?”
  • “I need TEKS-aligned content for kids working on dividing in middle school.”
  • “What types of content can I search for?”
  • “Can I get details on n-sc-1096?”

After each run, capture:

  • Tool-call counts (how many server calls per user request).
  • Token usage (input + output tokens) and approximate cost using your model pricing.
  • Defaults_applied rate: the percentage of requests satisfied by server defaults (see Implementation hygiene below for a definition).
  • Mismatch telemetry: intended intent versus chosen parameters.

Tradeoffs summed up

Every pattern trades accuracy, context size, latency, cost, and operational control. A few practical notes:

  • V2 improves mapping accuracy but increases context tokens.
  • V3 wins a lot of the time: enums and good defaults reduce confusion with small token overhead.
  • V4 is the leanest token-wise but may add user‑perceived latency unless you cache taxonomy results smartly.
  • V5 centralizes behavior across clients. Anthropic and other vendors have reported substantial token savings when moving to on‑demand definitions, but vendor‑reported figures vary. Validate on your traffic.
  • V6 gives the most control and auditability at the highest infra cost.

Security, privacy, and auditability

  • Be explicit about what you send to server-side LLMs. Strip or mask PII and sensitive text before introspection calls.
  • Return a compact justification along with structured parameters from server-side introspection (e.g., mapping_reason) for auditing and compliance.
  • Agents that call internal services should operate with least privilege and strong input validation to minimize exfiltration risk.
  • Version and sign tool definitions and taxonomies so clients can detect stale skills and reject unexpected changes.

Practical testing checklist and suggested thresholds

  • Measure tokens per version (input + output). Multiply by your model pricing to estimate cost per 1, 000 queries.
  • Track latency per end‑user request. Aim for no more than ~1.2× baseline latency for critical UX flows when you add lazy-loading or server-side calls.
  • Define defaults_applied = percentage of queries where server defaults satisfied the intent without extra client input. If defaults_applied < 50% after two weeks of live traffic, your defaults or enums need expansion. Consider V5 for ambiguous domains.
  • Log intent → parameter mappings and set an SLO for mismatches (for example, target <10% mismatch rate in early rollouts, then tighten based on user impact).
  • Run A/B tests across client LLMs and multilingual/adversarial inputs to validate generalization.

Implementation hygiene: patterns that pay off

  • Prefer enums/Literal types and conservative defaults over huge free-text lists.
  • Keep short, user-friendly hint text in the protocol and move long documentation server-side to avoid token bloat.
  • Cache taxonomy responses on the client with TTL and an invalidation path to reduce V4 round trips.
  • Return both structured parameters and a compact mapping_reason field from server-side introspection for traceability.
  • Consider a hybrid: lightweight client intent classifiers or embeddings + vector matching for common mappings, with V5 as a fallback for ambiguous cases.

Recommended rollout plan (practical)

  1. Audit current MCP tools: count parameters, measure tokens per tool definition, and record defaults_applied baseline.
  2. Migrate to schema+defaults (V3) and add short public hints. Measure token savings and mismatch telemetry for two weeks.
  3. If ambiguity remains (defaults_applied < 50% or mismatch rate > your SLO), pilot server-side introspection (V5) on a subset of traffic and compare cost/latency.
  4. If you require orchestration across many services or strict centralized behavior, pilot agent-as-tool (V6) on non-critical flows first and instrument heavily for traces and audits.

Notable quotes from the examples

“Performs a global search for educational resources.”

“search requires 2 or more terms in query”

“no results”

Key takeaways, questions you should be asking

  • Why do MCP tools fail more often than the protocol itself?

    Most failures trace to tool-level design: excessive field/description bloat consumes the model’s context, and ambiguous or poorly constrained parameters cause models to choose wrong values.

  • Which pattern should I start with for production?

    Start with schema + defaults (V3) and short public hints. It reduces ambiguity and token use without adding inference calls or major infra changes.

  • When is server-side introspection or agent-as-tool worth the extra cost?

    When you need consistent mapping across heterogeneous clients, centralized testing, or complex orchestration that enums/defaults can’t capture, pilot V5 or V6 and measure whether the added cost buys the reduction in mismatch and support overhead.

  • How do I quantify the cost/latency tradeoffs?

    Measure tokens (input + output) and latency per version, then apply your model pricing (e.g., Bedrock per-token rates) to estimate cost per 1, 000 queries; include any extra inference calls for V5/V6 in the math.

  • What tests prove a design is robust?

    Run A/B tests across client LLMs, track mismatch telemetry (intended vs. chosen params), include adversarial and multilingual queries, and monitor defaults_applied and tool-call counts.

Tool design is context engineering: the MCP will carry your tools, but the shape you give those tools decides whether the model carries a feather or a boulder. Start small with constrained schemas and defaults, add lazy loading where taxonomies are large, and move to server-side introspection or agents only when telemetry justifies the extra cost and complexity.