Developer docs
MCP server for AI agents
Let Claude Code, Cursor, VS Code / Copilot, Claude Desktop, claude.ai or any MCP client read your organization's approved documents. Ops publishes v14 of the checkout spec; the developer asks “pull the approved Checkout spec and implement section 3” and the agent reads v14, never a draft.
Endpoint: https://versodocs.com/api/mcp: remote, Streamable HTTP. The organization comes from the credential, so the URL carries none.
Connect with one click (OAuth)
Recommended for people. Add the URL to your client; on first use it opens VersoDocs in your browser, you sign in, pick the organization and click Allow. No key to copy. Implements the MCP Authorization spec (2025-06-18).
Claude Code
claude mcp add --transport http versodocs https://versodocs.com/api/mcp- Run the command in your terminal (add --scope project to share it with your repo).
- In Claude Code, type /mcp, pick versodocs and choose Authenticate.
- Your browser opens VersoDocs. Sign in and click Allow.
Cursor
- Settings → MCP → Add new MCP server → type "Streamable HTTP" (or "url"), URL https://versodocs.com/api/mcp.
- Click Connect (or Needs login) next to versodocs.
- Sign in to VersoDocs in the browser window and click Allow.
VS Code / Copilot
- Command Palette → "MCP: Add Server…" → HTTP → paste https://versodocs.com/api/mcp → name it versodocs.
- Start the server; VS Code asks to authenticate. Click Allow.
- Sign in to VersoDocs in the browser and click Allow.
Claude Desktop / claude.ai
- Settings → Connectors → Add custom connector.
- Name it VersoDocs and paste https://versodocs.com/api/mcp.
- Click Connect, sign in to VersoDocs and click Allow.
The consent screen shows which app is asking, an organization picker if you belong to several, what the app can and can't do, your plan's limits, the scopes to grant (on Team: review proposals and propose changes, the latter only if you can edit, 25 agent proposals a month per organization; on Pro also manage docs & folders and publish approved proposals, unchecked by default) and, on Pro, an optional single-workspace limit.
- The app acts as you: it sees only workspaces you can open, and stops working if you leave the org.
- Each connected app (per app, member and org) counts as one key toward the plan's limit (Free 1, Team 5, Pro 20). Reconnecting the same app replaces the old connection.
- Admins see and disconnect apps under Settings → Developer → Connected AI apps.
Connect with an API key
For CI, scripts and clients without OAuth. Use a key from Organization settings → Developer, the same keys as the REST API. The config-file snippets read the key from an environment variable or a secure prompt, so it never lands in a committed file.
Claude Code
Run in your terminal (add --scope project to share with your repo, minus the key)
claude mcp add --transport http versodocs https://versodocs.com/api/mcp --header "Authorization: Bearer vsd_YOUR_KEY"Cursor
.cursor/mcp.json (set VERSODOCS_API_KEY in your environment)
{
"mcpServers": {
"versodocs": {
"url": "https://versodocs.com/api/mcp",
"headers": {
"Authorization": "Bearer ${env:VERSODOCS_API_KEY}"
}
}
}
}VS Code / Copilot
.vscode/mcp.json (VS Code prompts for the key once and stores it securely)
{
"inputs": [
{
"type": "promptString",
"id": "versodocs-key",
"description": "VersoDocs API key",
"password": true
}
],
"servers": {
"versodocs": {
"type": "http",
"url": "https://versodocs.com/api/mcp",
"headers": {
"Authorization": "Bearer ${input:versodocs-key}"
}
}
}
}Claude Desktop and claude.ai connect with OAuth only (custom connector), with no mcp-remote bridge or key needed.
Tools
30 tools. tools/list shows a tool only when the credential holds its scope, so an agent never sees write tools it wasn't granted. A held tool outside the org's plan (e.g. Team reads on Free, or after a downgrade) stays listed with a plan note in its description; calling it returns an isError result naming the plan that unlocks it and the upgrade URL.
| Tool | What it does | Plan |
|---|---|---|
search_docs{query, limit?} | Find published (approved) VersoDocs documents (specs, runbooks, policies) matching a query. Returns titles, paths, current version and ids; read one with get_doc. Full-text search on Team/Pro; on Free it matches titles and paths only. | All plans |
list_docs{folder?, updated_since?, cursor?, limit?} | List published (approved) documents, most recently updated first. Filter by folder path (<workspace> or <workspace>/<folder>, includes subfolders) and/or updated_since to see what changed recently. Paginate with the returned cursor. | All plans |
get_doc{path_or_id, version?, format?, section?} | Read a document’s approved content: the Current published version by default (never drafts). Returns Markdown with a header (title, version, published date, URL, citable reference). Pass section (a heading, e.g. "3" or "Payment flow") to read just that part of a long spec; version reads an older published version (Team/Pro). | All plans |
get_doc_tree{workspace?} | The workspace → folder → document hierarchy of published docs, with paths you can pass to get_doc. Optionally limit to one workspace (its slug). | Team+ |
list_versions{path_or_id} | Version history of a document: every published (approved) version, newest first, with date, author and citable reference. Use diff_versions to see what changed between two. | Team+ |
diff_versions{path_or_id, from?, to?} | What changed in a document between two published versions: a summary plus a unified Markdown diff. from defaults to the version before to; to defaults to Current. Ideal for "what changed in the spec since v12?" (from: 12). | Team+ |
check_reference{reference} | Check whether a cited spec version is still the Current approved one. Pass a reference versodocs:<org>/<id>@vN or a VersoDocs doc URL with ?v=N (or a block of text containing several, e.g. a PR description). Reports current / outdated / ahead per reference. | All plans |
list_attachments{path_or_id} | Files attached to the Current version of a document (PDFs, images, exports…) with their ids, names and sizes. Fetch one with get_attachment_url. | Team+ |
get_attachment_url{path_or_id, attachment_id} | A short-lived (5 minute) signed download URL for one attachment of a document’s Current version. Download it right away; do not store or share the link. | Team+ |
propose_change{path_or_id, title, description?, content_markdown?, edits?, reviewers?} | Open a proposal to change a document. It goes to human review in VersoDocs and is NEVER published by this tool; reviewers approve and publish it. Send EITHER content_markdown (the COMPLETE new document) OR edits: a list of { find, replace } applied in order to the Current Markdown (each find must match exactly once; read the doc with get_doc first and copy text exactly). Prefer edits for small changes to long docs. Images:  embeds an image by URL; to add a new image or file, call upload_attachment first and paste the Markdown it returns. Existing images and attachments (as get_doc shows them) are kept when you keep their Markdown. Optionally request reviewers by email. | Team+ · docs:propose |
upload_attachment{path_or_id, filename, content_type, data_base64, alt?} | Upload an image (PNG, JPEG, GIF, WebP) or file (PDF, Word, Excel, PowerPoint, CSV, text, Markdown, JSON, ZIP) of up to 5.0 MB, base64-encoded, for a document you will propose a change to. Returns a Markdown snippet: put it in content_markdown or an edit's replace of propose_change / update_proposal to embed it. Nothing is published by this tool; an upload no proposal uses is deleted after a day. Larger files: use the REST API's presigned upload. | Team+ · docs:propose or docs:write |
get_context_pack{workspace?, folder?, max_chars?} | Every approved (Current) document in scope concatenated as Markdown, with an index and a header per doc (path, version, approvers, publish date, link). Use it to ground a task in the whole spec set at once; narrow with workspace or folder and cap size with max_chars (default 200000). | Team+ |
list_links{proposal_id?, path_or_id?} | Jira / Linear issues linked to a document or a proposal: key, provider, cached title and status, URL. | Team+ |
link_issue{proposal_id?, path_or_id?, url} | Link a Jira Cloud or Linear issue (paste its URL) to a proposal or a document, so reviewers see what the change implements. Proposals need the proposals:review scope, documents docs:write. | Team+ · proposals:review or docs:write |
list_proposals{status?, document?, limit?} | Proposals (suggested changes under review) on documents you can see, newest first. Default: open ones (in review, changes requested, approved). Filter by status or by document. Read one with get_proposal. | Team+ · proposals:review |
get_proposal{proposal_id, include_content?} | One proposal: status, the diff against the version it forked from, reviewers and their verdicts, advisory AI reviews and the comment threads (with comment ids for replies). Set include_content to also get the full proposed Markdown (needed before update_proposal edits). | Team+ · proposals:review |
comment_on_proposal{proposal_id, body, quote?, reply_to?} | Leave a review comment on a proposal, posted as your user and labeled as coming from this agent. quote (exact text from one paragraph of the proposal) anchors it inline; reply_to (a comment_id from get_proposal) answers a thread; neither posts to the conversation. @mention people by email. | Team+ · proposals:review |
resolve_comment{proposal_id, comment_id, resolved?} | Mark a comment thread on a proposal resolved (or reopen it with resolved: false). Any comment_id of the thread works. | Team+ · proposals:review |
submit_review{proposal_id, verdict, body?} | Give your verdict on a proposal: approve, request_changes or comment (body required for the last two). Your verdict is ADVISORY: shown to reviewers as an AI review; it never counts as a required human approval and never publishes anything. | Team+ · proposals:review |
update_proposal{proposal_id, title?, description?, content_markdown?, edits?} | Revise a proposal THIS agent opened (e.g. after review feedback): new title / description, and the content as either content_markdown (whole document) or edits ([{ find, replace }] applied to the proposal’s current text; read it with get_proposal include_content first). New images or files: upload_attachment, then paste its Markdown. Editing after approval dismisses the approvals (reviewers must look again). | Team+ · docs:propose |
close_proposal{proposal_id, reason?} | Close (withdraw) a proposal THIS agent opened, without publishing. Participants are notified. | Team+ · docs:propose |
publish_proposal{proposal_id} | Publish a proposal that human reviewers APPROVED: its text becomes the new Current version. Refused when it is not approved, is stale, breaks a workspace publish rule, or the workspace requires an interactive e-signature (a person must publish then). | Pro · proposals:publish |
create_doc{workspace, title, folder_path?, content_markdown?} | Create a new document in a workspace (optionally inside a folder). Nothing is published: content_markdown, if given, opens a first proposal ("Initial version") for human review. | Pro · docs:write |
create_folder{workspace, name, parent_path?} | Create a folder in a workspace, at the root or inside parent_path. | Pro · docs:write |
rename_doc{path_or_id, title} | Change a document’s title (its URL slug follows). Content and versions are untouched. | Pro · docs:write |
move_doc{path_or_id, folder_path} | Move a document to another folder of the SAME workspace ("" = workspace root). | Pro · docs:write |
delete_doc{path_or_id, confirm_title} | PERMANENTLY delete a document and all its drafts and proposals. This cannot be undone. Only documents with NO published version can be deleted by an agent; approved documents must be deleted by a person in VersoDocs. confirm_title must repeat the exact title. Ask the user before calling this. | Pro · docs:write |
list_document_comments{path_or_id, version?, include_resolved?} | Comment threads on a published document: the Current version by default, or version. Includes threads written on an older version and carried forward (marked with the version they came from). Open threads only unless include_resolved. Comment ids are for comment_on_document reply_to / resolve_document_comment. | Team+ |
comment_on_document{path_or_id, body, quote?, reply_to?, version?} | Comment on a published document, posted as your user and labeled as coming from this agent. Start a thread with quote (exact text from one paragraph of the version, default Current; read it with get_doc), or answer one with reply_to (a comment_id from list_document_comments). @mention people by email. To suggest an edit, use propose_change instead. | Team+ · proposals:review |
resolve_document_comment{path_or_id, comment_id, resolved?} | Mark a comment thread on a document resolved (or reopen it with resolved: false). Any comment_id of the thread works. | Team+ · proposals:review |
path_or_idaccepts a document id, thepathshown bylist_docs, a VersoDocs URL, or a referenceversodocs:<org>/<id>@vN. A URL or reference that pins a version (?v=12,@v12) reads that version on Team+; on Free the agent gets Current and a note.search_docsis full-text on Team and Pro; on Free it matches titles and paths and says so. Explicitget_docversionneeds Team.propose_changeandupdate_proposaltake the content ascontent_markdown(the whole document) oredits:[{ find, replace }]applied in order, eachfindmatching exactly once (all or nothing). Prefereditsfor small changes to long documents.- Images and files in proposals:
upload_attachmenttakes the bytes base64-encoded (up to 5.0 MB) and returns a Markdown snippet to paste intocontent_markdownor an edit.embeds an image by URL; images and attachments already in the document survive as long as their Markdown is kept. Larger files: the REST presigned upload. proposal_idaccepts the proposal id or its VersoDocs URL.get_proposalreturns the diff, reviewers, advisory AI reviews and comment ids (forreply_to/resolve_comment);include_contentadds the full text.list_document_commentsreads the comment threads on a published document (Current, orversion; withdocs:read), including threads carried forward from older versions with the version each was written on.comment_on_document(aquotefrom the version, orreply_to) andresolve_document_commentneedproposals:review.- Read tools carry
readOnlyHint: true;delete_doccarriesdestructiveHint: true(every other toolfalse); every tool hasopenWorldHint: false. - What agents may do, the safety rules (advisory verdicts, own proposals only, no e-signatures, guarded deletes) and the REST twin of each tool: Agent workflows.
Example prompts
- “Pull the approved Checkout spec from VersoDocs and implement section 3.”
- “What changed in the Payments API spec since v12? Update the client to match.”
- “Is the spec referenced in this PR description still Current?”
- “Search VersoDocs for our retry policy and follow it in this change.”
- “Cite the spec version you implemented in a code comment.” Agents cite the
reference;check_referenceand the GitHub spec check later tell you when it moved on. - Team / Pro: “Draft the API changes we just made back into the Payments spec and propose it for review by lead@acme.com.”
- Team / Pro: “Review the open proposals on the Payments spec against our API style guide; comment inline and leave a verdict.”
- Pro: “Address the review comments on my proposal, update it, and publish it once it’s approved.”
- Pro: “Create an
apis/v2folder in eng and move the v2 specs into it.”
Limits & protocol
- Rate limit: the key's or connected app's plan budget (30 / 120 / 600 req/min), shared with the REST API. A batch is charged one request per
tools/call. - Large documents: tool text is capped at about 60,000 characters. A longer document is truncated with a note and its heading outline, and the agent re-asks with
section. Diffs and trees truncate the same way. - Protocol: MCP
2025-06-18(also negotiates2025-03-26and2024-11-05). Stateless, JSON responses only (no SSE, no session id).GET→ 405. POST one JSON-RPC message or a batch (max 20); notifications → 202. Methods:initialize,ping,tools/list,tools/call. - CORS is open (
*) because auth is a bearer header, never a cookie.
OAuth flow (for client authors)
Standard MCP Authorization: you only need this if you are building an MCP client.
POST /api/mcpwithout a token →401withWWW-Authenticate: Bearer resource_metadata="https://versodocs.com/.well-known/oauth-protected-resource".GET /.well-known/oauth-protected-resource(RFC 9728) →resource=https://versodocs.com/api/mcp,authorization_servers=["https://versodocs.com"].GET /.well-known/oauth-authorization-server(RFC 8414) → authorize/oauth/authorize, token/api/oauth/token, registration/api/oauth/register, revocation/api/oauth/revoke.S256only, public clients only.POST /api/oauth/register(RFC 7591 dynamic registration) →client_id. Public clients (token_endpoint_auth_method: none); redirect URIs must be https, loopback http (localhost,127.0.0.1,[::1], any port) or an app scheme such ascursor://. Throttled per IP.- Browser →
/oauth/authorizewithresponse_type=code,client_id,redirect_uri,code_challenge,code_challenge_method=S256,state,resource=https://versodocs.com/api/mcpand optionalscope→ sign in → consent → redirect withcode,state,iss(deny →error=access_denied). POST /api/oauth/token(form-encoded; JSON accepted) withgrant_type=authorization_code,code,redirect_uri,client_id,code_verifier.- Call
/api/mcpwithAuthorization: Bearer vsd_at_…. Refresh withgrant_type=refresh_token.
{
"access_token": "vsd_at_…",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "vsd_rt_…",
"scope": "docs:read"
}| Credential | Lifetime | Notes |
|---|---|---|
| Authorization code | 60 seconds | Single use; a replayed code revokes what it minted |
vsd_at_… | 1 hour | Valid on /api/mcp only |
vsd_rt_… | 30 days | Rotates on every use; reusing an old one revokes the whole grant |
Scopes (space-separated in scope; omitted = everything the plan allows, narrowed on the consent screen): docs:read (always), proposals:review (Team and Pro), docs:propose (Team and Pro, members with edit rights; on Team capped at 25 agent proposals per organization per calendar month, UTC), docs:write and proposals:publish (Pro, members with edit rights, opt-in). See scopes per plan. Revoke with POST /api/oauth/revoke (RFC 7009).
Security
- Read-only by default. A credential has
docs:readunless given more scopes.docs:propose: on Team only one-click connections (acting as their member), 25 agent proposals a month per organization; on Pro API keys too, unlimited.propose_changeopens a proposal your reviewers must approve; it never edits Current. - Reviews are advisory.
submit_reviewverdicts are shown as AI reviews and never count toward required approvals or trigger auto-publish. - Publishing needs people.
publish_proposal(Pro,proposals:publish) only publishes proposals humans approved, never with an override, and is refused when the workspace requires an e-signature. Agents revise or close only their own proposals. - Guarded deletes.
delete_doc(Pro,docs:write) only removes documents with no published version, and needs the exact title inconfirm_title. - Published content only. Drafts and proposals in progress never leave VersoDocs; archived, unpublished and out-of-scope documents read as not found.
- Connected apps act as their member: same workspace visibility, and a member demoted to viewer loses
docs:propose,docs:writeandproposals:publishimmediately. Their comments and actions are labeled with the app name. - Scope. Free credentials see the org's active workspace only; a Pro workspace-scoped key sees just its workspace, as on the REST API.
- OAuth hardening. PKCE S256 mandatory,
staterequired, exact redirect-URI matching (loopback may change port), only SHA-256 hashes of codes and tokens stored,isson responses (RFC 9207), consent page cannot be framed. Tokens are never logged. - Revocation is immediate: revoke a key or disconnect an app in Settings → Developer. Use one key per developer or tool.
- Auditing: on Pro, MCP usage is sampled into the audit log (
api.request, endpointPOST /api/mcp), proposals are logged asapi.proposal_created, and agent writes (verdicts, edits, publishes, docs & folders) carry the credential that made them.