new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

LangChain · Agents · all subjects

agents/tools

147 notes in this subject, read out of this brain and free to use. This is page 1 of 3.

Tools can be Python callables, LangChain tools, or tool dicts

The tools parameter accepts any Python callable, LangChain tool, or tool dict. This provides flexibility in how tools are defined and integrated with the agent.

Filtering tools by user permissions from runtime context

Example: Use @wrap_model_call to read user_role from request.runtime.context. If user_role == 'admin', provide all tools. If user_role == 'editor', exclude 'delete_data' tool. Otherwise (viewers), only include tools with names starting with 'read_'. Use request.override(tools=filtered_tools).

Tools can read from state

Tools can read from state via runtime.state parameter. Example: check_authentication tool reads runtime.state.get('authenticated', False) to check current auth status. Access this within the tool function to make decisions based on session-specific information.

Tools can read from runtime context

Tools can access runtime context via runtime.context parameter. Example: fetch_user_data tool reads user_id, api_key, and db_connection from runtime.context to configure database queries and API calls.

Tool access to runtime context and store

Tools can access runtime context and store through the `runtime` parameter of type `ToolRuntime[Context]`. The runtime provides access to `runtime.context` to read user-specific data and `runtime.store` to get and put persistent data. Use `store.get(namespace, key)` to retrieve existing data and `store.put(namespace, key, value)` to write updates.

Tool definition requires clear metadata

Each tool needs a clear name, description, argument names, and argument descriptions. These are not just metadata—they guide the model's reasoning about when and how to use the tool. The @tool decorator with parse_docstring=True in Python auto-extracts this from docstrings.

Filtering tools by authentication state and conversation length

Example: Use @wrap_model_call to filter tools based on state. Check is_authenticated from request.state.get('authenticated', False). If not authenticated, filter to tools with names starting with 'public_'. If authenticated but message_count < 5, exclude 'advanced_search' tool. Use request.override(tools=filtered_tools).

Tools can read from store

Tools can read from store via runtime.store parameter. Example: get_preference tool reads user_id from runtime.context, then calls runtime.store.get(('preferences',), user_id) to retrieve persisted user preferences across sessions.

Filtering tools by feature flags from store

Example: Use @wrap_model_call to read feature_flags from store via request.runtime.store.get(('features',), user_id). Extract enabled_tools list and filter request.tools to only include tools in enabled_tools list. Use request.override(tools=filtered_tools).

Dynamic tool selection principle

Not every tool is appropriate for every situation. Too many tools may overwhelm the model and increase errors; too few limit capabilities. Dynamic tool selection adapts the available toolset based on authentication state, user permissions, feature flags, or conversation stage.

Tool creation in JavaScript with schema

In JavaScript/TypeScript, tools are created using `tool()` function which accepts a handler function and configuration object. The configuration object must include: `name` (string), `description` (string), and `schema` (Zod schema defining input parameters). The handler function receives the parsed inputs and `runtime: ToolRuntime<any, typeof contextSchema>`.

Tool decorator usage in Python with runtime

In Python, tools decorated with `@tool` can accept a `runtime` parameter of type `ToolRuntime[Context]` to access context and store within the tool implementation. This allows tools to read and write to persistent storage keyed by user ID or other identifiers.

Tool definition example with search_orders

Python example using @tool(parse_docstring=True) decorator: search_orders(user_id: str, status: str, limit: int = 10). Docstring includes description, usage context, and Args section with user_id (unique identifier), status (pending/shipped/delivered), and limit (max results, default 10).

save_preference tool example implementation

The save_preference tool demonstrates reading existing preferences from store, merging new preferences, and writing back to store. In Python: `def save_preference(preference_key: str, preference_value: str, runtime: ToolRuntime[Context]) -> str:` reads user_id from runtime.context, gets existing prefs with `store.get(("preferences",), user_id)`, merges the new preference into the dict, and saves with `store.put(("preferences",), user_id, prefs)`.

Tools can write to state using Command

Tools can update state by returning a Command object. Example: authenticate_user tool returns Command(update={'authenticated': True}) or Command(update={'authenticated': False}) to write authentication status to state based on password verification.

