Tools
Purpose and Scope
A tool is the server-side MCP primitive for actions that a connected client, and usually the model driving that client, can invoke. In the TypeScript SDK, tool registration is intentionally centered on one public call: server.registerTool(name, config, handler). The name is the stable protocol identifier, the config describes what the model should know, and the handler performs the action after the SDK has parsed and validated the input. This page explains how that lifecycle works from registration through tools/list, tools/call, validation failures, structured output, and request-scoped behavior inside handlers.
Sources: docs/servers/tools.md
Tool design should start from the model's view of the action. The model sees the tool name, description, and JSON Schema derived from the input schema. It does not see your TypeScript types or implementation details, so argument names and schema descriptions carry real product weight. The SDK examples emphasize using .describe() on schema fields because those descriptions survive conversion and become the documentation available to the model when it decides how to call the tool.
Sources: docs/servers/tools.md
Relevant Source Files
docs/servers/tools.md— Primary guide for registering tools, calling them from a client, validation behavior, structured output, and the v2registerToolreplacement for v1tool().docs/servers/errors.md— Defines the distinction between model-visible tool errors withisError: trueand JSON-RPC protocol errors, including thrown tool-handler exceptions.docs/servers/input-required.md— Shows the 2026-07-28 input-required pattern where a tool returns embedded input requests and later resumes with client-provided responses.docs/servers/elicitation.md— Covers the older push-stylectx.mcpReq.elicitInputflow used by tool handlers on supported protocol revisions.docs/servers/logging-progress-cancellation.md— Documents handler context features such as progress notifications, logging, and cancellation-related request helpers.docs/servers/completion.md— Provides adjacent server-side schema behavior for prompt and resource argument completion; it is useful context when designing schemas, though completion is not registered on tools in the supplied guide.
Registering a Tool
Create tools on an McpServer with registerTool. The config commonly includes a human-readable description and an inputSchema. In the guide, inputSchema is a Zod v4 object, and the SDK uses that single schema for three purposes: it derives the JSON Schema advertised by tools/list, validates incoming arguments before the handler runs, and infers the handler argument type for TypeScript. This means you do not maintain parallel runtime validation, protocol schema, and handler type declarations for the same input contract.
Sources: docs/servers/tools.md
import { McpServer } from '@modelcontextprotocol/server';
import * as z from 'zod/v4';
const server = new McpServer({ name: 'catalog', version: '1.0.0' });
server.registerTool(
'search',
{
description: 'Search the product catalog',
inputSchema: z.object({
query: z.string().describe('Substring to match against product names'),
limit: z.number().int().max(50).optional()
})
},
async ({ query, limit }) => {
return { content: [{ type: 'text', text: `Searching for ${query}` }] };
}
);The handler receives parsed arguments rather than raw JSON. In the catalog example, a valid call with { query: 'mug' } reaches the handler as a typed object, and the handler returns a standard tool result whose content array is passed back unchanged to the client. A host calling over stdio or HTTP observes the same protocol behavior as an in-memory test client: it first discovers the registered tool through tools/list, then invokes it with tools/call by name and arguments.
Sources: docs/servers/tools.md
Client Invocation and Validation Behavior
Clients invoke a tool with client.callTool({ name, arguments }). The server validates arguments against the registered input schema before the handler runs. If validation rejects the payload, the SDK returns an ordinary tool result with isError: true instead of executing the handler. In the guide, a limit greater than the schema maximum produces a text message such as Input validation error: Invalid arguments for tool search: limit: Too big: expected number to be <=50. That result is model-visible, so the model can read the problem and retry with corrected arguments.
Sources: docs/servers/tools.md, docs/servers/errors.md
This distinction matters when choosing error channels. Tool errors are successful JSON-RPC results whose payload says the action failed; protocol errors are JSON-RPC error responses for cases where the request itself is invalid or cannot be processed as that method. Tool handlers can either return isError: true explicitly or throw an exception; the SDK catches thrown tool-handler exceptions and converts them to the same tool-result shape. Returning explicitly gives you better control over recovery hints, and recovery hints should be in the text content because that is what the model reads.
Sources: docs/servers/errors.md
Structured Output
For tools that produce machine-readable data, add outputSchema and return a matching value as structuredContent alongside human-readable content. The guide's product-details tool uses an input schema for the product name and an output schema containing name and price. Its handler returns both a textual JSON rendering and the structured object. This lets a model or host present a friendly answer while another component consumes the typed data without parsing prose.
Sources: docs/servers/tools.md
Structured output does not replace content; it complements it. The visible text is still important for model reasoning and user-facing transcripts, while structuredContent is the contract for callers that need stable fields. Error results are treated differently: the errors guide states that the SDK skips outputSchema validation on any isError result. That allows a failure path to return a clear recovery message even when it cannot produce the normal structured payload.
Sources: docs/servers/tools.md, docs/servers/errors.md
Handler Context: Progress, Input, and Long-Running Calls
Every handler receives a second argument, commonly named ctx, whose request-scoped MCP helpers live on ctx.mcpReq. For long-running tools, clients can ask for progress by passing an onprogress callback; the SDK puts a progressToken in request _meta, and the handler can send notifications/progress through ctx.mcpReq.notify. The documented pattern checks whether a progress token exists before sending notifications, so the same tool can run quietly when the client did not request progress.
Sources: docs/servers/logging-progress-cancellation.md
Tools can also require human input while a call is in progress, but the recommended shape depends on protocol revision. The input-required guide describes the 2026-07-28 pattern: a tool returns inputRequired(...) with one or more embedded input requests, the client fulfills them, and the call is retried with inputResponses. On re-entry, helpers such as acceptedContent and inputResponse let the handler inspect and validate the responses before continuing. The elicitation guide documents the older push-style ctx.mcpReq.elicitInput helper, which asks the connected client to present a form or URL flow and resolves with accept, decline, or cancel.
Sources: docs/servers/input-required.md, docs/servers/elicitation.md
System-to-Code Mapping
| Concern | Public surface or behavior | Source |
|---|---|---|
| Tool registration | server.registerTool(name, config, handler) with description, inputSchema, and optional outputSchema | docs/servers/tools.md |
| Discovery | Registered tools appear through tools/list with JSON Schema derived from the input schema | docs/servers/tools.md |
| Invocation | Clients call client.callTool({ name, arguments }); valid calls return handler content | docs/servers/tools.md |
| Input validation | Rejected arguments produce a model-visible isError: true tool result before the handler runs | docs/servers/tools.md |
| Handler failures | Returned isError: true and thrown handler exceptions become tool errors; protocol errors are separate | docs/servers/errors.md |
| Structured data | outputSchema validates normal structuredContent; error results skip output validation | docs/servers/tools.md, docs/servers/errors.md |
| Request context | ctx.mcpReq exposes progress, logging, input, and request metadata helpers | docs/servers/logging-progress-cancellation.md, docs/servers/input-required.md, docs/servers/elicitation.md |
Practical Design Checklist
When adding a production tool, first choose a name that is stable and specific enough for clients to cache or display. Then write a concise description that tells the model when to use it, not just what function it calls. Put the strongest constraints into the schema: required fields, string descriptions, numeric limits, enum-like choices, and object shapes. The SDK will advertise and enforce those constraints, so good schema design reduces invalid calls before they reach business logic.
Sources: docs/servers/tools.md, docs/servers/errors.md
Next, decide how the tool should communicate success, recoverable failure, and exceptional request problems. Return normal content and optionally structuredContent for success. Return isError: true with actionable text when the model can recover, such as choosing another id or changing an argument. Let thrown exceptions become tool errors only when the default message is sufficient, and reserve protocol errors for non-tool methods or cases where the request itself is invalid at the protocol layer. For long operations, wire progress only when _meta.progressToken is present, and use input-required or elicitation patterns deliberately when the user must decide something mid-call.
Sources: docs/servers/errors.md, docs/servers/logging-progress-cancellation.md, docs/servers/input-required.md, docs/servers/elicitation.md
Next Steps
After implementing a tool, test it through a real Client call rather than only unit-testing the handler function. That verifies discovery, schema conversion, validation, and result shape together. Read the related pages on resources and prompts when your server needs to expose context or reusable prompt templates, and read the errors, input-required, and logging/progress guides before building tools that interact with users or run for more than a brief request-response cycle.