Skip to content

From LangChain tools

Your tools are LangChain @tool functions. Move them into a registry so any framework can use them, while LangChain agents keep working.

flowchart LR
  L["LangChain @tool"] -- "step 1: import_langchain" --> REG[("Registry")]
  P["plain function"] -- "step 2: @registry.tool" --> REG
  REG -- "step 3: structured_tools" --> LC["LangChain agent"]
  REG --> OTHER["OpenAI, MCP, other frameworks"]
  LC --> EX["Executor"]
  OTHER --> EX

Mapping

LangChain Agent Adapter
@tool from langchain_core.tools @registry.tool
args_schema= type hints, Annotated[..., Field(...)], or args_model=
return_direct, callbacks events and hooks (LoggingHook, your own Hook)
RunnableConfig["configurable"]["thread_id"] read by exported tools as ctx.thread_id
HumanInTheLoopMiddleware, interrupts confirm= plus an approval port
tool.invoke(args) await executor.run("name", args)

Step 1: import, no rewrite

from langchain_core.tools import tool

from agent_adapter.integrations.langchain import import_langchain

def shout(text: str) -> str:
    """Upper-case a text."""
    return text.upper()

legacy_tool = tool(shout)  # what `@tool` did in the old module
legacy = Registry()
# policy options (timeout, retries, confirm, ...) apply to imported tools too
import_langchain(legacy, [legacy_tool], prefix="lc_", timeout=5)
return Executor(legacy).run("lc_shout", {"text": "faq"})

Imported tools run through the executor like any other, with the policy you pass.

Step 2: rewrite as plain functions

@registry.tool(kind="search", parallel_safe=True, timeout=10)  # was: @tool from langchain_core
def word_count(text: str) -> int:
    """Count the words in a text."""
    return len(text.split())


executor = Executor(registry)  # approval, budgets, cache and ledger live here now
result = await executor.run_call(ToolCall(name="word_count", arguments={"text": "a b c"}))
print(result.value, result.status)

Step 3: give LangChain the registry

from agent_adapter.integrations.langchain import structured_tools

tools = structured_tools(registry, executor=executor)  # pass to bind_tools / an agent
call = {"name": "word_count", "args": {"text": "one two"}, "id": "call_1", "type": "tool_call"}
return tools[0].ainvoke(call)  # a ToolMessage

Output

3 ok
FAQ
2

Without langchain-core installed, the example prints 3 ok and skips steps 1 and 3.

Gained and lost

Gained Lost
tools without a LangChain dependency LangChain-only features on the tool object (return_direct, tool callbacks)
the same tools for every other framework and model API
approval with one-use decisions, budgets, caching, ledger

Checklist

  • [ ] pip install "agent-adapter[langchain]" for steps 1 and 3.
  • [ ] Each tool rewritten keeps its name, so prompts and traces still match.
  • [ ] Agents get structured_tools(registry, executor=executor), not the old list.
  • [ ] Remove langchain_core imports from tool modules once step 2 is done.

Files

File Does
examples/guide_migrate_langchain.py the import, the rewrite and the export
tests/docs/test_migration.py runs it offline; the LangChain steps run when langchain-core is installed