---
question: How do I expose a workflow as agent tools?
kind: task
via_web: na
via_cli: na
via_python: available
via_api: na
via_config: na
ref_python: as_tools()
prereq: You need a workflow class in a module, or (ABI 3.x, NATS project) another module's durable workflow. [Create a workflow](/v2/capabilities/build/agents/creating-a-workflow) first.
next:
  - /v2/capabilities/build/agents/give-it-tools
  - /v2/capabilities/build/workflows/test-a-workflow
  - /v2/capabilities/build/agents/test-an-agent
---

> **You are reading the v2.x docs (engine modules). New projects use v3.** [Read this page in v3](/capabilities/build/workflows/expose-as-a-tool) · [Migrate from 2.x to 3.0](/capabilities/upgrade/migrate-to-3)

# Expose as a tool

An agent calls a workflow through LangChain tools. Where they come from depends on the workflow:

| Workflow | Tools | Runs | Line |
|---|---|---|---|
| A workflow class | `as_tools()`, which the class implements (`abi new workflow` writes one to fill in) | `run` in process | ABI 2.x and 3.x |
| A durable workflow of another module | A `StructuredTool` you write, whose function starts the run | A durable run, recorded in System › Jobs | ABI 3.x, NATS project |

## Web UI

Not applicable. Nexus lists an agent's tools in Settings › Agents but cannot attach a workflow's tools: they are set in Python, in the agent's `New()`. Use the Python steps on this page.

## CLI

Not applicable. No command adds a tool. `abi new workflow` writes an `as_tools()` stub with the workflow: fill it in as in the Python steps.

## Python

### Return the tools

One `StructuredTool` per action. The `args_schema` is the parameters model, so its `Field(description=...)` texts tell the model how to fill each argument:

```python title="workflows/CustomersWorkflow.py"
from langchain_core.tools import BaseTool, StructuredTool


class CustomersWorkflow(Workflow[CustomersWorkflowParameters]):
    ...

    def as_tools(self) -> list[BaseTool]:
        return [
            StructuredTool(
                name="summarize_entity",
                description=(
                    "Summarize an entity from the knowledge graph. "
                    "Use when asked for information about an entity."
                ),
                func=lambda **kwargs: self.run(CustomersWorkflowParameters(**kwargs)),
                args_schema=CustomersWorkflowParameters,
            )
        ]
```

Tool names may hold letters, digits, `_` and `-`. The agent replaces any other character with `_`.

### Hand them to an agent

In the agent's `New()`, import the workflow and add its tools. Import inside `New()`: the engine treats every exposed class found in an `agents/` file as an agent.

```python title="agents/CustomersAgent.py"
tools: list = []

from my_module.workflows.CustomersWorkflow import (
    CustomersWorkflow,
    CustomersWorkflowConfiguration,
)

tools += CustomersWorkflow(CustomersWorkflowConfiguration()).as_tools()
```

The scaffolded `New()` lists the tools in the `[TOOLS]` block of the system prompt. The full method is on [Give it tools](/v2/capabilities/build/agents/give-it-tools).

### Give an agent another module's workflow

ABI 3.x, in a NATS project. A durable workflow of another module is started through a proxy, `self.engine.modules["<module id>"].get_workflow(name)`, with that module listed in `ModuleDependencies(modules=[...])`. The proxy's `run(input)` is a coroutine that starts a durable run and returns its output; from sync code, run it with `asyncio.run`. Engine agents call their tools synchronously, so wrap that call in a `StructuredTool` with a parameters model of your own:

```python title="agents/CustomersAgent.py"
import asyncio

from langchain_core.tools import StructuredTool
from pydantic import BaseModel, Field


class LookupParameters(BaseModel):
    item_id: str = Field(description="The ID of the item to look up")


def lookup_item(item_id: str) -> dict:
    from my_module import ABIModule

    proxy = ABIModule.get_instance().engine.modules["items"].get_workflow(
        "workflow.lookup_workflow"
    )
    return asyncio.run(proxy.run({"item_id": item_id}))


tools += [
    StructuredTool(
        name="lookup_item",
        description="Look up an item by its ID.",
        func=lookup_item,
        args_schema=LookupParameters,
    )
]
```

- The proxy is looked up when the tool runs, not when the agent is built: `get_workflow` raises `JobsNotHosted` until the module's job host has started, at the end of `on_initialized`, and always without NATS.
- `run` raises `WorkflowRunFailed` when the run did not succeed. Pass `timeout=` to bound the wait; the run goes on.
- Each call is a recorded run, with the workflow's limits: one run at a time for a published class.
- For the module's own workflow, `ABIModule.get_instance().get_workflow(name)` gives the same proxy.

### Restart and try it

Restart the engine or the module, then ask the agent for that intent in Nexus chat, or with `abi chat my_module CustomersAgent`. See [Test an agent](/v2/capabilities/build/agents/test-an-agent).

## REST API

Not applicable. A tool is agent-internal: the API gives a LangChain tool no route. To call the workflow over HTTP, see [Expose as an API](/v2/capabilities/build/workflows/expose-as-an-api).

## Config

Not applicable. Which tools an agent has is set in Python, in its `New()`, not in `config.yaml`.
