Custom Tools not working in the SDK

Status Fixed / completed
Maintainer reply ✓ Yes — dltn
Activity 7 comments · opened Sep 14, 2025 · closed Sep 24, 2025
💡 Likely answer: A maintainer (dltn, contributor) responded on this thread — see the highlighted reply below.

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Im an trying the new Custom tool feaure in the SDK but im not getting any response from the query object neither im getting any exception so Not really sure what is wrong in the code

import { query, tool, createSdkMcpServer } from "@anthropic-ai/claude-code";

import { z } from "zod";

const customServer = createSdkMcpServer({
  name: "my-custom-tools",
  version: "1.0.0",
  tools: [
    tool(
      "get_weather",
      "Get current weather for a location",
      {
        location: z.string().describe("City name or coordinates"),
        units: z
          .enum(["celsius", "fahrenheit"])
          .default("celsius")
          .describe("Temperature units"),
      },
      async (args) => {
        // Call weather API
        const response = await fetch(
          `https://api.weather.com/v1/current?q=${args.location}&units=${args.units}`
        );
        const data = await response.json();

        return {
          content: [
            {
              type: "text",
              text: `Temperature: ${data.temp}°\nConditions: ${data.conditions}\nHumidity: ${data.humidity}%`,
            },
          ],
        };
      }
    ),
  ],
});

async function* generateMessages() {
  yield {
    type: "user",
    message: {
      role: "user",
      content: "What's the weather in San Francisco?",
    },
  };
}

const messages = [];
try {
  console.log("starting query ...");
  for await (const message of query({
    prompt: generateMessages(), // Use async generator for streaming input
    options: {
      permissionMode: 'bypassPermissions',  
      mcpServers: {
        "my-custom-tools": customServer, // Pass as object/dictionary, not array
      },
      // Optionally specify which tools Claude can use
      allowedTools: [
        "mcp__my-custom-tools__get_weather", // Allow the weather tool
        // Add other tools as needed
      ],
      maxTurns: 10,
    },
  })) {
    console.log("✅ Success:", message);
  }
} catch (error) {
  console.error("❌ Error:", error.message);
}

What Should Happen?

Console.log should print something

Error Messages/Logs

Steps to Reproduce

import { query, tool, createSdkMcpServer } from "@anthropic-ai/claude-code";

import { z } from "zod";

const customServer = createSdkMcpServer({
  name: "my-custom-tools",
  version: "1.0.0",
  tools: [
    tool(
      "get_weather",
      "Get current weather for a location",
      {
        location: z.string().describe("City name or coordinates"),
        units: z
          .enum(["celsius", "fahrenheit"])
          .default("celsius")
          .describe("Temperature units"),
      },
      async (args) => {
        // Call weather API
        const response = await fetch(
          `https://api.weather.com/v1/current?q=${args.location}&units=${args.units}`
        );
        const data = await response.json();

        return {
          content: [
            {
              type: "text",
              text: `Temperature: ${data.temp}°\nConditions: ${data.conditions}\nHumidity: ${data.humidity}%`,
            },
          ],
        };
      }
    ),
  ],
});

async function* generateMessages() {
  yield {
    type: "user",
    message: {
      role: "user",
      content: "What's the weather in San Francisco?",
    },
  };
}

const messages = [];
try {
  console.log("starting query ...");
  for await (const message of query({
    prompt: generateMessages(), // Use async generator for streaming input
    options: {
      permissionMode: 'bypassPermissions',  
      mcpServers: {
        "my-custom-tools": customServer, // Pass as object/dictionary, not array
      },
      // Optionally specify which tools Claude can use
      allowedTools: [
        "mcp__my-custom-tools__get_weather", // Allow the weather tool
        // Add other tools as needed
      ],
      maxTurns: 10,
    },
  })) {
    console.log("✅ Success:", message);
  }
} catch (error) {
  console.error("❌ Error:", error.message);
}

Claude Model

None

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

1.0.113

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

_No response_

View original on GitHub ↗

7 Comments

github-actions[bot] · 11 months ago

Found 2 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/7279
  2. https://github.com/anthropics/claude-code/issues/6710

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

ajasingh · 11 months ago

same issue is observed with 1.0.115 version and also 1.0.117 , any idea when this would be resolved

v-raja · 11 months ago

Same issue with python sdk version 0.0.22. When trying to use custom tools with the sdk, I get the error:

claude_code_sdk/_internal/transport/subprocess_cli.py", line 269, in write
    |     raise CLIConnectionError("ProcessTransport is not ready for writing")
    | claude_code_sdk._errors.CLIConnectionError: ProcessTransport is not ready for writing
v-raja · 11 months ago

Works:

options = ClaudeCodeOptions(
    mcp_servers={"context-tools": context_server},
)
async with ClaudeSDKClient(options=options) as client:
    await client.query("Calculate 5 + 3 and translate 'hello' to Spanish")
    
    # Process messages
    async for message in client.receive_response():
        print(message)

Does not work (throws the CLIConnectionError above):

# Create streaming input generator (required for MCP servers)
async def message_generator():
    yield {
        "type": "user",
        "message": {
            "role": "user",
            "content": initial_prompt
        }
    }

response = query(
    prompt=message_generator(),  
    options=options
)

async for message in response:
    print(message)
dltn contributor · 11 months ago

Hi folks! Fix is ready and should be out soon in ~v1.0.121. You'll no longer need the timeout, but you will still need to provide a AsyncIterable<SDKUserMessage> streaming prompt (not a string prompt)

ajasingh · 11 months ago

Working fine with version 1.0.123 thanks very much for fixing

github-actions[bot] · 11 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.