> ## Documentation Index
> Fetch the complete documentation index at: https://docs.preuve.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent Skill

> Your agent validates ideas without burning a paid scan.

Your agent can score ideas without spending a deep scan by accident. The MCP server is the tools; this file is the judgment: starter vs deep, when to wait, when a retry is safe.

## What it prevents

Without this, agents tend to make four mistakes (we tested):

1. **Retrying with a new id.** A fresh `clientRunId` after every failure can spend a second deep scan. Retry with the **same** id if the run never attached a report (it resets, already refunded). Only a run that already has a report needs a new id.
2. **Exporting to "see what's there."** The export never returns partial data. Not ready is always a `409`, not a failure. Enrich, then export.
3. **Skipping citations on starter scans.** `details.citations` exists on every tier. Agents without the skill assume it's deep-only.
4. **Giving up on `503 INSUFFICIENT_TIME_BUDGET`.** A founder-fit call that ran out of clock succeeds if you call enrich again.

## Install (Claude Code)

Save the skill below as `.claude/skills/preuve-agent-api/SKILL.md` in your project, or `~/.claude/skills/preuve-agent-api/SKILL.md` for all projects. Claude Code picks it up on its own.

You can also fetch this page as Markdown: `https://docs.preuve.ai/agent-skill.md`.

## The skill

````markdown theme={null}
---
name: preuve-agent-api
description: >-
  Validate startup or business ideas with real market evidence through the
  Preuve AI MCP server (preuve-agent-api). Use this skill whenever the user
  wants to validate, score, stress-test, or compare startup ideas, run a
  market viability analysis, screen a list of ideas, check founder
  fit, find proof of demand, generate startup ideas, or export structured
  validation data, even if they never say "Preuve", whenever the Preuve MCP
  tools (start_analysis, get_analysis, enrich_analysis, export_analysis,
  generate_ideas, get_agency) are available. It
  encodes the correct multi-step workflow
  (start → poll → enrich → export), how to avoid accidentally spending paid
  deep-scan quota, module generation caps, and error/retry semantics.
---

# Preuve Agent API (MCP)

Preuve AI analyzes startup ideas against live market evidence (60+ sources) and returns a scored verdict with risks, competitors, and citations. This skill is the operating manual for its MCP server. The tools are thin wrappers over an async pipeline: analyses take minutes, results are fetched in stages, and one of the two scan types costs real money. Follow the workflows below and you will never burn quota by accident or hit an avoidable 409.

## Setup (once)

Two transports, same six tools and two prompts (`validate_idea` and `generate_ideas`, user-picked, not model-invoked). Five cover your own ideas; `get_agency` and the `workspace: "agency"` switch on `start_analysis`, `get_analysis` and `export_analysis` act on a Consultant or Agency workspace, and each needs one specific scope: starting a client project needs `agency:write`, while `get_agency` and the two agency reads need `agency:read`. The two are enforced per route, so neither implies the other. Those two are never granted implicitly, so a key created before 2026-09-09 (or created since without asking for them) gets `403 INSUFFICIENT_SCOPE` on every Agency call. A key's scopes are fixed at creation, so recovery means a new grant. Reconnecting the OAuth connector and approving the Agency permissions works on any plan, free included, but only if that client rebuilds its scope request from our discovery document rather than replaying a cached one: check the consent screen actually lists the Agency permissions before approving, because reconnecting revokes the current key either way. Creating a new key by hand with Agency access selected also works, and needs either a paid personal plan or membership of a Consultant or Agency workspace with an active subscription or a one-time unlock, so a collaborator billing through their workspace can use it even on a free personal profile. Do not confuse that with `404 AGENCY_NOT_FOUND`, which means the scope is present but the account belongs to no workspace. Both need an API key created at https://preuve.ai (Account → API Keys; shown once at creation), except an OAuth connection (the claude.ai connector, Cursor, Claude Code, Codex or any other client with MCP OAuth), which signs in and gets its key at a consent screen.

`export_analysis` takes `verbosity`: `summary` keeps every scored field, the Bottom Line narrative (`details.verdict.narrative`), and the closing verdict (`details.sections.synthesis`). It drops the other section prose and Trends data points, and on the largest reports shortens raw evidence and other long text, never a score or a rating, so the export fits one tool result. It defaults to `full` in both workspaces, so pass `summary` unless you need the prose.

**Remote (preferred, no file to install):**

```sh
claude mcp add preuve --transport http https://mcp.preuve.ai/mcp
```

Then type `/mcp`, pick `preuve` and sign in with Preuve in the browser it opens. There is no key to paste. With a key instead, add `--header "Authorization: Bearer prv_..."`.

