@commandable/integration-data 0.0.1

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 (335) hide show
  1. package/dist/credentials-index.d.ts +30 -0
  2. package/dist/credentials-index.d.ts.map +1 -0
  3. package/dist/credentials-index.js +292 -0
  4. package/dist/credentials-index.js.map +1 -0
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +2 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/loader.d.ts +69 -0
  10. package/dist/loader.d.ts.map +1 -0
  11. package/dist/loader.js +164 -0
  12. package/dist/loader.js.map +1 -0
  13. package/integrations/README.md +52 -0
  14. package/integrations/airtable/__tests__/get_handlers.test.ts +137 -0
  15. package/integrations/airtable/__tests__/usage_parity.test.ts +35 -0
  16. package/integrations/airtable/__tests__/write_and_admin_handlers.test.ts +113 -0
  17. package/integrations/airtable/credentials.json +20 -0
  18. package/integrations/airtable/credentials_hint.md +4 -0
  19. package/integrations/airtable/handlers/create_record.js +12 -0
  20. package/integrations/airtable/handlers/delete_record.js +7 -0
  21. package/integrations/airtable/handlers/get_record.js +4 -0
  22. package/integrations/airtable/handlers/get_table_schema.js +6 -0
  23. package/integrations/airtable/handlers/list_bases.js +4 -0
  24. package/integrations/airtable/handlers/list_records.js +25 -0
  25. package/integrations/airtable/handlers/list_table_fields.js +6 -0
  26. package/integrations/airtable/handlers/list_tables.js +4 -0
  27. package/integrations/airtable/handlers/list_views.js +6 -0
  28. package/integrations/airtable/handlers/search_records.js +6 -0
  29. package/integrations/airtable/handlers/update_record.js +11 -0
  30. package/integrations/airtable/manifest.json +83 -0
  31. package/integrations/airtable/schemas/create_record.json +12 -0
  32. package/integrations/airtable/schemas/delete_record.json +11 -0
  33. package/integrations/airtable/schemas/empty.json +6 -0
  34. package/integrations/airtable/schemas/get_record.json +11 -0
  35. package/integrations/airtable/schemas/id_base.json +9 -0
  36. package/integrations/airtable/schemas/id_base_table.json +10 -0
  37. package/integrations/airtable/schemas/list_records.json +26 -0
  38. package/integrations/airtable/schemas/search_records.json +12 -0
  39. package/integrations/airtable/schemas/update_record.json +13 -0
  40. package/integrations/github/__tests__/get_handlers.test.ts +117 -0
  41. package/integrations/github/__tests__/usage_parity.test.ts +34 -0
  42. package/integrations/github/__tests__/write_handlers.test.ts +311 -0
  43. package/integrations/github/credentials.json +20 -0
  44. package/integrations/github/credentials_hint.md +7 -0
  45. package/integrations/github/handlers/add_labels_to_issue.js +12 -0
  46. package/integrations/github/handlers/close_issue.js +5 -0
  47. package/integrations/github/handlers/comment_on_issue.js +5 -0
  48. package/integrations/github/handlers/create_branch.js +33 -0
  49. package/integrations/github/handlers/create_commit.js +91 -0
  50. package/integrations/github/handlers/create_issue.js +10 -0
  51. package/integrations/github/handlers/create_or_update_file.js +21 -0
  52. package/integrations/github/handlers/create_pull_request.js +16 -0
  53. package/integrations/github/handlers/create_repo.js +11 -0
  54. package/integrations/github/handlers/delete_repo.js +6 -0
  55. package/integrations/github/handlers/get_issue.js +4 -0
  56. package/integrations/github/handlers/get_repo.js +4 -0
  57. package/integrations/github/handlers/list_branches.js +4 -0
  58. package/integrations/github/handlers/list_commits.js +12 -0
  59. package/integrations/github/handlers/list_issues.js +12 -0
  60. package/integrations/github/handlers/list_pull_requests.js +8 -0
  61. package/integrations/github/handlers/list_repos_install.js +4 -0
  62. package/integrations/github/handlers/list_repos_user.js +4 -0
  63. package/integrations/github/handlers/merge_pull_request.js +14 -0
  64. package/integrations/github/handlers/update_issue.js +15 -0
  65. package/integrations/github/manifest.json +26 -0
  66. package/integrations/github/schemas/add_labels_to_issue.json +12 -0
  67. package/integrations/github/schemas/close_issue.json +10 -0
  68. package/integrations/github/schemas/comment_on_issue.json +11 -0
  69. package/integrations/github/schemas/create_branch.json +12 -0
  70. package/integrations/github/schemas/create_commit.json +25 -0
  71. package/integrations/github/schemas/create_issue.json +13 -0
  72. package/integrations/github/schemas/create_or_update_file.json +15 -0
  73. package/integrations/github/schemas/create_pull_request.json +15 -0
  74. package/integrations/github/schemas/create_repo.json +12 -0
  75. package/integrations/github/schemas/delete_repo.json +10 -0
  76. package/integrations/github/schemas/empty.json +5 -0
  77. package/integrations/github/schemas/get_issue.json +10 -0
  78. package/integrations/github/schemas/get_repo.json +9 -0
  79. package/integrations/github/schemas/list_commits.json +12 -0
  80. package/integrations/github/schemas/list_issues.json +12 -0
  81. package/integrations/github/schemas/list_pull_requests.json +10 -0
  82. package/integrations/github/schemas/merge_pull_request.json +14 -0
  83. package/integrations/github/schemas/owner_repo.json +9 -0
  84. package/integrations/github/schemas/update_issue.json +15 -0
  85. package/integrations/google-calendar/__tests__/get_handlers.test.ts +112 -0
  86. package/integrations/google-calendar/__tests__/usage_parity.test.ts +34 -0
  87. package/integrations/google-calendar/__tests__/write_and_admin_handlers.test.ts +161 -0
  88. package/integrations/google-calendar/credentials.json +36 -0
  89. package/integrations/google-calendar/credentials_hint.md +9 -0
  90. package/integrations/google-calendar/handlers/create_event.js +6 -0
  91. package/integrations/google-calendar/handlers/delete_acl.js +6 -0
  92. package/integrations/google-calendar/handlers/delete_event.js +7 -0
  93. package/integrations/google-calendar/handlers/freebusy_query.js +4 -0
  94. package/integrations/google-calendar/handlers/get_acl.js +5 -0
  95. package/integrations/google-calendar/handlers/get_calendar.js +4 -0
  96. package/integrations/google-calendar/handlers/get_event.js +5 -0
  97. package/integrations/google-calendar/handlers/insert_acl.js +6 -0
  98. package/integrations/google-calendar/handlers/list_acl.js +5 -0
  99. package/integrations/google-calendar/handlers/list_calendars.js +4 -0
  100. package/integrations/google-calendar/handlers/list_colors.js +4 -0
  101. package/integrations/google-calendar/handlers/list_events.js +21 -0
  102. package/integrations/google-calendar/handlers/list_settings.js +4 -0
  103. package/integrations/google-calendar/handlers/move_event.js +6 -0
  104. package/integrations/google-calendar/handlers/patch_event.js +5 -0
  105. package/integrations/google-calendar/handlers/quick_add.js +6 -0
  106. package/integrations/google-calendar/handlers/update_acl.js +7 -0
  107. package/integrations/google-calendar/handlers/update_event.js +5 -0
  108. package/integrations/google-calendar/manifest.json +26 -0
  109. package/integrations/google-calendar/schemas/create_event.json +34 -0
  110. package/integrations/google-calendar/schemas/delete_acl.json +9 -0
  111. package/integrations/google-calendar/schemas/empty.json +1 -0
  112. package/integrations/google-calendar/schemas/freebusy_query.json +13 -0
  113. package/integrations/google-calendar/schemas/get_acl.json +9 -0
  114. package/integrations/google-calendar/schemas/id_calendar.json +8 -0
  115. package/integrations/google-calendar/schemas/id_calendar_event.json +9 -0
  116. package/integrations/google-calendar/schemas/insert_acl.json +18 -0
  117. package/integrations/google-calendar/schemas/list_events.json +15 -0
  118. package/integrations/google-calendar/schemas/move_event.json +10 -0
  119. package/integrations/google-calendar/schemas/patch_event.json +10 -0
  120. package/integrations/google-calendar/schemas/quick_add.json +9 -0
  121. package/integrations/google-calendar/schemas/update_acl.json +10 -0
  122. package/integrations/google-calendar/schemas/update_event.json +10 -0
  123. package/integrations/google-docs/__tests__/get_handlers.test.ts +72 -0
  124. package/integrations/google-docs/__tests__/usage_parity.test.ts +34 -0
  125. package/integrations/google-docs/__tests__/write_handlers.test.ts +249 -0
  126. package/integrations/google-docs/credentials.json +36 -0
  127. package/integrations/google-docs/credentials_hint.md +9 -0
  128. package/integrations/google-docs/handlers/append_text.js +12 -0
  129. package/integrations/google-docs/handlers/batch_update.js +13 -0
  130. package/integrations/google-docs/handlers/create_document.js +9 -0
  131. package/integrations/google-docs/handlers/delete_first_match.js +50 -0
  132. package/integrations/google-docs/handlers/get_document.js +12 -0
  133. package/integrations/google-docs/handlers/get_document_structured.js +6 -0
  134. package/integrations/google-docs/handlers/get_document_text.js +17 -0
  135. package/integrations/google-docs/handlers/insert_inline_image_after_first_match.js +41 -0
  136. package/integrations/google-docs/handlers/insert_page_break_after_first_match.js +49 -0
  137. package/integrations/google-docs/handlers/insert_table_after_first_match.js +49 -0
  138. package/integrations/google-docs/handlers/insert_text_after_first_match.js +51 -0
  139. package/integrations/google-docs/handlers/replace_all_text.js +8 -0
  140. package/integrations/google-docs/handlers/style_first_match.js +42 -0
  141. package/integrations/google-docs/handlers/update_document_style.js +8 -0
  142. package/integrations/google-docs/handlers/update_paragraph_style_for_first_match.js +48 -0
  143. package/integrations/google-docs/manifest.json +58 -0
  144. package/integrations/google-docs/schemas/append_text.json +10 -0
  145. package/integrations/google-docs/schemas/apply_text_style.json +13 -0
  146. package/integrations/google-docs/schemas/batch_update.json +16 -0
  147. package/integrations/google-docs/schemas/create_document.json +8 -0
  148. package/integrations/google-docs/schemas/delete_content_range.json +11 -0
  149. package/integrations/google-docs/schemas/delete_first_match.json +10 -0
  150. package/integrations/google-docs/schemas/get_document.json +11 -0
  151. package/integrations/google-docs/schemas/get_document_structured.json +9 -0
  152. package/integrations/google-docs/schemas/get_document_text.json +9 -0
  153. package/integrations/google-docs/schemas/insert_inline_image.json +12 -0
  154. package/integrations/google-docs/schemas/insert_inline_image_after_first_match.json +13 -0
  155. package/integrations/google-docs/schemas/insert_page_break.json +10 -0
  156. package/integrations/google-docs/schemas/insert_page_break_after_first_match.json +11 -0
  157. package/integrations/google-docs/schemas/insert_table.json +12 -0
  158. package/integrations/google-docs/schemas/insert_table_after_first_match.json +13 -0
  159. package/integrations/google-docs/schemas/insert_text_after_first_match.json +12 -0
  160. package/integrations/google-docs/schemas/insert_text_at.json +11 -0
  161. package/integrations/google-docs/schemas/replace_all_text.json +12 -0
  162. package/integrations/google-docs/schemas/style_first_match.json +12 -0
  163. package/integrations/google-docs/schemas/update_document_style.json +11 -0
  164. package/integrations/google-docs/schemas/update_paragraph_style.json +13 -0
  165. package/integrations/google-docs/schemas/update_paragraph_style_for_first_match.json +12 -0
  166. package/integrations/google-sheet/__tests__/get_handlers.test.ts +129 -0
  167. package/integrations/google-sheet/__tests__/usage_parity.test.ts +35 -0
  168. package/integrations/google-sheet/__tests__/write_handlers.test.ts +171 -0
  169. package/integrations/google-sheet/credentials.json +36 -0
  170. package/integrations/google-sheet/credentials_hint.md +9 -0
  171. package/integrations/google-sheet/handlers/append_values.js +18 -0
  172. package/integrations/google-sheet/handlers/batch_clear_values.js +6 -0
  173. package/integrations/google-sheet/handlers/batch_clear_values_by_data_filter.js +6 -0
  174. package/integrations/google-sheet/handlers/batch_get_values.js +16 -0
  175. package/integrations/google-sheet/handlers/batch_update.js +14 -0
  176. package/integrations/google-sheet/handlers/batch_update_values.js +16 -0
  177. package/integrations/google-sheet/handlers/batch_update_values_by_data_filter.js +16 -0
  178. package/integrations/google-sheet/handlers/clear_values.js +6 -0
  179. package/integrations/google-sheet/handlers/copy_to_spreadsheet.js +6 -0
  180. package/integrations/google-sheet/handlers/create_spreadsheet.js +5 -0
  181. package/integrations/google-sheet/handlers/get_developer_metadata.js +6 -0
  182. package/integrations/google-sheet/handlers/get_spreadsheet.js +12 -0
  183. package/integrations/google-sheet/handlers/get_spreadsheet_by_data_filter.js +10 -0
  184. package/integrations/google-sheet/handlers/get_values.js +14 -0
  185. package/integrations/google-sheet/handlers/get_values_by_data_filter.js +14 -0
  186. package/integrations/google-sheet/handlers/search_developer_metadata.js +7 -0
  187. package/integrations/google-sheet/handlers/update_values.js +16 -0
  188. package/integrations/google-sheet/manifest.json +125 -0
  189. package/integrations/google-sheet/schemas/append_values.json +16 -0
  190. package/integrations/google-sheet/schemas/batch_clear_values.json +10 -0
  191. package/integrations/google-sheet/schemas/batch_clear_values_by_data_filter.json +10 -0
  192. package/integrations/google-sheet/schemas/batch_get_values.json +13 -0
  193. package/integrations/google-sheet/schemas/batch_update.json +13 -0
  194. package/integrations/google-sheet/schemas/batch_update_values.json +25 -0
  195. package/integrations/google-sheet/schemas/batch_update_values_by_data_filter.json +25 -0
  196. package/integrations/google-sheet/schemas/clear_values.json +10 -0
  197. package/integrations/google-sheet/schemas/copy_to_spreadsheet.json +11 -0
  198. package/integrations/google-sheet/schemas/create_spreadsheet.json +11 -0
  199. package/integrations/google-sheet/schemas/get_developer_metadata.json +10 -0
  200. package/integrations/google-sheet/schemas/get_spreadsheet.json +15 -0
  201. package/integrations/google-sheet/schemas/get_spreadsheet_by_data_filter.json +11 -0
  202. package/integrations/google-sheet/schemas/get_values.json +13 -0
  203. package/integrations/google-sheet/schemas/get_values_by_data_filter.json +17 -0
  204. package/integrations/google-sheet/schemas/search_developer_metadata.json +14 -0
  205. package/integrations/google-sheet/schemas/update_values.json +15 -0
  206. package/integrations/google-slides/__tests__/get_handlers.test.ts +69 -0
  207. package/integrations/google-slides/__tests__/usage_parity.test.ts +34 -0
  208. package/integrations/google-slides/__tests__/write_handlers.test.ts +125 -0
  209. package/integrations/google-slides/credentials.json +36 -0
  210. package/integrations/google-slides/credentials_hint.md +9 -0
  211. package/integrations/google-slides/handlers/append_text_to_title_of_first_slide.js +17 -0
  212. package/integrations/google-slides/handlers/batch_update.js +15 -0
  213. package/integrations/google-slides/handlers/create_presentation.js +8 -0
  214. package/integrations/google-slides/handlers/create_slide_after_first_match.js +20 -0
  215. package/integrations/google-slides/handlers/get_page_thumbnail.js +12 -0
  216. package/integrations/google-slides/handlers/get_presentation.js +6 -0
  217. package/integrations/google-slides/handlers/insert_image_after_first_match.js +19 -0
  218. package/integrations/google-slides/handlers/insert_shape_after_first_match.js +21 -0
  219. package/integrations/google-slides/handlers/replace_text_first_match.js +9 -0
  220. package/integrations/google-slides/handlers/set_background_color_for_slide_index.js +15 -0
  221. package/integrations/google-slides/handlers/style_text_first_match.js +48 -0
  222. package/integrations/google-slides/manifest.json +41 -0
  223. package/integrations/google-slides/schemas/append_text_to_title_of_first_slide.json +11 -0
  224. package/integrations/google-slides/schemas/batch_update.json +13 -0
  225. package/integrations/google-slides/schemas/create_presentation.json +8 -0
  226. package/integrations/google-slides/schemas/create_slide_after_first_match.json +11 -0
  227. package/integrations/google-slides/schemas/get_page_thumbnail.json +12 -0
  228. package/integrations/google-slides/schemas/get_presentation.json +9 -0
  229. package/integrations/google-slides/schemas/insert_image_after_first_match.json +13 -0
  230. package/integrations/google-slides/schemas/insert_shape_after_first_match.json +13 -0
  231. package/integrations/google-slides/schemas/replace_text_first_match.json +12 -0
  232. package/integrations/google-slides/schemas/set_background_color_for_slide_index.json +11 -0
  233. package/integrations/google-slides/schemas/style_text_first_match.json +12 -0
  234. package/integrations/new_integration_prompt.md +41 -0
  235. package/integrations/notion/__tests__/get_handlers.test.ts +153 -0
  236. package/integrations/notion/__tests__/usage_parity.test.ts +34 -0
  237. package/integrations/notion/__tests__/write_and_admin_handlers.test.ts +190 -0
  238. package/integrations/notion/credentials.json +21 -0
  239. package/integrations/notion/credentials_hint.md +5 -0
  240. package/integrations/notion/handlers/append_block_children.js +7 -0
  241. package/integrations/notion/handlers/create_comment.js +10 -0
  242. package/integrations/notion/handlers/create_database.js +11 -0
  243. package/integrations/notion/handlers/create_page.js +13 -0
  244. package/integrations/notion/handlers/delete_block.js +8 -0
  245. package/integrations/notion/handlers/get_me.js +4 -0
  246. package/integrations/notion/handlers/list_block_children.js +10 -0
  247. package/integrations/notion/handlers/list_comments.js +14 -0
  248. package/integrations/notion/handlers/list_users.js +10 -0
  249. package/integrations/notion/handlers/query_database.js +10 -0
  250. package/integrations/notion/handlers/retrieve_block.js +4 -0
  251. package/integrations/notion/handlers/retrieve_database.js +4 -0
  252. package/integrations/notion/handlers/retrieve_page.js +4 -0
  253. package/integrations/notion/handlers/retrieve_page_property_item.js +10 -0
  254. package/integrations/notion/handlers/retrieve_user.js +4 -0
  255. package/integrations/notion/handlers/search.js +11 -0
  256. package/integrations/notion/handlers/update_block.js +7 -0
  257. package/integrations/notion/handlers/update_database.js +10 -0
  258. package/integrations/notion/handlers/update_page_properties.js +10 -0
  259. package/integrations/notion/manifest.json +139 -0
  260. package/integrations/notion/prompt.md +26 -0
  261. package/integrations/notion/schemas/append_block_children.json +10 -0
  262. package/integrations/notion/schemas/create_comment.json +18 -0
  263. package/integrations/notion/schemas/create_database.json +18 -0
  264. package/integrations/notion/schemas/create_page.json +22 -0
  265. package/integrations/notion/schemas/delete_block.json +9 -0
  266. package/integrations/notion/schemas/empty.json +6 -0
  267. package/integrations/notion/schemas/id_block.json +9 -0
  268. package/integrations/notion/schemas/id_database.json +9 -0
  269. package/integrations/notion/schemas/id_page.json +9 -0
  270. package/integrations/notion/schemas/id_user.json +9 -0
  271. package/integrations/notion/schemas/list_block_children.json +11 -0
  272. package/integrations/notion/schemas/list_comments.json +15 -0
  273. package/integrations/notion/schemas/list_users.json +9 -0
  274. package/integrations/notion/schemas/query_database.json +13 -0
  275. package/integrations/notion/schemas/retrieve_page_property_item.json +12 -0
  276. package/integrations/notion/schemas/search.json +27 -0
  277. package/integrations/notion/schemas/update_block.json +10 -0
  278. package/integrations/notion/schemas/update_database.json +13 -0
  279. package/integrations/notion/schemas/update_page_properties.json +13 -0
  280. package/integrations/trello/__tests__/get_handlers.test.ts +240 -0
  281. package/integrations/trello/__tests__/usage_parity.test.ts +34 -0
  282. package/integrations/trello/__tests__/write_and_admin_handlers.test.ts +189 -0
  283. package/integrations/trello/credentials.json +26 -0
  284. package/integrations/trello/credentials_hint.md +4 -0
  285. package/integrations/trello/handlers/add_checklist_to_card.js +5 -0
  286. package/integrations/trello/handlers/add_member_to_card.js +5 -0
  287. package/integrations/trello/handlers/archive_list.js +5 -0
  288. package/integrations/trello/handlers/create_card.js +13 -0
  289. package/integrations/trello/handlers/create_list.js +7 -0
  290. package/integrations/trello/handlers/delete_card.js +9 -0
  291. package/integrations/trello/handlers/get_board.js +4 -0
  292. package/integrations/trello/handlers/get_board_cards.js +4 -0
  293. package/integrations/trello/handlers/get_board_custom_fields.js +4 -0
  294. package/integrations/trello/handlers/get_board_labels.js +4 -0
  295. package/integrations/trello/handlers/get_board_lists.js +4 -0
  296. package/integrations/trello/handlers/get_board_members.js +4 -0
  297. package/integrations/trello/handlers/get_board_memberships.js +4 -0
  298. package/integrations/trello/handlers/get_card.js +4 -0
  299. package/integrations/trello/handlers/get_card_actions.js +4 -0
  300. package/integrations/trello/handlers/get_card_attachments.js +4 -0
  301. package/integrations/trello/handlers/get_card_checklists.js +4 -0
  302. package/integrations/trello/handlers/get_card_custom_field_items.js +4 -0
  303. package/integrations/trello/handlers/get_card_members.js +4 -0
  304. package/integrations/trello/handlers/get_list.js +4 -0
  305. package/integrations/trello/handlers/get_list_cards.js +4 -0
  306. package/integrations/trello/handlers/get_member.js +4 -0
  307. package/integrations/trello/handlers/get_member_boards.js +4 -0
  308. package/integrations/trello/handlers/get_member_organizations.js +4 -0
  309. package/integrations/trello/handlers/get_organization.js +4 -0
  310. package/integrations/trello/handlers/get_organization_boards.js +4 -0
  311. package/integrations/trello/handlers/move_card_to_list.js +5 -0
  312. package/integrations/trello/handlers/remove_member_from_card.js +9 -0
  313. package/integrations/trello/handlers/search.js +5 -0
  314. package/integrations/trello/handlers/update_card.js +19 -0
  315. package/integrations/trello/handlers/update_list.js +11 -0
  316. package/integrations/trello/manifest.json +231 -0
  317. package/integrations/trello/schemas/add_checklist_to_card.json +10 -0
  318. package/integrations/trello/schemas/add_member_to_card.json +10 -0
  319. package/integrations/trello/schemas/archive_list.json +9 -0
  320. package/integrations/trello/schemas/create_card.json +13 -0
  321. package/integrations/trello/schemas/create_list.json +11 -0
  322. package/integrations/trello/schemas/delete_card.json +9 -0
  323. package/integrations/trello/schemas/display_trello_cards.json +45 -0
  324. package/integrations/trello/schemas/empty.json +5 -0
  325. package/integrations/trello/schemas/get_member.json +5 -0
  326. package/integrations/trello/schemas/id_board.json +8 -0
  327. package/integrations/trello/schemas/id_card.json +8 -0
  328. package/integrations/trello/schemas/id_list.json +8 -0
  329. package/integrations/trello/schemas/id_org.json +8 -0
  330. package/integrations/trello/schemas/move_card_to_list.json +10 -0
  331. package/integrations/trello/schemas/remove_member_from_card.json +10 -0
  332. package/integrations/trello/schemas/search.json +8 -0
  333. package/integrations/trello/schemas/update_card.json +16 -0
  334. package/integrations/trello/schemas/update_list.json +12 -0
  335. package/package.json +45 -0
