Barrio
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@BarrioBook my hair with Lucía, like last time."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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 (
caresForpermissions). 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_dashboardshows 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 webOpen 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 --> DDBOn 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 |
| zod schemas for every tool input and output, error codes, demo users |
| MCP server + booking domain. Knows nothing about voice or Bedrock. |
| Simulated Alexa+: Bedrock Converse agent loop, real MCP client, HTTP + SSE API for the web |
| Browser simulator: user switcher, voice, MCP Apps card host, notifications, business dashboard, debug panel |
| 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 |
| Who the user is, who they can book for, saved preferences, recent bookings (memory), open waitlist offers ( | true | — | — |
| Find a business by name or category | true | — | — |
| Open slots; | true | — | — |
| Holds the slot for 5 minutes; returns a summary, the deposit and a | false | false | true |
| Same, to move an existing booking | false | false | true |
| Summary and whether the deposit is refunded; returns a | true | — | — |
| Runs the prepared action (book, move, cancel); charges or refunds; frees slots to the waitlist. MCP App receipt | false | true | true (per token) |
| My bookings and those of the people I care for; each has | true | — | — |
| Join the waitlist for a service, day and time window | false | false | true |
| Accept (books directly) or decline (passes to the next person) a waitlist offer | false | false | false (a retry finds the offer gone) |
| Simulated notification inbox | true | — | — |
| 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 withlist).ui://barrio/slots-card: the MCP App card, served astext/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/):McpServerfrom@modelcontextprotocol/sdk1.32 withStreamableHTTPServerTransportin stateless mode (one server + transport per POST, JSON responses), so the same code runs on Lambda. Protocol 2025-11-25 is negotiated ininitialize(checked inserver/test/integration/mcp.test.ts). Tools are registered with zod input/output schemas,annotations(readOnlyHint,destructiveHint,idempotentHint) andstructuredContent.search_slotsandconfirmare MCP Apps tools registered withregisterAppToolfrom@modelcontextprotocol/ext-appsand point toui://barrio/slots-cardvia_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 SDKClientwithStreamableHTTPClientTransportrunsinitialize,tools/list,tools/callandresources/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 ConversetoolConfig; tool results (structured content or structured errors) go back to the model astoolResultblocks (forsearch_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 notoolUseloses itstoolResult(assistant/src/history.ts). When a tool declares aui://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 withsandbox="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; seedocs/friction-log.md.Correctness: no double bookings (a booking writes one lock per 30 minutes in a single conditional transaction; DynamoDB
TransactWriteItemson AWS, the same conditions inInMemoryStore);confirmis 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 |
|
Confirm the outcome clearly | Two-step |
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 |
|
AWS Lambda |
|
|
Amazon DynamoDB | Single table ( |
|
Amazon EventBridge Scheduler | One one-time |
|
AWS CDK (TypeScript) | The whole stack, unit-tested with |
|
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
confirmwith one token (even concurrently) gives one booking and the same receipt.MCP integration: a real SDK client against the HTTP server:
initialize,tools/listwith 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_ENDPOINTis 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
docs/deploy.md: deploy to AWS with a budget capdocs/friction-log.md: friction logdocs/product-feedback.md: feedback on every tool used, plus feature requestsdocs/validation.md: conversations with local businessesdocs/video-script.md,docs/devpost-description.md: submission materials
License
MIT. Business associations and city councils can deploy and adapt Barrio for their own neighbourhoods.
This server cannot be deployed
Maintenance
Related MCP Connectors
Verified local businesses, bookable by AI agents: services, prices, availability and appointments.
Agentic CRM for service businesses — bookings, customers, WhatsApp, loyalty, invoicing.
Your AI agent phones businesses for you: books, reschedules, cancels, asks, and waits on hold.
The Mayor — AI front desk for local businesses: bookings, missed calls, and customer follow-up.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables AI assistants to find, book, and cancel salon appointments via SalonRunner accounts. Self-hosted for local or remote use.628 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables 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 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables voice-driven appointment booking for small businesses, letting customers check availability, book, reschedule, and cancel appointments through any MCP client.1 npmMIT
- AlicenseAqualityAmaintenanceEnables 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.3MIT