Skip to content
Dashboard

MCP Server

SuiteOp hosts a Model Context Protocol (MCP) server so AI assistants and agents can work with your SuiteOp data. Every public REST operation except the four webhook operations (the three webhook subscription operations and listWorkflowZapierSteps) is also an MCP tool: the tools are generated from the same operation catalogue as the REST API, run through the same permission checks, and return the same fields.

Connecting an assistant with OAuth (Claude, ChatGPT and other clients that sign you in): use one address for every region:

https://mcp.suiteop.com/mcp

SuiteOp routes each request to your organization’s region for you, so you don’t need to know which region you are in.

Connecting with an API key (sk_live_…): use the endpoint next to the REST API in your organization’s region. The global address only accepts OAuth tokens.

RegionMCP endpoint for API keys
UShttps://api-us.suiteop.com/mcp
EUhttps://api-eu.suiteop.com/mcp
APAChttps://api-apac.suiteop.com/mcp

How the server behaves on the wire:

  • Transport: MCP Streamable HTTP, stateless. There is no session. Every request is independent and responses come back as plain JSON, not as an event stream.
  • Accept: send Accept: application/json, text/event-stream. A request that lists only application/json is refused with 406. The official MCP SDK clients send both.
  • Methods: only POST /mcp. GET and DELETE return 405 with Allow: POST.
  • Batches: a JSON-RPC batch may hold up to 20 messages. A larger batch is refused with 400 and JSON-RPC error code -32600.
  • Browsers: requests that carry an Origin header from a site SuiteOp doesn’t allow are refused with 403. Server-side clients send no Origin and aren’t affected.
  • Server info: the server identifies itself as suiteop (title SuiteOp), version 1.0.0, and offers tools only (no resources or prompts). Its initialize response carries instructions, a short description of SuiteOp that asks the model to confirm intent before calling destructive or open-world tools.

/mcp accepts the same credentials as /api/v1, sent as a Bearer token:

  • OAuth 2.1 (recommended for assistants that people connect themselves). When a client calls /mcp without a token, the server answers 401 with a WWW-Authenticate header that points to /.well-known/oauth-protected-resource. An OAuth-capable client follows it, registers itself, and walks the user through consent. The assistant then acts as that user, limited to what that user can do. See OAuth 2.1.
  • An API key (for your own agents and scripts). Send Authorization: Bearer sk_live_…. The agent then acts as the key’s machine member with the key’s permissions.
  1. In your assistant’s settings, add a remote MCP server (sometimes called a “custom connector”) and paste https://mcp.suiteop.com/mcp.

  2. The assistant opens a SuiteOp sign-in page. Sign in, pick the organization, review the requested permissions, and click Authorize.

  3. The assistant lists the tools your permissions allow and can start calling them.

  • One tool per REST operation, except listWebhookSubscriptions, createWebhookSubscription, deleteWebhookSubscription and listWorkflowZapierSteps, which are REST-only. The tool name is the operation ID from the API Reference, for example listTasks, createReservation or unlockDevice.
  • You only see what you may call. tools/list includes a tool only when the credential holds every permission the operation requires. The list for a key with only view_tasks contains listDepartments, listTasks, getTask, listTaskRequirements and listAnswerLists, plus the four operations that need no specific permission (searchCoverIcons, searchStockPhotos, getTranslationEntries, getTranslationEntriesBatch). See the scope reference. Calling a tool that isn’t in your list returns an error result Unknown tool: <name>.
  • Arguments are one flat object. Path parameters, query parameters and the request body of the REST operation are all fields of the same arguments object. For example, updateTask takes { "id": "…", "data": { "priority": "high" } }. Each tool publishes its inputSchema. Follow it, because for a few tools it accepts slightly more lenient input than the REST body.
  • Outputs have a schema. Each tool also publishes an outputSchema. A successful call returns the result as structuredContent and again as JSON text in content. When an operation returns a bare list, it is wrapped as { "data": [...] }. Paginated lists return { "items": [...], "total": …, "limit": …, "offset": … }, the same fields REST splits into data and meta.pagination.

Every tool carries a title (the operation’s summary without its final period, for example List tasks for the organization) and MCP annotations so a client can decide what needs a confirmation:

Annotationtrue when
readOnlyHintThe operation only reads, and its REST form is a GET
destructiveHintThe operation isn’t read-only and isn’t purely additive. This includes refreshDeviceStatus, which is a query sent as POST. Every such operation is destructive except these creates: createTask, createCode, createTranslation, create_workflow, create_email_template, addPropertyScope, createUpsell, createPrecheckStep, createElementCatalogEntry, createPropertyElement, addTemplateGroup, addTemplateChecklistItem, addTaskRequirement, createInstruction, createDepartment, createTemplate, createPropertyGroup, createPortalManual, createPortalManualCategory
idempotentHintThe operation only reads, or is a delete. Repeating it has no further effect
openWorldHintThe operation can reach outside your organization’s internal data: a lock or thermostat, your PMS, a guest-facing page or email, or a person by message. Among GET reads, only searchStockPhotos (a public photo search). Among everything that isn’t read-only, including refreshDeviceStatus, all except createDepartment, updateDepartment, createTemplate, updateTemplate, addTemplateGroup, updateTemplateGroup, addTemplateChecklistItem, updateTemplateChecklistItem, applyTemplateToTask, addTaskRequirement, updateTaskRequirement and create_workflow

refreshDeviceStatus is the one query whose REST form is a POST, so it is marked idempotent but not read-only. Everything else (creates, updates, status changes, the other device commands) is non-idempotent. Those tools’ descriptions end with a retry note, because MCP has no idempotency key (see below).

A failed tool call comes back as a normal result with isError: true and a single text block. The text starts with the same error code the REST API uses, followed by the message:

NOT_FOUND: Task not found: 7d0e…
FORBIDDEN: Missing permission: modify_tasks
  • Validation errors start with VALIDATION_ERROR: and list the failing fields in parentheses as path: message, separated by semicolons, for example VALIDATION_ERROR: Action input validation failed (data.priority: Invalid option…). At most six fields are listed, followed by , and N more when there are others, and the list is cut at 400 characters.
  • Known business errors are rewritten into a sentence that says what to change, with the original machine key appended as (key: …).
  • Provider failures start with CONNECTOR_ERROR:. The provider’s own response is withheld.
  • Provider refusals start with PROVIDER_REJECTED:. The device’s provider answered and said no. Never retry these: repeating the call sends the real device another command. Check the device in SuiteOp and report what was refused. The device command tools (lockDevice, unlockDevice, setDeviceTemperature, setDeviceMode) say this in their descriptions.
  • Unexpected errors return Internal error with no detail.

Transport-level problems are not tool results. They are HTTP errors: 401 (missing or invalid credential), 403 (disallowed browser origin), 405 (wrong method), 400 (oversized batch) and 429 (rate limit).

The REST API’s Idempotency-Key has no MCP equivalent. If a tool call’s response is lost, the call may already have been applied, and retrying a create can make a duplicate. Before repeating a non-idempotent tool call, confirm the outcome (for example with the matching list… or get… tool). Read-only tools and deletes are safe to repeat.

MCP shares the rate limits of the credential: the same per-minute and per-second budget as REST calls made with that key or token. Each message in a JSON-RPC batch counts as one request. When the limit is hit, the server answers HTTP 429 with a Retry-After header, the usual X-RateLimit-* headers, and the body {"error":"API rate limit exceeded"}. The per-organization budget for AI-backed operations also applies, and surfaces as a tool error starting RATE_LIMITED:.