MCP server
One hosted, stateless Streamable HTTP endpoint for every host, with OAuth 2.1, plus a small npx wrapper for hosts that only run local servers.
Endpoint: https://mcp.50heads.com/mcp
Hosted: add the endpoint and sign in with OAuth in the browser. Nothing to install, and you set the connection's daily spend cap when you sign in. Local: npx -y @50heads/mcp runs the same tools over stdio for hosts that launch a command. It needs Node 20 or later and an API key from Developers in the portal, or a one-time npx -y @50heads/mcp login.
Claude (claude.ai and Claude Desktop)
Hosted, sign in with OAuth. Settings, Connectors, Add custom connector. Paste the URL and sign in when asked. On Team and Enterprise plans an owner adds it for the organisation first.
https://mcp.50heads.com/mcpOr with an API key, in claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\).
{
"mcpServers": {
"50heads": {
"command": "npx",
"args": [
"-y",
"@50heads/mcp"
],
"env": {
"FIFTYHEADS_API_KEY": "fh_live_…"
}
}
}
}Claude Code
Hosted, sign in with OAuth. Run in a terminal, then /mcp in Claude Code to sign in.
claude mcp add --transport http 50heads https://mcp.50heads.com/mcpOr with an API key (CI and scripts):
claude mcp add --transport http 50heads https://mcp.50heads.com/mcp --header "Authorization: Bearer fh_live_…"Cursor
Hosted, sign in with OAuth. ~/.cursor/mcp.json (or .cursor/mcp.json in a project). Cursor opens the sign-in page.
{
"mcpServers": {
"50heads": {
"url": "https://mcp.50heads.com/mcp"
}
}
}Or with an API key:
{
"mcpServers": {
"50heads": {
"url": "https://mcp.50heads.com/mcp",
"headers": {
"Authorization": "Bearer fh_live_…"
}
}
}
}ChatGPT
Hosted, sign in with OAuth. Settings, Apps and Connectors, Advanced settings, turn on Developer mode, then Create. Paste the URL and choose OAuth. On Business and Enterprise plans an admin turns on Developer mode first.
https://mcp.50heads.com/mcpVS Code
Hosted, sign in with OAuth. .vscode/mcp.json in the workspace, or MCP: Add Server from the command palette.
{
"servers": {
"50heads": {
"type": "http",
"url": "https://mcp.50heads.com/mcp"
}
}
}Or from a terminal:
code --add-mcp '{"name":"50heads","type":"http","url":"https://mcp.50heads.com/mcp"}'Windsurf
Local, with an API key. ~/.codeium/windsurf/mcp_config.json, or Windsurf Settings, Cascade, MCP servers, View raw config.
{
"mcpServers": {
"50heads": {
"command": "npx",
"args": [
"-y",
"@50heads/mcp"
],
"env": {
"FIFTYHEADS_API_KEY": "fh_live_…"
}
}
}
}Or the hosted server with an API key:
{
"mcpServers": {
"50heads": {
"serverUrl": "https://mcp.50heads.com/mcp",
"headers": {
"Authorization": "Bearer fh_live_…"
}
}
}
}Any other host
Local, with an API key. Hosts that launch a local command. Needs Node 20 or later and an API key from the dashboard.
FIFTYHEADS_API_KEY=fh_live_… npx -y @50heads/mcpHosts that speak Streamable HTTP: https://mcp.50heads.com/mcp with the header "Authorization: Bearer fh_live_…", or OAuth.
{
"mcpServers": {
"50heads": {
"url": "https://mcp.50heads.com/mcp",
"headers": {
"Authorization": "Bearer fh_live_…"
}
}
}
}npx -y @50heads/mcp config [host] prints any of these. To check it works, ask your host to use 50heads to estimate a question: estimate is free and never spends credits.
MCP Registry
50heads is in the official MCP Registry as com.50heads/mcp, with the hosted server and the npm package. Hosts and directories that read the registry can add it from there. Releases use calendar versions (2026.924.1 is the first release on 24 September 2026); the hosted server, the npm package and the registry entry carry the same one.
OAuth
The server is an OAuth 2.1 resource server. Hosts discover everything from https://mcp.50heads.com/.well-known/oauth-protected-resource, which points at the authorization server https://auth.50heads.com. PKCE (S256) and the resource parameter are required; dynamic client registration and client ID metadata documents are both supported. Access tokens last an hour; refresh tokens rotate and last 30 days.
At consent the person picks scopes and a daily spend cap for the connection (£50.00 unless they change it). They can change or revoke it any time under Developers in the portal; a revoked connection stops within a second.
Scopes
questions:read, questions:write, templates:read, account:read. A call that needs a scope the token lacks returns insufficient_scope so the host can ask for it.
Tools
| Tool | Scope | Does |
|---|---|---|
estimate | questions:write | Price, time and validation. Never spends. Call it first. |
ask | questions:write | Posts a question; returns a task (or a question id on older hosts). Needs idempotency_key. |
get_results | questions:read | The results object, partial while live. |
wait_for_results | questions:read | Older hosts: waits on our side up to 600 seconds, with min_answers. |
list_questions | questions:read | Recent questions by status and date, to reuse before re-asking. |
cancel | questions:write | Stops a live question; returns refund_credits. |
templates | none | Fixed-price templates. Cacheable. |
balance | account:read | Credits, reserved and cap_remaining. |
upload_image | questions:write | An image from base64 or an https link into 50heads; returns image_url for a question. |
get_answers | questions:read | Individual answers, a page at a time, filtered by option, tier, country, age band or keyword. |
add_heads | questions:write | More heads on the same question, merged into its result. Needs idempotency_key. |
flag_answer | questions:write | Reports one answer by its attestation_ref for review; upheld flags are refunded. |
export | questions:read | The results as a file: CSV, and a PDF report or PNG card where served. |
build_ask_link | none | A portal link with the question filled in (the parameters below). Spends nothing. |
list_targeting | none | Countries, languages, pool bands and the tags you can target. |
search_help | none | Searches the help centre; returns articles with links. |
send_feedback | none | Sends feedback or a problem to support as a ticket. |
Pre-filled ask links
https://50heads.com/app/ask?… opens the composer with a question filled in, for a person to check the price and time and ask. build_ask_link writes these; you can write them by hand too. Nothing is asked until the person presses Ask. The composer fills itself only when the link has text or at least one optionLabel.
| Parameter | Meaning |
|---|---|
type | single_choice, multi_choice, ab_image, pairwise, scale_1_5, ranking, yes_mostly_no or free_text. |
text | The question. |
context | One line of background. |
language | Such as en or pt-BR. Default en. |
optionLabel | An option; repeat for each, in order. |
optionImage | An https image for the option in the same position; repeat, empty for none. |
stimulusImage, stimulusText | Shown above the question. |
neither=on | Adds a Neither option. |
n | Heads, 10 to 5,000. |
tier | 1, 2 or 3. |
rush=on | Aim for under an hour. |
country | ISO code; repeat for each. |
tag | A tag id; repeat. Kept at Tier 2 and 3 only. |
template | Opens a template instead (other fields are ignored). |
followUp | Opens a follow-up to that question id; type and text apply. |
reask | Re-asks the unanswered part of that question id. |
mode=bulk | Opens the bulk composer. |
Errors
JSON-RPC errors carry error.data.code: validation and not_found as −32602; insufficient_credits, spend_cap_exceeded, idempotency_conflict and content_refused as −32001; rate_limited and unavailable as −32000. Every message is one sentence a model can act on.
Limits
1 MB request bodies. Images by URL or upload only, never large inline base64. 60 requests a minute per connection and 10 asks a minute.