Example: insufficient tool responses in JavaScript

Example showing problematic pattern in JavaScript: when a model requests two tool calls but only one ToolMessage is provided for the first tool call, invoke fails with INVALID_TOOL_RESULTS: ```javascript // Model requests two tool calls responseMessage.tool_calls // Returns 2 calls // But only one ToolMessage provided chatHistory.push({ role: "tool", content: toolResponse, tool_call_id: responseMessage.tool_calls[0].id }); await modelWithTools.invoke(chatHistory); // Fails with INVALID_TOOL_RESULTS ```

Tool message protocol requirement

When a model returns an AIMessage with tool_calls, you must provide exactly one corresponding ToolMessage for each tool call, with matching tool_call_id values. An assistant message with tool_calls must be followed by tool messages responding to each tool_call_id.

Insufficient tool responses cause INVALID_TOOL_RESULTS

If a model requests multiple tool executions but you only provide fewer response ToolMessage objects than requested tool calls, the model rejects the incomplete message chain with INVALID_TOOL_RESULTS error.

Orphaned tool messages cause INVALID_TOOL_RESULTS

Sending a ToolMessage without a preceding AIMessage containing tool calls violates protocol requirements and results in INVALID_TOOL_RESULTS error.

INVALID_TOOL_RESULTS error definition

The INVALID_TOOL_RESULTS error occurs when passing mismatched, insufficient, or excessive ToolMessage objects to a model during tool calling operations. This error is currently only used in langchainjs (JavaScript/TypeScript).

Duplicate or unmatched tool messages cause INVALID_TOOL_RESULTS

Providing multiple ToolMessage objects for the same tool_call_id results in rejection. Unmatched tool_call_id values between ToolMessage and AIMessage.tool_calls also causes INVALID_TOOL_RESULTS error.

Tool message troubleshooting steps

To resolve INVALID_TOOL_RESULTS error: count matching pairs to ensure one ToolMessage exists per tool_call in the preceding AIMessage; verify each ToolMessage.tool_call_id matches an actual tool_call identifier from the AIMessage.

Headless tools best practices

Keep tools small and typed; prefer many narrow tools over one generic tool. Return JSON-serializable results. Share definitions and separate implementations between agent and client. Surface tool state in the UI using stream.toolCalls and onTool to show pending, success, and error states. Add review patterns for sensitive client-side actions using Human-in-the-loop.

Headless tool mirror definition in TypeScript

Mirror tool definitions in TypeScript using zod schemas. Each tool definition specifies a name (matching the Python tool name), description, and schema object with typed fields. Use z.unknown() for flexible field types and z.boolean().optional() for optional parameters.

Implementing browser behavior with .implement()

Use .implement(...) on tool definitions to attach client-only behavior. The implement method receives an async callback that executes the browser-side logic. Return JSON-serializable results. Do not attempt to return DOM nodes, file handles, or other non-serializable browser objects.

Headless tools implementation steps for Python

To implement headless tools in Python: (1) Register a tool on the agent that immediately calls interrupt() to defer execution to the frontend. (2) Mirror the same tool names and argument fields in frontend definitions. (3) Implement the matching tools in the frontend with .implement(...) and pass them to useStream({ tools: [...] }). (4) When the agent invokes a matching tool, the client handles the action and resumes the interrupted run with the tool result.

Headless tools implementation steps for JavaScript

To implement headless tools in JavaScript: (1) Register a schema-only tool definition on the agent. (2) Implement the matching tool in the frontend with .implement(...). (3) Pass those implementations to useStream({ tools: [...] }). (4) When the agent emits a matching tool call, the client runs it and resumes the interrupted run with the tool result.

Headless tools pattern overview

Headless tools allow an agent to call tools whose implementation runs on the client instead of the server. The agent sees a normal tool schema, but the real execution happens in the frontend where it can access browser APIs like IndexedDB, geolocation, clipboard, canvas, or file pickers. This pattern keeps data local to the device.

Python agent tool registration with interrupt

