Skip to main content
Glama

vitta-mcp

An MCP server that lets AI agents book appointments at the beauty and health businesses that run on Vitta: salons, barbershops, nail and brow studios and clinics in Brazil.

"Book me a manicure at Studio Ana on Friday afternoon."

The agent looks the business up, finds Friday's free times, reads them back, waits for a yes, and books. Or, if you haven't given it your Vitta login, it hands you the booking link with the time already chosen.

Vitta is a scheduling SaaS I built. Until now its only user was a person tapping through the booking page. This server makes agents a second kind of user, without giving them any power that person doesn't have.

Try it in a minute

With no configuration the server runs Vitta's demo: the same two sample businesses the app shows, studio-ana (nails) and barbearia-nove (barbershop), with their services, prices, opening hours and a week of appointments counted from today. You act as a demo customer, and bookings live in memory until the server stops.

git clone https://github.com/leonardobrigolini/vitta-mcp.git
cd vitta-mcp
npm install
npm run build
claude mcp add vitta -- node "$PWD/dist/index.js"

Then ask Claude something like "What can I book at studio-ana, and when is the first free time for a gel manicure?".

Related MCP server: salonrunner-mcp

Tools

Tool

What it does

Needs a customer login

get_business

Services (id, duration, price in BRL), opening hours, contact, booking rules

No

find_available_slots

Free start times for one service, grouped by day, in the business's time zone

No

book_appointment

Books one of those times

Yes (otherwise returns the booking link)

list_my_appointments

The customer's appointments across every Vitta business

Yes

cancel_appointment

Cancels one of them

Yes

The server also sends the agent short instructions on the expected flow: look up, find, confirm with the person, book.

Built for an agent, not a form

The booking page can lean on its UI. An agent can't, so the guarantees moved into the server:

  • Same answer as the booking page. The availability engine is the app's own (src/lib, copied with its tests). An agent can never be offered a time a person on the page wouldn't see. This matters more than it looks: the database function that books checks double booking and minimum notice, but not opening hours. On the page, the UI keeps people inside opening hours. For agents, book_appointment re-computes the free times and refuses anything that isn't on that list.

  • Errors say what to do next. "Someone else just booked 09:00. Call find_available_slots again and offer the person new times." Every failure names the tool to call or the link to hand over, so the agent can recover without a human reading a stack trace.

  • No time-zone math for the model. Every time goes out as ISO 8601 with the business's offset (2026-10-02T09:00:00-03:00), plus the date, weekday and wall-clock time. starts_at only accepts that format: Date.parse would happily read "amanhã às 9" as a date, and a timestamp without an offset would land in whatever zone the server runs in.

  • Honest annotations. Look-ups are readOnlyHint, cancelling is destructiveHint, and the booking and cancelling descriptions tell the agent to get an explicit yes first.

  • Read-only without a login. Connected to a real Vitta but without a customer login, the server can't write anything. It still does the useful part, finding the time, and returns the booking link to finish by hand.

Connect to a real Vitta

Point the server at a Vitta deployment with its Supabase URL and publishable key, the same pair its booking page ships in its JavaScript. Add the customer's own Vitta login (WhatsApp number + password) to let the agent book:

claude mcp add vitta \
  -e VITTA_SUPABASE_URL="https://<project>.supabase.co" \
  -e VITTA_SUPABASE_KEY="sb_publishable_..." \
  -e VITTA_CUSTOMER_WHATSAPP="(54) 99999-1234" \
  -e VITTA_CUSTOMER_PASSWORD="your-vitta-password" \
  -- node /absolute/path/to/vitta-mcp/dist/index.js

For Claude Desktop, Cursor and other clients, the same goes in the MCP config:

{
  "mcpServers": {
    "vitta": {
      "command": "node",
      "args": ["/absolute/path/to/vitta-mcp/dist/index.js"],
      "env": {
        "VITTA_SUPABASE_URL": "https://<project>.supabase.co",
        "VITTA_SUPABASE_KEY": "sb_publishable_...",
        "VITTA_CUSTOMER_WHATSAPP": "(54) 99999-1234",
        "VITTA_CUSTOMER_PASSWORD": "your-vitta-password"
      }
    }
  }
}

Variable

Default

VITTA_SUPABASE_URL

none (demo)

The Vitta deployment to talk to. Set both or neither.

