Keystone Developers
Open Keystone
MCP

MCP

Keystone MCP server

Nine tools: five the model calls to read a business and stage changes, four the Keystone card calls on a click to claim, open, apply, and undo.

The server

Endpoint https://mcp.keystone.app/mcp. Streamable HTTP, stateless, serving the 2026-07-28 protocol and 2025-era clients; any client that speaks MCP connects the same way (Connecting an MCP client). Clients authenticate with an OAuth access token issued by the Auth service; the server forwards every call to the Edits API with that token and its own service key, and holds no data of its own.

The model never changes anything directly. It proposes; the user reviews each change on a card that names the business and applies with a click the model cannot make. SOR enforces who may edit which business, compare-and-swap on every field, and keeps the change log the console shows under Change History.

Called by the modelRead-only

find_business Find a business

Find the business the user wants to work on, by name, website or domain. Shows a picker card: the user clicks the business, which opens an edit session, and its session id comes back to the model. The model cannot open a session or claim a business itself. A business can be edited while the user holds it (a claim, for 24 hours) and its site is not live; one nobody holds they can claim with a click on the card.

Input

FieldTypeDescription
queryrequiredstring (2–200)

A business name, website or domain.

Returns

Text listing each match as editable or the reason it is locked, and structuredContent.view = "picker" with the matches. _meta.cardTokens carries one token per click the card may make (open an editable business, claim an unclaimed one).

Edits API calls

Called by the modelRead-only

get_snapshot Business snapshot

Everything about the business in an edit session: the profile in full and every record (locations, services, service items, packages, team, FAQs, job postings, offers) with its id, label and version. Start here, before proposing changes. The user sees a card of what needs work. Hours live on the main location and are read separately so the card can show them.

Input

FieldTypeDescription
session_idrequireduuid

The edit session id the user opened (returned when they pick a business on the card).

Returns

The snapshot as JSON text, and structuredContent.view = "snapshot" with the business, summary rows for the card, and a console URL.

Edits API calls

Called by the modelRead-only

read_business Read a record

One record in full, with its write field names and version: the profile (entity "profile", record_id "profile") or any record get_snapshot lists.

Input

FieldTypeDescription
session_idrequireduuid

The edit session id the user opened (returned when they pick a business on the card).

entityrequiredstring

The entity, as get_snapshot names it.

record_idrequiredstring

The record id, or "profile" for the profile.

Returns

The record as JSON text.

Edits API calls

Called by the model

propose_changes Propose changes

Stage changes to the business for the user to review. Nothing is saved: they see each change on a card that names the business, untick any they do not want, and click Apply. Send every change in one call, up to 50. Include expected (the values read) or version, so anything changed since is caught. Changes that look like they came from another of the user's businesses start unticked. The call is idempotent on its content within a session.

Input

FieldTypeDescription
session_idrequireduuid

The edit session id the user opened (returned when they pick a business on the card).

summarystring (≤500)

One line saying what these changes do.

itemsrequiredChangeItem[] (1–50)

The changes; see ChangeItem below.

Returns

Text naming the staged changeset and each change, with a console link for applying without the card; structuredContent.view = "changeset"; _meta.cardTokens.apply for the card's Apply click. Changes SOR refuses are listed by position with a reason so the model can fix only those.

Edits API calls

Called by the model

discard_changeset Discard a draft

Drop a staged changeset the user no longer wants. Nothing was saved from it.

Input

FieldTypeDescription
changeset_idrequireduuid

The changeset id propose_changes returned.

Returns

Confirmation text.

Edits API calls

Called by the card

claim_business Claim this business

Called by the Keystone card when the user clicks Claim on the business picker. Claims an unclaimed, unlaunched business for the caller for 24 hours so nobody else can take it meanwhile.

Input

FieldTypeDescription
business_idrequireduuid

The business id, as find_business lists it.

card_tokenrequiredstring

The token the card was given for this exact action and target. Minted per click; the model never has one.

Returns

Text with the claim's expiry; structuredContent.view = "claimed"; a fresh open:<business_id> card token for the next click.

Edits API calls

Called by the card

open_session Work on this business

Called by the Keystone card when the user picks a business. Binds the chat to that business by opening an edit session; the model then uses the session id for get_snapshot and propose_changes.

Input

FieldTypeDescription
business_idrequireduuid

The business id, as find_business lists it.

card_tokenrequiredstring

The token the card was given for this exact action and target. Minted per click; the model never has one.

Returns

Text naming the business, the session id and its expiry; structuredContent.view = "session".

Edits API calls

Called by the cardDestructive

apply_changeset Apply changes

Called by the Keystone card when the user clicks Apply. Applies the ticked items (or exactly the ones given). Each applied change lands in the console's Change History.

Input

FieldTypeDescription
changeset_idrequireduuid

The changeset id propose_changes returned.

item_idsuuid[] (≤50)

Exactly these items; omitted, the ticked ones.

card_tokenrequiredstring

The token the card was given for this exact action and target. Minted per click; the model never has one.

Returns

Text with a tally (applied, skipped, refused) and each change; structuredContent.view = "changeset"; an undo card token.

Edits API calls

Called by the cardDestructive

undo_changeset Undo changes

Called by the Keystone card when the user clicks Undo. Reverts an applied changeset. A value someone changed again since the apply is kept, so an undo can undo nothing; the text says so.

Input

FieldTypeDescription
changeset_idrequireduuid

The changeset id propose_changes returned.

card_tokenrequiredstring

The token the card was given for this exact action and target. Minted per click; the model never has one.

Returns

Text with what was reverted and what was kept; structuredContent.view = "changeset".

Edits API calls

ChangeItem

One change inside propose_changes. What the model may send is exactly what the Edits API accepts; a change the contract no longer takes fails at the tool boundary with a reason.

FieldTypeDescription
entityrequiredstring

The entity, as get_snapshot names it: profile, location, service, service_item, package, team_member, faq, job_posting, offer.

oprequired"create" | "update" | "delete"

What to do.

record_idstring

The record to update or delete; omit for the profile and for creates.

fieldsobject

New values by write field name; a nested object may be sent in part.

expectedobject

The values you read for those fields, so a change made since is caught (compare-and-swap).

versionstring

The record version you read, from get_snapshot or read_business. An alternative to expected.