Write clear MCP tool schemas an agent can use
What we checked
We read the tool and resource definitions your server exposes and assessed how usable they are for a model:
Why it matters
The model never sees your implementation; it sees your schema. The tool
description, each parameter’s description (a standard
JSON Schema keyword that MCP tool inputs are built
on), and any examples are the entire brief the model works from when it
decides whether to call a tool and how to fill in the arguments. Vague or
missing descriptions lead directly to wrong calls, malformed arguments, and
tools that get ignored.
There’s a tension to manage: richer descriptions help the model, but every token of every definition is loaded into the context window on every request. Bloated schemas crowd out the actual task and cost latency and money. The goal is clear and lean: say what’s needed, nothing more.
How to fix it
Give every tool a precise description, describe every parameter, and add a
short usage example. Then trim anything that isn’t pulling its weight. The
contrast below shows a poorly-described tool beside an agent-ready one.
// @modelcontextprotocol/sdk - describe the tool AND each parameter.
import { z } from "zod";
// Poor: the model can't tell what 'q' means or what this returns.
server.registerTool(
"search",
{ description: "Search", inputSchema: { q: z.string() } },
handler,
);
// Good: precise description, every param described, an example in prose.
server.registerTool(
"search_invoices",
{
description:
"Search invoices by customer or status. Returns up to 50 matches, " +
"newest first. Example: status='overdue', customer='acme' finds Acme's " +
"overdue invoices.",
inputSchema: {
customer: z.string().describe("Customer name or ID to filter by."),
status: z
.enum(["draft", "open", "paid", "overdue"])
.describe("Invoice status to filter by."),
},
},
handler,
); # Official 'mcp' SDK (FastMCP) - the docstring becomes the tool description;
# typed, described fields become the parameter schema.
from typing import Literal
from pydantic import Field
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-server")
@mcp.tool()
def search_invoices(
customer: str = Field(description="Customer name or ID to filter by."),
status: Literal["draft", "open", "paid", "overdue"] = Field(
description="Invoice status to filter by."
),
) -> list[dict]:
"""Search invoices by customer or status. Returns up to 50 matches,
newest first. Example: status='overdue', customer='acme' finds Acme's
overdue invoices."""
... How much detail should a tool schema include?
Enough that a model can pick the tool and fill every argument without guessing, and no more. Describe the tool, describe each parameter, and give one short example, then stop. We grade description coverage against a per-tool token budget (around 100 tokens per item for a lean pass), so wording that repeats across tools spends that budget without adding clarity. Aim for clear and specific rather than long.
How we re-check
We re-read your schemas on the next capture: a crawl for a hosted endpoint, or a fresh sandbox run of your published package. As description coverage rises, you add examples, and the definitions stay lean, the schema-quality signals improve on the following score refresh.
Give this to your AI
Paste this into Claude Code (or any coding agent) from inside your server's repository. It states the failing signal, the outcome we re-check for, and the format the fix has to take.
Context: this repository publishes an MCP server, either as a hosted endpoint or as a package on a registry such as npm or PyPI. VerifyMCP's schema quality check failed, which means the descriptions a model relies on are missing, vague, or bloated, or the schemas lack a usage example. Note: the descriptions part of this check measures prompt and resource descriptions; tool and parameter descriptions are scored separately under Tool Coverage, but the same writing bar applies to both. Goal: every tool has a precise `description`, every parameter is described, at least one short usage example exists, and each schema stays within the roughly 100-token-per-item footprint budget, so the next capture of your schema passes on descriptions, examples, AI-judged clarity, and footprint. Do this: 1. List every tool, prompt, and resource your server registers and check each one's `description` field and, for tools, every parameter's `description` keyword. 2. Write a precise, tool-specific description for each tool: what it does, what it returns, and one short concrete example of when to call it (e.g. `status='overdue', customer='acme'`). 3. Describe every parameter individually in terms specific to that field, not a sentence copy-pasted across tools. 4. Trim anything not pulling its weight: shared boilerplate, restated type information, or filler prose that inflates token count without adding clarity. Rules: - Never pad descriptions with repeated boilerplate purely to consume the token budget, and never stuff keyword lists into a description just to influence the AI-judged clarity score; write for genuine comprehension. - Keep descriptions accurate to what the tool actually does; do not describe behaviour or guarantees the implementation does not provide. Report back: the tools, prompts, resources, and parameters you added or rewrote descriptions for, the examples you added, and an approximate token count per schema before and after. Reference: https://verifymcp.io/docs/remote/schema-quality