version90 DEVELOPERS

MCP server

One URL. Your contracts, agent-ready.

version90 ships a remote MCP server — nothing to install or host. Point any MCP-capable agent (Claude Code, claude.ai, Cursor, …) at the endpoint and it gets seven tools over your organization's contracts. Stateless Streamable HTTP: every request self-contained, plain JSON responses, batch requests accepted. Tool results are returned as MCP text content containing JSON with the exact shapes documented below.

Endpoint: https://api.version90.com/api/v1/mcp
Auth: OAuth 2.0 — you sign in with your version90 account; the client runs the flow. No token to paste.
Protocol: MCP 2025-06-18 · capabilities: tools

Claude Code
claude mcp add version90 https://api.version90.com/api/v1/mcp --transport http
# On first use the client opens a browser to sign in with version90 (Clerk).
# No API key: the MCP OAuth flow issues and refreshes the token for you.
Cursor / generic mcp.json
{
  "mcpServers": {
    "version90": {
      "url": "https://api.version90.com/api/v1/mcp"
    }
  }
}

Just the URL — no headers block. The client discovers how to authenticate and prompts you to sign in the first time.

Authentication (OAuth)

The server is an OAuth 2.0 Resource Server; version90's identity provider (Clerk) is the authorization server. You never mint or paste a key — any spec-compliant MCP client runs the flow for you:

  1. The client calls the endpoint with no token and gets 401 with a WWW-Authenticate header pointing at the server's protected-resource metadata.
  2. It reads /.well-known/oauth-protected-resource/api/v1/mcp, which names the authorization server.
  3. It registers itself (Dynamic Client Registration) and opens a browser for you to sign in and consent — scoped to the organization you're signed into.
  4. It retries with the issued access token, and refreshes it silently thereafter.

The access token is bound to this MCP server (audience-checked), so it can't be replayed against anything else. Access follows your membership: the agent acts as you, in your org, with your role — and, like everything over MCP, can never edit a document.

Tool reference

search_archive #

Search executed agreements on extracted facts. All filters optional and AND-combined; query is a substring match over title, counterparty, summary and filename.

arguments
{
  "query": "renewal",              // optional free text
  "counterparty": "Meridian",      // optional, substring
  "agreement_type": "MSA",         // optional, substring
  "date_from": "2024-01-01",       // optional, effective_date >=
  "date_to": "2026-12-31",         // optional, effective_date <=
  "limit": 25                      // optional, max 100
}
returns (JSON in text content)
{
  "count": 2,
  "results": [{
    "id": "9d2f4c1a-…",
    "title": "Master Services Agreement",
    "filename": "Meridian_MSA_signed.pdf",
    "counterparty": "Meridian Robotics, Inc.",
    "agreement_type": "Master Services Agreement",
    "effective_date": "2024-09-02",     // or null
    "expiration_date": "2026-09-01",    // or null
    "governing_law": "Delaware",
    "status": "ready",                  // received | analyzing | ready | failed
    "summary": "Two-year MSA with auto-renewal…"
  }]
}
get_archived_contract #

Everything extracted from one document — the payload for answering detailed questions with citations.

arguments
{ "archive_id": "9d2f4c1a-…" }   // required
returns (JSON in text content)
{
  …all search_archive fields, plus:
  "executed_date": "2024-08-28",
  "key_terms":   [ { "field": "payment_terms", "value": "Net 60" } ],
  "key_clauses": [ { "clause_type": "limitation_of_liability",
                     "title": "Limitation of Liability",
                     "snippet": "…liable for all damages arising…" } ],
  "risks":       [ { "title": "Liability is uncapped",
                     "description": "No aggregate cap…",
                     "severity": "high" } ],
  "error": null
}
upload_to_archive #

File an executed contract (PDF or DOCX, base64-encoded). Returns immediately; extraction runs in the background — fetch with get_archived_contract after a few seconds. Contracts signed through version90 e-sign file themselves into the archive on completion — no need to re-upload those.

arguments
{
  "filename": "signed_nda.pdf",           // required
  "content_base64": "JVBERi0xLjcK…"       // required
}
returns (JSON in text content)
{
  "id": "7c8d9e0f-…",
  "status": "received",
  "message": "Stored. Extraction runs in the background — fetch it with get_archived_contract in a few seconds."
}
list_contracts #

Active negotiations with lifecycle status and whose turn it is.

