LangChain ToolRuntime: A three-way split for context, state, and store
Typed runtime injection keeps user identity and permissions out of the model's control, while a namespaced store gives agents durable, cross-session memory without leaking sensitive data. That removes two common failure points in LangChain tool design: trusting model-supplied IDs and mixing temporary workflow data with persistent preferences.
ToolRuntime is the runtime object LangChain auto-injects into a tool when the tool declares a `runtime` parameter. It carries three objects that map cleanly onto data lifetimes: `context` holds fixed run conditions like user ID, role, and database connections; `state` holds the current flow's messages, steps, and temporary results; `store` holds data that must survive across requests, such as user preferences and long-term memory.
The separation is enforced by typing. `context_schema` declares the context shape, and the program creates the context object and passes it via `agent.invoke(..., context=...)`; the model cannot write to it. For state, a custom `state_schema` lets middleware extend the built-in message list, for example counting model calls with an `after_model` hook. Store operations are wrapped in tools that read the user ID from `context` and use namespaced keys, with `InMemoryStore` as a simple in-memory implementation.
The article's core advice is to decide where a value belongs by its lifecycle before writing tool code. It repeatedly warns that user identity must come from the login system, not from model-generated text, and that store contents should be curated rather than dumping every chat message into long-term memory.
The strict separation of context, state, and store maps directly to data lifetime, which is a cleaner design than passing a generic kwargs dict into every tool.
The repeated warning that user IDs must come from the login system, not from model text, is a security invariant many simple tool examples routinely violate.
The middleware example shows state is not just a read-only view; tools and middleware can cooperatively update it, enabling rate limiting and debugging counters without changing the model interface.