> For the complete documentation index, see [llms.txt](https://docs.shortcutco.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.shortcutco.com/developers/mcp/overview.md).

# MCP overview

What the Shortcut MCP server is, how a token shapes what an agent can do, and the protocol details for building your own client.

After this page you will know what the Shortcut MCP server gives an AI agent, how to choose the token that decides what the agent can do, and how the server behaves if you are building your own client.

## What it is

The [Model Context Protocol](https://modelcontextprotocol.io) is the open standard AI apps use to call tools. Shortcut runs a hosted MCP server, so Claude, Claude Code, Cursor, and agents you build yourself can look up and update customers, read the schedule, check invoices and payments, and work with leads, estimates, and jobs in a Shortcut workspace, with no code on your side.

```
https://api.shortcutco.com/mcp
```

Workspaces on Shortcut's dev environment connect to `https://dev.shortcutco.com/api/mcp` instead; see [Environments](/developers/concepts/conventions.md#environments).

The MCP server and the REST API are two doors to the same thing. Every tool runs the same operation as its REST endpoint, with the same token, scopes, validation, rate limits, and idempotency, and returns the same JSON. Anything this documentation says about the API holds for tools.

To connect an app, follow [Connect Claude](/developers/mcp/connect-claude.md) or [Connect ChatGPT](/developers/mcp/connect-chatgpt.md). Any MCP client that supports OAuth sign-in, or that can send a request header, connects the same way.

## The token decides everything

An agent can do exactly what its token's scopes allow, and nothing else:

* **`tools/list` shows only the tools the token can use.** A read-only token gives the agent a read-only toolset, so it never even sees a write tool.
* **A call to a tool the token cannot use is refused** with the scope it needs, which the agent can pass on to you.
* **Parts of an answer the token cannot read are left out**, and the answer says which scope would add them. `get_schedule` without `teams:read` shows crew ids instead of crew names.
* **`explain_scopes` tells the agent what it can and cannot do**, so it can say "ask an admin for a token with Write leads" instead of failing.

Start every new agent on a **Read everything** token. Once you trust what it does, give it a second token with the few write scopes it needs. Keep `visits:complete`, `payments:write`, `automations:trigger`, and the `send` scopes away from any agent that acts without a person approving each step: those create invoices, move money, or message your customers.

## Tools

### Workflow tools

| Tool                                                                      | What it does                                                                                                                                                                                                                 | Scope                                                                                      |
| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| [`explain_scopes`](/developers/mcp/tools.md#explain_scopes)               | Explain what this connection can and cannot do: the scopes its token holds, which tools are available, which tools or parts of answers need a scope it lacks, and how a company admin grants more.                           | No scope                                                                                   |
| [`find_customer`](/developers/mcp/tools.md#find_customer)                 | Find customers by any mix of name, company, email, phone, or street address.                                                                                                                                                 | `customers:read`                                                                           |
| [`get_customer_overview`](/developers/mcp/tools.md#get_customer_overview) | Everything about one customer in one call: contact details and properties, active jobs, the next scheduled and last completed visit, the open balance (owed and past due, matching get\_open\_invoices), and open estimates. | `customers:read`; parts need `jobs:read`, `visits:read`, `invoices:read`, `estimates:read` |
| [`get_open_invoices`](/developers/mcp/tools.md#get_open_invoices)         | List invoices still waiting for payment: unpaid (sent, not yet due) and overdue, newest first, optionally for one customer (customerId) or one status.                                                                       | `invoices:read`                                                                            |
| [`get_schedule`](/developers/mcp/tools.md#get_schedule)                   | Get the schedule for a date or range (YYYY-MM-DD in the workspace's timezone; defaults to today, and the answer says what today is).                                                                                         | `visits:read`; parts need `teams:read`, `customers:read`                                   |

### Crews

| Tool                                                | What it does                                                                                                                  | Scope        |
| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------ |
| [`get_team`](/developers/mcp/tools.md#get_team)     | Get one team (crew) by id, with its active members in crew order, whether it is the default crew, and archivedAt if archived. | `teams:read` |
| [`list_teams`](/developers/mcp/tools.md#list_teams) | List the workspace's teams (crews) with their active members, which crew is the default, and archived crews with archivedAt.  | `teams:read` |

### Custom fields

| Tool                                                                | What it does                                                                                                                        | Scope                |
| ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
| [`list_custom_fields`](/developers/mcp/tools.md#list_custom_fields) | List the workspace's custom customer fields with their ids, names, types (text, select, date, number, boolean), and select options. | `custom_fields:read` |

### Customers

| Tool                                                                                | What it does                                                                                                                                                                    | Scope             |
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- |
| [`add_property`](/developers/mcp/tools.md#add_property)                             | Add a property (service address) to the customer whose id you pass as id, with an optional label such as Home, coordinates, and lawn square footage.                            | `customers:write` |
| [`create_customer`](/developers/mcp/tools.md#create_customer)                       | Create a customer with a name, their first service address, and optionally email, phone, and company.                                                                           | `customers:write` |
| [`deactivate_customer`](/developers/mcp/tools.md#deactivate_customer)               | Deactivate (archive) the customer whose id you pass as id: they become inactive and every one of their pending or scheduled jobs is paused, so no further visits are scheduled. | `customers:write` |
| [`get_customer`](/developers/mcp/tools.md#get_customer)                             | Get one customer by id, with their contact details, tags, and properties (service addresses).                                                                                   | `customers:read`  |
| [`get_customer_custom_fields`](/developers/mcp/tools.md#get_customer_custom_fields) | Get the custom-field values of the customer whose id you pass as id.                                                                                                            | `customers:read`  |
| [`reactivate_customer`](/developers/mcp/tools.md#reactivate_customer)               | Reactivate the inactive customer whose id you pass as id.                                                                                                                       | `customers:write` |
| [`set_customer_custom_fields`](/developers/mcp/tools.md#set_customer_custom_fields) | Set custom-field values on the customer whose id you pass as id, by field id (from list\_custom\_fields).                                                                       | `customers:write` |
| [`set_customer_tags`](/developers/mcp/tools.md#set_customer_tags)                   | Replace the whole tag list of the customer whose id you pass as id with the tag ids you pass (from list\_tags).                                                                 | `customers:write` |
| [`update_customer`](/developers/mcp/tools.md#update_customer)                       | Change the first or last name, company name, email, or phone of the customer whose id you pass as id.                                                                           | `customers:write` |
| [`update_property`](/developers/mcp/tools.md#update_property)                       | Change a property's address (with latitude and longitude), label, or lawn square footage.                                                                                       | `customers:write` |

### Estimates

| Tool                                                            | What it does                                                                                                                                                                                     | Scope                              |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------- |
| [`approve_estimate`](/developers/mcp/tools.md#approve_estimate) | Approve an estimate by id on the customer's behalf.                                                                                                                                              | `estimates:write`                  |
| [`create_estimate`](/developers/mcp/tools.md#create_estimate)   | Create a draft estimate (a price offer) for a lead: pass leadId and lines, each with a catalog serviceId, cadence (recurring or one\_off), and optionally rate, quantity, options, and products. | `estimates:write`                  |
| [`get_estimate`](/developers/mcp/tools.md#get_estimate)         | Get one estimate by id with its status, total, and lines: each line's service, price, cadence, options, and products, and whether the customer chose or left it out.                             | `estimates:read`                   |
| [`list_estimates`](/developers/mcp/tools.md#list_estimates)     | List estimates (prices offered to leads and customers) newest first, with optional filters: status (draft, pending, approved, declined, voided, expired), customerId, or leadId.                 | `estimates:read`                   |
| [`remind_estimate`](/developers/mcp/tools.md#remind_estimate)   | Remind the customer about a pending estimate by id, by email and/or text (defaults to how it was last sent).                                                                                     | `estimates:send`, `estimates:read` |
| [`send_estimate`](/developers/mcp/tools.md#send_estimate)       | Send an estimate by id to the customer by email (default) and optionally text, as sending in the app does.                                                                                       | `estimates:send`, `estimates:read` |
| [`update_estimate`](/developers/mcp/tools.md#update_estimate)   | Change a draft estimate by id: its message, property, or lines (lines replaces all of them; each needs a catalog serviceId and cadence).                                                         | `estimates:write`                  |
| [`void_estimate`](/developers/mcp/tools.md#void_estimate)       | Void an estimate by id so the customer can no longer approve it.                                                                                                                                 | `estimates:write`                  |

### Invoices

| Tool                                                        | What it does                                                                                                                                                                                                                                             | Scope                            |
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
| [`get_invoice`](/developers/mcp/tools.md#get_invoice)       | Get one invoice by id: its number, kind, status, amounts in US dollars (subtotal, sales tax, processing fee, late fees, total), due date (YYYY-MM-DD, as printed on the invoice), and its lines as the customer sees them, each with the visit it bills. | `invoices:read`                  |
| [`list_invoices`](/developers/mcp/tools.md#list_invoices)   | List invoices with filters: status (draft, unpaid, overdue, paid, void), customerId, or dueBefore (YYYY-MM-DD; invoices whose dueDate is earlier).                                                                                                       | `invoices:read`                  |
| [`remind_invoice`](/developers/mcp/tools.md#remind_invoice) | Send the customer a payment reminder for an unpaid or overdue invoice by email and text (sendEmail, sendSms; both default to true), as Remind does in the app.                                                                                           | `invoices:send`, `invoices:read` |
| [`send_invoice`](/developers/mcp/tools.md#send_invoice)     | Send a draft invoice to its customer by email, as Send does in the app.                                                                                                                                                                                  | `invoices:send`, `invoices:read` |
| [`void_invoice`](/developers/mcp/tools.md#void_invoice)     | Void an unpaid or overdue invoice, with a reason that is recorded on the customer's notes.                                                                                                                                                               | `invoices:write`                 |

### Jobs

| Tool                                                          | What it does                                                                                                                                                                                                                  | Scope                      |
| ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
| [`cancel_job`](/developers/mcp/tools.md#cancel_job)           | Cancel a job and all of its open visits.                                                                                                                                                                                      | `jobs:write`               |
| [`create_jobs`](/developers/mcp/tools.md#create_jobs)         | Create jobs for a customer from catalog services: one job per service, each one\_off or recurring (with cadenceFrequency and cadencePeriod), priced by rate and quantity.                                                     | `jobs:write`               |
| [`get_job`](/developers/mcp/tools.md#get_job)                 | Get one job by id: its name, customer, property and address, status, cadence, price (rate, quantity, subtotal), crew (teamId, and effectiveTeamId for the crew doing the next visit), next visit date, and pause details.     | `jobs:read`                |
| [`list_job_visits`](/developers/mcp/tools.md#list_job_visits) | List one job's visits (its scheduled services), optionally filtered by status, with each visit's date, status, crew, and times.                                                                                               | `jobs:read`, `visits:read` |
| [`list_jobs`](/developers/mcp/tools.md#list_jobs)             | List jobs (services a customer buys, one-time or recurring) with filters: customerId, propertyId, status (pending, scheduled, paused, canceled, completed), or crew (teamId, the crew doing the job's next visit).            | `jobs:read`                |
| [`pause_job`](/developers/mcp/tools.md#pause_job)             | Pause a scheduled job, indefinitely or until pausedUntil (YYYY-MM-DD, the workspace's timezone), with an optional reason.                                                                                                     | `jobs:write`               |
| [`resume_job`](/developers/mcp/tools.md#resume_job)           | Resume a paused job today or on resumeDate (YYYY-MM-DD, the workspace's timezone): its open visits are replaced by a new series starting on that date.                                                                        | `jobs:write`               |
| [`schedule_job`](/developers/mcp/tools.md#schedule_job)       | Schedule a pending job: creates its visits starting on startDate (YYYY-MM-DD, the workspace's timezone, today or later), one visit for a one-time job or a series for a recurring one, on the job's crew or the teamId given. | `jobs:write`               |
| [`update_job`](/developers/mcp/tools.md#update_job)           | Change a job's name, description, or crew (teamId, recurring jobs only; null removes the crew).                                                                                                                               | `jobs:write`               |

### Leads

| Tool                                                    | What it does                                                                                                                                                                                  | Scope         |
| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| [`create_lead`](/developers/mcp/tools.md#create_lead)   | Create a lead (a request for service) with a name, email, service address, and optionally phone, company, services requested, and notes.                                                      | `leads:write` |
| [`dismiss_lead`](/developers/mcp/tools.md#dismiss_lead) | Dismiss a lead by id: set it aside so it no longer counts as open (status becomes dismissed).                                                                                                 | `leads:write` |
| [`get_lead`](/developers/mcp/tools.md#get_lead)         | Get one lead (a request for service) by id, with contact details, address, what they asked for, status, and stage.                                                                            | `leads:read`  |
| [`list_leads`](/developers/mcp/tools.md#list_leads)     | List leads (requests for service) newest first, with optional filters: status (pending or dismissed), lifecycleStage (lead, prospect, customer), and source (intake\_form, manual, csv, api). | `leads:read`  |
| [`restore_lead`](/developers/mcp/tools.md#restore_lead) | Restore a dismissed lead by id so it is open again (status becomes pending).                                                                                                                  | `leads:write` |
| [`update_lead`](/developers/mcp/tools.md#update_lead)   | Change a lead's name, company, email, phone, address, hours estimate, or square footage.                                                                                                      | `leads:write` |

### Payments

| Tool                                                                        | What it does                                                                                                                                                                                                                                                                    | Scope                             |
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| [`get_payment`](/developers/mcp/tools.md#get_payment)                       | Get one payment by id: the invoice and customer it belongs to, its amount in US dollars, how it was paid (online, cash, check, ach, other), any reference such as a check number, the amount refunded so far (succeeded refunds at the time of the call), and when it was paid. | `payments:read`                   |
| [`list_payments`](/developers/mcp/tools.md#list_payments)                   | List payments with filters: invoiceId or customerId.                                                                                                                                                                                                                            | `payments:read`                   |
| [`record_offline_payment`](/developers/mcp/tools.md#record_offline_payment) | Record a cash, check, or other offline payment that pays an invoice in full (amount must equal the invoice total), with an optional paidOn date (YYYY-MM-DD) and reference such as a check number.                                                                              | `payments:write`                  |
| [`reverse_payment`](/developers/mcp/tools.md#reverse_payment)               | Undo a manual (cash, check, or other offline) payment, with a reason that is recorded on the customer's notes.                                                                                                                                                                  | `payments:write`, `invoices:read` |

### Products

| Tool                                                        | What it does                                                                                                                                                             | Scope            |
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------- |
| [`create_product`](/developers/mcp/tools.md#create_product) | Add a product (a priced good such as mulch or stone) to the catalog with a name, unit label, and default unit price, and optionally a description, taxable, and display. | `products:write` |
| [`get_product`](/developers/mcp/tools.md#get_product)       | Get one product from the catalog by id, with its unit, default price, tax setting, and status.                                                                           | `products:read`  |
| [`list_products`](/developers/mcp/tools.md#list_products)   | List the workspace's products (priced goods such as mulch, stone, or plants that are added to estimates and visits) with unit and default price.                         | `products:read`  |
| [`update_product`](/developers/mcp/tools.md#update_product) | Change a product's name, unit label, default unit price, description, taxable, or display, or archive it (archived: true) or restore it (archived: false).               | `products:write` |

### Services

| Tool                                                                | What it does                                                                                                                                                                            | Scope            |
| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
| [`create_service`](/developers/mcp/tools.md#create_service)         | Add a standard or snow service to the catalog with a name, price structure (flat, per\_sqft, per\_hour, per\_man\_hour), and rate or pricing tiers; snow services also need snow terms. | `services:write` |
| [`deactivate_service`](/developers/mcp/tools.md#deactivate_service) | Deactivate a standard or snow service so it is no longer offered for new estimates and jobs.                                                                                            | `services:write` |
| [`get_service`](/developers/mcp/tools.md#get_service)               | Get one service from the catalog by id, with its price structure, rate or pricing tiers, status, and snow terms for snow services.                                                      | `services:read`  |
| [`list_services`](/developers/mcp/tools.md#list_services)           | List the workspace's service catalog (what it sells: mowing, aeration, snow plowing, chemical applications), with prices and status.                                                    | `services:read`  |
| [`reactivate_service`](/developers/mcp/tools.md#reactivate_service) | Reactivate a deactivated standard or snow service so it can be offered on new estimates and jobs again.                                                                                 | `services:write` |
| [`update_service`](/developers/mcp/tools.md#update_service)         | Change a standard or snow service's name, description, price structure, rate, pricing tiers, completion message, or snow terms.                                                         | `services:write` |

### Staff

| Tool                                                | What it does                                                                                        | Scope        |
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ------------ |
| [`get_user`](/developers/mcp/tools.md#get_user)     | Get one staff user (company admin or employee) by id, with name, email, role, status, and crew.     | `users:read` |
| [`list_users`](/developers/mcp/tools.md#list_users) | List the workspace's staff (company admins and employees) with name, email, role, status, and crew. | `users:read` |

### Tags

| Tool                                                | What it does                                                                    | Scope        |
| --------------------------------------------------- | ------------------------------------------------------------------------------- | ------------ |
| [`create_tag`](/developers/mcp/tools.md#create_tag) | Create a tag (a label for customers) with a name up to 60 characters.           | `tags:write` |
| [`delete_tag`](/developers/mcp/tools.md#delete_tag) | Permanently delete a tag and remove it from every customer and lead wearing it. | `tags:write` |
| [`list_tags`](/developers/mcp/tools.md#list_tags)   | List the workspace's tags (labels attached to customers), with their ids.       | `tags:read`  |
| [`rename_tag`](/developers/mcp/tools.md#rename_tag) | Rename a tag; every customer wearing it shows the new name.                     | `tags:write` |

### Visits

| Tool                                                            | What it does                                                                                                                                                                                                                                                                                                              | Scope                            |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
| [`assign_visit`](/developers/mcp/tools.md#assign_visit)         | Assign one scheduled visit to a crew (teamId).                                                                                                                                                                                                                                                                            | `visits:write`                   |
| [`cancel_visit`](/developers/mcp/tools.md#cancel_visit)         | Cancel one open visit (and every crew's portion of a split visit); the job and its other visits are unchanged.                                                                                                                                                                                                            | `visits:write`                   |
| [`complete_visit`](/developers/mcp/tools.md#complete_visit)     | Complete a visit as the app does.                                                                                                                                                                                                                                                                                         | `visits:complete`, `visits:read` |
| [`get_visit`](/developers/mcp/tools.md#get_visit)               | Get one visit by id: its date (YYYY-MM-DD, the workspace's timezone), status, job, customer, address, crew (effectiveTeamId), route position, and arrival and completion times.                                                                                                                                           | `visits:read`                    |
| [`list_visits`](/developers/mcp/tools.md#list_visits)           | List visits (scheduled services) with filters: from and to dates (YYYY-MM-DD, the workspace's timezone, inclusive), crew (teamId), status, customerId, or jobId.                                                                                                                                                          | `visits:read`                    |
| [`reschedule_visit`](/developers/mcp/tools.md#reschedule_visit) | Move a visit to a new date (YYYY-MM-DD, the workspace's timezone, today or later). scope this\_only moves just this visit; this\_and\_future deletes this and all later untouched visits of the job, creates a new series from the date, and sets the job to scheduled (clearing a pause), so confirm that with the user. | `visits:write`                   |
| [`skip_visit`](/developers/mcp/tools.md#skip_visit)             | Skip one open visit: it is closed like a cancel, and the job's later visits stay scheduled.                                                                                                                                                                                                                               | `visits:write`                   |

Each tool carries the standard annotations. `readOnlyHint` is `true` for the 30 read tools, and `idempotentHint` for reads only. `destructiveHint` is `true` for `cancel_job`, `cancel_visit`, `complete_visit`, `deactivate_customer`, `deactivate_service`, `delete_tag`, `reschedule_visit`, `resume_job`, `reverse_payment`, `set_customer_tags`, `skip_visit`, `update_lead`, `update_property`, `void_estimate`, and `void_invoice`, which can clear, replace, or withdraw data, so clients ask before running them. `openWorldHint` is `true` for `add_property`, `approve_estimate`, `cancel_visit`, `complete_visit`, `create_customer`, `create_estimate`, `create_lead`, `create_service`, `deactivate_customer`, `deactivate_service`, `delete_tag`, `dismiss_lead`, `reactivate_customer`, `reactivate_service`, `record_offline_payment`, `remind_estimate`, `remind_invoice`, `reschedule_visit`, `reverse_payment`, `schedule_job`, `send_estimate`, `send_invoice`, `set_customer_custom_fields`, `set_customer_tags`, `skip_visit`, `update_customer`, `update_service`, `void_estimate`, and `void_invoice`, whose effects can reach people or systems outside the workspace: messages to customers, automations, or QuickBooks sync. Without `automations:trigger`, records the API creates or changes start none of your **Automations** rules; see [automations:trigger](/developers/concepts/scopes.md#automationstrigger).

Every tool's arguments, scopes, and effects are listed in [MCP tools](/developers/mcp/tools.md).

## Answers

A tool result has the REST response body twice: as `structuredContent`, and as JSON text in `content` for clients that read text only.

* Lists hold at most 50 items. When there are more, the result says `meta.hasMore: true` and carries `meta.nextCursor`; the agent passes it back as `cursor` for the next page.
* Text fields longer than 4,000 characters are cut, ending in `…[truncated]`. Cursors are never cut.
* Dates are `YYYY-MM-DD` in the workspace's timezone, and `get_schedule` says what today is there, so an agent never guesses "today" from its own clock.
* Tool results are your business's data. The server tells agents to treat text inside them as data, never as instructions.

## Protocol details

You need this section only if you are writing your own MCP client or calling the server over plain HTTP.

| Item              | Value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Transport         | Streamable HTTP. Send every message as a `POST` with `Content-Type: application/json` and `Accept: application/json, text/event-stream`. Answers are single JSON responses. `GET` is answered `405`.                                                                                                                                                                                                                                                                                 |
| Sessions          | None. Each `POST` stands alone and is authenticated on its own, so there is no session id to keep and any request can go to any server.                                                                                                                                                                                                                                                                                                                                              |
| Protocol versions | `2025-11-25` and `2025-06-18`. `initialize` answers with your version when it is one of these, otherwise with `2025-11-25`.                                                                                                                                                                                                                                                                                                                                                          |
| Authentication    | `Authorization: Bearer scut_...` on every request: an API token, or an OAuth access token from signing in. OAuth access tokens last one hour and are bound to this server; refresh tokens rotate on every use. A used refresh token presented within 30 seconds of its rotation, as when a client refreshes twice at once, is refused and nothing else happens; presented later, it disconnects the app.                                                                             |
| Browsers          | Requests that carry an `Origin` header are accepted only from Claude's web apps. Server-side clients send no `Origin` and are always accepted.                                                                                                                                                                                                                                                                                                                                       |
| Batches           | One JSON-RPC message per request.                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| Discovery         | `/.well-known/oauth-protected-resource` on the MCP server's host (see [Environments](/developers/concepts/conventions.md#environments)) names the server, its scopes, that it takes bearer tokens in the header, and Shortcut's OAuth authorization server in `authorization_servers`. That server's own metadata is at `<issuer>/.well-known/oauth-authorization-server`, where `<issuer>` is the value the document names; MCP clients that sign in find all of this on their own. |
| Unauthenticated   | `401` with `WWW-Authenticate: Bearer resource_metadata="…", scope="…"`, where `resource_metadata` is that protected-resource document's address and `scope` lists every read scope (the "Read everything" preset). OAuth clients request exactly those when you sign in.                                                                                                                                                                                                             |

Because the server keeps no session, a single `tools/call` works on its own, which makes the MCP server easy to use from a script:

```bash
curl https://api.shortcutco.com/mcp \
  -X POST \
  -H "Authorization: Bearer $SHORTCUT_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2025-11-25" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": { "name": "find_customer", "arguments": { "query": "Alvarez Elm" } }
  }'
```

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"data\":[{\"id\":\"9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d\",\"object\":\"customer_match\",\"name\":\"Robin Alvarez\",\"companyName\":null,\"email\":\"robin.alvarez@example.com\",\"phone\":\"(406) 555-0142\",\"status\":\"active\",\"addresses\":[\"412 Elm St, Bozeman, MT 59715\"]}],\"meta\":{\"hasMore\":false}}"
      }
    ],
    "structuredContent": {
      "data": [
        {
          "id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
          "object": "customer_match",
          "name": "Robin Alvarez",
          "companyName": null,
          "email": "robin.alvarez@example.com",
          "phone": "(406) 555-0142",
          "status": "active",
          "addresses": ["412 Elm St, Bozeman, MT 59715"]
        }
      ],
      "meta": { "hasMore": false }
    }
  }
}
```

A failed tool call comes back as a result with `isError: true` and the standard [error body](/developers/concepts/errors.md#errors-on-the-mcp-server) in `structuredContent`. MCP tool calls count against the same [rate limits](/developers/concepts/idempotency-and-retries.md#rate-limits) as REST requests.

## Revoking an agent's access

Revoke its token in **Settings → API & integrations**, or, for an app connected by signing in, disconnect it under **Settings → API & integrations → Connected apps**. The agent's next request is refused, whichever app it runs in.

## Next

* [Connect Claude](/developers/mcp/connect-claude.md): Claude.ai, Cowork, Claude Desktop, and Claude Code.
* [Connect ChatGPT](/developers/mcp/connect-chatgpt.md): ChatGPT, by signing in.
* [Recipes](/developers/mcp/recipes.md): what to ask, and prompts that work well.
* [Daily schedule bot](/developers/guides/daily-schedule-bot.md): calling `get_schedule` from a script.
