Skip to main content
Glama

OpenBook

Open-source appointment booking for small businesses (barbershops, salons, studios), built for voice. Customers book, reschedule and cancel by talking to Alexa+ or any other MCP client; the assistant talks to OpenBook through a Model Context Protocol server.

🧑 "Book a fade with Marcus at Fade Studio Saturday afternoon." 🔊 "Marcus has three times available Saturday afternoon: 12:30 PM, 1:15 PM, or 3 PM. Which one works for you?"

(a real reply from the simulated Alexa+: Claude on Amazon Bedrock calling list_services and search_availability on the OpenBook MCP server)

Built for the Amazon Developer Hackathon (Alexa+ track, AWS Builder and Open Source challenges).

Status

OpenBook is being built in milestones. This section is kept honest: only what is listed as done exists and is tested.

#

Milestone

Status

1

Foundation: monorepo, Postgres schema with exclusion constraints, seed data, CI

✅ Done

2

Core domain: availability, holds, bookings, reschedule, cancel, idempotency

✅ Done

3

MCP server (Streamable HTTP, 8 tools, dev auth)

✅ Done

4

Simulated Alexa+ (/assistant, Bedrock agent + MCP client)

✅ Done

5

Owner dashboard

✅ Done

6

Notifications + outbox worker

✅ Done

7

Cognito OAuth + AWS CDK deployment + connect to real Alexa+ (MCP Toolkit)

✅ Deployed²

8

Stretch: Bedrock Knowledge Base FAQ, Google Calendar

✅ Built¹

9

Polish: README, architecture, demo script, friction log, product feedback

✅ Done

10

Store finder: live "salons near me" search (OpenBook + real-world shops)

✅ Done

¹ Code and tests done; the Knowledge Base is created on AWS deploy, and Google Calendar needs a Google OAuth client, so neither has been exercised against the live service yet.

² Deployed to AWS (us-east-1) on 2026-09-24: API health, the MCP OAuth 401 challenge and metadata, migrations + seed, and the web app verified live. The Alexa+ registration still needs the project owner's Alexa developer account.

Architecture: docs/ARCHITECTURE.md. Every design decision: docs/DECISIONS.md.

What works today:

  • End to end by voice: docker compose up, open http://localhost:3000/assistant, hold the button and talk. The simulated Alexa+ (Claude Haiku 4.5 on Amazon Bedrock, Converse API with tool use) books, reschedules and cancels through the OpenBook MCP server, answers questions from the business's policies, and shows every MCP tool call with its arguments and result. Verified live: book → parking question → reschedule → list → cancel with the $10 late fee quoted first.

  • Owner dashboard at http://localhost:3000/dashboard (local "Dev login" as Fade Studio's owner): day/week calendar by staff member with cancel and no-show, services, staff with services and weekly hours, time off, policies and FAQ, settings with the MCP endpoint URL and a copy button. Backed by an owner REST API (/api/v1, same server as MCP) that authorises every request against business_owners. Changes reach the voice assistant immediately. A Playwright test covers "owner creates a service → it appears in list_services".

  • Store finder (live): choose "Any shop near me" on /assistant and ask "find me a barber near Williamsburg that can do a shave today". OpenBook shops come back with distance, open now and the next free slot (bookable right there), plus real-world shops from Amazon Location Service (name, distance, hours, phone; not bookable). Served by the directory MCP endpoint /mcp.

  • Notifications: every booking, reschedule and cancellation queues a message in the outbox; the worker sends a confirmation SMS and email (Amazon SES / AWS End User Messaging in AWS, logged locally), retrying with backoff and never sending a channel twice.

  • docker compose up starts Postgres 16, applies migrations, seeds two demo businesses, and runs the MCP server (:3001) and web app (:3000).

  • The database itself rejects double bookings, overlapping holds, bookings inside another appointment's buffer, and cross-tenant references (see Data integrity).

  • MCP server at http://localhost:3001/mcp/{businessSlug}: Streamable HTTP, stateless, JSON responses, built on the official MCP TypeScript SDK v2. It serves MCP 2026-07-28 and the 2025-era protocol (including 2025-11-25). All 8 tools are listed in docs/MCP_TOOLS.md. Tools return speakable text plus structured data, and stable error codes.

  • OAuth 2.1 resource-server behaviour: 401 + RFC 9728 challenge, JWT validation against a JWKS (issuer, expiry, audience/client id), per-business protected-resource metadata. Locally, DEV_AUTH signs in as the demo customer Jen. Real Cognito sign-in is milestone 7.

  • Verified with MCP Inspector 2.8.0 (tools/list, tools/call) against the Docker Compose stack.

  • packages/core holds every booking rule: availability (working hours, buffers, time off, notice, horizon, DST-safe), 5-minute holds, booking, rescheduling, cancellation with the late fee, idempotent writes, a transactional outbox, service/staff matching for spoken requests, and policy-based FAQ answers.

  • 165 tests, including 20 concurrent holds for one slot (exactly one wins), concurrent retries with one idempotency key (one booking), tenant isolation, a full book → reschedule → cancel flow driven by the SDK client over Streamable HTTP in both protocol eras, and the Bedrock ↔ MCP agent bridge against the real server (with a scripted model, so CI needs no AWS account).