VITTA_SUPABASE_KEY

none (demo)

Its publishable key.

VITTA_CUSTOMER_WHATSAPP

none

The customer's Vitta login. Set both or neither.

VITTA_CUSTOMER_PASSWORD

none

VITTA_APP_URL

https://vitta-nu.vercel.app

Base of the booking links it hands out.

VITTA_CUSTOMER_EMAIL_DOMAIN

clientes.vitta.app

Vitta signs customers in with an e-mail derived from their WhatsApp.

Security

  • Against a real Vitta it uses only the four database functions the public booking page already calls: public_org, book_appointment, my_bookings, cancel_booking. Row-level security answers per signed-in user, so the server can't see another customer's data even if an agent asks for it.

  • The customer login lives only in the person's local MCP config. It's used to sign in once per process and is never logged.

  • No secrets in this repository: the demo needs none, and a real deployment's publishable key is public by design.

Development

npm test        # 82 tests
npm run smoke   # the built server over stdio: demo by default, read-only against a real Vitta

The tests come in four layers:

  • The availability engine, with the app's own tests: lunch breaks, existing appointments, services that don't fit before closing, businesses that close at midnight, computing in the business's time zone instead of the machine's.

  • Every tool, in memory, against a fake API: the times it refuses to book, the messages it sends back, the read-only mode.

  • The demo, end to end: the seeded agenda blocks the right times, and a booking takes its slot until it's canceled.

  • The real process over stdio, with the real Supabase client talking HTTP to a fake Vitta that answers like the SQL functions. This layer covers what the in-memory tests can't: argument names on the wire, the sign-in, the JSON shapes coming back, the environment handling.

src/lib and the demo data are copied from the Vitta app. If the booking page's rules or the demo change, copy them again, so the two never disagree.


Built by Leonardo Brigolini · MIT license

Available Tools

5 tools
book_appointmentBook an appointmentA

Book a service for the customer account configured on this server. Only call this after the person has explicitly confirmed the business, service, day, time and price. starts_at must be copied exactly from find_available_slots. If no customer account is configured, this returns a booking link to hand to the person instead.

Inputs: business (required), starts_at (required), service_id (required).

cancel_appointmentCancel an appointmentA
DestructiveIdempotent

Cancel one of the configured customer's upcoming appointments. The time goes back to other people and this can't be undone, so confirm with the person first. Get appointment_id from list_my_appointments.

Inputs: appointment_id (required).

find_available_slotsFind free timesA
Read-only

List the start times a business would offer for one service on its own booking page, grouped by day, in the business's time zone. Only these exact times can be booked: don't invent, round or shift them, and pass starts_at to book_appointment unchanged. Times change as other people book, so look again if the person takes a while to decide.

Inputs: business (required), service_id (required), days, from_date.

get_businessLook up a businessA
Read-only

Look up a business on Vitta by its booking link or slug. Returns the services it offers (with service_id, duration and price in BRL), opening hours, address, contact and whether it's taking online bookings. Call this first: the other tools need a service_id from here.

Inputs: business (required).

list_my_appointmentsList my appointmentsA
Read-only

List the appointments of the customer account configured on this server, across every Vitta business, soonest first. By default only upcoming pending or scheduled ones. Use it to answer "when is my appointment?" and to get the appointment_id for cancel_appointment.

Inputs: include_past.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.4/5.0

    Scored across 5 tools

    Disambiguation5/5

    Each tool targets a distinct step of the booking lifecycle: business lookup, availability discovery, booking, listing the user's appointments, and cancellation. There is no overlap or risk of misselection, and descriptions explicitly state prerequisites (service_id, appointment_id, starts_at).

    Naming Consistency5/5

    All names follow a consistent verb_noun snake_case pattern (get_business, list_my_appointments, find_available_slots, book_appointment, cancel_appointment). Verbs are distinct and predictable throughout.

    Tool Count5/5

    Five tools map cleanly onto the minimal end-to-end booking flow, with each tool earning its place. Nothing feels padded or thin for a single-purpose booking server.

    Completeness4/5

    The core lifecycle (discover business → check availability → book → list → cancel) is fully covered with no dead ends. Minor gaps remain: no reschedule/update appointment and no business search by name or location, though agents can work around these.

    Related MCP Connectors

    Related MCP Servers