Burrowbox Blog
← All posts

Project Mariner runs ten tasks at once on cloud VMs. Fan out your own agents with LangChain

· The Burrowbox team · 3 min read

#What Project Mariner does

Project Mariner is Google's agent that browses and acts on websites for people. At I/O in May 2025, Google said the new version runs on virtual machines in the cloud. That lets it handle up to 10 tasks at once while people get on with other work. It launched first for U.S. subscribers to the $249.99-a-month AI Ultra plan. (TechCrunch)

#The pattern

When the agent runs in the user's own browser, it does one thing at a time, and it stops when the laptop closes. Moving it to cloud machines means one machine per task. Tasks run side by side, can't interfere with each other, and keep going in the background. The machines have to be ready quickly, though, or the parallelism goes into waiting for boots.

#Build it with LangChain and Burrowbox

Here, a Python job takes a list of tasks, claims a ready Burrowbox machine for each one, and runs a LangChain agent on each machine at the same time. Burrowbox isn't involved with Google.

#1. Keep machines warm

A warm pool keeps booted machines waiting, so a claim returns one instantly. You can also give it a template, so each machine is already set up (here, the browser is already open on the site).

curl -X POST https://burrowbox.dev/api/pools \
  -H "Authorization: Bearer $BURROWBOX_KEY" -H "Content-Type: application/json" \
  -d '{"name": "browse", "size": "tiny", "target": 5,
       "setup": [{"type": "tool", "tool": "browser_navigate", "arguments": {"url": "https://shop.example.com"}}]}'
# → { "id": "pool_3fa91c", "ready": 0, "warming": 5, … }

#2. Fan out

pip install langchain-mcp-adapters langgraph "langchain[anthropic]" httpx

import asyncio, os
import httpx
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent

BB = "https://burrowbox.dev"
POOL_ID = os.environ["POOL_ID"]
auth = {"Authorization": f"Bearer {os.environ['BURROWBOX_KEY']}"}

async def run_task(http: httpx.AsyncClient, task: str) -> str:
    # A fresh machine for this task; it's deleted afterwards (and destroyed on expiry as a backstop).
    m = (await http.post(f"{BB}/api/pools/{POOL_ID}/claim", headers=auth,
                         json={"ttlMinutes": 30, "onExpire": "destroy"})).json()
    try:
        mcp = MultiServerMCPClient({
            "machine": {
                "transport": "http",
                "url": m["mcpUrl"],
                "headers": {"Authorization": f"Bearer {m['mcpToken']}"},
            }
        })
        agent = create_agent("anthropic:claude-opus-5-5", await mcp.get_tools())
        result = await agent.ainvoke({"messages": task})
        return result["messages"][-1].content
    finally:
        await http.delete(f"{BB}/api/machines/{m['id']}", headers=auth)

async def main():
    tasks = [
        "Find the price of the 2 kg bag of espresso beans on shop.example.com",
        "Check whether shop.example.com ships to Portugal and what it costs",
        "List the three newest grinders on shop.example.com with prices",
    ]
    async with httpx.AsyncClient(timeout=120) as http:
        for task, answer in zip(tasks, await asyncio.gather(*(run_task(http, t) for t in tasks))):
            print(f"## {task}\n{answer}\n")

asyncio.run(main())

Each agent gets the machine's tools (browser_navigate, browser_snapshot, browser_click, browser_fill, shell_run, …), scoped to that machine by its token.

Some details from the docs:

  • Claims never reuse a machine. A claimed machine is brand new, and the pool refills itself in the background. If the pool is empty, the claim boots a new machine in a few seconds ("fromPool": false).
  • Tag claims with externalId if the tasks belong to different customers. GET /api/usage?groupBy=externalId then shows what each customer cost.
  • To watch a run, create a live-view link for any machine. Use mode: "browser" to see just the browser.

#What it costs

Idle pool machines are billed like running ones: a pool of 5 tiny machines costs about $0.35 an hour. Each task machine costs $0.07 an hour, charged by the minute, until you delete it. Set the pool's target to match how many tasks you usually run at once, or 0 outside working hours (PATCH /api/pools/{id}). See Billing.

Create an account to try it.

#Sources