Altea MCP
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., "@Altea MCPwhat pickleball courts are open tomorrow at 3 pm?"
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.
Altea MCP
An unofficial Model Context Protocol server and CLI for the Altea Active booking app (myaltea.app). It lets an AI assistant such as Claude answer "which sessions does instructor X run on Monday?", "how many spots are left in the next Hot Yin?", "any pickleball courts open tomorrow at 3 pm?", list your bookings with their cancellation deadlines, and book, cancel or join a waitlist when you ask.
It talks to the app the way your browser does (same session, same requests), runs entirely on your Mac, and never stores your password. Not affiliated with Altea Active.
What you need
macOS with Google Chrome installed (the booking action must run inside a real Chrome page; see How it works).
Node.js 22 or newer.
An Altea Active membership.
Optional: Claude Code or Claude Desktop to use it as an MCP server.
Related MCP server: Zenoti MCP Server
Quick start
git clone https://github.com/noorabdalla04/altea-mcp.git && cd altea-mcp
npm install
node bin/altea.mjs login # opens Chrome once; sign in to myaltea.app; cookies are saved to ~/.altea
node bin/altea.mjs status # signedIn: true, your home club, session expiry
node bin/altea.mjs schedule tomorrowEverything lives in ~/.altea/ (Chrome profile, cookie jar, caches). Delete that folder to sign out completely.
Use it from Claude
Claude Code (one command, registers the server for every project and installs the skill):
bash scripts/install.sh --member "Your Name" # add --community "Altea Toronto" to override the detected home clubClaude Desktop: add to ~/Library/Application Support/Claude/claude_desktop_config.json and restart the app:
{ "mcpServers": { "altea": {
"command": "/opt/homebrew/bin/node",
"args": ["/absolute/path/to/altea-mcp/bin/mcp-server.mjs"],
"env": { "ALTEA_MEMBER_NAME": "Your Name" } } } }Any other MCP client: stdio transport, command node bin/mcp-server.mjs. Use an absolute path to node
(which node) because GUI apps don't inherit your shell PATH.
Times are shown in your club's local time zone (auto-detected from the app; ALTEA_TZ overrides).
Then ask naturally: "what's on at Altea tomorrow evening?", "next Hot Yin?", "book me into the 9 am Main Stage Ride", "cancel my Sunday class". The assistant will confirm before it books or cancels.
Membership rules the tool enforces
Rule | Behaviour | Override |
Cancellation window (default 8 h) |
|
|
Booking window (default 48 h) |
|
|
Waivers, paid options, cards | never signed, never bought implicitly, never added; | pass |
The app's own values (per membership option) take precedence over the defaults whenever it reports them. |
CLI
node bin/altea.mjs who x mon # everything instructor X teaches next Monday, all groups
node bin/altea.mjs next hot yin # next Hot Yin: spots, waitlist, bookable now?
node bin/altea.mjs schedule tomorrow --group courts --at 3pm --available
node bin/altea.mjs schedule "this week" --instructor timo
node bin/altea.mjs find "reformer level 2" --days 7
node bin/altea.mjs event evt_… # description, my booking, policy, options, waitlist
node bin/altea.mjs bookings # upcoming, with free-cancel deadlines
node bin/altea.mjs book evt_… | cancel evt_… [--force] | waitlist join|leave evt_…
node bin/altea.mjs meta | rules | actions --refresh | statusDates: YYYY-MM-DD | today | tomorrow | mon..sun | next mon | +N | "this week" | "next week" | "weekend".
Times: 15:00 | 3pm | 3:30pm. Groups: Boutique Fitness (default), Pickleball/courts, Aquatics/pool,
Recovery & Wellness/recovery, Personalized Performance/rx, Active Kids Club/kids, or all.
--json for machine output, --verbose for timings, --community "Altea Toronto" for another club.
MCP surface
Tool | Annotations | Purpose |
| read | session, home club, cookie expiry, rules, action ids |
| read | day/range listing with filters ( |
| read | words across all groups for N days |
| read | next occurrence + next with spots, waitlist, booking window |
| read | an instructor's sessions across all groups, "did you mean" |
| read | one session in depth: booking, window, options, conflicts, waivers |
| read | upcoming bookings with free-cancel deadlines (past days counted only) |
| additive, idempotent | book (guards: window, full, conflict, paid option, waiver) |
| destructive, idempotent | cancel (guard: late cancel, always checked) |
| additive, idempotent | join / leave |
| read | reference data, action-id refresh |
Results are concise text plus compact structuredContent; long runs of identical slots (recovery pods, courts)
collapse into one series line. format: "detailed" returns full objects with ids and descriptions; limit caps
per-day lists. Errors read ERROR[CODE]: … Retry safe: yes|no. Next: … (codes: NOT_SIGNED_IN, BAD_INPUT,
NOT_FOUND, WINDOW_NOT_OPEN, LATE_CANCEL, EVENT_FULL, CONFLICT, UNSIGNED_AGREEMENT, NO_MEMBERSHIP, PAID_OPTION,
UNKNOWN_ACTION, UPSTREAM, TIMEOUT). Resources: altea://rules, altea://meta, altea://bookings/upcoming,
altea://schedule/{date}. Prompts: altea-day-brief, altea-book-request. Design notes and the checklist the
server was validated against: docs/mcp-design.md; question → tool cookbook: docs/questions.md.
Configuration (environment variables)
Variable | Default | Meaning |
|
| how the assistant refers to you in tool text |
| auto-detected home club | club name or |
|
| group used for plain schedule questions |
| 480 / 2880 | rule fallbacks in minutes |
|
|
|
| the club's zone (auto-detected; Toronto fallback) | time zone for rendering and for the app's |
|
| where the profile, cookies and caches live |
| 45000 / 8 | read cache and parallel fetches |
| 30000 / 60000 / 60000 | request and Chrome launch budgets |
How it works
The app is a Next.js site with no public API: reads are React Server Component payloads (parsed by
src/rsc.mjs), writes are Next.js server actions whose ids change on every deploy (discovered from the JS bundles bysrc/discover.mjsand cached).Auth is the app's own HttpOnly session cookies, exported once from the Chrome profile after
login; reads and the cancel / waitlist-leave actions run from Node with that cookie jar.POST /booking/*is guarded by Vercel BotID (Kasada), so booking and waitlist-join run inside a real Chrome page of the persistent profile. Headless Chrome is refused for bookings, and an off-screen window is clamped back on-screen by macOS, so the defaulthiddenmode hides the Chrome process via System Events right after launch (a blank window can flash for about half a second) and falls back to a visible window if refused.Tools never return your address, phone or card details (only masked card labels in the event view). Test fixtures are scrubbed with
scripts/scrub.mjs; a test fails if personal data ever lands in them.
Measured (Altea Ottawa, 2026-09)
Call | Path | Time |
one day, one group (100–170 KB payload) | Node fetch | 1.2–1.7 s |
one day, all 6 groups | parallel | 1.9 s |
7 days, all groups | parallel | 7.6 s |
cancel / waitlist leave | Node | 0.7–0.9 s |
book / waitlist join | hidden Chrome | 5–8 s |
Development
npm test # unit + in-process MCP protocol tests (no network)
npm run eval # live: real questions through the server with assertions (needs a signed-in session)
npm run smoke # stdio smoke test; add --live for example calls
node scripts/make-fixture.mjs # refresh scrubbed fixtures from your own sessionCaveats
Session cookies expire (Firebase, roughly two weeks); tools then return
NOT_SIGNED_INand you runloginagain.A redeploy that changes payload shapes needs a parser update; changed action ids are handled automatically.
Two processes can't share the Chrome profile; a second one falls back to a temporary context seeded from the cookie jar.
Please be considerate: each day fetch is a full server render on Altea's side. Polling every minute for spots is not what this is for.
Use at your own risk: bookings and cancellations are real, and late cancellations cost money.
License
MIT. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Manage fitness coaching clients, workouts, programs, chats and funnels from your assistant.
Access approved FITsociety clients, schedules, bookings and coaching data with AI assistants.
- FitnitoOAuthcom.fitnito
Manage a gym or studio: schedule classes, members, passes, memberships, staff and bookings.
Manage an EasyWeek business from AI: bookings, availability, customers, services, orders, messaging.
Related MCP Servers
- AlicenseCqualityCmaintenanceProvides AI assistants with complete access to the Mindbody API for fitness and wellness studio management, including class scheduling, client management, bookings, payments, and staff operations across 50+ tools.3938 npm7MIT
- AlicenseCqualityCmaintenanceEnables AI agents to manage spa/wellness operations via Zenoti API, including appointments, guests, services, and billing.2372 npmMIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to interact with ActiveNet OpenAPI services for managing organizations, activities, customers, facilities, and more.1GPL 2.0
- AlicenseAqualityBmaintenanceEnables reserving, canceling, and listing fitness classes on AimHarder through natural language, with support for multiple boxes and waitlists.5MIT