Feedback MCP server

Usero runs a remote Model Context Protocol server at https://usero.io/mcp. Point Claude Code, Cursor, Claude Desktop, OpenCode or Codex at it and the agent can read your feedback inbox, pull the clusters with verbatim quotes, file feedback, and ask Usero to open an AI-written pull request, all from inside your editor.

Nothing to install. One config block with the URL, plus an API key only on clients that need a static header. Fixes ship on our side. No account yet? The agent can sign you up itself, see the next section.

Set up from inside the agent

No account, no key, no dashboard visit. Pick your client:

Terminal
claude mcp add --transport http usero https://usero.io/mcp

Codex: codex mcp login starts the browser login.

Then send your agent this:

text
set me up with usero

What happens next depends on the client.

OAuth clients (Claude Code, Cursor, Claude Desktop, OpenCode, Codex). The client registers itself with the server, you approve the connection in the browser (it names the permission: read and file feedback, open pull requests, nothing else), and the credential stays with the client. Signing in emails you a six-character code: type it on the page where you started, so the connection finishes there even if you read the email on your phone. Nothing to paste, no key to store, and the agent skips the key-mint flow below. A new account starts with a "My First Client" client, as a web signup does. After you approve, the agent finishes setup itself: your product client plus the GitHub App install. With no code to hand (a claude.ai chat, say), ask it to "use Usero to make a short feedback form for my product and give me the link to share": create_form returns a public link you can send to users straight away.

Key clients (older builds, anything needing a static header). Without a key the server exposes two tools, start_signup and check_signup, and the agent runs this flow:

  1. The agent shows you a short code and sends a one-button email to your address. Press the button within 15 minutes, type the code on the page that opens and confirm. That signs you in, creating the account if the email is new. Someone who opens the email without the code cannot complete the sign-in.
  2. The agent collects its API key and finishes setup itself: your product client plus the GitHub App install.

An existing account signing in this way gets a new key and nothing else changes. Limits: 5 links per email per hour, 10 per IP per hour. Every other tool answers 401 until a key is sent.

Get an API key

  1. Sign in and open your profile.
  2. Under API keys, create a key. Name it after the tool that will use it ("Claude Code on my laptop").
  3. Copy it once. Keys look like usk_live_... and are only shown at creation.

The key acts as you: the agent sees every client you are a member of and nothing else. Revoke it from the same page.

Connect your client

Same blocks with your key in the header. Replace usk_live_... with your key:

Terminal
export USERO_API_KEY=usk_live_...   # in ~/.zshrc or ~/.bashrc
claude mcp add --transport http usero https://usero.io/mcp --header 'Authorization: Bearer ${USERO_API_KEY}'

Claude Code: the single quotes matter, the shell leaves ${USERO_API_KEY} alone and the command stores it as written (check with claude mcp get usero). Add --scope user for every project, verify with claude mcp list, then try claude -p "list my usero clients". For a team config, commit .mcp.json instead (that is the only form Anthropic promises expansion for):

json
{
	"mcpServers": {
		"usero": {
			"type": "http",
			"url": "https://usero.io/mcp",
			"headers": {
				"Authorization": "Bearer ${USERO_API_KEY}"
			}
		}
	}
}

Cursor: reload the window, Settings then MCP should show a green dot and a tool count.

Desktop, step by step: Settings → Connectors → Add custom connector, name it Usero, paste https://usero.io/mcp. Authentication → Sign in now. OAuth client → Register automatically (not the Recommended published identity, the server does not support it yet). Add: the browser opens, enter your email, then type the sign-in code from the email on that page (or press the email button if you are reading it in the same browser), and approve. "Sign in when needed" also works: the server asks Claude to sign in on its first request. No request headers needed, OAuth carries the credential. Restart Desktop and ask again to prove the token refreshes.

Anything else

Any client that speaks MCP over streamable HTTP works. The server is stateless JSON-RPC 2.0 over POST, no session id, no SSE. A raw call looks like this:

bash
curl -s https://usero.io/mcp \
  -H "Authorization: Bearer usk_live_..." \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Tools

Every tool is scoped to the clients your key can access. Read tools are marked read-only so clients that ask before running side effects can skip the prompt. Argument schemas are strict: an unknown argument is an error, not a silent no-op. Date arguments accept a full ISO datetime or a bare YYYY-MM-DD (read as midnight UTC).

