DEV Community

Jeff
Jeff

Posted on Originally published at powerduck.com

Loose MCP Input Schemas Let the Agent Send Invalid Parameters

Quick answer: An MCP tool with a loose input schema — everything typed as a string, required fields left unmarked, or a catch-all object — pushes validation to runtime and invites the agent to send malformed parameters that the upstream API rejects. The tool's input schema is the contract the model reads to decide what to send, so it should be precise: explicit types, required fields, enums where the set is closed, and constraints that mirror the API. When the schema is tight, the model gets structured guidance and many invalid calls are rejected at the tool boundary before they ever reach the upstream service.

Loose schemas are usually born from haste. A tool is wrapped quickly with a generic object parameter, and it works for the happy path because the agent happens to send the right shape. Then a slightly different request omits a field or passes a number where the API expects an ISO string, and the error surfaces far from the cause, as a 400 from the upstream API. The model cannot infer constraints it was never given. Treating the input schema as a first-class design artifact, rather than a wrapper detail, is what keeps an agent's tool calls reliable.

What a precise schema communicates

The model uses the schema to construct arguments, so each piece of it carries real guidance.

Schema element Loose version Precise version
Types Everything string string/integer/boolean/array
Required Nothing marked Required fields explicit
Closed sets Free string enum with documented values
Constraints None minLength, pattern, minimum
Nested shape Catch-all object Defined properties

Define the schema the model actually reads

An MCP tool declares a JSON Schema for its arguments. Mirroring the upstream API's constraints means the agent receives the same structural guidance a generated client would enforce.

{ "name": "create_order", "description": "Create a new order in the billing service.", "inputSchema": { "type": "object", "properties": { "customerId": { "type": "string", "pattern": "^cus_[A-Za-z0-9]+$", "description": "Stripe-style customer identifier." }, "currency": { "type": "string", "enum": ["usd", "eur", "gbp"], "description": "ISO 4217 currency for the order." }, "amount": { "type": "integer", "minimum": 1, "description": "Amount in the smallest currency unit." }, "metadata": { "type": "object", "additionalProperties": { "type": "string" } } }, "required": ["customerId", "currency", "amount"], "additionalProperties": false } }

Validate at the tool boundary

A precise schema lets the tool reject invalid arguments with a clear, local error before calling upstream, which is faster to debug and protects the API from malformed traffic. The error should tell the model exactly which constraint failed so it can correct the arguments rather than guessing.

async function createOrder(args) { const valid = validator.validate(CREATE_ORDER_SCHEMA, args); if (!valid) { // structured, local error the model can act on const detail = validator.errors.map((e) =>${e.instancePath} ${e.message}).join("; "); throw new ToolInputError("Invalid create_order arguments: " + detail); } return api.post("/v1/orders", args); }

Generate the schema from the spec to avoid drift

When the MCP tool is generated from an OpenAPI document, the input schema is derived from the operation's request schema, so it stays aligned as the API evolves rather than being hand-maintained. A field that becomes required or an enum that gains a value propagates automatically, and contract validation catches a mismatch. This keeps the guidance the model relies on accurate without a second schema to maintain, and it turns a loose, error-prone wrapper into a tightly specified tool the agent can call correctly.

Frequently asked questions

Does a tight schema limit the agent's flexibility?

It constrains inputs to what the API actually accepts, which is the goal. Genuine flexibility belongs in fields the API allows; a loose schema does not add capability, it only delays validation.

What if the upstream accepts dynamic, unknown fields?

Use additionalProperties with a defined value type rather than an untyped catch-all, so the model knows the shape of dynamic values while still sending a valid structure.

Should validation happen in the MCP layer or rely on the API?

Both serve different purposes. Local validation gives fast, structured feedback and blocks bad calls; the API remains the final authority. Relying only on the API means every mistake costs a round trip and an opaque error.

How do I keep the schema from drifting as the API changes?

Generate the MCP tool and its input schema from the OpenAPI spec, and run contract validation in CI. A hand-written wrapper is where drift and loose types tend to creep back in.

This is part of the MCP Server Guide — start there for the full picture of building reliable agent tools.

Top comments (0)