Skip to content
Scalekit Docs
Talk to an Engineer Dashboard

Connect AI agents to the Calendly MCP server

Vendor MCP
Open markdown

The Calendly MCP connector routes your AI agent's tool calls to Calendly's own MCP server through Scalekit. Each user signs in to Calendly once, and Scalekit stores and refreshes their tokens, so your agent never handles credentials. It comes with 36 tools.

Tools
36
What they doRead · write · destructive
25 · 8 · 325 read8 write3 destructive
Users sign in with
OAuth app
Your own Calendly app

What you can do

  • Book and cancel meetings: book a time slot for an invitee on an event type and cancel scheduled meetings
  • Review scheduled meetings: list upcoming and past meetings, view invitees, and mark or clear no-shows
  • Check availability: find bookable time slots, busy times, and availability schedules for users and event types
  • Manage event types: create and update event types, including their durations, settings, and availability rules
  • Share scheduling links: create single-use booking links, with optional custom duration, location, or availability
  • Manage your organization: list members, invite new users, revoke invitations, and read routing form submissions

Setup

  1. Install the SDK

    Terminal window
    npm install @scalekit-sdk/node dotenv
  2. Set your credentials

    Add your Scalekit credentials to your .env file. Find values in app.scalekit.com > Developers > API Credentials.

    .env
    SCALEKIT_ENVIRONMENT_URL=<your-environment-url>
    SCALEKIT_CLIENT_ID=<your-client-id>
    SCALEKIT_CLIENT_SECRET=<your-client-secret>
  3. Create the Calendly MCP connection

    In AgentKit > Connections, create a Calendly MCP connection and copy its redirect URI. The name you give it is the connection_name your code passes. See Configure connections.

  4. Register an OAuth app

    Calendly connections use your own OAuth app. Register one with Calendly and add the redirect URI you copied.

    Then enter the app's Client ID and Client Secret on the Calendly MCP connection.

    Console steps with screenshots

    Calendly uses OAuth 2.1 with Dynamic Client Registration (DCR) and PKCE. Calendly hosts its authorization server on a different domain (calendly.com) than its MCP server (mcp.calendly.com), so you register an OAuth client with Calendly yourself and save the resulting client ID in Scalekit. Complete this setup once per environment.

    1. Copy the redirect URI from Scalekit

      In the Scalekit dashboard, go to AgentKit > Connections > Create connection. Find Calendly and click Create. Copy the redirect URI — it looks like https://<SCALEKIT_ENVIRONMENT_URL>/sso/v1/oauth/<CONNECTION_ID>/callback. You pass this value as the redirect_uris entry in the next step, and it must match exactly.

    2. Register an OAuth client with Calendly

      Send a registration request to Calendly’s DCR endpoint. Replace <SCALEKIT_CONNECTION_CALLBACK_URL> with the redirect URI you copied.

      Terminal window
      curl -X POST https://calendly.com/oauth/register \
      -H "Content-Type: application/json" \
      -d '{
      "client_name": "Scalekit Calendly MCP Connector",
      "redirect_uris": ["<SCALEKIT_CONNECTION_CALLBACK_URL>"],
      "grant_types": ["authorization_code", "refresh_token"],
      "response_types": ["code"],
      "token_endpoint_auth_method": "none",
      "scope": "mcp:scheduling:read mcp:scheduling:write"
      }'

      Calendly responds with the registered client. Calendly issues a public PKCE client, so the response contains a client_id and no client secret.

      {
      "client_id": "90pDPl704dEMw2mTwRDvLsYOVBCXcWWiGb-44ehwdLU",
      "token_endpoint_auth_method": "none",
      "grant_types": ["authorization_code", "refresh_token"],
      "response_types": ["code"],
      "scopes": ["mcp:scheduling:read", "mcp:scheduling:write"]
      }
    3. Save the client ID in Scalekit

      Copy the client_id from the response. In the Scalekit dashboard, open AgentKit > Connections > Calendly, paste the value into the Client ID field of the connection’s OAuth configuration, and click Save. Leave the client secret empty, because Calendly issues a public PKCE client.

    4. Authorize the connection

      Generate an authorization link for a user and complete the consent flow. Calendly prompts the user to grant the mcp:scheduling:read and mcp:scheduling:write scopes. After consent, the connected account becomes active and Scalekit manages token refresh for every user who authorizes the connection.

  5. Authorize a user and make your first call

    quickstart.mts
    import { ScalekitClient } from '@scalekit-sdk/node'
    import 'dotenv/config'
    import { createInterface } from 'node:readline/promises'
    const scalekit = new ScalekitClient(
    process.env.SCALEKIT_ENVIRONMENT_URL,
    process.env.SCALEKIT_CLIENT_ID,
    process.env.SCALEKIT_CLIENT_SECRET,
    )
    const actions = scalekit.actions
    const connector = 'calendlymcp'
    const identifier = 'user_123'
    // Generate an authorization link for the user
    const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier })
    console.log('Authorize Calendly MCP:', link)
    const rl = createInterface({ input: process.stdin, output: process.stdout })
    await rl.question('Press Enter after authorizing...')
    rl.close()
    // Make your first call
    const result = await actions.executeTool({
    connector,
    identifier,
    toolName: 'calendlymcp_users_get_current_user',
    toolInput: {},
    })
    console.log(result)
    Terminal window
    npx tsx quickstart.mts

    Each user signs in once. See Authorize a user for the full flow and statuses.

