@qvac/skills 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/hash.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Autogenerated by scripts/build.mjs from skills/. Do not edit.
2
- export const SKILLS_HASH = '0a22513646401785'
2
+ export const SKILLS_HASH = 'ba65671970b03d38'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qvac/skills",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Skills for the QV.AC app — the SKILL.md tree plus a content-addressed bundle of it.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -5,28 +5,28 @@ tools: [exec(osascript)]
5
5
  platform: [darwin]
6
6
  metadata:
7
7
  {
8
- 'openclaw':
9
- {
10
- 'requires': { 'bins': ['osascript'] },
11
- 'setup':
8
+ "openclaw": {
9
+ "requires": {
10
+ "bins": [
11
+ "osascript"
12
+ ]
13
+ },
14
+ "setup": {
15
+ "summary": "Apple Notes works through macOS automation (osascript), which ships with macOS — nothing to install. The first command triggers a one-time permission prompt to let this app control Notes.",
16
+ "routes": [
12
17
  {
13
- 'summary': 'Apple Notes works through macOS automation (osascript), which ships with macOS — nothing to install. The first command triggers a one-time permission prompt to let this app control Notes.',
14
- 'routes':
15
- [
16
- {
17
- 'kind': 'instructions',
18
- 'label': 'Allow Notes automation',
19
- 'description': "macOS asks once for permission to control Notes. Commands fail until it's granted.",
20
- 'steps':
21
- [
22
- 'Use the skill once — macOS shows a permission prompt to allow control of Notes.',
23
- 'Click Allow.',
24
- 'If it was denied, enable it under System Settings → Privacy & Security → Automation.'
25
- ]
26
- }
27
- ]
18
+ "kind": "instructions",
19
+ "label": "Allow Notes automation",
20
+ "description": "macOS asks once for permission to control Notes. Commands fail until it's granted.",
21
+ "steps": [
22
+ "Use the skill once — macOS shows a permission prompt to allow control of Notes.",
23
+ "Click Allow.",
24
+ "If it was denied, enable it under System Settings → Privacy & Security → Automation."
25
+ ]
28
26
  }
27
+ ]
29
28
  }
29
+ }
30
30
  }
31
31
  ---
32
32
 
@@ -6,6 +6,54 @@ platform: [darwin, linux, win32]
6
6
  credentials: [asana_mcp_access_token]
7
7
  allow_list: [https://mcp.asana.com/v2/mcp]
8
8
  mcp_reads: [get_task, get_tasks, get_my_tasks, get_projects, search_tasks, search_objects]
9
+ metadata:
10
+ {
11
+ "openclaw": {
12
+ "setup": {
13
+ "routes": [
14
+ {
15
+ "kind": "oauth",
16
+ "label": "Asana",
17
+ "provider": "asana",
18
+ "credentialKey": "asana_mcp_access_token",
19
+ "description": "Tasks, projects, and comments",
20
+ "helpUrl": "https://app.asana.com/0/my-apps",
21
+ "steps": [
22
+ "Click Connect and approve access in the browser. Approving grants access to the Asana workspace you allow."
23
+ ],
24
+ "configSteps": [
25
+ "[Open the Asana developer console](https://app.asana.com/0/my-apps) and create an MCP app",
26
+ "In OAuth settings, add the exact redirect URI shown below",
27
+ "In \"Manage distribution\", allow the workspace you want this connection to access"
28
+ ],
29
+ "fields": [
30
+ {
31
+ "key": "clientId",
32
+ "label": "Client ID",
33
+ "placeholder": "OAuth client ID from your app"
34
+ },
35
+ {
36
+ "key": "clientSecret",
37
+ "label": "Client Secret",
38
+ "secret": true
39
+ }
40
+ ],
41
+ "oauth": {
42
+ "stateKey": "asana_oauth",
43
+ "authUrl": "https://app.asana.com/-/oauth_authorize",
44
+ "tokenUrl": "https://app.asana.com/-/oauth_token",
45
+ "tokenAuth": "secret-in-body",
46
+ "port": 18981,
47
+ "scopes": [],
48
+ "extraAuthParams": {
49
+ "resource": "https://mcp.asana.com/v2"
50
+ }
51
+ }
52
+ }
53
+ ]
54
+ }
55
+ }
56
+ }
9
57
  ---
10
58
 
11
59
  # Asana
@@ -6,6 +6,57 @@ tools: [http_request, gmail_send, gmail_draft]
6
6
  platform: [darwin, linux, win32]
7
7
  credentials: [gmail_access_token]
8
8
  allow_list: [https://gmail.googleapis.com/gmail/v1/users/me/]
9
+ metadata:
10
+ {
11
+ "openclaw": {
12
+ "setup": {
13
+ "routes": [
14
+ {
15
+ "kind": "oauth",
16
+ "label": "Gmail",
17
+ "provider": "google",
18
+ "credentialKey": "gmail_access_token",
19
+ "description": "Read, search, send, and manage Gmail messages and labels",
20
+ "steps": [
21
+ "Click Connect and approve access in the browser. Approving grants access to your Gmail only.",
22
+ "Each Google skill is connected separately, with its own app and its own approval."
23
+ ],
24
+ "configSteps": [
25
+ "[Open Google Cloud Console](https://console.cloud.google.com/flows/enableapi?apiid=gmail.googleapis.com) to enable the Gmail API",
26
+ "[Open OAuth clients](https://console.cloud.google.com/auth/clients) to create an OAuth client — choose \"Desktop app\" as the type",
27
+ "[Open OAuth audience](https://console.cloud.google.com/auth/audience) and add your Google account as a test user"
28
+ ],
29
+ "fields": [
30
+ {
31
+ "key": "clientId",
32
+ "label": "Client ID",
33
+ "placeholder": "OAuth client ID from your app"
34
+ },
35
+ {
36
+ "key": "clientSecret",
37
+ "label": "Client Secret",
38
+ "secret": true
39
+ }
40
+ ],
41
+ "oauth": {
42
+ "stateKey": "gmail_oauth",
43
+ "authUrl": "https://accounts.google.com/o/oauth2/v2/auth",
44
+ "tokenUrl": "https://oauth2.googleapis.com/token",
45
+ "tokenAuth": "secret-in-body",
46
+ "port": 18978,
47
+ "scopes": [
48
+ "https://www.googleapis.com/auth/gmail.modify"
49
+ ],
50
+ "extraAuthParams": {
51
+ "access_type": "offline",
52
+ "prompt": "consent"
53
+ }
54
+ }
55
+ }
56
+ ]
57
+ }
58
+ }
59
+ }
9
60
  ---
10
61
 
11
62
  # Gmail
@@ -6,6 +6,57 @@ tools: [http_request, calendar_create_meet_event, calendar_add_meet]
6
6
  platform: [darwin, linux, win32]
7
7
  credentials: [google_calendar_access_token]
8
8
  allow_list: [https://www.googleapis.com/calendar/v3/]
9
+ metadata:
10
+ {
11
+ "openclaw": {
12
+ "setup": {
13
+ "routes": [
14
+ {
15
+ "kind": "oauth",
16
+ "label": "Google Calendar",
17
+ "provider": "google",
18
+ "credentialKey": "google_calendar_access_token",
19
+ "description": "List, create, update, and delete Google Calendar events",
20
+ "steps": [
21
+ "Click Connect and approve access in the browser. Approving grants access to your calendars only.",
22
+ "Each Google skill is connected separately, with its own app and its own approval."
23
+ ],
24
+ "configSteps": [
25
+ "[Open Google Cloud Console](https://console.cloud.google.com/flows/enableapi?apiid=calendar-json.googleapis.com) to enable the Google Calendar API",
26
+ "[Open OAuth clients](https://console.cloud.google.com/auth/clients) to create an OAuth client — choose \"Desktop app\" as the type",
27
+ "[Open OAuth audience](https://console.cloud.google.com/auth/audience) and add your Google account as a test user"
28
+ ],
29
+ "fields": [
30
+ {
31
+ "key": "clientId",
32
+ "label": "Client ID",
33
+ "placeholder": "OAuth client ID from your app"
34
+ },
35
+ {
36
+ "key": "clientSecret",
37
+ "label": "Client Secret",
38
+ "secret": true
39
+ }
40
+ ],
41
+ "oauth": {
42
+ "stateKey": "google_calendar_oauth",
43
+ "authUrl": "https://accounts.google.com/o/oauth2/v2/auth",
44
+ "tokenUrl": "https://oauth2.googleapis.com/token",
45
+ "tokenAuth": "secret-in-body",
46
+ "port": 18978,
47
+ "scopes": [
48
+ "https://www.googleapis.com/auth/calendar"
49
+ ],
50
+ "extraAuthParams": {
51
+ "access_type": "offline",
52
+ "prompt": "consent"
53
+ }
54
+ }
55
+ }
56
+ ]
57
+ }
58
+ }
59
+ }
9
60
  ---
10
61
 
11
62
  # Google Calendar
@@ -5,6 +5,58 @@ tools: [http_request, docs_create, docs_append_text]
5
5
  platform: [darwin, linux, win32]
6
6
  credentials: [google_docs_access_token]
7
7
  allow_list: [https://docs.googleapis.com/v1/documents, https://www.googleapis.com/drive/v3/files]
8
+ metadata:
9
+ {
10
+ "openclaw": {
11
+ "setup": {
12
+ "routes": [
13
+ {
14
+ "kind": "oauth",
15
+ "label": "Google Docs",
16
+ "provider": "google",
17
+ "credentialKey": "google_docs_access_token",
18
+ "description": "Create, read, and edit Google Docs documents",
19
+ "steps": [
20
+ "Click Connect and approve access in the browser. Approving grants access to your documents and Drive files only.",
21
+ "Each Google skill is connected separately, with its own app and its own approval."
22
+ ],
23
+ "configSteps": [
24
+ "[Open Google Cloud Console](https://console.cloud.google.com/flows/enableapi?apiid=docs.googleapis.com,drive.googleapis.com) to enable the Google Docs API",
25
+ "[Open OAuth clients](https://console.cloud.google.com/auth/clients) to create an OAuth client — choose \"Desktop app\" as the type",
26
+ "[Open OAuth audience](https://console.cloud.google.com/auth/audience) and add your Google account as a test user"
27
+ ],
28
+ "fields": [
29
+ {
30
+ "key": "clientId",
31
+ "label": "Client ID",
32
+ "placeholder": "OAuth client ID from your app"
33
+ },
34
+ {
35
+ "key": "clientSecret",
36
+ "label": "Client Secret",
37
+ "secret": true
38
+ }
39
+ ],
40
+ "oauth": {
41
+ "stateKey": "google_docs_oauth",
42
+ "authUrl": "https://accounts.google.com/o/oauth2/v2/auth",
43
+ "tokenUrl": "https://oauth2.googleapis.com/token",
44
+ "tokenAuth": "secret-in-body",
45
+ "port": 18978,
46
+ "scopes": [
47
+ "https://www.googleapis.com/auth/documents",
48
+ "https://www.googleapis.com/auth/drive"
49
+ ],
50
+ "extraAuthParams": {
51
+ "access_type": "offline",
52
+ "prompt": "consent"
53
+ }
54
+ }
55
+ }
56
+ ]
57
+ }
58
+ }
59
+ }
8
60
  ---
9
61
 
10
62
  # Google Docs
@@ -5,6 +5,57 @@ tools: [http_request, drive_create_folder, drive_trash]
5
5
  platform: [darwin, linux, win32]
6
6
  credentials: [google_drive_access_token]
7
7
  allow_list: [https://www.googleapis.com/drive/v3/]
8
+ metadata:
9
+ {
10
+ "openclaw": {
11
+ "setup": {
12
+ "routes": [
13
+ {
14
+ "kind": "oauth",
15
+ "label": "Google Drive",
16
+ "provider": "google",
17
+ "credentialKey": "google_drive_access_token",
18
+ "description": "Search, list, and manage Google Drive files and folders",
19
+ "steps": [
20
+ "Click Connect and approve access in the browser. Approving grants access to your Drive files only.",
21
+ "Each Google skill is connected separately, with its own app and its own approval."
22
+ ],
23
+ "configSteps": [
24
+ "[Open Google Cloud Console](https://console.cloud.google.com/flows/enableapi?apiid=drive.googleapis.com) to enable the Google Drive API",
25
+ "[Open OAuth clients](https://console.cloud.google.com/auth/clients) to create an OAuth client — choose \"Desktop app\" as the type",
26
+ "[Open OAuth audience](https://console.cloud.google.com/auth/audience) and add your Google account as a test user"
27
+ ],
28
+ "fields": [
29
+ {
30
+ "key": "clientId",
31
+ "label": "Client ID",
32
+ "placeholder": "OAuth client ID from your app"
33
+ },
34
+ {
35
+ "key": "clientSecret",
36
+ "label": "Client Secret",
37
+ "secret": true
38
+ }
39
+ ],
40
+ "oauth": {
41
+ "stateKey": "google_drive_oauth",
42
+ "authUrl": "https://accounts.google.com/o/oauth2/v2/auth",
43
+ "tokenUrl": "https://oauth2.googleapis.com/token",
44
+ "tokenAuth": "secret-in-body",
45
+ "port": 18978,
46
+ "scopes": [
47
+ "https://www.googleapis.com/auth/drive"
48
+ ],
49
+ "extraAuthParams": {
50
+ "access_type": "offline",
51
+ "prompt": "consent"
52
+ }
53
+ }
54
+ }
55
+ ]
56
+ }
57
+ }
58
+ }
8
59
  ---
9
60
 
10
61
  # Google Drive
@@ -6,6 +6,58 @@ platform: [darwin, linux, win32]
6
6
  credentials: [google_sheets_access_token]
7
7
  allow_list:
8
8
  [https://sheets.googleapis.com/v4/spreadsheets, https://www.googleapis.com/drive/v3/files]
9
+ metadata:
10
+ {
11
+ "openclaw": {
12
+ "setup": {
13
+ "routes": [
14
+ {
15
+ "kind": "oauth",
16
+ "label": "Google Sheets",
17
+ "provider": "google",
18
+ "credentialKey": "google_sheets_access_token",
19
+ "description": "Create, read, and update Google Sheets spreadsheets",
20
+ "steps": [
21
+ "Click Connect and approve access in the browser. Approving grants access to your spreadsheets and Drive files only.",
22
+ "Each Google skill is connected separately, with its own app and its own approval."
23
+ ],
24
+ "configSteps": [
25
+ "[Open Google Cloud Console](https://console.cloud.google.com/flows/enableapi?apiid=sheets.googleapis.com,drive.googleapis.com) to enable the Google Sheets API",
26
+ "[Open OAuth clients](https://console.cloud.google.com/auth/clients) to create an OAuth client — choose \"Desktop app\" as the type",
27
+ "[Open OAuth audience](https://console.cloud.google.com/auth/audience) and add your Google account as a test user"
28
+ ],
29
+ "fields": [
30
+ {
31
+ "key": "clientId",
32
+ "label": "Client ID",
33
+ "placeholder": "OAuth client ID from your app"
34
+ },
35
+ {
36
+ "key": "clientSecret",
37
+ "label": "Client Secret",
38
+ "secret": true
39
+ }
40
+ ],
41
+ "oauth": {
42
+ "stateKey": "google_sheets_oauth",
43
+ "authUrl": "https://accounts.google.com/o/oauth2/v2/auth",
44
+ "tokenUrl": "https://oauth2.googleapis.com/token",
45
+ "tokenAuth": "secret-in-body",
46
+ "port": 18978,
47
+ "scopes": [
48
+ "https://www.googleapis.com/auth/spreadsheets",
49
+ "https://www.googleapis.com/auth/drive"
50
+ ],
51
+ "extraAuthParams": {
52
+ "access_type": "offline",
53
+ "prompt": "consent"
54
+ }
55
+ }
56
+ }
57
+ ]
58
+ }
59
+ }
60
+ }
9
61
  ---
10
62
 
11
63
  # Google Sheets
@@ -15,6 +15,35 @@ mcp_reads:
15
15
  notion-get-async-task,
16
16
  notion-query-data-sources
17
17
  ]
18
+ metadata:
19
+ {
20
+ "openclaw": {
21
+ "setup": {
22
+ "routes": [
23
+ {
24
+ "kind": "oauth",
25
+ "label": "Notion",
26
+ "provider": "notion",
27
+ "credentialKey": "notion_mcp_access_token",
28
+ "description": "Pages, databases, and blocks",
29
+ "steps": [
30
+ "Click Connect and approve access in the browser — Workbench uses Notion's hosted MCP OAuth, so no app credentials are needed."
31
+ ],
32
+ "oauth": {
33
+ "stateKey": "notion_oauth",
34
+ "authUrl": "https://mcp.notion.com/authorize",
35
+ "tokenUrl": "https://mcp.notion.com/token",
36
+ "tokenAuth": "pkce-only",
37
+ "port": 18982,
38
+ "registrationEndpoint": "https://mcp.notion.com/register",
39
+ "sharedIosSession": true,
40
+ "scopes": []
41
+ }
42
+ }
43
+ ]
44
+ }
45
+ }
46
+ }
18
47
  ---
19
48
 
20
49
  # Notion
@@ -8,51 +8,86 @@ credentials: [spotify_access_token]
8
8
  allow_list: [https://api.spotify.com/v1/]
9
9
  metadata:
10
10
  {
11
- "openclaw":
12
- {
13
- "requires":
11
+ "openclaw": {
12
+ "requires": {
13
+ "credentials": [
14
+ "spotify_access_token"
15
+ ],
16
+ "credentialChecks": {
17
+ "spotify_access_token": {
18
+ "url": "https://api.spotify.com/v1/me"
19
+ }
20
+ }
21
+ },
22
+ "setup": {
23
+ "routes": [
14
24
  {
15
- "credentials": ["spotify_access_token"],
16
- "credentialChecks":
17
- { "spotify_access_token": { "url": "https://api.spotify.com/v1/me" } }
25
+ "kind": "oauth",
26
+ "label": "Spotify",
27
+ "provider": "spotify",
28
+ "credentialKey": "spotify_access_token",
29
+ "description": "Search, library, and playback control — playback requires Spotify Premium",
30
+ "helpUrl": "https://developer.spotify.com/dashboard",
31
+ "steps": [
32
+ "Search and library browsing work with any Spotify account; controlling playback (play, pause, skip) requires Spotify Premium and an open Spotify app on a device.",
33
+ "Create your own app at https://developer.spotify.com/dashboard (Spotify requires the app owner to hold a Premium subscription).",
34
+ "In the app settings, add these exact Redirect URIs: http://127.0.0.1:18974/callback (desktop) and, for mobile, the Redirect URI shown in this connect form (the scheme varies per build, e.g. qvac://oauth-callback or qvac-dev://oauth-callback).",
35
+ "Paste the Client ID above and click Connect, then approve access in the browser. No Client Secret is needed — Workbench uses PKCE."
36
+ ],
37
+ "fields": [
38
+ {
39
+ "key": "clientId",
40
+ "label": "Client ID",
41
+ "placeholder": "From your app at developer.spotify.com"
42
+ }
43
+ ],
44
+ "oauth": {
45
+ "stateKey": "spotify_oauth",
46
+ "authUrl": "https://accounts.spotify.com/authorize",
47
+ "tokenUrl": "https://accounts.spotify.com/api/token",
48
+ "tokenAuth": "pkce-only",
49
+ "port": 18974,
50
+ "sharedIosSession": true,
51
+ "scopes": [
52
+ "user-read-private",
53
+ "user-read-playback-state",
54
+ "user-modify-playback-state",
55
+ "user-read-currently-playing",
56
+ "playlist-read-private",
57
+ "playlist-read-collaborative",
58
+ "user-library-read",
59
+ "user-top-read",
60
+ "user-read-recently-played"
61
+ ]
62
+ }
18
63
  }
64
+ ]
19
65
  }
66
+ }
20
67
  }
21
68
  ---
22
69
 
23
70
  # Spotify
24
71
 
25
- Use `http_request` against `https://api.spotify.com/v1`. The Spotify credential is attached automatically to every `api.spotify.com` request — **never include an `auth` block**. Never invent track/album/artist URIs — search first and copy `uri` from the JSON response.
72
+ Use `http_request` against `https://api.spotify.com/v1`. The Spotify credential is attached automatically to every `api.spotify.com` request — **never include an `auth` block**.
26
73
 
27
- ```json
28
- {
29
- "url": "https://api.spotify.com/v1/search",
30
- "method": "GET",
31
- "query": { "q": "Radiohead Creep", "type": "track", "limit": 5 }
32
- }
33
- ```
74
+ ## Playing a song takes two calls. Always two.
34
75
 
35
- ## Hard rules
76
+ "Play X" is not answered until **both** have run:
36
77
 
37
- - A bare song/artist/album/playlist name is enough — search immediately, take the top match, and tell the user what you picked. Do not ask "which one?" before searching.
38
- - Always search before playing by name. Play carries URIs only in the JSON `body` (`"uris": ["…"]`), never as query parameters.
39
- - A bare `PUT /me/player/play` with no body only resumes paused playback — it never plays a requested song. For an album/artist/playlist use `{ "context_uri": "<uri>" }` instead of `uris`.
40
- - Do not ask about tokens or setup up front. A **401** means Spotify isn't connected (tell the user to run `/connect spotify`). A **404** from `/me/player` means no active device (tell them to open Spotify).
78
+ 1. `GET /v1/search` — find the track.
79
+ 2. `PUT /v1/me/player/play` — start it.
41
80
 
42
- ## Recipe: play a song by name
43
-
44
- 1. Search:
81
+ Search alone plays nothing. If you have searched and not yet called play, you are not finished: make the play call now.
45
82
 
46
83
  ```json
47
84
  {
48
85
  "url": "https://api.spotify.com/v1/search",
49
86
  "method": "GET",
50
- "query": { "q": "Radiohead Creep", "type": "track", "limit": 5 }
87
+ "query": { "q": "Radiohead Creep", "type": "track", "limit": 3, "market": "from_token" }
51
88
  }
52
89
  ```
53
90
 
54
- 2. Copy `tracks.items[0].uri` into the play body:
55
-
56
91
  ```json
57
92
  {
58
93
  "url": "https://api.spotify.com/v1/me/player/play",
@@ -61,6 +96,20 @@ Use `http_request` against `https://api.spotify.com/v1`. The Spotify credential
61
96
  }
62
97
  ```
63
98
 
99
+ Take `tracks.items[0].uri` from the search response and paste it into `uris`. A 204 means it started.
100
+
101
+ ## Hard rules
102
+
103
+ - **`q` carries every word the user named — the title and the artist.** "play Creep by Radiohead" searches `q=Radiohead Creep`, never `q=Creep`. Drop the artist and the top hit is a different band's song with the same title.
104
+ - **Before playing, check the item you picked.** Compare its `artists[0].name` with the artist the user named. If they do not match, take the first result that does. A title match under the wrong artist is the wrong song, and the user hears it immediately.
105
+ - **Never write a URI, a JSON block, or "I'll play it now" to the user in place of calling play.** Describing the call is not making it.
106
+ - **Every URI you send is one you copied from a search response in this turn.** Never type a `spotify:track:` id from memory or from an earlier turn. A well-formed id that is not real stops what was playing and starts nothing.
107
+ - **A track goes in `uris`. Only `uris`.** `context_uri` takes an album, artist or playlist URI — a track URI there plays nothing. URIs go in the JSON `body`, never in query parameters.
108
+ - **One call per intent.** A 204 means the call landed; do not send it again. Repeating a queue or play call burns the turn and changes nothing.
109
+ - **Name what actually played, read back from the item you used** — its `name` and `artists[0].name`. A 204 says the call was accepted, not which song it was, so never report a title you did not read out of the response.
110
+ - A bare song/artist/album/playlist name is enough — search immediately, take the top match, and tell the user what you picked. Do not ask "which one?" before searching.
111
+ - Do not ask about tokens or setup up front. A **401** means Spotify isn't connected (tell the user to run `/connect spotify`). A **404** from `/me/player` means no active device (tell them to open Spotify).
112
+
64
113
  ## Other operations
65
114
 
66
115
  All paths are under `https://api.spotify.com/v1`.
@@ -69,17 +118,20 @@ All paths are under `https://api.spotify.com/v1`.
69
118
  | --------------- | ----------------------------------------------------------------------- |
70
119
  | What's playing? | `GET /me/player/currently-playing` |
71
120
  | Pause | `PUT /me/player/pause` |
72
- | Resume | `PUT /me/player/play` (no body) |
121
+ | Resume | `PUT /me/player/play` (no body — resumes only, never starts a new song) |
73
122
  | Next track | `POST /me/player/next` |
74
123
  | Add to queue | `POST /me/player/queue` with `query`: `{ "uri": "spotify:track:<id>" }` |
124
+ | Play an album/artist/playlist | `PUT /me/player/play` with `body`: `{ "context_uri": "<uri>" }` |
75
125
  | My playlists | `GET /me/playlists` |
76
126
  | Top tracks | `GET /me/top/tracks` with `query`: `{ "time_range": "medium_term" }` |
77
127
  | Recently played | `GET /me/player/recently-played` |
78
128
  | List devices | `GET /me/player/devices` |
79
129
 
130
+ Queueing is the same two calls as playing: search for the track, then `POST /me/player/queue` with the `uri` you just read. Queueing does not start playback.
131
+
80
132
  ## Notes
81
133
 
82
- - Keep responses small — they truncate past 8KB, and raw JSON burns tokens on small models. Keep `limit` at 5. On playlist/track endpoints request only what you need with `fields` (e.g. `query`: `{ "fields": "items(track(name,artists(name),uri))" }`). Read just the top item unless the user asked for a list.
134
+ - Keep responses small — they truncate past 8KB, and raw JSON burns tokens on small models. Keep `limit` at 3 and **always pass `market`**: a search without it spends most of the budget on `available_markets`, and the results behind the first one are cut off before you can read them. `/search` has no `fields` param, so `market` is the only lever there. On playlist/track endpoints request only what you need with `fields` (e.g. `query`: `{ "fields": "items(track(name,artists(name),uri))" }`). Read just the top item unless the user asked for a list.
83
135
  - Present results as a short numbered list — track, artist, album, duration — and devices as `1. Name (active/idle)`. Never dump raw JSON to the user.
84
136
  - Playback commands return **204 No Content** on success (empty body). A **204** from currently-playing means nothing is playing.
85
137
  - **403** with `PREMIUM_REQUIRED` → user needs Spotify Premium. Other **403**s are usually app-scope/Development Mode limits — report and stop.