Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clients/api/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
74 changes: 74 additions & 0 deletions cmd/resource/connect.go
Original file line number Diff line number Diff line change
@@ -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> [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
}
1 change: 1 addition & 0 deletions cmd/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ func init() {
Cmd.AddCommand(listCmd)
Cmd.AddCommand(addCmd)
Cmd.AddCommand(removeCmd)
Cmd.AddCommand(connectCmd)
}
13 changes: 8 additions & 5 deletions plugins/shared/skills/resources_googlecalendar/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <resourceId>` 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.

---

Expand Down Expand Up @@ -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)

11 changes: 11 additions & 0 deletions plugins/shared/skills/resources_list-resources/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <resourceId>` 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.