@commandable/integration-data 0.0.5 → 0.0.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.
Files changed (183) hide show
  1. package/dist/credentials-index.d.ts.map +1 -1
  2. package/dist/credentials-index.js +64 -0
  3. package/dist/credentials-index.js.map +1 -1
  4. package/integrations/github/__tests__/get_handlers.test.ts +202 -7
  5. package/integrations/github/__tests__/write_handlers.test.ts +171 -21
  6. package/integrations/github/handlers/create_commit.js +10 -2
  7. package/integrations/github/handlers/create_pull_request_review.js +10 -0
  8. package/integrations/github/handlers/create_release.js +14 -0
  9. package/integrations/github/handlers/delete_branch.js +8 -0
  10. package/integrations/github/handlers/delete_file.js +9 -0
  11. package/integrations/github/handlers/fork_repo.js +10 -0
  12. package/integrations/github/handlers/get_commit.js +8 -0
  13. package/integrations/github/handlers/get_file_contents.js +21 -0
  14. package/integrations/github/handlers/get_job_logs.js +6 -0
  15. package/integrations/github/handlers/get_latest_release.js +4 -0
  16. package/integrations/github/handlers/get_me.js +4 -0
  17. package/integrations/github/handlers/get_pull_request.js +4 -0
  18. package/integrations/github/handlers/get_pull_request_diff.js +8 -0
  19. package/integrations/github/handlers/get_repo_tree.js +12 -0
  20. package/integrations/github/handlers/get_workflow_run.js +4 -0
  21. package/integrations/github/handlers/list_branches.js +6 -1
  22. package/integrations/github/handlers/list_commits.js +5 -6
  23. package/integrations/github/handlers/list_issue_comments.js +8 -0
  24. package/integrations/github/handlers/list_issues.js +5 -6
  25. package/integrations/github/handlers/list_labels.js +8 -0
  26. package/integrations/github/handlers/list_pull_request_comments.js +8 -0
  27. package/integrations/github/handlers/list_pull_request_files.js +8 -0
  28. package/integrations/github/handlers/list_pull_requests.js +7 -2
  29. package/integrations/github/handlers/list_releases.js +8 -0
  30. package/integrations/github/handlers/list_tags.js +8 -0
  31. package/integrations/github/handlers/list_workflow_runs.js +11 -0
  32. package/integrations/github/handlers/request_pull_request_reviewers.js +10 -0
  33. package/integrations/github/handlers/search_code.js +8 -0
  34. package/integrations/github/handlers/search_issues.js +8 -0
  35. package/integrations/github/handlers/search_pull_requests.js +8 -0
  36. package/integrations/github/handlers/search_repos.js +10 -0
  37. package/integrations/github/handlers/update_pull_request.js +13 -0
  38. package/integrations/github/manifest.json +58 -20
  39. package/integrations/github/schemas/create_pull_request_review.json +17 -0
  40. package/integrations/github/schemas/create_release.json +16 -0
  41. package/integrations/github/schemas/delete_branch.json +10 -0
  42. package/integrations/github/schemas/delete_file.json +13 -0
  43. package/integrations/github/schemas/fork_repo.json +11 -0
  44. package/integrations/github/schemas/get_commit.json +12 -0
  45. package/integrations/github/schemas/get_file_contents.json +11 -0
  46. package/integrations/github/schemas/get_job_logs.json +10 -0
  47. package/integrations/github/schemas/get_pull_request.json +10 -0
  48. package/integrations/github/schemas/get_pull_request_diff.json +10 -0
  49. package/integrations/github/schemas/get_repo_tree.json +12 -0
  50. package/integrations/github/schemas/get_workflow_run.json +10 -0
  51. package/integrations/github/schemas/list_branches.json +12 -0
  52. package/integrations/github/schemas/list_commits.json +5 -3
  53. package/integrations/github/schemas/list_issue_comments.json +12 -0
  54. package/integrations/github/schemas/list_issues.json +4 -2
  55. package/integrations/github/schemas/list_labels.json +11 -0
  56. package/integrations/github/schemas/list_pull_request_comments.json +12 -0
  57. package/integrations/github/schemas/list_pull_request_files.json +12 -0
  58. package/integrations/github/schemas/list_pull_requests.json +7 -1
  59. package/integrations/github/schemas/list_releases.json +11 -0
  60. package/integrations/github/schemas/list_tags.json +11 -0
  61. package/integrations/github/schemas/list_workflow_runs.json +18 -0
  62. package/integrations/github/schemas/request_pull_request_reviewers.json +20 -0
  63. package/integrations/github/schemas/search_code.json +10 -0
  64. package/integrations/github/schemas/search_issues.json +10 -0
  65. package/integrations/github/schemas/search_pull_requests.json +10 -0
  66. package/integrations/github/schemas/search_repos.json +12 -0
  67. package/integrations/github/schemas/update_pull_request.json +15 -0
  68. package/integrations/google-calendar/__tests__/write_and_admin_handlers.test.ts +0 -13
  69. package/integrations/google-calendar/handlers/get_event.js +5 -1
  70. package/integrations/google-calendar/handlers/list_events.js +2 -0
  71. package/integrations/google-calendar/manifest.json +17 -18
  72. package/integrations/google-calendar/prompt.md +68 -0
  73. package/integrations/google-calendar/schemas/id_calendar_event.json +4 -2
  74. package/integrations/google-calendar/schemas/list_events.json +10 -8
  75. package/integrations/google-docs/__tests__/get_handlers.test.ts +4 -19
  76. package/integrations/google-docs/__tests__/write_handlers.test.ts +31 -48
  77. package/integrations/google-docs/handlers/read_document.js +189 -0
  78. package/integrations/google-docs/manifest.json +16 -31
  79. package/integrations/google-docs/prompt.md +49 -0
  80. package/integrations/google-docs/schemas/{get_document_text.json → read_document.json} +5 -2
  81. package/integrations/google-docs/todo.md +18 -0
  82. package/integrations/google-drive/__tests__/handlers.test.ts +43 -0
  83. package/integrations/google-drive/__tests__/usage_parity.test.ts +9 -0
  84. package/integrations/google-drive/handlers/get_file.js +2 -4
  85. package/integrations/google-drive/handlers/get_file_content.js +41 -0
  86. package/integrations/google-drive/handlers/list_files.js +15 -0
  87. package/integrations/google-drive/handlers/search_files.js +20 -0
  88. package/integrations/google-drive/handlers/share_file.js +20 -0
  89. package/integrations/google-drive/manifest.json +37 -10
  90. package/integrations/google-drive/prompt.md +59 -0
  91. package/integrations/google-drive/schemas/get_file.json +2 -2
  92. package/integrations/google-drive/schemas/get_file_content.json +11 -0
  93. package/integrations/google-drive/schemas/list_files.json +12 -0
  94. package/integrations/google-drive/schemas/search_files.json +14 -0
  95. package/integrations/google-drive/schemas/share_file.json +23 -0
  96. package/integrations/google-gmail/__tests__/get_handlers.test.ts +134 -0
  97. package/integrations/google-gmail/__tests__/usage_parity.test.ts +9 -0
  98. package/integrations/google-gmail/__tests__/write_and_admin_handlers.test.ts +211 -0
  99. package/integrations/google-gmail/credentials.json +57 -0
  100. package/integrations/google-gmail/credentials_hint_oauth_token.md +8 -0
  101. package/integrations/google-gmail/credentials_hint_service_account.md +10 -0
  102. package/integrations/google-gmail/handlers/create_draft_email.js +27 -0
  103. package/integrations/google-gmail/handlers/create_label.js +12 -0
  104. package/integrations/google-gmail/handlers/delete_draft.js +13 -0
  105. package/integrations/google-gmail/handlers/delete_label.js +13 -0
  106. package/integrations/google-gmail/handlers/delete_message.js +13 -0
  107. package/integrations/google-gmail/handlers/delete_thread.js +13 -0
  108. package/integrations/google-gmail/handlers/get_draft.js +6 -0
  109. package/integrations/google-gmail/handlers/get_label.js +6 -0
  110. package/integrations/google-gmail/handlers/get_message.js +14 -0
  111. package/integrations/google-gmail/handlers/get_profile.js +5 -0
  112. package/integrations/google-gmail/handlers/get_thread.js +14 -0
  113. package/integrations/google-gmail/handlers/list_drafts.js +15 -0
  114. package/integrations/google-gmail/handlers/list_labels.js +5 -0
  115. package/integrations/google-gmail/handlers/list_messages.js +19 -0
  116. package/integrations/google-gmail/handlers/list_threads.js +19 -0
  117. package/integrations/google-gmail/handlers/modify_message.js +11 -0
  118. package/integrations/google-gmail/handlers/modify_thread.js +11 -0
  119. package/integrations/google-gmail/handlers/read_email.js +56 -0
  120. package/integrations/google-gmail/handlers/send_draft.js +15 -0
  121. package/integrations/google-gmail/handlers/send_email.js +22 -0
  122. package/integrations/google-gmail/handlers/trash_message.js +6 -0
  123. package/integrations/google-gmail/handlers/trash_thread.js +6 -0
  124. package/integrations/google-gmail/handlers/untrash_message.js +6 -0
  125. package/integrations/google-gmail/handlers/untrash_thread.js +6 -0
  126. package/integrations/google-gmail/handlers/update_label.js +15 -0
  127. package/integrations/google-gmail/manifest.json +33 -0
  128. package/integrations/google-gmail/prompt.md +52 -0
  129. package/integrations/google-gmail/schemas/create_draft_email.json +16 -0
  130. package/integrations/google-gmail/schemas/create_label.json +26 -0
  131. package/integrations/google-gmail/schemas/get_message.json +20 -0
  132. package/integrations/{google-docs/schemas/get_document_structured.json → google-gmail/schemas/get_profile.json} +4 -2
  133. package/integrations/google-gmail/schemas/get_thread.json +20 -0
  134. package/integrations/google-gmail/schemas/id_draft.json +16 -0
  135. package/integrations/google-gmail/schemas/id_label.json +16 -0
  136. package/integrations/google-gmail/schemas/id_message.json +16 -0
  137. package/integrations/google-gmail/schemas/id_thread.json +16 -0
  138. package/integrations/google-gmail/schemas/list_drafts.json +30 -0
  139. package/integrations/{google-sheet/schemas/get_developer_metadata.json → google-gmail/schemas/list_labels.json} +4 -3
  140. package/integrations/google-gmail/schemas/list_messages.json +35 -0
  141. package/integrations/google-gmail/schemas/list_threads.json +35 -0
  142. package/integrations/google-gmail/schemas/modify_message.json +24 -0
  143. package/integrations/google-gmail/schemas/modify_thread.json +24 -0
  144. package/integrations/google-gmail/schemas/read_email.json +10 -0
  145. package/integrations/google-gmail/schemas/send_draft.json +29 -0
  146. package/integrations/google-gmail/schemas/send_email.json +17 -0
  147. package/integrations/google-gmail/schemas/update_label.json +33 -0
  148. package/integrations/google-sheet/__tests__/get_handlers.test.ts +6 -52
  149. package/integrations/google-sheet/__tests__/write_handlers.test.ts +0 -20
  150. package/integrations/google-sheet/handlers/get_spreadsheet.js +2 -0
  151. package/integrations/google-sheet/handlers/read_sheet.js +75 -0
  152. package/integrations/google-sheet/manifest.json +13 -62
  153. package/integrations/google-sheet/prompt.md +49 -0
  154. package/integrations/google-sheet/schemas/get_spreadsheet.json +5 -4
  155. package/integrations/google-sheet/schemas/read_sheet.json +21 -0
  156. package/integrations/google-slides/__tests__/get_handlers.test.ts +12 -9
  157. package/integrations/google-slides/handlers/read_presentation.js +51 -0
  158. package/integrations/google-slides/manifest.json +13 -13
  159. package/integrations/google-slides/prompt.md +56 -0
  160. package/integrations/new_integration_prompt.md +5 -1
  161. package/package.json +1 -1
  162. package/integrations/google-calendar/handlers/update_event.js +0 -5
  163. package/integrations/google-calendar/schemas/update_event.json +0 -10
  164. package/integrations/google-docs/handlers/get_document.js +0 -12
  165. package/integrations/google-docs/handlers/get_document_structured.js +0 -6
  166. package/integrations/google-docs/handlers/get_document_text.js +0 -17
  167. package/integrations/google-docs/schemas/get_document.json +0 -11
  168. package/integrations/google-sheet/handlers/batch_clear_values_by_data_filter.js +0 -6
  169. package/integrations/google-sheet/handlers/batch_get_values.js +0 -16
  170. package/integrations/google-sheet/handlers/batch_update_values_by_data_filter.js +0 -16
  171. package/integrations/google-sheet/handlers/get_developer_metadata.js +0 -6
  172. package/integrations/google-sheet/handlers/get_spreadsheet_by_data_filter.js +0 -10
  173. package/integrations/google-sheet/handlers/get_values.js +0 -14
  174. package/integrations/google-sheet/handlers/get_values_by_data_filter.js +0 -14
  175. package/integrations/google-sheet/handlers/search_developer_metadata.js +0 -7
  176. package/integrations/google-sheet/schemas/batch_clear_values_by_data_filter.json +0 -10
  177. package/integrations/google-sheet/schemas/batch_get_values.json +0 -13
  178. package/integrations/google-sheet/schemas/batch_update_values_by_data_filter.json +0 -25
  179. package/integrations/google-sheet/schemas/get_spreadsheet_by_data_filter.json +0 -11
  180. package/integrations/google-sheet/schemas/get_values.json +0 -13
  181. package/integrations/google-sheet/schemas/get_values_by_data_filter.json +0 -17
  182. package/integrations/google-sheet/schemas/search_developer_metadata.json +0 -14
  183. package/integrations/google-slides/handlers/get_presentation.js +0 -6
