Middleware · Getting Started

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:

import {
  bindClientTools,
  clientToolsChannel,
  clientToolsRouter,
} from '@threadplane/middleware/langgraph';

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

  1. The browser sends tool specs with the run request.
  2. Your LangGraph node calls bindClientTools() inside the run, because the catalog can differ per request.
  3. The model emits a tool call for a browser-declared tool.
  4. clientToolsRouter() routes client-only tool calls to END.
  5. 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:

APIPurpose
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