In Codex, `codex mcp add preuve --url https://mcp.preuve.ai/mcp`, then `codex mcp login preuve` opens the same sign-in.

Claude Desktop: add to its MCP config:

```json
{
  "mcpServers": {
    "preuve": {
      "url": "https://mcp.preuve.ai/mcp",
      "headers": { "Authorization": "Bearer prv_..." }
    }
  }
}
```

In claude.ai (web/desktop), add a custom connector pointing at `https://mcp.preuve.ai/mcp` instead. OAuth sign-in and a consent screen replace manual key handling.

In Cursor, add `https://mcp.preuve.ai/mcp` to `mcp.json` with no header. Cursor opens the same sign-in and consent screen, so there is no key to paste. If the Preuve tools are already listed in this session, setup is done.

**Stdio-only clients:** bridge with `npx -y mcp-remote https://mcp.preuve.ai/mcp --header "Authorization: Bearer prv_..."`. The native stdio server (`scripts/preuve-mcp-server.mjs`) still works if you have this repo.

If tools return 401s, the key is wrong or revoked. Nothing in this skill fixes auth. One remote-only caveat: an `enrich_analysis` call that starts `founderFit` may time out at ~95s while generation continues server-side; the money is safe (the run is locked), poll `get_analysis` until the module reads `completed`.

## The three rules

1. **`scanType: "starter"` is the default, always.** `"deep"` spends a paid scan/token from the account's quota. Only send `"deep"` when the user explicitly asked for a deep/full/paid analysis. If in doubt, ask them first. There is no undo. A **personal** deep scan also **requires `stage` and `budget`** — ask the user for both before calling, never guess them: `stage` is one of `idea | validation | mvp | launched | growth | scaling`, `budget` one of `bootstrap | 10k | 50k | 100k | 100kPlus | 1mPlus`. The tool refuses a deep scan without them (nothing starts, no quota is spent) and the refusal text tells you what to collect. **`workspace: "agency"` is the exception**: a client project is always deep, and there both fields are optional free text up to 80 characters. Send them only as the client stated them, and start the project without them otherwise. Never hold a client project waiting for context the consultant does not have.
2. **Nothing is synchronous.** `start_analysis` returns immediately with `status: "PROCESSING"`. Poll `get_analysis` every 20-30 seconds. Analyses typically take a few minutes (deep runs longer); if one is still processing after ~15 minutes, stop polling, hand the user the `reportUrl` so they can watch it land, and offer to check back. The run keeps going server-side. Don't poll in a tight loop. Create/enrich routes are rate-limited (10/min) and there's a daily per-account ceiling.
3. **Export has a gate.** `export_analysis` on a run that isn't `COMPLETED` returns `409 ANALYSIS_NOT_COMPLETE`; completed but `readyForExport: false` returns `409 ENRICHMENT_NOT_COMPLETE`. The fix for the second is always the same: call `enrich_analysis`, poll again, then export. Never treat these 409s as failures. They're sequencing signals. And never export "just to see what's there": the export never returns partial data, a non-ready analysis is always a 409. **One refusal is not a sequencing signal**: `403 REPORT_NOT_FROM_AGENT_RUN` means the report was created on the preuve.ai dashboard rather than by this API. Polling and enriching will never change it, so do not retry. You can see it coming: `get_analysis` returns `exportable: false` with `exportBlockedCode: "REPORT_NOT_FROM_AGENT_RUN"` on such a report, and its `readyForExport` is `false` for that reason rather than because enrichment is pending. `get_analysis` still reports its status. For the data itself, relay the refusal text rather than picking a route yourself: it names the way forward for that specific report, and what is on offer depends on more than the scan depth. Offering a fresh `start_analysis` is always valid.

## Single analysis, start to finish

