Center booking connector
One public MCP server for personal AI assistants. An assistant uses it to help the person who asked, and books only once that person approves.
What it does
- Finds businesses published on Center.
- Answers questions from a business's public facts only.
- Checks open times.
- Books for the signed-in person, with their approval.
It is separate from the person's own Center errands server.
Endpoint and discovery
https://app.center.com/mcp
Streamable HTTP: one JSON-RPC message per POST, as application/json. GET and DELETE answer 405.
| What | Where |
|---|---|
| MCP endpoint | https://app.center.com/mcp |
| Protected-resource metadata (RFC 9728) | https://app.center.com/.well-known/oauth-protected-resource |
| Protected-resource metadata (RFC 9728) | https://app.center.com/.well-known/oauth-protected-resource/mcp |
| Authorization-server metadata (RFC 8414), issuer https://app.center.com | https://app.center.com/.well-known/oauth-authorization-server |
| Authorization endpoint (consent page, English and Spanish) | https://app.center.com/connect/authorize |
| Dynamic client registration (RFC 7591) | https://app.center.com/v1/platform/connector/oauth/register |
| Token | https://app.center.com/v1/platform/connector/oauth/token |
| Revocation (RFC 7009) | https://app.center.com/v1/platform/connector/oauth/revoke |
Authorization (OAuth 2.1)
- With no bearer token, POST /mcp answers 401 with a WWW-Authenticate challenge that names the protected-resource metadata and the scopes.
- The assistant reads the discovery documents and registers itself. Clients are public only (token_endpoint_auth_method: none). Redirects must be https, or http on loopback; they match exactly, and a loopback redirect may use any port.
- The person opens the consent page. The request must carry code_challenge with code_challenge_method=S256 (PKCE S256 is required; plain is refused), state and resource. When resource is present it must be https://app.center.com/mcp.
- The person signs in with a one-time code sent by text to their phone. Email sign-in is not available yet.
- The consent page names the assistant (as it registered itself), the scopes, and exactly what a business receives when the person books. The person chooses Allow or Don’t allow.
- A single-use code (10 minutes) returns with state and iss (RFC 9207). The token exchange checks the PKCE verifier and returns an access token (1 hour) and a refresh token (60 days). Refresh tokens rotate on every use; reusing an old one revokes the whole link. Tokens are bound to https://app.center.com/mcp.
Credentials are opaque. Center stores only their SHA-256 digests.
Scopes
| Scope | Tools |
|---|---|
businesses.read | search_businesses, get_business, find_available_slots, ask_business |
bookings.write | prepare_booking, confirm_booking, get_booking_status, cancel_booking, reschedule_booking |
Any signed-in person may use the read tools. A booking tool called without bookings.write gets HTTP 403 insufficient_scope and a step-up challenge.
Tools
The connector only sees published businesses. Each result is built field by field from the public record.
search_businesses
- Read or write
- Read (
businesses.read) - Returns
- Published, indexable, real businesses only. For each: id, handle, name, category, city, rating summary, price range, top services (price and duration), whether online booking is supported, and the site URL.
- Side effects
- None.
- Annotations
readOnlyHint: yes,destructiveHint: no,idempotentHint: yes,openWorldHint: no
| Field | Type | Meaning |
|---|---|---|
query | string | What the person is looking for. |
near | string (city) | {lat, lng} | A city, or a point. |
category (optional) | string | A kind of business. |
when (optional) | date or date-time | When the person wants to go. |
max_price (optional) | number | Highest price, in whole currency units. |
language (optional) | 'en' or 'es' | The language of the answer. |
get_business
- Read or write
- Read (
businesses.read) - Returns
- The public profile: services with price and duration, hours, address, phone, policies (deposit and cancellation), amenities, languages, showcased reviews (4★ and up), and photo URLs.
- Side effects
- None.
- Annotations
readOnlyHint: yes,destructiveHint: no,idempotentHint: yes,openWorldHint: no
| Field | Type | Meaning |
|---|---|---|
business_id | string | The business, from search_businesses. |
find_available_slots
- Read or write
- Read (
businesses.read) - Returns
- Open times, each with the current price, in the business's time zone. Never who holds any other time.
- Side effects
- None.
- Annotations
readOnlyHint: yes,destructiveHint: no,idempotentHint: no,openWorldHint: no
| Field | Type | Meaning |
|---|---|---|
business_id | string | The business. |
service_id | string | The service. |
from | date or date-time | Start of the window. |
to | date or date-time | End of the window. |
party (optional) | integer | How many people. |
ask_business
- Read or write
- Read (
businesses.read) - Returns
- An answer written only from the business's published facts, in the language of the question. It says when it does not know.
- Side effects
- None.
- Annotations
readOnlyHint: yes,destructiveHint: no,idempotentHint: no,openWorldHint: no
| Field | Type | Meaning |
|---|---|---|
business_id | string | The business. |
question | string | The question, in any language. |
prepare_booking
- Read or write
- Write (holds one time) (
bookings.write) - Returns
- A proposal: proposal_id, the exact service, the time in the business's time zone, the total, the policies, what the business will receive (name and phone), expires_at, and the words of the confirmation-text consent.
- Side effects
- A 5-minute hold on the time that nobody else can see. One person holds at most one time per business; a new proposal at the same business replaces the earlier hold.
- Annotations
readOnlyHint: no,destructiveHint: no,idempotentHint: no,openWorldHint: no
| Field | Type | Meaning |
|---|---|---|
business_id | string | The business. |
service_id | string | The service. |
start | date-time | The chosen time. |
notes (optional) | string | A note for the business. |
confirm_booking
- Read or write
- Write (
bookings.write) - Returns
- A stable booking_ref, the time and the status.
- Side effects
- Books through the business's own booking, which notifies the business as for any booking on its site. Repeating the call answers the same booking.
- Annotations
readOnlyHint: no,destructiveHint: no,idempotentHint: yes,openWorldHint: no
| Field | Type | Meaning |
|---|---|---|
proposal_id | string | From prepare_booking. |
texts_consent | boolean | Whether the person agreed to the confirmation texts, in the words prepare_booking returned. |
get_booking_status
- Read or write
- Read (own booking) (
bookings.write) - Returns
- Status, service, time, business, and whether it can still be changed.
- Side effects
- None.
- Annotations
readOnlyHint: yes,destructiveHint: no,idempotentHint: yes,openWorldHint: no
| Field | Type | Meaning |
|---|---|---|
booking_ref | string | From confirm_booking. |
cancel_booking
- Read or write
- Write (
bookings.write) - Returns
- The cancelled status.
- Side effects
- Cancels the booking, within the business's own cancellation cutoff.
- Annotations
readOnlyHint: no,destructiveHint: yes,idempotentHint: yes,openWorldHint: no
| Field | Type | Meaning |
|---|---|---|
booking_ref | string | From confirm_booking. |
reason (optional) | string | Why, for the business. |
reschedule_booking
- Read or write
- Write (
bookings.write) - Returns
- The moved time.
- Side effects
- Moves the booking. Same service, same staff.
- Annotations
readOnlyHint: no,destructiveHint: yes,idempotentHint: no,openWorldHint: no
| Field | Type | Meaning |
|---|---|---|
booking_ref | string | From confirm_booking. |
new_start | date-time | The new time. |
A price is stated only when the owner confirmed it or it comes from the business's own public page. Any other price reads "price not published", never $0. A business with nothing bookable online returns booking_supported: false with how to book (its booking or reservation link and its phone).
Deposits
The connector does not take payments, and money never moves through an assistant. When a service collects a deposit or the full price at booking, prepare_booking refuses with deposit_required. The refusal carries the deposit amount when it is published, plus the business's booking page and phone. Nothing is held, and nothing is booked without the deposit the business requires.
Errors
- No valid token: HTTP 401 with the RFC 9728 challenge (WWW-Authenticate: Bearer resource_metadata=…, scope=…).
- Missing scope: HTTP 403 insufficient_scope.
- Past a rate limit: HTTP 429 with Retry-After, and a JSON-RPC error whose data is {error: 'rate_limited', retry_after_seconds}.
- A tool refusal is a tool result with isError: true and structuredContent: {error: <reason>, message, …}.
Reasons:
invalid_argumentsbusiness_not_foundservice_not_foundservice_not_bookable_onlinebooking_not_supporteddeposit_requiredslot_unavailableproposal_not_foundproposal_expiredterms_changedbooking_not_foundcancel_window_passedalready_cancelledtest_account_real_businessbooking_unavailableask_unavailable
Rate limits
Per minute:
| Who | Reads | ask_business | prepare_booking | Each booking write |
|---|---|---|---|---|
| Per person | 60 | 10 | 10 | 5 |
| Per client | 3000 | 300 | 300 | 150 |
Each address may make up to 600 requests a minute to /mcp, because assistants call from shared addresses.
Privacy
- Facts come only from a business's public record. No tool reads a business's clients, bookings or conversations.
- Times are open times only: the connector never says who holds another time, never names another client, and never gives a staff member's private details.
- A person sees, cancels and moves only the bookings they made through the connector. Anyone else's booking reads booking_not_found.
When the person books, the business receives the name on their Center account and their verified phone. The consent page says so before they allow access.
Support and policies
Questions or problems: help@center.com