Not yet: notifications are queued in the outbox but not sent (milestone 6), the owner dashboard (milestone 5), and AWS deployment with Cognito sign-in (milestone 7). The assistant signs in as the demo customer Jen with the development token.

Related MCP server: MCP Medical Appointments Demo

Quick start

Requirements: Docker. For the voice assistant, AWS credentials with Amazon Bedrock access to a Claude model (your ~/.aws profile is mounted read-only, or export AWS_PROFILE / AWS_ACCESS_KEY_ID etc.). For development also Node.js 24 and pnpm 10.

docker compose up

Then open http://localhost:3000/assistant (Chrome or Edge for speech recognition; any browser works with the text box), hold the button and say "Book a fade with Marcus Saturday afternoon."

This starts Postgres on localhost:54329, runs migrations, seeds demo data (only the first time; your bookings survive restarts), and starts the MCP server on localhost:3001 and the web app on localhost:3000. The default model is us.anthropic.claude-haiku-4-5-20251001-v1:0 in us-east-1; set BEDROCK_MODEL_ID / AWS_REGION to change it. Without Bedrock access the page still loads and says why it can't answer.

The MCP server works on its own too:

curl localhost:3001/healthz
npx @modelcontextprotocol/inspector --cli http://localhost:3001/mcp/fade-studio --transport http \
  --header "Authorization: Bearer dev-token-jen-0123456789" --method tools/list

To wipe and reseed:

SEED_RESET=true docker compose up

Demo data

Business

Slug

Time zone

Staff

Services

Fade Studio

fade-studio

America/New_York

Marcus, Dana

Fade (45 min, $35), Beard Trim (20 min, $15), Cut & Beard (60 min + 5 min buffer, $45)

Glow Salon

glow-salon

America/Los_Angeles

Priya

Blowout (45 min, $40), Haircut (60 min + 10 min buffer, $65)

Fade Studio is open Tuesday–Saturday, 9 AM–6 PM, has a 24-hour cancellation window with a $10 late fee, welcomes walk-ins, and has free street parking on Elm St. The upcoming Saturday is partly booked so availability looks realistic. Demo customer Jen (OAuth subject dev|jen) exists in both businesses as separate customer records.

Architecture

flowchart LR
  subgraph Browser
    A["/assistant<br/>push-to-talk (Web Speech API)<br/>speech synthesis · Tool calls panel"]
  end
  subgraph web["apps/web (Next.js)"]
    R["POST /api/assistant<br/>agent loop"]
  end
  subgraph AWS
    B["Amazon Bedrock<br/>Claude · Converse API + tool use"]
  end
  subgraph mcp["apps/mcp-server (Hono + MCP SDK v2)"]
    M["POST /mcp/{businessSlug}<br/>Streamable HTTP · stateless · JSON<br/>OAuth 2.1 resource server"]
    T["8 tools<br/>get_business_info · list_services · search_availability · hold_slot<br/>create_booking · find_my_bookings · reschedule_booking · cancel_booking"]
  end
  C["packages/core<br/>availability · holds · bookings<br/>idempotency · policies"]
  D[("PostgreSQL 16<br/>exclusion constraints<br/>outbox")]
  X["Any MCP client<br/>(Alexa+, MCP Inspector, desktop assistants)"]

  A -- "what the customer said" --> R
  R -- "Converse (messages + tool specs)" --> B
  B -- "toolUse" --> R
  R -- "MCP tools/list, tools/call<br/>Bearer token" --> M
  X -- "MCP over Streamable HTTP" --> M
  M --> T --> C --> D

Each spoken turn: the page sends the transcript to /api/assistant, which asks Bedrock what to do. The model's tool calls go to the MCP server exactly as any MCP client's would, and the reply is spoken back. The panel under each reply lists those MCP calls.

Deploy to AWS

infra/ is an AWS CDK app that deploys everything as one stack: API Gateway + Lambda (MCP server and owner API), a Lambda container for the web app, RDS PostgreSQL, Cognito, the outbox worker on an EventBridge schedule, a Bedrock Knowledge Base, and migrations that run on every deploy. Architecture and trade-offs: docs/DECISIONS.md (D39).

pnpm install
cd infra
npx cdk bootstrap                      # once per account/region
npx cdk deploy \
  -c ownerBootstrap=you@example.com:fade-studio \
  -c sesFromEmail=you@example.com       # optional: SES sender (verify the email when asked)
# add -c sesIdentityExists=true if that email is already a verified SES identity

Requirements: AWS credentials, Docker (to build the web image), Bedrock access to Claude Haiku 4.5 in the region (default us-east-1). Outputs: WebUrl (dashboard + /assistant), McpEndpoint, McpDirectoryEndpoint (register this one with the Alexa+ MCP Toolkit), CognitoDomain, AlexaClientId. Sign up on the web URL with the ownerBootstrap email to manage Fade Studio. npx cdk destroy removes it all (the database leaves a final snapshot).