@@ -0,0 +1,15 @@
1
+ async (input) => {
2
+ const params = new URLSearchParams()
3
+ const qParts = ['trashed = false']
4
+ if (input.folderId)
5
+ qParts.push(`'${input.folderId}' in parents`)
6
+ params.set('q', qParts.join(' and '))
7
+ params.set('fields', input.fields || 'nextPageToken,files(id,name,mimeType,modifiedTime,size,parents)')
8
+ params.set('pageSize', String(input.pageSize || 50))
9
+ if (input.pageToken)
10
+ params.set('pageToken', input.pageToken)
11
+ if (input.orderBy)
12
+ params.set('orderBy', input.orderBy)
13
+ const res = await integration.fetch(`/files?${params.toString()}`)
14
+ return await res.json()
15
+ }
@@ -0,0 +1,20 @@
1
+ async (input) => {
2
+ const params = new URLSearchParams()
3
+ const qParts = []
4
+ if (input.query)
5
+ qParts.push(input.query)
6
+ if (input.name)
7
+ qParts.push(`name contains '${input.name.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`)
8
+ if (input.mimeType)
9
+ qParts.push(`mimeType = '${input.mimeType}'`)
10
+ if (!input.includeTrashed)
11
+ qParts.push('trashed = false')
12
+ if (qParts.length > 0)
13
+ params.set('q', qParts.join(' and '))
14
+ params.set('fields', input.fields || 'nextPageToken,files(id,name,mimeType,modifiedTime,size,parents)')
15
+ params.set('pageSize', String(input.pageSize || 20))
16
+ if (input.pageToken)
17
+ params.set('pageToken', input.pageToken)
18
+ const res = await integration.fetch(`/files?${params.toString()}`)
19
+ return await res.json()
20
+ }
@@ -0,0 +1,20 @@
1
+ async (input) => {
2
+ const fileId = encodeURIComponent(input.fileId)
3
+ const params = new URLSearchParams()
4
+ if (input.sendNotificationEmail !== undefined)
5
+ params.set('sendNotificationEmail', String(input.sendNotificationEmail))
6
+ if (input.emailMessage)
7
+ params.set('emailMessage', input.emailMessage)
8
+ const body = {
9
+ role: input.role,
10
+ type: input.type,
11
+ }
12
+ if (input.emailAddress) body.emailAddress = input.emailAddress
13
+ if (input.domain) body.domain = input.domain
14
+ const qs = params.toString()
15
+ const res = await integration.fetch(`/files/${fileId}/permissions${qs ? `?${qs}` : ''}`, {
16
+ method: 'POST',
17
+ body,
18
+ })
19
+ return await res.json()
20
+ }
@@ -2,41 +2,68 @@
2
2
  "name": "google-drive",
