# Are dynamic tool lists allowed when using create\_agent?

**URL:** <https://forum.langchain.com/t/are-dynamic-tool-lists-allowed-when-using-create-agent/1920>\
**Category:** Talking Shop\
**Tags:** intro-to-langgraph, python-help\
**Created:** [October 24, 2025, 9:59am UTC](https://forum.langchain.com/t/are-dynamic-tool-lists-allowed-when-using-create-agent/1920 "2025-10-24T09:59:57Z")\
**Posts on this page:** 1\
**Showing post:** 16

<div class="post-metadata">

**Author:** ![pawel-twardziak](https://yyz1.discourse-cdn.com/flex007/user_avatar/forum.langchain.com/pawel-twardziak/32/960_2.png) [@pawel-twardziak](https://forum.langchain.com/u/pawel-twardziak)\
**Post date:** [February 3, 2026, 6:34pm UTC](https://forum.langchain.com/t/are-dynamic-tool-lists-allowed-when-using-create-agent/1920/16 "2026-02-03T18:34:37Z")

</div>

hi,

this is a result of my investigation:

### 1) How tools are “registered”

In LangGraph, tools aren’t registered in some global registry as part of graph compilation; they’re typically _embedded into the graph as a node_.

- In the standard agent loop created by `create_agent`, the “tools” step is a `ToolNode` constructed from the tool list. `ToolNode. __init__ ` immediately builds an internal `name -> tool` mapping (`self._tools_by_name`) and also precomputes injection metadata once (`self._injected_args[tool_name] = ...`) for runtime efficiency. That’s the practical “registration” moment. See `langgraph/libs/prebuilt/langgraph/prebuilt/tool_node.py` where `_InjectedArgs` is “built once during ToolNode initialization” and `ToolNode. __init__ ` populates `_tools_by_name` / `_injected_args`.

Sources:

- `langgraph/libs/prebuilt/langgraph/prebuilt/tool_node.py` (ToolNode init builds `self._tools_by_name`; injected args “built once during ToolNode initialization”).

LangChain’s `create_agent` is explicitly graph-based and uses LangGraph under the hood.

Sources:

- LangChain Agents docs: [Agents - Docs by LangChain](https://docs.langchain.com/oss/python/langchain/agents) (notes `create_agent` builds a graph-based runtime on LangGraph)
- `langchain/libs/langchain_v1/langchain/agents/factory.py` (imports `StateGraph` + `ToolNode`, and constructs the agent graph)

### 2) Is it compile-time or runtime?

There are two different “tool” concerns:

1. **What the graph can execute** (the executor side)

2. **What the model is allowed to call** (the schema/binding side)

Sources:

- `langgraph/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py` (dynamic model section: bind tools; bound tools must be subset of `tools` parameter)
- LangChain Agents docs: [Agents - Docs by LangChain](https://docs.langchain.com/oss/python/langchain/agents)

### 3) Can you modify the tool list at runtime?

Yes - but only if you distinguish _“tools visible to the model”_ vs _“tools executable by the graph”_.

#### A) Runtime _filtering_ of pre-registered tools (recommended)

If you know all tools ahead of time, register them once (pass them to `create_agent(...)`) and then **filter which ones are exposed to the model per request** via middleware that overrides `request.tools`.

This is explicitly documented as a supported pattern (“Filtering pre-registered tools”).

Source:

- LangChain Agents docs, “Dynamic tools” section: [Agents - Docs by LangChain](https://docs.langchain.com/oss/python/langchain/agents)

#### B) Runtime _addition_ of brand-new tools (possible, but you must also handle execution)

If middleware adds tools that were not included in `create_agent(tools=[...])`, the agent graph’s `ToolNode` won’t know how to execute them by default.

- The LangChain implementation even ships an explicit error template explaining this failure mode and the two fixes:
  - Register tools at creation time (`create_agent(tools=[...])` or `middleware.tools`), or
  - Implement `wrap_tool_call` to execute/override the dynamically-added tool.

Sources:

- `langchain/libs/langchain_v1/langchain/agents/factory.py` (see `DYNAMIC_TOOL_ERROR_TEMPLATE`, especially the guidance: middleware modifying `request.tools` must either pre-register tools or handle them in `wrap_tool_call`)
- LangChain Agents docs, “Runtime tool registration”: [Agents - Docs by LangChain](https://docs.langchain.com/oss/python/langchain/agents)

Minimal sketch (conceptual):

```python
from langchain.agents import create_agent
from langchain.agents.middleware import AgentMiddleware

class DynamicToolMiddleware(AgentMiddleware):
    def wrap_model_call(self, request, handler):
        # expose the new tool to the model
        return handler(request.override(tools=[*request.tools, my_dynamic_tool]))

    def wrap_tool_call(self, request, handler):
        # teach the graph how to execute it
        if request.tool_call["name"] == my_dynamic_tool.name:
            return handler(request.override(tool=my_dynamic_tool))
        return handler(request)

agent = create_agent(model, tools=[some_static_tool], middleware=[DynamicToolMiddleware()])

```

#### C) If you’re using raw LangGraph `StateGraph` (no `create_agent`)

You can always build your own “tools node” callable that looks up tools from `runtime.context` / state and executes them dynamically. That’s a custom architecture choice; it’s not what `ToolNode` does out of the box.

### Practical guidance / gotchas

- Compiled graphs are best treated as immutable: if the executable tool set changes, rebuild a new `ToolNode` / new graph.
- If you dynamically change tool exposure to the model, keep the executor in sync:
  - Filtering pre-registered tools is easy.
  - Adding new tools requires `wrap_tool_call` (or a custom tool execution node), otherwise you’ll hit the “unknown tools” error described in `DYNAMIC_TOOL_ERROR_TEMPLATE`.

---

_[View the full topic](https://forum.langchain.com/t/are-dynamic-tool-lists-allowed-when-using-create-agent/1920)._