In Python, define normal tools on the server that immediately call interrupt(). The interrupt function accepts a structured payload with type 'tool' containing the tool_call id, name, and args. The tool uses ToolRuntime to access runtime.tool_call_id. Each tool is decorated with @tool specifying name, description, and args_schema (a Pydantic BaseModel).

Example Python headless tool with interrupt

The memory_put tool is defined with @tool decorator specifying name 'memory_put', description 'Store a memory in the user's browser', and args_schema of MemoryPutInput (BaseModel with key: str and value: Any fields). The tool calls _interrupt_for_client passing the tool name, args dict, and runtime. The _interrupt_for_client helper returns interrupt() with a payload containing type 'tool' and tool_call dict with id from runtime.tool_call_id, name, and args.

Example TypeScript tool definition for geolocation

const geolocationGet = tool({ name: 'geolocation_get', description: 'Get the user's current location from the browser.', schema: z.object({ save: z.boolean().optional() }) }); The tool name matches the Python tool exactly, and the schema uses zod to define optional boolean parameters.

Headless tools best practice: separate definitions and implementations

Keep tool definitions and implementations in separate modules. Share the definitions between the agent and the frontend so tool names and schemas stay aligned, then keep browser-only code in a client-only impl module.

Example geolocation implementation

export const geolocationGet = geolocationGetDefinition.implement(async ({ save = true }) => { const position = await new Promise<GeolocationPosition>((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject)); const location = { latitude: position.coords.latitude, longitude: position.coords.longitude, accuracy: position.coords.accuracy }; if (save) { await saveMemory('user_location', location); } return location; }); The implementation accesses the Geolocation API, optionally persists the result, and returns the location object.

Example headless tool implementation with localStorage

export const memoryPut = memoryPutDefinition.implement(async ({ key, value }) => { await saveMemory(key, value); return { success: true, key }; }); The implement callback receives destructured arguments matching the schema, performs async browser operations, and returns a JSON-serializable result object.

AI Elements tool call extraction from AIMessage

Extract tool calls from an AIMessage via msg.tool_calls array. Each tool call object has id, name, args, and can have output. Tool calls render with the Tool component containing ToolHeader, ToolInput, and optionally ToolOutput.

Tool calling pattern overview

When a LangGraph agent decides it needs external data, it emits one or more tool calls as part of an AI message. Each tool call includes a name (the tool being invoked, e.g. 'get_weather' or 'calculator'), args (the structured arguments passed to the tool), and id (a unique identifier linking the call to its result). The agent runtime executes the tool, and the result comes back as a ToolMessage. The useStream hook unifies all of this into a single toolCalls array that can be rendered directly.

Error card component example

function ErrorCard({ name, error }: { name: string; error?: unknown }) { return ( <div className="rounded-lg border border-red-300 bg-red-50 p-4"> <h3 className="font-semibold text-red-700">Error in {name}</h3> <p className="text-sm text-red-600"> {String(error ?? "Tool execution failed")} </p> </div> ); } This example shows how to display an error card when tool execution fails, with tool name and error message.

ToolCallFromTool type utility example

import { tool } from '@langchain/core/tools'; import { z } from 'zod'; const getWeather = tool(async ({ location }) => { /* ... */ }, { name: 'get_weather', description: 'Get the current weather for a location', schema: z.object({ location: z.string().describe('City name'), }), }); type WeatherToolCall = ToolCallFromTool<typeof getWeather>; // WeatherToolCall.input and WeatherToolCall.args are now { location: string } This example shows how to use ToolCallFromTool to get compile-time typed arguments from a tool definition, automatically inferring the shape from the tool's Zod schema.

Tool card switch pattern example

function ToolCard({ toolCall }: { toolCall: AssembledToolCall }) { if (toolCall.status === 'running') { return <LoadingCard name={toolCall.name} />; } if (toolCall.status === 'error') { return <ErrorCard name={toolCall.name} error={toolCall.error} />; } switch (toolCall.name) { case 'get_weather': return <WeatherCard input={toolCall.input} output={toolCall.output} />; case 'calculator': return ( <CalculatorCard input={toolCall.input} output={toolCall.output} /> ); case 'web_search': return <SearchCard input={toolCall.input} output={toolCall.output} />; default: return <GenericToolCard toolCall={toolCall} />; } } This pattern checks the tool call status first, then dispatches to specialized card components based on the tool name, with a fallback for unknown tools.

Loading card component example

function LoadingCard({ name }: { name: string }) { return ( <div className="flex items-center gap-2 rounded-lg border p-4 animate-pulse"> <Spinner /> <span>Running {name}...</span> </div> ); } This example shows how to display a loading card while a tool is executing, with visual feedback via animation and a spinner.

Weather card component example

function WeatherCard({ input, output, }: { input: { location: string }; output: { temperature: number; condition: string }; }) { return ( <div className="rounded-lg border p-4"> <div className="flex items-center gap-2"> <CloudIcon /> <h3 className="font-semibold">{input.location}</h3> </div> <div className="mt-2 text-3xl font-bold">{output.temperature}°F</div> <p className="text-muted-foreground">{output.condition}</p> </div> ); } This example shows a specialized card for displaying weather tool results with location name, temperature, and condition fields.

Filtering tool calls for a message example

function Message({ message, toolCalls, }: { message: AIMessage; toolCalls: AssembledToolCall[]; }) { const messageToolCalls = toolCalls.filter((tc) => message.tool_calls?.find((t) => t.id === tc.callId) ); return ( <div> <p>{message.text}</p> {messageToolCalls.map((tc) => ( <ToolCard key={tc.callId} toolCall={tc} /> ))} </div> ); } This example shows how to filter the global toolCalls array to get only calls belonging to a specific AI message, then render specialized tool cards for each.

Tool calling code example with React

import { useStream } from '@langchain/react'; const AGENT_URL = 'http://localhost:2024'; export function Chat() { const stream = useStream<typeof myAgent>({ apiUrl: AGENT_URL, assistantId: 'tool_calling', }); return ( <div> {stream.messages.map((msg) => ( <Message key={msg.id} message={msg} toolCalls={stream.toolCalls} /> ))} </div> ); } This example shows how to set up useStream with an agent backend and render messages with their associated tool calls.

Tool calling best practices

When building tool call UIs: (1) Always handle all three states (running, finished, error), never show blank cards. (2) Validate results safely since tool outputs are typed as unknown until narrowed for specific cards. (3) Provide a generic fallback for unknown tools, rendering a collapsible JSON view. (4) Show the tool name and args during loading so users know what the agent is doing before results arrive. (5) Keep cards compact since they sit inline with chat messages and should not overwhelm the conversation.

ToolCallFromTool utility type for type-safe arguments

If tools are defined with structured schemas using Zod, use the ToolCallFromTool utility type to get fully typed args and input properties. This provides compile-time safety, and if the tool schema changes, UI components will flag type errors immediately. Example: type WeatherToolCall = ToolCallFromTool<typeof getWeather> makes input and args typed as { location: string } based on the tool's schema.

Multiple concurrent tool calls handling

Agents can invoke several tools in parallel. The toolCalls array will contain multiple entries with status 'running' simultaneously. Each resolves independently, so the UI should handle partial completion gracefully by separating pending and completed tool calls.

Tool call streaming and inline rendering

Tool calls often arrive interleaved with streamed text. The useStream hook keeps toolCalls in sync with the stream, so pending cards appear as soon as the agent emits the call, before the tool has finished executing. Users see the AI's text as it streams, a loading card the moment a tool call is emitted, and the card updates to show the result once the tool completes.

Handling loading and error states in tool cards

Always handle all three tool call states (running, finished, error) to give users clear feedback. For running state, display a loading card with a spinner and tool name. For error state, display an error card with the tool name and error message. Users should never see a blank card.

Tool call lifecycle states

Tool calls transition through three lifecycle states: 'running' when the agent has emitted the call but the tool has not yet completed execution, 'finished' when the tool has successfully executed and returned output, and 'error' when the tool execution fails. The same callId transitions between these states in place, causing the UI to re-render the same component with new state.

Building specialized tool cards by name

Rather than displaying raw JSON, build dedicated UI components for each tool using the toolCall.name property to select the appropriate card component. Each tool (e.g. 'get_weather', 'calculator', 'web_search') can have its own card implementation that formats the input and output data appropriately for that tool's domain.

Filtering tool calls per message

An AI message may trigger multiple tool calls. To render the right tool cards under each message, filter toolCalls by matching callId against the message's tool_calls array. This ensures only tool calls belonging to a specific message are displayed with that message in the conversation.

AssembledToolCall interface structure

The AssembledToolCall interface has the following properties: name (string, the name of the tool e.g. 'get_weather'), callId (string, unique ID matching the AI message's tool_calls entry), id (string, alias for callId matching message-level tool calls), namespace (string array, namespace where the tool call was emitted), input (unknown, structured arguments the agent passed to the tool), args (unknown, alias for input matching message-level tool calls), output (unknown or null, tool output after successful call or null while running or after error), status (enum: 'running', 'finished', or 'error', the lifecycle state), and error (string or undefined, error details when the tool call fails).

useStream hook setup for tool calling

The useStream hook must be wired up to the agent backend and returns reactive state including a toolCalls array that updates in real time as the agent streams. The hook is available for React (@langchain/react), Vue (@langchain/vue), Svelte (@langchain/svelte), and Angular (@langchain/angular) frameworks. Configuration requires an apiUrl pointing to the agent backend (e.g. 'http://localhost:2024') and an assistantId to identify the specific agent instance (e.g. 'tool_calling').

Handling multiple concurrent tool calls example

function ToolCallList({ toolCalls }: { toolCalls: AssembledToolCall[] }) { const pending = toolCalls.filter((tc) => tc.status === 'running'); const completed = toolCalls.filter((tc) => tc.status === 'finished'); return ( <div className="space-y-2"> {completed.map((tc) => ( <ToolCard key={tc.callId} toolCall={tc} /> ))} {pending.map((tc) => ( <LoadingCard key={tc.callId} name={tc.name} /> ))} </div> ); } This example shows how to separate pending and completed tool calls, rendering completed results first followed by loading cards for in-flight calls.

MCP tool error handling in LangChain TypeScript adapter

In @langchain/mcp-adapters, when an MCP tool execution fails (CallToolResult with isError: true), a ToolException is raised. Wrap tool calls in a try/catch to handle these errors. Unlike the Python adapter, the TypeScript adapter does not return the error to the model as a failed tool message.

MCP tool error handling in LangChain Python adapter

By default in langchain-mcp-adapters>=0.3.0, when an MCP tool fails, the error is passed back to the model as a tool message with status='error' instead of raising an exception. This lets the agent read the error and try again. To raise an exception instead, set handle_tool_errors=False on MultiServerMCPClient or load_mcp_tools. This applies only to tool execution errors (CallToolResult(isError=True)); transport, session, and content-conversion failures always raise.

HTTP transport with custom headers

When connecting to MCP servers over HTTP, custom headers can be included using the headers field in the connection configuration for http and streamable_http transports. This is useful for authentication, tracing, or custom headers like X-Custom-Header.

MCP transport mechanisms

MCP supports two main transport mechanisms: HTTP (also called streamable-http) uses HTTP requests for client-server communication and works with local or hosted servers; stdio launches the server as a subprocess and communicates via standard input/output, best for local tools. The stdio transport is inherently stateful at the transport level, though MultiServerMCPClient still creates fresh sessions per tool call by default.

MultiServerMCPClient default behavior is stateless

MultiServerMCPClient is stateless by default. Each tool invocation creates a fresh MCP ClientSession, executes the tool, and then cleans up. For stateful sessions that maintain context across tool calls, use client.session() to create a persistent ClientSession.

Model Context Protocol (MCP) overview

Model Context Protocol is an open protocol that standardizes how applications provide tools and context to LLMs. LangChain agents can use tools defined on MCP servers using the langchain-mcp-adapters (Python) or @langchain/mcp-adapters (JavaScript) library.

Give your agent this brain