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.
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 >
Tool name as it appears to the model. Use snake_case to match MCP conventions (e.g. 'get_weather', 'run_tests').
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.
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 }
Optional metadata for the tool. MCP tool annotations controlling behavior hints. Fields include readOnly, destructive, and openWorld.
Hint used by the tool-discovery system when deciding whether to load this tool into context.
When true, this tool is always included in the model’s context regardless of the tool-discovery system’s decisions.
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 for the MCP server. Visible in server status displays.
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.
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\n Done 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 }]
}