Tools

Pass the exact name to execute_tool
Try in PlaygroundRequest a tool
  • calendlymcp_availability_get_user_availability_scheduleUse: Fetch details for one named availability schedule.Read-only

    GetUserAvailabilitySchedule

    Use: Fetch details for one named availability schedule. When: User asks about specific schedule rules or needs rules for a named schedule. Needs: Schedule URI from `availability-list_user_availability_schedules`. Do: Read `rules` array and `timezone` for display or to inform event-type updates. Avoid: Writing to this schedule—use event-type-level update for changes. Then: Report schedule details to user or use rules for availability queries.

    Inputs

    uristringrequired
    Availability schedule URI. not a browsable link — never show to users.
  • calendlymcp_availability_list_user_availability_schedulesUse: List all named availability schedules for the connected user.Read-only

    ListUserAvailabilitySchedules

    Use: List all named availability schedules for the connected user. When: User asks about availability schedules or you need to identify one by name. Needs: User URI from `users-get_current_user`. Do: Use returned `uri` to fetch schedule details or associate with an event type. Avoid: Confusing user availability schedules with event-type-level schedules. Then: Use schedule URI in `availability-get_user_availability_schedule` for details.

    Inputs

    userstringrequired
    A URI reference to a user
  • calendlymcp_availability_list_user_busy_timesUse: List a user's busy time blocks within a date range.Read-only

    ListUserBusyTimes

    Use: List a user's busy time blocks within a date range. When: User asks when they are busy or you need to avoid conflicts before suggesting slots. Needs: User URI from `users-get_current_user` and ISO 8601 start/end range. Do: Pass user URI and date range; use returned intervals to report conflicts. Avoid: Substituting for `event_types-list_event_type_available_times`—they differ. Then: Use busy intervals to inform scheduling recommendations.

    Inputs

    end_timestringrequired
    End time of the requested availability range. Date must be in the future of start_time.
    start_timestringrequired
    Start time of the requested availability range. Date cannot be in the past.
    userstringrequired
    The uri associated with the user
  • calendlymcp_event_types_get_event_typeUse: Fetch full details for one event type by URI.Read-only

    GetEventType

    Use: Fetch full details for one event type by URI. When: Before updating an event type or confirming its current configuration. Needs: Event type URI. Do: Use the returned fields as the baseline for any patch payload. Avoid: Constructing update payloads without reading current state first. Then: Proceed with `event_types-update_event_type` using the returned data as base.

    Inputs

    uristringrequired
    Event type URI. not a browsable link — never show to users.
  • calendlymcp_event_types_list_event_type_availability_scheduleUse: Read the current availability schedule (rules) for an event type.Read-only

    ListEventTypeAvailabilitySchedule

    Use: Read the current availability schedule (rules) for an event type. When: Before calling `event_types-update_event_type_availability_schedule`. Needs: Event type URI. Do: Retain the full `rules` array verbatim—it is the required base for updates. Avoid: Calling the update endpoint without first reading the current rules. Then: Pass the rules to `event_types-update_event_type_availability_schedule` with only the needed edits.

    Inputs

    event_typestringrequired
    The URI associated with the event type
  • calendlymcp_event_types_list_event_type_available_timesUse: List bookable time slots for an event type within a date range.Read-only

    ListEventTypeAvailableTimes

    Use: List bookable time slots for an event type within a date range. When: User wants slots, or to confirm availability before booking. Needs: Event type URI and start/end date range (ISO 8601). Do: Pass `start_time` verbatim to subsequent tool calls; do not rewrite UTC values. Avoid: Stale data, or trusting a prior local label—re-check UTC `start_time` if questioned. Then: Pass UTC `start_time` to `meetings-create_invitee`.

    Inputs

    end_timestringrequired
    End time of the requested availability range. Date must be in the future of start_time.
    event_typestringrequired
    The uri associated with the event type
    start_timestringrequired
    Start time of the requested availability range. Date cannot be in the past.
  • calendlymcp_event_types_list_event_typesUse: List all event types for the connected user or org.Read-only

    ListEventTypes

    Use: List all event types for the connected user or org. When: Start of any event-type task, or when selecting an event type by name. Needs: User URI from `users-get_current_user`. Do: Filter by current user URI unless user explicitly asks about a different host or org. Avoid: Assuming a cached list is current—call fresh each session. Then: Carry the selected `uri` into get, update, or availability tools.

    Inputs

    activestring
    Return only active event types if true, only inactive if false, or all event types if this parameter is omitted.
    admin_managedstring
    Return only admin managed event types if true, exclude admin managed event types if false, or include all event types if this parameter is omitted.
    countstring
    The number of rows to return
    organizationstring
    View available personal, team, and organization event types associated with the organization's URI.
    page_tokenstring
    The token to pass to get the next or previous portion of the collection
    sortstring
    Order results by the specified field and direction. Accepts comma-separated list of {field}:{direction} values.Supported fields are: name, position, created_at, updated_at. Sort direction is specified as: asc, desc.
    userstring
    View available personal, team, and organization event types associated with the user's URI.
    user_availability_schedulestring
    Used in conjunction with `user` parameter, returns a filtered list of Event Types that use the given primary availability schedule.
  • calendlymcp_list_calendly_skillsList or search available Calendly skill guides.Read-only

    ListCalendlySkills

    List or search available Calendly skill guides. Each skill gives domain-specific context, recommended tool usage, and best practices for working with Calendly MCP tools. Call this when the user's goal has no obvious single tool (e.g. reschedule or move a meeting). Load a matching skill before using related workflow tools. Use `keywords` to search by name or description (any keyword may match), and `include_header=true` to include each skill's description and related skills.

    Inputs

    include_headerboolean
    Include each skill's description and related skills in the results.default false
    keywordsstring
    Keywords to search skill names and descriptions by. Any keyword may match.