What it costs (us-east-1, on-demand, from the AWS Price List API, September 2026)

Always-on baseline, before any traffic:

Resource

Price

Per month

RDS PostgreSQL db.t4g.micro, single-AZ

$0.016/hour

$11.68

RDS gp3 storage, 20 GB

$0.115/GB-month

$2.30

NAT instance t4g.micro

$0.0084/hour

$6.13

Public IPv4 address (NAT instance)

$0.005/hour

$3.65

Secrets Manager, 2 secrets

$0.40/secret

$0.80

Bedrock Knowledge Base index storage

$5.00/GB-month (policy documents are kilobytes)

~$0.00

CloudWatch Logs, ECR image, S3, SSM

small

< $1

Baseline

≈ $25–26

Per use:

What

Price

Example

Claude Haiku 4.5 on Bedrock (US cross-region)

$1.10 / M input tokens, $5.50 / M output; cache reads $0.11 / M

One spoken turn ≈ 2–3 model calls ≈ 5–14k input tokens → ≈ $0.01–0.02; a full booking conversation ≈ $0.05

Amazon Location Service

Geocode (Core) $0.50 / 1,000; Search Text with contact details (Advanced) $1.50 / 1,000

One "barber near Williamsburg" search ≈ $0.002

Knowledge Base retrieval

$1.00 / 1,000 Retrieve calls

per FAQ question ≈ $0.001

API Gateway HTTP API

$1.00 / million requests

negligible

Lambda (arm64)

$0.20 / million requests + $0.0000133 / GB-second

negligible at demo scale

Cognito Lite

first 10,000 monthly active users free, then $0.0055 / MAU

$0 for a demo

SES email

$0.16 / 1,000 emails (plan-dependent)

negligible

A hackathon month with a few hundred test conversations comes to roughly $25–35. Deleting the stack stops all charges except the final database snapshot (~$0.095/GB-month). The Knowledge Base prices are from the Bedrock pricing page; everything else is from the AWS Price List API.

Development

npm install -g pnpm@10      # or: corepack enable (needs admin rights on Windows)
pnpm install
cp .env.example .env
docker compose up -d        # Postgres + migrations + seed

pnpm test                   # unit + integration tests (starts its own Postgres via Testcontainers)
pnpm lint && pnpm typecheck && pnpm format:check
pnpm db:migrate             # apply migrations to DATABASE_URL
pnpm db:seed                # seed DATABASE_URL (add -- --reset to reseed)

Integration tests need Docker. To use an existing Postgres instead, set TEST_DATABASE_URL to an admin connection string (the user must be allowed to CREATE DATABASE).

Repository layout

apps/
  mcp-server/         # MCP server (+ owner REST API): Hono, MCP SDK v2, OAuth resource server
  web/                # Next.js: /assistant (simulated Alexa+) and /dashboard (owners)
  worker/             # Outbox delivery (email, SMS, Google Calendar) and maintenance sweeps
packages/
  db/                 # SQL migrations, migration runner, Kysely types, seed, test helpers
  core/               # Domain logic: availability, holds, bookings, policies, store finder
  notifications/      # SES / End User Messaging / Google Calendar clients, templates
  shared/             # Zod schemas and types shared across apps
infra/                # AWS CDK stack
docs/
  ARCHITECTURE.md     # Components, request flows, data integrity, deployment
  MCP_TOOLS.md        # Every tool: description, inputs, outputs, examples, errors
  DEMO_SCRIPT.md      # The 3-minute demo, line by line
  PRODUCT_FEEDBACK.md # Feedback on Bedrock and the other AWS services used
  DECISIONS.md        # Architecture decisions and deviations from the brief
  FRICTION_LOG.md     # Every tool/SDK/doc problem hit while building

Data integrity

All instants are stored as timestamptz in UTC. Each business has an IANA time zone, validated by a trigger, and working hours are local wall-clock times in that zone.

Guarantee

Enforced by

No two confirmed bookings for the same staff member overlap

EXCLUDE USING gist (staff_id WITH =, blocked_range WITH &&) WHERE (status = 'confirmed')

No two active holds for the same staff member overlap

Same, on holds, WHERE (status = 'active')

Buffers after an appointment are respected

Constraints run on blocked_range = appointment + buffer

A hold becomes at most one booking

UNIQUE (hold_id) on bookings

Idempotency keys are unique per business

UNIQUE (business_id, key)

No cross-tenant references

Composite foreign keys on (business_id, id)

Tested directly: 20 concurrent inserts for the same slot result in exactly one booking.

License

MIT

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    An MCP server that enables interaction with OnSched's consumer-facing appointment scheduling API through natural language, allowing users to manage bookings, appointments, and scheduling operations.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables users to manage medical appointments by searching for doctors, checking availability, and booking sessions through a natural language interface. It serves as a reference implementation for advanced MCP features like symptom-based specialist recommendations and multi-step scheduling workflows.
    6 npm
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Exposes the Appointment Scheduler API to AI agents as an MCP server, enabling them to list users and services, check availability, book, list, and cancel appointments via natural language.
    Apache 2.0