Ask people for a reaction a model can't give; get each answer and what their page observed.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Optional: Bearer <key>. Simulated calls need no key. A free test key (POST https://api.person.tools/v1/keys with {"class":"test"}) adds a balance of test money and caps; it reaches simulated people only during the preview.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Opens a call: one short task to several people. During the preview a test key or no key reaches simulated people: their answers are made up and marked simulated. Returns a call_id at once; ask_people_results gives each person's answers, whether each was typed or pasted, and an evidence level. Use it only when your user asked for other people's reactions to this material, or said yes when you offered, and the task needs a human reaction a model cannot give: how a song feels, whether a page is clear in ten seconds, whether a game is fun, which version people prefer. Not for your user's own taste (ask them), facts a search can find, or anything done away from a screen. Who answers: pool defaults to "simulated". Simulated people answer in seconds; every simulated answer carries simulated: true and begins "[simulated] ", and none is a person's view. Real people are rung only on pool "panel:<panel_id>", a panel the account owner invited, in minutes when they are available and never in their quiet hours (9 PM to 8 AM their time by default). Each person is told what is recorded before they take the call. Cost: during the preview every amount is test money; nothing is charged and nobody receives real pay. max_total_usd (required) is the most your user approved, every fee included. With no approved amount, show your user the for_your_user sentence from ask_people_quote and pass only the amount they approve. A paid call holds the people's pay, a 25% reserve for anyone who takes longer (returned if unused) and a 10% router fee (at least $0.25), each on its own line. A simulated call or a panel at zero pay costs nothing. Nothing opens above max_total_usd (error over_max_total_usd, with the price) or above the key's cap (over_key_cap, with the price and an approve_url for the account owner). If fewer people answer than asked, you pay for those who answered and anyone who stopped partway; anyone still in a seat at the deadline may finish and is paid in full; the rest of the hold returns. A person who stops partway is paid for the time spent, at least 25 cents or the full pay if less; 'Stop: this upset me' pays in full. content: an https URL (copied into our storage before anyone is asked), {"text": "..."}, or for compare a list of 2 to 6 of these; not used for kind answer. Send only material your user made or may show, and nothing that shows or identifies a person who has not agreed. Ask only for reactions and opinions: calls that ask about health, religion, sexuality, ethnicity, immigration or politics, for a name or any way to reach someone, or for passwords, one-time codes, PINs, keys, government ID, card or bank numbers are refused, and nothing is charged. Every call is screened before anyone is rung; it may be held for review (state held) or refused (state refused; nothing is charged). Each default used comes back in defaults_used. Look again after next_check_after_seconds. A retry within 24 hours with the same idempotency_key returns the first call; without a key, the same request within 10 minutes counts as the same call, and after that opens a second one. Example: {"ask":"Listen once to the end. Then say what you felt, in your own words.","kind":"listen","content":"https://example.com/demo-track.mp3","people":3,"answer_fields":{"felt":"text","would_play_again":"boolean"},"max_total_usd":"0.00","idempotency_key":"first-try-1"}
What each person reads first, in plain words. Ask for their own reaction in their own words. The material opens in our page: never ask them to download, install or join anything (a call that does is refused).
listen, watch, view, play, compare or answer. Decides what the person's page shows and records.
How many people to ask.
Required: the most this call may cost, every fee included. At most two decimals.
Simulated pool only: "instant" (the default) or "human" (answers over 1 to 6 minutes).
"simulated" (the default; any test key) or "panel:<panel_id>" for a panel the account owner made.
How many answers make the call usable. Default: 70% of people, rounded up.
Simulated pool only: one entry per seat in order: {accept (true, false or "miss"), delay_seconds, play_fraction, realtime, visible_fraction, revealed, seat_seconds, stop_at_fraction, stop_reason, send_partial, answers, answer_input}.
An https URL (copied into our storage first; on the simulated pool with a test key it is checked for shape and never fetched), {"text": "..."}, or a media id. For compare, a list of 2 to 6. Not used for kind answer. For play: one HTML file with all its scripts, images and sounds inside; no zip.
Each person's estimated time. Default: the material's length rounded up plus one for listen and watch, else 2.
Selectors over people's claims: country, region, language, age_band, device, each a list of accepted values.
Named fields, each a type ("text", "boolean", "number", "choice", "scale") or {"type", "label", "optional", "choices", "min", "max"}. Default {"answer": "text"}.
Optional unique string; a repeat with the same key and fields returns the first result for 24 hours.
Minutes until the call closes and returns what came in. Default 120.
What each person is paid. Default "0.00" on the simulated pool and on panels.
Whether your principal may train models on the written answers (default false). People see this before they start.
The server marks this tool as destructive. Running it may delete or overwrite data on the server or in any account you connected with your headers.
Pick your client. Values in capitals, like YOUR_TOKEN, are yours to fill in.
Run in a terminal
claude mcp add --transport http ask-people https://api.person.tools/mcp
Headers
Authorization (optional, not in the config above): Optional: Bearer <key>. Simulated calls need no key. A free test key (POST https://api.person.tools/v1/keys with {"class":"test"}) adds a balance of test money and caps; it reaches simulated people only during the preview.Opens a call: one short task to several people. During the preview a test key or no key reaches simulated people: their answers are made up and marked simulated. Returns a call_id at once; ask_people_results gives each person's answers, whether each was typed or pasted, and an evidence level. Use it only when your user asked for other people's reactions to this material, or said yes when you offered, and the task needs a human reaction a model cannot give: how a song feels, whether a page is clear in ten seconds, whether a game is fun, which version people prefer. Not for your user's own taste (ask them), facts a search can find, or anything done away from a screen. Who answers: pool defaults to "simulated". Simulated people answer in seconds; every simulated answer carries simulated: true and begins "[simulated] ", and none is a person's view. Real people are rung only on pool "panel:<panel_id>", a panel the account owner invited, in minutes when they are available and never in their quiet hours (9 PM to 8 AM their time by default). Each person is told what is recorded before they take the call. Cost: during the preview every amount is test money; nothing is charged and nobody receives real pay. max_total_usd (required) is the most your user approved, every fee included. With no approved amount, show your user the for_your_user sentence from ask_people_quote and pass only the amount they approve. A paid call holds the people's pay, a 25% reserve for anyone who takes longer (returned if unused) and a 10% router fee (at least $0.25), each on its own line. A simulated call or a panel at zero pay costs nothing. Nothing opens above max_total_usd (error over_max_total_usd, with the price) or above the key's cap (over_key_cap, with the price and an approve_url for the account owner). If fewer people answer than asked, you pay for those who answered and anyone who stopped partway; anyone still in a seat at the deadline may finish and is paid in full; the rest of the hold returns. A person who stops partway is paid for the time spent, at least 25 cents or the full pay if less; 'Stop: this upset me' pays in full. content: an https URL (copied into our storage before anyone is asked), {"text": "..."}, or for compare a list of 2 to 6 of these; not used for kind answer. Send only material your user made or may show, and nothing that shows or identifies a person who has not agreed. Ask only for reactions and opinions: calls that ask about health, religion, sexuality, ethnicity, immigration or politics, for a name or any way to reach someone, or for passwords, one-time codes, PINs, keys, government ID, card or bank numbers are refused, and nothing is charged. Every call is screened before anyone is rung; it may be held for review (state held) or refused (state refused; nothing is charged). Each default used comes back in defaults_used. Look again after next_check_after_seconds. A retry within 24 hours with the same idempotency_key returns the first call; without a key, the same request within 10 minutes counts as the same call, and after that opens a second one. Example: {"ask":"Listen once to the end. Then say what you felt, in your own words.","kind":"listen","content":"https://example.com/demo-track.mp3","people":3,"answer_fields":{"felt":"text","would_play_again":"boolean"},"max_total_usd":"0.00","idempotency_key":"first-try-1"}
| Argument | Type | Description |
|---|---|---|
| ask* | string | What each person reads first, in plain words. Ask for their own reaction in their own words. The material opens in our page: never ask them to download, install or join anything (a call that does is refused). |
| ext | object | |
| kind* | string | listen, watch, view, play, compare or answer. Decides what the person's page shows and records. |
| pace | string | Simulated pool only: "instant" (the default) or "human" (answers over 1 to 6 minutes). |
| pool | string | "simulated" (the default; any test key) or "panel:<panel_id>" for a panel the account owner made. |
| enough | integer | How many answers make the call usable. Default: 70% of people, rounded up. |
| people* | integer | How many people to ask. |
| script | array | Simulated pool only: one entry per seat in order: {accept (true, false or "miss"), delay_seconds, play_fraction, realtime, visible_fraction, revealed, seat_seconds, stop_at_fraction, stop_reason, send_partial, answers, answer_input}. |
| content | any | An https URL (copied into our storage first; on the simulated pool with a test key it is checked for shape and never fetched), {"text": "..."}, or a media id. For compare, a list of 2 to 6. Not used for kind answer. For play: one HTML file with all its scripts, images and sounds inside; no zip. |
Stops a call. On cancel, nobody new is rung; people already in a seat may finish and are paid in full. Their pay is charged to the hold (test money in the preview: nobody receives real pay), so spent_usd can rise until ask_people_results shows complete: true; the unused hold returns. Returns the call's state and the money so far. Repeating it changes nothing; a cancel cannot be undone. Use it when your user says the answers in hand are enough or the task has changed. A call closes by itself at its deadline. Example: {"call_id": "call_..."}
| Argument | Type | Description |
|---|---|---|
| call_id* | string |
{
"type": "object",
"required": [
"call_id"
],
"properties": {
"call_id": {
"type": "string"
}
},
"additionalProperties": false
}Makes a panel, a named group of people the account owner invites, and returns its panel_id and a join link for your user to share. Each person joins on their own phone, states that they are an adult, is told what is recorded while they answer (how much played, whether the page stayed in front, pauses and replays, typed or pasted), chooses when they can be rung, and can leave at any time. Calls with pool "panel:<panel_id>" then reach only them, and cost nothing at zero pay. On a panel of fewer than five, both sides are told that answers cannot be anonymous. Use it only when your user asks to set up a group of people they will invite themselves; nobody is added without joining. Needs a key issued for real calls; a test key gets an error saying so. A retry with the same idempotency_key returns the same panel; without one, a retry makes a second panel. Example: {"name": "album-listeners", "idempotency_key": "panel-1"}
| Argument | Type | Description |
|---|---|---|
| name* | string | The panel's name (live keys only). |
| idempotency_key | string |
{
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "The panel's name (live keys only)."
},
"idempotency_key": {
"type": "string"
}
},
"additionalProperties": false
}Prices a call without opening it: nobody is rung and nothing is spent. Takes the same fields as ask_people, without max_total_usd or idempotency_key. Nothing else is needed first: leave content out until your user gives you the material (the quote is priced from minutes), and leave pay out unless your user named one (left out, it is 0.00 on the simulated pool and on a panel; any pay you set must meet the $18-an-hour floor). Returns each line of the price (the people's pay, each fee, any reserve returned if unused), total_usd, expected_minutes_to_enough, test_money, and for_your_user: a plain sentence to show your user before anything is spent. A quote reads ask and content only for screening and the minutes estimate; it neither fetches a content URL nor keeps the ask or the content. Use it to get an amount your user can approve, or to see how people, pay or deadline move the price. Not needed when max_total_usd already covers what your user approved: ask_people opens nothing above it. Example: {"ask":"Which headline makes you want to read on, and why?","kind":"compare","content":[{"text":"Plan a week of dinners in five minutes"},{"text":"Dinner, decided: a week of meals in five minutes"}],"people":12,"pay_per_person_usd":"1.00","minutes":2,"pool":"simulated"}
| Argument | Type | Description |
|---|---|---|
| ask* | string | |
| ext | object | |
| kind* | string | |
| pace | string | |
| pool | string |
Returns a call's answers so far, given the call_id from ask_people: first a summary (the state, a tally of each answer field, how many responses reached each evidence level), then one entry per person with their answers and whether each was typed or pasted, then the money (held_usd, spent_usd, returned_usd, and test_money, true throughout the preview: nobody receives real pay). detail: "full" adds each person's record: how much of the material played, whether the page stayed in front, where they paused or replayed. States: held (under review; nobody rung yet), open, enough (enough answers; more may come until the deadline), then complete, expired, cancelled or refused. Evidence: attended (the material played in real time with its page in front for nearly all its length; that does not show the person watched or listened) or said (an answer without that record; its pay still counts). No level shows that an answer was formed without help. wait_seconds (up to 25) waits for a new answer; pass the last result's cursor as after to get only new ones; complete: true means the call has closed. Look again after next_check_after_seconds; sooner usually returns nothing new. Simulated answers carry simulated: true and begin "[simulated] ". Example: {"call_id": "call_...", "after": "<cursor>", "wait_seconds": 25}
| Argument | Type | Description |
|---|---|---|
| after | string | The cursor from the last result; only newer answers come back. |
| detail | string | "full" adds each person's record and what they were shown. |
| call_id* | string | The call_id ask_people returned. |
| wait_seconds | integer |
Sends the people who answered a call one line (at most 280 characters, and up to three lines a call) saying what you did with their answers, for example "Cut the intro from 40 to 12 seconds; most of you drifted there." The line is reviewed the way a call is and may be refused; if allowed, it appears on each answering person's receipt and they are notified. Costs nothing. Returns whether the line was allowed and how many people it reached. With an idempotency_key, a repeat with the same key changes nothing; without one, the same line sent again within 10 minutes counts as the same send. Use it, with your user's agreement, once the answers have changed the work. Not for questions or new tasks (open a new call), and never for anything that identifies a person. Example: {"call_id": "call_...", "text": "Cut the intro from 40 to 12 seconds; most of you drifted there.", "idempotency_key": "outcome-1"}
| Argument | Type | Description |
|---|---|---|
| text* | string | One line: what you did with the answers. |
| call_id* | string | |
| idempotency_key | string |
{
"type": "object",
"required": [
"call_id",
"text"
],
"properties": {
"text": {
"type": "string",
"maxLength": 280,
"description": "One line: what you did with the answers."
},
"call_id": {
"type": "string"
},
"idempotency_key": {
"type": "string",
"maxLength": 128,
"minLength": 1
}
},
"additionalProperties": false
}We read every tool's name, description and inputs the way the model will, looking for instructions hidden from you. A clean result means no warning signs, not a guarantee. Scanned Nov 15, 2026.
| minutes | number | Each person's estimated time. Default: the material's length rounded up plus one for listen and watch, else 2. |
| require | object | Selectors over people's claims: country, region, language, age_band, device, each a list of accepted values. |
| answer_fields | object | Named fields, each a type ("text", "boolean", "number", "choice", "scale") or {"type", "label", "optional", "choices", "min", "max"}. Default {"answer": "text"}. |
| max_total_usd* | string | number | Required: the most this call may cost, every fee included. At most two decimals. |
| content_rating | string |
| idempotency_key | string | Optional unique string; a repeat with the same key and fields returns the first result for 24 hours. |
| deadline_minutes | integer | Minutes until the call closes and returns what came in. Default 120. |
| pay_per_person_usd | string | number | What each person is paid. Default "0.00" on the simulated pool and on panels. |
| may_train_on_answers | boolean | Whether your principal may train models on the written answers (default false). People see this before they start. |
{
"type": "object",
"$defs": {
"item": {
"oneOf": [
{
"type": "string",
"description": "a media id (m_...) or an https address"
},
{
"type": "object",
"required": [
"text"
],
"properties": {
"text": {
"type": "string",
"maxLength": 5000
}
},
"additionalProperties": false
}
]
}
},
"required": [
"ask",
"kind",
"people",
"max_total_usd"
],
"properties": {
"ask": {
"type": "string",
"maxLength": 2000,
"minLength": 1,
"description": "What each person reads first, in plain words. Ask for their own reaction in their own words. The material opens in our page: never ask them to download, install or join anything (a call that does is refused)."
},
"ext": {
"type": "object"
},
"kind": {
"enum": [
"listen",
"watch",
"view",
"play",
"compare",
"answer"
],
"type": "string",
"description": "listen, watch, view, play, compare or answer. Decides what the person's page shows and records."
},
"pace": {
"enum": [
"instant",
"human"
],
"type": "string",
"description": "Simulated pool only: \"instant\" (the default) or \"human\" (answers over 1 to 6 minutes)."
},
"pool": {
"type": "string",
"description": "\"simulated\" (the default; any test key) or \"panel:<panel_id>\" for a panel the account owner made."
},
"enough": {
"type": "integer",
"minimum": 1,
"description": "How many answers make the call usable. Default: 70% of people, rounded up."
},
"people": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "How many people to ask."
},
"script": {
"type": "array",
"items": {
"type": "object"
},
"description": "Simulated pool only: one entry per seat in order: {accept (true, false or \"miss\"), delay_seconds, play_fraction, realtime, visible_fraction, revealed, seat_seconds, stop_at_fraction, stop_reason, send_partial, answers, answer_input}."
},
"content": {
"anyOf": [
{
"$ref": "#/$defs/item"
},
{
"type": "array",
"items": {
"$ref": "#/$defs/item"
},
"maxItems": 6,
"minItems": 2
}
],
"description": "An https URL (copied into our storage first; on the simulated pool with a test key it is checked for shape and never fetched), {\"text\": \"...\"}, or a media id. For compare, a list of 2 to 6. Not used for kind answer. For play: one HTML file with all its scripts, images and sounds inside; no zip."
},
"minutes": {
"type": "number",
"maximum": 120,
"minimum": 0.5,
"description": "Each person's estimated time. Default: the material's length rounded up plus one for listen and watch, else 2."
},
"require": {
"type": "object",
"description": "Selectors over people's claims: country, region, language, age_band, device, each a list of accepted values.",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"answer_fields": {
"type": "object",
"description": "Named fields, each a type (\"text\", \"boolean\", \"number\", \"choice\", \"scale\") or {\"type\", \"label\", \"optional\", \"choices\", \"min\", \"max\"}. Default {\"answer\": \"text\"}.",
"additionalProperties": {
"anyOf": [
{
"enum": [
"text",
"boolean",
"number",
"choice",
"scale"
],
"type": "string"
},
{
"type": "object",
"required": [
"type"
],
"properties": {
"max": {
"type": "number"
},
"min": {
"type": "number"
},
"type": {
"enum": [
"text",
"boolean",
"number",
"choice",
"scale"
],
"type": "string"
},
"label": {
"type": "string"
},
"choices": {
"type": "array",
"items": {
"type": "string"
}
},
"multiple": {
"type": "boolean"
},
"optional": {
"type": "boolean"
},
"max_label": {
"type": "string"
},
"min_label": {
"type": "string"
},
"max_length": {
"type": "integer"
}
}
}
]
}
},
"max_total_usd": {
"type": [
"string",
"number"
],
"description": "Required: the most this call may cost, every fee included. At most two decimals."
},
"content_rating": {
"enum": [
"general"
],
"type": "string"
},
"idempotency_key": {
"type": "string",
"maxLength": 128,
"minLength": 1,
"description": "Optional unique string; a repeat with the same key and fields returns the first result for 24 hours."
},
"deadline_minutes": {
"type": "integer",
"maximum": 10080,
"minimum": 1,
"description": "Minutes until the call closes and returns what came in. Default 120."
},
"pay_per_person_usd": {
"type": [
"string",
"number"
],
"description": "What each person is paid. Default \"0.00\" on the simulated pool and on panels."
},
"may_train_on_answers": {
"type": "boolean",
"description": "Whether your principal may train models on the written answers (default false). People see this before they start."
}
},
"additionalProperties": false
}| enough | integer |
| people* | integer |
| script | array |
| content | any |
| minutes | number |
| require | object |
| answer_fields | object |
| max_total_usd | string | number |
| content_rating | string |
| idempotency_key | string |
| deadline_minutes | integer |
| pay_per_person_usd | string | number |
| may_train_on_answers | boolean |
{
"type": "object",
"$defs": {
"item": {
"oneOf": [
{
"type": "string",
"description": "a media id (m_...) or an https address"
},
{
"type": "object",
"required": [
"text"
],
"properties": {
"text": {
"type": "string",
"maxLength": 5000
}
},
"additionalProperties": false
}
]
}
},
"required": [
"ask",
"kind",
"people"
],
"properties": {
"ask": {
"type": "string",
"maxLength": 2000,
"minLength": 1
},
"ext": {
"type": "object"
},
"kind": {
"enum": [
"listen",
"watch",
"view",
"play",
"compare",
"answer"
],
"type": "string"
},
"pace": {
"enum": [
"instant",
"human"
],
"type": "string"
},
"pool": {
"type": "string"
},
"enough": {
"type": "integer",
"minimum": 1
},
"people": {
"type": "integer",
"maximum": 100,
"minimum": 1
},
"script": {
"type": "array",
"items": {
"type": "object"
}
},
"content": {
"anyOf": [
{
"$ref": "#/$defs/item"
},
{
"type": "array",
"items": {
"$ref": "#/$defs/item"
},
"maxItems": 6,
"minItems": 2
}
]
},
"minutes": {
"type": "number",
"maximum": 120,
"minimum": 0.5
},
"require": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"answer_fields": {
"type": "object",
"additionalProperties": {
"anyOf": [
{
"enum": [
"text",
"boolean",
"number",
"choice",
"scale"
],
"type": "string"
},
{
"type": "object",
"required": [
"type"
],
"properties": {
"max": {
"type": "number"
},
"min": {
"type": "number"
},
"type": {
"enum": [
"text",
"boolean",
"number",
"choice",
"scale"
],
"type": "string"
},
"label": {
"type": "string"
},
"choices": {
"type": "array",
"items": {
"type": "string"
}
},
"multiple": {
"type": "boolean"
},
"optional": {
"type": "boolean"
},
"max_label": {
"type": "string"
},
"min_label": {
"type": "string"
},
"max_length": {
"type": "integer"
}
}
}
]
}
},
"max_total_usd": {
"type": [
"string",
"number"
]
},
"content_rating": {
"enum": [
"general"
],
"type": "string"
},
"idempotency_key": {
"type": "string",
"maxLength": 128,
"minLength": 1
},
"deadline_minutes": {
"type": "integer",
"maximum": 10080,
"minimum": 1
},
"pay_per_person_usd": {
"type": [
"string",
"number"
]
},
"may_train_on_answers": {
"type": "boolean"
}
},
"additionalProperties": false
}| Wait up to this many seconds for a new answer (default 20). |
{
"type": "object",
"required": [
"call_id"
],
"properties": {
"after": {
"type": "string",
"description": "The cursor from the last result; only newer answers come back."
},
"detail": {
"enum": [
"summary",
"full"
],
"type": "string",
"description": "\"full\" adds each person's record and what they were shown."
},
"call_id": {
"type": "string",
"description": "The call_id ask_people returned."
},
"wait_seconds": {
"type": "integer",
"maximum": 25,
"minimum": 0,
"description": "Wait up to this many seconds for a new answer (default 20)."
}
},
"additionalProperties": false
}