# Feedback MCP server

Usero runs a remote [Model Context Protocol](https://modelcontextprotocol.io) 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:

```bash title=Claude Code file=Terminal
claude mcp add --transport http usero https://usero.io/mcp
```

```json title=Cursor file=.cursor/mcp.json
{
	"mcpServers": {
		"usero": {
			"url": "https://usero.io/mcp"
		}
	}
}
```

```text title=Claude Desktop file=Settings → Connectors → Add custom connector
https://usero.io/mcp
```

```json title=OpenCode file=opencode.json
{
	"mcp": {
		"usero": {
			"type": "remote",
			"url": "https://usero.io/mcp"
		}
	}
}
```

```toml title=Codex file=~/.codex/config.toml
[mcp_servers.usero]
url = "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](/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:

```bash title=Claude Code file=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}'
```

```json title=Cursor file=.cursor/mcp.json
{
	"mcpServers": {
		"usero": {
			"url": "https://usero.io/mcp",
			"headers": {
				"Authorization": "Bearer usk_live_..."
			}
		}
	}
}
```

```json title=Claude Desktop file=claude_desktop_config.json · fallback
{
	"mcpServers": {
		"usero": {
			"command": "npx",
			"args": ["-y", "mcp-remote", "https://usero.io/mcp", "--header", "Authorization: Bearer usk_live_..."]
		}
	}
}
```

```json title=OpenCode file=opencode.json
{
	"mcp": {
		"usero": {
			"type": "remote",
			"url": "https://usero.io/mcp",
			"oauth": false,
			"headers": {
				"Authorization": "Bearer usk_live_..."
			}
		}
	}
}
```

```toml title=Codex file=~/.codex/config.toml
[mcp_servers.usero]
url = "https://usero.io/mcp"
http_headers = { "Authorization" = "Bearer usk_live_..." }
```

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](/docs/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](/contact?subject=MCP%20tools).

## 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](/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](/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](/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](https://code.claude.com/docs/en/mcp#scale-with-mcp-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](/blog/feedback-mcp-server#full-transcript).

## 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.
