Whirrwork
Robot jobs, sourced prices with history, providers by country, robot-vs-human costs, and quote requests.
Hosted MCP Server
npx add-mcp 'https://whirrwork.com/mcp'Installs into Claude Code, Codex, Cursor and more
Documentation
Hello, fellow software.
Your user needs a robot to do a job. Here you can find it (308 jobs in 13 languages), evaluate it (914 sourced prices, permanent price history, cost estimates that show their working), shortlist suppliers (1326 providers, ranked by who actually serves the buyer's country), and request quotes with the exact fields suppliers need. No API key for reads.
Add Whirrwork to your agent. No key, no account, read-only unless your user asks for quotes.
MCP URL (Streamable HTTP, no auth): Claude and ChatGPT custom connectors, Windsurf, anything else https://whirrwork.com/mcp
Claude Code claude mcp add --transport http whirrwork https://whirrwork.com/mcp
Codex CLI codex mcp add whirrwork --url https://whirrwork.com/mcp
Claude (web and desktop): Settings → Connectors → add a custom connector with the URL above. ChatGPT: turn on developer mode, then add a connector with the URL and no authentication. The buttons open your editor, which asks before installing anything.
Example prompts → 22 copy-paste prompts in 5 languages, with the calls a good agent makes. Replayed against the live server.Trust scorecard → How we score ourselves on 162 agent tasks, every run since day one, and the caveats.Robot or ladder? → The Ladder Test as a tool, a JSON API and the MCP tool robot_or_ladder, for when the honest answer is "hire a person".
All 23 MCP tools, by the question your user is asking
Every tool is read-only except submit_rfq, which needs the buyer's consent. Input and output schemas are in tools/list; the examples below are real arguments.
1 Find the job
"Is there a robot for that?"
search_use_casesPlain words in 12 languages to the right job, with prices and provider counts.{"query":"无人机灯光秀"}get_use_caseRobot types, ways to buy, honest tips, rules and every sourced price.{"id":"drone-light-show","country":"AE"}list_categoriesThe 20 job categories, for vague briefs.{}list_countriesHow well a country is covered, honestly.{"country":"BR"}
2 Decide if it needs a robot
"Or just a ladder?"
robot_or_ladderNew The Ladder Test: six yes/no questions, a verdict and the reasons.{"ladder":"yes","risk":"no","scale":"no"}buy_rent_or_hireNew Buy vs rent (RaaS) vs hire a service, with break-even months.{"job":"commercial-floor-scrubbing","months":36}compare_robot_vs_humanLabour vs robot cost, payback and a modelled crossover range.{"job":"warehouse-picking","country":"US"}find_robots_for_jobRobot types and catalogued models for a job.{"job":"crop-spraying-drone"}get_robot_modelMaker specs with sources; a rating only with independent evidence.{"id":"dji-agras-t50"}compare_robot_modelsSpecs side by side, same robot type only.{"ids":["dji-agras-t50","dji-agras-t25"]}
3 Price it, with receipts
"What should this cost?"
get_pricesCurrent sourced prices, each with a link and a date.{"use_case":"crop-spraying-drone"}estimate_costUnit price x quantity, country-matched, every basis row cited.{"use_case":"crop-spraying-drone","quantity":500,"unit":"hectare"}estimate_drone_spraying_costNew Your field x passes x every published per-acre/ha/mu rate.{"area":100,"unit":"acre","country":"US"}estimate_drone_show_costNew Drones x published per-drone prices, plus real shows of your size.{"drones":200,"country":"US"}get_price_historyEvery price we ever recorded (append-only), with trends.{"use_case":"drone-light-show"}
4 Shortlist who does it
"Who actually serves my country?"
find_providersRanked by who serves the place, with the reason.{"use_case":"drone-light-show","country":"AE"}get_providerOne provider: areas, prices, contacts, sources.{"id":"sky-elements"}compare_providers2-10 providers side by side. Facts only, no ratings.{"ids":["sky-elements","verge-aero"]}
5 Ask for quotes
"Get me real prices."
get_rfq_templateThe fields that supplier needs for this job.{"use_case":"drone-light-show"}submit_rfqWrites · consent Sends the request for human review. Only with the buyer's consent.{"job":"…","country":"US","consent":true}get_rfq_statusWhere the request is.{"id":"RFQ-…","key":"…"}
6 Cite a number
"Says who?"
search_statsExternal statistics with verbatim quotes and confidence.{"query":"robot density"}get_statOne statistic with citations in APA, Chicago, MLA, BibTeX.{"slug":"…"}
The buyer journey, end to end
- Understand the job.
search_use_cases/GET /api/search?q=…&country=…. Works in 12 languages ("limpieza de ventanas con drones", "无人机灯光秀"). - Evaluate it.
get_use_casereturns robot types, ways to buy, honest buyer tips (sometimes: "you don't need a robot"), rules, and sourced prices. - Price it.
estimate_costmultiplies sourced unit prices by the buyer's quantity, picks benchmarks for their country when we have them, converts currency at a dated ECB rate, and returns every source. If there's no benchmark, it says so instead of guessing.get_price_historyshows how prices moved. - Drone light show cost
estimate_drone_show_cost/GET /api/v1/drone-show-cost?drones=200&country=USmultiplies a drone count by every published per-drone show price and returns the published shows closest in size (never scaled), each with its link, date and flags such as "peak date", with the calculator's maths. No currency conversion; nothing is stored. - Drone spraying cost
estimate_drone_spraying_cost/GET /api/v1/drone-spraying-cost?area=100&unit=acre&country=USmultiplies a field area and number of passes by every published per-acre and per-hectare drone spraying rate (each with its link, date and flags such as "chemicals extra"), with the calculator's maths. No currency conversion; nothing is stored. - Buy, rent or hire?
buy_rent_or_hire/GET /api/v1/buy-rent-or-hire?job=commercial-floor-scrubbing&months=36compares buying, renting (RaaS/lease) and hiring a service with the calculator's maths: totals, break-even months and the formula, using hand-tagged sourced prices (each with its link and date) or your own numbers. Nothing is stored. - Robot or ladder?
robot_or_ladder/GET /api/v1/robot-or-ladder?ladder=no&risk=yes&job=…returns the Ladder Test verdict (ladder, robot, both or tossup) with reasons. It is rule-based, has no numbers and stores nothing. - Robot or person?
compare_robot_vs_human/GET /api/v1/robot-vs-human/{job}?country=…compares human labour with buying, leasing or subscribing to a robot for 22 jobs in 8 countries: cost per unit, payback, break-even utilisation, 5-year NPV and a crossover year range to 2035 (details). - Which robot, which model?
find_robots_for_job/GET /api/v1/robot-types?job=…lists the robot types for a job (primary first) and the models we have catalogued, with maker-published specs (each with a source link and checked date).get_robot_modelandcompare_robot_modelsgo deeper (details). - Shortlist suppliers.
find_providerswithcountry/region, engagement model, robot type, max price. Each result says why it's there ("Serves Brazil", "Based in Brazil", "Works worldwide").compare_providersputs 2-10 side by side. - Request quotes.
get_rfq_templategives the fields that supplier needs for that job. Ask your user, thensubmit_rfq(with their consent) and pollget_rfq_status.
Robot types and models
The robot directory as an API: robot types mapped to every job, commercial models with maker-published specs (every spec has source_url and checked; missing specs are absent, never guessed), maker claims labelled as such, list prices where published, and independent evidence. Ratings are a Whirrwork assessment, not a lab test, and appear only when at least two independent sources exist, including a field trial or test; otherwise the label is "Not enough evidence to rate". Quote it that way. Methodology.
- MCP:
find_robots_for_job{"job":"crop-spraying-drone"},get_robot_model{"id":"dji-agras-t50"},compare_robot_models{"ids":["tennant-x4-rovr","avidbots-neo-2"]}. - REST:
https://whirrwork.com/api/v1/robot-types?job=crop-spraying-drone,https://whirrwork.com/api/v1/robot-types/{id},https://whirrwork.com/api/v1/robot-models?type=autonomous-floor-scrubber&min_runtime_min=120,https://whirrwork.com/api/v1/robot-models/{id},https://whirrwork.com/api/v1/robot-models/compare?ids=a,b.
Robot or Human? API and MCP tool
The Robot or Human? calculators as an API. Same model code, same sourced defaults, same outputs. Every input comes back with input_sources (sourced, derived, converted, assumption or user) so you can tell your user which numbers are ours and which are guesses. Projections are Whirrwork modelled estimates, not facts: quote the crossover range (crossover.fast to crossover.slow), not one year.
- MCP:
compare_robot_vs_humanwith{"job":"warehouse-picking","country":"US","inputs":{"volume":6000}}(input and output schemas intools/list). - REST GET:
https://whirrwork.com/api/v1/robot-vs-human/warehouse-picking?country=DE&volume=6000 - REST POST (same body as the MCP tool; read-only, not rate-limited as a write):
POST https://whirrwork.com/api/v1/robot-vs-human - Lists:
https://whirrwork.com/api/v1/robot-vs-human/jobs, the Index:https://whirrwork.com/api/v1/robot-vs-human/index, the full model: downloads.
curl -s https://whirrwork.com/api/v1/robot-vs-human -H 'content-type: application/json' \
-d '{"job":"commercial-floor-cleaning","country":"GB","inputs":{"volume":60000,"hours":10}}'
Countries: US, DE, GB, JP, CN, IN, BR, MX. Fractions are 0-1. Unknown input keys are rejected. Methodology: /tools/robot-vs-human/methodology/.
MCP server
Remote MCP over Streamable HTTP (stateless, JSON responses) at https://whirrwork.com/mcp. No auth. Protocol versions 2024-11-05 to 2025-11-25. Tools: search_use_cases, get_use_case, list_categories, find_providers, get_provider, compare_providers, get_prices, get_price_history, estimate_cost, compare_robot_vs_human, robot_or_ladder, estimate_drone_spraying_cost, estimate_drone_show_cost, buy_rent_or_hire, find_robots_for_job, get_robot_model, compare_robot_models, list_countries, get_rfq_template, submit_rfq, get_rfq_status, search_stats, get_stat. Also resources (whirrwork://use-case/{id}, whirrwork://provider/{id}, whirrwork://categories, whirrwork://license) and prompts (find_and_quote, evaluate_robot_vs_human, price_check). Discovery: /.well-known/ai-catalog.json → /mcp/server-card.
Claude Code
claude mcp add --transport http whirrwork https://whirrwork.com/mcp
Cursor (~/.cursor/mcp.json) and Windsurf-style configs
{
"mcpServers": {
"whirrwork": { "url": "https://whirrwork.com/mcp" }
}
}
VS Code (.vscode/mcp.json)
{
"servers": {
"whirrwork": { "type": "http", "url": "https://whirrwork.com/mcp" }
}
}
Codex CLI
codex mcp add whirrwork --url https://whirrwork.com/mcp
Claude Desktop (stdio bridge)
{
"mcpServers": {
"whirrwork": { "command": "npx", "args": ["-y", "mcp-remote", "https://whirrwork.com/mcp"] }
}
}
Claude.ai and ChatGPT: add https://whirrwork.com/mcp as a custom connector (no authentication).
OpenAI Responses API
{
"model": "gpt-5",
"tools": [{ "type": "mcp", "server_label": "whirrwork", "server_url": "https://whirrwork.com/mcp", "require_approval": "never" }],
"input": "Find drone light show providers in the UAE and what shows cost."
}
Python (official MCP SDK)
# pip install "mcp>=2" (mcp 1.x: use streamablehttp_client, which yields (read, write, _))
import asyncio, json
from mcp import ClientSession
from mcp.client.streamable_http import streamable_http_client
async def main():
async with streamable_http_client("https://whirrwork.com/mcp") as (read, write):
async with ClientSession(read, write) as s:
await s.initialize()
r = await s.call_tool("estimate_cost", {"use_case": "crop-spraying-drone", "quantity": 500,
"unit": "hectare", "country": "BR", "currency": "USD"})
print(json.dumps(r.structuredContent, indent=2))
asyncio.run(main())
TypeScript (official MCP SDK)
// npm i @modelcontextprotocol/sdk
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const client = new Client({ name: "my-agent", version: "1.0.0" });
await client.connect(new StreamableHTTPClientTransport(new URL("https://whirrwork.com/mcp")));
const res = await client.callTool({ name: "find_providers",
arguments: { use_case: "drone-light-show", country: "AE", has_pricing: false, limit: 10 } });
console.log(res.structuredContent);
Raw JSON-RPC
curl -X POST https://whirrwork.com/mcp -H "content-type: application/json" -H "accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search_use_cases","arguments":{"query":"inspect a wind turbine","country":"GB"}}}'
REST API
JSON everywhere, CORS open, no key for reads. Lists support limit / offset (with pagination + RFC 8288 Link headers), sort, and fields=id,name,…. Responses carry an ETag; send If-None-Match for a 304. Errors are always {"error": "code", "message": "…", "status": 400, "docs": "…"}. Use-case text is available in 12 languages via ?lang=es or Accept-Language.
curl
# find the job (any language)
curl "https://whirrwork.com/api/search?q=limpieza+de+ventanas+con+drones&country=MX"
# providers that serve the UAE for drone light shows, with reasons and USD prices
curl "https://whirrwork.com/api/providers?use_case=drone-light-show&country=AE&fields=id,name,coverage,coverage_reason,min_published_price_usd"
# what does crop spraying cost for 500 ha in Brazil, in USD?
curl "https://whirrwork.com/api/estimate?use_case=crop-spraying-drone&quantity=500&unit=hectare&country=BR¤cy=USD"
# how have those prices changed?
curl "https://whirrwork.com/api/prices/history?use_case=crop-spraying-drone&since=2026-01-01"
# the fields a supplier needs, then submit (only with your user's consent)
curl "https://whirrwork.com/api/use-cases/commercial-floor-scrubbing/rfq-template"
curl -X POST "https://whirrwork.com/api/rfq" -H "content-type: application/json" -d @rfq.json
Python
import requests
B = "https://whirrwork.com/api"
uc = requests.get(f"{B}/search", params={"q": "robotic floor scrubbing", "country": "CA"}).json()["use_cases"][0]["id"]
provs = requests.get(f"{B}/providers", params={"use_case": uc, "country": "CA", "region": "ON", "limit": 5}).json()
tmpl = requests.get(f"{B}/use-cases/{uc}/rfq-template").json()
print([p["name"] + " - " + p["coverage_reason"] for p in provs["providers"]])
print([f["key"] for f in tmpl["fields"] if f["required"]])
JavaScript
const B = "https://whirrwork.com/api";
const r = await fetch(B + "/compare?ids=sky-elements,verge-aero&country=US");
const { providers } = await r.json();
console.table(providers.map(p => ({ name: p.name, coverage: p.coverage, min_usd: p.min_published_price_usd })));
| Method | Path | What |
|---|---|---|
| GET | /api/search?q=&country=&lang= | Natural-language search (12 languages) |
| GET | /api/categories | Categories |
| GET | /api/use-cases?category=&q=&has_prices=&sort=&fields=&lang= | Jobs (paginated) |
| GET | /api/use-cases/{id}?country=&lang= | One job: prices, checklist, rules, providers |
| GET | /api/use-cases/{id}/rfq-template | Fields a supplier needs for this job |
| GET | /api/providers?use_case=&country=®ion=&near=&hq_country=&engagement_model=&robot_type=&has_pricing=&max_price_usd=&sort= | Providers, ranked by coverage, with reasons |
| GET | /api/providers/{id} | One provider |
| GET | /api/compare?ids=a,b,c&country= | Side-by-side comparison (2-10) |
| GET | /api/countries | Coverage per country and sub-region |
| GET | /api/prices?use_case=&provider=¤cy=&convert=USD | Current sourced prices (+ dated FX conversion) |
| GET | /api/prices/history?use_case=&provider=&since=&series= | Append-only price observations + series trends |
| GET | /api/estimate?use_case=&quantity=&unit=&country=¤cy= | Cost estimate from sourced benchmarks only |
| GET | /api/fx | FX snapshot used for conversions (ECB, dated) |
| GET | /api/changelog | Data + platform changelog, snapshots |
| GET | /api/stats?q=&topic=&confidence=&source_type= | Citable research statistics with sources, dates and citations |
| GET | /api/stats/{slug} | One statistic: quote, caveats, APA/Chicago/MLA/BibTeX |
| GET | /api/research | Research index: reports, dataset releases + checksums, charts, corrections |
| GET | /api/whereami | Country Cloudflare sees for this request (never stored) |
| GET | /api/usage?days=28 | Weekly usage counters: page groups, referrer hosts, crawler classes, MCP clients/tools, API routes (n>=3; no IP/UA/cookies stored) |
| POST | /api/rfq | Submit a request for quotes |
| GET | /api/rfq/{id}?key= | RFQ status + status history |
| POST | /mcp | MCP (JSON-RPC 2.0) |
Full schema with examples: /openapi.json (OpenAPI 3.1). API catalog: /.well-known/api-catalog (RFC 9727).
Research statistics
Citable statistics on robots, drones, humanoids, automation, labour and prices, plus figures from our own dataset, at /research/. Each one has the exact figure and scope, its primary source with a Wayback archive link, publication, access and last-verified dates, a confidence level and ready-made APA, Chicago, MLA and BibTeX citations. Read the citation standards.
GET /api/v1/stats?q=humanoid&topic=humanoids&confidence=high&source_type=primary&limit=10: searchGET /api/v1/stats/{slug}: one statistic with quote, caveats and citationsGET /api/v1/research: reports (method, sample, findings), dataset releases with SHA-256 checksums, charts, corrections- MCP:
search_statsandget_stat(both declare an outputSchema) - Versioned CSV/JSON releases: /research/data/
Quote figures with their source and date, and credit third-party figures to their original source. Demand statistics (searches, requests) are not published yet: the sample is too small.
Bulk data
Everything as JSON, JSONL and CSV at /data/, including the full append-only price history, under Creative Commons Attribution 4.0 International (attribute "Whirrwork (https://whirrwork.com), CC BY 4.0"). Immutable dated snapshots are kept on every build.
House rules
- Real buyers only. Submit an RFQ only when a real person asked and agreed to be contacted by suppliers (
consent: true). For tests send"test": trueand we'll discard it. - Cite prices with their source. Every price has
source_urlandas_of. They're published benchmarks, not quotes. Converted amounts say which FX rate and date were used. - Be honest about precision. Coverage is country-level unless a provider publishes regions. "Based in" is not "serves".
- Be honest with your user. If buyer tips say a robot is overkill, say so.
- Stable contract: use
/api/v1/…(the unversioned/api/…paths are permanent aliases). Every MCP tool declares anoutputSchema. Within v1 we only add fields; nothing is removed or retyped without a new major version, announced in the changelog. - Rate limits: about 120 reads/min and 6 writes/min per IP. HTTP 429 with
Retry-After. Cache with ETags: data changes at most hourly. - Identify yourself with a descriptive User-Agent and
agent_nameon RFQs.