Tool Packs

A Tool Pack is a reusable library of tool definitions — each tool has a name, description, parameters, and its own visual implementation graph. Build your tools once in a Tool Pack project, then import the pack into any Agent or Server Graph project. No more duplicating tool schemas and handler code across projects.

Why Tool Packs?

Without Tool Packs, tools are defined in two places:

  • Agent projects — the Dispatch Tool node declares tool schemas (name + params) but the actual handler code lives outside Loom
  • Server Graph projects — the Tool Definition node declares schemas and wires handler logic inline on the canvas

If the same tool appears in both an agent and a server graph, you maintain two copies. Change the schema in one place and forget the other — things break silently.

Tool Packs solve this by centralizing tool definitions. Define the tool once with its full visual implementation, then import it everywhere.

Creating a Tool Pack

  1. On the Dashboard, click New Project and select Weave AI → Tool Pack
  2. Name your pack (e.g. "Database Tools", "Search Utils")
  3. The editor opens with a Tools panel on the left sidebar

The Tools Panel

The Tools panel lists all tools in your pack. Each tool has:

Field Description
Name The tool name (snake_case, e.g. search_web)
Description What the tool does — this is sent to the LLM
Parameters Typed input parameters (name + type)

To add a tool:

  1. Click + Add at the top of the Tools panel
  2. A new tool appears with a default name — double-click to rename
  3. Click the tool to select it — the description and parameter fields appear below
  4. Set the description (sent to the LLM) and add parameters with + param
  5. The canvas automatically navigates to the selected tool's implementation graph

Each tool has its own graph canvas. The breadcrumb shows Pack Name → Tool Name so you always know which tool you're editing.

Wiring a Tool Implementation

When you navigate into a tool's graph, you'll see a Tool Entry node already placed. This is the entry point for the tool's handler — it reads the tool's name, description, and parameters from the Tools panel (not from node state).

Wire your implementation between Tool Entry and Tool Return:

Tool Entry
  -> Get Tool Arg (extract parameters)
  -> ... your logic (HTTP requests, data transforms, agents, etc.)
  -> Tool Return (return the result)

For error cases, use Tool Error to return an error message instead:

Tool Entry
  -> Try/Catch
      |-- exec_try -> ... logic ... -> Tool Return
      |-- exec_catch -> Tool Error

All the same nodes available in Server Graph tool handlers are available here: flow control, data transforms, HTTP requests, file I/O, caching, and more.

Tool Entry Node

The Tool Entry node is auto-placed when you create a tool — you don't add it from the palette. It's the starting point of every tool implementation graph. The node is an entry node (exec out, no exec in) that provides a single data output:

  • input — The tool's parameters as an mcp.ToolInput object

Use Get Tool Arg downstream to extract individual parameters by name.

The node's header reflects the active tool — it shows the tool name as its title and the description as its subtitle. Parameters are displayed read-only in the node body. All of these are managed in the Tools panel sidebar, not on the node.

Generated Output

Clicking Generate on a Tool Pack project produces:

tools.py          <- all tool functions
requirements.txt  <- dependencies

Each tool becomes a plain Python function:

async def search_web(query: str, max_results: int = 10) -> Any:
    """Search the web for a query string."""
    # ... generated from your visual graph ...
    return results

async def fetch_page(url: str) -> Any:
    """Fetch and return the contents of a web page."""
    response = await httpx.AsyncClient().get(url)
    return response.text

Tool Pack output is a standalone Python module — you can import and use it directly without Loom. But the real power is importing packs into other projects.

Importing into an Agent Project

To use a Tool Pack's tools in an Agent:

  1. Open your Agent project
  2. Find the Dispatch Tool node in your agent's behavior graph
  3. Switch the mode toggle from Inline to Pack
  4. Select your Tool Pack project from the dropdown
  5. The node auto-populates with the pack's tool bindings

When the mode is set to Pack, the Dispatch Tool reads tool schemas and implementations from the referenced Tool Pack project. At codegen time, each pack tool becomes a Python function above the AgentRunner class, and the dispatch routing calls them by name.

The active persona's tool whitelist still applies — tools not on the whitelist are excluded from the generated output, even if they're in the pack.

Generated Agent Output with Tool Pack

# --- Tool Pack functions (from "Search Utils") ---

async def search_web(query: str, max_results: int = 10) -> Any:
    """Search the web for a query string."""
    ...

async def fetch_page(url: str) -> Any:
    """Fetch and return the contents of a web page."""
    ...

# --- Agent ---

TOOLS = [
    {"name": "search_web", "description": "Search the web", "input_schema": {...}},
    {"name": "fetch_page", "description": "Fetch a web page", "input_schema": {...}},
]

class AgentRunner:
    async def _dispatch_tool(self, name, input):
        if name == "search_web":
            return await search_web(**input)
        elif name == "fetch_page":
            return await fetch_page(**input)
    ...

Importing into a Server Graph

To use a Tool Pack's tools in a Server Graph:

  1. Open your Server Graph project
  2. Right-click the canvas and add a Tool Pack Import node
  3. Select your Tool Pack project from the dropdown
  4. The node shows a preview of all tools that will be imported

The Tool Pack Import node is a standalone node — it has no exec ports and doesn't connect to the flow graph. It acts as a declaration: "include all tools from this pack in the generated server." At codegen time, each pack tool becomes a @server.tool() MCP handler in server.py, alongside any inline tools you've defined with Tool Definition nodes.

Generated Server Output with Tool Pack

# --- Inline tools ---

@server.tool()
async def analyze(text: str) -> str:
    """Analyze text sentiment."""
    ...

# --- Tool Pack tools (from "Search Utils") ---

@server.tool()
async def search_web(query: str, max_results: int = 10) -> str:
    """Search the web for a query string."""
    ...

@server.tool()
async def fetch_page(url: str) -> str:
    """Fetch and return the contents of a web page."""
    ...

Inline tools and pack tools coexist in the same server. If a tool name appears in both an inline definition and a pack import, the inline definition takes precedence.

Best Practices

  • One pack per domain — group related tools together (e.g. "Database Tools", "Search Tools", "File Processing"). Don't put everything in one mega-pack.
  • Descriptive tool names — use clear snake_case names. The LLM reads these to decide when to call a tool.
  • Write good descriptions — the description field is sent to the LLM. Be specific about what the tool does, what it returns, and any constraints.
  • Type your parameters — choose the most specific type (int, bool, list) rather than defaulting to string. This helps the LLM generate correct arguments.
  • Test standalone first — generate the Tool Pack on its own and verify the output before importing into an agent or server graph.