Tool Arguments What it returns
start_signup email, clientName? No key needed. Emails a sign-in link naming the agent and replaces any earlier pending link for that address; returns pollToken, userCode (show it to the user, the confirm page asks for it) and expiresInMinutes. 5 per email per hour, 10 per IP per hour.
check_signup pollToken No key needed. pending until the user types the code and confirms on the emailed page, then ready with apiKey (minted at that moment) exactly once; the token is dead afterwards. Expired tokens return an error naming the fix.
create_client name, repo? Creates a client (one per product) and returns its clientId and dashboard URL. repo only works if the GitHub App already reaches it; otherwise leave it out and use connect_github.
connect_github clientId, repo? Returns the GitHub App install URL bound to the client. Once installed, repo picks which of the covered repositories PRs go to (single-repo installs are picked automatically).
check_github clientId pending until the install lands, then connected with the chosen repo (or null) and every repos the installation covers.
list_clients nameContains?, limit? (default 25, max 100), offset? A page of your clients, newest first, each with id, name, open feedback count, environment names (busiest first) and dashboard URL, plus totalCount, hasMore and nextOffset. Call this first.
search_feedback clientId, environment?, query?, status? (open, resolved, all), source?, since? (created), resolvedSince? (resolved), sort? (newest, oldest, severity), hasScreenshot?, limit? (max 50) Feedback with AI summary, category, severity, verbatim quotes, sender, screenshot count, replay flag, resolvedAt and resolutionMessage, and dashboard URL. Defaults to open items, newest first. Comment bodies cut at 400 chars.
get_feedback id One item in full: comment, quotes, person, screenshots, session replay link, clusters, pull requests, and linked tracker issues (issues: [{ tracker, identifier, url, status }]).
list_clusters clientId, includeAddressed?, limit? (max 100) Feedback clusters biggest first with severity, urgency, AI summary, up to three sample verbatim quotes each, and PR state. Clusters span environments.
get_cluster clusterId, memberLimit? (default 50, max 200) The cluster with its members (highest confidence first), each with quotes and sender, open and total member counts, plus its pull request if any.
list_unanswered_board_posts clientId, minDaysSilent? (default 0), limit? (default 25, max 100) Public board posts with no official team reply and no status change since they were posted (merged and hidden posts excluded), most voted first then oldest, each with its public URL, votes and daysSilent. Also total, boardName, boardUrl and the reminder setting (enabled, days).
add_post_vote clientId, postId, email, name?, priority? (nice_to_have, important, must_have), note?, sourceFeedbackId? Logs a vote on a board post for a user who asked somewhere else (ticket, email, call). Counts toward the post total and cluster demand; the user is not emailed at vote time, only when the post status changes. Returns voteId, voteCount and alreadyVoter (true, with no new vote, when that email already voted on the post). The note is private to the team.
list_post_voters clientId, postId Every voter on a board post: email and name where known, company (name, source: trait or domain, null for free-mail and anonymous voters), priority, private note, selfCast (false when an admin or agent logged it), who logged it and the source feedback id. Cookie-only board visitors appear as anonymous rows so the list matches voteCount. companies groups the voters: companyCount, individualCount, anonymousCount and per company voterCount, mustHaveCount, mrr (highest among its people) and voteIds. Company comes from identify() traits (company, companyName, company_name, organization, grouped by companyId when set), else the work email domain.
set_board_post_target clientId, postId, targetMonth (YYYY-MM or null to clear), targetPublic? Set, move or clear the month a board post is aimed at. Voters see a chip on the post, board and roadmap: Target Nov 2026, Target Nov 2026, running late once the month passes unshipped, or Was Nov 2026, now Jan 2027 after a move (targetMovedFrom keeps the previous month). targetPublic hides the chip for that post. Returns the post with targetMonth, targetMovedFrom, targetPublic, state (none, upcoming, late, moved) and its public url.
create_feedback clientId, environment?, title, body, pageUrl?, userEmail? Files a feedback item with source mcp. It is classified and clustered like any other item.
note_replay_moment clientId, sessionReplayId? or userTestSessionId? (exactly one), replayAtMs, title, description?, severity? (high, medium, low), pageUrl?, quote? Files a feedback item with source replay-note, pinned to a millisecond of a session replay or a human user test recording; the replay link on the item opens at that second and the quote is stored as evidence. Identity comes from the replay's person (or the tester for a user test). Classified and clustered like any other item.
send_feedback_about_usero title, body, pageUrl?, userEmail? Files feedback about Usero itself into the Usero team inbox with source mcp. No clientId needed; any authenticated key may call it. It returns the id and creation time only, no dashboard link. Use this for Usero bugs and ideas, create_feedback for the caller's own product.
connect_app_reviews clientId, appleAppId?, appleCountry? (default us), playPackageName? (at least one store id required) Connects App Store or Google Play review sync through the same path as the dashboard Integrations page, then returns at once while the import runs in the background; poll app_reviews_status for progress. Additive only, disconnect stays in the dashboard. Apple RSS blocks Workers egress, so prod Apple syncs often report a 403 in lastSyncError while Play imports land first.
app_reviews_status clientId Read-only status of app review sync: connected config, per-store imported counts, lastSyncAt, lastSyncError and the sync phase with live per-store progress.
intercom_status clientId Read-only status of the Intercom integration: status (connected, disconnected, never_connected), workspaceId, the saved filter (tagAllowlist, includeTickets, ticketTypeAllowlist), lastSyncedAt (wall clock of the last sync run), lastPolledAt (the incremental cursor: updated_at of the newest item ingested, not the run time), importedCount with importedTickets and importedConversations, a plain-language diagnosis of the latest run, and the last 5 syncRuns (fetched, matched, new, duplicate, error). Never returns tokens.
sync_intercom clientId Queues an Intercom sync now through the same background path as the dashboard's Sync now, then returns queued: true at once; poll intercom_status for the new run and counts. Errors when Intercom is not connected; connecting (OAuth) stays in the dashboard Integrations page.
get_usage clientId Read-only usage this month for the account that owns the client: plan (free, pro, team), noLimits, and meters (sessionReplays, replayAnalyses, aiUserTests, humanTests, issues, draftPRs), each with used and limit. limit: null means no limit; used: null means not counted. Amounts are per account across all of the owner's clients and follow the owner's plan. feedback is always unlimited. Months run on UTC.
get_pr_status feedbackId Whether an AI pull request exists for the item, its status, URL and progress.
request_ai_pr feedbackId, guidance? Usero's agent writes and opens a pull request server-side on the client's connected GitHub repo; the caller needs no local checkout. Returns a prId. Counts against the monthly draft PR amount on the client owner's plan.
create_issue feedbackId, title?, body?, tracker? (github, linear, jira; default is the client's selected tracker), demandDetail? (full, counts, none; can only narrow the default) Creates a GitHub, Linear or Jira issue from a feedback item through the same path as the dashboard's "Create issue" button and links it. Title and body default to the dashboard draft (comment, page, severity, link back), plus a Demand section when the feedback has a linked board post with 2+ votes: full detail on Linear, Jira and private GitHub repos, counts only on a public or unknown-visibility repo. Returns the identifier (#123, ENG-12 or PROJ-123), URL, tracker and demand (included, mode full or counts, voteCount); a feedback item that already has an issue on that tracker comes back with alreadyLinked: true. Needs GitHub, Linear or Jira connected; the error names the connected trackers otherwise.
list_forms clientId Hosted forms and surveys with response counts and public URLs.
list_form_responses clientId, formId, page?, limit? (max 50) Responses to one form, newest first, with every answer.
get_form formId One form in full: fields with ids, types and options, settings, published state, response count, public URL, embed URL, builder URL.
create_form clientId, title, description?, fields, settings?, published? Creates a form or survey and returns it with its public URL, live immediately. fields is a typed list (see below); settings.surveyMetric scores it as NPS, CSAT or CES.
update_form formId, title?, description?, fields?, settings?, published? Edits a form. Only passed arguments change; fields replaces the whole list (keep ids from get_form), settings merges. published: false closes it without losing responses.
delete_form formId Deletes a form and every response to it. Irreversible; announced as destructive so clients ask first.
get_form_theme_options formId? Read-only reference pack for theming: the appearance schema, every preset as a full object, the texture library, layouts, font pairings, contrast rules and three worked briefs. With formId, that form's current appearance too.
preview_form_theme formId, appearance (full object or { preset }) Validates the theme, runs the contrast gate and returns a signed one-hour previewUrl that renders the form with it. Nothing is saved; the preview page cannot be submitted. ok is false when a row blocks saving.
get_form_analytics clientId, formId Aggregate funnel for one form: sessions (unique visitors), views (page loads), submit attempts and successes, completion rate, average session duration, per-field completions and response count. Use list_form_responses for individual answers. Events capped at the most recent 10,000 (truncated says when hit).
import_github_issue clientId, issueUrl Imports a GitHub issue as a feedback item (title as comment, body and labels in metadata) that appears in inbox search and clusters. Use only for a GitHub issue URL; use create_feedback for pasted text. The URL must belong to the client's connected repo; repeats return the existing item with alreadyImported: true. Needs GitHub connected.
list_ai_user_test_runs clientId, environment? (default environment when omitted), limit? (default 20, max 50) AI user test runs newest first with verdict (clean is the only pass), summary, finding counts and schedule flag. Read-only; starting runs and scheduling stay in the dashboard.
get_ai_user_test_run clientId, runId, includeSteps? (default false) One AI user test run with verdict (clean means no issues found), summary, whether on a schedule, and every finding (severity, category, what happened, timestamp to jump to in the replay); the step trace only when includeSteps is true. Starting runs and scheduling stay in the dashboard.
create_user_test clientId, name, targetUrl, tasks (1 to 20 prompts), introMessage?, rewardDollars?, rewardCurrency?, minDurationSeconds? (default 30), referralBonusDollars?, referralCapPerReferrer? (default 5, 1 to 50) Creates a human user test and returns it with the participant shareUrl (/ut/<slug>), live immediately. Omit rewardDollars for an unpaid test. referralBonusDollars turns on referrals for a paid test (at most the reward, same currency): people you add with add_referrer can send others into it, and the bonus is owed per referred person you pay.
list_user_tests clientId, limit? (default 20, max 50) Tests newest first with share URL, reward, task count, session counts by status, the readyToPayCount awaiting payout, and referrals (enabled, bonus, cap, counts by status, bonus totals per currency).
get_user_test clientId, testId One test in full: tasks in order, session counts, referrals (enabled, bonus, cap, counts by status, bonus totals per currency), plus the 20 most recent sessions with status, payment state, quality flag and duration.
list_user_test_sessions clientId, testId, status?, paymentStatus?, limit? (default 20, max 50) Sessions newest first with tester, status, payment state, quality flag, duration, audio flag, completed-task and note counts, auto-release deadline, referral (who referred the tester, bonus status and flags) and referrer (friends this tester referred).
get_user_test_session clientId, sessionId, fullTranscript? (default false) One session in full: tester, payment and payout details, per-task completions, notes with timestamps, first 2000 chars of transcript (fullTranscript: true returns the whole text plus timed segments on the replay clock), findings list (each with verbatim quote, replayAtMs and the inbox feedbackId it was filed as), replay id plus the audio offsetMs (never audio bytes), muted segments, end note, referral and referrer as in list_user_test_sessions. Needs only sessionId, no testId.
search_user_test_transcripts clientId, testId, query (2 to 200 chars), limit? (max matching segments per session, default 5, max 20) Every session of a test whose transcript or filed insights contain the phrase: verbatim transcript segments with one neighbour either side (context: true), each with replayAtMs and a replayUrl that opens the session at that moment, plus matching insights shaped like get_user_test_session findings. Only what participants said and what was filed, never a generated answer. Sessions with no match are omitted; sorted by hit count, top 25 returned, sessionsOmitted counts the rest.
update_user_test clientId, testId, name?, targetUrl?, introMessage?, tasks? (1 to 20 prompts), rewardDollars?, rewardCurrency?, minDurationSeconds?, referralBonusDollars?, referralCapPerReferrer? Edits a test. Only passed arguments change; tasks replaces the whole list (refused once the test has sessions, to protect completion history). introMessage: "" clears it, rewardDollars: "" makes the test unpaid and turns referrals off, referralBonusDollars: "" turns referrals off. A bonus change applies to friends not yet paid.
delete_user_test clientId, testId Deletes a test and its tasks; the share link stops resolving. Irreversible; announced as destructive so clients ask first. Refused when the test has any sessions, keeping session history.
release_payment clientId, sessionId Confirm with the user first, naming the tester and reward. Marks a ready_to_pay session paid and returns the new status with paidAt. Anything else returns an error naming the current status. This only marks paid, actual money movement happens off-platform. On a test with referrals, paying a referred person makes their referrer's bonus ready to pay (or flagged for review).
add_referrer clientId, name, email, note? (private) Adds someone (a tester or not) who can refer people into the client's open referral tests and returns their shareUrl. Usero emails them the link once, with a private status page; replies go to the client's contact email. An email that is already a referrer is returned unchanged (created: false).
list_referrers clientId The client's referrers newest first: name, email, note, whether the link is on, friendsReferred, shareUrl.
set_referrer_disabled clientId, referrerId, disabled Turns a referrer's link off or on. Off cancels their unpaid bonuses (returns voided) and blocks new joins; paid bonuses stay paid; on does not restore cancelled ones.
list_referrals clientId, testId?, status? (joined, testing, earned, flagged, paid, void), limit? (default 20, max 50) Referred friends, what needs action first: flagged (needs review, fraud flags in words), earned (ready to pay), then testing, joined, paid and void. Each row has the referrer, the friend, the test, the friend's session and payment status, the bonus and bonusPayoutDestination.
review_referral clientId, referralId, decision (approve, reject), disableReferrer? (only with reject) approve moves a flagged bonus to earned; reject voids a flagged or earned one. A paid bonus cannot be rejected. disableReferrer: true also turns off referrals for that referrer, voiding their other unpaid bonuses. A repeated decision returns alreadyDone: true; other errors name the current status.
release_referral_bonus clientId, referralId Confirm with the user first, naming the referrer, the friend and the bonus. Marks an earned bonus paid and emails the referrer; an already paid bonus returns alreadyDone: true with no second email, anything else returns an error naming the current status. This only marks paid, actual money movement happens off-platform.
list_pending_board_posts clientId Public-board posts and comments held for review (the "Hold new posts for review" board setting), each with author, excerpt and id, plus the dashboard queue URL.
moderate_board_post clientId, id, decision (approve, reject), kind? (post, comment; default post) Approves or rejects a held post or comment. Approving a post publishes it, mirrors it into the feedback inbox and emails the author. Rejected items stay hidden but are kept.

The environment argument takes the environment name you send from the widget. Omit it to search every environment. Use the literal no-env for feedback that was sent without one.

Build a form from the agent

create_form takes the questions as a typed list, so an agent can build a survey from a sentence without reading any source. Each field is an object with a type and the options that type supports:

type Extra arguments Answer stored as
text, textarea, email placeholder? string
number placeholder?, min?, max? number
select, radio options (1 to 50 strings) the chosen option text
multiselect, checkbox options (1 to 50 strings) array of option texts
rating max? (stars, default 5) integer 1 to max
scale min? (default 1), max? (default 5), minLabel?, maxLabel? integer min to max

Every field also takes label (required), description? (help text), required? (default false), id? and condition?. Ids are the keys answers are stored under; omit them and they are generated from the label. A condition ({ fieldId, operator, value? }, operators equals, not_equals, contains, not_empty) shows the field only when an earlier field's answer matches.

settings is optional: surveyMetric (nps needs a scale 0 to 10, csat a rating, ces a scale 1 to 7; turns on the score panel and mirrors responses into the feedback inbox), routeResponsesToInbox, themeColor, successMessage, submitButtonText, notifyOnSubmission, and appearance, the brand theme of the public page. Pass a preset, { "preset": "minimal" | "dark-slate" | "warm-editorial" | "clean-saas" | "high-contrast" }, and the server stores the expanded theme; get_form returns it in full (colorMode, background, surface, colors, typography, motion, optional https logoUrl) so an agent can change one key and send the object back through update_form. appearance: null returns the form to the plain themeColor rendering. A theme whose text contrast fails is refused on a published form; unpublish first or fix the colors.

Ask the agent for "an NPS survey with a follow-up question" and it calls:

json
{
	"clientId": "client_...",
	"title": "How are we doing?",
	"description": "Two questions, thirty seconds.",
	"fields": [
		{
			"id": "nps-score",
			"type": "scale",
			"label": "How likely are you to recommend us to a friend or colleague?",
			"required": true,
			"min": 0,
			"max": 10,
			"minLabel": "Not likely",
			"maxLabel": "Very likely"
		},
		{
			"id": "nps-reason",
			"type": "textarea",
			"label": "What's the main reason for your score?",
			"placeholder": "A sentence or two"
		}
	],
	"settings": { "surveyMetric": "nps" }
}

The result carries publicUrl (https://usero.io/f/<slug>, live at once), embedUrl for an iframe, and builderUrl for the dashboard. To change it later the agent calls get_form, edits the field list and sends it back whole with update_form, reusing the ids so responses already collected stay attached to their questions. update_form with published: false pauses collection; delete_form removes the form and its responses for good. The same field shape is accepted by the REST forms API.

Theme a form from a brief

Ask for "make the signup survey feel like a warm print newsletter" and the agent works in three steps, none of which needs the dashboard:

  1. get_form_theme_options (once per session, optionally with the formId) returns everything the theme can be built from: the appearance schema with its enums and ranges, the six presets as complete objects, twenty Usero-authored page textures with a one-line blurb and strength range each, the three layouts (card, hero, focused), six font pairings, the contrast rules, and three worked briefs already turned into appearance objects.
  2. The agent composes an appearance (usually a preset or example with a few keys changed) and calls preview_form_theme. The response carries the contrast report and a previewUrl of the form https://usero.io/f/<slug>?preview=<token>. The token is an HMAC-signed copy of the theme, valid for one hour and bound to that form; opening it renders the real form with the candidate theme, a "Preview, not saved" strip, noindex, and a disabled submit button. Nothing is written and no response can be filed through it. Expired or altered links show a friendly expiry page, never the stored theme.
  3. When the user likes it, update_form with settings: { appearance } saves it. A published form refuses a theme with a blocking contrast row (below 3:1 on body, input, error, button, or page text for the hero and focused layouts), so the agent fixes those from the report before saving. Embeds (?embed=1) always render the card layout on a transparent page, so the texture and layout only show on the hosted page.

Resources

Two resource templates let an agent pin data into its context without a tool call:

  • usero://clients/{clientId}/clusters: the open clusters for a client, same JSON as list_clusters.
  • usero://feedback/{id}: one feedback item, same JSON as get_feedback.

Prompts

Four prompt templates. Each takes an optional clientId; when omitted and your key has exactly one client, that client is used, otherwise the prompt tells the agent to pick one via list_clients:

  • get_first_feedback: for a new account. Asks what the product is, builds a short feedback form with create_form and hands back the public link to share, then mentions the widget for anyone with a codebase.
  • triage_inbox: read the open feedback, group it into themes with verbatim quotes, recommend what to fix first.
  • fix_top_complaint: pick the biggest concrete cluster, find the cause in the current repo, fix it or call request_ai_pr.
  • write_changelog_from_feedback: turn the last two weeks of resolved feedback into a short user-facing changelog entry.

In Claude Code they appear as /usero:triage_inbox and so on once the server is added.

What agents can and cannot do

Can: sign you up and mint their own key; create clients and connect GitHub to them; read every feedback item, cluster, form and response for your clients; file new feedback; connect App Store and Google Play review sync and read its status; read Intercom sync status and queue an Intercom sync; build, edit, close and delete hosted forms and surveys; read form analytics and AI user test runs with findings; import a GitHub issue as feedback; create a GitHub, Linear or Jira issue from a feedback item; log a vote on a public board post on behalf of a user and list a post's voters; run paid human user tests (create, edit and delete tests, review sessions, search transcripts for a phrase, release payment); request an AI pull request on a client that has GitHub connected.

Cannot: resolve or delete feedback, change client settings, connect integrations other than GitHub and app reviews (Intercom connects in the dashboard, the agent can only sync it), invite members, touch billing, or see clients you are not a member of. Those stay in the dashboard on purpose. If your workflow needs one of them from an agent, tell us through the contact page.

Rate limits and caps

Limit Value What happens past it
Tool calls 120 per API key per minute The tool returns an error naming the seconds to wait.
start_signup 5 per email and 10 per IP, hourly The tool returns an error; check the inbox for an earlier link.
Draft PRs the client owner's monthly amount The tool returns an error with the reset date and an upgrade link.
Real-person user tests the client owner's monthly amount create_user_test returns the same error; existing tests keep taking participants.
search_feedback results 50 per call Narrow with query, since, resolvedSince or environment.
get_cluster members 50 by default, 200 max membersTruncated is true and memberCount has the full size.

Troubleshooting

HTTP 401 on every call. The Authorization header is missing or the key is wrong. It must read exactly Bearer usk_live_.... Keys are shown once at creation; if you lost it, create a new one on your profile, or remove the header and let the agent call start_signup for a fresh one.

check_signup says the token is unknown or expired. Links and poll tokens last 15 minutes from start_signup, and sending a new link expires the previous one. Ask the agent to call start_signup again and open the newest email.

HTTP 406 from curl. The transport requires Accept: application/json, text/event-stream on every POST (both types, in one header). Copy the curl example above.

list_clients returns an empty list. The key is valid but its user is not a member of any client, and the result's next tells the agent to call create_client. If you expected a client, sign in to the dashboard with the same account and check you can see it there. Keys from a teammate's account see the teammate's clients, not yours.

A tool says "Client ... not found". Either the id is mistyped (ids start with client_) or the key's user is not a member of that client. The same message covers both so ids cannot be probed.

request_ai_pr says GitHub is not connected. Connect the repo on the client's Integrations page (the error includes the link), then call again. The PR is opened by the Usero GitHub App, not by your agent's credentials.

Claude Code shows the server but no tools. Run claude mcp list and check the transport is http, not sse. Remove and re-add with --transport http if needed.

Cursor shows a red dot. Cursor reads .cursor/mcp.json on startup. Reload the window after editing it, and make sure the file is valid JSON (a trailing comma is the usual culprit).

Responses are slow the first time. The first call after a quiet period warms the Worker and the database. Later calls in the same minute are fast.

Privacy and security

The MCP server is the same Worker and database as the dashboard. Only the SHA-256 hash of your key and its first 12 characters are stored; each request's key is hashed and matched against that, never written. The server makes no LLM calls of its own: it is a deterministic interface over your data, and the agent on your side does the thinking.

FAQ

OAuth or API key? Claude Code, Cursor, Claude Desktop, OpenCode and Codex connect with OAuth: the client registers itself (dynamic client registration, no client ID to configure), you approve in the browser, and the tokens expire. There is nothing to paste and no secret lands in chat transcripts or config files. Older builds that still need a static header use API keys instead: create them one at a time on your profile, make one per client, name it after the machine, and revoke exactly that one when the machine goes. Usero stores the SHA-256 hash of the key plus its first 12 characters for display. On each request the key in the header is hashed and matched against that hash; the raw key is never written anywhere. Either credential acts as you, so the agent sees the clients you are a member of and nothing more.

I signed up through the agent. How do I sign in to the dashboard? The browser tab where you pressed "Yes, connect" is already signed in, so open usero.io there. From any other browser or device, the account has no password yet (the agent flow never asked for one): use Forgot password with the same email to set one, then sign in as normal. The same goes for accounts created through the OAuth browser sign-in.

How much of my context does this use? Measured against the live server on 2026-09-05 with cl100k tokenisation, so treat the numbers as approximate: about 300 tokens in Claude Code, about 3,200 in Cursor or Claude Desktop. Claude Code 2.1 defers MCP tool definitions and loads only the tool names and the server instructions at session start (tool search), which for the 13 tools an authenticated key sees is 43 tokens of names plus 252 of instructions. Clients that load every schema up front take the whole tools/list response, 2,957 tokens of JSON, plus the instructions.

What does a real session look like? A full claude -p transcript against the live Usero inbox, every tool call included, is in the blog post.

Changelog

Date Change
2026-10-19 v1.28.0: demand on the ticket. create_issue adds a ### Demand section to the drafted body when the feedback has a linked board post with 2+ votes (votes, companies, MRR, priorities and two voter quotes on Linear, Jira and private repos; counts only on a public or unknown-visibility GitHub repo; never an email), takes demandDetail (full, counts, none) to narrow it, and returns demand saying whether it went in and in which mode.
2026-10-15 v1.27.0: votes grouped by company. list_post_voters returns a company per voter and a companies summary (company count, voters, must-haves and MRR per company, individual and anonymous counts). Company comes from identify() traits, else the work email domain; free-mail voters count as individuals.
2026-10-14 v1.26.1: tools act with the key owner's role on the project. A Viewer's key can call every read tool; a tool that changes something returns "You have view-only access to this project. Ask an owner or admin to change your role." Owners, admins and members are unchanged. The REST API does the same with a 403.
2026-10-14 v1.26.0: get_usage reads this month's usage for the account that owns a client: the plan and each meter (session replays, AI replay analysis, AI user tests, user tests with real people, issues, draft PRs) with used and limit. Feedback is always unlimited. Read-only; nothing is blocked yet.
2026-10-14 v1.25.1: tool annotations say more. Tools that email someone, open a GitHub PR or issue, or change something public (board, forms, user tests) are announced with openWorldHint: true; tools that overwrite, publish or mark money paid (update_form, update_user_test, moderate_board_post, release_payment, release_referral_bonus, review_referral, set_referrer_disabled) are announced destructive so clients ask first. Every tool carries _meta.securitySchemes. The OAuth server returns iss on every authorization response (RFC 9207).
2026-10-13 v1.25.0: user test referrals. create_user_test and update_user_test take referralBonusDollars and referralCapPerReferrer; list_user_tests and get_user_test return referrals settings, counts and bonus totals; list_user_test_sessions and get_user_test_session return referral and referrer per session. New add_referrer, list_referrers and set_referrer_disabled (referrers are added by the client and need not be testers), and list_referrals, review_referral and release_referral_bonus (confirm with the user first).
2026-10-11 v1.24.0: a first step that needs no code. New get_first_feedback prompt (form plus share link). list_clients returns next when no listed client has any feedback or form yet, pointing at create_form and its publicUrl before the widget; the no-clients next, the create_client next and the OAuth line of the server instructions name create_form too.
2026-10-10 v1.23.2: Claude's connector (claude.ai and Claude Desktop) gets the OAuth sign-in on plain https://usero.io/mcp. A request with no Authorization header from Anthropic's published outbound range is answered with the 401 OAuth challenge instead of the keyless signup tools, because Claude starts its sign-in on a 401. Every other client without a key still gets the signup tools.
2026-10-09 v1.23.1: list_clients with no clients returns next, pointing the agent at create_client and then the widget. Server instructions tell an OAuth-connected agent it is already signed in and should skip signup. A new account created through the OAuth browser sign-in starts with a "My First Client" client, like a web signup.
2026-10-08 https://usero.io/mcp/oauth is the same server with the OAuth challenge and no query string, and the Claude Code plugin (v0.12.1) connects there. ?auth=oauth keeps working; the plain URL is unchanged.
2026-10-03 v1.23.0: every tool has a human-readable title (for example "Search Feedback", "Request AI Pull Request"), sent as both the tool title and annotations.title so clients can show it instead of the snake_case name. The create_feedback, create_issue and send_feedback_about_usero descriptions name where the item lands.
2026-10-03 https://usero.io/mcp?auth=oauth answers a request with no Authorization header with the 401 OAuth challenge instead of the keyless signup tools, so the client opens its browser sign-in. The Claude Code plugin (v0.12.0) connects this way and no longer reads USERO_API_KEY. The plain URL is unchanged.
2026-09-29 v1.22.0: device-code binding for signup. start_signup returns a userCode (ABC-DEF); the agent shows it to the user, who types it on the emailed confirm page before pressing "Yes, connect". Five wrong codes expire the link. Server instructions and the start_signup description name the code step.
2026-09-19 v1.21.0: create_issue accepts tracker: "jira" and opens a Jira issue on the connected project (returns the PROJ-123 key); the not-connected error names Jira alongside GitHub and Linear. Server instructions Fixes line updated.
2026-09-18 v1.20.0: vote on behalf of a user. add_post_vote logs a vote on a board post for someone who asked in a ticket, email or call (optional priority and private note, alreadyVoter on repeats, feeds cluster demand, voter emailed only when the post status changes); list_post_voters shows who is behind a post's count.
2026-09-18 v1.19.0: public-board moderation. list_pending_board_posts lists posts and comments held by the "Hold new posts for review" board setting; moderate_board_post approves or rejects one.
2026-09-18 v1.18.0: set_board_post_target sets, moves or clears the target month on a public board post (YYYY-MM, with targetPublic to hide the chip); list_unanswered_board_posts rows gain targetMonth, targetMovedFrom and targetPublic. Server instructions Board line mentions the new tool.
2026-09-18 v1.17.0: search_user_test_transcripts searches every participant transcript in a user test for a phrase and returns the verbatim segments (with context, replayAtMs and a deep link into the replay) plus matching filed insights, never a generated answer; get_user_test_session takes fullTranscript: true for the whole transcript with timed segments on the replay clock.
2026-09-18 v1.15.0: Intercom from the agent. intercom_status reads connection status, workspace id, the saved tag and ticket filter, lastSyncedAt (run wall clock) and lastPolledAt (newest item cursor), imported conversation and ticket counts, a plain-language diagnosis and the last 5 sync runs; sync_intercom queues a sync through the dashboard's Sync now path and returns at once. Connecting stays in the dashboard. Server instructions gain an Intercom line.
2026-09-17 v1.14.0: list_unanswered_board_posts lists public board posts still waiting on a team reply (no official comment, no status change), most voted first, with public URLs, days silent and the client's reply-reminder setting. Server instructions gain a Board line.
2026-09-12 v1.13.0: create_issue opens a GitHub or Linear issue from a feedback item through the dashboard's create path and links it (alreadyLinked: true when one exists on that tracker); get_feedback now lists linked tracker issues under issues.
2026-09-12 v1.12.0: note_replay_moment files a feedback item pinned to a moment of a session replay or a human user test recording (source replay-note). Same helper as the dashboard's "Note this moment"; the item's replay link opens at that second.
2026-09-09 v1.11.0: app review sync from the agent. connect_app_reviews saves the store config and queues a background sync through the same path as the dashboard (additive only, disconnect stays dashboard-only); app_reviews_status reads the connected config, per-store imported counts, last sync time and error, and the sync phase. Apple RSS blocks Workers egress, so prod Apple syncs often report a 403 while Play imports land first.
2026-09-09 v1.10.0: onboarding surface slimmed. Server instructions are a one-line-per-workflow router with the no-key bootstrap first; start_signup names check_signup and the two-click confirm, check_signup names key saving and create_client, connect_github names the check_github call, and the check_signup key-saving steps are client-neutral with a pointer to the per-client setup docs.
2026-09-09 v1.9.0: send_feedback_about_usero files feedback about Usero itself into the Usero team inbox. No clientId needed, any authenticated key may call it, and it returns no dashboard link. create_feedback stays scoped to the caller's own product inbox.
2026-09-07 v1.8.0: full user-test lifecycle from the agent. update_user_test edits a test (partial patch, tasks replaced whole and refused once sessions exist so completion history survives); delete_user_test removes a session-less test and its tasks (destructive, refused when sessions exist).
2026-09-06 v1.7.0: REST parity plus AI test reads. get_form_analytics returns sessions, views, completion rate and per-field completions for one form; import_github_issue files a GitHub issue as feedback (same-repo guard, idempotent on repeats); list_ai_user_test_runs and get_ai_user_test_run review runs with verdicts and findings (starting runs and scheduling stay dashboard-only).
2026-09-06 v1.6.0: run paid human user tests from the agent. create_user_test returns the participant share link (/ut/<slug>); list_user_tests and get_user_test review tests with session states and counts; list_user_test_sessions and get_user_test_session review sessions (transcript excerpt, replay pointer, findings, quality flags, no audio bytes); release_payment marks a ready_to_pay session paid with the same guard as the dashboard, confirm with the user first.
2026-09-05 v1.5.0: theme a form from a brief. get_form_theme_options returns the appearance schema, presets, textures, layouts, pairings, contrast rules and worked examples; preview_form_theme validates a theme and returns a signed one-hour preview URL (/f/<slug>?preview=<token>, HMAC-SHA256, no database write, submissions disabled). update_form stays the only write.
2026-09-05 v1.4.0: create_form and update_form accept settings.appearance (a preset name or the full theme, null to clear); get_form returns it. The contrast gate that guards publishing in the dashboard applies to MCP edits too.
2026-09-05 Signup email redesigned around one button; start_signup returns emailSentTo and emailSubject, check_signup pending says which inbox to check, and ready leads with the USERO_API_KEY form of claude mcp add, removes an existing usero server only when one is configured, and returns docsUrl.
2026-09-05 v1.3.0: build forms from the agent. create_form (typed field list, survey scoring via settings.surveyMetric), get_form, update_form (partial patch, fields replaced whole, settings merged) and delete_form (announced destructive). The REST forms API now also accepts fields and settings as JSON, not only as JSON strings.
2026-09-04 v1.2.1: the emailed link opens a confirm page with a button; opening the page alone no longer signs anyone in, and the key is minted only when check_signup collects it. A new start_signup replaces any earlier pending link for that address. list_clients pages (limit, offset, nameContains) and returns totalCount and hasMore; per-client feedbackCount, githubRepo and createdAt dropped.
2026-09-04 v1.2.0: sign up from inside the agent. start_signup and check_signup work with no API key (emailed confirm page, key minted when the agent collects it, once). create_client, connect_github and check_github finish setup. Server instructions lead with the no-key path.
2026-09-04 v1.1.0: create_pr renamed request_ai_pr. search_feedback gains resolvedSince, sort, hasScreenshot, screenshotCount, resolvedAt, resolutionMessage and 400-char bodies. list_clusters gains sampleQuotes. get_cluster gains memberLimit (default 50). list_clients gains openFeedbackCount and dashboardUrl. Strict argument schemas, server instructions.
2026-09-04 v1.0.0: server launched with 10 tools, 2 resources, 3 prompts. API key auth, 120 calls/min, create_pr capped at 5 per key per day.

Tools are never removed inside 90 days of being announced. A tool on its way out gets "deprecated" in its description first. create_pr was renamed before launch, so no deprecation window applies.