Workflows

Resolve the connected user first

Most Calendly tools need the connected host’s user URI. Call calendlymcp_users_get_current_user once at the start of a workflow, then reuse the returned resource.uri (and timezone) in later calls.

const me = await actions.executeTool({
connector: 'calendlymcp',
identifier: 'user_123',
toolName: 'calendlymcp_users_get_current_user',
toolInput: {},
});
const userUri = me.resource.uri;
console.log(userUri, me.resource.timezone);

List upcoming meetings and their invitees

Use calendlymcp_meetings_list_events to fetch scheduled meetings, then calendlymcp_meetings_list_event_invitees to see who is attending a specific meeting. Pass the user URI from the previous step and filter by status to limit results to active meetings.

// Step 1 — list active meetings for the connected user
const events = await actions.executeTool({
connector: 'calendlymcp',
identifier: 'user_123',
toolName: 'calendlymcp_meetings_list_events',
toolInput: {
user: userUri,
status: 'active',
count: '20',
},
});
const meetingUri = events.collection[0].uri;
// Step 2 — list the invitees for that meeting
const invitees = await actions.executeTool({
connector: 'calendlymcp',
identifier: 'user_123',
toolName: 'calendlymcp_meetings_list_event_invitees',
toolInput: { uri: meetingUri },
});
console.log(invitees);

Book a slot on an event type

Find a bookable slot with calendlymcp_event_types_list_event_type_available_times, then book it with calendlymcp_meetings_create_invitee. Pass the UTC start_time from the availability response verbatim — do not rewrite it to a local label.

// Step 1 — find available times for the event type
const slots = await actions.executeTool({
connector: 'calendlymcp',
identifier: 'user_123',
toolName: 'calendlymcp_event_types_list_event_type_available_times',
toolInput: {
event_type: 'https://api.calendly.com/event_types/EVENT_TYPE_UUID',
start_time: '2026-07-01T00:00:00Z',
end_time: '2026-07-07T00:00:00Z',
},
});
const startTime = slots.collection[0].start_time;
// Step 2 — book the slot for an invitee
const booking = await actions.executeTool({
connector: 'calendlymcp',
identifier: 'user_123',
toolName: 'calendlymcp_meetings_create_invitee',
toolInput: {
post_invitee_request: {
event_type: 'https://api.calendly.com/event_types/EVENT_TYPE_UUID',
start_time: startTime,
name: 'Jordan Lee',
email: 'jordan@example.com',
},
},
});
console.log(booking);

Use calendlymcp_scheduling_links_create_single_use_scheduling_link to generate a one-time booking link for an event type, then send the returned booking_url to the invitee. Use this when you want the link to follow the event type’s existing settings without overrides.

const link = await actions.executeTool({
connector: 'calendlymcp',
identifier: 'user_123',
toolName: 'calendlymcp_scheduling_links_create_single_use_scheduling_link',
toolInput: {
create_scheduling_link_request: {
owner: 'https://api.calendly.com/event_types/EVENT_TYPE_UUID',
owner_type: 'EventType',
max_event_count: 1,
},
},
});
console.log(link.resource.booking_url);

Cancel a meeting

Use calendlymcp_meetings_cancel_event with the meeting uri from calendlymcp_meetings_list_events. Canceling notifies every invitee, so confirm the action with the user first. To reschedule instead, surface the invitee’s reschedule_url rather than canceling.

await actions.executeTool({
connector: 'calendlymcp',
identifier: 'user_123',
toolName: 'calendlymcp_meetings_cancel_event',
toolInput: {
uri: meetingUri,
create_scheduled_event_cancellation_request: 'Host unavailable — will follow up to reschedule.',
},
});