MCP server

Programmatic admin for VerifyHuman from any MCP-compatible client — Claude Desktop, Claude Code, Cursor, Continue, or your own. Hosted and OAuth-secured. Connect once, then ask your LLM to manage projects, rotate signing keys, mint demo keys, or inspect analytics — same surface as the dashboard.

Install (Claude Code)

From a terminal where the Claude Code CLI is installed:

claude mcp add verifyhuman https://vhuman.riwi.com/mcp

First call triggers an OAuth 2.1 + PKCE consent in your browser. After that, Claude Code has the full 34-tool surface available — see first project below for a 60-second walkthrough.

Other clients (Claude Desktop, Cursor, Continue): use the builder below for a connector spec, or paste the URL into your client's MCP-server config panel.

Quick connect

Pick your MCP client. First tool call opens a browser for OAuth login — same Auth0 session as the VerifyHuman dashboard.

Claude Desktop

Paste into claude_desktop_config.json (Settings → Developer → Edit Config).

{
  "mcpServers": {
    "verifyhuman": {
      "url": "https://vhuman.riwi.com/mcp"
    }
  }
}
JSON

Claude Code

Run from a terminal in any directory. Adds to your local Claude Code config.

claude mcp add verifyhuman https://vhuman.riwi.com/mcp
bash

Cursor / Continue / others

Any MCP-compatible client supporting Streamable HTTP + OAuth 2.1.

{
  "mcpServers": {
    "verifyhuman": {
      "url": "https://vhuman.riwi.com/mcp"
    }
  }
}
JSON

Staging URL: https://vhuman.riwi.com/mcp

Your first project — paste this prompt into Claude

Once the MCP is connected, the fastest way to bootstrap a new integration is one paste into Claude:

Create a new VerifyHuman project named "My survey site" and mint
me a project-scoped API key for it. Then tell me:
  - the project ID
  - the API key (vf_live_*)
  - what env-var names you recommend I use

Claude will call provision_project_with_key (the one-round-trip combo of project create + key mint — the recommended path for first integrations), show you the UUID + secret, and walk you through the NEXT_PUBLIC_VH_SITE_KEY / VH_API_KEY split.

Save the API key on first display — it's shown ONCE. You can always re-discover what project a key is configured for via GET /api/v1/api-keys/current later.

What is it?

VerifyHuman exposes its admin API as a remote Model Context Protocol server. Any MCP-compatible LLM client can connect to it via OAuth 2.1 + PKCE — no local install, no Python, no env vars. Once connected, the LLM has tools for the full customer surface:

  • Projects — list, get, create_project, and provision_project_with_key (the latter creates a project AND mints an API key for it in one round-trip; ideal for survey platforms that auto-provision one VH project per study).
  • API keys — list, create, revoke
  • Verifications — list, look up, query analytics
  • Widget config — get, set mode
  • Webhooks — full CRUD, test delivery
  • Signing keys — inspect, rotate
  • Demo keys — mint short-lived test keys (billing-isolated)
  • Phase 3 — read per-question scoring events
  • Server-Side (fraud_gate) — the full VH-Lite journey: fetch the integration contract (lite_integration_guide), create a fraud_gate project (lite_create_project), fire a live test score (lite_score_test_call), read the effective scoring config (lite_get_config), pull usage stats (lite_stats), simulate a threshold change (lite_simulate_threshold), read fitted ground-truth and recommendations (lite_ground_truth, lite_threshold_recommendations), and apply the calibration bands (lite_set_thresholds). See the Server-Side scoring API.

OAuth discovery

For MCP clients that don't have a built-in "add server" UI: discovery metadata is served at https://vhuman.riwi.com/api/v1/.well-known/oauth-authorization-server. Well-behaved clients fetch that automatically. (The non-standard /api/v1/ prefix is historical — for clients that look at the root path, /.well-known/oauth-authorization-server is being added as a mirror in a follow-up.) Demo verifications minted via the MCP demo-key tool run with is_demo=true and stay isolated from billed usage — safe to play with.

What the LLM can do

Once connected, your LLM has 34 tools spanning the admin surface. A few representative prompts:

  • "List my VerifyHuman projects and tell me which ones haven't been verified against in the last week."
  • "Create a 30-minute demo key for project X and send me the URL."
  • "Rotate the signing key for project Y. Confirm the rotation timestamp moved."
  • "Show me yesterday's analytics for project Z — pass rate, failure breakdown, average liveness score."
  • "Add a webhook on project A pointing at https://my-receiver.example/vh for the verification.completed event, then test it."
  • "For my Server-Side project, fire a test score for a datacenter IP and show me the recommendation, then simulate moving the block threshold to 80."

