> ## Documentation Index
> Fetch the complete documentation index at: https://langchain-5e9cc07a-preview-docssu-1780674012-e7ef61e.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Tools

Tools extend what [agents](/oss/javascript/langchain/agents) can do—letting them fetch real-time data, execute code, query external databases, and take actions in the world.

Under the hood, tools are callable functions with well-defined inputs and outputs that get passed to a [chat model](/oss/javascript/langchain/models). The model decides when to invoke a tool based on the conversation context, and what input arguments to provide.

<Tip>
  For details on how models handle tool calls, see [Tool calling](/oss/javascript/langchain/models#tool-calling). Trace tool calls and debug errors with [LangSmith](https://smith.langchain.com?utm_source=docs\&utm_medium=cta\&utm_campaign=langsmith-signup\&utm_content=oss-langchain-tools). Follow the [tracing quickstart](/langsmith/trace-with-langchain) to get set up.

  We recommend you also set up [LangSmith Engine](/langsmith/engine) which monitors your traces, detects issues, and proposes fixes.
</Tip>

## Create tools

### Basic tool definition

The simplest way to create a tool is by importing the `tool` function from the `langchain` package. You can use [zod](https://zod.dev/) to define the tool's input schema:

```ts theme={null}
import * as z from "zod"
import { tool } from "langchain"

const searchDatabase = tool(
  ({ query, limit }) => `Found ${limit} results for '${query}'`,
  {
    name: "search_database",
    description: "Search the customer database for records matching the query.",
    schema: z.object({
      query: z.string().describe("Search terms to look for"),
      limit: z.number().describe("Maximum number of results to return"),
    }),
  }
);
```

<Note>
  **Server-side tool use:** Some chat models feature built-in tools (web search, code interpreters) that are executed server-side. See [Server-side tool use](#server-side-tool-use) for details.
</Note>

<Warning>
  Prefer `snake_case` for tool names (e.g., `web_search` instead of `Web Search`). Some model providers have issues with or reject names containing spaces or special characters with errors. Sticking to alphanumeric characters, underscores, and hyphens helps to improve compatibility across providers.
</Warning>

## Access context

Tools are most powerful when they can access runtime information like conversation history, user data, and persistent memory. This section covers how to access and update this information from within your tools.

### Context

Context provides immutable configuration data that is passed at invocation time. Use it for user IDs, session details, or application-specific settings that shouldn't change during a conversation.

<Note>
  While `thread_id` (passed via `config={"configurable": {"thread_id": ...}}`) scopes the *conversation*: message history and checkpoints, `context` carries *per-run* data your tools and middleware read at invocation time. In production you typically pass both together: a stable `thread_id` per conversation, and a `context` object on every invoke.
</Note>

Tools can access an agent's runtime context through the `config` parameter. Pass `context` alongside a `thread_id` so the conversation is persisted across turns:

<CodeGroup>
  ```ts Google theme={null}
  import * as z from "zod";
  import { ChatOpenAI } from "@langchain/openai";
  import { createAgent, tool } from "langchain";

  const getUserName = tool(
    (_, config) => {
      return config.context.user_name;
    },
    {
      name: "get_user_name",
      description: "Get the user's name.",
      schema: z.object({}),
    },
  );

  const contextSchema = z.object({
    user_name: z.string(),
  });

  const agent = createAgent({
    model: new ChatOpenAI({ model: "google-genai:gemini-3.5-flash" }),
    tools: [getUserName],
    contextSchema,
  });

  const result = await agent.invoke(
    {
      messages: [{ role: "user", content: "What is my name?" }],
    },
    {
      configurable: { thread_id: crypto.randomUUID() },
      context: { user_name: "John Smith" },
    },
  );
  ```

  ```ts OpenAI theme={null}
  import * as z from "zod";
  import { ChatOpenAI } from "@langchain/openai";
  import { createAgent, tool } from "langchain";

  const getUserName = tool(
    (_, config) => {
      return config.context.user_name;
    },
    {
      name: "get_user_name",
      description: "Get the user's name.",
      schema: z.object({}),
    },
  );

  const contextSchema = z.object({
    user_name: z.string(),
  });

  const agent = createAgent({
    model: new ChatOpenAI({ model: "openai:gpt-5.4" }),
    tools: [getUserName],
    contextSchema,
  });

  const result = await agent.invoke(
    {
      messages: [{ role: "user", content: "What is my name?" }],
    },
    {
      configurable: { thread_id: crypto.randomUUID() },
      context: { user_name: "John Smith" },
    },
  );
  ```

  ```ts Anthropic theme={null}
  import * as z from "zod";
  import { ChatOpenAI } from "@langchain/openai";
  import { createAgent, tool } from "langchain";

  const getUserName = tool(
    (_, config) => {
      return config.context.user_name;
    },
    {
      name: "get_user_name",
      description: "Get the user's name.",
      schema: z.object({}),
    },
  );

  const contextSchema = z.object({
    user_name: z.string(),
  });

  const agent = createAgent({
    model: new ChatOpenAI({ model: "anthropic:claude-sonnet-4-6" }),
    tools: [getUserName],
    contextSchema,
  });

  const result = await agent.invoke(
    {
      messages: [{ role: "user", content: "What is my name?" }],
    },
    {
      configurable: { thread_id: crypto.randomUUID() },
      context: { user_name: "John Smith" },
    },
  );
  ```

  ```ts OpenRouter theme={null}
  import * as z from "zod";
  import { ChatOpenAI } from "@langchain/openai";
  import { createAgent, tool } from "langchain";

  const getUserName = tool(
    (_, config) => {
      return config.context.user_name;
    },
    {
      name: "get_user_name",
      description: "Get the user's name.",
      schema: z.object({}),
    },
  );

  const contextSchema = z.object({
    user_name: z.string(),
  });

  const agent = createAgent({
    model: new ChatOpenAI({ model: "openrouter:anthropic/claude-sonnet-4-6" }),
    tools: [getUserName],
    contextSchema,
  });

  const result = await agent.invoke(
    {
      messages: [{ role: "user", content: "What is my name?" }],
    },
    {
      configurable: { thread_id: crypto.randomUUID() },
      context: { user_name: "John Smith" },
    },
  );
  ```

  ```ts Fireworks theme={null}
  import * as z from "zod";
  import { ChatOpenAI } from "@langchain/openai";
  import { createAgent, tool } from "langchain";

  const getUserName = tool(
    (_, config) => {
      return config.context.user_name;
    },
    {
      name: "get_user_name",
      description: "Get the user's name.",
      schema: z.object({}),
    },
  );

  const contextSchema = z.object({
    user_name: z.string(),
  });

  const agent = createAgent({
    model: new ChatOpenAI({ model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b" }),
    tools: [getUserName],
    contextSchema,
  });

  const result = await agent.invoke(
    {
      messages: [{ role: "user", content: "What is my name?" }],
    },
    {
      configurable: { thread_id: crypto.randomUUID() },
      context: { user_name: "John Smith" },
    },
  );
  ```

  ```ts Baseten theme={null}
  import * as z from "zod";
  import { ChatOpenAI } from "@langchain/openai";
  import { createAgent, tool } from "langchain";

  const getUserName = tool(
    (_, config) => {
      return config.context.user_name;
    },
    {
      name: "get_user_name",
      description: "Get the user's name.",
      schema: z.object({}),
    },
  );

  const contextSchema = z.object({
    user_name: z.string(),
  });

  const agent = createAgent({
    model: new ChatOpenAI({ model: "baseten:zai-org/GLM-5" }),
    tools: [getUserName],
    contextSchema,
  });

  const result = await agent.invoke(
    {
      messages: [{ role: "user", content: "What is my name?" }],
    },
    {
      configurable: { thread_id: crypto.randomUUID() },
      context: { user_name: "John Smith" },
    },
  );
  ```

  ```ts Ollama theme={null}
  import * as z from "zod";
  import { ChatOpenAI } from "@langchain/openai";
  import { createAgent, tool } from "langchain";

  const getUserName = tool(
    (_, config) => {
      return config.context.user_name;
    },
    {
      name: "get_user_name",
      description: "Get the user's name.",
      schema: z.object({}),
    },
  );

  const contextSchema = z.object({
    user_name: z.string(),
  });

  const agent = createAgent({
    model: new ChatOpenAI({ model: "ollama:devstral-2" }),
    tools: [getUserName],
    contextSchema,
  });

  const result = await agent.invoke(
    {
      messages: [{ role: "user", content: "What is my name?" }],
    },
    {
      configurable: { thread_id: crypto.randomUUID() },
      context: { user_name: "John Smith" },
    },
  );
  ```
</CodeGroup>

### Long-term memory (Store)

The [`BaseStore`](https://reference.langchain.com/javascript/langchain-core/stores/BaseStore) provides persistent storage that survives across conversations. Unlike state (short-term memory), data saved to the store remains available in future sessions.

Access the store through `config.store`. The store uses a namespace/key pattern to organize data:

```ts expandable theme={null}
import * as z from "zod";
import { createAgent, tool } from "langchain";
import { InMemoryStore } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";

const store = new InMemoryStore();

// Access memory
const getUserInfo = tool(
  async ({ user_id }) => {
    const value = await store.get(["users"], user_id);
    console.log("get_user_info", user_id, value);
    return value;
  },
  {
    name: "get_user_info",
    description: "Look up user info.",
    schema: z.object({
      user_id: z.string(),
    }),
  }
);

// Update memory
const saveUserInfo = tool(
  async ({ user_id, name, age, email }) => {
    console.log("save_user_info", user_id, name, age, email);
    await store.put(["users"], user_id, { name, age, email });
    return "Successfully saved user info.";
  },
  {
    name: "save_user_info",
    description: "Save user info.",
    schema: z.object({
      user_id: z.string(),
      name: z.string(),
      age: z.number(),
      email: z.string(),
    }),
  }
);

const agent = createAgent({
  model: new ChatOpenAI({ model: "gpt-5.4" }),
  tools: [getUserInfo, saveUserInfo],
  store,
});

// First session: save user info
await agent.invoke({
  messages: [
    {
      role: "user",
      content: "Save the following user: userid: abc123, name: Foo, age: 25, email: foo@langchain.dev",
    },
  ],
});

// Second session: get user info
const result = await agent.invoke({
  messages: [
    { role: "user", content: "Get user info for user with id 'abc123'" },
  ],
});

console.log(result);
// Here is the user info for user with ID "abc123":
// - Name: Foo
// - Age: 25
// - Email: foo@langchain.dev
```

### Stream writer

Stream real-time updates from tools during execution. This is useful for providing progress feedback to users during long-running operations.

Use `config.writer` to emit custom updates:

```ts theme={null}
import * as z from "zod";
import { tool, ToolRuntime } from "langchain";

const getWeather = tool(
  ({ city }, config: ToolRuntime) => {
    const writer = config.writer;

    // Stream custom updates as the tool executes
    if (writer) {
      writer(`Looking up data for city: ${city}`);
      writer(`Acquired data for city: ${city}`);
    }

    return `It's always sunny in ${city}!`;
  },
  {
    name: "get_weather",
    description: "Get weather for a given city.",
    schema: z.object({
      city: z.string(),
    }),
  }
);
```

### Execution info

Access thread ID, run ID, and retry state from within a tool via `runtime.execution_info`:

```ts theme={null}
import { tool } from "langchain";
import * as z from "zod";

const logExecutionContext = tool(
  async (_input, runtime) => {
    const info = runtime.executionInfo;
    console.log(`Thread: ${info.threadId}, Run: ${info.runId}`);  // [!code highlight]
    console.log(`Attempt: ${info.nodeAttempt}`);
    return "done";
  },
  {
    name: "log_execution_context",
    description: "Log execution identity information.",
    schema: z.object({}),
  }
);
```

<Note>
  Requires `deepagents>=1.9.0` (or `@langchain/langgraph>=1.2.8`).
</Note>

### Server info

When your tool runs on LangGraph Server, access the assistant ID, graph ID, and authenticated user via `runtime.server_info`:

```ts theme={null}
import { tool } from "langchain";
import * as z from "zod";

const getAssistantScopedData = tool(
  async (_input, runtime) => {
    const server = runtime.serverInfo;
    if (server != null) {
      console.log(`Assistant: ${server.assistantId}, Graph: ${server.graphId}`);  // [!code highlight]
      if (server.user != null) {
        console.log(`User: ${server.user.identity}`);  // [!code highlight]
      }
    }
    return "done";
  },
  {
    name: "get_assistant_scoped_data",
    description: "Fetch data scoped to the current assistant.",
    schema: z.object({}),
  }
);
```

`serverInfo` is `null` when the tool is not running on LangGraph Server.

<Note>
  Requires `deepagents>=1.9.0` (or `@langchain/langgraph>=1.2.8`).
</Note>

## Tool execution

In LangChain, tools are used by agents (for example via [`create_agent`](https://reference.langchain.com/javascript/langchain/index/createAgent)) and tool error handling is configured through [middleware](/oss/javascript/langchain/middleware).

For LangGraph workflows, tool execution is handled by [`ToolNode`](https://reference.langchain.com/javascript/langchain-langgraph/prebuilt/ToolNode). See [ToolNode](/oss/javascript/langgraph/workflows-agents#toolnode).

### Tool return values

You can choose different return values for your tools:

* Return a `string` for human-readable results.
* Return an `object` for structured results the model should parse.
* Return a `Command` with optional message when you need to write to state.

#### Return a string

Return a string when the tool should provide plain text for the model to read and use in its next response.

```ts theme={null}
import { tool } from "langchain";
import * as z from "zod";

const getWeather = tool(({ city }) => `It is currently sunny in ${city}.`, {
  name: "get_weather",
  description: "Get weather for a city.",
  schema: z.object({ city: z.string() }),
});
```

Behavior:

* The return value is converted to a `ToolMessage`.
* The model sees that text and decides what to do next.
* No agent state fields are changed unless the model or another tool does so later.

Use this when the result is naturally human-readable text.

#### Return an object

Return an object (for example, a `dict`) when your tool produces structured data that the model should inspect.

```ts theme={null}
import { tool } from "langchain";
import * as z from "zod";

const getWeatherData = tool(
  ({ city }) => ({
    city,
    temperature_c: 22,
    conditions: "sunny",
  }),
  {
    name: "get_weather_data",
    description: "Get structured weather data for a city.",
    schema: z.object({ city: z.string() }),
  },
);
```

Behavior:

* The object is serialized and sent back as tool output.
* The model can read specific fields and reason over them.
* Like string returns, this does not directly update graph state.

Use this when downstream reasoning benefits from explicit fields instead of free-form text.

#### Return a Command

Return a [`Command`](https://reference.langchain.com/javascript/langchain-langgraph/index/Command) when the tool needs to update graph state (for example, setting user preferences or app state).
You can return a `Command` with or without including a `ToolMessage`.
If the model needs to see that the tool succeeded (for example, to confirm a preference change), include a `ToolMessage` in the update, using `runtime.tool_call_id` for the `tool_call_id` parameter.

```ts theme={null}
import { tool, ToolMessage, type ToolRuntime } from "langchain";
import { Command } from "@langchain/langgraph";
import * as z from "zod";

const setLanguage = tool(
  async ({ language }, config: ToolRuntime) => {
    return new Command({
      update: {
        preferredLanguage: language,
        messages: [
          new ToolMessage({
            content: `Language set to ${language}.`,
            tool_call_id: config.toolCallId,
          }),
        ],
      },
    });
  },
  {
    name: "set_language",
    description: "Set the preferred response language.",
    schema: z.object({ language: z.string() }),
  },
);
```

Behavior:

* The command updates state using `update`.
* Updated state is available to subsequent steps in the same run.
* Use reducers for fields that may be updated by parallel tool calls.

Use this when the tool is not just returning data, but also mutating agent state.

### Error handling

Handle tool errors using LangChain agent [middleware](/oss/javascript/langchain/middleware) to retry failed tool calls or return custom error messages:

<CodeGroup>
  ```ts Google theme={null}
  import { createAgent, createMiddleware, ToolMessage } from "langchain";

  const handleToolErrors = createMiddleware({
    name: "HandleToolErrors",
    wrapToolCall: async (request, handler) => {
      try {
        return await handler(request);
      } catch (error) {
        return new ToolMessage({
          content: `Tool error: Please check your input and try again. (${error})`,
          tool_call_id: request.toolCall.id!,
        });
      }
    },
  });

  const agent = createAgent({
    model: "google-genai:gemini-3.5-flash",
    tools: [],
    middleware: [handleToolErrors],
  });
  ```

  ```ts OpenAI theme={null}
  import { createAgent, createMiddleware, ToolMessage } from "langchain";

  const handleToolErrors = createMiddleware({
    name: "HandleToolErrors",
    wrapToolCall: async (request, handler) => {
      try {
        return await handler(request);
      } catch (error) {
        return new ToolMessage({
          content: `Tool error: Please check your input and try again. (${error})`,
          tool_call_id: request.toolCall.id!,
        });
      }
    },
  });

  const agent = createAgent({
    model: "openai:gpt-5.4",
    tools: [],
    middleware: [handleToolErrors],
  });
  ```

  ```ts Anthropic theme={null}
  import { createAgent, createMiddleware, ToolMessage } from "langchain";

  const handleToolErrors = createMiddleware({
    name: "HandleToolErrors",
    wrapToolCall: async (request, handler) => {
      try {
        return await handler(request);
      } catch (error) {
        return new ToolMessage({
          content: `Tool error: Please check your input and try again. (${error})`,
          tool_call_id: request.toolCall.id!,
        });
      }
    },
  });

  const agent = createAgent({
    model: "anthropic:claude-sonnet-4-6",
    tools: [],
    middleware: [handleToolErrors],
  });
  ```

  ```ts OpenRouter theme={null}
  import { createAgent, createMiddleware, ToolMessage } from "langchain";

  const handleToolErrors = createMiddleware({
    name: "HandleToolErrors",
    wrapToolCall: async (request, handler) => {
      try {
        return await handler(request);
      } catch (error) {
        return new ToolMessage({
          content: `Tool error: Please check your input and try again. (${error})`,
          tool_call_id: request.toolCall.id!,
        });
      }
    },
  });

  const agent = createAgent({
    model: "openrouter:anthropic/claude-sonnet-4-6",
    tools: [],
    middleware: [handleToolErrors],
  });
  ```

  ```ts Fireworks theme={null}
  import { createAgent, createMiddleware, ToolMessage } from "langchain";

  const handleToolErrors = createMiddleware({
    name: "HandleToolErrors",
    wrapToolCall: async (request, handler) => {
      try {
        return await handler(request);
      } catch (error) {
        return new ToolMessage({
          content: `Tool error: Please check your input and try again. (${error})`,
          tool_call_id: request.toolCall.id!,
        });
      }
    },
  });

  const agent = createAgent({
    model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b",
    tools: [],
    middleware: [handleToolErrors],
  });
  ```

  ```ts Baseten theme={null}
  import { createAgent, createMiddleware, ToolMessage } from "langchain";

  const handleToolErrors = createMiddleware({
    name: "HandleToolErrors",
    wrapToolCall: async (request, handler) => {
      try {
        return await handler(request);
      } catch (error) {
        return new ToolMessage({
          content: `Tool error: Please check your input and try again. (${error})`,
          tool_call_id: request.toolCall.id!,
        });
      }
    },
  });

  const agent = createAgent({
    model: "baseten:zai-org/GLM-5",
    tools: [],
    middleware: [handleToolErrors],
  });
  ```

  ```ts Ollama theme={null}
  import { createAgent, createMiddleware, ToolMessage } from "langchain";

  const handleToolErrors = createMiddleware({
    name: "HandleToolErrors",
    wrapToolCall: async (request, handler) => {
      try {
        return await handler(request);
      } catch (error) {
        return new ToolMessage({
          content: `Tool error: Please check your input and try again. (${error})`,
          tool_call_id: request.toolCall.id!,
        });
      }
    },
  });

  const agent = createAgent({
    model: "ollama:devstral-2",
    tools: [],
    middleware: [handleToolErrors],
  });
  ```
</CodeGroup>

### State injection

Tools access graph state through [`ToolRuntime`](https://reference.langchain.com/javascript/langchain/index/Runtime). See [Access context](#access-context) for state, context, store, and streaming APIs.

## Dynamic tool selection

With dynamic tools, the set of tools available to the agent is modified at runtime rather than defined all upfront. Not every tool is appropriate for every situation. Too many tools may overwhelm the model (overload context) and increase errors; too few limit capabilities. Dynamic tool selection enables adapting the available toolset based on authentication state, user permissions, feature flags, or conversation stage.

There are two approaches depending on whether tools are known ahead of time:

<Tabs>
  <Tab title="Filtering pre-registered tools">
    When all possible tools are known at agent creation time, you can pre-register them and dynamically filter which ones are exposed to the model based on state, permissions, or context.

    <Tabs>
      <Tab title="State">
        Enable advanced tools only after certain conversation milestones:

        ```typescript theme={null}
        import { createMiddleware, tool } from "langchain";
        import { createDeepAgent } from "deepagents";

        const stateBasedTools = createMiddleware({
            name: "StateBasedTools",
            wrapModelCall: (request, handler) => {
                // Read from State: check authentication and conversation length
                const state = request.state as typeof request.state & {
                    authenticated?: boolean;
                };
                const isAuthenticated = state.authenticated ?? false;
                const messageCount = state.messages.length;

                let filteredTools = request.tools;

                // Only enable sensitive tools after authentication
                if (!isAuthenticated) {
                    filteredTools = request.tools.filter(
                        (t: any) => typeof t.name === "string" && t.name.startsWith("public_"),
                    );
                } else if (messageCount < 5) {
                    filteredTools = request.tools.filter(
                        (t: any) => typeof t.name === "string" && t.name !== "advanced_search",
                    );
                }

                return handler({ ...request, tools: filteredTools });
            },
        });

        const agent = await createDeepAgent({
            model: "claude-sonnet-4-20250514",
            tools: tools,
            middleware: [stateBasedTools] as any,
        });
        ```
      </Tab>

      <Tab title="Store">
        Filter tools based on user preferences or feature flags in Store:

        ```typescript theme={null}
        import { createMiddleware } from "langchain";
        import { createDeepAgent, StoreBackend } from "deepagents";
        import * as z from "zod";
        import { InMemoryStore } from "@langchain/langgraph";

        const contextSchema = z.object({
          userId: z.string(),
        });

        const storeBasedTools = createMiddleware({
          name: "StoreBasedTools",
          contextSchema,
          wrapModelCall: async (request, handler) => {
            const userId =
              (request.runtime?.context as { userId?: string } | undefined)?.userId ??
                "user-123";

            // Read from Store: get user's enabled features
            const runtimeStore = request.runtime?.store as InMemoryStore | undefined;
            const rawFlags = (await runtimeStore?.get(
              ["features"],
              userId as string,
            )) as unknown;
            const featureFlags = rawFlags as FeatureFlags | undefined;

            let filteredTools = request.tools;

            if (featureFlags) {
              const enabledFeatures = featureFlags.enabledTools || [];
              filteredTools = request.tools.filter((t) =>
                enabledFeatures.includes(t.name as string)
              );
            }

            return handler({ ...request, tools: filteredTools });
          },
        });

        const agent = await createDeepAgent({
          model: "claude-sonnet-4-20250514",
          backend: new StoreBackend(),
          store,
          checkpointer,
          tools,
          middleware: [storeBasedTools] as any,
        });
        ```
      </Tab>

      <Tab title="Runtime Context">
        Filter tools based on user permissions from Runtime Context:

        ```typescript theme={null}
        import * as z from "zod";
        import { createMiddleware } from "langchain";
        import { createDeepAgent } from "deepagents";

        const contextSchema = z.object({
          userRole: z.string(),
        });

        const contextBasedTools = createMiddleware({
          name: "ContextBasedTools",
          contextSchema,
          wrapModelCall: (request, handler) => {
            // Read from Runtime Context: get user role
            const userRole = request.runtime.context.userRole;

            let filteredTools = request.tools;

            if (userRole === "admin") {
              // Admins get all tools
            } else if (userRole === "editor") {
              filteredTools = request.tools.filter((t) => t.name !== "delete_data");
            } else {
              filteredTools = request.tools.filter(
                (t) => (t.name as string).startsWith("read_"),
              );
            }

            return handler({ ...request, tools: filteredTools });
          },
        });

        const agent = await createDeepAgent({
          model: "claude-sonnet-4-20250514",
          store,
          checkpointer,
          tools,
          middleware: [contextBasedTools] as any,
        });
        ```
      </Tab>
    </Tabs>

    This approach is best when:

    * All possible tools are known at compile/startup time
    * You want to filter based on permissions, feature flags, or conversation state
    * Tools are static but their availability is dynamic

    See [Dynamically selecting tools](/oss/javascript/langchain/middleware/custom#dynamically-selecting-tools) for more examples.
  </Tab>

  <Tab title="Runtime tool registration">
    When tools are discovered or created at runtime (e.g., loaded from an MCP server, generated based on user data, or fetched from a remote registry), you need to both register the tools and handle their execution dynamically.

    This requires two middleware hooks:

    1. `wrap_model_call` - Add the dynamic tools to the request
    2. `wrap_tool_call` - Handle execution of the dynamically added tools

    ```typescript theme={null}
    import { createAgent, createMiddleware, tool } from "langchain";
    import * as z from "zod";

    // A tool that will be added dynamically at runtime
    const calculateTip = tool(
      ({ billAmount, tipPercentage = 20 }) => {
        const tip = billAmount * (tipPercentage / 100);
        return `Tip: $${tip.toFixed(2)}, Total: $${(billAmount + tip).toFixed(2)}`;
      },
      {
        name: "calculate_tip",
        description: "Calculate the tip amount for a bill",
        schema: z.object({
          billAmount: z.number().describe("The bill amount"),
          tipPercentage: z.number().default(20).describe("Tip percentage"),
        }),
      }
    );

    const dynamicToolMiddleware = createMiddleware({
      name: "DynamicToolMiddleware",
      wrapModelCall: (request, handler) => {
        // Add dynamic tool to the request
        // This could be loaded from an MCP server, database, etc.
        return handler({
          ...request,
          tools: [...request.tools, calculateTip],
        });
      },
      wrapToolCall: (request, handler) => {
        // Handle execution of the dynamic tool
        if (request.toolCall.name === "calculate_tip") {
          return handler({ ...request, tool: calculateTip });
        }
        return handler(request);
      },
    });

    const agent = createAgent({
      model: "gpt-4o",
      tools: [getWeather], // Only static tools registered here
      middleware: [dynamicToolMiddleware],
    });

    // The agent can now use both getWeather AND calculateTip
    const result = await agent.invoke({
      messages: [{ role: "user", content: "Calculate a 20% tip on $85" }],
    });
    ```

    This approach is best when:

    * Tools are discovered at runtime (e.g., from an MCP server)
    * Tools are generated dynamically based on user data or configuration
    * You're integrating with external tool registries

    <Note>
      The `wrap_tool_call` hook is required for runtime-registered tools because the agent needs to know how to execute tools that weren't in the original tool list. Without it, the agent won't know how to invoke the dynamically added tool.
    </Note>
  </Tab>
</Tabs>

## Prebuilt tools

LangChain provides a large collection of prebuilt tools and toolkits for common tasks like web search, code interpretation, database access, and more. These ready-to-use tools can be directly integrated into your agents without writing custom code.

See the [tools and toolkits](/oss/javascript/integrations/tools) integration page for a complete list of available tools organized by category.

## Server-side tool use

Some chat models feature built-in tools that are executed server-side by the model provider. These include capabilities like web search and code interpreters that don't require you to define or host the tool logic.

Refer to the individual [chat model integration pages](/oss/javascript/integrations/providers) and the [tool calling documentation](/oss/javascript/langchain/models#server-side-tool-use) for details on enabling and using these built-in tools.

***

<div className="source-links">
  <Callout icon="terminal-2">
    [Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers.
  </Callout>

  <Callout icon="edit">
    [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/oss/langchain/tools.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
  </Callout>
</div>
