Skip to main content
Custom tools extend the Agent SDK by letting you define your own functions that Claude can call during a conversation. Using the SDK’s in-process MCP server, you can give Claude access to databases, external APIs, domain-specific logic, or any other capability your application needs.

Quick reference

Create a custom tool

A tool is defined by four parts, passed as arguments to the tool() helper in TypeScript or the @tool decorator in Python:
  • Name: a unique identifier Claude uses to call the tool.
  • Description: what the tool does. Claude reads this to decide when to call it.
  • Input schema: the arguments Claude must provide. In TypeScript this is always a Zod schema, and the handler’s args are typed from it automatically. In Python this is a dict mapping names to types, like {"latitude": float}, which the SDK converts to JSON Schema for you. The Python decorator also accepts a full JSON Schema dict directly when you need enums, ranges, optional fields, or nested objects.
  • Handler: the async function that runs when Claude calls the tool. It receives the validated arguments and must return an object with:
    • content (required): an array of result blocks, each with a type of "text", "image", "audio", "resource", or "resource_link". See Return images and resources for non-text blocks.
    • structuredContent (optional): a JSON object holding the result as machine-readable data, returned alongside content. See Return structured data.
    • isError (optional): set to true to signal a tool failure so Claude can react to it. See Handle errors.
After defining a tool, wrap it in a server with createSdkMcpServer (TypeScript) or create_sdk_mcp_server (Python). The server runs in-process inside your application, not as a separate process.

Weather tool example

This example defines a get_temperature tool and wraps it in an MCP server. It only sets up the tool; to pass it to query and run it, see Call a custom tool below.
See the tool() TypeScript reference or the @tool Python reference for full parameter details, including JSON Schema input formats and return value structure.
To make a parameter optional: in TypeScript, add .default() to the Zod field. In Python, the dict schema treats every key as required, so leave the parameter out of the schema, mention it in the description string, and read it with args.get() in the handler. The get_precipitation_chance tool below shows both patterns.

Call a custom tool

Pass the MCP server you created to query via the mcpServers option. The key in mcpServers becomes the {server_name} segment in each tool’s fully qualified name: mcp__{server_name}__{tool_name}. List that name in allowedTools so the tool runs without a permission prompt. These snippets reuse the weatherServer from the example above to ask Claude what the weather is in a specific location.