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, 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) 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 seven tools. 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.
**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.
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`. 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. 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.
## 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"]` 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` 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.
- Module payloads then appear in the export under `details.founderFit` / `details.playbook` / `details.proofOfDemand`.
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.
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.
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.