V12 Docs
Public API

MCP Server

Connect Claude Desktop, Cursor, VS Code, or any MCP host to V12 for automated security audits.

Server URL

https://v12.sh/api/mcp

Setup

OAuth

Paste the server URL into your host's MCP configuration and complete the V12 sign-in prompt. Claude Desktop, Cursor, Codex, VS Code, and Windsurf handle this end-to-end — no PAT needed.

Browser-origin policy

Desktop and CLI MCP hosts usually do not send an Origin header, so they are unaffected by browser CORS rules.

If you are connecting from a browser-based MCP client or local inspector:

  • loopback browser origins such as http://localhost:*, http://127.0.0.1:*, and http://[::1]:* are allowed automatically
  • the app's exact origin (https://v12.sh) is allowed automatically
  • any non-loopback browser origin must be listed exactly in MCP_ALLOWED_ORIGINS
  • disallowed browser origins are rejected with HTTP 403

Personal access token

For hosts that take a JSON config with a bearer header:

Create the token at Settings → Developer. PATs are bound to the currently selected organization, so switch orgs before creating the token if you want the MCP client to see a different account.

{
  "mcpServers": {
    "v12": {
      "url": "https://v12.sh/api/mcp",
      "headers": {
        "Authorization": "Bearer ${env:V12_PAT}"
      }
    }
  }
}

Cursor with a PAT

For Cursor, put the remote server in either project config (.cursor/mcp.json) or global config (~/.cursor/mcp.json):

{
  "mcpServers": {
    "v12": {
      "url": "https://v12.sh/api/mcp",
      "headers": {
        "Authorization": "Bearer v12p_YOUR_TOKEN_HERE",
        "Accept": "application/json, text/event-stream"
      }
    }
  }
}

Cursor only interpolates environment variables it inherited when the app started. If Cursor is launched from the macOS Dock/Finder, values in a project .env or shell-only profile may not be visible to it. Set the token in the GUI environment, then fully quit and reopen Cursor:

launchctl setenv V12_PAT 'v12p_YOUR_TOKEN_HERE'

If you launch Cursor from a terminal where V12_PAT is exported, restarting from that terminal works too. Use Output → MCP Logs in Cursor to distinguish authentication errors from connection errors.

stdio fallback (mcp-remote)

For stdio-only hosts (no Streamable HTTP support):

{
  "mcpServers": {
    "v12": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://v12.sh/api/mcp"],
      "env": {
        "MCP_HEADERS": "Authorization: Bearer ${env:V12_PAT}"
      }
    }
  }
}

Tools

ToolScopeDescription
v12_whoamiuser:readProfile and active scopes for the current token.
v12_reposrepos:readList GitHub repos connected to V12. Pagination via search, limit, offset.
v12_audit_githubruns:writeStart a GitHub audit. Pass exactly one of repoUid (preferred) or repoFullName. branch and sha optional.
v12_upload_zipruns:writeCreate a presigned upload slot for a ZIP archive and return { zipUid, uploadUrl }.
v12_audit_zipruns:writeStart a ZIP-backed audit. Pass exactly one of zipUid or sourceZip; patchContent (or patchUid from a prior estimate) is optional for diff review.
v12_estimate_costruns:readEstimate audit cost without creating a run. Pass exactly one of repoUid/repoFullName or zipUid; diffReviewConfig quotes a diff review. Returns the scope file list, billable file/LOC counts, and a cost band in USD cents; inline patches echo back as patchUid for reuse.
v12_watchruns:readPoll a run. terminal: true carries findingCounts.
v12_findingsruns:readList findings with severity/validity filters and pagination.
v12_triage_findingfindings:writeAtomic update of validity, severity, and/or comment.
v12_cancel_runruns:manageCancel a non-terminal run.

Address findings by findingUid. The F-001-style numbers shown in the V12 web UI are display-only.

Resources

