Without a JSON Schema inputSchema, AI clients have no contract for what arguments a tool accepts — they guess at parameter names, types, and format. OWASP LLM09 (Misinformation) is the direct risk: the AI constructs plausible-looking but wrong invocations, producing silently incorrect behavior. Missing required-field markers mean the AI may omit critical parameters; missing descriptions mean it cannot distinguish a file path from a query string. In prompt-injection scenarios (CWE-20, OWASP A03), undefined inputs widen the attack surface because the server cannot reject malformed or adversarial arguments before execution.
Critical because a missing inputSchema means the AI client has no enforceable contract, so every tool invocation is a guess that can silently corrupt behavior or enable injection (CWE-20).
Add a Zod schema as the third argument to every server.tool() call in your TypeScript MCP server. The SDK converts it to inputSchema automatically.
// src/tools/search.ts
server.tool(
'search_files',
'Search for files matching a glob pattern',
{
pattern: z.string().describe('Glob pattern, e.g. "**/*.ts"'),
directory: z.string().optional().describe('Root directory to search; defaults to project root'),
maxResults: z.number().int().min(1).max(500).default(50),
},
async ({ pattern, directory, maxResults }) => {
// handler
}
)
For Python, add type annotations and a docstring with Args: block — the SDK derives the schema from them automatically.
ID: mcp-server.tool-definitions.input-schemas
Severity: critical
What to look for: Count all tool definitions in the MCP server. Enumerate which have JSON Schema input definitions vs. which accept unvalidated input. Examine every tool registration. In the TypeScript SDK, check the second argument to server.tool() — it should be a Zod schema or a JSON Schema object defining inputSchema with type, properties, required, and description for each property. In Python, check @mcp.tool() decorated functions for type annotations and docstrings, or explicit inputSchema in tool registration. For custom implementations, check the inputSchema field in the tools/list response. Every tool parameter should have a type, a description, and be marked required or optional. Before evaluating, extract and quote the first 3 tool input schema definitions found in the codebase.
Pass criteria: Every registered tool has an inputSchema that defines types for all parameters, marks required fields, and includes description strings for each property. No tool has an empty or missing inputSchema. At least 100% of tools must have JSON Schema input definitions.
Fail criteria: Any tool is missing inputSchema, or has an inputSchema with no properties, or parameters lack type definitions, or required fields are not specified, or descriptions are missing from parameters.
Skip (N/A) when: The server registers no tools (resource-only or prompt-only server). All checks skip when no MCP server is detected.
Cross-reference: For input validation enforcement, see input-validation. For tool naming, see tool-naming.
Detail on fail: "3 of 5 tools have no inputSchema — 'search_files', 'run_query', 'deploy' accept arguments but don't define their schemas. AI clients will guess at parameter names and types" or "Tool 'create_issue' has inputSchema but 'title' and 'body' parameters have no descriptions — AI must guess what format to use"
Remediation: Input schemas are how AI clients know what arguments your tools accept. Without them, the AI guesses — and guesses wrong:
// src/tools/search.ts — JSON Schema for tool input
{ name: "search", inputSchema: { type: "object", properties: { query: { type: "string" } }, required: ["query"] } }
// TypeScript SDK — Zod schema defines inputSchema automatically
import { z } from 'zod'
server.tool(
'search_files',
'Search for files matching a pattern in the project',
{
pattern: z.string().describe('Glob pattern to match files (e.g., "**/*.ts")'),
directory: z.string().optional().describe('Directory to search in, defaults to project root'),
maxResults: z.number().default(50).describe('Maximum number of results to return'),
},
async ({ pattern, directory, maxResults }) => {
// handler
}
)
# Python SDK — type annotations define inputSchema
@mcp.tool()
async def search_files(
pattern: str,
directory: str | None = None,
max_results: int = 50,
) -> list[str]:
"""Search for files matching a glob pattern in the project.
Args:
pattern: Glob pattern to match files (e.g., "**/*.ts")
directory: Directory to search in, defaults to project root
max_results: Maximum number of results to return
"""
# handler