@@ -0,0 +1,36 @@
1
+ {
2
+ "schema": {
3
+ "type": "object",
4
+ "properties": {
5
+ "token": {
6
+ "type": "string",
7
+ "title": "OAuth Access Token (optional)",
8
+ "description": "Google OAuth access token to use as a Bearer token (typically short-lived)."
9
+ },
10
+ "serviceAccountJson": {
11
+ "type": "string",
12
+ "title": "Service Account JSON (recommended)",
13
+ "description": "Full service account key JSON (contents of the downloaded JSON file)."
14
+ },
15
+ "subject": {
16
+ "type": "string",
17
+ "title": "Subject / impersonated user (optional)",
18
+ "description": "Optional user email to impersonate when using Google Workspace domain-wide delegation."
19
+ },
20
+ "scopes": {
21
+ "type": "array",
22
+ "title": "OAuth scopes (optional)",
23
+ "description": "Optional override for OAuth scopes.",
24
+ "items": { "type": "string" }
25
+ }
26
+ },
27
+ "required": [],
28
+ "additionalProperties": false
29
+ },
30
+ "injection": {
31
+ "headers": {
32
+ "Authorization": "Bearer {{token}}"
33
+ }
34
+ }
35
+ }
36
+
@@ -0,0 +1,9 @@
1
+ Recommended: use a Google service account.
2
+
3
+ - Create a service account in Google Cloud
4
+ - Download a JSON key
5
+ - Paste the JSON into `serviceAccountJson` (or use `env:GOOGLE_SERVICE_ACCOUNT_JSON`)
6
+ - Share your test presentation with the service account `client_email`
7
+
8
+ You can also paste a short-lived OAuth access token into `token`, but it will expire.
9
+
@@ -0,0 +1,17 @@
1
+ async (input) => {
2
+ const { presentationId, text, slideIndex = 0 } = input
3
+ const presRes = await integration.fetch(`/presentations/${encodeURIComponent(presentationId)}`)
4
+ const pres = await presRes.json()
5
+ const slide = (pres?.slides || [])[slideIndex]
6
+ if (!slide)
7
+ return { presentationId, applied: false, replies: [] }
8
+ const titleShape = (slide.pageElements || []).find(el => el.shape?.placeholder?.type === 'TITLE')
9
+ if (!titleShape?.objectId)
10
+ return { presentationId, applied: false, replies: [] }
11
+ const res = await integration.fetch(`/presentations/${encodeURIComponent(presentationId)}:batchUpdate`, {
12
+ method: 'POST',
13
+ body: { requests: [{ insertText: { objectId: titleShape.objectId, insertionIndex: -1, text } }] },
14
+ })
15
+ const out = await res.json()
16
+ return out?.presentationId ? { ...out, applied: true } : { presentationId, applied: true, replies: out?.replies || [] }
17
+ }
@@ -0,0 +1,15 @@
1
+ async (input) => {
2
+ const { presentationId, requests, includePresentationInResponse, responsePageObjectIds, writeControl } = input
3
+ const params = new URLSearchParams()
4
+ if (includePresentationInResponse !== undefined)
5
+ params.set('includePresentationInResponse', String(includePresentationInResponse))
6
+ if (Array.isArray(responsePageObjectIds))
7
+ responsePageObjectIds.forEach(id => params.append('responsePageObjectIds', String(id)))
8
+ const qs = params.toString()
9
+ const path = `/presentations/${encodeURIComponent(presentationId)}:batchUpdate${qs ? `?${qs}` : ''}`
10
+ const body = { requests }
11
+ if (writeControl)
12
+ body.writeControl = writeControl
13
+ const res = await integration.fetch(path, { method: 'POST', body })
14
+ return await res.json()
15
+ }
@@ -0,0 +1,8 @@
1
+ async (input) => {
2
+ const body = {}
3
+ if (input && typeof input.title === 'string') {
4
+ body.title = input.title
5
+ }
6
+ const res = await integration.fetch('/presentations', { method: 'POST', body })
7
+ return await res.json()
8
+ }
@@ -0,0 +1,20 @@
1
+ async (input) => {
2
+ const { presentationId, findText, layout = 'BLANK' } = input
3
+ const presRes = await integration.fetch(`/presentations/${encodeURIComponent(presentationId)}`)
4
+ const pres = await presRes.json()
5
+ let targetSlideId = null
6
+ for (const slide of (pres?.slides || [])) {
7
+ const text = JSON.stringify(slide)
8
+ if (text && text.includes(findText)) { targetSlideId = slide.objectId; break }
9
+ }
10
+ if (!targetSlideId)
11
+ return { presentationId, applied: false, replies: [] }
12
+ const newSlideId = `slide_${Date.now()}`
13
+ const currentIndex = (pres?.slides || []).findIndex(s => s.objectId === targetSlideId)
14
+ const requests = [
15
+ { createSlide: { objectId: newSlideId, insertionIndex: currentIndex >= 0 ? currentIndex + 1 : (pres?.slides?.length || 0), slideLayoutReference: { predefinedLayout: layout } } },
16
+ ]
17
+ const res = await integration.fetch(`/presentations/${encodeURIComponent(presentationId)}:batchUpdate`, { method: 'POST', body: { requests } })
18
+ const out = await res.json()
19
+ return out?.presentationId ? { ...out, applied: true } : { presentationId, applied: true, replies: out?.replies || [] }
20
+ }
@@ -0,0 +1,12 @@
1
+ async (input) => {
2
+ const { presentationId, pageObjectId } = input
3
+ const params = new URLSearchParams()
4
+ if (input['thumbnailProperties.thumbnailSize'])
5
+ params.set('thumbnailProperties.thumbnailSize', String(input['thumbnailProperties.thumbnailSize']))
6
+ if (input['thumbnailProperties.mimeType'])
7
+ params.set('thumbnailProperties.mimeType', String(input['thumbnailProperties.mimeType']))
8
+ const qs = params.toString()
9
+ const path = `/presentations/${encodeURIComponent(presentationId)}/pages/${encodeURIComponent(pageObjectId)}/thumbnail${qs ? `?${qs}` : ''}`
10
+ const res = await integration.fetch(path)
11
+ return await res.json()
12
+ }
@@ -0,0 +1,6 @@
1
+ async (input) => {
2
+ const { presentationId } = input
3
+ const path = `/presentations/${encodeURIComponent(presentationId)}`
4
+ const res = await integration.fetch(path)
5
+ return await res.json()
6
+ }
@@ -0,0 +1,19 @@
1
+ async (input) => {
2
+ const { presentationId, findText, uri, width = 2000000, height = 2000000 } = input
3
+ const presRes = await integration.fetch(`/presentations/${encodeURIComponent(presentationId)}`)
4
+ const pres = await presRes.json()
5
+ let targetSlideId = null
6
+ for (const slide of (pres?.slides || [])) {
7
+ const text = JSON.stringify(slide)
8
+ if (text && text.includes(findText)) { targetSlideId = slide.objectId; break }
9
+ }
10
+ if (!targetSlideId)
11
+ return { presentationId, applied: false, replies: [] }
12
+ const elementId = `image_${Date.now()}`
13
+ const requests = [
14
+ { createImage: { objectId: elementId, url: uri, elementProperties: { pageObjectId: targetSlideId, size: { width: { magnitude: width, unit: 'EMU' }, height: { magnitude: height, unit: 'EMU' } }, transform: { scaleX: 1, scaleY: 1, translateX: 1000000, translateY: 1000000, unit: 'EMU' } } } },
15
+ ]
16
+ const res = await integration.fetch(`/presentations/${encodeURIComponent(presentationId)}:batchUpdate`, { method: 'POST', body: { requests } })
17
+ const out = await res.json()
18
+ return out?.presentationId ? { ...out, applied: true } : { presentationId, applied: true, replies: out?.replies || [] }
19
+ }
@@ -0,0 +1,21 @@
1
+ async (input) => {
2
+ const { presentationId, findText, shapeType = 'RECTANGLE', width = 2000000, height = 1000000 } = input
3
+ const presRes = await integration.fetch(`/presentations/${encodeURIComponent(presentationId)}`)
4
+ const pres = await presRes.json()
5
+ // Find slide with first text match
6
+ let targetSlideId = null
7
+ for (const slide of (pres?.slides || [])) {
8
+ const text = JSON.stringify(slide)
9
+ if (text && text.includes(findText)) { targetSlideId = slide.objectId; break }
10
+ }
11
+ if (!targetSlideId)
12
+ return { presentationId, applied: false, replies: [] }
13
+ // Insert a shape at a default position near center
14
+ const elementId = `shape_${Date.now()}`
15
+ const requests = [
16
+ { createShape: { objectId: elementId, shapeType, elementProperties: { pageObjectId: targetSlideId, size: { width: { magnitude: width, unit: 'EMU' }, height: { magnitude: height, unit: 'EMU' } }, transform: { scaleX: 1, scaleY: 1, translateX: 1000000, translateY: 1000000, unit: 'EMU' } } } },
17
+ ]
18
+ const res = await integration.fetch(`/presentations/${encodeURIComponent(presentationId)}:batchUpdate`, { method: 'POST', body: { requests } })
19
+ const out = await res.json()
20
+ return out?.presentationId ? { ...out, applied: true } : { presentationId, applied: true, replies: out?.replies || [] }
21
+ }
@@ -0,0 +1,9 @@
1
+ async (input) => {
2
+ const { presentationId, findText, replaceText, matchCase } = input
3
+ const res = await integration.fetch(`/presentations/${encodeURIComponent(presentationId)}:batchUpdate`, {
4
+ method: 'POST',
5
+ body: { requests: [{ replaceAllText: { containsText: { text: findText, matchCase: Boolean(matchCase) }, replaceText } }] },
6
+ })
7
+ const out = await res.json()
8
+ return out?.presentationId ? { ...out, applied: true } : { presentationId, applied: true, replies: out?.replies || [] }
9
+ }
@@ -0,0 +1,15 @@
1
+ async (input) => {
2
+ const { presentationId, slideIndex, rgbColor } = input
3
+ const presRes = await integration.fetch(`/presentations/${encodeURIComponent(presentationId)}`)
4
+ const pres = await presRes.json()
5
+ const slide = (pres?.slides || [])[slideIndex]
6
+ if (!slide?.objectId)
7
+ return { presentationId, applied: false, replies: [] }
8
+ const color = { color: { rgbColor } }
9
+ const res = await integration.fetch(`/presentations/${encodeURIComponent(presentationId)}:batchUpdate`, {
10
+ method: 'POST',
11
+ body: { requests: [{ updatePageProperties: { objectId: slide.objectId, pageProperties: { pageBackgroundFill: { solidFill: color } }, fields: 'pageBackgroundFill.solidFill.color' } }] },
12
+ })
13
+ const out = await res.json()
14
+ return out?.presentationId ? { ...out, applied: true } : { presentationId, applied: true, replies: out?.replies || [] }
15
+ }
@@ -0,0 +1,48 @@
1
+ async (input) => {
2
+ const { presentationId, findText, textStyle, fields } = input
3
+ // Replace first match with a marker to derive objectId/range
4
+ const marker = `__CMD_MARK_${Date.now()}__`
5
+ const rep = await integration.fetch(`/presentations/${encodeURIComponent(presentationId)}:batchUpdate`, {
6
+ method: 'POST',
7
+ body: { requests: [{ replaceAllText: { containsText: { text: findText, matchCase: false }, replaceText: marker } }] },
8
+ })
9
+ await rep.json()
10
+ // Scan pages for marker and apply style to that range on the text element
11
+ const presRes = await integration.fetch(`/presentations/${encodeURIComponent(presentationId)}`)
12
+ const pres = await presRes.json()
13
+ let targetObjectId = null
14
+ let startIndex = -1
15
+ let endIndex = -1
16
+ for (const slide of (pres?.slides || [])) {
17
+ for (const el of (slide.pageElements || [])) {
18
+ const text = el.shape?.text
19
+ if (!text)
20
+ continue
21
+ for (const pe of (text.textElements || [])) {
22
+ const t = pe.textRun?.content
23
+ if (!t)
24
+ continue
25
+ const idx = t.indexOf(marker)
26
+ if (idx >= 0) {
27
+ targetObjectId = el.objectId
28
+ startIndex = (pe.startIndex || 0) + idx
29
+ endIndex = startIndex + marker.length
30
+ break
31
+ }
32
+ }
33
+ if (targetObjectId)
34
+ break
35
+ }
36
+ if (targetObjectId)
37
+ break
38
+ }
39
+ if (!targetObjectId)
40
+ return { presentationId, applied: false, replies: [] }
41
+ const requests = [
42
+ { updateTextStyle: { objectId: targetObjectId, style: textStyle, textRange: { type: 'FIXED_RANGE', startIndex, endIndex }, fields: fields || Object.keys(textStyle || {}).join(',') } },
43
+ { replaceAllText: { containsText: { text: marker, matchCase: true }, replaceText: findText } },
44
+ ]
45
+ const res = await integration.fetch(`/presentations/${encodeURIComponent(presentationId)}:batchUpdate`, { method: 'POST', body: { requests } })
46
+ const out = await res.json()
47
+ return out?.presentationId ? { ...out, applied: true } : { presentationId, applied: true, replies: out?.replies || [] }
48
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "google-slides",
3
+ "version": "0.1.0",
4
+ "tools": [
5
+ {
6
+ "name": "get_presentation",
7
+ "description": "Retrieve presentation metadata, including slides/pages and elements.",
8
+ "inputSchema": "schemas/get_presentation.json",
9
+ "handler": "handlers/get_presentation.js",
10
+ "scope": "read"
11
+ },
12
+ {
13
+ "name": "get_page_thumbnail",
14
+ "description": "Get a thumbnail image for a page (slide) in a presentation.",
15
+ "inputSchema": "schemas/get_page_thumbnail.json",
16
+ "handler": "handlers/get_page_thumbnail.js",
17
+ "scope": "read"
18
+ },
19
+ {
20
+ "name": "create_presentation",
21
+ "description": "Create a new presentation with optional initial title.",
22
+ "inputSchema": "schemas/create_presentation.json",
23
+ "handler": "handlers/create_presentation.js",
24
+ "scope": "write"
25
+ },
26
+ {
27
+ "name": "batch_update",
28
+ "description": "Send a batchUpdate request to modify presentation structure and content.",
29
+ "inputSchema": "schemas/batch_update.json",
30
+ "handler": "handlers/batch_update.js",
31
+ "scope": "write"
32
+ },
33
+ { "name": "append_text_to_title_of_slide_index", "description": "Append text to the title shape of the specified slide (slideIndex, default 0).", "inputSchema": "schemas/append_text_to_title_of_first_slide.json", "handler": "handlers/append_text_to_title_of_first_slide.js", "scope": "write" },
34
+ { "name": "replace_text_first_match", "description": "Replace text for the first match anywhere in the presentation.", "inputSchema": "schemas/replace_text_first_match.json", "handler": "handlers/replace_text_first_match.js", "scope": "write" },
35
+ { "name": "style_text_first_match", "description": "Apply text style to the first match of given text.", "inputSchema": "schemas/style_text_first_match.json", "handler": "handlers/style_text_first_match.js", "scope": "write" },
36
+ { "name": "insert_shape_after_first_match", "description": "Insert a rectangle shape near the first match of text on a slide.", "inputSchema": "schemas/insert_shape_after_first_match.json", "handler": "handlers/insert_shape_after_first_match.js", "scope": "write" },
37
+ { "name": "insert_image_after_first_match", "description": "Insert an image near the first match of text on a slide.", "inputSchema": "schemas/insert_image_after_first_match.json", "handler": "handlers/insert_image_after_first_match.js", "scope": "write" },
38
+ { "name": "create_slide_after_first_match", "description": "Create a new slide after the slide that contains the first text match.", "inputSchema": "schemas/create_slide_after_first_match.json", "handler": "handlers/create_slide_after_first_match.js", "scope": "write" },
39
+ { "name": "set_background_color_for_slide_index", "description": "Set background color for a slide by index.", "inputSchema": "schemas/set_background_color_for_slide_index.json", "handler": "handlers/set_background_color_for_slide_index.js", "scope": "write" }
40
+ ]
41
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "presentationId": { "type": "string" },
6
+ "text": { "type": "string" },
7
+ "slideIndex": { "type": "number", "minimum": 0, "description": "Zero-based slide index to target (default 0)." }
8
+ },
9
+ "required": ["presentationId", "text"],
10
+ "additionalProperties": false
11
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "required": ["presentationId", "requests"],
5
+ "properties": {
6
+ "presentationId": { "type": "string", "description": "ID of the presentation." },
7
+ "requests": { "type": "array", "items": { "type": "object" }, "description": "Array of Slides API requests." },
8
+ "writeControl": { "type": "object", "description": "Optional write control object." },
9
+ "includePresentationInResponse": { "type": "boolean" },
10
+ "responsePageObjectIds": { "type": "array", "items": { "type": "string" } }
11
+ },
12
+ "additionalProperties": false
13
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "title": { "type": "string", "description": "Optional title for the new presentation." }
6
+ },
7
+ "additionalProperties": false
8
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "presentationId": { "type": "string" },
6
+ "findText": { "type": "string" },
7
+ "layout": { "type": "string", "default": "BLANK" }
8
+ },
9
+ "required": ["presentationId", "findText"],
10
+ "additionalProperties": false
11
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "required": ["presentationId", "pageObjectId"],
5
+ "properties": {
6
+ "presentationId": { "type": "string", "description": "ID of the presentation." },
7
+ "pageObjectId": { "type": "string", "description": "Object ID of the page (slide)." },
8
+ "thumbnailProperties.thumbnailSize": { "type": "string", "enum": ["THUMBNAIL_SIZE_UNSPECIFIED", "LARGE", "MEDIUM", "SMALL"], "description": "Requested size hint for the thumbnail." },
9
+ "thumbnailProperties.mimeType": { "type": "string", "enum": ["PNG", "JPEG"], "description": "Requested image MIME type." }
10
+ },
11
+ "additionalProperties": false
12
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "required": ["presentationId"],
5
+ "properties": {
6
+ "presentationId": { "type": "string", "description": "ID of the presentation." }
7
+ },
8
+ "additionalProperties": false
9
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "presentationId": { "type": "string" },
6
+ "findText": { "type": "string" },
7
+ "uri": { "type": "string" },
8
+ "width": { "type": "number", "default": 2000000 },
9
+ "height": { "type": "number", "default": 2000000 }
10
+ },
11
+ "required": ["presentationId", "findText", "uri"],
12
+ "additionalProperties": false
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "presentationId": { "type": "string" },
6
+ "findText": { "type": "string" },
7
+ "shapeType": { "type": "string", "default": "RECTANGLE" },
8
+ "width": { "type": "number", "default": 2000000 },
9
+ "height": { "type": "number", "default": 1000000 }
10
+ },
11
+ "required": ["presentationId", "findText"],
12
+ "additionalProperties": false
13
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "presentationId": { "type": "string" },
6
+ "findText": { "type": "string" },
7
+ "replaceText": { "type": "string" },
8
+ "matchCase": { "type": "boolean" }
9
+ },
10
+ "required": ["presentationId", "findText", "replaceText"],
11
+ "additionalProperties": false
12
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "presentationId": { "type": "string" },
6
+ "slideIndex": { "type": "number", "minimum": 0 },
7
+ "rgbColor": { "type": "object", "properties": { "red": { "type": "number" }, "green": { "type": "number" }, "blue": { "type": "number" } }, "required": ["red", "green", "blue"] }
8
+ },
9
+ "required": ["presentationId", "slideIndex", "rgbColor"],
10
+ "additionalProperties": false
11
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "presentationId": { "type": "string" },
6
+ "findText": { "type": "string" },
7
+ "textStyle": { "type": "object" },
8
+ "fields": { "type": "string" }
9
+ },
10
+ "required": ["presentationId", "findText", "textStyle"],
11
+ "additionalProperties": false
12
+ }
@@ -0,0 +1,41 @@
1
+ ## Task
2
+
3
+ Write a new integration-data folder for `<INTEGRATION_NAME>`.
4
+
5
+ ## Required structure
6
+
7
+ Create `commandable-mcp/packages/server/integration-data/<name>/` containing:
8
+
9
+ - `manifest.json`: flat `tools[]`; each tool has `name`, `description`, `inputSchema` (relative path), `handler` (relative path), `scope` (`read` | `write` | optionally `admin`)
10
+ - `schemas/*.json`: JSON Schema for each tool input. Prefer `additionalProperties: false` for strict contracts; use `true` only for pass-through bodies (common in Google APIs). Reuse `empty.json` for no-arg tools.
11
+ - `handlers/*.js`: async arrow functions `async (input) => { ... }` using `integration.fetch(path, init?)` for all HTTP calls. Pass `body` as a plain object (proxy will JSON-stringify). Return parsed JSON.
12
+ - `credentials.json`: `{ schema, injection }` where `schema` is JSON Schema for secrets and `injection` is `headers` and/or `query` templates using `{{placeholder}}`
13
+ - `credentials_hint.md`: **numbered steps** (1., 2., 3., …) telling a user exactly how to obtain/provide credentials
14
+ - `prompt.md` (optional): extra LLM guidance only if the API has non-obvious usage patterns
15
+
16
+ Also register the provider in `src/integrations/providerRegistry.ts` with its base URL and auth factory.
17
+
18
+ ## Tool design goals
19
+
20
+ - **Default**: small, composable tools that mirror API resources.
21
+ - **Read tools**: side-effect free (GET only).
22
+ - **Write tools**: POST/PUT/PATCH/DELETE where appropriate.
23
+ - **Coverage**: start with core CRUD + list/search + get-by-id; add convenience tools after.
24
+
25
+ ## When to go beyond REST proxying
26
+
27
+ Most handlers should be thin proxies (fetch → return JSON). Only add multi-step/orchestrating handlers when the raw API is genuinely hard for an LLM to use correctly, e.g.:
28
+
29
+ - multi-step workflows (GitHub tree → blob → commit → ref)
30
+ - required transforms (e.g. base64 encoding) that the caller shouldn’t manage
31
+ - awkward addressing that benefits from higher-level helpers (“find text then act” patterns like Docs/Slides)
32
+
33
+ If an endpoint is already clean and self-contained, just proxy it.
34
+
35
+ ## Reference examples (follow exactly)
36
+
37
+ - `github/`: simple proxies + complex orchestration (`create_commit.js`)
38
+ - `notion/`: clean proxying + optional `prompt.md`
39
+ - `google-calendar/`: query param building + `read`/`write`/`admin` scopes
40
+ - `trello/`: query-param credential injection + `displayCards`
41
+ - `google-docs/`, `google-slides/`: higher-level write helpers over batchUpdate APIs
@@ -0,0 +1,153 @@
1
+ import { beforeAll, describe, expect, it } from 'vitest'
2
+ import { IntegrationProxy } from '../../../src/integrations/proxy.js'
3
+ import { loadIntegrationTools } from '../../../src/integrations/dataLoader.js'
4
+
5
+ // LIVE Notion integration tests using managed OAuth
6
+ // Required env vars:
7
+ // - COMMANDABLE_MANAGED_OAUTH_BASE_URL
8
+ // - COMMANDABLE_MANAGED_OAUTH_SECRET_KEY
9
+ // - NOTION_TEST_CONNECTION_ID (managed OAuth connection for provider 'notion')
10
+
11
+ interface Ctx {
12
+ database_id?: string
13
+ page_id?: string
14
+ block_id?: string
15
+ }
16
+
17
+ const env = process.env as Record<string, string>
18
+ const hasEnv = (...keys: string[]) => keys.every(k => !!env[k] && env[k].trim().length > 0)
19
+ const suite = hasEnv(
20
+ 'COMMANDABLE_MANAGED_OAUTH_BASE_URL',
21
+ 'COMMANDABLE_MANAGED_OAUTH_SECRET_KEY',
22
+ 'NOTION_TEST_CONNECTION_ID',
23
+ )
24
+ ? describe
25
+ : describe.skip
26
+
27
+ suite('notion read handlers (live)', () => {
28
+ const ctx: Ctx = {}
29
+ let buildHandler: (name: string) => ((input: any) => Promise<any>)
30
+
31
+ beforeAll(async () => {
32
+ const { COMMANDABLE_MANAGED_OAUTH_BASE_URL, COMMANDABLE_MANAGED_OAUTH_SECRET_KEY, NOTION_TEST_CONNECTION_ID } = env
33
+
34
+ const proxy = new IntegrationProxy({
35
+ managedOAuthBaseUrl: COMMANDABLE_MANAGED_OAUTH_BASE_URL,
36
+ managedOAuthSecretKey: COMMANDABLE_MANAGED_OAUTH_SECRET_KEY,
37
+ })
38
+ const integrationNode = { id: 'node-notion', type: 'notion', label: 'Notion', connectionId: NOTION_TEST_CONNECTION_ID } as any
39
+
40
+ const tools = loadIntegrationTools('notion')
41
+ expect(tools).toBeTruthy()
42
+
43
+ buildHandler = (name: string) => {
44
+ const tool = tools!.read.find(t => t.name === name)
45
+ expect(tool, `tool ${name} exists`).toBeTruthy()
46
+ const integration = {
47
+ fetch: (path: string, init?: RequestInit) => proxy.call(integrationNode, path, init),
48
+ }
49
+ const build = new Function('integration', `return (${tool!.handlerCode});`)
50
+ return build(integration) as (input: any) => Promise<any>
51
+ }
52
+
53
+ // Try to discover some IDs via search
54
+ const search = buildHandler('search')
55
+ const searchRes = await search({ query: '' })
56
+ const page = searchRes?.results?.find((r: any) => r.object === 'page')
57
+ const database = searchRes?.results?.find((r: any) => r.object === 'database')
58
+ ctx.page_id = page?.id
59
+ ctx.database_id = database?.id
60
+ if (ctx.page_id)
61
+ ctx.block_id = ctx.page_id
62
+ }, 60000)
63
+
64
+ it('search returns results', async () => {
65
+ const handler = buildHandler('search')
66
+ const result = await handler({ query: '' })
67
+ expect(result).toBeTruthy()
68
+ }, 30000)
69
+
70
+ it('retrieve_page returns a page', async () => {
71
+ if (!ctx.page_id)
72
+ return expect(true).toBe(true)
73
+ const handler = buildHandler('retrieve_page')
74
+ const result = await handler({ page_id: ctx.page_id })
75
+ expect(result?.object).toBe('page')
76
+ }, 30000)
77
+
78
+ it('retrieve_database returns a database', async () => {
79
+ if (!ctx.database_id)
80
+ return expect(true).toBe(true)
81
+ const handler = buildHandler('retrieve_database')
82
+ const result = await handler({ database_id: ctx.database_id })
83
+ expect(result?.object).toBe('database')
84
+ }, 30000)
85
+
86
+ it('query_database returns results', async () => {
87
+ if (!ctx.database_id)
88
+ return expect(true).toBe(true)
89
+ const handler = buildHandler('query_database')
90
+ const result = await handler({ database_id: ctx.database_id, page_size: 3 })
91
+ expect(result?.results).toBeTruthy()
92
+ }, 30000)
93
+
94
+ it('retrieve_block returns a block', async () => {
95
+ if (!ctx.block_id)
96
+ return expect(true).toBe(true)
97
+ const handler = buildHandler('retrieve_block')
98
+ const result = await handler({ block_id: ctx.block_id })
99
+ expect(result?.object).toBeTruthy()
100
+ }, 30000)
101
+
102
+ it('list_block_children returns children', async () => {
103
+ if (!ctx.block_id)
104
+ return expect(true).toBe(true)
105
+ const handler = buildHandler('list_block_children')
106
+ const result = await handler({ block_id: ctx.block_id, page_size: 5 })
107
+ expect(Array.isArray(result?.results)).toBe(true)
108
+ }, 30000)
109
+
110
+ it('list_users returns users', async () => {
111
+ const handler = buildHandler('list_users')
112
+ const result = await handler({ page_size: 5 })
113
+ expect(result).toBeTruthy()
114
+ }, 30000)
115
+
116
+ it('get_me returns current user', async () => {
117
+ const handler = buildHandler('get_me')
118
+ const result = await handler({})
119
+ expect(result).toBeTruthy()
120
+ }, 30000)
121
+
122
+ it('retrieve_user returns a user', async () => {
123
+ const list = buildHandler('list_users')
124
+ const users = await list({ page_size: 1 })
125
+ const uid = users?.results?.[0]?.id
126
+ if (!uid)
127
+ return expect(true).toBe(true)
128
+ const handler = buildHandler('retrieve_user')
129
+ const result = await handler({ user_id: uid })
130
+ expect(result?.id).toBe(uid)
131
+ }, 30000)
132
+
133
+ it('retrieve_page_property_item returns property data', async () => {
134
+ if (!ctx.page_id)
135
+ return expect(true).toBe(true)
136
+ const page = buildHandler('retrieve_page')
137
+ const p = await page({ page_id: ctx.page_id })
138
+ const propId = Object.values(p?.properties || {})?.[0]?.id
139
+ if (!propId)
140
+ return expect(true).toBe(true)
141
+ const handler = buildHandler('retrieve_page_property_item')
142
+ const result = await handler({ page_id: ctx.page_id, property_id: propId, page_size: 5 })
143
+ expect(result).toBeTruthy()
144
+ }, 30000)
145
+
146
+ it('list_comments returns comments for page or block', async () => {
147
+ if (!ctx.page_id)
148
+ return expect(true).toBe(true)
149
+ const handler = buildHandler('list_comments')
150
+ const result = await handler({ block_id: ctx.page_id })
151
+ expect(result).toBeTruthy()
152
+ }, 30000)
153
+ })