VitrinaAPI
MCP

Use the whole API from your AI

Reach any published operation of the API from your assistant.

Any published operation of the API is within your assistant's reach, without anyone having had to write it a tool by hand. Three connector tools do that, and none of them is tied to a resource. The named ones are in Connector tools.

ToolWhat it does
search_operationsFinds operations this credential can call. Returns identifiers.
describe_operationOne operation's contract: parameters, which are required, and what it answers.
call_operationActually calls it and returns the response body.

Every operation search_operations returns carries its method, whether it writes and whether it is destructive. Destructive means it ends something that can't be brought back. That's how your assistant tells which of two similar identifiers is the one that deletes.

The difference from the other tools is in the design, not in the permissions. The others answer in one call what a person asks in words, and hand back an answer already shaped. These three answer what an integration asks: a record field by field, a list under the API's exact filters, or something that has no tool of its own yet.

One full conversation

First, what is there:

// search_operations  { "query": "locations" }
{
  "total": 2,
  "returned": 2,
  "tags": ["Leads", "Locations", "Pipelines", "Public Stock", "Vehicles", "…"],
  "operations": [
    {
      "operation_id": "locations_list",
      "method": "GET",
      "path": "/locations",
      "tag": "Locations",
      "tier": "beta",
      "summary": "List locations (branches / stores / clinics / offices)",
      "scopes": ["tenant:read"]
    },
    {
      "operation_id": "location_get",
      "method": "GET",
      "path": "/locations/{id}",
      "tag": "Locations",
      "tier": "beta",
      "summary": "Get a location (uuid or B- display id)",
      "scopes": ["tenant:read"]
    }
  ]
}

Then, how to call it:

// describe_operation  { "operation_id": "location_get" }
{
  "operation_id": "location_get",
  "aliases": ["GET /locations/{id}"],
  "method": "GET",
  "path": "/locations/{id}",
  "url": "/api/v1/locations/{id}",
  "tier": "beta",
  "scopes": ["tenant:read"],
  "parameters": [
    { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
  ],
  "responses": { "200": { "description": "Location" } }
}

And finally the call:

// call_operation  { "operation_id": "location_get", "params": { "id": "B-2" } }
{
  "data": {
    "id": "6812d9f0-9bed-44b5-91df-4e5b90941f6b",
    "display_id": "B-2",
    "name": "Sucursal Norte",
    "…": "…"
  }
}

Path parameters and query parameters go together in params, under the names describe_operation lists. A name the operation doesn't declare is refused rather than ignored. A filter that falls away in silence returns more rows than you asked for, and your assistant would report them to you as filtered.

The identifiers

locations_list, location_get, webhook_deliveries_list. They read, and they're stable. The raw "GET /locations/{id}" form, the one that appears under aliases, works too if you're reading the OpenAPI document.

What they reach, and what they do not

Trap

What is not listed cannot be called either

An operation that doesn't come back from search_operations can't be called either: it answers Unknown operation, whether you ask for it by identifier or by its raw form.

What's inside is the published operations, the ones the API promises and versions, and nothing else.

BoundaryWhat it means
Reads alwaysReads (GET) are there from day one.
Writes only where you granted themA write appears, and works, only in the areas whose write permission you switched on when you authorised the application, and never beyond what your role reaches. One in an area you didn't grant is not in the list and answers Unknown operation.
Never sensitive dataWhat the specification marks as sensitive stays out under any permission, whatever the workspace's vertical. No checkbox opens that filter. Which ones they are, and what holding them obliges, is in Personal and health data.
Nothing internalWhat the API doesn't publish (application surfaces, internal operations tooling) is not in this catalogue.
Only what your credential already openedAn operation is offered if your credential's permissions open it and the workspace's vertical has it enabled. A connector without the webhooks permission doesn't see the webhook operations: they don't exist for it.

Writing

If you switched on an area when you authorised the application, «Leads», say, that area's write operations appear in search_operations and call_operation runs them. The request body goes in body, and describe_operation tells you which fields it takes.

// call_operation
{
  "operation_id": "leads_create",
  "body": { "contact_id": "…", "pipeline_id": "…", "title": "SUV enquiry" }
}

Trap

Retrying is safe: repeats are protected by default

An identical call (same operation, same params, same body) replays the first response instead of writing twice. A retry after a timeout won't leave you with two identical leads. When you do want a second identical write, send it with a different idempotency_key.

Destructive operations are marked ("destructive": true) in the list and in the contract. The tool announces itself as destructive to your MCP client, which is what makes Claude ask you before running one. It's a signal so you can decide: what actually decides is the API, with your credential's permissions.

Sending a message to a customer of yours has its own area, «Messages to customers», and its own rules. One contact at a time goes out directly. From the fourth distinct contact within ten minutes, the message waits for approval. See Send messages.

It is your own credential

call_operation is not a shortcut through Vitrina's insides: it builds the HTTP request you'd have made and sends it through the same API. The credential is the one your assistant connected with. Everything applies once, from the place it always has: authentication, permissions, validation, visibility, the audit log and the usage limits. The record and branch visibility is the authorising person's.

That is why the response is identical, byte for byte, to what the API would have answered had it been called directly with that credential. And why an error arrives with the API's exact text:

GET /vehicles answered 400: {"error":{"code":"VALIDATION_ERROR",
  "message":"Request validation failed",
  "details":{"query":[{"path":"sort","message":"Invalid enum value…"}]},
  "requestId":"f7451f40-cf32-4a7f-aefc-77d791ff6afb"}}

That requestId is the same one the server logs carry, so it is what to quote when asking what happened.

What it returns about a person

It depends on the workspace's vertical: in one of them identity travels trimmed by default.

call_operation returns every contact the way your team sees them: name, phone and email. There is no identity filter on top; what trims the view is your credential's permissions.

Files

An operation that answers with a file (an export, an attachment's contents) doesn't send the bytes over MCP. Your assistant receives the description and the size:

{
  "operation_id": "vehicles_export_list",
  "status": 200,
  "content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  "size_bytes": 7746,
  "note": "Binary response — call this operation over HTTPS to download it; the bytes are not returned over MCP."
}

A binary file inside a conversation is unreadable text filling the whole context. To download it, the operation is still there in the API.

On this page