arguments
{
  "query": "Meridian",     // optional, substring on title/counterparty
  "status": "with_counterparty",  // optional lifecycle status
  "limit": 25              // optional, max 100
}
returns (JSON in text content)
{
  "count": 1,
  "results": [{
    "id": "1f7a9e42-…",
    "title": "MSA — Meridian Robotics",
    "counterparty": "Meridian Robotics, Inc.",
    "status": "with_counterparty",
    "turn": "counterparty",          // "internal" = waiting on your team
    "updated_at": "2026-07-11T09:41:37+00:00"
  }]
}
get_contract #

One negotiation in depth: the immutable version chain, open AI findings, and — once sent for signature — the signing state (signer progress, cc'd watchers). Read-only — no editing tool exists over MCP.

arguments
{ "workspace_id": "1f7a9e42-…" }   // required
returns (JSON in text content)
{
  "id": "1f7a9e42-…",
  "title": "MSA — Meridian Robotics",
  "counterparty": "Meridian Robotics, Inc.",
  "status": "with_counterparty",
  "turn": "counterparty",
  "versions": [{
    "id": "e8d1b2c3-…",
    "filename": "MSA_Meridian_v3.docx",
    "source": "counterparty_return",   // upload | internal_edit | …
    "created_at": "2026-07-11T09:41:35+00:00"
  }],
  "open_findings": [{
    "title": "Liability is uncapped",
    "type": "nonstandard",
    "severity_note": "Your standard caps liability at 12 months of fees…"
  }],
  "signature": {                       // null until sent for signature
    "status": "partially_signed",      // pending | sent | partially_signed |
                                       // completed | declined | voided | failed
    "provider": "native",              // native | docusign | dropbox_sign
    "signers": [{ "name": "Dana Ferro", "email": "[email protected]",
                  "status": "signed", "signed_at": "2026-07-12T15:02:11+00:00" }],
    "cc_emails": ["[email protected]"],  // watchers, notified each step
    "completed_at": null
  }
}
search_playbook #

The organization's Playbook — approved clause language, standard documents (baselines), and knowledge sources the AI cites. The tool for quoting the org's own preferred terms: 'what is our standard limitation of liability language?'. Approved clauses only; candidates never surface.

arguments
{
  "query": "liability",    // optional free text over clause names/types/language
  "limit": 25              // optional, max 100
}
returns (JSON in text content)
{
  "clauses": [{
    "id": "4b5c6d7e-…",
    "name": "Limitation of liability",
    "clause_type": "limitation_of_liability",
    "intent": "Cap aggregate liability at 12 months of fees.",
    "language": "IN NO EVENT SHALL EITHER PARTY'S AGGREGATE LIABILITY EXCEED…"
  }],
  "standards": [ { "id": "8e9f0a1b-…", "name": "Standard MSA" } ],
  "knowledge": [ { "id": "2c3d4e5f-…", "name": "Security policy" } ]
}
get_usage_meter #

This month's plan usage — lets an agent warn before running out.

arguments
{}
returns (JSON in text content)
{
  "contracts_used": 6,   "contracts_included": 10,
  "ai_credits_used": 84, "ai_credits_included": 300,
  "archive_used": 41,    "archive_capacity": 100
}

Note what's absent: there is no editing tool. That's not a permission setting — the capability doesn't exist over MCP, so no prompt injection, agent bug, or over-eager automation can redline a contract. Humans edit; agents read, file, and answer. Tool errors come back as isError: true with a message in the text content — the request itself still returns 200.

On the wire

tools/call — raw JSON-RPC (what your MCP client does for you)
curl -X POST https://api.version90.com/api/v1/mcp \
  -H "Authorization: Bearer $OAUTH_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0", "id": 1, "method": "tools/call",
    "params": { "name": "search_archive",
                "arguments": { "counterparty": "Meridian" } }
  }'
# → { "jsonrpc":"2.0", "id":1, "result": {
#      "content": [{ "type":"text", "text":"{\"count\":2,\"results\":[…]}" }],
#      "isError": false } }

Prompts that just work

  • "Which of our agreements auto-renew in the next 90 days, and what notice does each require?"
  • "Summarize every contract we have with Meridian Robotics, newest first."
  • "File the attached signed NDA into the archive and tell me what it says about data retention."
  • "Do any of our archived contracts have uncapped liability? List them with the clause."
  • "What's our standard limitation of liability language, and does the Meridian draft match it?"
  • "What's waiting on us right now across active negotiations, and where is the Meridian signing at?"