3
3
  "version": "0.1.0",
4
4
  "tools": [
5
+ {
6
+ "name": "list_files",
7
+ "description": "List files in a Google Drive folder or across all of Drive. Returns id, name, mimeType, modifiedTime, size, and parents for each file. Excludes trashed files by default. Use folderId to scope to a specific folder. For name- or type-based search, use search_files instead. Defaults to 50 results per page; use pageToken from the response for the next page.",
8
+ "inputSchema": "schemas/list_files.json",
9
+ "handler": "handlers/list_files.js",
10
+ "scope": "read"
11
+ },
12
+ {
13
+ "name": "search_files",
14
+ "description": "Search Google Drive for files by name, MIME type, or raw Drive query syntax. Use the 'name' shorthand for simple name searches (e.g. name='quarterly report'). Use 'mimeType' to filter by type. Use 'query' for advanced Drive search expressions (e.g. \"modifiedTime > '2024-01-01'\" or \"'folderId' in parents and mimeType='application/pdf'\"). All provided filters are combined with AND. Defaults to 20 results per page.",
15
+ "inputSchema": "schemas/search_files.json",
16
+ "handler": "handlers/search_files.js",
17
+ "scope": "read"
18
+ },
19
+ {
20
+ "name": "get_file",
21
+ "description": "Get metadata for a Drive file or folder by ID. Returns id, name, mimeType, modifiedTime, createdTime, size, parents, trashed status, and webViewLink by default. To read the actual file contents, use get_file_content. To find a file by name, use search_files.",
22
+ "inputSchema": "schemas/get_file.json",
23
+ "handler": "handlers/get_file.js",
24
+ "scope": "read"
25
+ },
26
+ {
27
+ "name": "get_file_content",
28
+ "description": "Read the text content of a Drive file. For Google Workspace files (Docs, Sheets, Slides), automatically exports to text/plain or text/csv. Pass the 'mimeType' from get_file or search_files results to enable automatic export format detection. Use 'exportMimeType' to override the format (e.g. 'text/html' for formatted HTML, 'text/csv' for Sheets data). Returns {fileId, mimeType, content} for text files. Binary files (PDFs, images) return a message suggesting an export format.",
29
+ "inputSchema": "schemas/get_file_content.json",
30
+ "handler": "handlers/get_file_content.js",
31
+ "scope": "read"
32
+ },
5
33
  {
6
34
  "name": "create_folder",
7
- "description": "Create a folder in Google Drive.",
35
+ "description": "Create a new folder in Google Drive. Optionally nest it inside a parent folder using parentId. Returns the created folder's metadata including its ID, which can be used as a parentId for subsequent file creation.",
8
36
  "inputSchema": "schemas/create_folder.json",
9
37
  "handler": "handlers/create_folder.js",
10
38
  "scope": "write"
11
39
  },
12
40
  {
13
41
  "name": "create_file",
14
- "description": "Create a Drive file (including Google Docs/Sheets/Slides) optionally inside a parent folder.",
42
+ "description": "Create a new Drive file (metadata only -- no content upload). To create a Google Workspace file, use the appropriate mimeType: 'application/vnd.google-apps.document' for Docs, 'application/vnd.google-apps.spreadsheet' for Sheets, 'application/vnd.google-apps.presentation' for Slides. Optionally place it in a folder with parentId. Returns the created file's metadata including its ID.",
15
43
  "inputSchema": "schemas/create_file.json",
16
44
  "handler": "handlers/create_file.js",
17
45
  "scope": "write"
18
46
  },
19
47
  {
20
48
  "name": "move_file",
21
- "description": "Move a file to a different parent folder (add/remove parents).",
49
+ "description": "Move a file or folder to a different parent folder by updating its parents. Provide the destination folder ID as addParents and optionally the current parent ID as removeParents (to remove it from the old location). Get the current parents from get_file.",
22
50
  "inputSchema": "schemas/move_file.json",
23
51
  "handler": "handlers/move_file.js",
24
52
  "scope": "write"
25
53
  },
26
54
  {
27
- "name": "get_file",
28
- "description": "Get a Drive file’s metadata by fileId.",
29
- "inputSchema": "schemas/get_file.json",
30
- "handler": "handlers/get_file.js",
31
- "scope": "read"
55
+ "name": "share_file",
56
+ "description": "Share a Drive file or folder with a specific user, group, domain, or make it publicly accessible. Use type='user' with emailAddress for individual sharing. Use type='anyone' with role='reader' to create a public view link. Use type='domain' with domain for organization-wide sharing. Returns the created permission resource.",
57
+ "inputSchema": "schemas/share_file.json",
58
+ "handler": "handlers/share_file.js",
59
+ "scope": "write"
32
60
  },
33
61
  {
34
62
  "name": "delete_file",
35
- "description": "Permanently delete a Drive file or folder by fileId.",
63
+ "description": "Permanently and immediately delete a Drive file or folder by ID. This bypasses Trash and cannot be undone. To move to Trash instead, use the Google Drive UI or modify file properties. Use search_files or list_files to find file IDs.",
36
64
  "inputSchema": "schemas/delete_file.json",
37
65
  "handler": "handlers/delete_file.js",
38
66
  "scope": "write"
39
67
  }
40
68
  ]
