Errand Crew
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Errand Crew - an observable six agentic AI workflow
Alexa+ MCP server that sends parallel browser agents across online shops, compares options, and speaks the answer back.
The project was developed in 7 hours during the Amazon Developer Hackaton! (https://devpost.com/software/errand-crew)
The MCP server uses a six agentic AI workflow
A planner (Claude Sonnet 5.5) turns the spoken request into a plan.
Four worker agents (Claude Haiku 5.5) browse Evomag, Altex, MediaGalaxy and Vexio in parallel, each in its own cloud browser.
The judge picks the cheapest match in code. A model (Claude Haiku 5.5) only words the result for speech, so the spoken answer never depends on a model behaving.
You can watch the agents work. The browsers run on the Kernel cloud browser infrastructure, as shown in the demo video below. If you run the server yourself with your own Kernel key and KERNEL_HEADLESS=false, you can see the four browsers live in the Kernel dashboard.
How it works
Comparing prices means opening shop after shop and scrolling. Errand Crew does that part for you, while you just ask a question out loud.
Say you want a cheap phone. You ask your voice assistant, and from there it takes about half a minute! Isn't it great?
Demo Video
https://github.com/user-attachments/assets/65202194-5317-4fb7-adba-221c09a53a1d
https://www.youtube.com/watch?v=wEPq4M-xuaM
Related MCP server: Playwright JEV
Current limitations (TLDR)
Shops: Agent only searches prices on Evomag, Altex, MediaGalaxy and Vexio.
Products: Agent only searches headphones, TVs, phones, laptops and monitors.
Both limits are deliberate. The agents can only visit the hardcoded shops and category pages listed in src/errand_crew/sites.yaml, which is a security measure (they can never be sent to another site) and also makes each request faster, because the agents go straight to the right listing instead of searching the web.
The limits are easy to extend: add a shop, or a category page for a shop, to sites.yaml. Each new entry needs its category URLs, and, where the shop supports it, a price-sorting template. Check the shop's robots.txt and terms of service first. The registry validates every URL when the server starts.
Build and run
You need your own Anthropic API key and Kernel API key in both options.
Environment variables
Variable | Meaning |
| Your Claude API key |
| Your Kernel API key |
| One bearer token per client, as |
| Optional. |
Generate a token with any random string tool, for example openssl rand -hex 16.
Option 1: Deploy on Render
Fork or connect this repository in Render and create a Web Service from it.
Use these settings:
Setting
Value
Language
Python 3 (3.13)
Branch
mainBuild command
pip install -r requirements.txtStart command
PYTHONPATH=src python -m errand_crew.serverHealth check path
/healthAdd the four environment variables above in the service's Environment settings. For a hosted demo we recommend
KERNEL_HEADLESS=true.Deploy. When the service shows Live, the MCP endpoint is
https://<your-service>.onrender.com/mcp.
Render's free plan puts the service to sleep when idle, so the first request after a pause is slow. A paid instance avoids that. Render sets the allowed host name by itself.
Try it with the demo client (see below), using the /mcp URL of your service.
Option 2: Run locally
git clone https://github.com/kantorarthur/alexa_plus_errand_crew_six_agentic_AI_workflow.git
cd ./alexa_plus_errand_crew_six_agentic_AI_workflow
python -m venv .venvInstall the dependencies (on Windows use .venv\Scripts\python, on macOS and Linux use .venv/bin/python):
.venv/bin/python -m pip install -r requirements.txtSet the environment variables, then start the server. macOS and Linux:
export ANTHROPIC_API_KEY="..."
export KERNEL_API_KEY="..."
export ERRAND_CREW_TOKENS="demo:<your-random-token>"
export PYTHONPATH=src
.venv/bin/python -m errand_crew.serverWindows PowerShell:
$env:ANTHROPIC_API_KEY = "..."
$env:KERNEL_API_KEY = "..."
$env:ERRAND_CREW_TOKENS = "demo:<your-random-token>"
$env:PYTHONPATH = "src"
.venv\Scripts\python -m errand_crew.serverThe server listens on http://localhost:8000, with the MCP endpoint at /mcp and a health check at /health.
Try it: the demo client
The demo client behaves like a voice assistant: it starts an errand, polls its status and prints the spoken summary and the Claude usage. In a second terminal, set MCP_TOKEN to the token from ERRAND_CREW_TOKENS:
export MCP_TOKEN="<your-random-token>" # PowerShell: $env:MCP_TOKEN = "<your-random-token>"
.venv/bin/python scripts/demo_client.py "desired item search input" http://localhost:8000/mcpFor a Render deployment, pass its URL instead: https://<your-service>.onrender.com/mcp.
Run the tests
.venv/bin/python -m pip install -r requirements-dev.txt
.venv/bin/python -m pytest -qThe tests use a scripted model and a fake browser, so they need no network, no keys and cost nothing.
Dependencies
Python: 3.13 on Render (PYTHON_VERSION=3.13.5); the tests also run on 3.14.
Runtime (requirements.txt)
Package | Version | What it is used for |
| 4.1.3 | MCP server over Streamable HTTP, bearer-token auth hook, MCP client in the demo script |
| 2.1.0 | Claude API client (planner, workers and judge wording) |
| 0.129.0 | Kernel SDK: creates and deletes the cloud browsers and runs Playwright code in them |
| 2.14.1 | Validation of every data shape, including everything a model returns |
| 6.0.3 | Loads the shop registry ( |
| 0.54.0 | ASGI server that runs the app |
Standard library: asyncio, hmac, statistics, contextvars, dataclasses, urllib, re, json, logging.
Development (requirements-dev.txt)
Package | Version | Use |
| 9.1.1 | Test runner |
| 1.4.0 | Async tests |
| 0.28.1 | Test client |
One test runs a real Chromium through playwright to check the price-decimal fix. It is optional and is skipped when Playwright or Chromium is not installed.
External services
Service | Role | Credential |
Anthropic API | Claude Sonnet 5.5 (planner), Claude Haiku 5.5 (workers, judge wording) |
|
Kernel | Cloud browsers, one per shop |
|
Render | Hosting for the MCP server | none in the code; secrets are set in its dashboard |
Clients authenticate to the server with a bearer token from ERRAND_CREW_TOKENS. Nothing else is needed: no database, no queue, no extra cloud service.
Technical overview
Stack: Python, FastMCP (MCP over Streamable HTTP), the Anthropic API (Claude Sonnet 5.5 and Claude Haiku 5.5), Kernel.sh API, deployed on Render.
MCP tools (the interface)
Defined in src/errand_crew/server.py, served at /mcp over Streamable HTTP. Every call needs Authorization: Bearer <token>. All three tools are marked read-only: they never log in, buy or submit anything.
A voice assistant should call start_errand, poll get_status every few seconds, then read spoken_summary from get_result.
start_errand(request)
Starts comparing prices in the background and returns at once.
Input | Type | Rules |
| string | 3 to 300 characters, for example |
Returns:
{ "errand_id": "<32 hex characters>", "status": "queued", "message": "Started. Check get_status in a few seconds." }Errors: "I'm busy with other errands. Please try again shortly." (two errands already running), and "The errand limit has been reached. Please try again later." (20 per hour or 100 per day).
get_status(errand_id)
Progress of one errand.
Input | Type | Rules |
| string | The id from |
Returns:
{
"errand_id": "…",
"status": "running",
"category": "smartphones",
"progress": { "evomag": "ok", "altex": "running", "mediagalaxy": "ok", "vexio": "running" }
}status is one of queued, planning, running, judging, done, failed. Each shop in progress is pending, running, ok, empty (nothing matched), blocked (the shop refused automated access) or error. category is null until the planner has answered.
Error: "Unknown errand." for an id that is malformed or not found.
get_result(errand_id)
The finished comparison. Same input as get_status.
While the errand is still working: { "ready": false, "status": "<state>" }.
If it failed: { "ready": true, "status": "failed", "spoken_summary": "Sorry, I couldn't finish that errand.", "error": "…" }.
When done:
{
"ready": true,
"status": "done",
"request": "cheapest nokia phone",
"spoken_summary": "The cheapest Nokia phone I found is …",
"plan": { "supported": true, "category": "smartphones", "site_ids": ["evomag", "altex", "mediagalaxy", "vexio"], "focus": "nokia", "reason": "" },
"sites": [
{
"site_id": "evomag", "site_name": "evomag.ro", "source_url": "https://…",
"status": "ok", "pages_read": 1, "sorted_by_price": true, "error": "",
"offers": [ { "name": "…", "price": 79.99, "currency": "RON", "specs": "" } ]
}
],
"verdict": { "summary": "…", "best_site_id": "evomag", "best_offer_name": "…", "caveats": "…" },
"sites_asked": 4,
"sites_compared": 3,
"usage": { "calls": 7, "input_tokens": 36109, "output_tokens": 4386, "estimated_cost_usd": 0.0097 }
}spoken_summaryis the text to read aloud: at most 400 characters, no links, no markdown.Offers in each shop are matching products only, cheapest first.
sorted_by_priceistruewhen the shop's own cheapest-first order was used, so the first match is the cheapest in the category. Whenfalse, the price is the cheapest among the pages read.sites_askedandsites_comparedare counted in code, never by a model.usage.estimated_cost_usdis an estimate from token counts.If the request is not a product search,
plan.supportedisfalse,sitesis empty,verdictisnull, andspoken_summaryholds the reason.
Other endpoint
GET /health answers ok without a token, for the platform health check.
Where the contract is defined in the code
What | File |
Tool names, inputs, errors, |
|
Result shapes ( |
|
Errand lifecycle, limits and the busy and rate-limit errors |
|
Bearer-token check |
|
Pipeline
Planner (Sonnet 5.5). Maps the request to one category and a list of shops. Its output must pass a Pydantic schema and then a code check: the category and shop ids must exist in the registry (
sites.yaml), at most four shops are kept, and thefocusphrase is cleaned and capped at 80 characters.Workers (Haiku 5.5), in parallel. One
asynciotask per shop, each with its own Kernel browser and a hard timeout of 150 s. A worker opens up to three pages of the category listing (sorted by price where the shop supports it through its URL), extracts products as structured output, and keeps only offers whose name and price really appear in the page text.Judge. Plain code picks the cheapest offer in a single currency and builds the facts (which shops were checked, which were blocked or empty, whether the winning shop was read in price order). A model only rewords those facts for speech. Its sentence is used only if it names the same shop and the exact price and invents no other price; otherwise a template sentence is spoken.
Problems I encountered
Prices mapped strange: Evomag prints
79and99as separate elements, so plain text extraction turned a price of 79.99 into7999. Worse, our own safety check would have accepted it, because7999really was in the extracted text. A small script now fixes this inside the page before the text is read, and a test runs it in a real browser.Forced tool choice is not available on Sonnet 5.5. The API rejected our first approach (forcing the model to call a tool) with a 400 error. Structured output now uses automatic tool choice, an instruction to call the tool exactly once, and strict validation. Plain text or an invalid answer raises an error instead of being trusted.
The judge model dropped good results. In early runs the judge sometimes returned nothing, and the assistant answered "I couldn't find matching listings" even though the workers had found offers. We moved the decision into code (the cheapest offer, which shops were checked) and let the model only reword it. If its wording disagrees with the data, a template sentence is spoken instead.
The cheapest product was not on the first page. Category pages hold hundreds of products in default order, so "cheapest Samsung phone" could come back empty at a shop that did stock one. We now open each shop's price-ascending listing and read up to three pages, and the answer says when a shop was only checked over its first pages.
A site rule limited our options. Evomag's
robots.txtdisallows the sorted second page, so Evomag is read on its first page only, and we respect that. Vexio cannot be sorted by link at all and, once, answered one of its pages with an access error. The result keeps what was read earlier and states which shop could not be checked.A shop sometimes blocked a run. Evomag once reported itself as blocked in one run and worked in the next, for a reason we could not determine. We log the HTTP status and size of every page we treat as blocked, report the shop as blocked in the answer, and do not try to get around it.
Cost is measured, not guessed. Every Claude call is counted from the API's own usage fields. The result reports calls, tokens and an estimated cost (about one US cent per request in our tests).
Security model: prompt injection and AI hallucinations
Errand Crew reads web pages written by strangers and lets language models handle that text, which is exactly where prompt injection and AI hallucinations come from. We do not rely on a model behaving well. The limits are in code.
Protection against prompt injection
Both direct prompt injection (a malicious spoken request) and indirect prompt injection (instructions hidden in a shop page) are covered:
Least privilege. The models have no tools, no URLs and no browser control. They only receive text and return data. Even a fully fooled model cannot browse elsewhere, click, log in or post anything.
URL allowlist, enforced twice. Every URL is built from a fixed shop registry and checked against an allowlist in the server and again inside the cloud browser, which blocks any other navigation. Neither the user nor a model can supply a URL.
Untrusted input is marked as data. The request and the page text are wrapped as untrusted content, and the models are told never to follow instructions found in them.
Validated plans. The planner's answer must pass a schema and a code check: only categories and shops from the registry are accepted, at most four shops are used, and the focus phrase is cleaned and capped.
Early screening. Obvious attack text in a request ("ignore all previous instructions", "waste tokens") is turned away before any model is called. This is only an extra layer, since a rephrased attack can get past a pattern, so the limits above are what we depend on.
Bounded inputs and outputs. Requests are limited to 300 characters, page text to 15,000 characters, and every model call has a token cap, so an attack cannot make a request expensive.
Protection against AI hallucinations
Grounding. A product is kept only if its name and price really appear in the page text. A model cannot invent a listing.
The code decides, not the model. The cheapest offer is chosen in code. A model only puts the result into words, and its sentence is used only if it names the same shop and the exact price and invents no other price, no link and no markdown. Otherwise a template sentence is spoken.
Strict structured output. Every model answer is validated against a schema. Plain text or an invalid answer is rejected, not trusted.
Plausibility check. A fake listing on a page would pass grounding, because its text is really there. So a price far below all other offers is flagged in the spoken answer, in the code's own words.
Honest coverage. The answer says what could not be checked: a blocked shop, an empty result, or a shop read only over its first pages.
Access and abuse control
A per-client bearer token is required (compared in constant time). Each client has its own token, so one can be revoked without affecting the others.
Rate limits: two errands at once, 20 per hour and 100 per day.
The service is read-only: it never logs in, adds to a cart or submits a form.
No secrets live in the repository. Keys and tokens come from environment variables.
These measures reduce the risk, they do not remove it. A convincing fake price on a page can still reach the answer, which is why it comes with a warning when it looks out of line.
Tests
91 automated tests run without network or cost, using a scripted model and a fake browser. They cover the planner, workers, judge, URL allowlist, pagination, the Claude wrapper, rate limits, and prompt-injection attempts in both the request and a shop page.
Known limits
Four shops and five categories. State and rate-limit counters are in memory and reset when the server restarts. Prices are read at request time and may be out of date. Shop terms of service should be checked before running at scale. The server is built to Alexa+'s MCP requirements, but we tested it with an MCP client that simulates a voice assistant, not on an Alexa+ device.
Co-Author: Claude Code.
This server cannot be deployed
Maintenance
Related MCP Connectors
Autonomous cloud agent tasks: real browser + your tools, structured evidence-backed results.
Policy review and purchase discovery for AI-agent commerce actions.
Where AI agents buy things for people: real US stores, delivered price and date, one order.
Related MCP Servers
- AlicenseAqualityBmaintenanceEnables AI assistants to control a web browser through Playwright-compatible tools, with an on-device decision engine for autonomous task completion and optional LLM escalation.25MIT
- AlicenseBqualityBmaintenanceEnables an LLM to drive a Chromium browser autonomously from high-level business goals expressed as short intents, automatically handling cookie/consent overlays, loops, and escalations, while exposing only seven semantic tools instead of low-level DOM commands. Keeps the accessibility tree out of the orchestrator's context, cutting token consumption by up to 99.8% on complex e-commerce sites.259,849,970 npmApache 2.0
- AlicenseBqualityAmaintenanceGives agents an autonomous browser that takes a single goal-level call and drives a real Chrome step by step, returning a verified result or a concrete question instead of guessing. It keeps passwords out of prompts, requires approval before buy/pay/delete actions, and detects login walls and bot challenges.98 npm2MIT
- AlicenseNot gradedqualityBmaintenanceEnables users to achieve high-level life and commerce goals by decomposing them into autonomous steps, such as planning trips, searching Prime-eligible products, optimizing a budget cart, and authorizing purchases through human-in-the-loop confirmation.MIT