@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/bundled.js +10 -10
- package/hash.js +1 -1
- package/package.json +1 -1
- package/skills/apple-notes/SKILL.md +19 -19
- package/skills/asana/SKILL.md +48 -0
- package/skills/gmail/SKILL.md +51 -0
- package/skills/google-calendar/SKILL.md +51 -0
- package/skills/google-docs/SKILL.md +52 -0
- package/skills/google-drive/SKILL.md +51 -0
- package/skills/google-sheets/SKILL.md +52 -0
- package/skills/notion/SKILL.md +29 -0
- package/skills/spotify/SKILL.md +79 -27
- package/skills/weather/SKILL.md +39 -23
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 = '
|
|
2
|
+
export const SKILLS_HASH = 'ba65671970b03d38'
|
package/package.json
CHANGED
|
@@ -5,28 +5,28 @@ tools: [exec(osascript)]
|
|
|
5
5
|
platform: [darwin]
|
|
6
6
|
metadata:
|
|
7
7
|
{
|
|
8
|
-
|
|
9
|
-
{
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
package/skills/asana/SKILL.md
CHANGED
|
@@ -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
|
package/skills/gmail/SKILL.md
CHANGED
|
@@ -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
|
package/skills/notion/SKILL.md
CHANGED
|
@@ -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
|
package/skills/spotify/SKILL.md
CHANGED
|
@@ -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
|
-
"
|
|
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
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
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**.
|
|
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
|
-
|
|
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
|
-
|
|
76
|
+
"Play X" is not answered until **both** have run:
|
|
36
77
|
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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":
|
|
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
|
|
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.
|