Back to writings

Claude Tool Use API: Building Custom Function Libraries for Enterprise AI

10 min read

Tool access is one of the highest-leverage primitives you can give an agent. On benchmarks like LAB-Bench FigQA and SWE-bench, adding even basic tools produces outsized capability gains, often surpassing human expert baselines.

But here's what most teams get wrong: they treat tool use like a feature, not a foundation. I've built agents that run in production across finance, operations, and customer success. The ones that work share one thing—they're built on solid tool architecture from day one.

What Tool Use Actually Is

Tool use lets Claude call functions you define or that Anthropic provides. Claude decides when to call a tool based on the user's request and the tool's description, then returns a structured call that your application executes.

This isn't magic. LLMs don't directly call functions themselves. Instead, when they determine that a request requires a tool, they generate a structured schema specifying the appropriate tool and the necessary parameters.

Here's the loop:

  1. You define tools as JSON schemas with clear descriptions
  2. Claude analyzes the user request and decides if a tool helps
  3. Claude returns a structured request to call that tool
  4. Your code executes the function and returns the result
  5. Claude incorporates the result and continues reasoning

That's it. The power comes from getting steps 1 and 5 right.

Designing Tool Schemas That Actually Work

The quality of your tool descriptions directly determines whether Claude calls the right tool with the right arguments. Vague descriptions cause tool misuse; specific descriptions with examples cause reliable, predictable behavior.

I've seen teams ship tools with one-liner descriptions. "Get user data." "Query database." Then they wonder why Claude calls the wrong tool or passes malformed arguments.

Here's what a production-grade tool schema looks like:

{
  name: "query_customer_database",
  description: "Query the customer database for user information. Use this when you need to retrieve customer records, transaction history, or account details. Do NOT use for product inventory or pricing queries.",
  input_schema: {
    type: "object",
    properties: {
      customer_id: {
        type: "string",
        description: "The unique customer ID (format: CUST-XXXXX). Required for lookups."
      },
      query_type: {
        type: "enum",
        enum: ["profile", "transactions", "account_status"],
        description: "What data to retrieve. 'profile' returns name, email, phone. 'transactions' returns last 30 days. 'account_status' returns subscription and billing info."
      },
      date_range: {
        type: "object",
        description: "Optional. Only used with query_type: 'transactions'. Limits results to specific date range.",
        properties: {
          start_date: { type: "string", description: "ISO 8601 format (YYYY-MM-DD)" },
          end_date: { type: "string", description: "ISO 8601 format (YYYY-MM-DD)" }
        }
      }
    },
    required: ["customer_id", "query_type"]
  }
}

Notice what's here:

  • Negative guidance — "Do NOT use for..." prevents misuse
  • Specific enum values — No ambiguity about what query_type accepts
  • Nested schemas — Optional parameters are clearly marked and scoped
  • Format specifications — ISO 8601 dates, ID patterns, all documented

Add strict: true to your tool definitions to ensure Claude's tool calls always match your schema exactly. This is non-negotiable for enterprise systems.

The Tool Use Loop in Production

Tool use is not a single API call—it's a loop. Many teams miss this. They make one call, get a tool_use response, and assume they're done.

Here's the actual pattern:

async function runAgent(userMessage: string): Promise<string> {
  const messages: Anthropic.MessageParam[] = [
    { role: "user", content: userMessage }
  ];

  while (true) {
    const response = await client.messages.create({
      model: "claude-opus-4-6",
      max_tokens: 4096,
      tools: toolDefinitions,
      messages
    });

    // Add Claude's response to history
    messages.push({ role: "assistant", content: response.content });

    // If Claude is done, return the final response
    if (response.stop_reason === "end_turn") {
      const textBlock = response.content.find(b => b.type === "text");
      return textBlock?.text || "No response generated";
    }

    // If Claude wants to use tools, process them
    if (response.stop_reason === "tool_use") {
      const toolResults: Anthropic.ToolResultBlockParam[] = [];

      for (const block of response.content) {
        if (block.type === "tool_use") {
          const result = await executeTool(block.name, block.input);
          toolResults.push({
            type: "tool_result",
            tool_use_id: block.id,
            content: JSON.stringify(result)
          });
        }
      }

      // Send tool results back to Claude
      messages.push({
        role: "user",
        content: toolResults
      });
    }
  }
}

