Reason8

Reason8 MCP

Overview

Reason8 MCP gives your agent a user researcher in its loop. Instead of hand-pasting interview notes, Slack threads, and survey exports into prompts, your agent calls Reason8 — and gets back how your users react to the thing it's about to ship.

Four tool families:

FamilyToolsSpeedUse when
Fast reactionsreason8_ask, reason8_reviewSynchronous, secondsIn-loop checks before shipping copy, screens, flows. Stays inside an active coding session.
Journey Witnessreason8_journey_start / _step / _synthesizeWalk live, synth asyncYour agent walks a whole flow screen-by-screen; the panel witnesses each screen, then returns a cited roast. Best for comparing variants.
Panel groundingreason8_seed_persona, reason8_list_setsMulti-turn, minutesOne-time setup: feed real evidence (interviews, support threads, reviews) so the panel reacts like your users, not generic ones.
Deep researchreason8_research_*Asynchronous, 10–20 minHigh-stakes decisions: launch framing, pricing, repositioning. Runs synthetic interviews, social listening, deep research; returns a full memo.

Latency expectations, up front: reason8_ask and reason8_review are built for the inner loop — single HTTP call, answer in seconds, no polling. The reason8_research_* workflow is the opposite: it's a background job your agent starts, then polls while doing other work. Don't block an interactive session on it.

