diff --git a/clients/api/structs.go b/clients/api/structs.go index 2298ebe..ec460be 100644 --- a/clients/api/structs.go +++ b/clients/api/structs.go @@ -100,6 +100,7 @@ type ResourceItem struct { Name string `json:"name"` Description string `json:"description"` Type string `json:"type"` + AuthMode string `json:"authMode"` } // GetApplicationResourcesResponse represents the response from GET /applications/:applicationId/resources diff --git a/cmd/resource/connect.go b/cmd/resource/connect.go new file mode 100644 index 0000000..615e089 --- /dev/null +++ b/cmd/resource/connect.go @@ -0,0 +1,74 @@ +package resource + +import ( + "fmt" + "strings" + + "github.com/major-technology/cli/middleware" + "github.com/major-technology/cli/singletons" + "github.com/major-technology/cli/utils" + "github.com/spf13/cobra" +) + +var environmentFlag string + +var connectCmd = &cobra.Command{ + Use: "connect [resourceId...]", + Short: "Open the browser to connect your OAuth accounts for per-user resources", + Long: `Opens the /connect page in your browser so you can authenticate with +per-user OAuth resources. Resource IDs can be found via the web UI connectors page +or from the list_resources MCP tool. + +If --environment is not provided, the environment is auto-resolved from +the app's git remote. If that also fails, the connect page will use +the org's default environment.`, + Args: cobra.MinimumNArgs(1), + PreRunE: middleware.Compose( + middleware.CheckLogin, + ), + RunE: func(cmd *cobra.Command, args []string) error { + return runConnect(cmd, args) + }, +} + +func init() { + connectCmd.Flags().StringVar(&environmentFlag, "environment", "", "Environment ID (defaults to app's current environment)") +} + +func runConnect(cmd *cobra.Command, resourceIds []string) error { + cfg := singletons.GetConfig() + if cfg == nil { + return fmt.Errorf("configuration not initialized") + } + + // Build the connect URL + resources := strings.Join(resourceIds, ",") + connectURL := fmt.Sprintf("%s/connect?resources=%s", cfg.FrontendURI, resources) + + // Append environment if explicitly provided or resolvable from app context + envID := environmentFlag + + if envID == "" { + appInfo, err := utils.GetApplicationInfo("") + if err == nil { + apiClient := singletons.GetAPIClient() + + envResp, err := apiClient.GetApplicationEnvironment(appInfo.ApplicationID) + if err == nil && envResp.EnvironmentID != nil { + envID = *envResp.EnvironmentID + } + } + } + + if envID != "" { + connectURL += fmt.Sprintf("&environmentId=%s", envID) + } + + if err := utils.OpenBrowser(connectURL); err != nil { + cmd.Printf("Failed to open browser automatically. Please visit:\n%s\n", connectURL) + return nil + } + + cmd.Printf("Opening connect page in your browser:\n%s\n", connectURL) + return nil +} diff --git a/cmd/resource/resource.go b/cmd/resource/resource.go index 1f6fe3b..90880ec 100644 --- a/cmd/resource/resource.go +++ b/cmd/resource/resource.go @@ -25,4 +25,5 @@ func init() { Cmd.AddCommand(listCmd) Cmd.AddCommand(addCmd) Cmd.AddCommand(removeCmd) + Cmd.AddCommand(connectCmd) } diff --git a/plugins/shared/skills/resources_googlecalendar/SKILL.md b/plugins/shared/skills/resources_googlecalendar/SKILL.md index 476d326..8ed3692 100644 --- a/plugins/shared/skills/resources_googlecalendar/SKILL.md +++ b/plugins/shared/skills/resources_googlecalendar/SKILL.md @@ -5,14 +5,16 @@ description: Implements Google Calendar event management and scheduling using ge # Major Platform Resource: Google Calendar -## Setting Up a Google Calendar Connector +## Per-User OAuth (required before use) -Google Calendar requires OAuth authentication before use. +Google Calendar uses per-user OAuth — each user must connect their own Google account before any tools will work (`requiresUserOAuth: true` in `list_resources`). -### When the user asks you to set up Google Calendar or connect their calendar: +**Before calling any Google Calendar tools**, check if the `mcp__user-oauth-setup__setup-user-oauth` tool is available: -1. Call `mcp__resource-setup__request-resource-setup` with `subtype: "googlecalendar"` — this prompts the user to authenticate with Google -2. Once setup completes, the resource is ready to use +- **If available** (web ai-coder): Call `setup-user-oauth` with the resource ID. It will prompt the user to connect via a popup. +- **If not available** (CLI): Run `major resource connect ` via the Bash tool — this opens the user's browser to complete OAuth. Then ask the user to confirm they've connected before retrying the resource tools. + +Do NOT attempt to call Google Calendar tools without completing this step — the calls will fail. --- @@ -86,3 +88,4 @@ const createResult = await googleCalendarClient.invoke("POST", "calendars/primar - **Scope presets**: The resource may be configured as "readonly" (can only read) or "readwrite" (can read and create/modify events). Write operations will fail with 403 if the resource is readonly. **Docs**: [Google Calendar API Reference](https://developers.google.com/calendar/api/v3/reference) + diff --git a/plugins/shared/skills/resources_list-resources/SKILL.md b/plugins/shared/skills/resources_list-resources/SKILL.md index 285be62..bb9454d 100644 --- a/plugins/shared/skills/resources_list-resources/SKILL.md +++ b/plugins/shared/skills/resources_list-resources/SKILL.md @@ -29,3 +29,14 @@ For each resource you plan to use: The agent will download the file, read it, and return a summary plus the local file path. 4. If the context document contains schema or API information, use it directly — do not make redundant queries (e.g. do not run `\d` table commands if the schema is already in the context doc) 5. Tell the user which context documents you read and what you learned, so they know their context is being used + +## Step 3: Handle per-user OAuth resources + +Some resources require each user to connect their own account (`requiresUserOAuth: true` in the `list_resources` response). **You must handle this before attempting to use those resources.** + +Check if the `mcp__user-oauth-setup__setup-user-oauth` tool is available: + +- **If available** (web ai-coder): Call `setup-user-oauth` with the resource ID. It will prompt the user to connect via a popup and block until complete. +- **If not available** (CLI): Run `major resource connect ` via the Bash tool — this opens the user's browser to complete OAuth. Then ask the user to confirm they've connected before retrying the resource tools. + +Do NOT attempt to call resource tools for a `requiresUserOAuth: true` resource without completing this step first — the calls will fail with an authentication error.