Open-Source Wikis

/

LangChain

/

Primitives

/

Tools

langchain-ai/langchain

Tools

The BaseTool abstraction lets a chat model invoke arbitrary code. Source: libs/core/langchain_core/tools/.

Purpose

A "tool" is a callable wrapped with:

  • a name (what the model uses to invoke it)
  • a description (what the model reads to decide when to use it)
  • an args schema (a Pydantic model or JSON Schema that the model fills)
  • an output schema (optional)
  • sync and async execution functions

The model's tool-calling endpoint receives the schemas; the runtime maps a tool call back to the right callable and runs it. BaseChatModel.bind_tools(tools) is how tools attach to a model.

Directory layout

libs/core/langchain_core/tools/
├── __init__.py
├── base.py                 # BaseTool (~55 KB), the @tool decorator helpers
├── convert.py              # tool() decorator, convert_runnable_to_tool, JSON-schema conversion
├── render.py               # render_text_description (for prompt rendering)
├── retriever.py            # create_retriever_tool
├── simple.py               # Tool (the simplest concrete subclass)
└── structured.py           # StructuredTool

Key abstractions

Symbol File Description
BaseTool libs/core/langchain_core/tools/base.py Abstract base; defines name, description, args_schema, _run, _arun, invoke, ainvoke
Tool libs/core/langchain_core/tools/simple.py Simplest subclass — wraps a single function with a string args schema
StructuredTool libs/core/langchain_core/tools/structured.py Tool with an arbitrary Pydantic args schema
@tool (decorator) libs/core/langchain_core/tools/convert.py Decorate a Python function and get a BaseTool automatically
convert_runnable_to_tool libs/core/langchain_core/tools/convert.py Wrap any Runnable as a BaseTool
create_retriever_tool libs/core/langchain_core/tools/retriever.py Make a BaseRetriever callable as a tool
render_text_description libs/core/langchain_core/tools/render.py Render a list of tools as text for prompt-based agents
ToolException libs/core/langchain_core/tools/base.py Raise to signal a recoverable tool error to the model
InjectedToolArg, InjectedToolCallId, InjectedState, InjectedStore libs/core/langchain_core/tools/base.py Annotation markers that exclude args from the schema and inject them at runtime

How @tool works

from langchain_core.tools import tool
from pydantic import BaseModel, Field

class SearchInput(BaseModel):
    query: str = Field(..., description="What to search for")
    max_results: int = Field(5, description="How many results to return")

@tool(args_schema=SearchInput)
def search(query: str, max_results: int = 5) -> list[str]:
    """Search the web for the given query."""
    return _do_search(query, max_results)

The decorator:

  1. Inspects the function signature and docstring.
  2. Generates a Pydantic args schema (or uses args_schema= if you provide one).
  3. Builds the JSON Schema the model will see.
  4. Returns a BaseTool instance whose _run calls the function.

You can also write @tool without arguments — the function's name and docstring become the tool's name and description, and a schema is derived from the type hints.

Async tools

A function decorated with @tool may be async; BaseTool.ainvoke will await it. For mixed sync/async tools, the base class falls back to running the sync _run in a thread when only _arun is requested, and vice versa.

Injected arguments

Sometimes a tool needs context that the model shouldn't see — for example, the user's session, the agent's full state, the call ID, or a backing key-value store. Annotate the parameter:

from typing import Annotated
from langchain_core.tools import InjectedState, InjectedToolCallId

@tool
def remember(
    fact: str,
    state: Annotated[dict, InjectedState],
    tool_call_id: Annotated[str, InjectedToolCallId],
) -> str:
    """Remember a fact."""
    state["facts"].append(fact)
    return "Remembered"

state and tool_call_id are stripped from the schema the model sees, and supplied at execution time by langgraph's ToolNode or langchain.agents.

ToolException

Raising ToolException("invalid input") from a tool body returns the message to the model as a ToolMessage so it can correct itself, instead of bubbling up as an unhandled exception. This is the recoverable-error idiom every agent loop expects.

Tool calls and tool messages

When a model emits an AIMessage with tool_calls=[ToolCall(name="search", args={...}, id="...")], the agent's tool node:

  1. Looks up BaseTool by name.
  2. Validates args against the tool's args schema.
  3. Calls tool.invoke(args) (or ainvoke).
  4. Appends a ToolMessage(tool_call_id="...", content=result) to the state.
  5. Loops back to the model.

If the tool raises a non-ToolException, the agent's tool-retry middleware (if installed) can catch it and retry; otherwise the error surfaces.

Schema conversion for providers

Different providers want tool schemas in slightly different shapes:

  • OpenAI Chat Completions: {"type": "function", "function": {"name": ..., "description": ..., "parameters": ...}}
  • OpenAI Responses API: variants for function, web_search, file_search
  • Anthropic: {"name": ..., "description": ..., "input_schema": ...}

BaseChatModel.bind_tools(tools) calls partner-specific converters that flatten the LangChain BaseTool into the provider's expected shape. The convention is a convert_to_<provider>_tool helper in each partner — for example convert_to_anthropic_tool is exported from libs/partners/anthropic/langchain_anthropic/__init__.py.

Integration points

  • Every chat model consumes tools via bind_tools.
  • Agents in langchain.agents.create_agent and langchain_classic.agents.AgentExecutor consume tools to build a tool node.
  • Retrievers can be exposed as tools via create_retriever_tool.
  • langgraph.prebuilt.ToolNode is the runtime that actually executes tools inside an agent graph.

Entry points for modification

  • For a new way to define a tool, extend convert.py (e.g. parsing a different decorator metadata format).
  • For a new injection annotation, follow the pattern of InjectedState / InjectedToolCallId in base.py. Annotated args are filtered before schema generation and supplied at runtime by the executor.
  • For provider-specific tool features (e.g. OpenAI's strict=True JSON schema validation), the partner's _compat.py is the right place — keep the base abstraction provider-agnostic.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Tools – LangChain wiki | Factory