@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.
- package/dist/credentials-index.d.ts +30 -0
- package/dist/credentials-index.d.ts.map +1 -0
- package/dist/credentials-index.js +292 -0
- package/dist/credentials-index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +69 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +164 -0
- package/dist/loader.js.map +1 -0
- package/integrations/README.md +52 -0
- package/integrations/airtable/__tests__/get_handlers.test.ts +137 -0
- package/integrations/airtable/__tests__/usage_parity.test.ts +35 -0
- package/integrations/airtable/__tests__/write_and_admin_handlers.test.ts +113 -0
- package/integrations/airtable/credentials.json +20 -0
- package/integrations/airtable/credentials_hint.md +4 -0
- package/integrations/airtable/handlers/create_record.js +12 -0
- package/integrations/airtable/handlers/delete_record.js +7 -0
- package/integrations/airtable/handlers/get_record.js +4 -0
- package/integrations/airtable/handlers/get_table_schema.js +6 -0
- package/integrations/airtable/handlers/list_bases.js +4 -0
- package/integrations/airtable/handlers/list_records.js +25 -0
- package/integrations/airtable/handlers/list_table_fields.js +6 -0
- package/integrations/airtable/handlers/list_tables.js +4 -0
- package/integrations/airtable/handlers/list_views.js +6 -0
- package/integrations/airtable/handlers/search_records.js +6 -0
- package/integrations/airtable/handlers/update_record.js +11 -0
- package/integrations/airtable/manifest.json +83 -0
- package/integrations/airtable/schemas/create_record.json +12 -0
- package/integrations/airtable/schemas/delete_record.json +11 -0
- package/integrations/airtable/schemas/empty.json +6 -0
- package/integrations/airtable/schemas/get_record.json +11 -0
- package/integrations/airtable/schemas/id_base.json +9 -0
- package/integrations/airtable/schemas/id_base_table.json +10 -0
- package/integrations/airtable/schemas/list_records.json +26 -0
- package/integrations/airtable/schemas/search_records.json +12 -0
- package/integrations/airtable/schemas/update_record.json +13 -0
- package/integrations/github/__tests__/get_handlers.test.ts +117 -0
- package/integrations/github/__tests__/usage_parity.test.ts +34 -0
- package/integrations/github/__tests__/write_handlers.test.ts +311 -0
- package/integrations/github/credentials.json +20 -0
- package/integrations/github/credentials_hint.md +7 -0
- package/integrations/github/handlers/add_labels_to_issue.js +12 -0
- package/integrations/github/handlers/close_issue.js +5 -0
- package/integrations/github/handlers/comment_on_issue.js +5 -0
- package/integrations/github/handlers/create_branch.js +33 -0
- package/integrations/github/handlers/create_commit.js +91 -0
- package/integrations/github/handlers/create_issue.js +10 -0
- package/integrations/github/handlers/create_or_update_file.js +21 -0
- package/integrations/github/handlers/create_pull_request.js +16 -0
- package/integrations/github/handlers/create_repo.js +11 -0
- package/integrations/github/handlers/delete_repo.js +6 -0
- package/integrations/github/handlers/get_issue.js +4 -0
- package/integrations/github/handlers/get_repo.js +4 -0
- package/integrations/github/handlers/list_branches.js +4 -0
- package/integrations/github/handlers/list_commits.js +12 -0
- package/integrations/github/handlers/list_issues.js +12 -0
- package/integrations/github/handlers/list_pull_requests.js +8 -0
- package/integrations/github/handlers/list_repos_install.js +4 -0
- package/integrations/github/handlers/list_repos_user.js +4 -0
- package/integrations/github/handlers/merge_pull_request.js +14 -0
- package/integrations/github/handlers/update_issue.js +15 -0
- package/integrations/github/manifest.json +26 -0
- package/integrations/github/schemas/add_labels_to_issue.json +12 -0
- package/integrations/github/schemas/close_issue.json +10 -0
- package/integrations/github/schemas/comment_on_issue.json +11 -0
- package/integrations/github/schemas/create_branch.json +12 -0
- package/integrations/github/schemas/create_commit.json +25 -0
- package/integrations/github/schemas/create_issue.json +13 -0
- package/integrations/github/schemas/create_or_update_file.json +15 -0
- package/integrations/github/schemas/create_pull_request.json +15 -0
- package/integrations/github/schemas/create_repo.json +12 -0
- package/integrations/github/schemas/delete_repo.json +10 -0
- package/integrations/github/schemas/empty.json +5 -0
- package/integrations/github/schemas/get_issue.json +10 -0
- package/integrations/github/schemas/get_repo.json +9 -0
- package/integrations/github/schemas/list_commits.json +12 -0
- package/integrations/github/schemas/list_issues.json +12 -0
- package/integrations/github/schemas/list_pull_requests.json +10 -0
- package/integrations/github/schemas/merge_pull_request.json +14 -0
- package/integrations/github/schemas/owner_repo.json +9 -0
- package/integrations/github/schemas/update_issue.json +15 -0
- package/integrations/google-calendar/__tests__/get_handlers.test.ts +112 -0
- package/integrations/google-calendar/__tests__/usage_parity.test.ts +34 -0
- package/integrations/google-calendar/__tests__/write_and_admin_handlers.test.ts +161 -0
- package/integrations/google-calendar/credentials.json +36 -0
- package/integrations/google-calendar/credentials_hint.md +9 -0
- package/integrations/google-calendar/handlers/create_event.js +6 -0
- package/integrations/google-calendar/handlers/delete_acl.js +6 -0
- package/integrations/google-calendar/handlers/delete_event.js +7 -0
- package/integrations/google-calendar/handlers/freebusy_query.js +4 -0
- package/integrations/google-calendar/handlers/get_acl.js +5 -0
- package/integrations/google-calendar/handlers/get_calendar.js +4 -0
- package/integrations/google-calendar/handlers/get_event.js +5 -0
- package/integrations/google-calendar/handlers/insert_acl.js +6 -0
- package/integrations/google-calendar/handlers/list_acl.js +5 -0
- package/integrations/google-calendar/handlers/list_calendars.js +4 -0
- package/integrations/google-calendar/handlers/list_colors.js +4 -0
- package/integrations/google-calendar/handlers/list_events.js +21 -0
- package/integrations/google-calendar/handlers/list_settings.js +4 -0
- package/integrations/google-calendar/handlers/move_event.js +6 -0
- package/integrations/google-calendar/handlers/patch_event.js +5 -0
- package/integrations/google-calendar/handlers/quick_add.js +6 -0
- package/integrations/google-calendar/handlers/update_acl.js +7 -0
- package/integrations/google-calendar/handlers/update_event.js +5 -0
- package/integrations/google-calendar/manifest.json +26 -0
- package/integrations/google-calendar/schemas/create_event.json +34 -0
- package/integrations/google-calendar/schemas/delete_acl.json +9 -0
- package/integrations/google-calendar/schemas/empty.json +1 -0
- package/integrations/google-calendar/schemas/freebusy_query.json +13 -0
- package/integrations/google-calendar/schemas/get_acl.json +9 -0
- package/integrations/google-calendar/schemas/id_calendar.json +8 -0
- package/integrations/google-calendar/schemas/id_calendar_event.json +9 -0
- package/integrations/google-calendar/schemas/insert_acl.json +18 -0
- package/integrations/google-calendar/schemas/list_events.json +15 -0
- package/integrations/google-calendar/schemas/move_event.json +10 -0
- package/integrations/google-calendar/schemas/patch_event.json +10 -0
- package/integrations/google-calendar/schemas/quick_add.json +9 -0
- package/integrations/google-calendar/schemas/update_acl.json +10 -0
- package/integrations/google-calendar/schemas/update_event.json +10 -0
- package/integrations/google-docs/__tests__/get_handlers.test.ts +72 -0
- package/integrations/google-docs/__tests__/usage_parity.test.ts +34 -0
- package/integrations/google-docs/__tests__/write_handlers.test.ts +249 -0
- package/integrations/google-docs/credentials.json +36 -0
- package/integrations/google-docs/credentials_hint.md +9 -0
- package/integrations/google-docs/handlers/append_text.js +12 -0
- package/integrations/google-docs/handlers/batch_update.js +13 -0
- package/integrations/google-docs/handlers/create_document.js +9 -0
- package/integrations/google-docs/handlers/delete_first_match.js +50 -0
- package/integrations/google-docs/handlers/get_document.js +12 -0
- package/integrations/google-docs/handlers/get_document_structured.js +6 -0
- package/integrations/google-docs/handlers/get_document_text.js +17 -0
- package/integrations/google-docs/handlers/insert_inline_image_after_first_match.js +41 -0
- package/integrations/google-docs/handlers/insert_page_break_after_first_match.js +49 -0
- package/integrations/google-docs/handlers/insert_table_after_first_match.js +49 -0
- package/integrations/google-docs/handlers/insert_text_after_first_match.js +51 -0
- package/integrations/google-docs/handlers/replace_all_text.js +8 -0
- package/integrations/google-docs/handlers/style_first_match.js +42 -0
- package/integrations/google-docs/handlers/update_document_style.js +8 -0
- package/integrations/google-docs/handlers/update_paragraph_style_for_first_match.js +48 -0
- package/integrations/google-docs/manifest.json +58 -0
- package/integrations/google-docs/schemas/append_text.json +10 -0
- package/integrations/google-docs/schemas/apply_text_style.json +13 -0
- package/integrations/google-docs/schemas/batch_update.json +16 -0
- package/integrations/google-docs/schemas/create_document.json +8 -0
- package/integrations/google-docs/schemas/delete_content_range.json +11 -0
- package/integrations/google-docs/schemas/delete_first_match.json +10 -0
- package/integrations/google-docs/schemas/get_document.json +11 -0
- package/integrations/google-docs/schemas/get_document_structured.json +9 -0
- package/integrations/google-docs/schemas/get_document_text.json +9 -0
- package/integrations/google-docs/schemas/insert_inline_image.json +12 -0
- package/integrations/google-docs/schemas/insert_inline_image_after_first_match.json +13 -0
- package/integrations/google-docs/schemas/insert_page_break.json +10 -0
- package/integrations/google-docs/schemas/insert_page_break_after_first_match.json +11 -0
- package/integrations/google-docs/schemas/insert_table.json +12 -0
- package/integrations/google-docs/schemas/insert_table_after_first_match.json +13 -0
- package/integrations/google-docs/schemas/insert_text_after_first_match.json +12 -0
- package/integrations/google-docs/schemas/insert_text_at.json +11 -0
- package/integrations/google-docs/schemas/replace_all_text.json +12 -0
- package/integrations/google-docs/schemas/style_first_match.json +12 -0
- package/integrations/google-docs/schemas/update_document_style.json +11 -0
- package/integrations/google-docs/schemas/update_paragraph_style.json +13 -0
- package/integrations/google-docs/schemas/update_paragraph_style_for_first_match.json +12 -0
- package/integrations/google-sheet/__tests__/get_handlers.test.ts +129 -0
- package/integrations/google-sheet/__tests__/usage_parity.test.ts +35 -0
- package/integrations/google-sheet/__tests__/write_handlers.test.ts +171 -0
- package/integrations/google-sheet/credentials.json +36 -0
- package/integrations/google-sheet/credentials_hint.md +9 -0
- package/integrations/google-sheet/handlers/append_values.js +18 -0
- package/integrations/google-sheet/handlers/batch_clear_values.js +6 -0
- package/integrations/google-sheet/handlers/batch_clear_values_by_data_filter.js +6 -0
- package/integrations/google-sheet/handlers/batch_get_values.js +16 -0
- package/integrations/google-sheet/handlers/batch_update.js +14 -0
- package/integrations/google-sheet/handlers/batch_update_values.js +16 -0
- package/integrations/google-sheet/handlers/batch_update_values_by_data_filter.js +16 -0
- package/integrations/google-sheet/handlers/clear_values.js +6 -0
- package/integrations/google-sheet/handlers/copy_to_spreadsheet.js +6 -0
- package/integrations/google-sheet/handlers/create_spreadsheet.js +5 -0
- package/integrations/google-sheet/handlers/get_developer_metadata.js +6 -0
- package/integrations/google-sheet/handlers/get_spreadsheet.js +12 -0
- package/integrations/google-sheet/handlers/get_spreadsheet_by_data_filter.js +10 -0
- package/integrations/google-sheet/handlers/get_values.js +14 -0
- package/integrations/google-sheet/handlers/get_values_by_data_filter.js +14 -0
- package/integrations/google-sheet/handlers/search_developer_metadata.js +7 -0
- package/integrations/google-sheet/handlers/update_values.js +16 -0
- package/integrations/google-sheet/manifest.json +125 -0
- package/integrations/google-sheet/schemas/append_values.json +16 -0
- package/integrations/google-sheet/schemas/batch_clear_values.json +10 -0
- package/integrations/google-sheet/schemas/batch_clear_values_by_data_filter.json +10 -0
- package/integrations/google-sheet/schemas/batch_get_values.json +13 -0
- package/integrations/google-sheet/schemas/batch_update.json +13 -0
- package/integrations/google-sheet/schemas/batch_update_values.json +25 -0
- package/integrations/google-sheet/schemas/batch_update_values_by_data_filter.json +25 -0
- package/integrations/google-sheet/schemas/clear_values.json +10 -0
- package/integrations/google-sheet/schemas/copy_to_spreadsheet.json +11 -0
- package/integrations/google-sheet/schemas/create_spreadsheet.json +11 -0
- package/integrations/google-sheet/schemas/get_developer_metadata.json +10 -0
- package/integrations/google-sheet/schemas/get_spreadsheet.json +15 -0
- package/integrations/google-sheet/schemas/get_spreadsheet_by_data_filter.json +11 -0
- package/integrations/google-sheet/schemas/get_values.json +13 -0
- package/integrations/google-sheet/schemas/get_values_by_data_filter.json +17 -0
- package/integrations/google-sheet/schemas/search_developer_metadata.json +14 -0
- package/integrations/google-sheet/schemas/update_values.json +15 -0
- package/integrations/google-slides/__tests__/get_handlers.test.ts +69 -0
- package/integrations/google-slides/__tests__/usage_parity.test.ts +34 -0
- package/integrations/google-slides/__tests__/write_handlers.test.ts +125 -0
- package/integrations/google-slides/credentials.json +36 -0
- package/integrations/google-slides/credentials_hint.md +9 -0
- package/integrations/google-slides/handlers/append_text_to_title_of_first_slide.js +17 -0
- package/integrations/google-slides/handlers/batch_update.js +15 -0
- package/integrations/google-slides/handlers/create_presentation.js +8 -0
- package/integrations/google-slides/handlers/create_slide_after_first_match.js +20 -0
- package/integrations/google-slides/handlers/get_page_thumbnail.js +12 -0
- package/integrations/google-slides/handlers/get_presentation.js +6 -0
- package/integrations/google-slides/handlers/insert_image_after_first_match.js +19 -0
- package/integrations/google-slides/handlers/insert_shape_after_first_match.js +21 -0
- package/integrations/google-slides/handlers/replace_text_first_match.js +9 -0
- package/integrations/google-slides/handlers/set_background_color_for_slide_index.js +15 -0
- package/integrations/google-slides/handlers/style_text_first_match.js +48 -0
- package/integrations/google-slides/manifest.json +41 -0
- package/integrations/google-slides/schemas/append_text_to_title_of_first_slide.json +11 -0
- package/integrations/google-slides/schemas/batch_update.json +13 -0
- package/integrations/google-slides/schemas/create_presentation.json +8 -0
- package/integrations/google-slides/schemas/create_slide_after_first_match.json +11 -0
- package/integrations/google-slides/schemas/get_page_thumbnail.json +12 -0
- package/integrations/google-slides/schemas/get_presentation.json +9 -0
- package/integrations/google-slides/schemas/insert_image_after_first_match.json +13 -0
- package/integrations/google-slides/schemas/insert_shape_after_first_match.json +13 -0
- package/integrations/google-slides/schemas/replace_text_first_match.json +12 -0
- package/integrations/google-slides/schemas/set_background_color_for_slide_index.json +11 -0
- package/integrations/google-slides/schemas/style_text_first_match.json +12 -0
- package/integrations/new_integration_prompt.md +41 -0
- package/integrations/notion/__tests__/get_handlers.test.ts +153 -0
- package/integrations/notion/__tests__/usage_parity.test.ts +34 -0
- package/integrations/notion/__tests__/write_and_admin_handlers.test.ts +190 -0
- package/integrations/notion/credentials.json +21 -0
- package/integrations/notion/credentials_hint.md +5 -0
- package/integrations/notion/handlers/append_block_children.js +7 -0
- package/integrations/notion/handlers/create_comment.js +10 -0
- package/integrations/notion/handlers/create_database.js +11 -0
- package/integrations/notion/handlers/create_page.js +13 -0
- package/integrations/notion/handlers/delete_block.js +8 -0
- package/integrations/notion/handlers/get_me.js +4 -0
- package/integrations/notion/handlers/list_block_children.js +10 -0
- package/integrations/notion/handlers/list_comments.js +14 -0
- package/integrations/notion/handlers/list_users.js +10 -0
- package/integrations/notion/handlers/query_database.js +10 -0
- package/integrations/notion/handlers/retrieve_block.js +4 -0
- package/integrations/notion/handlers/retrieve_database.js +4 -0
- package/integrations/notion/handlers/retrieve_page.js +4 -0
- package/integrations/notion/handlers/retrieve_page_property_item.js +10 -0
- package/integrations/notion/handlers/retrieve_user.js +4 -0
- package/integrations/notion/handlers/search.js +11 -0
- package/integrations/notion/handlers/update_block.js +7 -0
- package/integrations/notion/handlers/update_database.js +10 -0
- package/integrations/notion/handlers/update_page_properties.js +10 -0
- package/integrations/notion/manifest.json +139 -0
- package/integrations/notion/prompt.md +26 -0
- package/integrations/notion/schemas/append_block_children.json +10 -0
- package/integrations/notion/schemas/create_comment.json +18 -0
- package/integrations/notion/schemas/create_database.json +18 -0
- package/integrations/notion/schemas/create_page.json +22 -0
- package/integrations/notion/schemas/delete_block.json +9 -0
- package/integrations/notion/schemas/empty.json +6 -0
- package/integrations/notion/schemas/id_block.json +9 -0
- package/integrations/notion/schemas/id_database.json +9 -0
- package/integrations/notion/schemas/id_page.json +9 -0
- package/integrations/notion/schemas/id_user.json +9 -0
- package/integrations/notion/schemas/list_block_children.json +11 -0
- package/integrations/notion/schemas/list_comments.json +15 -0
- package/integrations/notion/schemas/list_users.json +9 -0
- package/integrations/notion/schemas/query_database.json +13 -0
- package/integrations/notion/schemas/retrieve_page_property_item.json +12 -0
- package/integrations/notion/schemas/search.json +27 -0
- package/integrations/notion/schemas/update_block.json +10 -0
- package/integrations/notion/schemas/update_database.json +13 -0
- package/integrations/notion/schemas/update_page_properties.json +13 -0
- package/integrations/trello/__tests__/get_handlers.test.ts +240 -0
- package/integrations/trello/__tests__/usage_parity.test.ts +34 -0
- package/integrations/trello/__tests__/write_and_admin_handlers.test.ts +189 -0
- package/integrations/trello/credentials.json +26 -0
- package/integrations/trello/credentials_hint.md +4 -0
- package/integrations/trello/handlers/add_checklist_to_card.js +5 -0
- package/integrations/trello/handlers/add_member_to_card.js +5 -0
- package/integrations/trello/handlers/archive_list.js +5 -0
- package/integrations/trello/handlers/create_card.js +13 -0
- package/integrations/trello/handlers/create_list.js +7 -0
- package/integrations/trello/handlers/delete_card.js +9 -0
- package/integrations/trello/handlers/get_board.js +4 -0
- package/integrations/trello/handlers/get_board_cards.js +4 -0
- package/integrations/trello/handlers/get_board_custom_fields.js +4 -0
- package/integrations/trello/handlers/get_board_labels.js +4 -0
- package/integrations/trello/handlers/get_board_lists.js +4 -0
- package/integrations/trello/handlers/get_board_members.js +4 -0
- package/integrations/trello/handlers/get_board_memberships.js +4 -0
- package/integrations/trello/handlers/get_card.js +4 -0
- package/integrations/trello/handlers/get_card_actions.js +4 -0
- package/integrations/trello/handlers/get_card_attachments.js +4 -0
- package/integrations/trello/handlers/get_card_checklists.js +4 -0
- package/integrations/trello/handlers/get_card_custom_field_items.js +4 -0
- package/integrations/trello/handlers/get_card_members.js +4 -0
- package/integrations/trello/handlers/get_list.js +4 -0
- package/integrations/trello/handlers/get_list_cards.js +4 -0
- package/integrations/trello/handlers/get_member.js +4 -0
- package/integrations/trello/handlers/get_member_boards.js +4 -0
- package/integrations/trello/handlers/get_member_organizations.js +4 -0
- package/integrations/trello/handlers/get_organization.js +4 -0
- package/integrations/trello/handlers/get_organization_boards.js +4 -0
- package/integrations/trello/handlers/move_card_to_list.js +5 -0
- package/integrations/trello/handlers/remove_member_from_card.js +9 -0
- package/integrations/trello/handlers/search.js +5 -0
- package/integrations/trello/handlers/update_card.js +19 -0
- package/integrations/trello/handlers/update_list.js +11 -0
- package/integrations/trello/manifest.json +231 -0
- package/integrations/trello/schemas/add_checklist_to_card.json +10 -0
- package/integrations/trello/schemas/add_member_to_card.json +10 -0
- package/integrations/trello/schemas/archive_list.json +9 -0
- package/integrations/trello/schemas/create_card.json +13 -0
- package/integrations/trello/schemas/create_list.json +11 -0
- package/integrations/trello/schemas/delete_card.json +9 -0
- package/integrations/trello/schemas/display_trello_cards.json +45 -0
- package/integrations/trello/schemas/empty.json +5 -0
- package/integrations/trello/schemas/get_member.json +5 -0
- package/integrations/trello/schemas/id_board.json +8 -0
- package/integrations/trello/schemas/id_card.json +8 -0
- package/integrations/trello/schemas/id_list.json +8 -0
- package/integrations/trello/schemas/id_org.json +8 -0
- package/integrations/trello/schemas/move_card_to_list.json +10 -0
- package/integrations/trello/schemas/remove_member_from_card.json +10 -0
- package/integrations/trello/schemas/search.json +8 -0
- package/integrations/trello/schemas/update_card.json +16 -0
- package/integrations/trello/schemas/update_list.json +12 -0
- 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,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,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,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,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
|
+
})
|