MCP server
Vendorica ships a Model Context Protocol server, so an AI agent can read your compliance posture, controls, vendors and incidents as tools rather than by you pasting exports into a chat window.
POST https://api.vendorica.com/mcpStateless Streamable HTTP, plain JSON responses. Every request is a complete JSON-RPC exchange — there is no session to keep alive and nothing to reconnect.
Two ways to authenticate
Section titled “Two ways to authenticate”Both resolve to the same principal, and both carry your organization’s scopes.
| Credential | Looks like | Use it for |
|---|---|---|
| API key | vnd_live_… |
your own agents and scripts |
| OAuth access token | vnd_mcp_at_… |
connecting a hosted assistant |
For the OAuth path the server implements discovery, dynamic client registration and PKCE, so a client that speaks MCP can connect without anyone pre-registering it:
GET /.well-known/oauth-authorization-serverPOST /mcp/oauth/registerGET /mcp/oauth/authorize ← the human consents herePOST /mcp/oauth/tokenA 401 from POST /mcp carries a WWW-Authenticate header pointing at the
protected-resource metadata, which is how a client discovers where to
authorize. That header is part of the contract, not an implementation detail.
The tools
Section titled “The tools”Tools come in two lanes, and the lane travels on the wire as the MCP tool annotations your client reads. That is what lets Claude — or any client that honours the annotations — run a read under a standing approval while stopping to ask a human before a write.
| Lane | Annotation | What a client should do with it |
|---|---|---|
| Read | readOnlyHint: true, openWorldHint: false |
Answers a question about data you already hold and changes nothing. Safe to allow once for the session. |
| Write | readOnlyHint: false, destructiveHint: false |
Mutates something, spends your AI budget, or leaves Vendorica. Put a person in front of each call. |
destructiveHint is false on every tool and always will be: nothing
that deletes or irreversibly overwrites is exposed as a tool at all.
Read lane
Section titled “Read lane”| Tool | Scope | What it answers |
|---|---|---|
get_compliance_posture |
compliance:read |
overall readiness, by framework |
get_control_status |
controls:read |
one control in full, or a search by name — status, owner, domain, and the framework citations it satisfies |
list_vendors |
vendors:read |
the vendor register, paginated — filter by name, lifecycle state, country or inherent-risk band |
query_vendor_risk |
vendors:read + risks:read |
vendors with their open risk scenarios, scores and approval status |
list_contracts |
contracts:read |
the contract register, paginated — filter by counterparty, lifecycle phase or creation date |
list_incidents |
incidents:read |
the incident register, paginated — filter by title, status, priority or vendor |
get_incident_context |
incidents:read |
one incident: classification, severity, status, reporter, timeline |
search_regulatory_requirements |
controls:read |
the regulatory knowledge base |
run_gap_analysis |
compliance:read |
per-citation gaps for a framework, worst first, with the counts behind each score |
get_audit_evidence |
evidence:read |
evidence for a control — automated test results and manual uploads together |
Write lane
Section titled “Write lane”| Tool | Scope | What it does |
|---|---|---|
draft_policy |
policies:write |
a policy draft grounded in the knowledge base, returned with its citations |
draft_policy saves nothing, so calling it a write is a judgement — and
it is the judgement the annotation exists to express. Each call spends
your organization’s AI budget and reaches a model provider (hence
openWorldHint: true), and it is the one tool whose scope the default
OAuth grant withholds. A client that auto-ran it would be auto-spending.
Paging a register
Section titled “Paging a register”The three list_* tools return pagination alongside the rows, and
default to 25 rows per call (100 maximum) rather than the /v1
default of 100. The bound is about the model, not the database: a page
of 500 rows is context the agent pays for and then has less room left to
reason with. Walk a whole register by following pagination.totalPages:
{ "tool": "list_vendors", "arguments": { "state": "active", "page": 2, "limit": 50 }}Plus resources for the regulatory texts themselves:
regulation://dora/articlesregulation://mica/articlesResources carry no lane — a resource is a read by definition, and the protocol has no annotation for one.
What an agent can and cannot do
Section titled “What an agent can and cannot do”Scopes bind the agent exactly as they bind a key. A tool call runs with
the scopes of the credential behind it, so a read-only key stays read-only
through MCP. get_audit_evidence additionally excludes evidence flagged
sensitive for agent connections.
The lane and the scope agree by construction. A write-lane tool gates
on a :write scope and a read-lane tool gates only on :read scopes —
the server refuses to start otherwise. So the annotation your client
trusts and the authorization the server enforces are two readings of one
declaration, not two lists someone keeps in sync.
The whole catalogue is listed even when your credential is narrower.
A tool you cannot call answers with an isError result naming the scope
to re-consent to, rather than being hidden — an agent that cannot see a
tool reports the feature as nonexistent, which is a worse answer than
“ask for this scope”.
draft_policy does not save anything. It returns markdown and the
citations it was grounded in; a human reviews it and creates the policy in
Vendorica. AI suggests, people confirm — and that boundary is in the server,
not in a prompt.
Every tool call is logged. Each invocation writes an audit row, and an OAuth-token call also records the human who granted the connection, so “the agent did it” is never the end of the trail.
Rate limit
Section titled “Rate limit”120/min, keyed per client — deliberately tighter than the /v1 read
budget because one tool call fans out into several kernel queries. Not
overridable. See rate limits.