Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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.

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.

  1. The client sends tools/list.
  2. The server returns the available definitions and their schemas.
  3. The model chooses a tool and the client sends tools/call with its name and arguments.
  4. 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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 Programming Language - Software Engineer & Coder T-Shirt
  • 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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 tools capability and sets listChanged only when needed.
  • Call tools/list and verify every name, description, and schema.
  • Test missing, extra, wrong-type, boundary, and malicious arguments.
  • Call each tool with valid data and validate structuredContent against outputSchema.
  • 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_changed and verify clients refresh.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.