Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/harshul786/claude-code-source/llms.txt

Use this file to discover all available pages before exploring further.

The Claude Code SDK lets you extend the agent with your own tools via the Model Context Protocol (MCP). Rather than running a separate MCP server process, you can define tools in TypeScript and register them as an in-process MCP server. The agent can call these tools just like any built-in tool — with full permission handling and streaming support.

tool()

Defines a single custom MCP tool.
function tool<Schema extends AnyZodRawShape>(
  name: string,
  description: string,
  inputSchema: Schema,
  handler: (args: InferShape<Schema>, extra: unknown) => Promise<CallToolResult>,
  extras?: {
    annotations?: ToolAnnotations
    searchHint?: string
    alwaysLoad?: boolean
  },
): SdkMcpToolDefinition<Schema>
name
string
required
Tool name as it appears to the model. Use snake_case to match MCP conventions (e.g. 'get_weather', 'run_tests').
description
string
required
Natural language description of what the tool does. The model uses this description to decide when and how to call the tool — write it clearly and include any important caveats or limitations.
inputSchema
AnyZodRawShape
required
A Zod object shape describing the tool’s input parameters. Use z.object({ ... }) fields directly (pass the shape, not the wrapped schema).
import { z } from 'zod'

// Pass the shape object, not z.object(shape)
const schema = {
  query: z.string().describe('The search query'),
  limit: z.number().optional().describe('Maximum results to return'),
}
handler
(args: InferShape<Schema>, extra: unknown) => Promise<CallToolResult>
required
Async function that executes the tool. Receives the validated input arguments typed according to the Zod schema.Return a CallToolResult from @modelcontextprotocol/sdk/types.js:
{ content: [{ type: 'text', text: 'result string' }] }
// or for errors:
{ content: [{ type: 'text', text: 'error message' }], isError: true }
extras
object
Optional metadata for the tool.
Returns: SdkMcpToolDefinition<Schema> — an opaque tool definition object to pass to createSdkMcpServer().

createSdkMcpServer()

Creates an in-process MCP server from a set of tool definitions. The server runs within the same Node.js process as the SDK consumer — no subprocess, no stdio transport, no network overhead.
If your SDK MCP tool handlers run longer than 60 seconds, set the CLAUDE_CODE_STREAM_CLOSE_TIMEOUT environment variable to a higher value.
function createSdkMcpServer(options: {
  name: string
  version?: string
  tools?: Array<SdkMcpToolDefinition<any>>
}): McpSdkServerConfigWithInstance
name
string
required
Name for the MCP server. Visible in server status displays.
version
string
Optional version string for the server (e.g. '1.0.0').
tools
Array<SdkMcpToolDefinition<any>>
Array of tool definitions created with tool(). The agent will have access to all listed tools.
Returns: McpSdkServerConfigWithInstance — a server config object to pass to the sdkMcpServers option of query() or SDKSessionOptions.

Registering tools with query()

Pass the server config in the sdkMcpServers option:
import { query, tool, createSdkMcpServer } from '@anthropic-ai/claude-code'
import { z } from 'zod'

const myTool = tool(
  'get_file_stats',
  'Returns the size and line count of a file.',
  {
    filePath: z.string().describe('Absolute path to the file'),
  },
  async ({ filePath }) => {
    const { statSync } = await import('fs')
    const { execSync } = await import('child_process')
    const stats = statSync(filePath)
    const lines = execSync(`wc -l < "${filePath}"`).toString().trim()
    return {
      content: [{ type: 'text', text: `Size: ${stats.size} bytes, Lines: ${lines}` }],
    }
  },
)

const server = createSdkMcpServer({
  name: 'my-tools',
  version: '1.0.0',
  tools: [myTool],
})

const stream = query({
  prompt: 'How many lines are in src/index.ts?',
  options: {
    cwd: '/path/to/project',
    sdkMcpServers: [server],
    model: 'claude-opus-4-5',
  },
})

for await (const message of stream) {
  if (message.type === 'result') {
    console.log(message.result)
  }
}

Complete example

The following example defines a custom database-query tool and a schema-inspection tool, registers them together, and runs an agent that can use both:
import { query, tool, createSdkMcpServer } from '@anthropic-ai/claude-code'
import { z } from 'zod'
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'

// --- Tool 1: execute a read-only SQL query ---
const sqlQueryTool = tool(
  'sql_query',
  'Executes a read-only SQL SELECT query against the application database and returns the results as JSON.',
  {
    query: z.string().describe('The SQL SELECT statement to execute'),
    limit: z
      .number()
      .int()
      .min(1)
      .max(1000)
      .optional()
      .describe('Maximum number of rows to return (default: 100)'),
  },
  async ({ query: sql, limit = 100 }): Promise<CallToolResult> => {
    // Replace with your actual database client
    try {
      const rows = await fakeDatabaseQuery(sql, limit)
      return {
        content: [{ type: 'text', text: JSON.stringify(rows, null, 2) }],
      }
    } catch (err) {
      return {
        content: [{ type: 'text', text: `Query error: ${err}` }],
        isError: true,
      }
    }
  },
  {
    annotations: { readOnly: true },
  },
)

// --- Tool 2: list available tables ---
const listTablesTool = tool(
  'list_tables',
  'Returns the names and row counts of all tables in the database.',
  {},
  async (): Promise<CallToolResult> => {
    const tables = await fakeListTables()
    return {
      content: [{ type: 'text', text: JSON.stringify(tables, null, 2) }],
    }
  },
  {
    annotations: { readOnly: true },
    alwaysLoad: true,
  },
)

// --- Create the in-process MCP server ---
const dbServer = createSdkMcpServer({
  name: 'database-tools',
  version: '1.0.0',
  tools: [sqlQueryTool, listTablesTool],
})

// --- Run the agent with the custom tools ---
async function main() {
  const stream = query({
    prompt: 'How many users signed up in the last 7 days? Show me the data.',
    options: {
      model: 'claude-opus-4-5',
      sdkMcpServers: [dbServer],
      permissionMode: 'acceptEdits',
      maxTurns: 5,
    },
  })

  for await (const message of stream) {
    if (message.type === 'assistant') {
      for (const block of message.message.content) {
        if (block.type === 'text') process.stdout.write(block.text)
      }
    }
    if (message.type === 'result') {
      console.log(`\n\nDone in ${message.num_turns} turns.`)
    }
  }
}

main().catch(console.error)

// Stubs — replace with real implementations
async function fakeDatabaseQuery(sql: string, limit: number) {
  return [{ user_id: 1, signup_date: '2026-03-25' }]
}
async function fakeListTables() {
  return [{ name: 'users', row_count: 1024 }]
}

Build docs developers (and LLMs) love