快速开始与核心概念(2/5)

        # Read from Store: get user's enabled features
        store = request.runtime.store
        feature_flags = store.get(("features",), user_id)

        if feature_flags:
            enabled_features = feature_flags.value.get("enabled_tools", [])
            # Only include tools that are enabled for this user
            tools = [t for t in request.tools if t.name in enabled_features]
            request = request.override(tools=tools)

        return handler(request)

    agent = create_agent(
        model="gpt-5.5",
        tools=[search_tool, analysis_tool, export_tool],
        middleware=[store_based_tools],
        context_schema=Context,
        store=InMemoryStore()
    )



Filter tools based on user permissions from Runtime Context:

```python
from dataclasses import dataclass
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from typing import Callable

@dataclass
class Context:
    user_role: str

@wrap_model_call
def context_based_tools(
    request: ModelRequest,
    handler: Callable[[ModelRequest], ModelResponse]
) -> ModelResponse:
    """Filter tools based on Runtime Context permissions."""
    # Read from Runtime Context: get user role
    if request.runtime is None or request.runtime.context is None:
        # If no context provided, default to viewer (most restrictive)
        user_role = "viewer"
    else:
        user_role = request.runtime.context.user_role

    if user_role == "admin":
        # Admins get all tools
        pass
    elif user_role == "editor":
        # Editors can't delete
        tools = [t for t in request.tools if t.name != "delete_data"]
        request = request.override(tools=tools)
    else:
        # Viewers get read-only tools
        tools = [t for t in request.tools if t.name.startswith("read_")]
        request = request.override(tools=tools)

    return handler(request)

agent = create_agent(
    model="gpt-5.5",
    tools=[read_data, write_data, delete_data],
    middleware=[context_based_tools],
    context_schema=Context
)
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](https://docs.langchain.com/oss/python/langchain/middleware/custom#dynamically-selecting-tools) for more examples.



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
from langchain.tools import tool
from langchain.agents import create_agent
from langchain.agents.middleware import AgentMiddleware, ModelRequest, ToolCallRequest

# A tool that will be added dynamically at runtime
@tool
def calculate_tip(bill_amount: float, tip_percentage: float = 20.0) -> str:
    """Calculate the tip amount for a bill."""
    tip = bill_amount * (tip_percentage / 100)
    return f"Tip: ${tip:.2f}, Total: ${bill_amount + tip:.2f}"

class DynamicToolMiddleware(AgentMiddleware):
    """Middleware that registers and handles dynamic tools."""

    def wrap_model_call(self, request: ModelRequest, handler):
        # Add dynamic tool to the request
        # This could be loaded from an MCP server, database, etc.
        updated = request.override(tools=[*request.tools, calculate_tip])
        return handler(updated)

    def wrap_tool_call(self, request: ToolCallRequest, handler):
        # Handle execution of the dynamic tool
        if request.tool_call["name"] == "calculate_tip":
            return handler(request.override(tool=calculate_tip))
        return handler(request)

agent = create_agent(
    model="gpt-5.5",
    tools=[get_weather],  # Only static tools registered here
    middleware=[DynamicToolMiddleware()],
)

# The agent can now use both get_weather AND calculate_tip
result = 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


  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.

Headless tools

Some tools should run where your user's app runs (typically the browser), not inside the process. Headless tools are tool definitions, which include the name, description, and argument schema, that you register on the server with your agent. The implementation is registered only on the client and executed after a short interrupt/resume handshake.

This is different from ordinary tools whose function body runs on the server, and from server-side tool use where the model provider executes built-in tools remotely.

When to use headless tools

Use them when the work depends on the environment, device, or UI that only exists on the client. For example:

  • Browser APIs: Geolocation, IndexedDB, Clipboard, Canvas 2D, file pickers, Battery API, etc.
  • Privacy and locality: Data stays on the device (for example, local “memory” in IndexedDB).
  • Latency: No extra server round trip for purely local operations.
  • Structured, safe effects: Prefer many small, typed tools (for example one tool per canvas primitive) instead of sending arbitrary code to eval.
How the pattern works

In both runtimes, the model sees a normal tool it can call, but the actual execution happens outside the server process.

  1. Define a headless tool with tool(name=..., description=..., args_schema=...) from langchain.tools. A headless tool is schema-only, with no in-process implementation.
  2. Register that tool with create_agent or your LangGraph graph so the model can call it normally.
  3. Handle the interrupt payload when the tool is invoked. Instead of running locally, the graph pauses with a payload shaped like {"type": "tool", "tool_call": {"id", "name", "args"}}.
  4. Resume the graph after your app, another service, or a human step performs the action. For browser-based flows, you can mirror the schema in the frontend and attach .implement(...) there.

If you call tool(...) in Python with only name, description, and args_schema, LangChain returns a HeadlessTool. There is no .implement() API on the Python side.

When the model issues a tool call for one of these tools, the run interrupts instead of executing the tool locally. Your app can inspect the payload, perform the action in the right environment (for example a browser, another service, or a human review step), then resume the graph with the tool result. When you use the supported JS SDK hooks, they can detect headless-tool interrupts, run the matching client implementation, and submit the resume command for you.

Use the optional onTool callback to observe lifecycle events (start, success, error) for UI feedback such as spinners or toasts.

See an end-to-end example of schema-only tools executed in the client with useStream.

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 integration page for a complete list of available tools organized by category.

Tools from MCP servers

Model Context Protocol (MCP) is an open protocol that standardizes how applications expose tools to language models. Rather than hand-authoring a tool, you connect to an MCP server and adapt the tools it advertises into LangChain tools, ready to pass to an agent like any other tool.

MCPAdapter discovers a server's tools and converts them into LangChain tools. Open the adapter, call list_tools(), and hand the result to create_agent:

from langchain.agents import create_agent
from langchain.mcp import MCPAdapter

async with MCPAdapter("https://example.com/mcp") as adapter:
    tools = await adapter.list_tools()
    agent = create_agent("claude-sonnet-4-6", tools)

The langchain.mcp namespace requires langchain[mcp]>=1.4.0 and is in beta. The API may change.

For transports, authentication, multiple servers, and handling tool results, see Model Context Protocol (MCP).

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 and the tool calling documentation for details on enabling and using these built-in tools.


<div> <Callout icon="terminal-2"> Connect these docs to your agent of choice via MCP for real-time answers. </Callout>

<Callout icon="edit"> Edit this page on GitHub or file an issue. </Callout> </div>