A tool in the Model Context Protocol (MCP) is a function that a server exposes so that a language model can call it through the host: search a bug tracker, query a database, send a message, call an external API. Each tool has a unique name, a description and a JSON Schema of its input parameters. The client gets the list of tools with tools/list and invokes a tool with tools/call.
Tools have been part of MCP since the first published version of the specification, 2024-11-05. Later revisions added annotations, structured results, icons, rules for names and caching hints. This article follows the current version of the specification, 2026-07-28. The basic concepts of the protocol (host, client, server, primitives) are described in the article What is MCP.
Overview
| Property | Value |
|---|---|
| Kind | Server primitive |
| Who decides to call | The model, under the control of the host and the user |
| Methods | tools/list, tools/call |
| Notification | notifications/tools/list_changed |
| Server capability | tools, optional listChanged |
| Definition fields | name, title, description, icons, inputSchema, outputSchema, annotations, _meta |
| Result fields | content, structuredContent, isError |
| Schema dialect | JSON Schema 2020-12 by default |
| Specification | 2026-07-28 |
The problem tools solve
A language model produces text. To act on the outside world it needs functions that an application executes on its behalf. Model APIs support this through function calling: the application sends descriptions of functions with a request, and the model returns a request to call one of them.
Function calling does not say where the functions come from. An MCP tool moves the description to the side of the service. The server author describes the tool once, and any host that supports MCP receives the description at runtime through tools/list.
The specification calls tools model-controlled: the model decides when to call them, based on the context and the request of the user. The protocol does not prescribe a user interface, but recommends a human in the loop who can deny a call.
History
The table lists the changes to tools in each version of the specification.
| Date | Event |
|---|---|
| November 2024 | First version of the specification, 2024-11-05: tools/list, tools/call, text, image and embedded resource results, isError, notifications/tools/list_changed |
| March 2025 | Specification 2025-03-26: tool annotations, audio content |
| June 2025 | Specification 2025-06-18: title, structured results with outputSchema and structuredContent, resource links in results |
| November 2025 | Specification 2025-11-25: icons (SEP-973), guidance on tool names (SEP-986), input validation errors as tool execution errors (SEP-1303), JSON Schema 2020-12 as the default dialect (SEP-1613) |
| 28 July 2026 | Specification 2026-07-28: x-mcp-header (SEP-2243), caching hints (SEP-2549), deterministic order of the list, change notifications through subscriptions/listen, any JSON Schema 2020-12 keywords in schemas and any JSON value in structuredContent (SEP-2106) |
Tool definition
A tool definition is a JSON object. The example shows the main fields:
{
"name": "search_issues",
"title": "Search issues",
"description": "Search issues in the bug tracker by text and status. Use it to check for an existing issue before creating a new one. Returns at most 20 issues, newest first. Does not return comments or attachments.",
"inputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Words to look for in the issue title and description"
},
"status": {
"type": "string",
"enum": ["open", "in_progress", "closed"],
"description": "Only issues with this status. All statuses if omitted"
}
},
"required": ["query"],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {
"issues": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"title": { "type": "string" },
"status": { "type": "string" }
},
"required": ["id", "title", "status"]
}
}
},
"required": ["issues"]
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": false
},
"icons": [
{ "src": "https://tracker.example.com/icons/search.png", "mimeType": "image/png", "sizes": ["48x48"] }
]
}The table shows the purpose of each field in the 2026-07-28 schema.
| Field | Required | Purpose |
|---|---|---|
name |
Yes | Unique identifier of the tool within the server |
title |
No | Display name for people |
description |
No | Text for the model: what the tool does and when to use it |
inputSchema |
Yes | JSON Schema of the arguments |
outputSchema |
No | JSON Schema of the structured result |
annotations |
No | Hints about the behavior of the tool |
icons |
No | Icons for the user interface |
_meta |
No | Additional metadata, for example of extensions |
Name, title and description
name is the identifier that the client passes in tools/call. title is meant for the user interface. A display name is chosen in this order: title, then annotations.title, then name. The schema describes description as a hint to the model about the purpose of the tool. The field is formally optional, but without it the model sees only the name and the schema.
Input schema
inputSchema defines the arguments of the tool. Arguments are always a JSON object, so the root of the schema has "type": "object". Since 2026-07-28 any JSON Schema 2020-12 keyword may appear next to it, including oneOf, anyOf, if/then/else and $ref with $defs.
A schema without a $schema field follows JSON Schema 2020-12; another dialect is declared in $schema. The schema must be a valid object, not null. For a tool without parameters the specification recommends { "type": "object", "additionalProperties": false }, which accepts only an empty object. Implementations must not automatically dereference a $ref that points to a network URI.
Output schema
outputSchema describes the structured result of the tool and can be any valid JSON Schema 2020-12, including an array schema. If a tool declares an output schema, the server must return structured results that conform to it, and the client should validate them.
Annotations
Annotations describe the behavior of a tool for the client. The table lists the fields of ToolAnnotations and their default values.
| Field | Meaning | Default |
|---|---|---|
title |
Human-readable title | None |
readOnlyHint |
The tool does not modify its environment | false |
destructiveHint |
The tool may perform destructive updates; meaningful only when readOnlyHint is false |
true |
idempotentHint |
Repeated calls with the same arguments have no additional effect; meaningful only when readOnlyHint is false |
false |
openWorldHint |
The tool interacts with an open world of external entities, such as the web | true |
All annotations are hints and are not guaranteed to describe the tool faithfully. Clients must treat annotations as untrusted unless they come from trusted servers.
Icons and metadata
icons is an array of icons, each with a src (an HTTPS URL or a data: URI) and optional mimeType, sizes and theme. Clients that render icons must support at least PNG and JPEG.
_meta carries metadata outside the core fields. Extensions use it: in MCP Apps, _meta.ui.resourceUri of a tool points to a ui:// resource with an interactive interface. Extensions are described in the article Extensions in MCP.
Tool names
The specification gives recommendations for names (SHOULD, not MUST):
- a name is 1 to 128 characters long and case-sensitive;
- only Latin letters
A-Zanda-z, digits0-9,_,-and.are used, without spaces or commas; - a name is unique within a server.
Uniqueness is limited to one server. A host that connects several servers can receive two tools named search. The specification recommends that such hosts and proxies disambiguate names, for example with a prefix that identifies the server. The server name from serverInfo is not guaranteed to be unique and should not be used for this.
The rules of model APIs can be narrower. In the Claude API a tool name must match ^[a-zA-Z0-9_-]{1,128}$, which does not allow a dot. The MCP specification does not define how a host converts names for the API of its model.
Listing tools
The client requests the list with tools/list. If the result contains nextCursor, the client requests the next page with this value in cursor.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": { "name": "example-client", "version": "1.0.0" },
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "complete",
"tools": [
{
"name": "get_issue",
"title": "Get issue",
"description": "Get one issue by its identifier, with status, assignee and description.",
"inputSchema": {
"type": "object",
"properties": {
"id": { "type": "string", "description": "Issue identifier, for example BUG-142" }
},
"required": ["id"]
},
"annotations": { "readOnlyHint": true }
}
],
"nextCursor": "page-2",
"ttlMs": 300000,
"cacheScope": "public"
}
}The specification sets rules for the content of the list:
- The list contains the tools currently available to the requesting client. It may be empty and may change over time.
- The list must not vary per connection or as a side effect of other requests.
- The list may vary by the authorization of the request, for example by the scopes of the token.
- The server should return tools in a deterministic order, which helps clients cache the list.
Calling a tool
The client calls a tool with tools/call, passing the name of the tool and its arguments:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "search_issues",
"arguments": { "query": "login timeout", "status": "open" },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": { "name": "example-client", "version": "1.0.0" },
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}Over Streamable HTTP the request also carries the headers Mcp-Method: tools/call and Mcp-Name: search_issues. The transports are described in the article Transports in MCP.
The server answers with the final result (resultType: "complete") or with a result of type input_required if it needs additional input, for example from the user. In the second case the client repeats the call with a new id and the answers in inputResponses. This mechanism, Multi Round-Trip Requests (MRTR), is described in the article Stateless MCP.
Tool results
A result can contain unstructured content, structured content or both.
Unstructured content
Unstructured content is the content array, which can hold several items of different types. The table lists the content types of the 2026-07-28 schema.
| Type | Fields | What it carries |
|---|---|---|
text |
text |
Text |
image |
data, mimeType |
Image in Base64 |
audio |
data, mimeType |
Audio in Base64 |
resource_link |
uri, name and optional fields of a resource |
Link to a resource that the client can read |
resource |
resource with uri and text or blob |
Contents of a resource embedded in the result |
All content types support optional annotations: audience, priority and lastModified. A resource link returned by a tool is not guaranteed to appear in resources/list. Resources and annotations are described in the article Resources and resource templates in MCP.
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"resultType": "complete",
"content": [
{ "type": "text", "text": "Issue BUG-142 has a log file attached." },
{
"type": "resource_link",
"uri": "tracker://issues/BUG-142/attachments/server.log",
"name": "server.log",
"mimeType": "text/plain"
}
]
}
}Structured content
Structured content is a JSON value in the structuredContent field. Since 2026-07-28 it can be any JSON value, not only an object. If the tool declares outputSchema, the value must conform to it. For backward compatibility a tool that returns structured content should also return the serialized JSON in a text block.
The specification notes that structuredContent is unrelated to the structured outputs of models.
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"resultType": "complete",
"content": [
{ "type": "text", "text": "{\"issues\": [{\"id\": \"BUG-142\", \"title\": \"Login times out\", \"status\": \"open\"}]}" }
],
"structuredContent": {
"issues": [{ "id": "BUG-142", "title": "Login times out", "status": "open" }]
}
}
}Two kinds of errors
Tools report errors in two ways. The table shows which errors fall into each.
| Kind | Examples | How it is returned | Passed to the model |
|---|---|---|---|
| Protocol error | Unknown tool, request that does not match the CallToolRequest schema, server failure |
JSON-RPC error | The client may pass it |
| Tool execution error | API failure, input validation error (date in a wrong format, value out of range), business logic error | Result with isError: true and an explanation |
The client should pass it |
{
"jsonrpc": "2.0",
"id": 4,
"error": {
"code": -32602,
"message": "Unknown tool: search_isues"
}
}{
"jsonrpc": "2.0",
"id": 5,
"result": {
"resultType": "complete",
"content": [
{ "type": "text", "text": "Invalid status \"done\". Use one of: open, in_progress, closed." }
],
"isError": true
}
}The difference matters because a model can correct its call only if it sees the error. Protocol errors are usually handled by the application, while tool execution errors return to the context of the model.
The current classification comes from SEP-1303 (created on 5 August 2025, status Final), which entered the specification in version 2025-11-25. Before it, "invalid arguments" were listed among protocol errors and "invalid input data" among tool execution errors. SEP-1303 moved all input validation errors to tool execution errors. The 2026-07-28 schema still lists invalid tool arguments among the uses of code -32602, so the schema and the tools page are not fully consistent on this point. How errors are tested is described in the article Testing MCP servers.
State between calls
MCP has no protocol session, so a server cannot relate calls through the connection. The specification gives non-normative guidance for tools that need state, such as a shopping cart or a database transaction. A creation tool returns an explicit handle, for example basket_id, and the following tools accept it as an argument.
According to the guidance, an authenticated server checks the authorization of the caller against the handle on every call. Handles are opaque, their lifetime is stated in the description of the creation tool, and a call with an expired handle returns a tool execution error that says so.
x-mcp-header
x-mcp-header is an extension property inside the schema of a tool parameter. It tells the client to copy the value of the parameter into the HTTP header Mcp-Param-{name} when the call goes over Streamable HTTP; the value of the property is the name part of the header. Load balancers, proxies and web application firewalls can then route requests by the parameter without parsing the body. The property was introduced in version 2026-07-28 by SEP-2243, created on 4 February 2026 by the MCP Transports Working Group.
{
"name": "execute_sql",
"description": "Execute SQL in the database of the given region",
"inputSchema": {
"type": "object",
"properties": {
"region": {
"type": "string",
"description": "The region to execute the query in",
"x-mcp-header": "Region"
},
"query": { "type": "string", "description": "The SQL query to execute" }
},
"required": ["region", "query"]
}
}A call with "region": "us-west1" gets the header Mcp-Param-Region: us-west1:
POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: execute_sql
Mcp-Param-Region: us-west1The property is optional for servers, but clients must support it. Its value is not empty, follows the token syntax of HTTP field names (RFC 9110), contains no control characters and is unique within the schema, case-insensitively. It is applied only to parameters of type string, integer or boolean, and only to properties reachable from the root through a chain of properties keys, without items, composition keywords or $ref.
A client on Streamable HTTP excludes a tool that violates these constraints from the result of tools/list and should log a warning. Clients on other transports may ignore the property. A value that cannot be sent as plain ASCII is encoded in Base64 in the form =?base64?{value}?=. If the parameter is absent or null, the header is omitted.
A server that processes the body checks that the headers match the arguments. On a mismatch it returns HTTP 400 and JSON-RPC error -32020 (HeaderMismatch). Header values are visible to network intermediaries, so the specification recommends against marking passwords, keys, tokens and personal data with x-mcp-header.
List changes and caching
A server that reports changes to its list declares "tools": { "listChanged": true }. Since 2026-07-28 the notification notifications/tools/list_changed reaches only clients that have opened a subscriptions/listen stream with toolsListChanged: true:
{
"jsonrpc": "2.0",
"id": 7,
"method": "subscriptions/listen",
"params": {
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": { "name": "example-client", "version": "1.0.0" },
"io.modelcontextprotocol/clientCapabilities": {}
},
"notifications": { "toolsListChanged": true }
}
}After the notification the client requests tools/list again. The subscription mechanism is common to all primitives and is described in the article Resources and resource templates in MCP.
Each complete result of tools/list carries two caching hints. ttlMs is the time in milliseconds during which the result may be considered fresh, similar to Cache-Control: max-age in HTTP. cacheScope is public if the result contains no user data and may be shared, including by gateways, or private if it may be reused only with the same authorization, for example the same access token. A list change notification invalidates a cached list at once, even before its TTL expires. The result of tools/call carries no caching hints.
How the description affects the choice of a tool
The model does not see the code of a tool. It sees what the host puts into its context: the name, the description and the input schema. For example, the Claude API builds a special system prompt from the tool definitions passed with a request. From this text the model decides whether a tool is needed, which one, and how to fill in its arguments. An inaccurate description leads to missed calls, calls of the wrong tool or wrong arguments, even when the tool works correctly.
The documentation of the Claude API calls detailed descriptions by far the most important factor in tool performance. It recommends at least three or four sentences per description: what the tool does, when it should and should not be used, what each parameter means, and which information the tool does not return.
The article "Writing effective tools for agents" (Anthropic, 11 September 2025) adds several practices:
- Unambiguous parameter names, such as
user_idinstead ofuser. - Namespacing with common prefixes, such as
asana_searchandjira_search. According to the authors, the choice between prefixes and suffixes noticeably affects evaluation results, and the effect differs between models. - Fewer, broader tools. Tools that merely wrap existing API endpoints are described as a common error.
- Meaningful results, with readable names instead of opaque identifiers and a choice of a concise or detailed response.
- Limited response size, with pagination, filtering and truncation. Claude Code limits tool responses to 25,000 tokens by default.
- Actionable errors that say what to change in the call, instead of opaque codes or stack traces.
Instructions hidden in a description from an untrusted server also reach the model; this attack, tool poisoning, and the security requirements for tools are described in the article Security of MCP. Whether descriptions lead models to the right calls is checked by running models on sets of tasks, as described in the article Evaluating MCP servers with models. How to choose and describe tools over an existing API is covered in the article MCP server for an existing API.
MCP tool and function calling definition
A host passes MCP tools to its model through the function calling of the model API. The table compares an MCP tool definition with a client tool definition in the Claude API, as an example of such an API.
| MCP tool | Claude API client tool | |
|---|---|---|
| Who writes the definition | Author of the MCP server | Developer of the application |
| How it reaches the model | tools/list, then the host |
In each API request |
| Name | Recommended 1-128 characters: letters, digits, _, -, . |
Required pattern ^[a-zA-Z0-9_-]{1,128}$ |
| Input schema | inputSchema |
input_schema |
| Output schema | outputSchema |
Not among the documented fields |
| Behavior hints | annotations |
Not among the documented fields |
| Usage examples | No such field | input_examples |
| Who executes the call | MCP server | Application |
The two definitions work at different levels. Function calling defines how the model asks for a call. An MCP tool definition defines how a server publishes a function so that any host can find it, show it to the user and call it.
Part of the MCP definition is meant for the host rather than the model. The MCP specification does not define which of these fields a host passes to the model.
Terms
| Term | Meaning |
|---|---|
| Tool | Function that a server exposes for the model to call through the host |
| Tool annotations | Hints about the behavior of a tool, such as read-only or destructive; untrusted unless the server is trusted |
| Structured content | JSON value in structuredContent that conforms to the output schema of the tool |
| Protocol error | JSON-RPC error: unknown tool, malformed request, server failure |
| Tool execution error | Tool result with isError: true: invalid input, API failure, business logic error |
| x-mcp-header | Property in a parameter schema that copies its value into the Mcp-Param-{name} HTTP header |
| Handle | Identifier that a tool returns and later calls accept, to keep state between calls |
| Caching hints | ttlMs and cacheScope in cacheable results, such as lists: how long the result is fresh and who may reuse it |
Sources
- Tools, specification 2026-07-28(opens in a new tab)
- Schema of the specification 2026-07-28 (schema.ts), GitHub(opens in a new tab)
- Overview: JSON Schema usage,
_metaand icons, specification 2026-07-28(opens in a new tab) - Streamable HTTP, specification 2026-07-28(opens in a new tab)
- Subscriptions, specification 2026-07-28(opens in a new tab)
- Caching, specification 2026-07-28(opens in a new tab)
- Key changes, specification 2026-07-28(opens in a new tab)
- Key changes, specification 2025-11-25(opens in a new tab)
- Key changes, specification 2025-06-18(opens in a new tab)
- Key changes, specification 2025-03-26(opens in a new tab)
- SEP-1303: Input Validation Errors as Tool Execution Errors(opens in a new tab)
- SEP-2243: HTTP Header Standardization for Streamable HTTP Transport(opens in a new tab)
- Define tools, Claude API documentation, Anthropic(opens in a new tab)
- Writing effective tools for agents, Anthropic, 11 September 2025(opens in a new tab)