URIDescription
v12://runsRecent runs.
v12://runs/{uid}Run detail.
v12://runs/{uid}/reportReport (JSON).
v12://runs/{uid}/report.mdReport (Markdown).
v12://runs/{uid}/findingsFirst page of findings. Use v12_findings to paginate.
v12://runs/{uid}/findings/{findingUid}Full finding detail.
v12://runs/{uid}/findings/{findingUid}/pocProof-of-concept (404 if none recorded).
v12://runs/{uid}/findings/{findingUid}/fixCompact fix payload (404 if none recorded).

Prompts

PromptArgumentsDescription
audit_pull_requestrepo_full_name, pr_numberAudit a pull request.
audit_repositoryrepo_full_name, paths?Audit a repository or path subset.
review_findingsrun_uidReview and triage findings from a completed run.
triage_findingrun_uid, finding_uid?Triage a single finding with explicit user confirmation before mutating.

Skill

V12 ships a short MCP skill that teaches your agent the audit workflow — repo selection, polling, presenting findings, triage. Hosts that surface MCP instructions (Claude Desktop, Cursor, Codex, Windsurf) load it on connect.

If yours doesn't, or you're using V12 over REST with your own agent, paste it into the system prompt:

V12 is an automated security audit platform. It analyzes code repositories — primarily smart contracts — for vulnerabilities, then produces findings with severity, validity, source locations, and optional fix/PoC artifacts.These notes cover what you need beyond what the tool schemas already tell you.## Audit lifecyclediscover → audit → poll → review → triageNot every session follows every step. A user may jump in at "review" with an existing runUid, or ask to triage a single finding without listing anything first. Adapt to what the user actually asks.## Starting an auditThere are two audit tools depending on the source, plus an upload helper for large zips.### v12_audit_githubAudit a GitHub repository. Requires exactly one of repoUid or repoFullName.- **Full audit:** pass branch/sha (auto-resolves to default HEAD if omitted) and optional paths to scope.- **Diff review:** pass `diffReviewConfig` with either (fromRef + toRef) for branch/tag/commit comparison, OR `patchContent` with a raw unified diff — not both. Include `prMetadata` when auditing a pull request.### v12_audit_zipAudit source code from a zip archive. Accepts either:- `zipUid` from `v12_upload_zip` (preferred for files over ~750 KiB)- `sourceZip` as inline base64 (convenient for small projects)Provide exactly one.- **Full audit:** just pass the zip source (+ optional paths to scope).- **Diff review:** also pass `patchContent` with a unified diff.### v12_upload_zipPre-upload a zip archive for use with `v12_audit_zip`. Returns `{ zipUid, uploadUrl }`. The agent must:1. Call `v12_upload_zip` to get the presigned URL2. HTTP PUT the raw zip bytes to `uploadUrl`3. Call `v12_audit_zip` with `zipUid`Use this when the zip exceeds the ~750 KiB inline limit (which is bounded by the MCP request body size).### v12_estimate_costEstimate the billable cost before starting an audit. Takes the same target selectors as the audit tools: exactly one of repoUid/repoFullName (with optional branch/sha/paths) or zipUid. Diff reviews are supported via `diffReviewConfig` — (fromRef + toRef), patchContent, or patchUid — and inline patches are stored and echoed back as `patchUid`, which you should pass to the audit tool so the queued run reviews the exact quoted patch. Returns the resolved scope file list plus billable file/LOC counts and a cost band in USD cents (`meanCostCents` / `upperCostCents`). Use it when the user asks what an audit will cost, or before queueing a large audit. The audit tools return the same `estimate` object once a run is queued.## Preparing a zip for uploadWhen auditing local source code:1. **Include only source files.** The zip should contain the auditable source tree (e.g., `.sol`, `.rs`, `.move` files and their directory structure). Exclude `node_modules`, `.git`, `build/`, `dist/`, `target/`, compiled binaries, and other non-source artifacts.2. **Preserve directory structure.** Paths inside the zip should match the project layout so findings reference the correct file locations.3. **Keep it small.** A typical smart contract project zips to well under 1 MiB. If your zip is larger, use `v12_upload_zip` instead of inline `sourceZip`.## The `paths` fieldThe `paths` field serves two purposes depending on audit mode:- **Full audit:** scopes which files are analyzed (only files matching these paths are audited).- **Diff review:** selects which affected files to include in the review (maps to `selectedFiles` internally). The diff scope computation determines affected files; `paths` narrows that set.## Repo selection (GitHub audits)v12_audit_github needs exactly one repo selector. Pick based on what you have:- A repoUid from v12_repos → use repoUid (preferred — no lookup ambiguity)- An owner/name string or git remote URL → use repoFullName (V12 validates installation itself)There is no separate repo-resolution step. v12_audit_github handles it.### Local git contextWhen running inside a code editor, resolve the local checkout so the audit matches what the user has open:- git remote get-url origin → parse into owner/name → repoFullName- git branch --show-current → branch- git rev-parse HEAD → shaIf local git is unavailable or the user explicitly wants the default branch, omit branch and sha. V12 resolves the repo's default-branch HEAD server-side. Never pass sha without branch.## PollingAll audit tools return nextPollAfterSeconds. Call v12_watch after that many seconds. Each v12_watch response updates the interval — always honor it. When terminal is true, stop. Typical audits take 5-15 minutes. Do not synthesize progress updates between polls.## Presenting resultsWhen the run completes: link the run webUrl first, then one sentence with severity counts from findingCounts, then list critical and high findings with title, sourceLocations, and webUrl. Skip info and qa severity unless the user asks. sourceLocations is an array — iterate all entries when a finding spans multiple files.## Reading finding detail- v12_findings tool: filtered, paginated listing. Use severity and validity filters to narrow.- v12://runs/{uid}/findings/{findingUid} resource: full detail for one finding including description, impact, root cause, and source URLs.- The aggregate resource v12://runs/{uid}/findings is capped to a first page. When hasMore is true, switch to v12_findings with offset.- Findings are always addressed by findingUid — not by number or title.## Resources- v12://runs — list of the user's runs- v12://runs/{uid} — single run detail- v12://runs/{uid}/findings — first page of findings (use v12_findings to paginate)- v12://runs/{uid}/findings/{findingUid} — full finding detail- v12://runs/{uid}/findings/{findingUid}/poc — proof-of-concept (404 if none recorded)- v12://runs/{uid}/findings/{findingUid}/fix — fix artifact (404 if none recorded)- v12://runs/{uid}/report — audit report as JSON- v12://runs/{uid}/report.md — audit report as Markdown## Triagev12_triage_finding is the single write tool for findings. It handles validity changes (valid, invalid, unreviewed, acknowledged), severity changes, comment-only notes (pass only comment), or any combination atomically. Only call after explicit user instruction. Restate the action before executing. One comment per finding is enforced server-side.## Cancellationv12_cancel_run stops a non-terminal run. Only call when the user explicitly asks.## ScopesCall v12_whoami to check available scopes. Main boundaries: runs:read enables v12_watch, v12_findings, v12_estimate_cost, and all resources; runs:write enables v12_audit_github, v12_audit_zip, and v12_upload_zip; runs:manage enables v12_cancel_run; repos:read enables v12_repos; findings:write enables v12_triage_finding; user:read enables v12_whoami.Tokens are account-bound: `v12_whoami` returns the token's `orgUid`, `orgName`, and `orgKind`. If the user needs access to another org, they need a different PAT or a separate OAuth authorization in that org.## Error handling- Repo unavailable: v12_audit_github returns a clear error when the repo is not installed or not accessible to this token. Surface it to the user and stop — do not retry with the same inputs.- Run not found (404): Accessing a run the token cannot see returns 404. This is intentional — the server does not distinguish "does not exist" from "not yours."- Rate limits: Tools are rate-limited per scope. If a call fails due to rate limiting, wait and retry.## Host noteSome MCP hosts display tool names with a prefix like `mcp_v12_<tool>`. V12 registers canonical `v12_*` names. Use whatever name your host shows.## Do not- Poll faster than nextPollAfterSeconds.- Auto-triage based on your own judgment.- Summarize info/qa findings unless the user asks.- Retry after a repo-availability failure without the user changing context.- Assume finding numbers are stable identifiers — always use findingUid.