diff --git a/.changeset/seven-rules-invent.md b/.changeset/seven-rules-invent.md new file mode 100644 index 0000000000..37a3d4f1bc --- /dev/null +++ b/.changeset/seven-rules-invent.md @@ -0,0 +1,4 @@ +--- +--- + +Allow working group leaders to schedule meetings for groups they lead via Addie. Added meeting and event tools documentation to Addie's system prompt. diff --git a/server/src/addie/mcp/meeting-tools.ts b/server/src/addie/mcp/meeting-tools.ts index 6045208a3f..bdd10432b9 100644 --- a/server/src/addie/mcp/meeting-tools.ts +++ b/server/src/addie/mcp/meeting-tools.ts @@ -34,8 +34,14 @@ export async function canScheduleMeetings(slackUserId: string): Promise const isAdmin = await isSlackUserAdmin(slackUserId); if (isAdmin) return true; - // TODO: Check if user is a working group leader - // For now, only admins can schedule + // Check if user is a leader of any working group + // getCommitteesLedByUser handles both Slack user IDs and WorkOS user IDs + const ledGroups = await workingGroupDb.getCommitteesLedByUser(slackUserId); + if (ledGroups.length > 0) { + logger.debug({ slackUserId, groupCount: ledGroups.length }, 'User is a working group leader'); + return true; + } + return false; } @@ -298,11 +304,11 @@ export function createMeetingToolHandlers( if (slackUserId) { const canSchedule = await canScheduleMeetings(slackUserId); if (!canSchedule) { - return '⚠️ You need to be an admin or working group leader to schedule meetings.'; + return '⚠️ You need to be an admin or committee leader to schedule meetings.'; } } else if (memberContext) { if (memberContext.org_membership?.role !== 'admin') { - return '⚠️ You need admin or working group leader access to schedule meetings.'; + return '⚠️ You need to be an admin or committee leader to schedule meetings.'; } } return null; @@ -324,6 +330,26 @@ export function createMeetingToolHandlers( return `❌ Working group not found: "${workingGroupSlug}". Check the slug and try again.`; } + // Non-admin users can only schedule meetings for groups they lead + // This check runs for both Slack and web channels + const userId = getUserId(); + if (userId) { + // Determine admin status from either Slack or web context + let isAdmin = false; + if (slackUserId) { + isAdmin = await isSlackUserAdmin(slackUserId); + } else if (memberContext?.org_membership?.role === 'admin') { + isAdmin = true; + } + + if (!isAdmin) { + const isGroupLeader = await workingGroupDb.isLeader(workingGroup.id, userId); + if (!isGroupLeader) { + return `⚠️ You can only schedule meetings for committees you lead. You're not a leader of "${workingGroup.name}".`; + } + } + } + // Parse start time const startTime = new Date(startTimeStr); if (isNaN(startTime.getTime())) { diff --git a/server/src/addie/prompts.ts b/server/src/addie/prompts.ts index 9fda4a4d79..bdb68670fc 100644 --- a/server/src/addie/prompts.ts +++ b/server/src/addie/prompts.ts @@ -158,6 +158,28 @@ When users set up agents or publishers, walk through the full verification chain - update_committee_document: Update a tracked document (leader only) - delete_committee_document: Remove a tracked document (leader only) +**Events (available to admins and committee leads):** +- create_event: Create an AAO event (meetup, webinar, summit, workshop, conference). Creates in both Luma (registration) and AAO website. Required: title, start_time, event_type. Optional: description, end_time, timezone, venue details, virtual_url, max_attendees. +- list_events: List events personalized for the user (events they're registered for, chapter events, industry gatherings, global summits). Use include_past=true to show past events. +- get_event_details: Get details about a specific event including registration counts +- manage_event_registrations: List, approve, or export event registrations +- update_event: Modify event details + +**Meetings (available to admins and committee leaders):** +- schedule_meeting: Schedule a committee meeting with Zoom and calendar invites. Requires working_group_slug, title, and start_time (ISO format). Optional: description, agenda, duration_minutes, timezone, topic_slugs. Creates Zoom link and sends calendar invites to committee members. +- list_upcoming_meetings: List upcoming meetings. Can filter by working_group_slug. +- get_my_meetings: Get the user's upcoming meetings +- get_meeting_details: Get details about a specific meeting including attendees and RSVP status +- rsvp_to_meeting: RSVP to a meeting (accepted, declined, tentative) +- cancel_meeting: Cancel a scheduled meeting (sends cancellation notices) +- add_meeting_attendee: Add someone to a meeting by email +- update_topic_subscriptions: Update which meeting topics a user is subscribed to in a working group + +Example meeting scenarios: +- "Schedule a technical working group call for next Tuesday at 2pm ET" → Use schedule_meeting with working_group_slug="technical", appropriate title and start_time +- "What meetings do I have coming up?" → Use get_my_meetings +- "Set up a recurring weekly call for governance" → Note: Recurring meetings must be scheduled one at a time currently, or use Google Calendar's recurring feature directly + **Member Profile:** - get_my_profile: Show user's profile - update_my_profile: Update profile fields (user-scoped)