41
69
  }
42
-
@@ -0,0 +1,59 @@
1
+ ## File and folder IDs
2
+
3
+ Drive file and folder IDs appear in the URL:
4
+ - Files: `https://drive.google.com/file/d/{fileId}/view`
5
+ - Folders: `https://drive.google.com/drive/folders/{folderId}`
6
+ - Google Docs: `https://docs.google.com/document/d/{documentId}/edit`
7
+
8
+ Use `search_files` or `list_files` to find IDs programmatically.
9
+
10
+ ## MIME types for Google Workspace files
11
+
12
+ When creating files with `create_file`, use these MIME types:
13
+
14
+ | File type | MIME type |
15
+ |---|---|
16
+ | Google Doc | `application/vnd.google-apps.document` |
17
+ | Google Sheet | `application/vnd.google-apps.spreadsheet` |
18
+ | Google Slides | `application/vnd.google-apps.presentation` |
19
+ | Google Forms | `application/vnd.google-apps.form` |
20
+ | Google Folder | `application/vnd.google-apps.folder` |
21
+
22
+ ## Drive search query syntax
23
+
24
+ The `query` parameter in `search_files` uses the Drive search syntax:
25
+
26
+ - `name contains 'budget'` — file name contains string
27
+ - `name = 'exact name'` — exact file name match
28
+ - `mimeType = 'application/pdf'` — filter by MIME type
29
+ - `mimeType != 'application/vnd.google-apps.folder'` — exclude folders
30
+ - `'folderId' in parents` — files in a specific folder
31
+ - `modifiedTime > '2024-01-01T00:00:00'` — modified after date (RFC3339)
32
+ - `trashed = true` — only trashed files
33
+ - `starred = true` — only starred files
34
+ - `sharedWithMe = true` — files shared with the user
35
+
36
+ Combine with `and`, `or`, `not`: `name contains 'report' and mimeType = 'application/pdf' and modifiedTime > '2024-01-01T00:00:00'`
37
+
38
+ ## Reading file contents
39
+
40
+ 1. Use `search_files` or `list_files` to find the file (note its `id` and `mimeType`)
41
+ 2. Call `get_file_content` with `fileId` and `mimeType`
42
+ - For Google Workspace files, the `mimeType` triggers automatic export
43
+ - For text/CSV/JSON files, content is returned directly
44
+
45
+ ## Export formats
46
+
47
+ Google Workspace files must be exported (they have no binary content):
48
+
49
+ | Source | Default export | Alternative exports |
50
+ |---|---|---|
51
+ | Google Docs | `text/plain` | `text/html`, `application/vnd.openxmlformats-officedocument.wordprocessingml.document`, `application/pdf` |
52
+ | Google Sheets | `text/csv` | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`, `application/pdf` |
53
+ | Google Slides | `text/plain` | `application/vnd.openxmlformats-officedocument.presentationml.presentation`, `application/pdf` |
54
+
55
+ ## Sharing files
56
+
57
+ - Individual user: `share_file` with `type='user'`, `role='writer'`, `emailAddress='user@example.com'`
58
+ - Anyone with link: `share_file` with `type='anyone'`, `role='reader'`
59
+ - Whole domain: `share_file` with `type='domain'`, `role='reader'`, `domain='example.com'`
@@ -4,7 +4,7 @@
4
4
  "required": ["fileId"],
5
5
  "additionalProperties": false,
6
6
  "properties": {
7
- "fileId": { "type": "string" }
7
+ "fileId": { "type": "string", "description": "Drive file or folder ID. Use search_files or list_files to find file IDs." },
8
+ "fields": { "type": "string", "description": "Comma-separated list of fields to return. Defaults to 'id,name,mimeType,modifiedTime,createdTime,size,parents,trashed,webViewLink'. See the Drive API fields reference for all available fields." }
8
9
  }
9
10
  }
10
-
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "required": ["fileId"],
5
+ "properties": {
6
+ "fileId": { "type": "string", "description": "Drive file ID. Use search_files or list_files to find file IDs." },
7
+ "mimeType": { "type": "string", "description": "The file's MIME type, obtained from get_file or search_files. Required for Google Workspace files (Docs, Sheets, Slides) to trigger automatic text export. Google Docs files start with 'application/vnd.google-apps.'." },
8
+ "exportMimeType": { "type": "string", "description": "Override the export format. For Google Docs: 'text/plain', 'text/html', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'. For Sheets: 'text/csv', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'. For Slides: 'text/plain', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'. For any: 'application/pdf'." }
9
+ },
10
+ "additionalProperties": false
11
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "folderId": { "type": "string", "description": "Parent folder ID to list contents of. Omit to list files across all of Drive (not scoped to a folder). Use search_files with query=\"'root' in parents\" to list only root-level files." },
6
+ "pageSize": { "type": "integer", "minimum": 1, "maximum": 1000, "default": 50, "description": "Maximum results per page. Defaults to 50. Use pageToken from the response for the next page." },
7
+ "pageToken": { "type": "string", "description": "Page token from a previous list_files response to retrieve the next page of results." },
8
+ "orderBy": { "type": "string", "description": "Sort order. Examples: 'modifiedTime desc', 'name', 'createdTime desc'. Default is unspecified." },
9
+ "fields": { "type": "string", "description": "Override the fields returned in each file. Defaults to 'nextPageToken,files(id,name,mimeType,modifiedTime,size,parents)'." }
10
+ },
11
+ "additionalProperties": false
12
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "query": { "type": "string", "description": "Raw Drive query string using the Drive search syntax. Examples: \"name contains 'budget' and mimeType = 'application/vnd.google-apps.spreadsheet'\", \"modifiedTime > '2024-01-01'\", \"'folderId' in parents\". Combined with name/mimeType shorthand via AND." },
6
+ "name": { "type": "string", "description": "Shorthand for name-based search. Translates to \"name contains 'value'\". Combined with query and mimeType via AND." },
7
+ "mimeType": { "type": "string", "description": "Filter by MIME type. Common values: 'application/vnd.google-apps.document' (Docs), 'application/vnd.google-apps.spreadsheet' (Sheets), 'application/vnd.google-apps.presentation' (Slides), 'application/vnd.google-apps.folder' (folders), 'application/pdf', 'text/plain'." },
8
+ "includeTrashed": { "type": "boolean", "description": "Include trashed files in results. Defaults to false (trashed files excluded)." },
9
+ "pageSize": { "type": "integer", "minimum": 1, "maximum": 1000, "default": 20, "description": "Maximum results per page. Defaults to 20. Use pageToken from the response for the next page." },
10
+ "pageToken": { "type": "string", "description": "Page token from a previous search_files response to retrieve the next page of results." },
11
+ "fields": { "type": "string", "description": "Override the fields returned in each file. Defaults to 'nextPageToken,files(id,name,mimeType,modifiedTime,size,parents)'." }
12
+ },
13
+ "additionalProperties": false
14
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "required": ["fileId", "role", "type"],
5
+ "properties": {
6
+ "fileId": { "type": "string", "description": "Drive file or folder ID to share." },
7
+ "role": {
8
+ "type": "string",
9
+ "enum": ["reader", "commenter", "writer", "organizer", "owner"],
10
+ "description": "Permission level. 'reader' = view only, 'commenter' = view + comment, 'writer' = edit, 'organizer' = organize in shared drives, 'owner' = full ownership transfer."
11
+ },
12
+ "type": {
13
+ "type": "string",
14
+ "enum": ["user", "group", "domain", "anyone"],
15
+ "description": "Grantee type. 'user' requires emailAddress, 'group' requires emailAddress, 'domain' requires domain, 'anyone' grants access to all (use role='reader' for public link)."
16
+ },
17
+ "emailAddress": { "type": "string", "description": "Email address of the user or group to share with. Required when type is 'user' or 'group'." },
18
+ "domain": { "type": "string", "description": "Domain to share with (e.g. 'example.com'). Required when type is 'domain'." },
19
+ "sendNotificationEmail": { "type": "boolean", "description": "Whether to send a notification email to the new recipient. Defaults to true. Set to false to share silently." },
20
+ "emailMessage": { "type": "string", "description": "Custom message to include in the notification email." }
21
+ },
22
+ "additionalProperties": false
23
+ }
@@ -0,0 +1,134 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest'
2
+ import { createCredentialStore, createIntegrationNode, createProxy, createToolbox, safeCleanup } from '../../__tests__/liveHarness.js'
3
+
4
+ const env = process.env as Record<string, string | undefined>
5
+
6
+ interface VariantConfig {
7
+ key: string
8
+ credentials: () => Record<string, string>
9
+ }
10
+
11
+ const variants: VariantConfig[] = [
12
+ {
13
+ key: 'service_account',
14
+ credentials: () => ({ serviceAccountJson: env.GOOGLE_SERVICE_ACCOUNT_JSON || '', subject: env.GOOGLE_IMPERSONATE_SUBJECT || '' }),
15
+ },
16
+ {
17
+ key: 'oauth_token',
18
+ credentials: () => ({ token: env.GOOGLE_TOKEN || '' }),
19
+ },
20
+ ].filter(v => Object.values(v.credentials()).some(val => val.trim().length > 0))
21
+
22
+ const suiteOrSkip = variants.length > 0 ? describe : describe.skip
23
+
24
+ suiteOrSkip('google-gmail read handlers (live)', () => {
25
+ for (const variant of variants) {
26
+ describe(`variant: ${variant.key}`, () => {
27
+ const ctx: { email?: string, labelId?: string, messageId?: string, threadId?: string, draftId?: string } = {}
28
+ let gmail: ReturnType<typeof createToolbox>
29
+
30
+ beforeAll(async () => {
31
+ const credentialStore = createCredentialStore(async () => variant.credentials())
32
+ const proxy = createProxy(credentialStore)
33
+ gmail = createToolbox(
34
+ 'google-gmail',
35
+ proxy,
36
+ createIntegrationNode('google-gmail', { label: 'Google Gmail', credentialId: 'google-gmail-creds', credentialVariant: variant.key }),
37
+ variant.key,
38
+ )
39
+
40
+ const profile = await gmail.read('get_profile')({})
41
+ ctx.email = profile?.emailAddress
42
+
43
+ const labels = await gmail.read('list_labels')({})
44
+ ctx.labelId = labels?.labels?.[0]?.id
45
+
46
+ const listedMessages = await gmail.read('list_messages')({ maxResults: 5 })
47
+ ctx.messageId = listedMessages?.messages?.[0]?.id
48
+ if (ctx.messageId) {
49
+ const msg = await gmail.read('get_message')({ messageId: ctx.messageId, format: 'minimal' })
50
+ ctx.threadId = msg?.threadId
51
+ }
52
+
53
+ if (ctx.email) {
54
+ const draft = await gmail.write('create_draft_email')({
55
+ to: ctx.email,
56
+ subject: `CmdTest Gmail Draft ${Date.now()}`,
57
+ body: 'Draft created by integration live tests.',
58
+ })
59
+ ctx.draftId = draft?.id
60
+ }
61
+ }, 60000)
62
+
63
+ afterAll(async () => {
64
+ await safeCleanup(async () => ctx.draftId ? gmail.write('delete_draft')({ draftId: ctx.draftId }) : Promise.resolve())
65
+ }, 30000)
66
+
67
+ it('get_profile returns mailbox profile', async () => {
68
+ const result = await gmail.read('get_profile')({})
69
+ expect(result?.emailAddress || result?.messagesTotal !== undefined).toBeTruthy()
70
+ }, 30000)
71
+
72
+ it('list_labels returns labels', async () => {
73
+ const result = await gmail.read('list_labels')({})
74
+ expect(Array.isArray(result?.labels)).toBe(true)
75
+ }, 30000)
76
+
77
+ it('get_label returns a label when available', async () => {
78
+ if (!ctx.labelId)
79
+ return expect(true).toBe(true)
80
+ const result = await gmail.read('get_label')({ labelId: ctx.labelId })
81
+ expect(result?.id).toBe(ctx.labelId)
82
+ }, 30000)
83
+
84
+ it('list_messages returns messages list', async () => {
85
+ const result = await gmail.read('list_messages')({ maxResults: 10 })
86
+ expect(result?.resultSizeEstimate !== undefined || Array.isArray(result?.messages)).toBe(true)
87
+ }, 30000)
88
+
89
+ it('get_message returns a message when available', async () => {
90
+ if (!ctx.messageId)
91
+ return expect(true).toBe(true)
92
+ const result = await gmail.read('get_message')({ messageId: ctx.messageId, format: 'full' })
93
+ expect(result?.id).toBe(ctx.messageId)
94
+ }, 30000)
95
+
96
+ it('list_threads returns threads list', async () => {
97
+ const result = await gmail.read('list_threads')({ maxResults: 10 })
98
+ expect(result?.resultSizeEstimate !== undefined || Array.isArray(result?.threads)).toBe(true)
99
+ }, 30000)
100
+
101
+ it('get_thread returns a thread when available', async () => {
102
+ if (!ctx.threadId)
103
+ return expect(true).toBe(true)
104
+ const result = await gmail.read('get_thread')({ threadId: ctx.threadId, format: 'full' })
105
+ expect(result?.id).toBe(ctx.threadId)
106
+ }, 30000)
107
+
108
+ it('list_drafts returns drafts list', async () => {
109
+ const result = await gmail.read('list_drafts')({ maxResults: 10 })
110
+ expect(result?.resultSizeEstimate !== undefined || Array.isArray(result?.drafts)).toBe(true)
111
+ }, 30000)
112
+
113
+ it('get_draft returns draft details when available', async () => {
114
+ if (!ctx.draftId)
115
+ return expect(true).toBe(true)
116
+ const result = await gmail.read('get_draft')({ draftId: ctx.draftId })
117
+ expect(result?.id).toBe(ctx.draftId)
118
+ }, 30000)
119
+
120
+ it('read_email returns flat decoded message when a message is available', async () => {
121
+ if (!ctx.messageId)
122
+ return expect(true).toBe(true)
123
+ const result = await gmail.read('read_email')({ messageId: ctx.messageId })
124
+ expect(result?.id).toBe(ctx.messageId)
125
+ expect(typeof result?.subject).toBe('string')
126
+ expect(typeof result?.from).toBe('string')
127
+ expect(typeof result?.date).toBe('string')
128
+ expect(typeof result?.snippet).toBe('string')
129
+ expect(typeof result?.body).toBe('string')
130
+ expect(Array.isArray(result?.labelIds)).toBe(true)
131
+ }, 30000)
132
+ })
133
+ }
134
+ })
@@ -0,0 +1,9 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { getMissingToolUsages } from '../../__tests__/usageParity.js'
3
+
4
+ describe('google-gmail static usage parity', () => {
5
+ it('every manifest tool is referenced in tests', () => {
6
+ const missing = getMissingToolUsages({ integrationName: 'google-gmail', importMetaUrl: import.meta.url })
7
+ expect(missing, `Missing handler usages in tests: ${missing.join(', ')}`).toEqual([])
8
+ })
9
+ })
@@ -0,0 +1,211 @@
1
+ import { Buffer } from 'node:buffer'
2
+ import { beforeAll, describe, expect, it } from 'vitest'
3
+ import { createCredentialStore, createIntegrationNode, createProxy, createToolbox, safeCleanup } from '../../__tests__/liveHarness.js'
4
+
5
+ const env = process.env as Record<string, string | undefined>
6
+
7
+ interface VariantConfig {
8
+ key: string
9
+ credentials: () => Record<string, string>
10
+ }
11
+
12
+ const variants: VariantConfig[] = [
13
+ {
14
+ key: 'service_account',
15
+ credentials: () => ({ serviceAccountJson: env.GOOGLE_SERVICE_ACCOUNT_JSON || '', subject: env.GOOGLE_IMPERSONATE_SUBJECT || '' }),
16
+ },
17
+ {
18
+ key: 'oauth_token',
19
+ credentials: () => ({ token: env.GOOGLE_TOKEN || '' }),
20
+ },
21
+ ].filter(v => Object.values(v.credentials()).some(val => val.trim().length > 0))
22
+
23
+ const suiteOrSkip = variants.length > 0 ? describe : describe.skip
24
+
25
+ function makeRawMessage(toEmail: string, subject: string, text: string): string {
26
+ const mime = [
27
+ `To: ${toEmail}`,
28
+ `Subject: ${subject}`,
29
+ 'MIME-Version: 1.0',
30
+ 'Content-Type: text/plain; charset=UTF-8',
31
+ '',
32
+ text,
33
+ ].join('\r\n')
34
+ return Buffer.from(mime, 'utf8').toString('base64url')
35
+ }
36
+
37
+ suiteOrSkip('google-gmail write/admin handlers (live)', () => {
38
+ for (const variant of variants) {
39
+ describe(`variant: ${variant.key}`, () => {
40
+ const ctx: { email?: string, inboxLabelId?: string, messageId?: string, threadId?: string } = {}
41
+ let gmail: ReturnType<typeof createToolbox>
42
+
43
+ beforeAll(async () => {
44
+ const credentialStore = createCredentialStore(async () => variant.credentials())
45
+ const proxy = createProxy(credentialStore)
46
+ gmail = createToolbox(
47
+ 'google-gmail',
48
+ proxy,
49
+ createIntegrationNode('google-gmail', { label: 'Google Gmail', credentialId: 'google-gmail-creds', credentialVariant: variant.key }),
50
+ variant.key,
51
+ )
52
+
53
+ const profile = await gmail.read('get_profile')({})
54
+ ctx.email = profile?.emailAddress
55
+
56
+ const labels = await gmail.read('list_labels')({})
57
+ ctx.inboxLabelId = labels?.labels?.find((l: any) => l?.name === 'INBOX')?.id || labels?.labels?.[0]?.id
58
+
59
+ const listedMessages = await gmail.read('list_messages')({ maxResults: 5 })
60
+ ctx.messageId = env.GMAIL_TEST_MESSAGE_ID || listedMessages?.messages?.[0]?.id
61
+ if (ctx.messageId) {
62
+ const msg = await gmail.read('get_message')({ messageId: ctx.messageId, format: 'minimal' })
63
+ ctx.threadId = env.GMAIL_TEST_THREAD_ID || msg?.threadId
64
+ }
65
+ }, 60000)
66
+
67
+ it('create_draft_email -> get_draft -> delete_draft', async () => {
68
+ if (!ctx.email)
69
+ return expect(true).toBe(true)
70
+ const created = await gmail.write('create_draft_email')({
71
+ to: ctx.email,
72
+ subject: `CmdTest Gmail Draft ${Date.now()}`,
73
+ body: 'Draft created by write tests.',
74
+ })
75
+ const draftId = created?.id
76
+ expect(draftId).toBeTruthy()
77
+ const got = await gmail.read('get_draft')({ draftId })
78
+ expect(got?.id).toBe(draftId)
79
+ const deleted = await gmail.write('delete_draft')({ draftId })
80
+ expect(deleted?.success === true || deleted === '').toBe(true)
81
+ }, 60000)
82
+
83
+ it('modify_message -> trash_message -> untrash_message on an existing message', async () => {
84
+ // Create a fresh draft so we own the message — avoids stale/trashed inbox IDs
85
+ const draft = await gmail.write('create_draft_email')({
86
+ to: ctx.email || 'noreply@example.com',
87
+ subject: `CmdTest Trash ${Date.now()}`,
88
+ body: 'Temporary message for trash/untrash test.',
89
+ })
90
+ const messageId = draft?.message?.id
91
+ const draftId = draft?.id
92
+ expect(messageId).toBeTruthy()
93
+ try {
94
+ const modified = await gmail.write('modify_message')({
95
+ messageId,
96
+ addLabelIds: ctx.inboxLabelId ? [ctx.inboxLabelId] : undefined,
97
+ })
98
+ expect(modified?.id).toBe(messageId)
99
+ const trashed = await gmail.write('trash_message')({ messageId })
100
+ expect(trashed?.id).toBe(messageId)
101
+ const untrashed = await gmail.write('untrash_message')({ messageId })
102
+ expect(untrashed?.id).toBe(messageId)
103
+ }
104
+ finally {
105
+ await safeCleanup(async () => draftId ? gmail.write('delete_draft')({ draftId }) : Promise.resolve())
106
+ }
107
+ }, 60000)
108
+
109
+ it('modify_thread -> trash_thread -> untrash_thread on an existing thread', async () => {
110
+ // Create a fresh draft so we own the thread — avoids stale inbox IDs
111
+ const draft = await gmail.write('create_draft_email')({
112
+ to: ctx.email || 'noreply@example.com',
113
+ subject: `CmdTest Thread Trash ${Date.now()}`,
114
+ body: 'Temporary message for thread trash/untrash test.',
115
+ })
116
+ const threadId = draft?.message?.threadId
117
+ const draftId = draft?.id
118
+ expect(threadId).toBeTruthy()
119
+ try {
120
+ const modified = await gmail.write('modify_thread')({
121
+ threadId,
122
+ addLabelIds: ctx.inboxLabelId ? [ctx.inboxLabelId] : undefined,
123
+ })
124
+ expect(modified?.id).toBe(threadId)
125
+ const trashed = await gmail.write('trash_thread')({ threadId })
126
+ expect(trashed?.id).toBe(threadId)
127
+ const untrashed = await gmail.write('untrash_thread')({ threadId })
128
+ expect(untrashed?.id).toBe(threadId)
129
+ }
130
+ finally {
131
+ await safeCleanup(async () => draftId ? gmail.write('delete_draft')({ draftId }) : Promise.resolve())
132
+ }
133
+ }, 60000)
134
+
135
+ it('create_label -> update_label -> delete_label', async () => {
136
+ const created = await gmail.admin('create_label')({
137
+ name: `CmdTest Label ${Date.now()}`,
138
+ labelListVisibility: 'labelShow',
139
+ messageListVisibility: 'show',
140
+ })
141
+ const labelId = created?.id
142
+ expect(labelId).toBeTruthy()
143
+ const updated = await gmail.admin('update_label')({
144
+ labelId,
145
+ name: `CmdTest Label Updated ${Date.now()}`,
146
+ labelListVisibility: 'labelHide',
147
+ })
148
+ expect(updated?.id).toBe(labelId)
149
+ const deleted = await gmail.admin('delete_label')({ labelId })
150
+ expect(deleted?.success === true || deleted === '').toBe(true)
151
+ }, 60000)
152
+
153
+ it('send_email sends mail when GMAIL_TEST_SEND_TO is set', async () => {
154
+ const to = env.GMAIL_TEST_SEND_TO
155
+ if (!to)
156
+ return expect(true).toBe(true)
157
+ const sent = await gmail.write('send_email')({
158
+ to,
159
+ subject: `CmdTest Gmail send_email ${Date.now()}`,
160
+ body: 'Message sent by integration live test via send_email.',
161
+ })
162
+ expect(sent?.id).toBeTruthy()
163
+ }, 60000)
164
+
165
+ it('send_draft sends mail when GMAIL_TEST_SEND_TO is set', async () => {
166
+ const to = env.GMAIL_TEST_SEND_TO
167
+ if (!to)
168
+ return expect(true).toBe(true)
169
+ const created = await gmail.write('create_draft_email')({
170
+ to,
171
+ subject: `CmdTest Gmail send_draft ${Date.now()}`,
172
+ body: 'Draft sent by integration live test.',
173
+ })
174
+ const draftId = created?.id
175
+ expect(draftId).toBeTruthy()
176
+ const sent = await gmail.write('send_draft')({ draftId })
177
+ expect(sent?.id || sent?.threadId).toBeTruthy()
178
+ }, 60000)
179
+
180
+ it('delete_message deletes message when GMAIL_TEST_DELETE_MESSAGE_ID is set', async () => {
181
+ const deleteMessageId = env.GMAIL_TEST_DELETE_MESSAGE_ID
182
+ if (!deleteMessageId)
183
+ return expect(true).toBe(true)
184
+ const deleted = await gmail.write('delete_message')({ messageId: deleteMessageId })
185
+ expect(deleted?.success === true || deleted === '').toBe(true)
186
+ }, 60000)
187
+
188
+ it('delete_thread deletes thread when GMAIL_TEST_DELETE_THREAD_ID is set', async () => {
189
+ const deleteThreadId = env.GMAIL_TEST_DELETE_THREAD_ID
190
+ if (!deleteThreadId)
191
+ return expect(true).toBe(true)
192
+ const deleted = await gmail.write('delete_thread')({ threadId: deleteThreadId })
193
+ expect(deleted?.success === true || deleted === '').toBe(true)
194
+ }, 60000)
195
+
196
+ it('send_draft supports raw payload mode', async () => {
197
+ const to = env.GMAIL_TEST_SEND_TO
198
+ if (!to)
199
+ return expect(true).toBe(true)
200
+ const raw = makeRawMessage(to, `CmdTest Gmail send_draft raw ${Date.now()}`, 'Raw payload draft-send mode.')
201
+ const sent = await gmail.write('send_draft')({ raw })
202
+ expect(sent?.id || sent?.threadId).toBeTruthy()
203
+ }, 60000)
204
+
205
+ it('cleanup helper remains available for optional future cleanup', async () => {
206
+ await safeCleanup(async () => Promise.resolve())
207
+ expect(true).toBe(true)
208
+ })
209
+ })
210
+ }
211
+ })