Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Define an MCP tool as a uniquely named object with a useful description and an object-shaped JSON Schema in inputSchema. Advertise the tools capability, answer discovery through tools/list, and execute requests through tools/call. Add outputSchema when clients need validated, machine-readable results.
This guide shows the wire contract, complete TypeScript and Python registration paths, structured output, annotations, errors, security, and practical troubleshooting.
The anatomy of an MCP tool definition
The current MCP tools model uses a JSON object with a required name, description, and inputSchema. The optional fields are title, icons, outputSchema, annotations, execution, and _meta.
| Field | Purpose | Requirement |
|---|---|---|
name |
Stable identifier clients send to tools/call |
Required; unique within the server, case-sensitive, 1–128 characters; use letters, digits, underscore, hyphen, or dot |
description |
Explains what the tool does, inputs, side effects, and limits to a model | Required in a useful definition |
inputSchema |
JSON Schema describing arguments | Required and must be an object-shaped schema |
outputSchema |
JSON Schema for machine-readable output | Optional; required results must conform when supplied |
annotations |
Hints such as read-only, destructive, idempotent, or open-world behavior | Optional and untrusted |
When $schema is omitted, MCP specifies JSON Schema 2020-12. Describe every argument with properties, mark mandatory arguments in required, and add constraints and descriptions that help a model produce valid calls.
#1 Best Overall
Minimal definition
{
"name": "get_weather",
"title": "Weather Information Provider",
"description": "Get current weather information for a location.",
"inputSchema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name or postal code"
}
},
"required": ["location"],
"additionalProperties": false
}
}
A no-argument tool should still declare an explicit object: {"type":"object","additionalProperties":false}. This prevents accidental, undocumented arguments.
How clients discover and call tools
During initialization, a server advertises support with a tools capability. Set listChanged when the catalog can change at runtime.
- The client sends
tools/list. - The server returns the available definitions and their schemas.
- The model chooses a tool and the client sends
tools/callwith its name and arguments. - The server returns a tool result containing content and, when applicable, structured data.
If the catalog changes, send notifications/tools/list_changed. The client should then call tools/list again. Names must remain stable: changing a name breaks prompts and cached client decisions.
Recommended Free Tools
TypeScript: register a tool with the official SDK
The official MCP TypeScript SDK provides the server registration API. The example below defines a read-only weather tool, validates its input, and returns structured output that matches the declared schema. Transport setup is intentionally separate because stdio, HTTP, and other transports vary by application.
Rank #2
- TypeScript implements a superset of syntax for strictly typed development, facilitating deep static analysis and enhanced development environment integration. The compiler translates source into standard script formats, ensuring parity across any runtime.
- TypeScript is ideal for front-end developers, full-stack engineers, and software architects who build large-scale web applications. It serves those looking to improve code excellence, reduce bugs through static checking, and maintain complex projects more.
- Lightweight, Classic fit, Double-needle sleeve and bottom hem
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
const server = new McpServer({
name: "weather-server",
version: "1.0.0"
});
server.registerTool(
"get_weather",
{
title: "Weather Information Provider",
description: "Get current weather for a city or postal code. Read-only; does not change data.",
inputSchema: {
location: z.string().min(1).describe("City name or postal code")
},
outputSchema: {
type: "object",
properties: {
location: { type: "string" },
temperatureC: { type: "number" },
condition: { type: "string" }
},
required: ["location", "temperatureC", "condition"],
additionalProperties: false
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true
}
},
async ({ location }) => {
// Replace with an authenticated weather-provider request.
const result = {
location,
temperatureC: 20,
condition: "clear"
};
return {
content: [{ type: "text", text: JSON.stringify(result) }],
structuredContent: result
};
}
);
// Connect `server` to the transport used by your host application.
Keep the human explanation in content. Put fields intended for programs in structuredContent. If the SDK rejects arguments, it normally returns a tool result describing the validation problem; protocol-level failures such as an unknown tool are different and can throw.
Explicit JSON Schema in TypeScript
Use an explicit schema when you need exact wire-level control, interoperability with non-TypeScript clients, or constraints that automatic type inference cannot express. Ensure the handler returns every required output property and no value that violates its declared type.
Python: low-level and decorator registration
The official Python SDK exposes low-level Server handlers named list_tools and call_tool. Its documentation treats both input and output schemas as JSON Schema and uses 2020-12 when $schema is absent.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
from mcp.server.lowlevel import Server
from mcp.types import Tool, TextContent
app = Server("weather-server")
@app.list_tools()
async def list_tools():
return [
Tool(
name="get_weather",
description="Get current weather for a city or postal code. Read-only.",
inputSchema={
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name or postal code"
}
},
"required": ["location"],
"additionalProperties": False
},
outputSchema={
"type": "object",
"properties": {
"location": {"type": "string"},
"temperatureC": {"type": "number"},
"condition": {"type": "string"}
},
"required": ["location", "temperatureC", "condition"],
"additionalProperties": False
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict):
if name != "get_weather":
raise ValueError(f"Unknown tool: {name}")
location = arguments["location"]
result = {"location": location, "temperatureC": 20, "condition": "clear"}
return [
TextContent(type="text", text=str(result))
]
# Attach `app` to the transport selected by your host process.
The Python SDK also documents decorator-based registration and a structured_output control for typed return values. That style is convenient when your application already uses Python type annotations; explicit schemas are preferable when you need to inspect or version the exact protocol contract.
Design input schemas that models can use safely
Make required data unambiguous
Use required for values the operation cannot perform without. Add descriptions for units, accepted formats, timezone assumptions, pagination limits, and whether an identifier is user-visible or internal. Set additionalProperties:false unless forward-compatible extra fields are deliberately supported.
Constrain risky values
Use JSON Schema types, enums, minimum and maximum limits, string patterns, and array limits. A delete tool should accept a specific identifier rather than an unconstrained query. Validate again inside the handler; schemas guide clients but are not an authorization boundary.
Keep names stable and specific
Names are case-sensitive and unique only within one server. Prefer calendar.create_event or files.read over vague names such as run. Stay within the 1–128-character recommendation and allowed character set.
Structured output, content, and errors
When an outputSchema is present, the server must return structured results conforming to it, normally in structuredContent. Clients should validate the result. Include text in content when a person or model also needs an explanation, warning, or concise status.
Tool results can additionally contain images, audio, resource links, or embedded resources. Separate presentation from data: do not force clients to parse a prose sentence to obtain an ID or status.
Recoverable versus protocol errors
- Invalid arguments: return a tool result explaining the field and expected value, or let the SDK’s validation path produce that result.
- External failure: return a clear, non-sensitive message and a retry hint when appropriate; do not claim success.
- Unknown tool: treat it as a protocol-level failure rather than silently dispatching a similarly named operation.
- Authorization failure: deny before the side effect and avoid leaking whether protected resources exist.
Annotations and side-effect safety
readOnlyHint, destructiveHint, idempotentHint, and openWorldHint help clients reason about behavior. They are hints, not guarantees. The MCP specification says clients must consider annotations from untrusted servers untrusted unless the server is trusted. Enforce permissions, confirmation, rate limits, and audit logging in server code instead of relying on annotations.
Testing and operational checklist
- Confirm the server advertises the
toolscapability and setslistChangedonly when needed. - Call
tools/listand verify every name, description, and schema. - Test missing, extra, wrong-type, boundary, and malicious arguments.
- Call each tool with valid data and validate
structuredContentagainstoutputSchema. - Test timeouts, upstream errors, retries, duplicate calls, and authorization failures.
- For mutating tools, verify idempotency behavior and confirmation requirements.
- When adding or removing tools dynamically, emit
notifications/tools/list_changedand verify clients refresh.
Common failures and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Client shows no tools | The server did not advertise tools, or the client has not completed discovery |
Advertise the capability and inspect the tools/list response |
| Schema rejected | inputSchema is not an object-shaped valid JSON Schema |
Add type:"object", valid properties, and correct required entries |
| Tool call says unknown name | Case mismatch, stale catalog, or a typo | Use the exact case-sensitive name returned by tools/list; refresh after a list-change notification |
| Structured result fails validation | Handler output differs from outputSchema |
Return every required field with the declared types, or revise the schema deliberately |
| Model sends unsafe values | Descriptions or constraints are vague | Add enums, ranges, formats, and server-side authorization and validation |
| Side effect repeats | Retries are not idempotent | Use an idempotency key or document and enforce duplicate-call behavior |
Or skip the browser setup
If your MCP server needs screenshots for an AI workflow, ScreenshotNeo provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools. Its capture flow accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →One request is enough:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for options such as full-page and selector capture, device and retina settings, PDF output, custom CSS or JavaScript, waits, request blocking, cookies, headers, geolocation, caching, signed links, asynchronous webhooks, bulk capture, and usage reporting. The free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
Frequently Asked Questions
What happens if a server omits outputSchema?
The tool can still return unstructured content. Add outputSchema when clients need a stable, machine-readable contract and validation.
Best Value
Can two MCP servers use the same tool name?
Yes. Names must be unique within each server; clients distinguish tools by their connected server.
Are tool annotations a security control?
No. They are advisory hints and must be treated as untrusted for servers that are not trusted.
Free tools Windows power users keep installed
One-click scans. No signup required.
When should I send a list-changed notification?
Send notifications/tools/list_changed whenever the available tool catalog changes, then let clients call tools/list again.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

