import { useMemo, useState } from "react"; import { ChevronDownIcon, PlusIcon, XIcon } from "lucide-react"; import { cn } from "@executor-js/react/lib/utils"; import { Badge } from "@executor-js/react/components/badge"; import { Button } from "@executor-js/react/components/button"; import { Checkbox } from "@executor-js/react/components/checkbox"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@executor-js/react/components/collapsible"; import { FieldLabel } from "@executor-js/react/components/field"; import { Input } from "@executor-js/react/components/input"; import { IntegrationFavicon } from "@executor-js/react/components/integration-favicon"; import { microsoftGraphScopePresets, microsoftGraphScopesForPresetIds, type MicrosoftGraphScopeAudience, type MicrosoftGraphScopePreset, } from "../sdk/presets"; type MicrosoftScopePickerProps = { readonly selectedPresetIds: ReadonlySet; readonly onToggle: (presetId: string, checked: boolean) => void; readonly customScopes: readonly string[]; readonly onAddCustomScope: (scope: string) => void; readonly onRemoveCustomScope: (scope: string) => void; }; const AUDIENCE_ORDER: readonly MicrosoftGraphScopeAudience[] = [ "productivity", "files-content", "collaboration", "directory-identity", "admin-security", "platform-business", ]; const AUDIENCE_LABEL: Readonly> = { productivity: "Productivity", "files-content": "Files and content", collaboration: "Collaboration", "directory-identity": "Directory and identity", "admin-security": "Admin and security", "platform-business": "Platform and business", }; const AUDIENCE_DESCRIPTION: Readonly> = { productivity: "User-facing Microsoft 365 operations for the connected account.", "files-content": "Drive, SharePoint, OneNote, workbook, and content APIs.", collaboration: "Teams, chats, online meetings, calls, and teamwork APIs.", "directory-identity": "Tenant directory, app, identity, policy, and governance APIs.", "admin-security": "Admin, audit, security, compliance, device, and education APIs.", "platform-business": "Search, connectors, extensions, print, places, and business services.", }; const ScopeRow = ({ preset, checked, onToggle, }: { readonly preset: MicrosoftGraphScopePreset; readonly checked: boolean; readonly onToggle: (checked: boolean) => void; }) => ( onToggle(next === true)} />
{preset.name}

{preset.summary}

); const CustomScopeInput = ({ customScopes, onAddCustomScope, onRemoveCustomScope, }: { readonly customScopes: readonly string[]; readonly onAddCustomScope: (scope: string) => void; readonly onRemoveCustomScope: (scope: string) => void; }) => { const [draft, setDraft] = useState(""); const trimmed = draft.trim(); const isValid = trimmed.length > 0 && !/\s/.test(trimmed); const isDuplicate = customScopes.includes(trimmed); const commit = () => { if (!isValid || isDuplicate) return; onAddCustomScope(trimmed); setDraft(""); }; return (
Add custom Graph scope
) => setDraft(event.target.value)} onKeyDown={(event: React.KeyboardEvent) => { if (event.key === "Enter") { event.preventDefault(); commit(); } }} placeholder="Sites.Read.All" className="font-mono text-[11px]" />
{trimmed.length > 0 && !isValid ? (

Scopes cannot contain whitespace.

) : null} {customScopes.length > 0 ? (
    {customScopes.map((scope: string) => (
  • {scope}
  • ))}
) : null}
); }; export function MicrosoftScopePicker({ selectedPresetIds, onToggle, customScopes, onAddCustomScope, onRemoveCustomScope, }: MicrosoftScopePickerProps) { const [scopesOpen, setScopesOpen] = useState(false); const groups = useMemo( () => AUDIENCE_ORDER.flatMap((audience: MicrosoftGraphScopeAudience) => { const presets = microsoftGraphScopePresets.filter( (preset: MicrosoftGraphScopePreset) => preset.audience === audience, ); return presets.length > 0 ? [{ audience, presets }] : []; }), [], ); const scopes = useMemo( () => microsoftGraphScopesForPresetIds([...selectedPresetIds], customScopes), [selectedPresetIds, customScopes], ); return (
Customize Microsoft Graph

Pick the operation groups to expose as tools. They share one Microsoft OAuth consent and one account connection.

{groups.map( ({ audience, presets, }: { readonly audience: MicrosoftGraphScopeAudience; readonly presets: readonly MicrosoftGraphScopePreset[]; }) => (
{AUDIENCE_LABEL[audience]}

{AUDIENCE_DESCRIPTION[audience]}

{presets.map((preset: MicrosoftGraphScopePreset) => ( onToggle(preset.id, checked)} /> ))}
), )}
    {scopes.map((scope: string) => (
  • {scope}
  • ))}
); } export default MicrosoftScopePicker;