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: Epiphany MCP Server

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
businessYesThe business's slug or booking link, e.g. 'studio-ana' or 'https://vitta-nu.vercel.app/studio-ana'.
starts_atYesA starts_at value from find_available_slots, unchanged, e.g. 2026-10-02T09:00:00-03:00.
service_idYesA service_id returned by get_business.

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=false, openWorldHint=true, idempotentHint=false, and destructiveHint=false, so the write/non-idempotent profile is covered structurally. The description adds genuinely new behavior: the confirmation prerequisite and the alternate return of a booking link when no customer account is configured. It stops short of describing failure modes (e.g. a slot taken between lookup and booking).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four short sentences, each carrying a distinct rule (purpose, confirmation gate, parameter provenance, no-account fallback), with the operational constraint front-loaded. No filler and nothing repeated from the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a three-required-parameter mutation tool with no output schema, the description covers the critical agent-facing concerns: prerequisite conversation state, where starts_at comes from, and one known return variant (booking link). It omits what a successful booking returns or how a taken slot is surfaced, which is a modest remaining gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds provenance semantics the schema does not: starts_at must be copied verbatim from find_available_slots and the fields must be confirmed with the person first. business and service_id sourcing is left to the schema, which already documents their origins.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Starts with a specific verb+resource ('Book a service') and scopes it to the customer account configured on this server, which distinguishes it cleanly from siblings like find_available_slots and cancel_appointment. The opening sentence alone lets an agent identify the operation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit gating condition ('Only call this after the person has explicitly confirmed the business, service, day, time and price'), a dependency ordering rule (starts_at must be copied exactly from find_available_slots), and a fallback path when no customer account exists. This is when-to-use, when-not, and alternative behavior all in one.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
appointment_idYesAn appointment_id from list_my_appointments.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already flag destructiveHint=true and idempotentHint=true, so the safety profile is covered. The description adds genuinely useful context beyond them: the time slot is released to other people and the action is irreversible, which justifies the confirmation requirement.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences, front-loaded with the action, followed by the consequence and the input source. No filler, and every clause is actionable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter mutation with rich annotations and no output schema, the description covers the action, its irreversibility, the confirmation workflow, and where the ID comes from. Only a note on failure modes (invalid or already-cancelled appointment_id) is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single parameter is fully documented in the schema. The description only repeats the provenance of appointment_id (from list_my_appointments), adding no format or constraint detail beyond it, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (cancel) and resource (upcoming appointments) scoped to the configured customer. It is immediately distinguishable from siblings like book_appointment and list_my_appointments, and it names the sibling that supplies its input.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear preconditions: confirm with the person first, and obtain appointment_id from list_my_appointments. It does not state exclusions or edge cases (e.g. already-cancelled or past appointments), so it falls just short of the explicit when/when-not bar.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
daysNoHow many days to look at from from_date (1-14, default 7).
businessYesThe business's slug or booking link, e.g. 'studio-ana' or 'https://vitta-nu.vercel.app/studio-ana'.
from_dateNoFirst day to look at, YYYY-MM-DD in the business's calendar. Defaults to today.
service_idYesA service_id returned by get_business.

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover the safety profile (readOnlyHint, openWorldHint), so the bar is lower, and the description still adds genuine behavior an agent cannot infer: availability is volatile ('times change as other people book'), and returned values are authoritative and must not be normalized. It does not discuss rate limits or result volume.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences, none redundant. The core action is front-loaded, followed by the two operational constraints that actually change agent behavior (no time manipulation, re-fetch when stale).

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, and the description partially compensates by describing the shape of the result (start times grouped by day, in the business's time zone) and naming the starts_at field. For a small read-only lookup with full annotation coverage, that is enough to call the tool correctly; only result paging/volume is unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all four parameters are already documented, including the days range, from_date format and business slug. The description only indirectly reinforces semantics by mentioning grouping by day and the business time zone. Baseline 3 is appropriate when the schema carries the parameter detail.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb and resource ('List the start times a business would offer for one service on its own booking page') plus scope details (grouped by day, business time zone). It clearly describes what comes back, but it does not explicitly contrast itself with the other read siblings like list_my_appointments or get_business.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It clearly frames the tool as a pre-booking step and constrains downstream use: only these exact times may be booked, and starts_at must be passed to book_appointment unchanged. It also tells the agent to re-run when the user deliberates. It stops short of stating prerequisites explicitly (get_business for service_id is only implied via the schema).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
businessYesThe business's slug or booking link, e.g. 'studio-ana' or 'https://vitta-nu.vercel.app/studio-ana'.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover the safety profile (readOnlyHint=true, openWorldHint=true), and the description adds substantial value beyond that by enumerating the returned payload: services with service_id, duration, price in BRL, opening hours, address, contact, and booking availability. Without an output schema, this return-shape disclosure is exactly what an agent needs; only minor gaps remain (no mention of not-found/error behavior).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with the action and identifier, then the return contents, then the dependency hint. No filler; every clause carries information an agent can act on.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter read-only lookup with no output schema, the description supplies the identifier forms, the fields returned, and the ordering prerequisite. It is nearly complete; only failure modes (invalid slug, unknown business) are left unspecified, which is a minor gap for a low-risk read.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the parameter's own description already gives both accepted forms with examples. The description's 'booking link or slug' restates that, adding no syntax or validation detail beyond the schema, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('look up a business on Vitta') plus the two accepted identifier forms ('booking link or slug'). This is clearly a lookup operation, distinguishable from the appointment-oriented siblings (find_available_slots, book_appointment, cancel_appointment) without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly instructs 'Call this first: the other tools need a service_id from here,' establishing ordering and dependency relative to the sibling tools. An agent knows exactly when to reach for this tool versus the booking/cancel tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
include_pastNoAlso return past, canceled and missed appointments.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=true and openWorldHint=true, so safety and cross-server reach are already covered. The description adds valuable behavioral context beyond the annotations: the default filtering behavior (only upcoming pending/scheduled) and the ordering (soonest first), which an agent needs to predict output. It does not describe output shape, but no output schema exists so that's a minor gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three concise sentences, front-loaded with the core purpose, then default behavior, then usage guidance. No filler or repetition of the name/title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple one-optional-param read tool with annotations covering safety and openness, the description supplies scope, default filtering, ordering, and a concrete use case. It omits return field descriptions, but with no output schema that's a reasonable omission for this simplicity level.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, with include_past fully documented in the schema. The description's mention of 'only upcoming pending or scheduled by default' overlaps with the schema's default behavior but adds slightly more semantic detail about what 'default' means. Baseline 3 is appropriate since the schema already carries the parameter meaning.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States the specific resource (appointments of the configured customer account), scope (across every Vitta business), and ordering (soonest first). The default filter (upcoming pending/scheduled) and the cross-business scope clearly distinguish it from siblings like book_appointment or find_available_slots.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete usage context: answer 'when is my appointment?' and retrieve appointment_id for cancel_appointment. This is a clear when-to-use signal. It doesn't explicitly state when NOT to use it or name alternatives, so it falls short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

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

  1. 5 tool updatesv0.1.0
    • First observedbook_appointment
    • First observedcancel_appointment
    • First observedfind_available_slots
    • First observedget_business
    • First observedlist_my_appointments

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers