Labsco
calcom logo

Cal.com MCP

from calcom

Connect AI clients to Cal.com scheduling through the Model Context Protocol using the hosted server at mcp.cal.com or a local instance.

πŸ”₯πŸ”₯πŸ”₯πŸ”₯βœ“ VerifiedAccount requiredAdvanced setup

@calcom/mcp-server

A Model Context Protocol (MCP) server that wraps the Cal.com Platform API v2. Connect it to Claude Desktop, Cursor, or any MCP-compatible client to manage bookings, event types, schedules, and more through natural language.

Features

  • 54 tools covering Bookings, Event Types, Schedules, Availability, Calendars, Conferencing, Booking Routing Trace, Routing Forms, Organizations, Teams, and User Profile (each with MCP tool annotations: title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint)
  • Dual transport β€” stdio for local dev tooling, StreamableHTTP for remote/production
  • Dual auth β€” API key for stdio (local dev), OAuth 2.1 Authorization Code + PKCE for HTTP (production)
  • Per-user token storage β€” encrypted at rest with AES-256-GCM in SQLite
  • Structured error handling with clean MCP error responses
  • Zod-validated inputs for every tool

Transport Modes

The server supports two transport modes selected via the MCP_TRANSPORT environment variable.

stdio (default) β€” Local Developer Tooling

Best for: Claude Desktop, Cursor, VS Code, and any MCP client that spawns the server as a subprocess.

# Start via stdio (default)
bun --filter @calcom/mcp-server start

# Or explicitly
MCP_TRANSPORT=stdio node apps/mcp-server/dist/index.js

The MCP client communicates over stdin/stdout. No HTTP server is started.

http β€” Remote / Production

Best for: Hosted deployments, shared services, remote MCP clients that connect over the network.

# Generate an encryption key
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

# Start HTTP server with OAuth 2.1
MCP_TRANSPORT=http \
  CAL_OAUTH_CLIENT_ID=your_client_id \
  CAL_OAUTH_CLIENT_SECRET=your_client_secret \
  TOKEN_ENCRYPTION_KEY=<64-hex-chars> \
  MCP_SERVER_URL=https://your-host.com \
  PORT=3100 \
  node apps/mcp-server/dist/index.js

This starts a StreamableHTTP server with OAuth 2.1 authentication:

MCP endpoints (require Authorization: Bearer <token>):

  • POST /mcp β€” JSON-RPC over Streamable HTTP (creates a new session on first request)
  • GET /mcp β€” SSE stream for server-initiated messages (requires mcp-session-id header)
  • DELETE /mcp β€” Terminate a session (requires mcp-session-id header)

OAuth 2.1 endpoints:

  • GET /.well-known/oauth-authorization-server β€” Authorization server metadata (RFC 8414)
  • GET /.well-known/oauth-protected-resource β€” Protected resource metadata (RFC 9728)
  • POST /oauth/register β€” Dynamic client registration (RFC 7591)
  • GET /oauth/authorize β€” Start authorization flow (redirects to Cal.com)
  • GET /oauth/callback β€” Cal.com OAuth callback (exchanges code for tokens)
  • POST /oauth/token β€” Token exchange (authorization_code or refresh_token grant)
  • POST /oauth/revoke β€” Token revocation (RFC 7009)

Other:

  • GET /health β€” Health check (returns { "status": "ok", "sessions": <count> })
  • GET /.well-known/openai-apps-challenge β€” OpenAI Apps domain verification token (plain text). Returns 404 unless OPENAI_APPS_CHALLENGE_TOKEN is set.

Each HTTP session gets its own McpServer instance with a unique session ID, so multiple clients can connect concurrently.

Connecting to MCP Clients

Claude Desktop (stdio)

Add this to your claude_desktop_config.json:

{
  "mcpServers": {
    "calcom": {
      "command": "node",
      "args": ["<path-to-repo>/apps/mcp-server/dist/index.js"],
      "env": {
        "CAL_API_KEY": "cal_live_xxxx"
      }
    }
  }
}

Cursor / VS Code (stdio)

Point your MCP client to apps/mcp-server/dist/index.js with the required environment variables.

Remote MCP Clients (http)

Start the server with MCP_TRANSPORT=http and point your client to http://<host>:3100/mcp.

Authentication

stdio mode β€” API Key

Every request to Cal.com includes:

  • Authorization: Bearer <CAL_API_KEY>
  • cal-api-version: 2024-08-13

HTTP mode β€” OAuth 2.1 Authorization Code + PKCE

The HTTP server implements a full OAuth 2.1 Authorization Server. MCP clients connect through a standard OAuth flow:

  1. Client registers β†’ POST /oauth/register with redirect_uris β†’ receives client_id
  2. Client starts auth β†’ GET /oauth/authorize with client_id, redirect_uri, code_challenge (S256), state
  3. Server redirects to Cal.com β†’ user authorizes on Cal.com
  4. Cal.com redirects back β†’ GET /oauth/callback with code + state
  5. Server exchanges code β†’ calls Cal.com token endpoint, stores encrypted Cal.com tokens in SQLite
  6. Server redirects to client β†’ with an authorization code + original state
  7. Client exchanges code β†’ POST /oauth/token with code, code_verifier, redirect_uri β†’ receives access_token + refresh_token
  8. Client uses token β†’ POST /mcp with Authorization: Bearer <access_token>

The server acts as an intermediary: it issues its own access tokens to MCP clients, and each token maps to encrypted Cal.com credentials stored in SQLite. When Cal.com tokens expire, the server auto-refreshes them transparently.

Security:

  • All Cal.com tokens are encrypted at rest with AES-256-GCM (via TOKEN_ENCRYPTION_KEY)
  • PKCE (S256) is required on both legs (clientβ†’server and serverβ†’Cal.com)
  • Auth codes are single-use
  • Expired tokens are cleaned up automatically every 5 minutes
  • In-process rate limiting on all OAuth endpoints (token bucket per IP, configurable via RATE_LIMIT_WINDOW_MS / RATE_LIMIT_MAX)
  • Redirect URIs registered via dynamic client registration are constrained: loopback (localhost / 127.0.0.0/8 / ::1) is always allowed, cleartext http to non-loopback hosts is always rejected, and non-loopback https hosts can be restricted to a vetted allowlist via ALLOWED_REDIRECT_HOSTS (recommended in production to limit the open-DCR phishing surface)

Tools (54)

Each tool exposes MCP tool annotations β€” a human-readable title plus behaviour hints (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) so MCP clients can render them appropriately and apply safety policies.

Hint presetreadOnlyHintdestructiveHintidempotentHintUsed for
Readtrueβ€”β€”All get_* / list endpoints
CreatefalsefalsefalseNew resources (create_*, add_*, calculate_routing_form_slots)
UpdatefalsefalsetrueMutations (update_*, reschedule_*, confirm_*, mark_*)
Destructivefalsetruetruedelete_*, cancel_booking

openWorldHint is true for every tool β€” they all call the external Cal.com API.

User Profile (2)

ToolTitleHintDescription
get_meGet My ProfileReadGet authenticated user profile
update_meUpdate My ProfileUpdateUpdate user profile

Event Types (6)

ToolTitleHintDescription
get_event_typesList Event TypesReadList all event types
get_event_typeGet Event TypeReadGet a specific event type by ID
get_scheduling_configGet Scheduling ConfigReadGet scheduling configuration for a team event type
create_event_typeCreate Event TypeCreateCreate a new event type
update_event_typeUpdate Event TypeUpdateUpdate an event type
delete_event_typeDelete Event TypeDestructiveDelete an event type

Bookings (10)

ToolTitleHintDescription
get_bookingsList BookingsReadList bookings with optional filters
get_bookingGet BookingReadGet a specific booking by UID
create_bookingCreate BookingCreateCreate a new booking
reschedule_bookingReschedule BookingUpdateReschedule a booking
cancel_bookingCancel BookingDestructiveCancel a booking
confirm_bookingConfirm BookingUpdateConfirm a pending booking
mark_booking_absentMark Booking AbsentUpdateMark a booking absence
get_booking_attendeesList Booking AttendeesReadGet all attendees for a booking
add_booking_attendeeAdd Booking AttendeeCreateAdd an attendee to a booking
get_booking_attendeeGet Booking AttendeeReadGet a specific attendee for a booking

Schedules (6)

ToolTitleHintDescription
get_schedulesList SchedulesReadList all schedules
get_scheduleGet ScheduleReadGet a specific schedule by ID
create_scheduleCreate ScheduleCreateCreate a new schedule
update_scheduleUpdate ScheduleUpdateUpdate a schedule
delete_scheduleDelete ScheduleDestructiveDelete a schedule
get_default_scheduleGet Default ScheduleReadGet default schedule

Availability / Slots (1)

ToolTitleHintDescription
get_availabilityGet AvailabilityReadGet available time slots (GET /v2/slots). Uses cal-api-version: 2024-09-04.

Calendars (2)

ToolTitleHintDescription
get_connected_calendarsList Connected CalendarsReadList calendar integrations connected to the user (returns credentialId + externalId for get_busy_times)
get_busy_timesGet Busy TimesReadGet busy/blocked time blocks from a connected calendar

Conferencing (1)

ToolTitleHintDescription
get_conferencing_appsList Conferencing AppsReadList conferencing applications

Booking Routing Trace (1)

ToolTitleHintDescription
get_booking_routing_traceGet Booking Routing TraceReadGet the step-by-step routing decision path for a booking (routing form evaluation, CRM lookups, host selection). Only for bookings that completed routing.

Routing Forms (1)

ToolTitleHintDescription
calculate_routing_form_slotsCalculate Routing Form SlotsCreateSubmit a routing form response and get available slots

Organizations: Attributes (7)

ToolTitleHintDescription
get_org_attributesList Org AttributesReadList attributes defined for an organization
get_org_attributeGet Org AttributeReadGet a single organization attribute by ID
get_attribute_optionsList Attribute OptionsReadList available options for a select attribute
get_user_attributesGet User AttributesReadGet attribute options assigned to a user
assign_attribute_to_userAssign Attribute to UserCreateAssign an attribute option or value to a user
update_user_attributeUpdate User Attribute AssignmentUpdateUpdate an existing user attribute assignment
unassign_attribute_from_userUnassign Attribute from UserDestructiveRemove an attribute option assignment from a user

Organizations: Bookings (2)

ToolTitleHintDescription
get_org_team_bookingsList Org Team BookingsReadList bookings for a team within an organization
get_org_user_bookingsList Org User BookingsReadList bookings for a specific user within an organization

Organizations: Memberships (5)

ToolTitleHintDescription
get_org_membershipsList Org MembershipsReadGet all organization memberships; supports take/skip pagination (take max 250)
create_org_membershipCreate Org MembershipCreateCreate an organization membership
get_org_membershipGet Org MembershipReadGet an organization membership
update_org_membershipUpdate Org MembershipUpdateUpdate an organization membership (role, accepted, impersonation)
delete_org_membershipDelete Org MembershipDestructiveDelete an organization membership

Organizations: Teams (2)

ToolTitleHintDescription
get_org_teamsList All Org TeamsReadList all teams in an organization; requires org admin access; supports take/skip pagination (take max 250)
get_my_teamsList My TeamsReadList teams the authenticated user belongs to; supports take/skip pagination (take max 250)

Organizations: Routing Forms (2)

ToolTitleHintDescription
get_org_routing_formsList Org Routing FormsReadGet organization routing forms
get_org_routing_form_responsesList Org Routing Form ResponsesReadGet routing form responses

Teams: Memberships (6)

ToolTitleHintDescription
get_team_membershipsList Team MembershipsReadGet all team memberships; supports take/skip pagination (take max 250) and email filtering
get_team_membershipGet Team MembershipReadGet a team membership
create_team_membershipCreate Team MembershipCreateCreate a team membership (role defaults to MEMBER)
update_team_membershipUpdate Team MembershipUpdateUpdate a team membership (accepted, role, impersonation)
delete_team_membershipDelete Team MembershipDestructiveDelete a team membership
create_team_inviteCreate Team InviteCreateGenerate a team invite link

API Version Notes

  • Default API version: 2024-08-13
  • /v2/slots endpoint uses: 2024-09-04 (automatically applied)