CrowdOS MCP Server
Simulated focus groups as agent-callable tools. Exposes the CrowdOS developer API as Model Context Protocol tools so AI agents (Claude Desktop, Cursor, Cline, LangGraph, CrewAI, AutoGPT, Devin, etc.) can run simulated public-opinion research with a single tool call.
What this gives you
Your agent can now do things like:
> Run a focus group on whether companies should mandate 4-day weeks.
Use 200 agents from the us_general_population preset.
[tool: run_focus_group]
{
"id": "ad4b3736-...",
"sentiment_summary": {
"positive_pct": 71.5, "negative_pct": 18.0, "neutral_pct": 10.5,
"positive": 143, "negative": 36, "neutral": 21
},
"sample_responses": [
{
"agent_name": "Maria Chen", "age": 34, "occupation": "Software engineer",
"sentiment": "positive",
"reasoning": "It would be great for parents — three full days with the kids ..."
},
...
]
}
No-install option: the hosted server
The same fifteen tools are served directly by the API — add
https://api.crowdos.ai/mcp as a remote MCP server in any client
that supports them (claude.ai connectors, Cursor, agent frameworks).
OAuth-capable clients sign in and approve on first connect; the grant
shows up as a revocable key under Account → API keys. Header-based
clients pass Authorization: Bearer crowd_.... The hosted server is
always current with the deploy; this pip package is the stdio
alternative for desktop hosts.
Installation
pip install crowdos-mcp
Then mint an API key at https://crowdos.ai/account/api-keys — API access is included with the Pro and Max subscriptions (panel caps 750 and 2,000 respondents per study respectively).
Configure for Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json
(macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"crowdos": {
"command": "crowdos-mcp",
"env": {
"CROWDOS_API_KEY": "crowd_..."
}
}
}
}
Restart Claude Desktop. The CrowdOS tools should appear in the slash-command picker.
Configure for Cursor
Settings → MCP Servers → Add. Same env block as above; command =
crowdos-mcp.
Configure for Cline (VS Code)
Settings → Cline → MCP Servers → Edit JSON:
{
"mcpServers": {
"crowdos": {
"command": "crowdos-mcp",
"env": { "CROWDOS_API_KEY": "crowd_..." }
}
}
}
Tools exposed
| Tool | What it does | Auth |
|---|---|---|
run_focus_group |
Simulated poll on a topic, returns sentiment + quotes | required |
run_debate |
Multi-round simulated debate, returns convergence + key arguments | required |
compare_options |
Simulated A/B test on 2-10 text options (plan limits: pro 5, max 10), returns leaderboard | required |
test_pricing |
Van Westendorp / Gabor-Granger pricing study, returns price points or a revenue curve | required |
rank_items |
MaxDiff (best-worst scaling) on 4-200 items, returns a rank order with bootstrap rank intervals | required |
pretest_questions |
Pretest 1-40 draft survey questions before fielding them — verdict (ok / check / revise) per question | required |
preview_cost |
Estimate cents + seconds before running (free to call) | required |
estimate_reach |
Preview how broad an audience a custom_audience filter describes (free to call) |
none |
list_demographic_presets |
Discover available audience templates | required |
get_simulation |
Fetch full results of a previously-run study | required |
cancel_simulation |
Stop a running study (refunds the wallet reservation) | required |
crowd_sample |
Browse the public CrowdOS crowd (sanitized) | none |
list_panels |
Your saved panels (from persist_panel / evolve_panel runs) — metadata only, newest first |
required |
get_panel |
One saved panel: size, expiry, demographic make-up, recorded sessions (never the roster) | required |
delete_panel |
Delete a saved panel you own — immediate and permanent | required |
Persistent panels — the same respondents across steps
Releasing: tag it —
git tag mcp-v<version> && git push origin mcp-v<version>— and.github/workflows/publish-mcp.ymlbuilds and publishes to PyPI over Trusted Publishing (no token on any machine). The run stops before building if the tag,pyproject.tomland__init__.pydisagree, if the vendored core has drifted from the server's copy, if the suite is red, or if the built server does not start on the installed SDK.scripts/publish.shstill works from a machine with a~/.pypirctoken if you need it.0.12.5: failed tool calls are marked
isError(they rendered in the host as successful ones), and error envelopes match the hosted endpoint's — forwarded detail nested underdetailand at the top level. Run tools returnbilling: the stream'scompleteevent is published before the study is priced, so the package now reads the stored row for it (at most three reads in ~6 s).get_simulationkeeps a pricing / MaxDiff / pretest run's headline and fullanalysis(it returned an empty focus-group shape).compare_optionsaccepts 2-10 options — the plan limits the API enforces — instead of 2-4.preview_costdescribes the fixed per-respondent price: a vote is charged exactlyestimate_cents, andmin_centsis only lower for a debate that can reach consensus early (the old text promised debits "typically lower" than the estimate, from token-metered billing that no longer exists). A Gabor-Grangertest_pricingresult carrieswarnings: when nobody would buy at any rung,revenue_maximizing_priceis null and the warning says so, instead of a null that read like a broken run. The package metadata points at the public docs and a support address rather than a private repository.0.12.3: progress notifications work again on the 2.x SDK — it removed the contextvar the emitter read and made request
metaa Mapping, so the "47/200 respondents" a host displays was silently going nowhere; the handler now uses the context the SDK hands it. Every tool property carries a description (nine did not, includingrun_debate.topic).0.12.2: runs on both MCP SDK generations — the 2.0 SDK removed the handler decorators this package used, so with
mcp>=2installed the server raisedAttributeErrorat startup and never served a request. Also sends serverinstructionsoninitialize. The 2.0 SDK removed the handler decorators this package used, so withmcp>=2installed the server raisedAttributeErrorat startup and never served a request; handler registration is now chosen from the SDK's own constructor signature. This release also sends serverinstructionsoninitialize— the recommended flow, panel reuse and the spend warning the hosted/mcpendpoint has always advertised.0.12.1:
sim_id/panel_idarguments are validated to the id shape the API mints (letters, digits,-,_; ≤ 64 chars) before any request is built — a malformed or hallucinated id returns{"error": "validation_error"}instead of reaching a route it never meant to. Every locally-caught caller mistake now reports asvalidation_error(earlier releases folded them intoconfiguration_error, which is reserved for a missingCROWDOS_API_KEY), and API errors forward the same structured detail keys as the hosted/mcptransport (panel_size,population_size,minimum,spec_error_details, …).
Every run tool takes four optional panel arguments (0.12.0+):
| Argument | Effect |
|---|---|
persist_panel: true |
Save this run's respondents; the result carries panel: { panel_id, size, preset_key, created_at, expires_at }. |
panel_id |
Run against a saved panel — the SAME people. The panel defines the audience (demographic_preset comes from it, custom_audience is refused) and always runs at exactly its own size: omit population_size (the tool sends none) or pass the panel's size — anything else is a validation_error (panel_size_mismatch). |
panel_memory: true |
Opt in, off by default. With persist_panel the new panel records this session; with panel_id each respondent recalls their own answers from up to the last 3 recorded sessions, and this session is recorded too (panel.memory in the result). |
evolve_panel: true |
With panel_id: catch the panel up on today's news into a NEW panel (parent_panel_id = the original, which stays frozen), then run on it. The result's panel block carries the new id and evolved_from. |
Panels live 90 days; an account holds up to 200. The cross-field rules
(evolve_panel without panel_id, panel_memory without a panel,
persist_panel on a panel_id run, custom_audience with panel_id)
fail locally as a validation_error before any request is sent.
run_focus_group and run_debate accept an optional image_url
(public HTTPS URL of a JPEG/PNG). One vision pass
extracts a description, which every text-only agent reacts to
alongside the question. ~$0.001 + ~1-3s overhead, regardless of
panel size — useful for testing diagrams, product photos, charts,
screenshots, or political imagery.
run_focus_group and run_debate block 5–120s depending on
population size — that's a real simulated-research call running
behind the scenes, not a cached response. The MCP server returns a
trimmed envelope (sentiment summary + first 5 representative quotes
- billing breakdown). Use
get_simulationto pull the full payload when you need every agent's full reasoning.
Custom audiences
run_focus_group, run_debate, compare_options, test_pricing, and
rank_items all accept an optional custom_audience object that
narrows the panel inside the chosen market preset: geography
(countries / regions / cities), age band, genders, interest tags,
ethnicity + religion (multi-ethnic markets), and income — either as
affluence tiers (0–7) or as real annual amounts in a named
currency:
{
"custom_audience": {
"cities": ["Bangkok"],
"age_min": 25, "age_max": 45,
"income_currency": "THB",
"income_min_amount": 1500000,
"income_match": "position"
}
}
income_match: "position" (default) selects the equivalent affluence
standing within each market's own income ladder; "absolute" matches
actual earnings at indicative FX. Preview a filter with the free
estimate_reach tool first — it reports the audience size and every
filter narrowing it, strongest first (narrowing_filters). Field-by-field docs:
GET https://api.crowdos.ai/api/v1/developer/docs.
Live progress
run_focus_group, run_debate, compare_options, test_pricing, and rank_items stream via
SSE under the hood. When your MCP host (Claude Desktop / Cursor /
Cline) sends a progressToken with the tool call (the default),
the server emits MCP notifications/progress as agents complete
— so the host displays "47/200 agents responded" live instead of
a blank "running tool..." indicator. Debate runs additionally
emit "Round 3/5 starting" / "Round 3 complete" message updates.
No client work needed; progress just shows up.
Recommended flow
For non-trivial studies the agent should:
list_demographic_presets— if the user didn't pick one, propose one based on the topic.preview_cost— gets a cents+seconds estimate before committing.- Confirm with the user — show them the cost estimate and the proposed audience/size.
run_focus_group/run_debate— only after confirmation.
When the study is a questionnaire rather than one question, put
pretest_questions before step 2: it runs on a small panel, costs
a fraction of the study, and returns a verdict per draft question —
fix or drop everything marked revise before spending on the real
field. Pretesting after fielding is how a bad question becomes a bad
dataset.
Defaults match the platform's calibrated quality bars — Standard
Pulse (200 agents) for run_focus_group, the platform-standard
debate (30 agents × 5 rounds) for run_debate. Cheaper defaults
would silently weaken the output, and the moat is calibrated
quality. Plan panel caps are 750 (Pro) and 2,000 (Max); an
over_plan_cap error names the cap to retry with.
When a study uses custom_audience, add a step 0: call
estimate_reach (free) to see how broad the audience is.
A small audience is not a blocked one — never abandon a run over
it. Your panel is delivered at the population_size you asked for,
with every respondent satisfying
the filter. Interests are the usual cause: each respondent carries a
handful of tags from a ~200-topic taxonomy, so pass several related
tags (they are OR-matched) rather than one.
Response shapes
Tool responses are mode-aware and field names are stable. Internal QA fields (consistency_score, model routing, harness flags, sampling metadata) are dropped — agents don't need them.
run_focus_group (voting mode):
{
"id": "...", "status": "complete", "mode": "voting",
"topic": "...", "demographic_preset": "us_general_population",
"population_size": 50,
"sentiment_summary": {
"positive": 30, "neutral": 12, "negative": 8,
"positive_pct": 60.0, "neutral_pct": 24.0, "negative_pct": 16.0
},
"sample_responses": [
{ "agent_name": "Maria Chen", "age": 34, "occupation": "Software engineer",
"sentiment": "positive", "reasoning": "..." },
"..."
],
"total_responses": 50,
"billing": { "actual_cents": 12, "plan": "pro" }
}
If a stance_statement was provided, the envelope additionally
carries stance_statement + sentiment_axis: { positive_label, neutral_label, negative_label } so the agent knows whether
positive means "agrees" vs. "supports".
run_debate:
{
"id": "...", "status": "complete", "mode": "debate",
"topic": "...", "num_rounds": 5, "agent_count": 30,
"final_consensus_score": 0.72,
"summary": "Most agents converged toward ...",
"key_arguments_for": [ "..." ],
"key_arguments_against": [ "..." ],
"dissenting_views": [ "..." ],
"final_round_responses": [
{ "agent_name": "...", "position": "FOR", "reasoning": "...",
"confidence": 0.8 },
"..."
],
"position_shifts_count": 8,
"billing": { "actual_cents": 18, "plan": "pro" }
}
get_simulation returns the same envelope as the originating tool
but with every response (no 5-quote cap), still with internal
QA fields stripped. Use this when the trimmed envelope from
run_focus_group / run_debate isn't enough.
Errors
API failures come back as a typed envelope so agents can
pattern-match and react. The error field is one of:
error |
When | Agent action |
|---|---|---|
auth_error |
401, 403 | Get a fresh API key |
over_plan_cap |
400 — population > tier limit | Lower population_size or upgrade |
validation_error |
400 / 422 — or caught locally before any request (bad id shape, cross-field panel rules, an out-of-range limit) |
Fix the request |
insufficient_funds |
402 — wallet drained | Top up at top_up_url |
not_found |
404 — a panel_id (or sim_id) that is missing, expired, or not yours |
list_panels shows what exists; save a new panel with persist_panel |
rate_limited |
429 | Sleep and retry (retry_after seconds when available) |
quota_exceeded |
429 — monthly allowance spent | Wait for next month or upgrade |
simulation_failed |
500 — sim crashed mid-run | Inspect sim_id; fall back to a smaller run |
stream_disconnected |
The connection dropped after the study launched (recoverable: true, with sim_id) |
Call get_simulation(sim_id) in 30-60 s — do not re-run, which launches and bills a second study |
server_error |
500/502/503/504 | Retry with backoff |
configuration_error |
local — CROWDOS_API_KEY unset |
Tell user to fix host config |
internal_error |
bug in this MCP server | Email hello@motifmotion.sg with the tool name and any sim_id (see Issues / questions below) |
Insufficient-funds responses also carry balance_cents,
needed_cents, and top_up_url so the agent can surface a clear
upgrade prompt. Every structured field the API sends is available both
at the top level and under detail (the hosted /mcp endpoint returns
the same shape), and a failed call's MCP result is marked isError.
Configuration
| Env var | Default | Required |
|---|---|---|
CROWDOS_API_KEY |
— | yes (except crowd_sample) |
CROWDOS_API_BASE_URL |
https://api.crowdos.ai |
no |
Cost
CrowdOS uses a metered wallet. The MCP server returns the actual
debit on every successful call inside billing.actual_cents. Top up
at https://crowdos.ai/account/billing.
A monthly developer-API allowance applies, denominated in list-price
spend (Pro $500/mo, Max $2,500/mo, pooled across an organization).
GET /api/v1/developer/billing reports usage against it, and the
complete written API reference lives at
GET https://api.crowdos.ai/api/v1/developer/docs.
Programmatic use (without an MCP host)
The server is also a regular Python module:
python -m crowdos_mcp
# stdio MCP server, waits for messages on stdin
Or import and embed:
from crowdos_mcp.server import build_server
server = build_server()
# server is a configured mcp.server.Server instance
Versioning
Follows semver. The MCP tool surface (tool names, input schemas) is stable; additive changes (new tools, new optional fields) ship as minor versions. Removing or renaming a tool is a major version.
Maintainer publish flow
# 1. Bump version in BOTH pyproject.toml and src/crowdos_mcp/__init__.py
# 2. Commit + push
# 3. Publish:
./scripts/publish.sh # → PyPI
./scripts/publish.sh --test # → TestPyPI (dry run)
The script verifies the two version numbers agree, runs tests,
cleans dist/, builds, and uploads via twine using ~/.pypirc
(needs username = __token__ + password = pypi-<token>).
License
MIT.
Issues / questions
Email hello@motifmotion.sg. Full API reference: https://crowdos.ai/developers
Release files for crowdos-mcp 0.12.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| crowdos_mcp-0.12.5.tar.gz | 45.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| crowdos_mcp-0.12.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 91.8 kB
Release files / crowdos_mcp-0.12.5.tar.gz
| Download URL | crowdos_mcp-0.12.5.tar.gz |
|---|---|
| Size | 45.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
32a37b0a592603c17ce2a6fcef3deec795d0813cadca67698780864d5e5e427b
|
|
BLAKE2b-256 checksum How to use checksums |
b6b0b2ec060efbc157b5b60c9f60cbc0ab3e0f56f898e99ccc0993073cb4d679
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency logRelease files / crowdos_mcp-0.12.5-py3-none-any.whl
| Download URL | crowdos_mcp-0.12.5-py3-none-any.whl |
|---|---|
| Size | 46.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d92951f17194cb230449300076d041908e315d4bfd8a1fbc28e3b9216269ccb0
|
|
BLAKE2b-256 checksum How to use checksums |
a1e2d322a13cbb937512b1ba94aa70cd984bdabbc79c5c154140bda9ada4577a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency log