What it prevents
Without this, agents tend to make four mistakes (we tested):- Retrying with a new id. A fresh
clientRunIdafter 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. - Exporting to “see what’s there.” The export never returns partial data. Not ready is always a
409, not a failure. Enrich, then export. - Skipping citations on starter scans.
details.citationsexists on every tier. Agents without the skill assume it’s deep-only. - 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
---
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, batch-analyze 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,
create_batch, get_batch, export_batch, generate_ideas) 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 eight tools and two prompts (`validate_idea` and `generate_ideas`, user-picked, not model-invoked). Both need an API key created at https://preuve.ai (Account → API Keys; shown once at creation), except the claude.ai connector, which handles the key via OAuth.
Both export tools take `verbosity`: `summary` drops section prose and Trends data points and keeps every scored field, the Bottom Line narrative (`details.verdict.narrative`), and the closing verdict (`details.sections.synthesis`). Prefer it unless you need the prose; `export_batch` already defaults to it.
**Remote (preferred, no file to install):**
```sh
claude mcp add preuve --transport http https://mcp.preuve.ai/mcp \
--header "Authorization: Bearer prv_..."
```
Cursor / Claude Desktop: add to MCP config (`~/.cursor/mcp.json`):
```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.
**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 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.
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-key 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.
## 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 deep scans — 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 `readyForExport` is `false`, call `enrich_analysis` (idempotent: it only generates what's missing), then poll until `readyForExport: true`.
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 the connector from claude.ai works on a free
Preuve account, and the first starter scans cost nothing. The deep Founder
Report is $29 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 $29" 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.
## Batches (up to 10 ideas)
For comparing or bulk-validating multiple ideas:
1. `create_batch` with a batch-level `clientRunId` plus `items`. Each item is a full analysis input with its **own unique** `clientRunId`. Set `scanType` per item; keep everything `"starter"` unless deep is intentional per idea — and every deep item must carry `stage` and `budget` (the batch refuses to start otherwise, naming each incomplete item).
2. Poll `get_batch` until items are terminal.
3. Completed items with `readyForExport: false` need `enrich_analysis` on their individual run ids.
4. `export_batch` → same v2 item shape, plus `counts.exported`/`counts.omitted` and an `omittedItems` list explaining every skipped item. Check `omittedItems`. An export that silently covers 7 of 10 ideas misleads the user.
Batch export is deterministic: the same batch state always serializes identically, so it's safe to diff across polls.
## 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 |
| `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 |
| `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-key daily run ceiling | Stop creating runs today; 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 blindly retry `create_batch` with a fresh batch id. Replay the same one first.
## 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.
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.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, aAGENTS.md / .cursorrules section, a RAG document). Everything below the frontmatter works standalone.