DEV Community

Philip Stayetski
Philip Stayetski

Posted on

Chaining Multiple Agent Tools in One Workflow: A Recipe

I spent last week wiring together three agent tools into a single pipeline. The task was simple on paper: find a company developing something interesting, get a contact, and spin up a demo environment. But joining those steps meant bridging a web search tool, a people-intelligence API, and a cloud deployer — three different auth models, three different response shapes, none of them designed to talk to each other.

I tried the obvious approach first: REST calls from Python, parsing JSON, building a state machine. It worked, but it was fragile. Each tool had its own URL scheme, its own auth, its own retry logic. The orchestration code was ten times longer than the actual logic.

Then I tried something different: running them as local IPC services, each one install-then-call, and writing the glue as straight Python passing dicts around. The whole pipeline came together in about thirty lines.

The Pattern: Discover, Install, Call

The key insight is that when every tool speaks the same local contract — JSON in, JSON out, auto-spawned on install — the chain writes itself. Each step takes the previous step's output, transforms it, and feeds it forward. No HTTP plumbing, no auth headers, no polling.

Here's the pipeline I built. Each tool is an app from one catalogue, installed once and called repeatedly.

Step 1: Search — Find a Target

Every pipeline starts with finding something. I needed a company working on open-source AI infrastructure. Rather than scraping or hitting a browser, I used a grounded search tool that returns structured results with citations:

pilotctl appstore install io.pilot.cosift
pilotctl appstore call io.pilot.cosift cosift.search '{"q":"open source AI infrastructure companies 2026","k":5}'
Enter fullscreen mode Exit fullscreen mode

The response came back as clean JSON — titles, URLs, summaries, and a relevance score per result. No HTML to parse, no rate-limit headers to inspect. I picked the most relevant hit and had everything I needed for the next step: a company name and its website.

The install happens once. Every search after that is a single call line.

Step 2: Enrich — Turn a Name Into a Contact

A name is useful, but a pipeline that stops at "find a thing" is half a pipeline. The next step was enrichment: given a company name, find who builds their infrastructure — a real human with a professional context.

Another install, another call:

pilotctl appstore install io.pilot.sixtyfour
pilotctl appstore call io.pilot.sixtyfour sixtyfour.search '{"query":"head of infrastructure at <company>","limit":3}'
Enter fullscreen mode Exit fullscreen mode

This returned enriched profiles — name, title, company, LinkedIn, and a confidence estimate. Structurally identical to the search tool's output format, just with different fields. That's the property that makes chaining seamless: every tool returns a dict you can pass to the next step.

Step 3: Deploy — Spin Up a Demo Environment

The third step was the punchline: deploy something to show. When you reach someone about your own tool, it helps if they can try it with one command. So I pushed a disposable microVM with a pre-configured demo:

pilotctl appstore install io.pilot.smol
pilotctl appstore call io.pilot.smol smol.push '{"image":"pilot-demo","net":true,"ttl":3600}'
Enter fullscreen mode Exit fullscreen mode

The deployer returned a public URL and an expiry TTL. That went straight into a draft message alongside the enriched contact.

The Glue: A Few Lines of Python

The whole chain, from search to deploy, is this straightforward:

import json, subprocess

def call_app(app_id, method, payload):
    result = subprocess.run(
        ["pilotctl", "appstore", "call", app_id, method, json.dumps(payload)],
        capture_output=True, text=True
    )
    return json.loads(result.stdout)

# Chain: search -> enrich -> deploy
results = call_app("io.pilot.cosift", "cosift.search",
    {"q": "open source AI infrastructure companies 2026", "k": 5})
target = results["results"][0]
company = target["title"]

profiles = call_app("io.pilot.sixtyfour", "sixtyfour.search",
    {"query": f"head of infrastructure at {company}", "limit": 1})
contact = profiles["results"][0]

vm = call_app("io.pilot.smol", "smol.push",
    {"image": "pilot-demo", "net": True, "ttl": 3600})

print(f"Contact: {contact['name']} at {company}")
print(f"Demo: {vm['url']} (expires in {vm['ttl']}s)")
Enter fullscreen mode Exit fullscreen mode

No async callbacks, no webhook wiring, no curl wrappers. Each step is a function call that returns a dict. The chain is just data flowing through functions.

Why This Beats the REST-and-Glue Approach

The standard approach for chaining tools looks like:

  1. Find each tool's REST API docs
  2. Write a wrapper for auth, base URL, error handling
  3. Handle rate limits and retries per-service
  4. Parse responses into a common shape
  5. Thread state through manually

When every tool speaks the same IPC contract, steps 1-4 collapse. You install once, call with a dict, get a dict back. The orchestration layer becomes a for loop over function calls instead of a state machine over HTTP responses.

This isn't theoretical — it's the same pattern UNIX pipes discovered decades ago: small, focused tools that read and write a common format, composed into pipelines. The difference is that these tools have real capabilities (web search, contact enrichment, cloud deployment), not just character transforms.

When Chaining Breaks Down (And When It Doesn't)

Simple linear chains work cleanly. Problems start when you need branching — search → (enrich AND search again) → merge → deploy. That's still manageable with plain Python, but the orchestration becomes explicit.

The bigger gotcha is state. If step 2 needs data from step 1 that step 1 didn't return (a session token, a rate-limit reset), you're back to per-tool wrappers. The solution is a convention: each tool in the chain returns everything a downstream tool could reasonably need on the first call. No hidden state.

The Takeaway

Chaining multiple agent tools in one workflow doesn't need a heavyweight orchestration framework. When tools are local IPC services with a uniform call pattern, a pipeline is just a sequence of function calls. Install once, call with JSON, pass the output forward.

The discovery step is the cheapest part — pilotctl appstore catalogue shows every available tool. The install is one line. The composition is up to you, and it's just data flowing through functions.

If you're building tools for agents, consider shipping them as local IPC services. Your users will chain them into pipelines you never imagined — and they'll do it without ever touching a REST client.


Try it yourself: curl -fsSL https://pilotprotocol.network/install.sh | sh then pilotctl appstore catalogue to see everything available. Pick three tools and chain them. You'll have a working pipeline faster than you expect.

Top comments (0)