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_cases Plain words in 12 languages to the right job, with prices and provider counts.{"query":"无人机灯光秀"}
  • get_use_case Robot types, ways to buy, honest tips, rules and every sourced price.{"id":"drone-light-show","country":"AE"}
  • list_categories The 20 job categories, for vague briefs.{}
  • list_countries How well a country is covered, honestly.{"country":"BR"}

2 Decide if it needs a robot

"Or just a ladder?"

  • robot_or_ladder New The Ladder Test: six yes/no questions, a verdict and the reasons.{"ladder":"yes","risk":"no","scale":"no"}
  • buy_rent_or_hire New Buy vs rent (RaaS) vs hire a service, with break-even months.{"job":"commercial-floor-scrubbing","months":36}
  • compare_robot_vs_human Labour vs robot cost, payback and a modelled crossover range.{"job":"warehouse-picking","country":"US"}
  • find_robots_for_job Robot types and catalogued models for a job.{"job":"crop-spraying-drone"}
  • get_robot_model Maker specs with sources; a rating only with independent evidence.{"id":"dji-agras-t50"}
  • compare_robot_models Specs side by side, same robot type only.{"ids":["dji-agras-t50","dji-agras-t25"]}

3 Price it, with receipts

"What should this cost?"

  • get_prices Current sourced prices, each with a link and a date.{"use_case":"crop-spraying-drone"}
  • estimate_cost Unit price x quantity, country-matched, every basis row cited.{"use_case":"crop-spraying-drone","quantity":500,"unit":"hectare"}
  • estimate_drone_spraying_cost New Your field x passes x every published per-acre/ha/mu rate.{"area":100,"unit":"acre","country":"US"}
  • estimate_drone_show_cost New Drones x published per-drone prices, plus real shows of your size.{"drones":200,"country":"US"}
  • get_price_history Every price we ever recorded (append-only), with trends.{"use_case":"drone-light-show"}

4 Shortlist who does it

"Who actually serves my country?"

  • find_providers Ranked by who serves the place, with the reason.{"use_case":"drone-light-show","country":"AE"}
  • get_provider One provider: areas, prices, contacts, sources.{"id":"sky-elements"}
  • compare_providers 2-10 providers side by side. Facts only, no ratings.{"ids":["sky-elements","verge-aero"]}

5 Ask for quotes

"Get me real prices."

  • get_rfq_template The fields that supplier needs for this job.{"use_case":"drone-light-show"}
  • submit_rfq Writes · consent Sends the request for human review. Only with the buyer's consent.{"job":"…","country":"US","consent":true}
  • get_rfq_status Where the request is.{"id":"RFQ-…","key":"…"}

6 Cite a number

"Says who?"

  • search_stats External statistics with verbatim quotes and confidence.{"query":"robot density"}
  • get_stat One statistic with citations in APA, Chicago, MLA, BibTeX.{"slug":"…"}

The buyer journey, end to end

  1. Understand the job. search_use_cases / GET /api/search?q=…&country=…. Works in 12 languages ("limpieza de ventanas con drones", "无人机灯光秀").
  2. Evaluate it. get_use_case returns robot types, ways to buy, honest buyer tips (sometimes: "you don't need a robot"), rules, and sourced prices.
  3. Price it. estimate_cost multiplies 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_history shows how prices moved.
  4. Drone light show cost estimate_drone_show_cost / GET /api/v1/drone-show-cost?drones=200&country=US multiplies 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.
  5. Drone spraying cost estimate_drone_spraying_cost / GET /api/v1/drone-spraying-cost?area=100&unit=acre&country=US multiplies 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.
  6. Buy, rent or hire? buy_rent_or_hire / GET /api/v1/buy-rent-or-hire?job=commercial-floor-scrubbing&months=36 compares 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.
  7. 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.
  8. 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).
  9. 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_model and compare_robot_models go deeper (details).
  10. Shortlist suppliers. find_providers with country / region, engagement model, robot type, max price. Each result says why it's there ("Serves Brazil", "Based in Brazil", "Works worldwide"). compare_providers puts 2-10 side by side.
  11. Request quotes. get_rfq_template gives the fields that supplier needs for that job. Ask your user, then submit_rfq (with their consent) and poll get_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_human with {"job":"warehouse-picking","country":"US","inputs":{"volume":6000}} (input and output schemas in tools/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&currency=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 })));
MethodPathWhat
GET/api/search?q=&country=&lang=Natural-language search (12 languages)
GET/api/categoriesCategories
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-templateFields a supplier needs for this job
GET/api/providers?use_case=&country=&region=&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/countriesCoverage per country and sub-region
GET/api/prices?use_case=&provider=&currency=&convert=USDCurrent 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=&currency=Cost estimate from sourced benchmarks only
GET/api/fxFX snapshot used for conversions (ECB, dated)
GET/api/changelogData + 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/researchResearch index: reports, dataset releases + checksums, charts, corrections
GET/api/whereamiCountry Cloudflare sees for this request (never stored)
GET/api/usage?days=28Weekly usage counters: page groups, referrer hosts, crawler classes, MCP clients/tools, API routes (n>=3; no IP/UA/cookies stored)
POST/api/rfqSubmit a request for quotes
GET/api/rfq/{id}?key=RFQ status + status history
POST/mcpMCP (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: search
  • GET /api/v1/stats/{slug}: one statistic with quote, caveats and citations
  • GET /api/v1/research: reports (method, sample, findings), dataset releases with SHA-256 checksums, charts, corrections
  • MCP: search_stats and get_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": true and we'll discard it.
  • Cite prices with their source. Every price has source_url and as_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 an outputSchema. 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_name on RFQs.