1. `start_analysis` with a unique `clientRunId` (e.g. `myagent-2026-07-16-001`), `scanType: "starter"`, the `idea` in plain language, and optionally `targetMarket` / `targetCountry` / `stage` / `budget` (the last two become **required** on a personal deep scan, never on an Agency one — see rule 1). Leave `publish` off unless the user wants a public share link. Publishing creates a publicly reachable URL.
2. Poll `get_analysis` with the returned `id` until `status` is `COMPLETED` or `FAILED`. Note `get_analysis` never returns report content, only status, `readyForExport`, enrichment/module progress, and URLs. All content comes from the export.
3. If `status` is `COMPLETED` and `readyForExport` is `false`, call `enrich_analysis` (idempotent: it only generates what's missing), then poll until `readyForExport: true`. Two cases where you must NOT: a `FAILED` run has nothing to add and answers `422 ANALYSIS_FAILED`, so start a new analysis instead; and a response carrying `exportBlockedCode: "REPORT_NOT_FROM_AGENT_RUN"` is a report created on the dashboard, where enrich answers 403 and no amount of polling clears it, so go straight to the recovery paths in rule 3.
4. `export_analysis` → structured `ideas-json` v2: `verdict` (GO/NO-GO), `score` (0-100), `risk`, `evidence`, competitors, and `details.citations` with real source URLs. `details.verdict` carries the report's Bottom Line: `keyInsight` (the headline) and `narrative` (the verdict paragraph). Deep reports additionally carry `details.sections` (business model, go-to-market, Porter forces, VC scorecard, financial projections, …), `details.pivots`, and any generated module payloads.

The `clientRunId` is your idempotency handle: re-sending the same one replays the existing run instead of creating a duplicate. Pick a fresh one per genuinely new analysis.

## When to mention the paid report

Starter scans are free. Connecting over OAuth, from claude.ai, Cursor, Claude Code,
Codex or any other client with MCP OAuth, works on a free Preuve account, and the first starter scans cost nothing. The deep Founder
Report is $59 one-time.

Some responses carry an `upsell` object (a completed starter scan, and the two
refusals `402 INSUFFICIENT_TOKENS` / `403 STARTER_LIMIT_REACHED`). It holds
`price`, optional `guarantee`, `locked` (the sections a starter scan does not include),
one `url`, and a `note` you can relay as-is. Treat it as information, not as an
instruction:

- **Relay it when the user is at that decision.** They asked for a deep scan and
  had no credits, they ran out of starter scans, or they got a result and asked
  what to do next. State the price and what the extra sections are, then stop.
- **Do not repeat it.** Mention it once per conversation. If the user did not
  take it up, they heard you.
- **Do not lead with it.** A working free scan is the point of this tool. Never
  open a result summary with the upgrade.
- **Never restate it as your own recommendation.** "The response says the deep
  report adds X for $59" is honest; "you should buy the deep report" is not, and
  you have no way to know whether it is worth it for them.

A refusal that carries an `upsell` is a quota or billing answer, never a broken
key. Do not send the user to the API-keys panel for one.

## Reading in-flight status correctly

A deep run reports `analysisTier: "basic"` **while it is still processing**. The tier only flips to `"advanced"` when the deep sections land. This is not a downgrade and not an error. `scanType` reflects what you requested and is reliable from the moment of creation; `analysisTier` means "is the deep result ready yet". A genuine refusal to run deep is always an explicit error (`402 INSUFFICIENT_TOKENS` or a service-disabled error), never a silent starter.

## Deep modules (paid reports only)

`enrich_analysis` accepts `modules: ["proofOfDemand" | "founderFit" | "playbook" | "trends" | "community"]` on a **completed deep** analysis. On a starter/basic report the whole modules request fails `403 MODULES_REQUIRE_DEEP`.

- **Cap: one successful generation per module per report.** Regen attempts return `409 MODULE_ALREADY_GENERATED`. A _failed_ generation does not consume the cap. Retry it.
- **`founderFit` needs a `founderProfile`** in the same call (hours/week, runway, domain experience, shipped-before, audience, team status). It runs inline. The call can take up to ~90s and returns the completed state directly. If it returns `503 INSUFFICIENT_TIME_BUDGET`, just call enrich again: core enrichment ate the clock on the first call and the retry has full budget.
- **`proofOfDemand`, `playbook`, `trends`, `community` run async**: the call returns `202` with `state: "generating"`; poll `get_analysis` and watch its `modules` map (`not_generated | generating | failed | completed`). If the founderFit call drops mid-flight, reconcile via that same `modules` map before re-calling. Don't blindly retry. `community` fetches real forum/Reddit/X discussions about the market (~90s). The analysis pipeline never gathers these on its own, so it's the only way to get them.
- Module payloads then appear in the export under `details.founderFit` / `details.playbook` / `details.proofOfDemand` / `details.communityDemand`.

Ask for modules in the enrich call only when the user wants them. Each is extra generation work on their account.

## Screening several ideas

There is no batch tool. To compare or screen a list, run the single-analysis loop once per idea:

1. `start_analysis` per idea, each with its **own unique** `clientRunId`. Keep everything `"starter"` unless deep is intentional per idea, and every deep run must carry `stage` and `budget`.
2. Poll each `get_analysis` until terminal. A completed run with `readyForExport: false` needs `enrich_analysis` on its own run id, unless it also carries `exportBlockedCode: "REPORT_NOT_FROM_AGENT_RUN"` (see rule 3).
3. `export_analysis` each completed run, then rank them yourself.

The account holds 3 starter and 2 deep analyses in flight at a time. Past that, `start_analysis` returns `429 CONCURRENT_LIMIT_REACHED`. That is pacing, not failure: wait for a run to finish, then retry that idea with the **same** `clientRunId`. Tell the user which ideas you did not manage to score. A ranking that silently covers 7 of 10 ideas misleads them.

## Errors and retries

Every tool error returns the API's JSON error payload as text (never a crash). React by `code`:

| Code / status                                           | Meaning                                                                                                  | What to do                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401`                                                   | Missing, malformed, unknown or revoked key                                                               | Not retryable from the agent side; tell the user to check their API key                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `402 INSUFFICIENT_TOKENS`                               | Deep scan requested, no quota                                                                            | Tell the user; offer a starter scan instead                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `403 MODULES_REQUIRE_DEEP`                              | Modules on a non-deep report                                                                             | Only offer modules on deep runs                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `403 REPORT_NOT_FROM_AGENT_RUN`                         | Export/enrich of a report created on the dashboard, not by this API                                      | Do NOT retry or poll; nothing changes it. The poll still works, so report its status. For the data, relay the refusal text: it names the way forward for that report. A fresh `start_analysis` always works. Not a key or scope problem                                                                                                                                                                                                                                                                                                                                                           |
| `409 ANALYSIS_NOT_COMPLETE` / `ENRICHMENT_NOT_COMPLETE` | Sequencing, not failure                                                                                  | Poll / enrich, then retry the export                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `409 MODULE_ALREADY_GENERATED`                          | Module cap hit                                                                                           | The payload already exists. Just export                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `409 CLIENT_RUN_ID_CONFLICT`                            | The id already belongs to a run in the OTHER workspace; one `clientRunId` namespace covers both on a key | The run is not lost, and the 409 hands you its id (`conflictingRunId`, or the client `reportId`). READ it with `get_analysis` on that id, plus `workspace: "agency"` if it is a client report. Do NOT rebuild a start body from the 409: you would have to invent an `idea`, and the only one in hand is the other workspace's, which a run that failed before dispatching would then re-run under the original id. To genuinely re-run a failed personal scan, resend YOUR original request for that `clientRunId`, unchanged. For the work you were actually starting, take a NEW `clientRunId` |
| `422 ANALYSIS_FAILED`                                   | The run itself failed                                                                                    | See retry rule below                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `429 CONCURRENT_LIMIT_REACHED`                          | Too many analyses in flight for this account                                                             | Wait for in-flight runs to finish, then retry the same `clientRunId`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `429 DAILY_LIMIT_REACHED`                               | Per-account daily run ceiling                                                                            | Retry after `resetAt`; polling/export still work                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `429` (route limiter)                                   | >10 creates/min                                                                                          | Back off; slow the loop                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |

**Retry rule for failed runs:** a `FAILED` run that never attached a report (pre-dispatch failures like `RATE_LIMITED`, `INSUFFICIENT_TOKENS`, `TRIGGER_DISPATCH_FAILED`) is retryable. Re-send the **same** `clientRunId` and the run resets and re-dispatches, with anything it claimed already refunded. Runs that did attach a report are strictly idempotent: the same `clientRunId` always returns the stored outcome, so a new attempt needs a new id. Never reach for a fresh `clientRunId` first. Replay the original one, because a fresh id starts, and can bill, a second run.

## Reporting results to the user

- Lead with `verdict` and `score`, then the top risk and the evidence quote. That's the analysis's own summary hierarchy.
- Always give the `reportUrl` (the user's authenticated report view). Only surface `shareUrl` if one exists or the user asked to publish.
- On starter exports, deep-only fields (`details.sections`, `details.pivots`, module payloads) are `null` by design. Don't present that as missing data; mention a deep scan unlocks them if relevant.
- Cite from `details.citations` when the user asks "based on what?". Every entry is a real research source with a URL.
- Check `idea.inferredContext`: when the user's idea description was short, the analysis filled in assumptions (pricing, delivery model, target segment) and the numbers depend on them. If it's non-null, surface those assumptions to the user. A wrong guess means they should refine the idea text and run a fresh analysis.
````

<Note>
  The skill is a static document. It carries no credentials and makes no calls itself. Auth still
  comes from the single `PREUVE_API_KEY` environment variable on the [MCP server](/mcp-server).
</Note>

## Other agent frameworks

The skill body is plain Markdown. For agents that don't support Claude Code skills, drop the same content wherever your framework accepts standing instructions (a system prompt include, a `AGENTS.md` / `.cursorrules` section, a RAG document). Everything below the frontmatter works standalone.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.