MCP server

Let AI agents such as Claude Code, Cursor and OpenAI's models use your clients' connections as tools.

The MCP server gives an AI agent your clients' connections as tools: list clients, find and run any of 89,000+ actions, call any endpoint. The agent holds an Operate As key, never a client's token. Every tool call goes through the same key limits, plan allowance and usage counting as the HTTPS API.

Connect your agent

The server is at https://api.operate.as/v1/mcp, with your key as Authorization: Bearer oa_....

claude mcp add --transport http operate-as https://api.operate.as/v1/mcp \
  --header "Authorization: Bearer oa_..."

Then ask "Which of my clients have Sheets connected?" and it calls list_clients.

Tools

There are thousands of actions, far more than an agent can be handed at once: a list that long fills its context and makes it worse at choosing. So the server lists a few tools that find and run the rest, and next to them the actions of the services your clients have connected.

ToolWhat it doesArguments
list_clientsThe clients this key can use, with their connected servicesNone
search_toolsThe actions that best match a few words, with their inputsquery, and optionally service and limit (10, up to 50)
get_toolOne action's inputs, with their full JSON Schemastool
run_toolRuns any action for one clientclient_id, tool, and input with the action's inputs
One per listed action, such as sheets_read_rowsRuns that action for one clientclient_id, plus the action's inputs
requestAny endpoint of a service's own API, like a pass-through requestclient_id, service, method, path, query, body
  • Which actions are listed. Those of the services the key's clients have connected, up to 100 in all, smallest services first. A service with more than fits, such as GitHub's 1,200, lists only its hand-written actions; the agent finds the rest with search_tools. Connect a new service and the list grows the next time the agent asks for it.
  • search_tools searches what's connected. Without service, it searches every service the key's clients have connected. Pass service to search one, connected or not.
  • Any action can be run. run_tool takes the name from search_tools, with a dot (stripe.post_customers) or an underscore (stripe_post_customers), and an action can also be called directly by its underscore name even when it isn't listed.
  • Names use underscores in tool names, since MCP tool names can't hold dots: sheets.read_rows is sheets_read_rows.
  • request takes method (GET, POST, PUT, PATCH, DELETE), path under the service's API such as /v4/spreadsheets/1BxiM..., an object of query parameters and a JSON body.
  • The server sends instructions: call list_clients first, use a listed tool when one fits, otherwise find one with search_tools, read it with get_tool and run it with run_tool.

Errors the agent can read

A failed tool call doesn't break the conversation with a protocol error. It's a normal tool result marked isError: true, in plain words:

{
  "content": [{ "type": "text", "text": "Miller Dental's Google access ended. Send them their link to reconnect." }],
  "isError": true
}

So the agent can react: tell you the client must reconnect, pick another client, or fix an input and retry. When the service itself answers 400 or more, the result holds its status and data as JSON, also marked isError: true.

A missing, wrong or revoked key is refused before MCP starts, with HTTP 401, so the client reports a connection failure.

Protocol details

DetailHow it works
TransportStreamable HTTP, answered as plain JSON on the same POST. No server-sent event stream, so GET answers 405 Method Not Allowed
Protocol versions2025-06-18, 2025-03-26, 2024-11-05. Ask for one and you get it; otherwise 2025-06-18
BatchesA JSON array of messages gets an array of answers

Give each agent its own key

An agent decides for itself what to call, so give it only what its job needs.

LimitUse it to
ClientsKeep an agent that works for one client away from the rest
ServicesAllow only, say, calendar and gmail for a booking assistant
ActionsKeep it read-only: sheets.read_rows without sheets.add_row, and no <service>.request so it can't use request

The agent only sees what its key may use: list_clients, the listed tools and search_tools all leave out the rest. Calling a tool outside the key's limits returns an error result like This key can't use hubspot. Set all three limits in the dashboard's create form by choosing Restricted. See API keys.

Example conversation

  1. You: Add Jane Doe's cleaning on 16 October to Miller Dental's bookings sheet.
  2. Tool call list_clients: Finds Miller Dental, with sheets connected.
  3. Tool call sheets_add_row: client_id, spreadsheet_id, range: "Bookings!A1", values: ["2026-10-16", "Jane Doe", "Cleaning"]. The result has "status": 200 and the updated range.
  4. Agent: Done. I added the row to Miller Dental's bookings sheet at row 12.

Had Miller Dental's Google access ended, the tool result would say so, and the agent would tell you to send them their link instead of failing silently.

When no listed tool fits, the agent searches:

  1. You: Open a GitHub issue on Miller Dental's website repo: the booking form breaks on Safari.
  2. Tool call search_tools: query: "create github issue" finds github.issues_create first, with its inputs owner, repo and body.
  3. Tool call run_tool: client_id, tool: "github.issues_create", input: {"owner": "millerdental", "repo": "website", "body": {"title": "Booking form breaks on Safari"}}.
  4. Agent: Opened issue #214 on millerdental/website.

On this page