The key insight: Claude sees the full conversation history, including tool calls and results. This is why context matters so much. For a deeper dive on implementing this pattern at scale, see Building Production-Ready AI Agents with Claude's Tool Use: A Complete Implementation Guide.

Secure Tool Execution Environments

Production tool use needs guardrails. You're giving Claude the ability to call real functions in your system. That power needs boundaries.

Use identity and access management (IAM) systems with authentication and authorization protocols to manage AI agents. AI agents accessing enterprise systems and data should have the same rigorous access controls as human users—and in some cases, more stringent controls given their autonomous capabilities.

Here's what I implement:

1. Input Validation

function validateToolInput(toolName: string, input: unknown): boolean {
  // Validate against schema
  const schema = toolSchemas[toolName];
  if (!schema) throw new Error(`Unknown tool: ${toolName}`);

  // Check for injection attempts
  if (typeof input === "string" && containsSuspiciousPatterns(input)) {
    return false;
  }

  // Validate field types and constraints
  return validateAgainstSchema(input, schema);
}

2. Execution Isolation

async function executeTool(
  toolName: string,
  input: unknown,
  context: ExecutionContext
): Promise<ToolResult> {
  // Validate first
  if (!validateToolInput(toolName, input)) {
    return { error: "Invalid input parameters" };
  }

  // Check permissions
  if (!context.user.hasPermission(toolName)) {
    return { error: "Insufficient permissions for this tool" };
  }

  // Log the execution
  auditLog.record({
    tool: toolName,
    user: context.user.id,
    timestamp: new Date(),
    input: sanitizeForLogging(input)
  });

  // Execute with timeout
  return executeWithTimeout(
    () => toolHandlers[toolName](input, context),
    30000 // 30 second timeout
  );
}

3. Output Filtering

Never return raw database results or sensitive fields to Claude. Always sanitize:

function sanitizeToolResult(toolName: string, result: unknown): unknown {
  if (toolName === "query_users") {
    const user = result as any;
    return {
      id: user.id,
      name: user.name,
      email: user.email,
      // Exclude passwordHash, apiKeys, etc.
    };
  }
  return result;
}

Optimizing Tool Selection

The most common tool use patterns in production: database lookup, API orchestration, code execution, and document retrieval. When you have 10 or more tools available, Claude needs help picking the right one. Tool descriptions matter, but so does organization.

Group related tools:

const tools = [
  // Customer data tools
  {
    name: "customer_lookup",
    description: "Find customer by ID or email"
  },
  {
    name: "customer_history",
    description: "Get customer transaction history"
  },

  // Order tools
  {
    name: "order_status",
    description: "Check order status and tracking"
  },
  {
    name: "order_modify",
    description: "Modify order (cancel, change shipping, etc.)"
  },

  // Inventory tools
  {
    name: "check_inventory",
    description: "Check product stock levels"
  }
];

Use system prompts to guide tool selection:

const systemPrompt = `You are a customer service agent. 
When a customer asks about their order, use order_status first. 
Only use customer_lookup if you need their account information.
Never use order_modify without explicit customer confirmation.`;

Claude's training includes extensive exposure to code, making it effective at reasoning through and chaining function calls. When tools are presented as callable functions within a code execution environment, Claude can leverage this strength to reason naturally about tool composition, chain operations and handle dependencies, and process large results efficiently.

Building a Custom Function Library

For enterprise systems, I recommend building a versioned function library. This becomes your contract between Claude and your backend.

// tools/library.ts
export const toolLibrary = {
  version: "1.0.0",
  tools: [
    {
      name: "query_database",
      version: "1.0",
      description: "...",
      input_schema: { ... }
    },
    {
      name: "call_api",
      version: "2.1",
      description: "...",
      input_schema: { ... }
    }
  ],
  handlers: {
    query_database: queryDatabaseHandler,
    call_api: callApiHandler
  }
};

This approach lets you:

  1. Version tools independently
  2. Deprecate old versions gracefully
  3. Test tool changes before deployment
  4. Share the same library across multiple agents