Quickstart

  1. Generate a token in the portal Integrations page. Copy the r8_mcp_… value (shown once).

  2. Install for your client — pick your tab in Install and paste the config. Your token goes into the client's own config file and persists across sessions; you don't re-enter it each time. (One exception: Codex can read the token from an environment variable instead — if you pick that path, put the export in your shell profile so it survives new sessions.)

  3. Ground the panel on your users — do this first. This is what makes the reactions yours instead of a generic LLM opinion. Open a fresh agent session and paste the prompt below. reason8 interviews your agent — it asks one targeted question at a time and your agent answers from your repo / connected tools, only relaying back to you what it genuinely can't find:

    You have the reason8 MCP tools. Ground a persona panel on MY real users, then stop and report.
    
    1. Call reason8_seed_persona (no args). reason8 interviews YOU — it asks one
       targeted question at a time for the biggest gap in the persona.
    2. Answer each question from MY real evidence — search the repo, connected docs,
       transcripts, tickets, reviews. Use the users' VERBATIM words, never your
       paraphrase. If you genuinely can't find evidence for what it asks, say so —
       never invent it.
    3. When reason8 returns questions_for_human + a set_id, stop and relay those
       questions to me verbatim, with the set_id.

    What happens: reason8's interviewer runs the checklist server-side — your agent no longer drives it. It asks one adaptive question for the biggest uncovered axis (WHO, GOAL, PAINS, OBJECTIONS, DECISION_CRITERIA, CONTEXT, ALTERNATIVES, TRIGGERS), stores only verbatim words (it rejects paraphrase — invented quotes store nothing), and reports coverage_by_axis each turn so it knows what's still missing. When it has enough, it returns questions_for_human — one per axis it couldn't ground — plus a set_id. The richer your agent's context, the fewer questions reach you. Reuse the set_id for every call below. Full detail in Grounding.

  4. Get your first roast. Point the agent at what you're shipping, passing that set_id:

    • Fast: reason8_ask (copy/text) or reason8_review (1–2 screenshots → ship / do-not-ship verdict with fixes). Answer in seconds.
    • Full walk: reason8_journey_startreason8_journey_stepreason8_journey_synthesize — the agent walks your page screen by screen and the panel reacts to the whole journey. Ideal for comparing variants: walk each one with the same set_id, then compare the roasts.

    Prerequisite for the walk: the journey tools need browser automation so the agent can capture a screenshot + page text each step — a native-browser agent (Gemini CLI, Antigravity) or any agent with a Playwright MCP. Grounding in step 3 needs none of this (it's pure text), which is why it comes first.

  5. Go deep when stakes are high. For launch decisions, use reason8_research_start — it runs a full research workflow and returns a memo. Track it with reason8_research_status (long-polls up to 50 s per call so your agent isn't busy-waiting).

Install

Supported today

Add Reason8 to ~/.codex/config.toml as a Streamable HTTP server. Paste your token directly so it persists across sessions:

[mcp_servers.reason8]
url = "https://api.prod.reason8.io/mcp"
http_headers = { Authorization = "Bearer r8_mcp_xxx" }

Confirm with codex mcp list, or /mcp in the Codex TUI.

Prefer an environment variable? Swap http_headers for bearer_token_env_var = "REASON8_MCP_TOKEN" — then add export REASON8_MCP_TOKEN="r8_mcp_xxx" to your ~/.zshrc, not just the current shell. Skip that and Codex reports Environment variable REASON8_MCP_TOKEN for MCP server 'reason8' is not set on the next session.

Not supported yet

Claude / Claude Desktop hosted custom connectors — the Settings → Connectors flow accepts a remote MCP URL and optional OAuth credentials, but not a static Authorization header. Reason8 currently issues static Bearer tokens, so use Claude Code, Codex, or Claude Desktop's local mcp-remote proxy until Reason8 supports OAuth for hosted connectors.

gemini.google.com chatbot — no 3rd-party MCP UI yet. Use Gemini CLI.

Fast tools

reason8_ask

Synchronous persona-panel reaction to a text artifact or screenshot. Single call, answer in seconds. Use before shipping copy, onboarding text, emails, error messages, landing sections — anywhere you'd want to know "how do my users read this?" without leaving the coding session.

Parameters:

FieldTypeRequiredDescription
artifactstringyesThe artifact text. For screenshots set artifact_type: "ui_image" and describe the image here.
artifact_type"text" | "ui_image"yestext for copy/landing page/email; ui_image for a screenshot.
audiencestringyesWho the artifact is for, in plain words. Used to resolve the persona panel.
goalstringyesWhat the artifact is trying to make the user do.
artifact_image_b64stringnoBase64 image bytes (no data: prefix) when artifact_type is ui_image.
artifact_mimestringnoImage MIME type (default image/png).
nint (1–6)noPanel size, default 5.
set_idstringnoGrounding set id from reason8_seed_persona. Omit for auto-routing.
grounding"auto" | "ungrounded" | "grounded"noPanel grounding mode, default auto (see Grounding).

Returns a panel_response with one reaction per persona — each split into verified (machine-checked: evidence counts, resolvable grounding refs) and inferred (the persona's reaction: leaning, why, in their own words) — plus a synthesis with honest panel metadata: degraded, degradation_reasons, n_requested vs n_returned, and grounding_fell_back. If the panel couldn't ground or a persona errored, the response says so instead of pretending.

reason8_review

Synchronous pre-ship review of one or two UI screenshots. The protocol asks resolved personas to perceive the screen blind to your intent first, then judges that perception against what you intended — so you learn what the screen actually communicates, not what you hoped it does.

Parameters:

FieldTypeRequiredDescription
screensobject[]yesOne or two screenshot objects, each with image_b64 (and optionally screen_ref, mime_type).
intentstringyesWhat you (the creator) want this screen to achieve — conversion or business objective.
goalstringyesThe end-user's task in their own words.
audiencestringyesTarget audience for persona resolution.
nintnoPanel size, default 5.
set_idstringnoGrounding set id. Omit for auto-routing.
grounding"auto" | "ungrounded" | "grounded"noDefault auto.
lensesstring[]noReview lenses: comprehension, direction, affect. Defaults to all three.

Use it as the gate before shipping user-facing screens. For text artifacts, use reason8_ask. For multi-screen flows or deeper protocols, use reason8_research_start.

Grounding the panel on your users

By default, panels are synthesized from your audience description — useful, but generic. reason8_seed_persona turns them into your users: it ingests real evidence (interview transcripts, support threads, app reviews, sales calls) and builds a grounded persona set from it.

Flow (multi-turn, conversational): reason8_seed_persona relays each call to reason8's persona-interviewer agent, which drives the conversation — your agent just answers. You don't run a checklist; the interviewer does.

  1. Call it with no args to open the interview. Each response carries status (needs_more / complete), the agent's next_question, and coverage_by_axis — what's covered vs. still missing across WHO, GOAL, PAINS, OBJECTIONS, DECISION_CRITERIA, CONTEXT, ALTERNATIVES, TRIGGERS.
  2. Answer each next_question by calling reason8_seed_persona again with your session_id + the verbatim evidence (real user words; up to 500 KB per call). The interviewer asks for one axis at a time, picking the biggest gap. It stores only verbatim material — paraphrase is rejected, nothing invented.
  3. On status: "complete" you get back a set_id plus questions_for_human — one direct question per axis it couldn't ground from your context. Relay those to the human and feed their answers back, or ship the set as-is (a sparse set still works; the response says how grounded it is).

If reason8's interviewer is briefly unavailable, the tool fails open to the built-in onboarding loop, so seeding never blocks. Pass the returned set_id to reason8_ask / reason8_review, or set grounding: "auto" and let Reason8 match your audience string to the best seeded set. With grounding: "grounded" and no usable set, the response honestly reports the fallback instead of silently using generic personas.

Honesty contract: grounded reactions cite the evidence they draw on (grounding_refs); refs that don't resolve to stored evidence are flagged speculative. Panel reactions are synthetic — grounding makes them evidence-anchored, not a replacement for talking to real users on irreversible decisions.

reason8_list_sets

List the grounded persona sets you've already built for this account. Use it to recall a set_id you didn't persist, or to pick an existing panel to enrich (pass the set_id back to reason8_seed_persona as continue_set_id) instead of starting fresh. Read-only — no arguments.

Returns { sets, count }, where each set has:

FieldTypeDescription
set_idstringPass to reason8_ask as set_id, or to reason8_seed_persona as continue_set_id.
name / audiencestringThe locked audience this panel models.
grounding_unit"individual" | "segment"Whether the set models one person or a cohort.
evidence_countnumberDistinct real-evidence chunks grounding the set.
status"active" | "thin"thin = grounded on little/no evidence yet.
coverage_by_axisobjectPer-axis coverage (covered / thin / missing), when recorded.
gap_axesstring[]Axes with no/skipped evidence — the honest gaps to fill.

Journey Witness

For a whole flow — not a single screen — your agent walks the product in its own browser while a persona panel witnesses each screen from behind a one-way mirror, then synthesizes a cited roast of the journey. Walk each variant with the same set_id to compare them apples-to-apples.

The contract: the panel never drives your walk. After each browser action you send the screen; you get back a pulse — an intensity signal only (e.g. "strong reaction this step"), never what the panel reacted to or where. Follow your mission, not the pulse. The full reaction comes only at synthesis. This keeps the walk honest — the agent can't optimize toward the panel mid-flow.

Prerequisite: browser automation, so the agent can capture a screenshot + accessibility/page text each step — a native-browser agent (Gemini CLI, Antigravity) or any agent with a Playwright MCP. Two capture rules that change the result:

  • Decline the cookie banner before the screenshot — otherwise the panel reacts to the consent overlay, not your page.
  • Scroll the page first, then full-page shot — lazy/Framer sections render on scroll; a cold grab catches gray placeholders and the panel calls it "broken."

reason8_journey_start

Open a session. Returns a journey_id. Sessions idle-expire after 30 minutes. (Scope: reason8_journey:run.)

Parameters:

FieldTypeRequiredDescription
targetobjectyes{ name, url, claim }. The caller's agent drives its own browser — Reason8 never fetches this URL.
panelobjectyes{ set_id } (a grounded set) or { personas }, plus size (3–5, default 5).
missionstringyesWhat the visitor is trying to decide. Keep it to the task ("land cold, decide whether to trust this enough to start the trial, name what stops you") — the set_id already supplies who reacts.
budgetobjectno{ max_steps }. Default 12, cap 20.

reason8_journey_step

Submit one observed screen. Returns a panel_pulse (intensity-only) and steps_remaining. Off-domain screenshots are accepted but flagged in the receipt and pulse warning. When status is budget_exhausted, call reason8_journey_synthesize. Idempotent on client_idempotency_key. (Scope: reason8_journey:run.)

Parameters:

FieldTypeRequiredDescription
journey_idstringyesFrom reason8_journey_start.
screenshotobjectyes{ image_b64, mime_type? } — base64 bytes, no data: prefix.
a11ystringyesAccessibility snapshot / page inner text for this screen.
urlstringyesCurrent browser URL for this step.
caller_notestringnoAgent-reported action note (caller-claimed only).
client_idempotency_keystringnoRetry key — replay returns the original receipt.

reason8_journey_synthesize

Close the walk and start synthesis. Returns a run_id on the same async surface as deep research — poll it with reason8_research_status and fetch the finished artifact with reason8_research_get_result. The artifact contains roast_md, structured findings with source refs, and a receipts trail. (Scope: reason8_journey:synth.)

Parameters: journey_id (string, required).

Errors (across the journey tools): journey_not_found, journey_expired, journey_already_synthesized, journey_budget_exhausted, payload_too_large, invalid_screenshot, invalid_panel_size — each with a recovery action.

Deep research

reason8_research_start

Start a research workflow. Returns immediately with a run_id and a status the agent can poll with reason8_research_status.

Parameters:

FieldTypeRequiredDescription
artifactstringyesThe decision, claim, plan, PR summary, copy, or design under review. Paste the actual content — not a pointer to it.
research_goalstringyesThe decision this research must inform (e.g. "decide whether to ship billing-first onboarding").
target_audiencestringyesWho the artifact is for (e.g. "B2B SaaS admins evaluating procurement").
contextstringnoSupporting evidence the agent already has: constraints, prior findings, links, or file excerpts.
research_questionsstring[]noSpecific questions for Reason8 to answer. Omit to let Reason8 derive them from the goal.
desired_outputstringnoThe result shape you need back (e.g. "go/no-go memo with the top risks").
risk_level"low" | "medium" | "high"noHow costly a wrong decision is. Default "medium". Higher risk demands stronger evidence before a proceed verdict.
client_idempotency_keystringnoStable key that makes retries safe. Reuse the same key to replay a prior start instead of creating a duplicate run.
metadataobjectnoPublic-safe key/value tags for the run. Exclude secrets, tokens, and internal identifiers.

Returns:

{
  "run_id": "r8run_abc...",
  "status": "queued",
  "poll_after_seconds": 15,
  "message": "Reason8 is preparing the research brief…",
  "input_request": null,
  "research_plan": null,
  "idempotency_replayed": false
}

status here is the initial workflow state — usually queued, sometimes needs_input if Reason8 immediately needs clarification, or brief_review on idempotent replay of an already-approved run.

curl (raw JSON-RPC): the server speaks MCP over streamable HTTP at /mcp — there is no per-tool REST path. Call any tool with a tools/call envelope. The server is stateless, so no initialize handshake is needed:

curl -X POST https://api.prod.reason8.io/mcp \
  -H "Authorization: Bearer $REASON8_MCP_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "reason8_research_start",
      "arguments": {
        "artifact": "Launch announcement copy: <paste here>",
        "research_goal": "Decide if framing lands with technical buyers",
        "target_audience": "Mid-market CTOs evaluating AI tooling"
      }
    }
  }'

In practice you call tools through an MCP client, not raw curl — the example is just to show the wire format.

TypeScript (via MCP SDK):

const result = await client.callTool({
  name: "reason8_research_start",
  arguments: {
    artifact: "...",
    research_goal: "...",
    target_audience: "...",
  },
});

Tracking progress

Call reason8_research_status to follow the run. It holds the connection open for up to 50 seconds and returns the moment the workflow state changes (backed by Postgres LISTEN/NOTIFY). On timeout it returns status: "running" with poll_again_in: 50 — call again immediately. There is no separate SSE channel; the long-poll is the canonical mechanism. This polling applies only to the deep-research workflow — reason8_ask and reason8_review answer in the same call.

reason8_research_status

Get the current state of a run. Long-polls up to 50 seconds and returns as soon as the workflow status changes. Returns a slim payload tailored to the current state — the full memo is fetched separately with reason8_research_get_result.

Parameters: run_id (string, required).

Status enum: queued | launching | running | needs_input | brief_review | cancel_requested | completed | failed | cancelled.

Response shapes per state:

// running, launching, queued, cancel_requested
{ "status": "running", "stage": "synthetic_interviews", "poll_again_in": 50, "next_action": "call reason8_research_status again" }

// needs_input or brief_review
{
  "status": "needs_input",
  "stage": "clarify",
  "next_action": "call reason8_research_continue",
  "input_request_id": "ir_abc...",
  "input_request": { "questions": [/* ... */] }
}

// completed
{ "status": "completed", "stage": "finalised", "next_action": "call reason8_research_get_result" }

// failed
{ "status": "failed", "stage": "specialist", "next_action": "inspect error", "error_code": "...", "error_message": "..." }

// cancelled
{ "status": "cancelled", "stage": "cancelled", "next_action": "run terminated" }

// rate_limited (per-credential status DoS cap hit)
{ "status": "rate_limited", "retry_after_seconds": 5, "active_calls": 3, "next_action": "retry after delay" }

There is no result field on this tool — when status === "completed", call reason8_research_get_result.

reason8_research_get_result

Fetch the full research memo for a completed run. Call only after reason8_research_status reports status: "completed". Idempotent — call as many times as you like.

Parameters: run_id (string, required).

Returns:

{
  "status": "completed",
  "run_id": "r8run_abc",
  "mode": "research",
  "summary": "Short executive summary…",
  "response_markdown": "# Full memo in markdown…",
  "next_steps": ["Recommended follow-ups"],
  "agent_action_summary": "What Reason8 did and why",
  "synthetic_caveat": "Notes on synthetic-evidence limits",
  "metadata": { "methods": [/* ... */], "artifact_uri": "gs://..." },
  "response_truncated": false,
  "response_full_bytes": 12345,
  "created_at": "2026-05-28T16:00:00Z",
  "completed_at": "2026-05-28T16:14:22Z"
}

Errors:

  • result_not_ready — run is still pre-terminal. Call reason8_research_status.
  • not_found — run does not exist or belongs to another credential.
  • result_unavailable — run completed but the response artifact is missing. Open a support ticket with the run_id.
  • result_temporarily_unavailable — transient. Retry after a short delay.

reason8_research_continue

Send a follow-up to a run that is needs_input (clarification) or brief_review (approve / edit the research brief before paid methods run).

Parameters:

FieldTypeRequiredDescription
run_idstringyesThe run_id from start or status.
input_request_idstringyesMust match the pending input_request_id returned by status.
answersobjectyesPayload — shape depends on the pending state (see below).
client_idempotency_keystringnoStable key that makes this continue call safe to retry.

answers shape:

  • needs_input — map each question id from input_request.questions to your answer:
    { "audience_segment": "early-stage founders", "deadline": "before our 2026-06-15 launch" }
  • brief_review — approve or edit the brief Reason8 synthesized:
    // approve as-is
    { "approved": true }
    
    // edit before approving
    {
      "approved": false,
      "brief": {
        "research_brief": "...",
        "target_audience": "...",
        "output_format": "..."
      }
    }

Answer truthfully from repository / agent context. If you do not know, say so explicitly ("not supplied" / "cannot infer") — do not fabricate.

reason8_research_cancel

Hard-stop a run.

Parameters: run_id.

Human-in-the-loop briefs

Reason8 may pause a deep-research run for two reasons: it needs more info from you (needs_input), or it wants you to approve the research brief before kicking off paid methods (brief_review).

start ──▶ needs_input ◄──▶ continue(answers)


        brief_review

   continue(answers={approved: true})

          running ──▶ completed ──▶ get_result

When you see needs_input: call reason8_research_status to read input_request.questions and the input_request_id, then reason8_research_continue with answers mapping each question id to your answer.

When you see brief_review: call reason8_research_status to read the brief, then reason8_research_continue with answers: {approved: true} to accept — or answers: {approved: false, brief: {...}} to revise it.

This is the only place a run will wait on you. Once approved, Reason8 runs to completion (or failure) without further prompts.

Authentication

All requests use HTTP Bearer auth with a token issued from the portal Integrations page.

Header: Authorization: Bearer r8_mcp_xxx

Scopes (all portal-issued tokens carry all nine today):

  • reason8_research:run — start and continue research runs
  • reason8_research:read — read run status and results
  • reason8_research:cancel — cancel runs
  • reason8_ask:run — fast persona-panel reactions
  • reason8_review:run — fast screen reviews
  • reason8_journey:run — start a Journey Witness walk and submit steps
  • reason8_journey:synth — synthesize a Journey Witness walk
  • reason8_persona:seed — seed grounded persona sets
  • reason8_persona:list — list grounded persona sets (read-only)

Tokens issued before a scope existed don't gain it automatically — regenerate the token in the portal to pick up newer scopes (e.g. reason8_persona:list).

Token format: r8_mcp_<url-safe random>. The full secret is shown once in the portal — store it immediately. Reason8 keeps only a salted hash and a short lookup prefix server-side.

Rotation: Revoke the old token and generate a new one. There's no overlap window — issue the new one, update your client, then revoke.

Stateless transport: every request is authenticated independently by the Bearer header. There is no session handshake and no mcp-session-id header — don't wait for one.

Rate limits & concurrency

Workflow limits enforce on reason8_research_start. Status/continue/cancel are not rate-limited.

LimitValueEnv override
Starts per hour per credential5DEFAULT_CREDENTIAL_HOURLY_START_LIMIT
Starts per day per user20DEFAULT_USER_DAILY_START_LIMIT
Starts per day per tenant100DEFAULT_TENANT_DAILY_START_LIMIT
Concurrent active runs per user5DEFAULT_USER_MAX_ACTIVE_RUNS

Two distinct error families:

  • Start quotas (hourly per credential, daily per user, daily per tenant) → 429 rate_limited with a Retry-After header and retry_after_seconds in the body. Wait before retrying.
  • Active-runs cap (5 concurrent per user) → 409 concurrent_run_limit_exceeded. The error body includes active_run_summaries (each with run_id, status, stage, started_at, elapsed_seconds) and next_actions. Cancel a stale run with reason8_research_cancel instead of retrying.

Troubleshooting

401 invalid token — Token is wrong or revoked. Generate a fresh one in the portal.

403 scope missing / insufficient_scope — Token doesn't have the required scope for the tool you called (see Authentication for the nine scopes). Freshly portal-issued tokens carry all nine; an older token can miss a newer scope (e.g. reason8_persona:list for reason8_list_sets) — regenerate it to pick the new scopes up.

409 concurrent_run_limit_exceeded — You hit the active-runs cap. Inspect active_run_summaries in the error body and call reason8_research_cancel on a run you no longer need before retrying start.

409 idempotency_conflict — You reused a client_idempotency_key with different inputs. Either use a fresh key or call reason8_research_status on the original run.

Run stuck in running — Runs hard-fail at the 60-minute max runtime; the watchdog inspects candidates after a 45-second silence grace. If yours has been silent for hours and is still running, file a bug with the run_id.

Waiting for a session id that never comes — The server is stateless HTTP. initialize returns no mcp-session-id header by design; Bearer auth per request is all you need.

Panel says degraded or grounding_fell_back — The fast tools report honestly when they couldn't run the panel as requested (no matching grounded set, persona errors). Check degradation_reasons; seed a set with reason8_seed_persona or pass an explicit set_id.

No structured progress in your client — Some MCP clients do not render the slim status payload. Call reason8_research_status directly and print the stage and next_action fields yourself; the human-readable progress message lives on start's message field.

FAQ

How is this different from the REST API? MCP exposes the same capabilities as a set of agent tools with structured schemas — no client code to write. The REST API is for non-agent integrations (dashboards, scripts).

Can I use this without a paid account? Today, no — token issuance requires a workspace with billing configured. Contact support for evaluation access.

Are results cached? No. Each call triggers a fresh panel or research run. Deep-research results are stored in your workspace's research history.

Are the personas real users? No — they're synthetic. Grounding (reason8_seed_persona) anchors them in your real evidence and makes reactions citable against it, but synthetic reactions complement real user contact; they don't replace it for irreversible decisions.

Can I tune the research specialist? Not via MCP today. Workspace-level prompt customization is on the roadmap.

Where do I report bugs? Email support@reason8.io with the run_id and a description.