Skilllibrary mcp-schema-contracts
Design and evolve MCP tool schemas (inputSchema, outputSchema), resource URIs, and prompt argument contracts. Use when designing JSON Schema for MCP tool parameters, implementing outputSchema for structured tool results, planning schema versioning for published servers, or validating schema backward compatibility.
git clone https://github.com/merceralex397-collab/skilllibrary
T=$(mktemp -d) && git clone --depth=1 https://github.com/merceralex397-collab/skilllibrary "$T" && mkdir -p ~/.claude/skills && cp -r "$T/07-mcp/mcp-schema-contracts" ~/.claude/skills/merceralex397-collab-skilllibrary-mcp-schema-contracts && rm -rf "$T"
07-mcp/mcp-schema-contracts/SKILL.mdPurpose
Design robust schemas for MCP tools, resources, and prompts. MCP uses JSON Schema for tool input/output definitions. Good schemas enable LLMs to use tools correctly, hosts to validate arguments, and servers to evolve without breaking clients.
When to use this skill
- Designing
for MCP toolsinputSchema - Implementing
for structured tool resultsoutputSchema - Planning schema evolution for published MCP servers
- Validating schema backward compatibility before releasing new versions
- Choosing between structured (
) and unstructured (structuredContent
) responsescontent
Do not use this skill when
- Implementing tool logic → use
mcp-tool-design - General server development → use
mcp-development - The schemas are trivial (1-2 string params, text output)
MCP schema fundamentals
inputSchema
Every tool declares an
inputSchema — a JSON Schema object that defines what arguments the tool accepts:
{ "name": "search_issues", "inputSchema": { "type": "object", "properties": { "query": { "type": "string", "description": "Search query text" }, "state": { "type": "string", "enum": ["open", "closed", "all"], "description": "Filter by issue state" }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20, "description": "Max results" } }, "required": ["query"] } }
Critical: The
description on each property is what the LLM reads to understand what to pass. Make descriptions clear and specific.
outputSchema
Optional JSON Schema defining the structured output a tool produces:
{ "name": "get_weather", "outputSchema": { "type": "object", "properties": { "temperature": { "type": "number", "description": "Temperature in Celsius" }, "conditions": { "type": "string" }, "humidity": { "type": "number" } }, "required": ["temperature", "conditions", "humidity"] } }
When
outputSchema is declared, the tool response includes structuredContent:
{ "content": [{ "type": "text", "text": "{\"temperature\": 22.5, ...}" }], "structuredContent": { "temperature": 22.5, "conditions": "Partly cloudy", "humidity": 65 } }
Rule: Always include the JSON-serialized form in
content[].text for backward compatibility with clients that don't support structuredContent.
Schema design patterns
Pattern 1 — Constrained enums
Use
enum for parameters with known valid values:
{ "type": "string", "enum": ["asc", "desc"], "description": "Sort direction" }
Pattern 2 — Optional params with defaults
{ "page": { "type": "integer", "default": 1, "minimum": 1, "description": "Page number (default: 1)" }, "per_page": { "type": "integer", "default": 20, "minimum": 1, "maximum": 100 } }
Pattern 3 — Nested objects
For complex inputs, nest JSON Schema objects:
{ "filters": { "type": "object", "properties": { "status": { "type": "string", "enum": ["active", "archived"] }, "created_after": { "type": "string", "format": "date-time" } } } }
Pattern 4 — Arrays
{ "tags": { "type": "array", "items": { "type": "string" }, "description": "Tags to filter by", "maxItems": 10 } }
Schema evolution rules
When a published MCP server changes its schemas:
Backward-compatible (minor version bump):
- Adding new optional parameters
- Adding new tools
- Widening an enum (adding new values)
- Relaxing a constraint (e.g., increasing maxLength)
Breaking (major version bump):
- Removing or renaming a tool
- Removing or renaming a parameter
- Making an optional parameter required
- Narrowing an enum (removing values)
- Tightening constraints on existing parameters
- Changing a parameter's type
Schema validation best practices
Using Zod (TypeScript):
import { z } from "zod"; const SearchParams = z.object({ query: z.string().describe("Search query"), limit: z.number().int().min(1).max(100).default(20).describe("Max results"), }); server.tool("search", SearchParams.shape, async (params) => { ... });
Using Pydantic (Python):
from pydantic import BaseModel, Field class SearchParams(BaseModel): query: str = Field(description="Search query") limit: int = Field(default=20, ge=1, le=100, description="Max results")
Decision rules
- Every
property MUST have ainputSchema
— the LLM needs this to use the tool correctlydescription - Use
array to distinguish mandatory vs optional parametersrequired - Prefer
over free-text for parameters with known valid valuesenum - Keep schemas as flat as possible — deep nesting confuses LLMs
- Use
when clients need to parse structured responses programmaticallyoutputSchema - Always include backward-compatible
alongsidecontent[].textstructuredContent - If a tool has more than 8 parameters, split into multiple tools or use optional params with defaults
Output requirements
for every tool with descriptions on all propertiesinputSchema
where structured responses are neededoutputSchema- Schema evolution plan for published servers
- Validation using Zod (TS) or Pydantic (Python)
Related skills
— tool design beyond schemasmcp-tool-design
— resource and prompt schemasmcp-resources-prompts
— versioning published serversmcp-marketplace-publishing
— evolving schemas for wrapped APIsmcp-migration-retrofit
Failure handling
- If an LLM sends invalid arguments, the host returns
— check that the schema matches what you expect-32602 Invalid params - If
is not supported by a client, fall back tostructuredContent
(this is automatic if you include both)content[].text - If schema changes break existing clients, issue a major version bump and document the migration