Server-to-server provisioning (CoolTool-style integrations)

Higher-level products that embed VerifyHuman often want to auto-create one VH project per study, panel, or campaign. The MCP provision_project_with_key tool is the intended entry point.

Heads up — MCP does NOT use an API key. The credential type for server-to-server MCP calls is OAuth 2.1 client credentials (a client_id and client_secret pair), not a vf_live_* API key. Your backend exchanges the credentials for a short-lived bearer token and passes that to the MCP server. If you find yourself wanting to set a single VERIFYHUMAN_PLATFORM_API_KEY env var for MCP, you're on the wrong path — either switch to the REST flow at /docs/api → /projects/provision or contact onboarding to register a service-account OAuth client.

Pseudocode (your backend, called from your "new study" handler):

# Pseudocode — your backend, called from your "new study" handler

response = await mcp_client.call_tool(
    "provision_project_with_key",
    {
        "name": f"Study {study.id}",
        "external_id": study.id,          # cross-reference back to you
        "api_key_name": f"study-{study.id}-key",
        "is_test_key": False,             # live key for production traffic
    },
)

# Persist BOTH in your study's config:
study.verifyhuman_site_key = response["site_key"]   # → SDK siteKey
study.verifyhuman_api_key  = response["api_key"]    # → /token/verify
# (api_key is the full plaintext — only shown ONCE)

The composite handles both halves atomically. If api-key minting fails after the project was created, the response has api_key: null + an api_key_error string — you can retry with create_api_key(project_id) using the returnedproject_id.

Scopes required: projects:write AND keys:write on the bearer token. First-party MCP clients (Claude Desktop, Claude Code) get the full scope set at consent time; service-account integrations request both explicitly.

When to use MCP vs. the dashboard vs. the SDK

  • SDK — you're embedding the verify widget in your product. See the SDK guide.
  • Dashboard — you want a visual UI to inspect projects, edit widget config, browse analytics, manage team.
  • MCP — you want to script those admin actions or have an LLM do them for you. Same operations as the dashboard, just via tool calls.

Security model

  • OAuth 2.1 with PKCE — the standard public-client flow. Each LLM client gets browser-driven consent and short-lived access tokens (1 hour) plus rotating refresh tokens (30 days).
  • Tokens are scoped to your org. Every tool call resolves the bearer to a specific user + organization; the LLM cannot reach projects in another org even if it knows their IDs.
  • PII never leaves the server in tool responses. Verification logs strip IP addresses and user-agent strings before returning to the LLM.
  • Plaintext secrets surface once. API keys, webhook signing secrets, and project signing keys are returned in plaintext only at creation/rotation; subsequent reads show metadata only.
  • Refresh-token theft detection. Re-using a rotated refresh token revokes the entire chain — both old and new bearers — forcing a fresh login.

Tool scopes

Different tools require different scopes on the bearer token. First-party clients (Claude Desktop, Claude Code) get the full scope set by default at consent time. Third-party clients can request a subset:

  • projects:read — list / read project metadata
  • projects:write — change widget mode, project settings, create fraud_gate projects, and set Server-Side (VH-Lite) calibration thresholds
  • verifications:read — read verification logs and per-question events
  • analytics:read — pass rates, quota, time-series
  • keys:write — mint / revoke API keys, rotate signing keys
  • webhooks:write — CRUD webhooks and test delivery

Not in scope

Some operations stay dashboard-only on purpose — they're admin-sensitive or human-in-the-loop:

  • Billing tier changes
  • Organization member invitations / removal
  • DSR / appeals / legal endpoints

Self-hosted / local stdio MCP

For air-gapped environments or contributors hacking on the tool surface, an older stdio-transport package is available at packages/mcp/ in the repository. It uses Auth0 Device Code flow and runs as a Python subprocess of the LLM client. Most customers should prefer the hosted server — it's zero-install, gets fixes immediately, and doesn't require local Python or env-var configuration.

Troubleshooting

  • Connection fails / 401 errors — your tokens may have expired. Most clients re-do the OAuth flow automatically; if not, remove the MCP server from your client config and re-add it. The browser opens for a fresh login.
  • "org not found" on a tool call — your bearer is scoped to a specific org. If you belong to multiple, the first one is selected by default; disconnecting and reconnecting lets you pick a different org during consent.
  • Rate-limit errors on demo-key minting — the demo-key endpoint is IP rate-limited (10/minute soft, then a short hard block). Slow down and retry.
  • Tool says "not_found" for a project I own — that response also covers cross-org access (an existence leak would be a security hole). Confirm you're connected as the right org via the get_organization tool.

Issues + roadmap: docs/plans/mcp_hosted_server.md.