|
7 | 7 | cancelBooking, |
8 | 8 | confirmBooking, |
9 | 9 | createBooking, |
| 10 | + createBookingPublic, |
10 | 11 | createEventType, |
11 | 12 | createSchedule, |
12 | 13 | declineBooking, |
@@ -122,13 +123,13 @@ To book, you need these 4 pieces: |
122 | 123 | 4. ${bold}Slot is available${bold} — call check_availability ONCE |
123 | 124 |
|
124 | 125 | ${bold}Option B — THEIR calendar (they host):${bold} |
125 | | -The other person is the host. The requesting user is the attendee. |
| 126 | +The other person is the host. The requesting user (you) is the attendee. |
126 | 127 | 1. Ask for the other person's Cal.com username if not provided. |
127 | 128 | 2. Call \`list_event_types_by_username\` with their username. |
128 | 129 | 3. Show their event types and let the user pick. Note the \`slug\` from the result. |
129 | 130 | 4. Call \`check_availability_public\` with the event type \`slug\` and \`username\`. Do NOT use \`check_availability\` — that requires the host's auth token which you don't have. |
130 | 131 | 5. Present available slots and let the user pick. |
131 | | -6. Call book_meeting with their event type ID. Use the requesting user's name + email (from Your Account above) as attendeeName/attendeeEmail. |
| 132 | +6. Call \`book_meeting_public\` (NOT book_meeting) with the event type slug + username. For attendeeName and attendeeEmail, use the ${bold}requesting user's${bold} name and email from "Your Account" above — the requesting user is the attendee in this flow. NEVER use the bot name "Cal.com" as attendeeName. |
132 | 133 |
|
133 | 134 | EVENT TYPE SELECTION: |
134 | 135 | - If there is only 1 non-hidden event type, auto-select it. Tell the user which one you're using. |
@@ -621,6 +622,85 @@ function createCalTools(teamId: string, userId: string, lookupPlatformUser?: Loo |
621 | 622 | }, |
622 | 623 | }), |
623 | 624 |
|
| 625 | + book_meeting_public: tool({ |
| 626 | + description: |
| 627 | + "Book a meeting on ANOTHER user's Cal.com calendar using their public event type. Use this for Option B (they host). Does NOT require the host's auth token. Pass eventTypeSlug + username instead of eventTypeId. The attendee is the requesting user (you) — use YOUR name and email.", |
| 628 | + inputSchema: z.object({ |
| 629 | + eventTypeSlug: z |
| 630 | + .string() |
| 631 | + .describe("The event type slug from list_event_types_by_username (e.g. 'meet')"), |
| 632 | + username: z |
| 633 | + .string() |
| 634 | + .describe("The Cal.com username of the host (e.g. 'peer')"), |
| 635 | + startTime: z |
| 636 | + .string() |
| 637 | + .describe("Start time in ISO 8601 UTC format (e.g. '2026-03-23T10:30:00Z')"), |
| 638 | + attendeeName: z |
| 639 | + .string() |
| 640 | + .describe("YOUR full name (the requesting user, not the host)"), |
| 641 | + attendeeEmail: z |
| 642 | + .string() |
| 643 | + .describe("YOUR email address (the requesting user, not the host)"), |
| 644 | + attendeeTimeZone: z |
| 645 | + .string() |
| 646 | + .nullable() |
| 647 | + .optional() |
| 648 | + .describe("Your timezone (e.g. 'Asia/Kolkata'). Defaults to your linked timezone."), |
| 649 | + guests: z |
| 650 | + .array(z.string()) |
| 651 | + .nullable() |
| 652 | + .optional() |
| 653 | + .describe("Optional additional guest emails"), |
| 654 | + notes: z.string().nullable().optional().describe("Optional notes for the booking"), |
| 655 | + lengthInMinutes: z |
| 656 | + .number() |
| 657 | + .nullable() |
| 658 | + .optional() |
| 659 | + .describe("Duration in minutes. Only needed if the event type supports multiple durations."), |
| 660 | + }), |
| 661 | + execute: async ({ |
| 662 | + eventTypeSlug, |
| 663 | + username, |
| 664 | + startTime, |
| 665 | + attendeeName, |
| 666 | + attendeeEmail, |
| 667 | + attendeeTimeZone, |
| 668 | + guests, |
| 669 | + notes, |
| 670 | + lengthInMinutes, |
| 671 | + }) => { |
| 672 | + const linked = await getLinkedUser(teamId, userId); |
| 673 | + |
| 674 | + try { |
| 675 | + const booking = await createBookingPublic({ |
| 676 | + eventTypeSlug, |
| 677 | + username, |
| 678 | + start: startTime, |
| 679 | + attendee: { |
| 680 | + name: attendeeName, |
| 681 | + email: attendeeEmail, |
| 682 | + timeZone: attendeeTimeZone ?? linked?.calcomTimeZone ?? "UTC", |
| 683 | + }, |
| 684 | + guests: guests?.filter(Boolean) ?? undefined, |
| 685 | + notes: notes ?? undefined, |
| 686 | + lengthInMinutes: lengthInMinutes ?? undefined, |
| 687 | + }); |
| 688 | + |
| 689 | + return { |
| 690 | + success: true, |
| 691 | + bookingUid: booking.uid, |
| 692 | + title: booking.title, |
| 693 | + start: booking.start, |
| 694 | + end: booking.end, |
| 695 | + meetingUrl: booking.meetingUrl, |
| 696 | + attendees: booking.attendees.map((a) => ({ name: a.name, email: a.email })), |
| 697 | + }; |
| 698 | + } catch (err) { |
| 699 | + return { error: err instanceof Error ? err.message : "Failed to create booking" }; |
| 700 | + } |
| 701 | + }, |
| 702 | + }), |
| 703 | + |
624 | 704 | add_booking_attendee: tool({ |
625 | 705 | description: |
626 | 706 | "Add a full attendee record (name + timezone) to an existing booking. Use after book_meeting for additional attendees resolved via lookup_platform_user on Slack where you have full profile details.", |
@@ -1155,6 +1235,7 @@ const CORE_TOOL_NAMES = new Set([ |
1155 | 1235 | "check_availability", |
1156 | 1236 | "check_availability_public", |
1157 | 1237 | "book_meeting", |
| 1238 | + "book_meeting_public", |
1158 | 1239 | "add_booking_attendee", |
1159 | 1240 | "list_bookings", |
1160 | 1241 | "get_booking", |
|
0 commit comments