Building Custom Tools for the Claude Agent SDK

Create tools that call external APIs, define their input schemas with Zod, package them into in-process MCP servers, and give your agent capabilities no built-in tool provides.

4 min read
custom tools agent SDK Zod schema tool createSDKMCPServer agent API tool build tool SDK

When built-in tools are not enough

The Agent SDK's built-in tools cover a lot: file operations, web search, bash execution. But real-world agents often need to do something specific — call a proprietary API, fetch live data from a particular source, interact with a service that has no MCP server yet.

Custom tools solve this. They let you define exactly what capability the agent gets, package it in the format the SDK expects, and wire it into your agent's tool list.

Zod: the schema layer

Before writing a custom tool, you need to understand Zod. Zod is a TypeScript schema validation library. The Agent SDK uses it to define what parameters each tool accepts.

The reason Zod works so well here: AI models understand it at a deep level. Once you have written one Zod schema, you can describe the next tool to a coding agent in plain English and it will write the Zod definition correctly. The pattern is highly learnable and highly delegatable.

Install it:

bun add zod

The four parts of a custom tool

Every tool you build has four parts:

1. Name — snakecase identifier (getweather, fetchclientdata) 2. Description — what the tool does and when to use it. The agent reads this to decide whether to call the tool. 3. Zod schema — the input parameters the tool accepts 4. Handler function — the actual code that runs when the agent calls the tool

Here is a complete example — a weather tool that calls a live API:

import { query, tool, createSDKMCPServer } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";

const getWeather = tool(
  "get_weather",
  "Get the current temperature for a location using latitude and longitude coordinates.",
  z.object({
    latitude: z.number().describe("Latitude coordinate"),
    longitude: z.number().describe("Longitude coordinate"),
  }),
  async (args) => {
    const response = await fetch(
      `https://api.open-meteo.com/v1/forecast?latitude=${args.latitude}&longitude=${args.longitude}&current=temperature_2m`
    );
    const data = await response.json() as { current: { temperature_2m: number } };
    return {
      content: [
        {
          type: "text" as const,
          text: `Current temperature: ${data.current.temperature_2m}°C`,
        },
      ],
    };
  }
);

The handler returns content in the MCP tool format — an array with a type and text field. This is the convention the SDK uses because custom tools follow MCP protocol conventions.

Packaging tools into an in-process MCP server

After creating individual tools, you have to package them into an in-process MCP server before passing them to the agent. This is required by the SDK architecture.

const weatherServer = createSDKMCPServer({
  name: "weather",
  tools: [getWeather],
});

You can group multiple related tools into one server:

const clientDataServer = createSDKMCPServer({
  name: "client-data",
  tools: [getClientProfile, getEngagementHistory, getDeliverableStatus],
});

Passing the server to the agent

Add the MCP server to your query options:

for await (const message of query(messages(), {
  model: "claude-sonnet-4-5",
  mcpServers: {
    weather: weatherServer,
  },
  permissionMode: "bypassPermissions",
  dangerouslyAllowBypassPermissions: true,
})) {
  if (message.type === "assistant") {
    for (const block of message.message.content) {
      if (block.type === "text") console.log(block.text);
    }
  }
}

The agent now sees the get_weather tool in its available tool list. When the prompt asks about weather, it will call the tool, pass the coordinates it determines from context, and get back live temperature data.

> The delegation pattern: You only need to write one custom tool yourself. After that, describe the next tool to your coding agent: "I need a tool that fetches the last 5 invoices for a client ID from our billing API." It knows the Zod schema pattern. It knows the handler format. It will write the correct code. Custom tools are one of those areas where AI coding assistance multiplies your output significantly.

Building tools for operator workflows

The weather example is a teaching pattern. Here is how the same structure maps to actual fractional work:

  • CRM tool — fetch a client's account status, last touchpoint date, open opportunities
  • Reporting tool — pull weekly metrics from an analytics API and return structured data
  • Document tool — read from a specific knowledge base or client folder and return relevant excerpts
  • Notification tool — post a message to a Slack channel or send an email when a workflow completes

Each of these follows exactly the same four-part structure. Name, description, Zod schema, handler. The handler is whatever API call or data fetch is specific to your stack.

---

Author: FractionalSkill

Keep Going

Ready to Start Building?

Pick the next step that matches where you are right now.

Tutorial
Claude Code Basics

Start with the terminal basics. A hands-on, step-by-step guide to your first 10 minutes with Claude Code.

Start the Tutorial
Guide
AI-Powered Workflows

Automate your client work. Learn how to connect AI tools into workflows that handle repetitive tasks for you.

Read the Guide
Community
Join the Community

Connect with other fractional leaders building with AI. Share workflows, get feedback, and learn from operators who are ahead of you.

Apply to Join