Introduction
@threadplane/middleware is the backend companion for Threadplane client tools. It lets a browser declare tools, lets the model call those tools, and routes client-tool-only turns back to the browser for execution.
The package currently publishes one runtime entry point:
There is no root @threadplane/middleware JavaScript entry point. Import from @threadplane/middleware/langgraph.
#What it does
The LangGraph entry point reads a client tool catalog from graph state, converts it into OpenAI function-tool objects, binds those tool stubs onto your chat model, and routes client-tool calls to END so the browser can execute them.
The catalog is read from state.tools first. If that channel is absent or empty, it falls back to state.client_tools.
#Runtime flow
- The browser sends tool specs with the run request.
- Your LangGraph node calls
bindClientTools()inside the run, because the catalog can differ per request. - The model emits a tool call for a browser-declared tool.
clientToolsRouter()routes client-only tool calls toEND.- The browser executes the local tool and resumes the graph with a
ToolMessage.
If a turn mixes server tool calls and client tool calls, server tools win the first route. The server tool node runs first, and the client call can surface on a later turn.
#Public surface
The entry point exports:
| API | Purpose |
|---|---|
clientToolsChannel() | Adds the tools and client_tools state channels to a LangGraph annotation. |
bindClientTools() | Binds server tools plus client-declared tool stubs onto a model. |
clientToolsRouter() | Creates a conditional-edge router for server-tool vs client-tool routing. |
clientToolSpecs() | Converts state catalog entries into OpenAI function-tool specs. |
clientToolNames() | Returns the set of client-declared tool names for a run. |
hasClientToolCall() | Checks whether the last message calls a client tool. |
hasServerToolCall() | Checks whether the last message calls a server or unknown tool. |
routeAfterAgent() | Lower-level routing helper used by clientToolsRouter(). |
lastMessage() | Reads the last message from state. |
#When to use it
Use middleware when you own a LangGraph.js backend and want browser-declared tools from @threadplane/chat to participate in model tool calling without executing browser-only code on the server.
If your backend already speaks AG-UI, use @threadplane/ag-ui instead. If your frontend talks directly to LangGraph and does not need browser-executed tools, @threadplane/langgraph can run without this middleware.
#Next steps
- Quick Start - install and wire the LangGraph helper.
- LangGraph Client Tools - routing details and server-tool behavior.
- Client Tool Helpers - generated API reference for the exported helpers.