Skip to main content
Glama

Barrio

The booking add-on for the businesses on your street.

Barrio is one self-hosted, multi-business MCP server that gives small local businesses (hair salons, physios, podiatrists) a voice booking add-on for Alexa+. Big brands can bring their own MCP server to Alexa+. The salon on the corner never will. Barrio is the shared server that covers that long tail: one deployment, many businesses, strict per-business isolation.

Built for the Build, Ship, Shape Amazon Developer Hackathon (Alexa+ track). Alexa+ itself is only available to partners, so this repo also contains a simulated Alexa+: an assistant that uses Amazon Bedrock as its brain and talks to Barrio as a real MCP client over Streamable HTTP, plus a web simulator with voice, an Echo Show-style card and a live MCP debug panel.

The three businesses and five people in the seed data (Lucía's Hair Salon, Retiro Physio, Malasaña Podiatry; Carmen, Elena, Javier, Lucía, Pablo) are demo data, not real customers.

What it does

  • Voice booking with confirmation: find a business, search slots, book, move or cancel. Every write is two steps (prepare_* → confirm) and the assistant reads the summary back before acting.

  • Memory across sessions: "Book my hair with Lucía, like last time" reuses the saved service, staff, weekday and time. The assistant usually asks one question (which of two times) and then confirms.

  • Booking for someone you care for: Elena can book or move appointments for her mother Carmen (caresFor permissions). Both get notified.

  • Agentic waitlist: when a slot is freed, Barrio offers it to the first matching person on the waitlist and holds it for them (1 minute in the demo). If they decline or the offer expires, it moves on to the next person, with no human in the loop. Businesses see a recovered slots counter.

  • Simulated deposit (for example €5 to a saved card). It is refunded when a booking is cancelled more than 24 h ahead, or when the waitlist fills the cancelled slot. No real payments.

  • Business view: get_business_dashboard shows the owner today's bookings, cancellations and recovered slots.

  • MCP Apps card: a weekly slots card (ui://barrio/slots-card) that highlights the two suggested times and turns into a receipt after booking.

Related MCP server: Counterpart

Quick start (local)

Requirements: Node.js 22+, npm 10+. The assistant needs AWS credentials (default AWS SDK chain, for example aws configure) with access to a Bedrock model that supports tool use. The server, the tests and the web build do not need AWS.

npm install
cp .env.example .env
# edit .env: set BEDROCK_MODEL_ID (a Bedrock model or inference profile ID your account can use in AWS_REGION)

.env is read by the assistant only. The MCP server takes its settings (CLOCK, OFFER_TTL_MS) as shell variables, as in the commands below.

Bedrock costs money. Every assistant turn makes a few Bedrock calls, and they count against your AWS bill. For rehearsals pick a Haiku-class model (small, cheap, supports tool use) and keep real-Bedrock rehearsals few; the fake-LLM tests in npm test cover the flows for free. The assistant keeps each call small: search_slots reaches the model as the 2 suggested slots plus a count (the card gets every slot), and only the last 6 user turns are sent. See docs/deploy.md for the budget.

Then start the three processes in three terminals, from the repo root:

# 1. MCP server on http://localhost:3001/mcp (in-memory store, seeded with demo data)
#    CLOCK=fake enables the "+1 min" demo button (it moves the server clock to expire waitlist offers)
CLOCK=fake npm run dev -w server

# 2. Assistant API on http://localhost:3002 (Bedrock agent loop + real MCP client); reads ../.env
npm run dev -w assistant

# 3. Web simulator on http://localhost:5173 (Vite; proxies /api to the assistant)
npm run dev -w web

Open http://localhost:5173 in Chrome (the Web Speech API is needed for the microphone; the text box works everywhere). Pick a demo user and try:

  • as Carmen: "Book my hair with Lucía, like last time."

  • as Elena: "Move my mom's physio to Thursday."

  • as Javier: "Yes, I'll take it." after the waitlist offer appears.

  • as Pablo: the dashboard shows the recovered slot.

The server restarts with fresh seed data every time it starts (it is in memory). The server also works on its own with any MCP client: POST http://localhost:3001/mcp with Authorization: Bearer demo-carmen (tokens: demo-carmen, demo-elena, demo-javier, demo-lucia, demo-pablo).

Architecture

flowchart TB
  subgraph Browser["web/ — simulator (Vite)"]
    V["Voice in/out<br/>Web Speech API + text box"]
    C["MCP Apps host<br/>sandboxed iframe + postMessage"]
    D["Live MCP debug panel"]
  end

  subgraph Assistant["assistant/ — simulated Alexa+ (Node)"]
    L["Agent loop<br/>max 6 tool steps per turn"]
    MC["Real MCP client<br/>@modelcontextprotocol/sdk Client"]
  end

  B[("Amazon Bedrock<br/>Converse API + tool use")]

  subgraph Server["server/ — Barrio MCP server"]
    T["12 tools · resources · ui://barrio/slots-card"]
    DOM["Domain: slots, two-step booking,<br/>memory, permissions, waitlist"]
    ST{{"Store"}}
    SC{{"Scheduler"}}
  end

  Browser -- "HTTP + SSE (debug events)" --> Assistant
  L <--> B
  L --> MC
  MC -- "MCP 2025-11-25 · Streamable HTTP · Bearer token" --> T
  T --> DOM
  DOM --> ST
  DOM --> SC
  ST -- local / tests --> MEM["InMemoryStore"]
  ST -- AWS --> DDB[("DynamoDB<br/>single table")]
  SC -- local --> LS["LocalScheduler"]
  SC -- AWS --> EBS["EventBridge Scheduler<br/>one-time at() per offer"]
  EBS --> ADV["Lambda AdvanceFn<br/>offer → next person"]
  ADV --> DDB

On AWS, the Barrio server runs as a Lambda function behind a Function URL (server/src/lambda.ts). Locally it is the same Express app (server/src/local.ts).

Package

What it is

shared/

zod schemas for every tool input and output, error codes, demo users

server/

MCP server + booking domain. Knows nothing about voice or Bedrock. Store, Scheduler and Clock are interfaces with local and AWS implementations

assistant/

Simulated Alexa+: Bedrock Converse agent loop, real MCP client, HTTP + SSE API for the web

web/

Browser simulator: user switcher, voice, MCP Apps card host, notifications, business dashboard, debug panel

infra/

AWS CDK stack: DynamoDB table, two Lambda functions, Function URL, EventBridge Scheduler role

MCP tools

All tools have an inputSchema and an outputSchema generated from zod (structured output). Errors come back as isError: true with { code, message, alternatives } (codes: SLOT_TAKEN, CLOSED, TOKEN_EXPIRED, NOT_ALLOWED, NOT_FOUND), so the assistant can offer an alternative instead of a dead end.

Tool

What it does

readOnlyHint

destructiveHint

idempotentHint

get_my_context

Who the user is, who they can book for, saved preferences, recent bookings (memory), open waitlist offers (pendingOffers)

true

—

—

find_businesses

Find a business by name or category

true

—

—

search_slots

Open slots; suggested (top 2 by preference) + all for the card. MCP App: ui://barrio/slots-card

true

—

—

prepare_booking

Holds the slot for 5 minutes; returns a summary, the deposit and a confirmationToken. Repeating it re-takes your own hold with a new token (the old one stops working), so a retry never blocks you

false

false

true

prepare_reschedule

Same, to move an existing booking

false

false

true

prepare_cancellation

Summary and whether the deposit is refunded; returns a confirmationToken

true

—

—

confirm

Runs the prepared action (book, move, cancel); charges or refunds; frees slots to the waitlist. MCP App receipt

false

true

true (per token)

list_bookings

My bookings and those of the people I care for; each has past: true/false (past ones can't be changed)

true

—

—

join_waitlist

Join the waitlist for a service, day and time window

false

false

true

respond_to_offer

Accept (books directly) or decline (passes to the next person) a waitlist offer

false

false

false (a retry finds the offer gone)

get_notifications

Simulated notification inbox

true

—

—

get_business_dashboard

Business owners only: today's bookings, cancellations, recovered slots

true

—

—

"—" means the annotation is not set.

Resources

  • barrio://businesses/{id}/catalog: services (duration, price, deposit), staff and opening hours of one business (resource template with list).

  • ui://barrio/slots-card: the MCP App card, served as text/html;profile=mcp-app, self-contained (no external scripts).

How MCP is used in code

Barrio calls MCP in code on both sides; nothing is mocked at the protocol level in the demo.

  • Server (server/src/http/app.ts, server/src/mcp/): McpServer from @modelcontextprotocol/sdk 1.32 with StreamableHTTPServerTransport in stateless mode (one server + transport per POST, JSON responses), so the same code runs on Lambda. Protocol 2025-11-25 is negotiated in initialize (checked in server/test/integration/mcp.test.ts). Tools are registered with zod input/output schemas, annotations (readOnlyHint, destructiveHint, idempotentHint) and structuredContent. search_slots and confirm are MCP Apps tools registered with registerAppTool from @modelcontextprotocol/ext-apps and point to ui://barrio/slots-card via _meta.ui.resourceUri.

  • Identity: Authorization: Bearer <token> is resolved to a user on every request (server/src/mcp/auth.ts). This simulates account linking; real OAuth is on the roadmap and would not change the tools.

  • Client (assistant/src/mcpSession.ts): the SDK Client with StreamableHTTPClientTransport runs initialize, tools/list, tools/call and resources/read. Every request and response is streamed to the web debug panel over SSE.

  • Bedrock bridge (assistant/src/toolMapping.ts, assistant/src/agentLoop.ts): MCP tool definitions become Bedrock Converse toolConfig; tool results (structured content or structured errors) go back to the model as toolResult blocks (for search_slots, only the 2 suggestions plus a count, to save tokens). The history sent per call is bounded to the last 6 user turns, cut only at turn starts so no toolUse loses its toolResult (assistant/src/history.ts). When a tool declares a ui:// resource, the assistant reads it and the web renders the full result.

  • MCP Apps host (web/src/cardHost.ts): renders the card in an iframe with sandbox="allow-scripts" (no same-origin) and speaks the MCP Apps postMessage protocol (ui/initialize, ui/notifications/initialized, ui/notifications/tool-input, ui/notifications/tool-result, ui/notifications/size-changed). We wrote a minimal host instead of pulling the SDK into the browser; see docs/friction-log.md.

  • Correctness: no double bookings (a booking writes one lock per 30 minutes in a single conditional transaction; DynamoDB TransactWriteItems on AWS, the same conditions in InMemoryStore); confirm is idempotent per token; every business query is keyed by an explicit business id; waitlist offer transitions are conditional on a version number.

Design: Alexa+ add-on principles

Add-on design principle

How Barrio does it

Do at least one thing better because it lives in Alexa+

Booking by voice while your hands are busy, for someone who can't use an app. The waitlist works in the background: Alexa tells the next person a slot opened, and they say "Yes, I'll take it."

Resolve the intent inside the conversation

Search, hold, confirm and receipt all happen in one conversation; the waitlist offer is answered in the same place. No links, no apps.

Ask only for what is essential

get_my_context first; "like last time" fills service, staff and usual time from memory, so usually one question (which of two times) plus the confirmation.

Confirm the outcome clearly

Two-step prepare_* → confirm; the assistant reads the summary (service, person, time, deposit, card) and waits for a clear yes. After acting it gives a one-sentence summary and the card shows a receipt.

Be consistent with Alexa's voice

Short sentences, at most two options out loud, never a list read aloud (the card shows the full week), times said the way people speak, patient tone for older users.

Kind errors

Structured errors carry alternatives: a taken slot or a closed time comes back with the next two open times; if nothing suits, the assistant offers the waitlist.

The voice rules live in assistant/src/systemPrompt.ts and in the tool descriptions (for example search_slots tells the model to say only the two suggested times). Both are covered by tests.

AWS services

Service

How Barrio uses it

Code

Amazon Bedrock

Converse API with tool use: the "brain" of the simulated Alexa+. Model chosen with BEDROCK_MODEL_ID

assistant/src/bedrockLlm.ts

AWS Lambda

McpFn runs the MCP server (Express via serverless-http) behind a Function URL; AdvanceFn moves an expired waitlist offer to the next person

server/src/lambda.ts, server/src/advanceLambda.ts

Amazon DynamoDB

Single table (PK/SK, GSI1 for bookings by customer, TTL on expiresAt, on-demand). Conditional transactions prevent double bookings

server/src/store/dynamo.ts

Amazon EventBridge Scheduler

One one-time at() schedule per waitlist offer that deletes itself after it runs

server/src/scheduler/eventbridge.ts

AWS CDK (TypeScript)

The whole stack, unit-tested with aws-cdk-lib/assertions; Node.js 22 runtime, log groups with 1-week retention

infra/lib/barrio-stack.ts

Deployment steps, budget guards and teardown: docs/deploy.md.

Status: the stack synthesizes and is unit-tested. TODO(user): update this line after the first real deploy (date, region, what was verified).

Bonus: Barrio inside Claude

TODO(user): placeholder until recorded. Fill in after trying it.

The same server works as a remote MCP connector in Claude, because it is a standard Streamable HTTP MCP server:

claude mcp add --transport http barrio <URL> --header "Authorization: Bearer demo-carmen"

<URL> is the deployed McpUrl (see docs/deploy.md) or a tunnel to http://localhost:3001/mcp. Do not publish the URL: it is public and the demo tokens are in this repo.

TODO(user): describe what was tried (Claude Desktop or Claude Code), which scene, and link the clip.

Testing

npm test          # all workspaces (vitest)
  • Domain (in-memory store + fake clock): slot computation, opening hours, preference ranking, deposits and refunds, caregiver permissions, memory updates, waitlist matching, offer expiry and advance.

  • Concurrency and idempotency: two simultaneous holds on one slot, exactly one wins; repeating confirm with one token (even concurrently) gives one booking and the same receipt.

  • MCP integration: a real SDK client against the HTTP server: initialize, tools/list with annotations and output schemas, resources, and demo scenes 1 and 2 end to end (server/test/integration/).

  • Assistant: scripted turns with a fake LLM check tool-call order, the 6-step limit, error results, card events, the Bedrock mapping and the voice rules in the system prompt.

  • Web: MCP Apps host and card handshake (jsdom), voice, session switching.

  • AWS: DynamoDB store unit tests (mocked SDK), Lambda adapter test with a Function URL event, CDK template assertions. A shared store contract runs against DynamoDB Local when DYNAMO_ENDPOINT is set; it is skipped by default because it needs Docker or Java (not yet run, see the friction log).

Roadmap

  • Onboarding from a photo: photograph the price list or the Google Maps listing; Bedrock extracts services, durations and prices and proposes opening hours.

  • Google Calendar adapter, so a business keeps its existing calendar.

  • Real OAuth account linking (MCP authorization) in place of the demo bearer tokens.

  • Business voice: "Close my diary on Friday afternoon", plus a fuller business panel.

  • Later: real payments, multiple languages.

Docs

License

MIT. Business associations and city councils can deploy and adapt Barrio for their own neighbourhoods.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables Alexa+ to run a small business by voice, with tools to open and move orders, add parts or labor, check and reorder stock, take payment, and get daily summaries, all generated from per-business YAML profiles.
    21,645 npm
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables voice-driven appointment booking for small businesses, letting customers check availability, book, reschedule, and cancel appointments through any MCP client.
    1 npm
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Enables AI assistants to answer a small business's customer questions from its own FAQs and order data, list upcoming open appointment times in the business's time zone, and book one of those offered slots with staff notification. Free for the first 1,000 businesses, using fast rule-based FAQ matching rather than free-form generation.
    3
    MIT