Connecting to Your Data

To build production-grade AI, you need three core capabilities working in concert: function calling (letting models invoke real tools and APIs), Model Context Protocol (a standardized interface for tool integration), and knowledge graphs (structured semantic memory that enhances retrieval).

For most enterprises, tool use connects to three data layers:

  1. Live operational data — Databases, APIs, real-time systems
  2. Retrieval augmented generation (RAG) — Vector databases, document stores
  3. Knowledge graphs — Structured relationships and context

Check out Enterprise AI Integration Patterns: Lessons from Real-World Anthropic Claude Deployments for a deep dive on integrating these layers with your tool architecture.

Common Patterns That Work

I've found these patterns consistently succeed in production:

1. Multi-step workflows

Claude chains tool calls naturally. A support agent might: lookup customer → check order → query inventory → generate response.

2. Parallel tool calls

Claude can request multiple tool calls in a single response. Use this for speed when tools are independent.

3. Error handling with fallbacks

async function executeTool(name: string, input: unknown) {
  try {
    return await toolHandlers[name](input);
  } catch (error) {
    // Return structured error, not exception
    return {
      error: true,
      message: "Tool execution failed",
      retry: true // Signal Claude to try again
    };
  }
}

4. Human-in-the-loop for risky operations

For operations that modify data, require explicit confirmation:

{
  name: "delete_customer_record",
  description: "Permanently delete a customer record. Requires explicit user confirmation before execution.",
  input_schema: { ... }
}

Then in your handler, check for confirmation before actually deleting.

Governance and Monitoring

Keep logs of all actions, decisions, and interactions made by AI agents. These audit trails are useful for compliance requirements and troubleshooting, performance optimization, and providing important records of inspection for regulatory reviews.

For enterprise deployments, I track:

  1. Every tool invocation with timestamp, user, and parameters
  2. Tool execution time and success/failure
  3. Which tools Claude chose (and why, when possible)
  4. Rate limiting and quota usage
  5. Tool schema versions in use

This data becomes invaluable for optimization and compliance. For more on scaling governance across your organization, see Building Production-Ready AI Agents with Claude: From Prototype to Enterprise Deployment.

Real-World Example: Document Processing Agent

Here's how these patterns come together. A document processing agent for a financial services firm:

const tools = [
  {
    name: "extract_document_text",
    description: "Extract text from uploaded PDF or image document",
    input_schema: {
      type: "object",
      properties: {
        document_id: { type: "string" },
        extract_tables: { type: "boolean" }
      },
      required: ["document_id"]
    }
  },
  {
    name: "query_regulatory_database",
    description: "Check if document matches known regulatory requirements",
    input_schema: {
      type: "object",
      properties: {
        regulation_type: { type: "enum", enum: ["sox", "gdpr", "hipaa"] },
        document_content: { type: "string" }
      },
      required: ["regulation_type", "document_content"]
    }
  },
  {
    name: "store_classification",
    description: "Store document classification and metadata for audit trail",
    input_schema: {
      type: "object",
      properties: {
        document_id: { type: "string" },
        classification: { type: "string" },
        confidence: { type: "number" }
      },
      required: ["document_id", "classification"]
    }
  }
];

The agent:

  1. Extracts text from uploaded document
  2. Queries regulatory requirements
  3. Stores results with full audit trail
  4. Returns structured compliance report

All without touching your core systems directly. All with full governance and traceability.

Moving Forward

The teams that succeed with enterprise AI at scale treat these systems with the same engineering rigor applied to any distributed system: defined interfaces, versioned schemas, layered security controls, and instrumentation at every boundary.

Tool use is where Claude becomes an actual agent instead of a chatbot. Build it right from the start. Invest in schema design, security boundaries, and monitoring. The complexity pays off.

The tools you build today become the foundation for every agent you ship tomorrow. Make them count.


Ready to implement Claude's tool use API? Start by auditing your existing tools and APIs. What data does Claude actually need to access? What operations should it perform? Build your schemas with that clarity, and the rest follows.

Get in touch if you're building enterprise AI and want to discuss your tool architecture.