Make any booking system bookable by AI agents through one integration.
Restaurants, salons, clinics: implement one BookingProvider interface, and OpenBooking exposes
it to agents over MCP, UCP and A2A. It enforces the rules that make agent bookings
safe:
Postgres storage for OpenBooking: bookings and holds, idempotency, Studio activity and connector records.
2026-10-28
ดาว
—
เครื่องมือ
—
อัปไทม์
—
holds that expire;
idempotent retries that never double-book;
explicit user confirmation;
policy terms disclosed up front.
Protocol
Status
What you get
MCP (Model Context Protocol)
✅ Supported
5 agent-friendly tools over Streamable HTTP (2025 and 2026-07-28 eras) and stdio
UCP (Universal Commerce Protocol)
🟡 Draft
/.well-known/ucp profile plus dev.ucp.lodging.booking REST sessions, extended for time slots (spec notes)
A2A (Agent2Agent)
🟡 Beta
v1.0 Agent Card plus a JSON-RPC SendMessage endpoint (/a2a) for search, hold, confirm and cancel; no streaming or tasks
Start in one command
npx create-openbooking my-salon
cd my-salon && npm run dev
You get a booking backend with a booking page, a website snippet, Studio and an MCP endpoint.
Edit business.ts for staff, services and opening hours.
Requires Node 22.12+ and pnpm. The published packages run on Node 20+. Run corepack enable or npm i -g pnpm.
git clone <this repo> openbooking && cd openbooking
pnpm install
pnpm dev
This starts Studio Nord, a fictional hair salon (in-memory) with three stylists, three services (haircut, beard trim, color & cut with a deposit), opening hours and a cancellation policy. Run DEMO=restaurant pnpm dev for the restaurant demo instead.
Try it with an agent. Point any MCP client at http://localhost:3000/mcp. For example, run
npx @modelcontextprotocol/inspector. Then ask the agent to "book me a haircut with Maria on Friday
afternoon".
# 1. find a slot (EXTENSION: sh.openbooking.availability)
curl "localhost:3000/ucp/availability?date=2026-10-09&party_size=1&offering_id=haircut&time_from=15:00"
# 2. hold it (a UCP booking session); copy an offer id from step 1
curl -X POST localhost:3000/ucp/booking-sessions \
-H 'content-type: application/json' -H "Idempotency-Key: $(uuidgen)" \
-d '{"stays":[{"id":"<offer id>"}],"booker":{"first_name":"Ada","last_name":"Lovelace","email":"[email protected]"}}'
# 3. confirm, only with explicit user consent
curl -X POST localhost:3000/ucp/booking-sessions/<id>/complete \
-H 'content-type: application/json' -H "Idempotency-Key: $(uuidgen)" \
-d '{"user_confirmed":true}'
Keep bookings in Postgres
The demos are in-memory by default. Set DATABASE_URL and bookings, holds, idempotency records and Studio activity are stored in Postgres (tables are created on start):
DATABASE_URL=postgres://user:pass@localhost:5432/openbooking pnpm dev
On Vercel, add a Postgres database from the Marketplace (Neon, Supabase…) and use its pooled connection string. In code:
Holds are reserved under a per-resource lock, so two servers can never hand out the same time.
OpenBooking Studio
The booking system for the people running the business, at /studio:
Calendar: a day view with a column per staff member or table, and the day's bookings and takings at a glance.
New booking: staff book phone calls and walk-ins. It finds free times, takes customer details, and records a deposit collected at the desk.
Bookings: filter by status. Open a booking for guest details, deposit, cancellation terms and history. Cancel or release a hold.
Customers: everyone who booked, with visits, spend, next visit and the channel they first came through.
Services & staff: what customers and AI assistants can book.
Insights and agent log: bookings per channel (Claude, ChatGPT, Studio…) and every agent call with its result.
In local dev (baseUrl on localhost) the Studio opens without a login. Anywhere else, set a token: createOpenBookingApp({ ..., studio: { token: process.env.STUDIO_TOKEN } }). Without a token, the Studio API stays locked. Agent names are self-reported by clients, so use them for analytics only, never for access control.
Hosted: many businesses, one deployment
pnpm dev:hosted
Businesses sign up at /signup and set up services, staff, hours and cancellation rules in Studio.
Each gets a booking page at /b/<id>. Every listed business is bookable through one MCP app at
/mcp (find_business → booking tools), with optional Google Calendar sync and confirmation
emails. See docs/HOSTED.md.
The MCP tools
Tool
Does
get_business_info
Services (duration, price), staff customers can ask for by name, opening hours and how far ahead bookings are accepted.
search_availability
Free times on a date, with who is booked, who else is free, price, deposit and cancellation policy. Nothing is reserved.
hold_slot
Reserves a slot until expires_at. Returns booking_id and the terms to show the user.
confirm_booking
Confirms a hold. Requires user_confirmed: true, customer details, and a payment_token if a deposit is due.
get_booking
Current status and details.
reschedule_booking
Moves a confirmed booking to a new time for the same service, keeping its id and code. Needs user_confirmed; allowed while cancelling is still free.
cancel_booking
Releases a hold, or cancels a confirmed booking. A confirmed booking needs user_confirmed; the fee follows the policy.
Design rules:
Every mutating tool takes an idempotency_key.
Every error is { code, message, suggested_next_action, retryable }.
Every response includes next_step, the instruction for the agent.
New holds are limited per caller IP (default 20 per 10 minutes), so nobody can tie up the
calendar. Set serviceOptions.holdLimit to tune or disable it.
Field reference
search_availability
Field
Type
Notes
date
YYYY-MM-DD
In the venue's time zone
party_size
integer
Default 1 (an appointment); guests for a table
offering_id
string
Service id from get_business_info, e.g. haircut
staff
string
A staff member by name or id, any capitalisation ("Kari")
preferences
string[]
Other required features, e.g. ["outdoor"]
time_from, time_to
HH:MM
Venue local time, inclusive
limit
integer
Default 20, max 50
customer (on hold_slot or confirm_booking): first_name, last_name, and at least
one of email or phone_number (international format, e.g. +4791234567).
Slots and bookings:
Times (start, end) are ISO 8601 with the venue's offset, e.g. 2026-10-13T10:00:00+02:00.
expires_at is in UTC.
Prices are in minor units: { amount: 45000, currency: "NOK" } is 450.00 NOK.
resource says who or what is booked; also_available lists other staff free at that time.
Booking status is held, confirmed, cancelled or expired.
Connect Cal.com (beta)
Already on Cal.com or self-hosted Cal.diy? Your event types become bookable by AI agents with no code:
Business failures throw BookingError with a specific code.
Prove it with the conformance suite (vitest). It books slots until the provider says no, fires
parallel holds, lets holds expire and cancels, and fails if any rule is broken:
import { describeProviderConformance } from '@openbooking-sh/conformance/vitest';
describeProviderConformance('MySystemProvider', {
create: () => new MySystemProvider(testConfig), // a fresh, empty provider per test
now: new Date('2030-06-03T08:00:00Z'), // before the date you search
query: { date: '2030-06-04', party_size: { total: 1 } }, // must return open slots
});
Repository layout
packages/
core/ Domain model, BookingProvider, BookingService (agent-safety rules)
provider-memory/ Configured provider (catalog + slot rules) + demo salon and restaurant
postgres/ Postgres storage: bookings, idempotency, Studio activity, Cal.com records
create-openbooking/ npx create-openbooking: a ready-to-run booking backend
adapter-mcp/ MCP tools (Streamable HTTP + stdio)
adapter-ucp/ UCP discovery + booking sessions (draft)
adapter-usp/ USP profile, catalog, availability, holds and bookings (draft)
adapter-a2a/ A2A Agent Card + SendMessage endpoint
studio/ OpenBooking Studio dashboard (/studio), incl. business settings
booking-page/ Public booking page: pre-filled links, JSON-LD, WebMCP, manage links, OpenAPI
client/ Typed fetch client for the booking page API
notifications/ Confirmation/cancellation emails with .ics invites
google-calendar/ Google Calendar sync (busy times, bookings as events)
server/ One Hono app mounting everything
hosted/ Many businesses on one deployment: sign-up, settings, the OpenBooking MCP app
examples/demo/
bench/ Agent-success benchmark (tasks + runner)
docs/ARCHITECTURE.md, docs/SPEC-NOTES.md, docs/HOSTED.md
Development
pnpm dev # demo server with watch
pnpm test # vitest (unit + MCP end-to-end + bench smoke test)
pnpm typecheck
pnpm lint
pnpm build # tsup → dist/ per package
pnpm bench # run the agent benchmark (scripted baseline)
pnpm changeset # describe a change for release
The benchmark (bench/) runs booking tasks against isolated servers. It measures: