From 464ac0e40e346569a74a910dd524abcd2d1a613a Mon Sep 17 00:00:00 2001 From: jottakka Date: Thu, 12 Feb 2026 15:13:07 -0300 Subject: [PATCH 01/15] [TOO-429] Improving docs gen provider id matching and updating engine api accesses for metadaqa --- .../data/toolkits/hubspot.json | 80 +- .../data/toolkits/hubspotautomationapi.json | 20 +- .../data/toolkits/hubspotcmsapi.json | 352 +++---- .../toolkits/hubspotconversationsapi.json | 50 +- .../data/toolkits/hubspotcrmapi.json | 908 +++++++++--------- .../data/toolkits/hubspoteventsapi.json | 14 +- .../data/toolkits/hubspotmarketingapi.json | 182 ++-- .../data/toolkits/hubspotmeetingsapi.json | 12 +- .../data/toolkits/hubspotusersapi.json | 16 +- .../data/toolkits/salesforce.json | 8 +- .../data/toolkits/ticktickapi.json | 24 +- .../data/toolkits/zendesk.json | 14 +- toolkit-docs-generator/src/cli/index.ts | 13 +- .../src/merger/data-merger.ts | 231 +++-- toolkit-docs-generator/src/merger/index.ts | 1 + .../src/merger/metadata-freshness.ts | 162 ++++ .../src/sources/engine-api.ts | 5 +- toolkit-docs-generator/src/sources/index.ts | 1 + .../src/sources/oauth-provider-resolver.ts | 117 +++ .../src/sources/tool-metadata-schema.ts | 58 +- .../tests/fixtures/engine-api-response.json | 83 +- .../tests/merger/data-merger.test.ts | 135 +++ .../tests/merger/metadata-freshness.test.ts | 493 ++++++++++ .../tests/sources/engine-api.test.ts | 46 +- .../sources/oauth-provider-resolver.test.ts | 159 +++ 25 files changed, 2185 insertions(+), 999 deletions(-) create mode 100644 toolkit-docs-generator/src/merger/metadata-freshness.ts create mode 100644 toolkit-docs-generator/src/sources/oauth-provider-resolver.ts create mode 100644 toolkit-docs-generator/tests/merger/metadata-freshness.test.ts create mode 100644 toolkit-docs-generator/tests/sources/oauth-provider-resolver.test.ts diff --git a/toolkit-docs-generator/data/toolkits/hubspot.json b/toolkit-docs-generator/data/toolkits/hubspot.json index bff510c65..bee406051 100644 --- a/toolkit-docs-generator/data/toolkits/hubspot.json +++ b/toolkit-docs-generator/data/toolkits/hubspot.json @@ -15,7 +15,7 @@ }, "auth": { "type": "oauth2", - "providerId": null, + "providerId": "hubspot", "allScopes": [ "crm.objects.companies.read", "crm.objects.companies.write", @@ -61,7 +61,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -123,7 +123,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -246,7 +246,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth", "crm.objects.contacts.write"] }, @@ -377,7 +377,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth", "crm.objects.contacts.write"] }, @@ -498,7 +498,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth", "crm.objects.companies.write"] }, @@ -621,7 +621,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth", "crm.objects.contacts.write"] }, @@ -755,7 +755,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth", "crm.objects.deals.write"] }, @@ -965,7 +965,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth", "crm.objects.contacts.write"] }, @@ -1155,7 +1155,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth", "crm.objects.contacts.write"] }, @@ -1267,7 +1267,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth", "crm.objects.contacts.write"] }, @@ -1318,7 +1318,7 @@ "description": "Get all users/owners in the HubSpot portal.\n\nThis tool retrieves a list of all users (owners) in your HubSpot portal,\nUseful for user management and assignment operations.\n\nUse this tool when needing information about ALL users in the HubSpot portal.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth", "crm.objects.owners.read"] }, @@ -1343,7 +1343,7 @@ "description": "Get all available industry types for HubSpot companies.\n\nReturns a sorted list of valid industry type values that can be used\nwhen creating companies.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth"] }, @@ -1409,7 +1409,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -1506,7 +1506,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -1595,7 +1595,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -1680,7 +1680,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -1749,7 +1749,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -1824,7 +1824,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -1885,7 +1885,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth", "crm.objects.deals.read"] }, @@ -1925,7 +1925,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth", "crm.objects.deals.read"] }, @@ -1997,7 +1997,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -2094,7 +2094,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -2191,7 +2191,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -2288,7 +2288,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -2353,7 +2353,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth", "crm.objects.owners.read"] }, @@ -2417,7 +2417,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -2518,7 +2518,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -2629,7 +2629,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -2793,7 +2793,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -2928,7 +2928,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -3077,7 +3077,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -3235,7 +3235,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -3399,7 +3399,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth", "crm.objects.deals.read", "crm.objects.deals.write"] }, @@ -3505,7 +3505,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -3594,7 +3594,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -3725,7 +3725,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -3879,7 +3879,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -3995,7 +3995,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "oauth", @@ -4050,7 +4050,7 @@ "description": "Get current user information from HubSpot.\n\nThis is typically the first tool called to understand the current user context.\n\nUse this tool when needing information about the current user basic HubSpot information.\nand the associated HubSpot portal.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth", "crm.objects.owners.read"] }, diff --git a/toolkit-docs-generator/data/toolkits/hubspotautomationapi.json b/toolkit-docs-generator/data/toolkits/hubspotautomationapi.json index 29736d5bb..048cf9aff 100644 --- a/toolkit-docs-generator/data/toolkits/hubspotautomationapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspotautomationapi.json @@ -15,7 +15,7 @@ }, "auth": { "type": "oauth2", - "providerId": null, + "providerId": "hubspot", "allScopes": [ "automation", "automation.sequences.enrollments.write", @@ -39,7 +39,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["automation.sequences.read"] }, @@ -95,7 +95,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["automation"] }, @@ -153,7 +153,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["automation"] }, @@ -222,7 +222,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["automation.sequences.enrollments.write"] }, @@ -302,7 +302,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["automation"] }, @@ -357,7 +357,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["automation.sequences.read"] }, @@ -388,7 +388,7 @@ "description": "Retrieve a list of sequences for a specific user.\n\nUse this tool to get sequences associated with a user in HubSpot. Useful for tracking user-specific automated processes.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["automation.sequences.read"] }, @@ -430,7 +430,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["automation"] }, @@ -483,7 +483,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["automation"] }, diff --git a/toolkit-docs-generator/data/toolkits/hubspotcmsapi.json b/toolkit-docs-generator/data/toolkits/hubspotcmsapi.json index 58cd4c1de..45f7a7eab 100644 --- a/toolkit-docs-generator/data/toolkits/hubspotcmsapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspotcmsapi.json @@ -15,7 +15,7 @@ }, "auth": { "type": "oauth2", - "providerId": null, + "providerId": "hubspot", "allScopes": [ "cms.domains.read", "cms.domains.write", @@ -59,7 +59,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -110,7 +110,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -150,7 +150,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -214,7 +214,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -293,7 +293,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -372,7 +372,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -451,7 +451,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -530,7 +530,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -609,7 +609,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -680,7 +680,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -730,7 +730,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -778,7 +778,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -839,7 +839,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -905,7 +905,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -987,7 +987,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -1055,7 +1055,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -1108,7 +1108,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -1161,7 +1161,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -1214,7 +1214,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["media_bridge.write"] }, @@ -1267,7 +1267,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -1320,7 +1320,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -1373,7 +1373,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -1426,7 +1426,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -1495,7 +1495,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -1558,7 +1558,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -1651,7 +1651,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -1745,7 +1745,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -1808,7 +1808,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -1869,7 +1869,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -1927,7 +1927,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -1980,7 +1980,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2073,7 +2073,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2151,7 +2151,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2204,7 +2204,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2265,7 +2265,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2323,7 +2323,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["media_bridge.write"] }, @@ -2384,7 +2384,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2442,7 +2442,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2495,7 +2495,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2612,7 +2612,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2705,7 +2705,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2751,7 +2751,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2792,7 +2792,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2840,7 +2840,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2886,7 +2886,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2934,7 +2934,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -2987,7 +2987,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3040,7 +3040,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3093,7 +3093,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3139,7 +3139,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3180,7 +3180,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3228,7 +3228,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3281,7 +3281,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -3326,7 +3326,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3366,7 +3366,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3406,7 +3406,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3446,7 +3446,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3486,7 +3486,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3526,7 +3526,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3566,7 +3566,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3614,7 +3614,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3667,7 +3667,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3720,7 +3720,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3773,7 +3773,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -3826,7 +3826,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -3871,7 +3871,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3961,7 +3961,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -4104,7 +4104,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -4278,7 +4278,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -4462,7 +4462,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -4573,7 +4573,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -4704,7 +4704,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -4794,7 +4794,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -4858,7 +4858,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -4913,7 +4913,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -5034,7 +5034,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -5124,7 +5124,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "cms.knowledge_base.settings.read", @@ -5220,7 +5220,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -5314,7 +5314,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -5385,7 +5385,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -5508,7 +5508,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "cms.knowledge_base.settings.read", @@ -5615,7 +5615,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -5674,7 +5674,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -5743,7 +5743,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -5814,7 +5814,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -5864,7 +5864,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -5920,7 +5920,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -6051,7 +6051,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -6165,7 +6165,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -6301,7 +6301,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -6415,7 +6415,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -6494,7 +6494,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -6573,7 +6573,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -6628,7 +6628,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -6749,7 +6749,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -6855,7 +6855,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -6905,7 +6905,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -6953,7 +6953,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -7006,7 +7006,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -7060,7 +7060,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -7105,7 +7105,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -7145,7 +7145,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -7185,7 +7185,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -7233,7 +7233,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -7287,7 +7287,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -7340,7 +7340,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -7409,7 +7409,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -7480,7 +7480,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -7538,7 +7538,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -7583,7 +7583,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -7631,7 +7631,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -7676,7 +7676,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -7716,7 +7716,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -7764,7 +7764,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -7817,7 +7817,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -7870,7 +7870,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -7923,7 +7923,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -7976,7 +7976,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -8029,7 +8029,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -8082,7 +8082,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -8135,7 +8135,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -8241,7 +8241,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -8325,7 +8325,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -8386,7 +8386,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -8517,7 +8517,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -8616,7 +8616,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -8669,7 +8669,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -8787,7 +8787,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -8888,7 +8888,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -8947,7 +8947,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9000,7 +9000,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9053,7 +9053,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9107,7 +9107,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9160,7 +9160,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9213,7 +9213,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9266,7 +9266,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9327,7 +9327,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9386,7 +9386,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9439,7 +9439,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9492,7 +9492,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9545,7 +9545,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9723,7 +9723,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9843,7 +9843,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9883,7 +9883,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9923,7 +9923,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -9963,7 +9963,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -10003,7 +10003,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -10043,7 +10043,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -10091,7 +10091,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["media_bridge.write"] }, @@ -10144,7 +10144,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -10213,7 +10213,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -10276,7 +10276,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -10337,7 +10337,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -10395,7 +10395,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -10464,7 +10464,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -10535,7 +10535,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -10593,7 +10593,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -10654,7 +10654,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -10768,7 +10768,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -10856,7 +10856,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -10917,7 +10917,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -10976,7 +10976,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -11061,7 +11061,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -11150,7 +11150,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["hubdb"] }, @@ -11229,7 +11229,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -11300,7 +11300,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -11414,7 +11414,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -11510,7 +11510,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -11568,7 +11568,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -11629,7 +11629,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -11703,7 +11703,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -11774,7 +11774,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -11832,7 +11832,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -11893,7 +11893,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -11959,7 +11959,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, diff --git a/toolkit-docs-generator/data/toolkits/hubspotconversationsapi.json b/toolkit-docs-generator/data/toolkits/hubspotconversationsapi.json index 1fb161f7e..b533fdccc 100644 --- a/toolkit-docs-generator/data/toolkits/hubspotconversationsapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspotconversationsapi.json @@ -15,7 +15,7 @@ }, "auth": { "type": "oauth2", - "providerId": null, + "providerId": "hubspot", "allScopes": [ "conversations.custom_channels.read", "conversations.custom_channels.write", @@ -40,7 +40,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.write"] }, @@ -120,7 +120,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "conversations.custom_channels.write", @@ -188,7 +188,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.read"] }, @@ -228,7 +228,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "conversations.custom_channels.write", @@ -279,7 +279,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.custom_channels.read"] }, @@ -324,7 +324,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.read"] }, @@ -364,7 +364,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.read"] }, @@ -395,7 +395,7 @@ "description": "Retrieve a list of conversation channels from Hubspot.\n\nUse this tool to get a list of conversation channels from Hubspot, applying optional filters and sorting if needed.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.read"] }, @@ -420,7 +420,7 @@ "description": "Retrieve a list of conversation inboxes.\n\nThis tool fetches a list of conversation inboxes, allowing for optional filters and sorting to customize the results.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.read"] }, @@ -470,7 +470,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.custom_channels.write"] }, @@ -521,7 +521,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.read"] }, @@ -561,7 +561,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.read"] }, @@ -609,7 +609,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "conversations.custom_channels.write", @@ -648,7 +648,7 @@ "description": "Retrieve a list of channel accounts from Hubspot.\n\nThis tool calls the Hubspot Conversations API to retrieve a list of channel accounts. It supports optional filters and sorting to refine the results.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.read"] }, @@ -682,7 +682,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.read"] }, @@ -713,7 +713,7 @@ "description": "Retrieve conversation threads from Hubspot Conversations.\n\nUse this tool to fetch a list of conversation threads from Hubspot Conversations. You can apply optional filters and sorting to tailor the results.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.read"] }, @@ -755,7 +755,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.read"] }, @@ -800,7 +800,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.read"] }, @@ -848,7 +848,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.read"] }, @@ -909,7 +909,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.write"] }, @@ -983,7 +983,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "conversations.custom_channels.write", @@ -1073,7 +1073,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "conversations.custom_channels.write", @@ -1152,7 +1152,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.write"] }, @@ -1226,7 +1226,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["conversations.custom_channels.write"] }, diff --git a/toolkit-docs-generator/data/toolkits/hubspotcrmapi.json b/toolkit-docs-generator/data/toolkits/hubspotcrmapi.json index c4dc175ae..60471d725 100644 --- a/toolkit-docs-generator/data/toolkits/hubspotcrmapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspotcrmapi.json @@ -15,7 +15,7 @@ }, "auth": { "type": "oauth2", - "providerId": null, + "providerId": "hubspot", "allScopes": [ "automation", "cms.membership.access_groups.write", @@ -152,7 +152,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.lists.write", @@ -210,7 +210,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.lists.write", @@ -259,7 +259,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -307,7 +307,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -360,7 +360,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.carts.write"] }, @@ -413,7 +413,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.commercepayments.write"] }, @@ -458,7 +458,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -506,7 +506,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -559,7 +559,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.courses.write"] }, @@ -620,7 +620,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -692,7 +692,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.deals.write"] }, @@ -740,7 +740,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -793,7 +793,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -846,7 +846,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -899,7 +899,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.goals.write"] }, @@ -944,7 +944,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.leads.write"] }, @@ -1000,7 +1000,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -1125,7 +1125,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -1178,7 +1178,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.invoices.write"] }, @@ -1231,7 +1231,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.leads.write"] }, @@ -1284,7 +1284,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -1337,7 +1337,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -1398,7 +1398,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -1523,7 +1523,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.deals.write"] }, @@ -1576,7 +1576,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.listings.write"] }, @@ -1629,7 +1629,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -1682,7 +1682,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.orders.write"] }, @@ -1727,7 +1727,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -1775,7 +1775,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -1828,7 +1828,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.products.write"] }, @@ -1873,7 +1873,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.quotes.write"] }, @@ -1921,7 +1921,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.quotes.write"] }, @@ -1974,7 +1974,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.services.write"] }, @@ -2027,7 +2027,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -2147,7 +2147,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -2200,7 +2200,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.write"] }, @@ -2269,7 +2269,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-clients.write"] }, @@ -2348,7 +2348,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-services.write"] }, @@ -2411,7 +2411,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.deals.write", @@ -2497,7 +2497,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -2634,7 +2634,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -2700,7 +2700,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-clients.read"] }, @@ -2774,7 +2774,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -2904,7 +2904,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -2957,7 +2957,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.courses.write"] }, @@ -3018,7 +3018,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -3159,7 +3159,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -3288,7 +3288,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.commercepayments.write"] }, @@ -3341,7 +3341,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -3394,7 +3394,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -3447,7 +3447,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -3500,7 +3500,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -3553,7 +3553,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -3606,7 +3606,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.carts.write"] }, @@ -3659,7 +3659,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.companies.write"] }, @@ -3712,7 +3712,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.courses.write"] }, @@ -3765,7 +3765,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -3818,7 +3818,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.invoices.write"] }, @@ -3871,7 +3871,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -3924,7 +3924,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.products.write"] }, @@ -3977,7 +3977,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.quotes.write"] }, @@ -4030,7 +4030,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.services.write"] }, @@ -4083,7 +4083,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.orders.write"] }, @@ -4144,7 +4144,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -4224,7 +4224,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -4344,7 +4344,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -4397,7 +4397,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["tickets"] }, @@ -4450,7 +4450,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.carts.write"] }, @@ -4503,7 +4503,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.commercepayments.write"] }, @@ -4556,7 +4556,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.companies.write"] }, @@ -4617,7 +4617,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -4750,7 +4750,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -4891,7 +4891,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.schemas.custom.write"] }, @@ -4954,7 +4954,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.schemas.custom.write"] }, @@ -5015,7 +5015,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -5095,7 +5095,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.write"] }, @@ -5148,7 +5148,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -5201,7 +5201,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -5254,7 +5254,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.lists.write", @@ -5311,7 +5311,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.goals.write"] }, @@ -5364,7 +5364,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.goals.write"] }, @@ -5417,7 +5417,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -5470,7 +5470,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -5523,7 +5523,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -5576,7 +5576,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.courses.write"] }, @@ -5637,7 +5637,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -5717,7 +5717,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.deals.write"] }, @@ -5770,7 +5770,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -5823,7 +5823,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.invoices.write"] }, @@ -5876,7 +5876,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -5929,7 +5929,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.listings.write"] }, @@ -5982,7 +5982,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -6035,7 +6035,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -6096,7 +6096,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -6221,7 +6221,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.orders.write"] }, @@ -6274,7 +6274,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.quotes.write"] }, @@ -6327,7 +6327,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -6380,7 +6380,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.leads.write"] }, @@ -6433,7 +6433,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.leads.write"] }, @@ -6486,7 +6486,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -6539,7 +6539,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.deals.write"] }, @@ -6592,7 +6592,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.listings.write"] }, @@ -6645,7 +6645,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -6722,7 +6722,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -6857,7 +6857,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.listings.write"] }, @@ -6910,7 +6910,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.companies.write"] }, @@ -6963,7 +6963,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -7016,7 +7016,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.deals.write"] }, @@ -7069,7 +7069,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.quotes.write"] }, @@ -7122,7 +7122,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -7191,7 +7191,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -7276,7 +7276,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -7329,7 +7329,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -7382,7 +7382,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.products.write"] }, @@ -7451,7 +7451,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -7536,7 +7536,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.services.write"] }, @@ -7589,7 +7589,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -7709,7 +7709,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -7762,7 +7762,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -7815,7 +7815,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["tickets"] }, @@ -7868,7 +7868,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.write"] }, @@ -7921,7 +7921,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -7966,7 +7966,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.extensions_calling_transcripts.write"] }, @@ -8006,7 +8006,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.commercepayments.write"] }, @@ -8054,7 +8054,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.companies.write"] }, @@ -8099,7 +8099,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.companies.write"] }, @@ -8139,7 +8139,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -8179,7 +8179,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.courses.write"] }, @@ -8227,7 +8227,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.schemas.custom.write"] }, @@ -8272,7 +8272,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.lists.write", @@ -8324,7 +8324,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -8444,7 +8444,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.schemas.custom.write"] }, @@ -8489,7 +8489,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -8529,7 +8529,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -8569,7 +8569,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -8609,7 +8609,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.goals.write"] }, @@ -8657,7 +8657,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["tickets"] }, @@ -8702,7 +8702,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.invoices.write"] }, @@ -8742,7 +8742,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -8782,7 +8782,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.lists.write", @@ -8826,7 +8826,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -8866,7 +8866,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -8906,7 +8906,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.services.write"] }, @@ -8946,7 +8946,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.orders.write"] }, @@ -9010,7 +9010,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -9103,7 +9103,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -9183,7 +9183,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -9258,7 +9258,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -9325,7 +9325,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.lists.write", @@ -9369,7 +9369,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.carts.write"] }, @@ -9409,7 +9409,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -9516,7 +9516,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -9556,7 +9556,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -9596,7 +9596,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["tickets"] }, @@ -9636,7 +9636,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.write"] }, @@ -9700,7 +9700,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.services.write"] }, @@ -9755,7 +9755,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -9861,7 +9861,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -9958,7 +9958,7 @@ "description": "Fetch enablement data from HubSpot CRM.\n\nUse this tool to obtain enablement information from the HubSpot CRM. It should be called when detailed enablement data is required for CRM-related operations or analyses.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -10066,7 +10066,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.lists.read"] }, @@ -10135,7 +10135,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.lists.read"] }, @@ -10241,7 +10241,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -10394,7 +10394,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.services.read"] }, @@ -10460,7 +10460,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.listings.read"] }, @@ -10526,7 +10526,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.lists.read"] }, @@ -10600,7 +10600,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.lists.read"] }, @@ -10663,7 +10663,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -10830,7 +10830,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["tickets"] }, @@ -10903,7 +10903,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -10964,7 +10964,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -11081,7 +11081,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -11251,7 +11251,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -11396,7 +11396,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -11547,7 +11547,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -11687,7 +11687,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -11789,7 +11789,7 @@ "description": "Get limits and usage for calculated properties in HubSpot CRM.\n\nCall this tool to retrieve overall limits and per object usage for calculated properties in HubSpot CRM.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -11932,7 +11932,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -12040,7 +12040,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -12148,7 +12148,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.carts.read"] }, @@ -12256,7 +12256,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.commercepayments.read"] }, @@ -12364,7 +12364,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -12472,7 +12472,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -12580,7 +12580,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -12688,7 +12688,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.courses.read"] }, @@ -12796,7 +12796,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.courses.read"] }, @@ -12904,7 +12904,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -12969,7 +12969,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.custom.highly_sensitive.read.v2", @@ -13022,7 +13022,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -13124,7 +13124,7 @@ "description": "Retrieve limits and usage for HubSpot custom object schemas.\n\nThis tool is used to get the limits and usage data for custom object schemas in HubSpot CRM. It's useful for monitoring and managing custom object types and understanding their current usage.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.custom.sensitive.write.v2", @@ -13158,7 +13158,7 @@ "description": "Retrieve limits and usage for custom properties per object.\n\nUse this tool to access the limits and usage statistics for custom properties categorized by object in HubSpot CRM.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -13301,7 +13301,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.deals.read"] }, @@ -13409,7 +13409,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.contacts.read", @@ -13520,7 +13520,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.contacts.read", @@ -13631,7 +13631,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -13739,7 +13739,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.goals.read"] }, @@ -13847,7 +13847,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.goals.read"] }, @@ -13903,7 +13903,7 @@ "description": "Retrieve limits and usage for records in HubSpot CRM.\n\nUse this tool to obtain detailed information about the limits and current usage of various objects within the HubSpot CRM records.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -14003,7 +14003,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.custom.highly_sensitive.read.v2", @@ -14091,7 +14091,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.deals.read"] }, @@ -14199,7 +14199,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -14307,7 +14307,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.leads.read"] }, @@ -14415,7 +14415,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.services.read"] }, @@ -14531,7 +14531,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -14711,7 +14711,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.invoices.read"] }, @@ -14819,7 +14819,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.leads.read"] }, @@ -14927,7 +14927,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -15035,7 +15035,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.listings.read"] }, @@ -15143,7 +15143,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -15251,7 +15251,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -15332,7 +15332,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -15425,7 +15425,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -15533,7 +15533,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.orders.read"] }, @@ -15641,7 +15641,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.orders.read"] }, @@ -15749,7 +15749,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-clients.read"] }, @@ -15857,7 +15857,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-clients.read"] }, @@ -15965,7 +15965,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-services.read"] }, @@ -16073,7 +16073,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-services.read"] }, @@ -16181,7 +16181,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [] }, @@ -16289,7 +16289,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [] }, @@ -16362,7 +16362,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -16494,7 +16494,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -16634,7 +16634,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -16779,7 +16779,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -16916,7 +16916,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -17083,7 +17083,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -17191,7 +17191,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -17299,7 +17299,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.products.read"] }, @@ -17407,7 +17407,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.products.read"] }, @@ -17472,7 +17472,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -17634,7 +17634,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.quotes.read"] }, @@ -17742,7 +17742,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.quotes.read"] }, @@ -17815,7 +17815,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.lists.read"] }, @@ -17903,7 +17903,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.subscriptions.read"] }, @@ -18011,7 +18011,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.subscriptions.read"] }, @@ -18119,7 +18119,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -18227,7 +18227,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -18335,7 +18335,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -18443,7 +18443,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -18551,7 +18551,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["tickets"] }, @@ -18616,7 +18616,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.extensions_calling_transcripts.read"] }, @@ -18699,7 +18699,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.read"] }, @@ -18807,7 +18807,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.read"] }, @@ -18896,7 +18896,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -19025,7 +19025,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.courses.read"] }, @@ -19078,7 +19078,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.services.write"] }, @@ -19131,7 +19131,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.services.read"] }, @@ -19200,7 +19200,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.listings.write"] }, @@ -19298,7 +19298,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.listings.read"] }, @@ -19395,7 +19395,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-clients.read"] }, @@ -19487,7 +19487,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-services.read"] }, @@ -19555,7 +19555,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.dealsplits.read_write"] }, @@ -19608,7 +19608,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.companies.write"] }, @@ -19661,7 +19661,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -19714,7 +19714,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.deals.write"] }, @@ -19775,7 +19775,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -19908,7 +19908,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -20033,7 +20033,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["tickets"] }, @@ -20110,7 +20110,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -20245,7 +20245,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.lists.write", @@ -20294,7 +20294,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.listings.write"] }, @@ -20342,7 +20342,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.lists.write", @@ -20399,7 +20399,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -20502,7 +20502,7 @@ "description": "Retrieve limits and usage for HubSpot CRM pipelines.\n\nCall this tool to obtain information about the limits and current usage of pipelines in HubSpot CRM. Useful for understanding capacity and utilization of your CRM pipelines.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -20610,7 +20610,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.dealsplits.read_write"] }, @@ -20671,7 +20671,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [] }, @@ -20737,7 +20737,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -20909,7 +20909,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -20998,7 +20998,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -21140,7 +21140,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -21307,7 +21307,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.services.read"] }, @@ -21372,7 +21372,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.lists.write", @@ -21448,7 +21448,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -21599,7 +21599,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-clients.write"] }, @@ -21678,7 +21678,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-services.write"] }, @@ -21733,7 +21733,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.products.write"] }, @@ -21782,7 +21782,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.lists.write", @@ -21839,7 +21839,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.lists.write", @@ -21928,7 +21928,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -22047,7 +22047,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -22129,7 +22129,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.lists.write", @@ -22216,7 +22216,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.companies.read"] }, @@ -22297,7 +22297,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -22363,7 +22363,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.companies.read"] }, @@ -22429,7 +22429,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -22495,7 +22495,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -22588,7 +22588,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.carts.read"] }, @@ -22669,7 +22669,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.carts.read"] }, @@ -22762,7 +22762,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.commercepayments.read"] }, @@ -22843,7 +22843,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.commercepayments.read"] }, @@ -22936,7 +22936,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.companies.read"] }, @@ -23001,7 +23001,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.lists.read"] }, @@ -23092,7 +23092,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -23245,7 +23245,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.deals.read"] }, @@ -23338,7 +23338,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -23419,7 +23419,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -23512,7 +23512,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -23593,7 +23593,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -23686,7 +23686,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -23759,7 +23759,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.companies.highly_sensitive.read.v2", @@ -23829,7 +23829,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.companies.highly_sensitive.read.v2", @@ -23907,7 +23907,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.contacts.read", @@ -23976,7 +23976,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -24026,7 +24026,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.lists.read"] }, @@ -24082,7 +24082,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.goals.read"] }, @@ -24156,7 +24156,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -24294,7 +24294,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.orders.read"] }, @@ -24360,7 +24360,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.products.read"] }, @@ -24426,7 +24426,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -24555,7 +24555,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -24690,7 +24690,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.courses.read"] }, @@ -24756,7 +24756,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -24822,7 +24822,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.read"] }, @@ -24888,7 +24888,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.invoices.read"] }, @@ -24981,7 +24981,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.invoices.read"] }, @@ -25062,7 +25062,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.leads.read"] }, @@ -25103,7 +25103,7 @@ "description": "Retrieve objects nearing or at HubSpot CRM association limits.\n\nThis tool is called to get objects from HubSpot CRM that have records close to or at their association limits. It helps in monitoring and managing association constraints.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -25246,7 +25246,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -25327,7 +25327,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -25420,7 +25420,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -25501,7 +25501,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -25567,7 +25567,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.owners.read"] }, @@ -25641,7 +25641,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.owners.read"] }, @@ -25712,7 +25712,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-services.read"] }, @@ -25778,7 +25778,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.quotes.read"] }, @@ -25844,7 +25844,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.subscriptions.read"] }, @@ -25910,7 +25910,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -25976,7 +25976,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [] }, @@ -26042,7 +26042,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.lists.write", @@ -26112,7 +26112,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -26237,7 +26237,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -26290,7 +26290,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.commercepayments.read"] }, @@ -26343,7 +26343,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.companies.read"] }, @@ -26396,7 +26396,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -26449,7 +26449,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.leads.read"] }, @@ -26502,7 +26502,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -26555,7 +26555,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.read"] }, @@ -26608,7 +26608,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.deals.read"] }, @@ -26661,7 +26661,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -26714,7 +26714,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -26767,7 +26767,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.contacts.read", @@ -26823,7 +26823,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -26876,7 +26876,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.goals.read"] }, @@ -26929,7 +26929,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.carts.read"] }, @@ -26982,7 +26982,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.listings.read"] }, @@ -27078,7 +27078,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.lists.read"] }, @@ -27156,7 +27156,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -27209,7 +27209,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -27270,7 +27270,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -27395,7 +27395,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [] }, @@ -27448,7 +27448,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.products.read"] }, @@ -27501,7 +27501,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.subscriptions.read"] }, @@ -27554,7 +27554,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -27607,7 +27607,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.invoices.read"] }, @@ -27660,7 +27660,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -27713,7 +27713,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.orders.read"] }, @@ -27766,7 +27766,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-clients.read"] }, @@ -27819,7 +27819,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-services.read"] }, @@ -27872,7 +27872,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.read"] }, @@ -27925,7 +27925,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.quotes.read"] }, @@ -27978,7 +27978,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.read"] }, @@ -28031,7 +28031,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["tickets"] }, @@ -28084,7 +28084,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.deals.write", @@ -28147,7 +28147,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.lists.read"] }, @@ -28198,7 +28198,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.lists.read"] }, @@ -28246,7 +28246,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -28299,7 +28299,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -28352,7 +28352,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -28405,7 +28405,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -28458,7 +28458,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.goals.write"] }, @@ -28511,7 +28511,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.companies.write"] }, @@ -28564,7 +28564,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -28617,7 +28617,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -28686,7 +28686,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -28765,7 +28765,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.carts.write"] }, @@ -28828,7 +28828,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.carts.write"] }, @@ -28897,7 +28897,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.commercepayments.write"] }, @@ -28960,7 +28960,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.commercepayments.write"] }, @@ -29029,7 +29029,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -29108,7 +29108,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -29171,7 +29171,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.courses.write"] }, @@ -29291,7 +29291,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.schemas.custom.write"] }, @@ -29400,7 +29400,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -29463,7 +29463,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -29532,7 +29532,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -29611,7 +29611,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -29690,7 +29690,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.goals.write"] }, @@ -29769,7 +29769,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.companies.write"] }, @@ -29848,7 +29848,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.courses.write"] }, @@ -29927,7 +29927,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.deals.write"] }, @@ -30006,7 +30006,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -30085,7 +30085,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -30172,7 +30172,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -30315,7 +30315,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -30440,7 +30440,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.orders.write"] }, @@ -30493,7 +30493,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.products.write"] }, @@ -30562,7 +30562,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -30641,7 +30641,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.write"] }, @@ -30720,7 +30720,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.invoices.write"] }, @@ -30783,7 +30783,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.invoices.write"] }, @@ -30852,7 +30852,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.leads.write"] }, @@ -30915,7 +30915,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.leads.write"] }, @@ -30984,7 +30984,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -31047,7 +31047,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -31110,7 +31110,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.lists.write", @@ -31180,7 +31180,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.lists.write", @@ -31242,7 +31242,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -31295,7 +31295,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -31348,7 +31348,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.listings.write"] }, @@ -31401,7 +31401,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.deals.write"] }, @@ -31462,7 +31462,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -31587,7 +31587,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -31640,7 +31640,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.write"] }, @@ -31701,7 +31701,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -31842,7 +31842,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.orders.write"] }, @@ -31921,7 +31921,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-clients.write"] }, @@ -31984,7 +31984,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-clients.write"] }, @@ -32053,7 +32053,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-services.write"] }, @@ -32116,7 +32116,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.partner-services.write"] }, @@ -32209,7 +32209,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -32333,7 +32333,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -32439,7 +32439,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -32518,7 +32518,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.products.write"] }, @@ -32597,7 +32597,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -32698,7 +32698,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.schemas.carts.write", @@ -32783,7 +32783,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.quotes.write"] }, @@ -32852,7 +32852,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.quotes.write"] }, @@ -32915,7 +32915,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.services.write"] }, @@ -32984,7 +32984,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -33114,7 +33114,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -33250,7 +33250,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -33313,7 +33313,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["tickets"] }, @@ -33382,7 +33382,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["tickets"] }, @@ -33445,7 +33445,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.extensions_calling_transcripts.write"] }, @@ -33498,7 +33498,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -33551,7 +33551,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.carts.write"] }, @@ -33604,7 +33604,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.commercepayments.write"] }, @@ -33657,7 +33657,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -33710,7 +33710,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -33763,7 +33763,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["tickets"] }, @@ -33816,7 +33816,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -33869,7 +33869,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.goals.write"] }, @@ -33922,7 +33922,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -33975,7 +33975,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.invoices.write"] }, @@ -34028,7 +34028,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.products.write"] }, @@ -34089,7 +34089,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", @@ -34214,7 +34214,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -34267,7 +34267,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.write"] }, @@ -34320,7 +34320,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.line_items.write"] }, @@ -34373,7 +34373,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -34426,7 +34426,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -34479,7 +34479,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.orders.write"] }, @@ -34532,7 +34532,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.contacts.write"] }, @@ -34585,7 +34585,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.carts.read", diff --git a/toolkit-docs-generator/data/toolkits/hubspoteventsapi.json b/toolkit-docs-generator/data/toolkits/hubspoteventsapi.json index 721bce113..7ce5f3bc5 100644 --- a/toolkit-docs-generator/data/toolkits/hubspoteventsapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspoteventsapi.json @@ -15,7 +15,7 @@ }, "auth": { "type": "oauth2", - "providerId": null, + "providerId": "hubspot", "allScopes": ["behavioral_events.event_definitions.read_write", "oauth"] }, "tools": [ @@ -35,7 +35,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["behavioral_events.event_definitions.read_write"] }, @@ -83,7 +83,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["behavioral_events.event_definitions.read_write"] }, @@ -128,7 +128,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["behavioral_events.event_definitions.read_write"] }, @@ -159,7 +159,7 @@ "description": "Retrieve a list of visible event type names.\n\nThis tool retrieves a list of event type names that are visible to the user, which can be used to query specific event instances.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["oauth"] }, @@ -225,7 +225,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["behavioral_events.event_definitions.read_write"] }, @@ -301,7 +301,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["behavioral_events.event_definitions.read_write"] }, diff --git a/toolkit-docs-generator/data/toolkits/hubspotmarketingapi.json b/toolkit-docs-generator/data/toolkits/hubspotmarketingapi.json index 2f8e80acf..bb46930af 100644 --- a/toolkit-docs-generator/data/toolkits/hubspotmarketingapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspotmarketingapi.json @@ -15,7 +15,7 @@ }, "auth": { "type": "oauth2", - "providerId": null, + "providerId": "hubspot", "allScopes": [ "content", "crm.objects.marketing_events.read", @@ -77,7 +77,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.write"] }, @@ -145,7 +145,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.write"] }, @@ -190,7 +190,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["forms"] }, @@ -246,7 +246,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.write"] }, @@ -304,7 +304,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -357,7 +357,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -418,7 +418,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -476,7 +476,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -553,7 +553,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.write"] }, @@ -621,7 +621,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.write"] }, @@ -674,7 +674,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.write"] }, @@ -727,7 +727,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -780,7 +780,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -833,7 +833,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["forms"] }, @@ -886,7 +886,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["transactional-email"] }, @@ -939,7 +939,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.write"] }, @@ -984,7 +984,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.write"] }, @@ -1024,7 +1024,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -1072,7 +1072,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -1125,7 +1125,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -1170,7 +1170,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["transactional-email"] }, @@ -1226,7 +1226,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.write"] }, @@ -1284,7 +1284,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -1345,7 +1345,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -1435,7 +1435,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.read"] }, @@ -1491,7 +1491,7 @@ "description": "Fetch aggregated email statistics in specified intervals.\n\nThis tool retrieves aggregated email statistics over specified time intervals, providing insights into email performance during each period.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -1525,7 +1525,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.read"] }, @@ -1598,7 +1598,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.read"] }, @@ -1658,7 +1658,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.read"] }, @@ -1706,7 +1706,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.read"] }, @@ -1767,7 +1767,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.read"] }, @@ -1825,7 +1825,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.read"] }, @@ -1870,7 +1870,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.read"] }, @@ -1935,7 +1935,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.read"] }, @@ -2014,7 +2014,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.revenue.read"] }, @@ -2077,7 +2077,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.read"] }, @@ -2146,7 +2146,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.read"] }, @@ -2201,7 +2201,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2241,7 +2241,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2272,7 +2272,7 @@ "description": "Get aggregated email statistics in a specified time span.\n\nUse this tool to obtain aggregated statistics for emails sent within a specific time period. It also provides a list of all emails sent during that period.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2314,7 +2314,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.read"] }, @@ -2367,7 +2367,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["forms"] }, @@ -2445,7 +2445,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.read"] }, @@ -2505,7 +2505,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2553,7 +2553,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -2606,7 +2606,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -2659,7 +2659,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.read"] }, @@ -2704,7 +2704,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -2784,7 +2784,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.read"] }, @@ -2849,7 +2849,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.read"] }, @@ -2921,7 +2921,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.read"] }, @@ -2981,7 +2981,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["transactional-email"] }, @@ -3012,7 +3012,7 @@ "description": "Retrieve and filter HubSpot marketing emails.\n\nUse this tool to retrieve and filter marketing emails from HubSpot. It allows you to apply various filters to find specific sets of emails based on your criteria.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -3062,7 +3062,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -3152,7 +3152,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.read"] }, @@ -3242,7 +3242,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["forms"] }, @@ -3321,7 +3321,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -3392,7 +3392,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -3442,7 +3442,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing-email", "transactional-email"] }, @@ -3506,7 +3506,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["transactional-email"] }, @@ -3585,7 +3585,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -3672,7 +3672,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -3764,7 +3764,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -3856,7 +3856,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -3948,7 +3948,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -4032,7 +4032,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -4095,7 +4095,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.write"] }, @@ -4140,7 +4140,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -4188,7 +4188,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -4233,7 +4233,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -4273,7 +4273,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["transactional-email"] }, @@ -4321,7 +4321,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -4374,7 +4374,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -4419,7 +4419,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -4459,7 +4459,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.read"] }, @@ -4507,7 +4507,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing-email", "transactional-email"] }, @@ -4560,7 +4560,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["transactional-email"] }, @@ -4605,7 +4605,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing-email", "transactional-email"] }, @@ -4653,7 +4653,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.write"] }, @@ -4738,7 +4738,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.write"] }, @@ -4819,7 +4819,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.write"] }, @@ -4909,7 +4909,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["marketing.campaigns.write"] }, @@ -4990,7 +4990,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -5056,7 +5056,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["forms"] }, @@ -5122,7 +5122,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["forms"] }, @@ -5188,7 +5188,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["content"] }, @@ -5262,7 +5262,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -5325,7 +5325,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -5386,7 +5386,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, @@ -5444,7 +5444,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.marketing_events.write"] }, diff --git a/toolkit-docs-generator/data/toolkits/hubspotmeetingsapi.json b/toolkit-docs-generator/data/toolkits/hubspotmeetingsapi.json index a15d579c2..8c1010b2a 100644 --- a/toolkit-docs-generator/data/toolkits/hubspotmeetingsapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspotmeetingsapi.json @@ -15,7 +15,7 @@ }, "auth": { "type": "oauth2", - "providerId": null, + "providerId": "hubspot", "allScopes": [ "crm.objects.appointments.sensitive.write.v2", "crm.objects.appointments.write", @@ -66,7 +66,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.listings.write", @@ -133,7 +133,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["scheduler.meetings.meeting-link.read"] }, @@ -173,7 +173,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["scheduler.meetings.meeting-link.read"] }, @@ -204,7 +204,7 @@ "description": "Retrieve a paged list of meeting scheduling links.\n\nUse this tool to get a paged list of meeting scheduling pages from Hubspot. It is useful when you need to see or manage the scheduling links available.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["scheduler.meetings.meeting-link.read"] }, @@ -246,7 +246,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": [ "crm.objects.listings.write", diff --git a/toolkit-docs-generator/data/toolkits/hubspotusersapi.json b/toolkit-docs-generator/data/toolkits/hubspotusersapi.json index 34647e283..440989c7b 100644 --- a/toolkit-docs-generator/data/toolkits/hubspotusersapi.json +++ b/toolkit-docs-generator/data/toolkits/hubspotusersapi.json @@ -15,7 +15,7 @@ }, "auth": { "type": "oauth2", - "providerId": null, + "providerId": "hubspot", "allScopes": [ "crm.objects.users.read", "crm.objects.users.write", @@ -90,7 +90,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.write", "settings.users.write"] }, @@ -168,7 +168,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.read", "settings.users.read"] }, @@ -204,7 +204,7 @@ "description": "Retrieve all user roles from an account.\n\nThis tool is called to fetch and list all the roles available on a specific account from Hubspot.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.read", "settings.users.read"] }, @@ -246,7 +246,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.write", "settings.users.write"] }, @@ -299,7 +299,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.read", "settings.users.read"] }, @@ -393,7 +393,7 @@ } ], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["crm.objects.users.write", "settings.users.write"] }, @@ -454,7 +454,7 @@ "description": "Retrieve all teams for the account.\n\nCall this tool to view all teams associated with a specific account in Hubspot. Useful for managing or reviewing team structures.", "parameters": [], "auth": { - "providerId": null, + "providerId": "hubspot", "providerType": "oauth2", "scopes": ["settings.users.teams.read"] }, diff --git a/toolkit-docs-generator/data/toolkits/salesforce.json b/toolkit-docs-generator/data/toolkits/salesforce.json index 4cf3a6d4b..44633b862 100644 --- a/toolkit-docs-generator/data/toolkits/salesforce.json +++ b/toolkit-docs-generator/data/toolkits/salesforce.json @@ -15,7 +15,7 @@ }, "auth": { "type": "oauth2", - "providerId": null, + "providerId": "salesforce", "allScopes": [ "read_account", "read_contact", @@ -107,7 +107,7 @@ } ], "auth": { - "providerId": null, + "providerId": "salesforce", "providerType": "oauth2", "scopes": ["write_contact"] }, @@ -199,7 +199,7 @@ } ], "auth": { - "providerId": null, + "providerId": "salesforce", "providerType": "oauth2", "scopes": [ "read_account", @@ -274,7 +274,7 @@ } ], "auth": { - "providerId": null, + "providerId": "salesforce", "providerType": "oauth2", "scopes": [ "read_account", diff --git a/toolkit-docs-generator/data/toolkits/ticktickapi.json b/toolkit-docs-generator/data/toolkits/ticktickapi.json index 98cb71160..fc7b8af35 100644 --- a/toolkit-docs-generator/data/toolkits/ticktickapi.json +++ b/toolkit-docs-generator/data/toolkits/ticktickapi.json @@ -15,7 +15,7 @@ }, "auth": { "type": "oauth2", - "providerId": null, + "providerId": "ticktick", "allScopes": ["tasks:read", "tasks:write"] }, "tools": [ @@ -67,7 +67,7 @@ } ], "auth": { - "providerId": null, + "providerId": "ticktick", "providerType": "oauth2", "scopes": ["tasks:write"] }, @@ -135,7 +135,7 @@ } ], "auth": { - "providerId": null, + "providerId": "ticktick", "providerType": "oauth2", "scopes": ["tasks:write"] }, @@ -188,7 +188,7 @@ } ], "auth": { - "providerId": null, + "providerId": "ticktick", "providerType": "oauth2", "scopes": ["tasks:write"] }, @@ -233,7 +233,7 @@ } ], "auth": { - "providerId": null, + "providerId": "ticktick", "providerType": "oauth2", "scopes": ["tasks:write"] }, @@ -273,7 +273,7 @@ } ], "auth": { - "providerId": null, + "providerId": "ticktick", "providerType": "oauth2", "scopes": ["tasks:read"] }, @@ -304,7 +304,7 @@ "description": "Retrieve all user-accessible projects from Ticktick.\n\nThis tool fetches a list of all projects that the authenticated user has access to in Ticktick. It should be called when a user wants to view or manage their projects within the platform.", "parameters": [], "auth": { - "providerId": null, + "providerId": "ticktick", "providerType": "oauth2", "scopes": ["tasks:read"] }, @@ -346,7 +346,7 @@ } ], "auth": { - "providerId": null, + "providerId": "ticktick", "providerType": "oauth2", "scopes": ["tasks:write"] }, @@ -391,7 +391,7 @@ } ], "auth": { - "providerId": null, + "providerId": "ticktick", "providerType": "oauth2", "scopes": ["tasks:read"] }, @@ -439,7 +439,7 @@ } ], "auth": { - "providerId": null, + "providerId": "ticktick", "providerType": "oauth2", "scopes": ["tasks:read"] }, @@ -524,7 +524,7 @@ } ], "auth": { - "providerId": null, + "providerId": "ticktick", "providerType": "oauth2", "scopes": ["tasks:write"] }, @@ -605,7 +605,7 @@ } ], "auth": { - "providerId": null, + "providerId": "ticktick", "providerType": "oauth2", "scopes": ["tasks:write"] }, diff --git a/toolkit-docs-generator/data/toolkits/zendesk.json b/toolkit-docs-generator/data/toolkits/zendesk.json index 69caa9bd7..82ed0185e 100644 --- a/toolkit-docs-generator/data/toolkits/zendesk.json +++ b/toolkit-docs-generator/data/toolkits/zendesk.json @@ -15,7 +15,7 @@ }, "auth": { "type": "oauth2", - "providerId": null, + "providerId": "zendesk", "allScopes": ["read", "tickets:write"] }, "tools": [ @@ -51,7 +51,7 @@ } ], "auth": { - "providerId": null, + "providerId": "zendesk", "providerType": "oauth2", "scopes": ["tickets:write"] }, @@ -106,7 +106,7 @@ } ], "auth": { - "providerId": null, + "providerId": "zendesk", "providerType": "oauth2", "scopes": ["read"] }, @@ -175,7 +175,7 @@ } ], "auth": { - "providerId": null, + "providerId": "zendesk", "providerType": "oauth2", "scopes": ["read"] }, @@ -251,7 +251,7 @@ } ], "auth": { - "providerId": null, + "providerId": "zendesk", "providerType": "oauth2", "scopes": ["tickets:write"] }, @@ -387,7 +387,7 @@ } ], "auth": { - "providerId": null, + "providerId": "zendesk", "providerType": "oauth2", "scopes": ["read"] }, @@ -473,7 +473,7 @@ "description": "Get comprehensive user profile and Zendesk account information.\n\nThis tool provides detailed information about the authenticated user including\ntheir name, email, role, organization details, and Zendesk account context.", "parameters": [], "auth": { - "providerId": null, + "providerId": "zendesk", "providerType": "oauth2", "scopes": ["read"] }, diff --git a/toolkit-docs-generator/src/cli/index.ts b/toolkit-docs-generator/src/cli/index.ts index 1bdfb6b1b..19dfa7768 100644 --- a/toolkit-docs-generator/src/cli/index.ts +++ b/toolkit-docs-generator/src/cli/index.ts @@ -42,6 +42,7 @@ import { createCustomSectionsFileSource } from "../sources/custom-sections-file. import { createDesignSystemMetadataSource } from "../sources/design-system-metadata.js"; import { createEmptyCustomSectionsSource } from "../sources/in-memory.js"; import { createMockMetadataSource } from "../sources/mock-metadata.js"; +import { createDesignSystemProviderIdResolver } from "../sources/oauth-provider-resolver.js"; import { createOverviewInstructionsFileSource } from "../sources/overview-instructions-file.js"; import { createArcadeToolkitDataSource, @@ -839,7 +840,6 @@ program .option("--skip-examples", "Skip LLM example generation", false) .option("--skip-summary", "Skip LLM summary generation", false) .option("--skip-overview", "Skip LLM overview generation", false) - .option("--skip-overview", "Skip LLM overview generation", false) .option("--no-verify-output", "Skip output verification") .option("--custom-sections ", "Path to custom sections JSON") .option( @@ -1059,6 +1059,10 @@ program allowMissing: true, }); + // Build provider ID resolver from design system OAuth catalogue + const resolveProviderId = + (await createDesignSystemProviderIdResolver()) ?? undefined; + spinner.succeed("Data sources initialized"); // Create generator (needed early for resume check) @@ -1202,6 +1206,7 @@ program ...(runAll ? { onToolkitProgress } : {}), ...(skipToolkitIds.size > 0 ? { skipToolkitIds } : {}), ...(onToolkitComplete ? { onToolkitComplete } : {}), + ...(resolveProviderId ? { resolveProviderId } : {}), }); // Process toolkits @@ -1284,6 +1289,7 @@ program onToolkitProgress, ...(skipToolkitIds.size > 0 ? { skipToolkitIds } : {}), ...(onToolkitComplete ? { onToolkitComplete } : {}), + ...(resolveProviderId ? { resolveProviderId } : {}), }); spinner.start(progressTracker.getProgressString()); @@ -1687,6 +1693,10 @@ program allowMissing: true, }); + // Build provider ID resolver from design system OAuth catalogue + const resolveProviderId = + (await createDesignSystemProviderIdResolver()) ?? undefined; + spinner.succeed("Data sources initialized"); // Create generator (needed early for resume check) @@ -1799,6 +1809,7 @@ program onToolkitProgress, ...(skipToolkitIds.size > 0 ? { skipToolkitIds } : {}), ...(onToolkitComplete ? { onToolkitComplete } : {}), + ...(resolveProviderId ? { resolveProviderId } : {}), }); spinner.start(progressTracker.getProgressString()); diff --git a/toolkit-docs-generator/src/merger/data-merger.ts b/toolkit-docs-generator/src/merger/data-merger.ts index 416c3c257..e1cad0517 100644 --- a/toolkit-docs-generator/src/merger/data-merger.ts +++ b/toolkit-docs-generator/src/merger/data-merger.ts @@ -15,7 +15,10 @@ import type { ICustomSectionsSource, IOverviewInstructionsSource, } from "../sources/interfaces.js"; -import type { IToolkitDataSource } from "../sources/toolkit-data-source.js"; +import type { + IToolkitDataSource, + ToolkitData, +} from "../sources/toolkit-data-source.js"; import type { CustomSections, DocumentationChunk, @@ -29,6 +32,10 @@ import type { ToolkitMetadata, } from "../types/index.js"; import { mapWithConcurrency } from "../utils/concurrency.js"; +import { + detectMetadataChanges, + formatFreshnessWarnings, +} from "./metadata-freshness.js"; // ============================================================================ // Merger Configuration @@ -59,6 +66,8 @@ export interface DataMergerConfig { onToolkitComplete?: ((result: MergeResult) => Promise) | undefined; /** Set of toolkit IDs to skip (for resume support) */ skipToolkitIds?: ReadonlySet | undefined; + /** Fallback resolver: toolkit ID → OAuth provider ID (design system) */ + resolveProviderId?: ((toolkitId: string) => string | null) | undefined; } export interface FailedTool { @@ -94,6 +103,8 @@ interface MergeToolkitOptions { overviewGenerator?: ToolkitOverviewGenerator; overviewInstructions?: ToolkitOverviewInstructions | null; skipOverview?: boolean; + /** Fallback resolver: toolkit ID → OAuth provider ID (design system) */ + resolveProviderId?: (toolkitId: string) => string | null; } // ============================================================================ @@ -702,7 +713,40 @@ export const mergeToolkit = async ( const description = getToolkitDescription(tools); - const auth = buildToolkitAuth(tools); + // Fallback: resolve OAuth provider IDs from the design system when Engine API returns null. + // We apply this at the tool level (so examples/signatures stay consistent) and then at the + // toolkit level (as a final guard). + const resolvedProviderId = + options.resolveProviderId && + tools.some( + (tool) => + tool.auth?.providerType === "oauth2" && tool.auth.providerId == null + ) + ? options.resolveProviderId(toolkitId) + : null; + + const toolsWithResolvedProviderId = + resolvedProviderId !== null + ? tools.map((tool) => { + if ( + tool.auth?.providerType === "oauth2" && + tool.auth.providerId == null + ) { + return { + ...tool, + auth: { ...tool.auth, providerId: resolvedProviderId }, + }; + } + return tool; + }) + : tools; + + let auth = buildToolkitAuth(toolsWithResolvedProviderId); + + // Fallback: resolve provider ID from design system when Engine API returns null + if (auth && !auth.providerId && resolvedProviderId) { + auth = { ...auth, providerId: resolvedProviderId }; + } const toolChunks = (customSections?.toolChunks ?? {}) as { [key: string]: DocumentationChunk[]; @@ -712,7 +756,7 @@ export const mergeToolkit = async ( options.previousToolkit ); const mergedTools = await buildMergedTools({ - tools, + tools: toolsWithResolvedProviderId, toolChunks, toolExampleGenerator, warnings, @@ -732,6 +776,17 @@ export const mergeToolkit = async ( previousToolkit: options.previousToolkit, }); + // Modular step: detect design system metadata drifts + const freshnessResult = detectMetadataChanges( + toolkitId, + toolkit.metadata, + toolkit.label, + options.previousToolkit + ); + if (freshnessResult) { + warnings.push(...formatFreshnessWarnings(freshnessResult)); + } + await applyOverviewIfNeeded({ toolkit, toolkitId, @@ -773,6 +828,9 @@ export class DataMerger { | ((result: MergeResult) => Promise) | undefined; private readonly skipToolkitIds: ReadonlySet; + private readonly resolveProviderId: + | ((toolkitId: string) => string | null) + | undefined; constructor(config: DataMergerConfig) { this.toolkitDataSource = config.toolkitDataSource; @@ -790,6 +848,7 @@ export class DataMerger { this.onToolkitProgress = config.onToolkitProgress; this.onToolkitComplete = config.onToolkitComplete; this.skipToolkitIds = config.skipToolkitIds ?? new Set(); + this.resolveProviderId = config.resolveProviderId; } private getPreviousToolkit(toolkitId: string): MergedToolkit | undefined { @@ -803,6 +862,84 @@ export class DataMerger { ); } + private buildMergeErrorResult( + toolkitId: string, + message: string + ): MergeResult { + return { + toolkit: { + id: toolkitId, + label: toolkitId, + version: "0.0.0", + description: null, + metadata: { + category: "development", + iconUrl: "", + isBYOC: false, + isPro: false, + type: isStarterToolkitId(toolkitId) ? "arcade_starter" : "arcade", + docsLink: "", + isComingSoon: false, + isHidden: false, + }, + auth: null, + tools: [], + documentationChunks: [], + customImports: [], + subPages: [], + generatedAt: new Date().toISOString(), + }, + warnings: [`Error processing toolkit: ${message}`], + failedTools: [], + }; + } + + private async mergeToolkitEntry( + toolkitId: string, + toolkitData: ToolkitData + ): Promise { + try { + const customSections = + await this.customSectionsSource.getCustomSections(toolkitId); + const overviewInstructions = + await this.overviewInstructionsSource.getOverviewInstructions( + toolkitId + ); + + const previousToolkit = this.getPreviousToolkit(toolkitId); + const result = await mergeToolkit( + toolkitId, + toolkitData.tools, + toolkitData.metadata, + customSections, + this.toolExampleGenerator, + { + ...(previousToolkit ? { previousToolkit } : {}), + llmConcurrency: this.llmConcurrency, + ...(this.overviewGenerator + ? { overviewGenerator: this.overviewGenerator } + : {}), + overviewInstructions, + skipOverview: this.skipOverview, + ...(this.resolveProviderId + ? { resolveProviderId: this.resolveProviderId } + : {}), + } + ); + await this.maybeGenerateSummary(result, previousToolkit); + + // Write immediately if callback provided (incremental mode) + if (this.onToolkitComplete) { + await this.onToolkitComplete(result); + } + + return result; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return this.buildMergeErrorResult(toolkitId, message); + } + } + private async maybeGenerateSummary( result: MergeResult, previousToolkit?: MergedToolkit @@ -865,9 +1002,14 @@ export class DataMerger { { ...(previousToolkit ? { previousToolkit } : {}), llmConcurrency: this.llmConcurrency, - overviewGenerator: this.overviewGenerator, + ...(this.overviewGenerator + ? { overviewGenerator: this.overviewGenerator } + : {}), overviewInstructions, skipOverview: this.skipOverview, + ...(this.resolveProviderId + ? { resolveProviderId: this.resolveProviderId } + : {}), } ); await this.maybeGenerateSummary(result, previousToolkit); @@ -893,80 +1035,15 @@ export class DataMerger { async ([toolkitId, toolkitData]) => { this.onToolkitProgress?.(toolkitId, "start"); - try { - const customSections = - await this.customSectionsSource.getCustomSections(toolkitId); - const overviewInstructions = - await this.overviewInstructionsSource.getOverviewInstructions( - toolkitId - ); - - const previousToolkit = this.getPreviousToolkit(toolkitId); - const result = await mergeToolkit( - toolkitId, - toolkitData.tools, - toolkitData.metadata, - customSections, - this.toolExampleGenerator, - { - ...(previousToolkit ? { previousToolkit } : {}), - llmConcurrency: this.llmConcurrency, - overviewGenerator: this.overviewGenerator, - overviewInstructions, - skipOverview: this.skipOverview, - } - ); - await this.maybeGenerateSummary(result, previousToolkit); - - // Write immediately if callback provided (incremental mode) - if (this.onToolkitComplete) { - await this.onToolkitComplete(result); - } + const result = await this.mergeToolkitEntry(toolkitId, toolkitData); - this.onToolkitProgress?.( - toolkitId, - "done", - result.toolkit.tools.length - ); - - return result; - } catch (error) { - // Report error but don't stop processing other toolkits - const message = - error instanceof Error ? error.message : String(error); - const errorResult: MergeResult = { - toolkit: { - id: toolkitId, - label: toolkitId, - version: "0.0.0", - description: null, - metadata: { - category: "development", - iconUrl: "", - isBYOC: false, - isPro: false, - type: isStarterToolkitId(toolkitId) - ? "arcade_starter" - : "arcade", - docsLink: "", - isComingSoon: false, - isHidden: false, - }, - auth: null, - tools: [], - documentationChunks: [], - customImports: [], - subPages: [], - generatedAt: new Date().toISOString(), - }, - warnings: [`Error processing toolkit: ${message}`], - failedTools: [], - }; - - this.onToolkitProgress?.(toolkitId, "done", 0); - - return errorResult; - } + this.onToolkitProgress?.( + toolkitId, + "done", + result.toolkit.tools.length + ); + + return result; }, this.toolkitConcurrency ); diff --git a/toolkit-docs-generator/src/merger/index.ts b/toolkit-docs-generator/src/merger/index.ts index 86f15c8fe..64bcd7804 100644 --- a/toolkit-docs-generator/src/merger/index.ts +++ b/toolkit-docs-generator/src/merger/index.ts @@ -2,3 +2,4 @@ * Merger module exports */ export * from "./data-merger.js"; +export * from "./metadata-freshness.js"; diff --git a/toolkit-docs-generator/src/merger/metadata-freshness.ts b/toolkit-docs-generator/src/merger/metadata-freshness.ts new file mode 100644 index 000000000..9528a243b --- /dev/null +++ b/toolkit-docs-generator/src/merger/metadata-freshness.ts @@ -0,0 +1,162 @@ +/** + * Metadata Freshness Check + * + * Compares the previously generated toolkit metadata with the current + * design system metadata and reports field-level drifts. + * + * This is a modular, pure step that can run as part of the merge pipeline + * or independently (e.g. in a CI check). + */ +import type { MergedToolkit, MergedToolkitMetadata } from "../types/index.js"; + +// ============================================================================ +// Types +// ============================================================================ + +/** A single field that differs between previous and current metadata. */ +export interface MetadataFieldChange { + readonly field: string; + readonly previous: unknown; + readonly current: unknown; +} + +/** Full result of a metadata freshness check for one toolkit. */ +export interface MetadataFreshnessResult { + readonly toolkitId: string; + /** True when the toolkit transitioned from default metadata to real DS data. */ + readonly gainedDesignSystemMetadata: boolean; + /** True when the toolkit label changed. */ + readonly labelChanged: boolean; + readonly previousLabel: string | null; + readonly currentLabel: string | null; + /** Individual field diffs inside MergedToolkitMetadata. */ + readonly fieldChanges: readonly MetadataFieldChange[]; + /** True if there are any changes at all. */ + readonly hasChanges: boolean; +} + +// ============================================================================ +// Helpers +// ============================================================================ + +/** Fields inside MergedToolkitMetadata we compare. */ +const METADATA_FIELDS: ReadonlyArray = [ + "category", + "iconUrl", + "isBYOC", + "isPro", + "type", + "docsLink", + "isComingSoon", + "isHidden", +]; + +/** + * Heuristic: metadata was built from defaults if the iconUrl follows the + * default pattern AND category is "development" (the hardcoded default). + */ +const looksLikeDefaultMetadata = ( + toolkitId: string, + metadata: MergedToolkitMetadata +): boolean => { + const normalized = toolkitId.toLowerCase().replace(/[^a-z0-9]/g, ""); + const expectedDefaultIcon = `https://design-system.arcade.dev/icons/${ + normalized.endsWith("api") ? normalized.slice(0, -3) : normalized + }.svg`; + + return ( + metadata.iconUrl === expectedDefaultIcon && + metadata.category === "development" + ); +}; + +// ============================================================================ +// Core detection (pure) +// ============================================================================ + +/** + * Detect metadata drifts between a previously generated toolkit and the + * current design system metadata. + * + * Returns null when there is no previous toolkit to compare against + * (nothing to check on a brand-new toolkit). + */ +export const detectMetadataChanges = ( + toolkitId: string, + currentMetadata: MergedToolkitMetadata, + currentLabel: string, + previousToolkit: MergedToolkit | undefined +): MetadataFreshnessResult | null => { + if (!previousToolkit) { + return null; + } + + const prev = previousToolkit.metadata; + const fieldChanges: MetadataFieldChange[] = []; + + for (const field of METADATA_FIELDS) { + const prevValue = prev[field]; + const currValue = currentMetadata[field]; + if (prevValue !== currValue) { + fieldChanges.push({ field, previous: prevValue, current: currValue }); + } + } + + const labelChanged = previousToolkit.label !== currentLabel; + + const gainedDesignSystemMetadata = + looksLikeDefaultMetadata(toolkitId, prev) && + !looksLikeDefaultMetadata(toolkitId, currentMetadata); + + const hasChanges = + fieldChanges.length > 0 || labelChanged || gainedDesignSystemMetadata; + + return { + toolkitId, + gainedDesignSystemMetadata, + labelChanged, + previousLabel: previousToolkit.label, + currentLabel, + fieldChanges, + hasChanges, + }; +}; + +// ============================================================================ +// Warning formatter (for merge pipeline integration) +// ============================================================================ + +/** + * Convert a freshness result into human-readable warning strings + * suitable for the merge pipeline's `warnings` array. + */ +export const formatFreshnessWarnings = ( + result: MetadataFreshnessResult +): string[] => { + if (!result.hasChanges) { + return []; + } + + const warnings: string[] = []; + const prefix = `[metadata-freshness] ${result.toolkitId}`; + + if (result.gainedDesignSystemMetadata) { + warnings.push( + `${prefix}: now has design system metadata (was using defaults)` + ); + } + + if (result.labelChanged) { + warnings.push( + `${prefix}: label changed "${result.previousLabel}" → "${result.currentLabel}"` + ); + } + + for (const change of result.fieldChanges) { + warnings.push( + `${prefix}: ${change.field} changed ${JSON.stringify(change.previous)} → ${JSON.stringify(change.current)}` + ); + } + + return warnings; +}; diff --git a/toolkit-docs-generator/src/sources/engine-api.ts b/toolkit-docs-generator/src/sources/engine-api.ts index 47e83df64..eb5efee15 100644 --- a/toolkit-docs-generator/src/sources/engine-api.ts +++ b/toolkit-docs-generator/src/sources/engine-api.ts @@ -87,11 +87,12 @@ export class EngineApiSource implements IToolDataSource { const url = new URL(this.endpoint); url.searchParams.set("limit", String(this.pageSize)); url.searchParams.set("offset", String(offset)); + // latest_only defaults to true on the server; set to false when we need all versions const includeAllVersions = options?.version ? true : this.includeAllVersions; if (includeAllVersions) { - url.searchParams.set("include_all_versions", "true"); + url.searchParams.set("latest_only", "false"); } if (options?.toolkitId) { @@ -101,7 +102,7 @@ export class EngineApiSource implements IToolDataSource { url.searchParams.set("version", options.version); } if (options?.providerId) { - url.searchParams.set("provider_id", options.providerId); + url.searchParams.set("auth_provider", options.providerId); } const response = await this.fetchFn(url.toString(), { diff --git a/toolkit-docs-generator/src/sources/index.ts b/toolkit-docs-generator/src/sources/index.ts index 015b7664f..9091c565d 100644 --- a/toolkit-docs-generator/src/sources/index.ts +++ b/toolkit-docs-generator/src/sources/index.ts @@ -11,6 +11,7 @@ export * from "./in-memory.js"; export * from "./interfaces.js"; export * from "./mock-engine-api.js"; export * from "./mock-metadata.js"; +export * from "./oauth-provider-resolver.js"; export * from "./overview-instructions-file.js"; export * from "./toolkit-data-source.js"; diff --git a/toolkit-docs-generator/src/sources/oauth-provider-resolver.ts b/toolkit-docs-generator/src/sources/oauth-provider-resolver.ts new file mode 100644 index 000000000..10f82ea95 --- /dev/null +++ b/toolkit-docs-generator/src/sources/oauth-provider-resolver.ts @@ -0,0 +1,117 @@ +/** + * OAuth Provider ID Resolver + * + * Resolves toolkit IDs to OAuth provider IDs using the design system's + * OAUTH_PROVIDER_CATALOGUE. This provides a fallback when the Engine API + * returns provider_id: null for toolkits that have known OAuth providers. + * + * Resolution strategy: + * 1. Exact match (normalized toolkit ID against catalogue keys) + * 2. Strip "api" suffix and try again + * 3. Longest-prefix match (e.g. "hubspotcrm" → "hubspot") + */ + +// ============================================================================ +// Types +// ============================================================================ + +/** Callback that resolves a toolkit ID to an OAuth provider ID, or null. */ +export type ProviderIdResolver = (toolkitId: string) => string | null; + +// ============================================================================ +// Helpers +// ============================================================================ + +const NORMALIZER = /[^a-z0-9]/g; + +/** + * Minimum provider ID length for prefix matching. + * Prevents false positives like "xero" matching "x" (X/Twitter). + */ +const MIN_PREFIX_LENGTH = 3; + +/** Lowercase and strip non-alphanumeric characters. */ +export const normalizeLookup = (value: string): string => + value.toLowerCase().replace(NORMALIZER, ""); + +// ============================================================================ +// Core resolver builder (pure, no external dependencies) +// ============================================================================ + +/** + * Build a provider ID resolver from a set of known provider IDs. + * + * The returned function maps a toolkit ID (e.g. "HubspotCrmApi") to the best + * matching provider ID (e.g. "hubspot"), or null if no match is found. + */ +export function buildProviderIdResolver( + knownProviderIds: ReadonlySet +): ProviderIdResolver { + // Pre-sort by length descending so the longest prefix wins. + const sortedByLength = [...knownProviderIds].sort( + (a, b) => b.length - a.length + ); + + return (toolkitId: string): string | null => { + const normalized = normalizeLookup(toolkitId); + + // 1. Exact match + if (knownProviderIds.has(normalized)) { + return normalized; + } + + // 2. Strip "api" suffix + if (normalized.endsWith("api")) { + const base = normalized.slice(0, -3); + + if (knownProviderIds.has(base)) { + return base; + } + + // 3. Longest-prefix match on the base (e.g. "hubspotcrm" → "hubspot") + // Require the prefix to be at least 3 chars to avoid false positives + // (e.g. "xero" should NOT match "x"). + for (const providerId of sortedByLength) { + if ( + providerId.length >= MIN_PREFIX_LENGTH && + base.startsWith(providerId) && + base !== providerId + ) { + return providerId; + } + } + } + + return null; + }; +} + +// ============================================================================ +// Design System factory (dynamic import) +// ============================================================================ + +/** + * Create a provider ID resolver from the design system's OAUTH_PROVIDER_CATALOGUE. + * + * Uses a dynamic import so the generator can still run in environments where + * `@arcadeai/design-system` is not installed (returns null in that case). + */ +export async function createDesignSystemProviderIdResolver(): Promise { + try { + const designSystem = await import("@arcadeai/design-system"); + const catalogue = ( + designSystem as { + OAUTH_PROVIDER_CATALOGUE?: Record; + } + ).OAUTH_PROVIDER_CATALOGUE; + + if (!catalogue || typeof catalogue !== "object") { + return null; + } + + const knownIds = new Set(Object.keys(catalogue)); + return buildProviderIdResolver(knownIds); + } catch { + return null; + } +} diff --git a/toolkit-docs-generator/src/sources/tool-metadata-schema.ts b/toolkit-docs-generator/src/sources/tool-metadata-schema.ts index 79b7d115b..996b476a8 100644 --- a/toolkit-docs-generator/src/sources/tool-metadata-schema.ts +++ b/toolkit-docs-generator/src/sources/tool-metadata-schema.ts @@ -49,7 +49,10 @@ const ToolMetadataSecretSchema = z.union([ const ToolMetadataRequirementsSchema = z .object({ - authorization: ToolMetadataAuthorizationSchema.optional(), + authorization: z + .array(ToolMetadataAuthorizationSchema) + .nullable() + .optional(), secrets: z.array(ToolMetadataSecretSchema).nullable().optional(), }) .nullable() @@ -68,6 +71,9 @@ const ToolMetadataItemSchema = z.object({ export const ToolMetadataResponseSchema = z.object({ items: z.array(ToolMetadataItemSchema), + limit: z.number().optional(), + offset: z.number().optional(), + page_count: z.number().optional(), total_count: z.number(), }); @@ -135,29 +141,33 @@ const normalizeSecrets = ( export const transformToolMetadataItem = ( apiTool: ToolMetadataItem -): ToolDefinition => ({ - name: apiTool.name, - qualifiedName: apiTool.qualified_name, - fullyQualifiedName: apiTool.fully_qualified_name, - description: apiTool.description, - toolkitDescription: apiTool.toolkit.description, - parameters: apiTool.input.parameters.map(transformParameter), - auth: apiTool.requirements?.authorization - ? { - providerId: apiTool.requirements.authorization.provider_id ?? null, - providerType: - apiTool.requirements.authorization.provider_type ?? "unknown", - scopes: apiTool.requirements.authorization.scopes ?? [], - } - : null, - secrets: normalizeSecrets(apiTool.requirements?.secrets), - output: apiTool.output - ? { - type: apiTool.output.value_schema?.val_type ?? "unknown", - description: apiTool.output.description ?? null, - } - : null, -}); +): ToolDefinition => { + // authorization is now an array; pick the first entry (most tools have 0 or 1) + const authEntry = apiTool.requirements?.authorization?.[0] ?? null; + + return { + name: apiTool.name, + qualifiedName: apiTool.qualified_name, + fullyQualifiedName: apiTool.fully_qualified_name, + description: apiTool.description, + toolkitDescription: apiTool.toolkit.description, + parameters: apiTool.input.parameters.map(transformParameter), + auth: authEntry + ? { + providerId: authEntry.provider_id ?? null, + providerType: authEntry.provider_type ?? "unknown", + scopes: authEntry.scopes ?? [], + } + : null, + secrets: normalizeSecrets(apiTool.requirements?.secrets), + output: apiTool.output + ? { + type: apiTool.output.value_schema?.val_type ?? "unknown", + description: apiTool.output.description ?? null, + } + : null, + }; +}; export const parseToolMetadataResponse = ( payload: unknown diff --git a/toolkit-docs-generator/tests/fixtures/engine-api-response.json b/toolkit-docs-generator/tests/fixtures/engine-api-response.json index 8161a95dc..f6db674ef 100644 --- a/toolkit-docs-generator/tests/fixtures/engine-api-response.json +++ b/toolkit-docs-generator/tests/fixtures/engine-api-response.json @@ -79,13 +79,15 @@ } }, "requirements": { - "authorization": { - "id": "github_oauth", - "provider_id": "github", - "provider_type": "oauth2", - "scopes": ["repo"] - }, - "secrets": null + "authorization": [ + { + "id": "github_oauth", + "provider_id": "github", + "provider_type": "oauth2", + "scopes": ["repo"] + } + ], + "secrets": [] } }, { @@ -145,13 +147,15 @@ } }, "requirements": { - "authorization": { - "id": "github_oauth", - "provider_id": "github", - "provider_type": "oauth2", - "scopes": ["public_repo"] - }, - "secrets": null + "authorization": [ + { + "id": "github_oauth", + "provider_id": "github", + "provider_type": "oauth2", + "scopes": ["public_repo"] + } + ], + "secrets": [] } }, { @@ -222,13 +226,15 @@ } }, "requirements": { - "authorization": { - "id": "github_oauth", - "provider_id": "github", - "provider_type": "oauth2", - "scopes": ["repo"] - }, - "secrets": null + "authorization": [ + { + "id": "github_oauth", + "provider_id": "github", + "provider_type": "oauth2", + "scopes": ["repo"] + } + ], + "secrets": [] } }, { @@ -277,13 +283,15 @@ } }, "requirements": { - "authorization": { - "id": "slack_oauth", - "provider_id": "slack", - "provider_type": "oauth2", - "scopes": ["chat:write"] - }, - "secrets": null + "authorization": [ + { + "id": "slack_oauth", + "provider_id": "slack", + "provider_type": "oauth2", + "scopes": ["chat:write"] + } + ], + "secrets": [] } }, { @@ -321,15 +329,20 @@ } }, "requirements": { - "authorization": { - "id": "slack_oauth", - "provider_id": "slack", - "provider_type": "oauth2", - "scopes": ["channels:read"] - }, - "secrets": null + "authorization": [ + { + "id": "slack_oauth", + "provider_id": "slack", + "provider_type": "oauth2", + "scopes": ["channels:read"] + } + ], + "secrets": [] } } ], + "limit": 25, + "offset": 0, + "page_count": 5, "total_count": 5 } diff --git a/toolkit-docs-generator/tests/merger/data-merger.test.ts b/toolkit-docs-generator/tests/merger/data-merger.test.ts index 0b418253c..cf71a3358 100644 --- a/toolkit-docs-generator/tests/merger/data-merger.test.ts +++ b/toolkit-docs-generator/tests/merger/data-merger.test.ts @@ -724,6 +724,141 @@ describe("mergeToolkit", () => { }); }); +// ============================================================================ +// mergeToolkit provider ID fallback tests +// ============================================================================ + +describe("mergeToolkit resolveProviderId fallback", () => { + it("uses resolveProviderId when tools have null providerId", async () => { + const tools = [ + createTool({ + name: "ReadAccount", + qualifiedName: "Salesforce.ReadAccount", + fullyQualifiedName: "Salesforce.ReadAccount@2.0.1", + auth: { providerId: null, providerType: "oauth2", scopes: ["read"] }, + }), + ]; + + const result = await mergeToolkit( + "Salesforce", + tools, + createMetadata({ id: "Salesforce", label: "Salesforce" }), + null, + undefined, + { + resolveProviderId: (toolkitId) => + toolkitId === "Salesforce" ? "salesforce" : null, + } + ); + + expect(result.toolkit.auth?.providerId).toBe("salesforce"); + expect(result.toolkit.tools[0]?.auth?.providerId).toBe("salesforce"); + }); + + it("does NOT override an existing non-null providerId from tools", async () => { + const tools = [ + createTool({ + name: "CreateIssue", + qualifiedName: "Github.CreateIssue", + fullyQualifiedName: "Github.CreateIssue@2.0.1", + auth: { + providerId: "github", + providerType: "oauth2", + scopes: ["repo"], + }, + }), + ]; + + const result = await mergeToolkit( + "Github", + tools, + createMetadata({ id: "Github", label: "GitHub" }), + null, + undefined, + { + resolveProviderId: () => "should-not-be-used", + } + ); + + expect(result.toolkit.auth?.providerId).toBe("github"); + }); + + it("does NOT call resolver when tools have no auth at all", async () => { + let resolverCalled = false; + const tools = [ + createTool({ + name: "PublicTool", + qualifiedName: "NoAuth.PublicTool", + fullyQualifiedName: "NoAuth.PublicTool@1.0.0", + auth: null, + secrets: [], + }), + ]; + + const result = await mergeToolkit( + "NoAuth", + tools, + createMetadata({ id: "NoAuth", label: "No Auth" }), + null, + undefined, + { + resolveProviderId: () => { + resolverCalled = true; + return "unexpected"; + }, + } + ); + + expect(result.toolkit.auth).toBeNull(); + expect(resolverCalled).toBe(false); + }); + + it("leaves providerId null when resolver returns null", async () => { + const tools = [ + createTool({ + name: "SomeTool", + qualifiedName: "Unknown.SomeTool", + fullyQualifiedName: "Unknown.SomeTool@1.0.0", + auth: { providerId: null, providerType: "oauth2", scopes: [] }, + }), + ]; + + const result = await mergeToolkit( + "Unknown", + tools, + createMetadata({ id: "Unknown", label: "Unknown" }), + null, + undefined, + { + resolveProviderId: () => null, + } + ); + + expect(result.toolkit.auth?.providerId).toBeNull(); + }); + + it("leaves providerId null when no resolver is provided", async () => { + const tools = [ + createTool({ + name: "SomeTool", + qualifiedName: "Unknown.SomeTool", + fullyQualifiedName: "Unknown.SomeTool@1.0.0", + auth: { providerId: null, providerType: "oauth2", scopes: [] }, + }), + ]; + + const result = await mergeToolkit( + "Unknown", + tools, + createMetadata({ id: "Unknown", label: "Unknown" }), + null, + undefined + ); + + expect(result.toolkit.auth?.providerId).toBeNull(); + }); +}); + describe("mergeToolkit overview handling", () => { it("replaces existing overview when instructions are present", async () => { const tool = createTool(); diff --git a/toolkit-docs-generator/tests/merger/metadata-freshness.test.ts b/toolkit-docs-generator/tests/merger/metadata-freshness.test.ts new file mode 100644 index 000000000..4973bf9e5 --- /dev/null +++ b/toolkit-docs-generator/tests/merger/metadata-freshness.test.ts @@ -0,0 +1,493 @@ +/** + * Tests for the Metadata Freshness Check module. + * + * Validates detection of design system metadata drifts between + * previously generated toolkits and the current design system state. + */ +import { describe, expect, it } from "vitest"; +import { + detectMetadataChanges, + formatFreshnessWarnings, + type MetadataFreshnessResult, +} from "../../src/merger/metadata-freshness.js"; +import type { + MergedToolkit, + MergedToolkitMetadata, +} from "../../src/types/index.js"; + +// ============================================================================ +// Fixtures +// ============================================================================ + +const createMetadata = ( + overrides: Partial = {} +): MergedToolkitMetadata => ({ + category: "development", + iconUrl: "https://design-system.arcade.dev/icons/github.svg", + isBYOC: false, + isPro: false, + type: "arcade", + docsLink: "https://docs.arcade.dev/en/mcp-servers/development/github", + isComingSoon: false, + isHidden: false, + ...overrides, +}); + +const createPreviousToolkit = ( + overrides: Partial & { + metadata?: Partial; + label?: string; + } = {} +): MergedToolkit => { + const { metadata: metaOverrides, ...rest } = overrides; + return { + id: "Github", + label: "GitHub", + version: "2.0.1", + description: "Test toolkit", + metadata: createMetadata(metaOverrides), + auth: null, + tools: [], + documentationChunks: [], + customImports: [], + subPages: [], + generatedAt: new Date().toISOString(), + ...rest, + }; +}; + +const assertNonNull = (value: T | null): T => { + if (value === null) { + throw new Error("Expected value to be non-null"); + } + return value; +}; + +// ============================================================================ +// detectMetadataChanges +// ============================================================================ + +describe("detectMetadataChanges", () => { + it("returns null when there is no previous toolkit", () => { + const result = detectMetadataChanges( + "Github", + createMetadata(), + "GitHub", + undefined + ); + + expect(result).toBeNull(); + }); + + it("reports no changes when metadata is identical", () => { + const previous = createPreviousToolkit(); + const result = assertNonNull( + detectMetadataChanges("Github", createMetadata(), "GitHub", previous) + ); + + expect(result.hasChanges).toBe(false); + expect(result.fieldChanges).toHaveLength(0); + expect(result.labelChanged).toBe(false); + expect(result.gainedDesignSystemMetadata).toBe(false); + }); + + it("detects a single field change (iconUrl)", () => { + const previous = createPreviousToolkit({ + metadata: { iconUrl: "https://old-cdn.example.com/github.svg" }, + }); + + const result = assertNonNull( + detectMetadataChanges("Github", createMetadata(), "GitHub", previous) + ); + + expect(result.hasChanges).toBe(true); + expect(result.fieldChanges).toHaveLength(1); + expect(result.fieldChanges[0]).toEqual({ + field: "iconUrl", + previous: "https://old-cdn.example.com/github.svg", + current: "https://design-system.arcade.dev/icons/github.svg", + }); + }); + + it("detects multiple field changes", () => { + const previous = createPreviousToolkit({ + metadata: { + category: "social", + isPro: true, + isHidden: true, + }, + }); + + const current = createMetadata({ + category: "development", + isPro: false, + isHidden: false, + }); + + const result = assertNonNull( + detectMetadataChanges("Github", current, "GitHub", previous) + ); + + expect(result.hasChanges).toBe(true); + expect(result.fieldChanges).toHaveLength(3); + + const changedFields = result.fieldChanges.map((c) => c.field); + expect(changedFields).toContain("category"); + expect(changedFields).toContain("isPro"); + expect(changedFields).toContain("isHidden"); + }); + + it("detects label change", () => { + const previous = createPreviousToolkit({ label: "Github" }); + + const result = assertNonNull( + detectMetadataChanges("Github", createMetadata(), "GitHub", previous) + ); + + expect(result.hasChanges).toBe(true); + expect(result.labelChanged).toBe(true); + expect(result.previousLabel).toBe("Github"); + expect(result.currentLabel).toBe("GitHub"); + expect(result.fieldChanges).toHaveLength(0); + }); + + it("detects transition from default metadata to real DS metadata", () => { + // Previous used defaults: development category + default icon URL pattern + const previous = createPreviousToolkit({ + id: "Pylon", + label: "Pylon", + metadata: { + category: "development", + iconUrl: "https://design-system.arcade.dev/icons/pylon.svg", + docsLink: "https://docs.arcade.dev/en/mcp-servers/development/pylon", + }, + }); + + // Now has real DS metadata with correct category and icon + const current = createMetadata({ + category: "customer-support", + iconUrl: "https://design-system.arcade.dev/icons/pylon.svg", + docsLink: "https://docs.arcade.dev/en/mcp-servers/customer-support/pylon", + }); + + const result = assertNonNull( + detectMetadataChanges("Pylon", current, "Pylon", previous) + ); + + expect(result.hasChanges).toBe(true); + expect(result.gainedDesignSystemMetadata).toBe(true); + // Also reports individual field diffs + const changedFields = result.fieldChanges.map((c) => c.field); + expect(changedFields).toContain("category"); + expect(changedFields).toContain("docsLink"); + }); + + it("does NOT flag gainedDesignSystemMetadata for non-default previous", () => { + // Previous already had real DS metadata (category is "sales", not default) + const previous = createPreviousToolkit({ + id: "Salesforce", + label: "Salesforce", + metadata: { + category: "sales", + iconUrl: "https://design-system.arcade.dev/icons/salesforce.svg", + }, + }); + + const current = createMetadata({ + category: "sales", + iconUrl: "https://design-system.arcade.dev/icons/salesforce-new.svg", + }); + + const result = detectMetadataChanges( + "Salesforce", + current, + "Salesforce", + previous + ); + + const nonNullResult = assertNonNull(result); + + expect(nonNullResult.gainedDesignSystemMetadata).toBe(false); + // But still detects the iconUrl change + expect(nonNullResult.hasChanges).toBe(true); + expect(nonNullResult.fieldChanges).toHaveLength(1); + expect(nonNullResult.fieldChanges[0]?.field).toBe("iconUrl"); + }); + + it("handles *Api toolkit default detection correctly", () => { + // PylonApi defaults: icon is "pylon.svg" (api stripped), category is "development" + const previous = createPreviousToolkit({ + id: "PylonApi", + label: "PylonApi", + metadata: { + category: "development", + iconUrl: "https://design-system.arcade.dev/icons/pylon.svg", + }, + }); + + const current = createMetadata({ + category: "customer-support", + iconUrl: "https://design-system.arcade.dev/icons/pylon.svg", + }); + + const result = detectMetadataChanges( + "PylonApi", + current, + "Pylon API", + previous + ); + + const nonNullResult = assertNonNull(result); + expect(nonNullResult.gainedDesignSystemMetadata).toBe(true); + expect(nonNullResult.labelChanged).toBe(true); + }); +}); + +// ============================================================================ +// formatFreshnessWarnings +// ============================================================================ + +describe("formatFreshnessWarnings", () => { + it("returns empty array when there are no changes", () => { + const result: MetadataFreshnessResult = { + toolkitId: "Github", + gainedDesignSystemMetadata: false, + labelChanged: false, + previousLabel: "GitHub", + currentLabel: "GitHub", + fieldChanges: [], + hasChanges: false, + }; + + expect(formatFreshnessWarnings(result)).toEqual([]); + }); + + it("formats gained DS metadata warning", () => { + const result: MetadataFreshnessResult = { + toolkitId: "Pylon", + gainedDesignSystemMetadata: true, + labelChanged: false, + previousLabel: "Pylon", + currentLabel: "Pylon", + fieldChanges: [], + hasChanges: true, + }; + + const warnings = formatFreshnessWarnings(result); + expect(warnings).toHaveLength(1); + expect(warnings[0]).toContain("[metadata-freshness]"); + expect(warnings[0]).toContain("Pylon"); + expect(warnings[0]).toContain("design system metadata"); + }); + + it("formats label change warning", () => { + const result: MetadataFreshnessResult = { + toolkitId: "Github", + gainedDesignSystemMetadata: false, + labelChanged: true, + previousLabel: "Github", + currentLabel: "GitHub", + fieldChanges: [], + hasChanges: true, + }; + + const warnings = formatFreshnessWarnings(result); + expect(warnings).toHaveLength(1); + expect(warnings[0]).toContain('label changed "Github" → "GitHub"'); + }); + + it("formats field change warnings", () => { + const result: MetadataFreshnessResult = { + toolkitId: "Salesforce", + gainedDesignSystemMetadata: false, + labelChanged: false, + previousLabel: "Salesforce", + currentLabel: "Salesforce", + fieldChanges: [ + { field: "iconUrl", previous: "old.svg", current: "new.svg" }, + { field: "category", previous: "development", current: "sales" }, + ], + hasChanges: true, + }; + + const warnings = formatFreshnessWarnings(result); + expect(warnings).toHaveLength(2); + expect(warnings[0]).toContain("iconUrl changed"); + expect(warnings[1]).toContain("category changed"); + }); + + it("combines all warning types", () => { + const result: MetadataFreshnessResult = { + toolkitId: "Pylon", + gainedDesignSystemMetadata: true, + labelChanged: true, + previousLabel: "Pylon", + currentLabel: "Pylon Support", + fieldChanges: [ + { + field: "category", + previous: "development", + current: "customer-support", + }, + ], + hasChanges: true, + }; + + const warnings = formatFreshnessWarnings(result); + // 1 gained DS + 1 label + 1 field = 3 + expect(warnings).toHaveLength(3); + }); +}); + +// ============================================================================ +// Integration with mergeToolkit (via data-merger.test.ts) +// ============================================================================ + +describe("mergeToolkit metadata freshness integration", () => { + // These tests import mergeToolkit to verify the step runs end-to-end + // We reuse a minimal import here since data-merger.test.ts already + // covers the happy path extensively. + + it("emits warnings when previous toolkit metadata differs", async () => { + const { mergeToolkit } = await import("../../src/merger/data-merger.js"); + + const tool = { + name: "TestTool", + qualifiedName: "TestKit.TestTool", + fullyQualifiedName: "TestKit.TestTool@1.0.0", + description: "A test tool", + toolkitDescription: "Toolkit", + parameters: [], + auth: null, + secrets: [], + output: null, + }; + + const metadata = { + id: "TestKit", + label: "Test Kit", + category: "sales" as const, + iconUrl: "https://design-system.arcade.dev/icons/testkit.svg", + isBYOC: false, + isPro: false, + type: "arcade" as const, + docsLink: "https://docs.arcade.dev/en/mcp-servers/sales/testkit", + isComingSoon: false, + isHidden: false, + }; + + // Previous toolkit had category "development" + const previousToolkit = { + id: "TestKit", + label: "Test Kit", + version: "1.0.0", + description: "Old", + metadata: { + category: "development" as const, + iconUrl: "https://design-system.arcade.dev/icons/testkit.svg", + isBYOC: false, + isPro: false, + type: "arcade" as const, + docsLink: "https://docs.arcade.dev/en/mcp-servers/development/testkit", + isComingSoon: false, + isHidden: false, + }, + auth: null, + tools: [], + documentationChunks: [], + customImports: [], + subPages: [], + generatedAt: new Date().toISOString(), + }; + + const result = await mergeToolkit( + "TestKit", + [tool], + metadata, + null, + undefined, + { + previousToolkit, + } + ); + + const freshnessWarnings = result.warnings.filter((w) => + w.includes("[metadata-freshness]") + ); + expect(freshnessWarnings.length).toBeGreaterThan(0); + expect(freshnessWarnings.some((w) => w.includes("category changed"))).toBe( + true + ); + expect(freshnessWarnings.some((w) => w.includes("docsLink changed"))).toBe( + true + ); + }); + + it("emits no freshness warnings when metadata is unchanged", async () => { + const { mergeToolkit } = await import("../../src/merger/data-merger.js"); + + const tool = { + name: "TestTool", + qualifiedName: "TestKit.TestTool", + fullyQualifiedName: "TestKit.TestTool@1.0.0", + description: "A test tool", + toolkitDescription: "Toolkit", + parameters: [], + auth: null, + secrets: [], + output: null, + }; + + const metadata = { + id: "TestKit", + label: "Test Kit", + category: "development" as const, + iconUrl: "https://design-system.arcade.dev/icons/testkit.svg", + isBYOC: false, + isPro: false, + type: "arcade" as const, + docsLink: "https://docs.arcade.dev/en/mcp-servers/development/testkit", + isComingSoon: false, + isHidden: false, + }; + + const previousToolkit = { + id: "TestKit", + label: "Test Kit", + version: "1.0.0", + description: "Same", + metadata: { + category: "development" as const, + iconUrl: "https://design-system.arcade.dev/icons/testkit.svg", + isBYOC: false, + isPro: false, + type: "arcade" as const, + docsLink: "https://docs.arcade.dev/en/mcp-servers/development/testkit", + isComingSoon: false, + isHidden: false, + }, + auth: null, + tools: [], + documentationChunks: [], + customImports: [], + subPages: [], + generatedAt: new Date().toISOString(), + }; + + const result = await mergeToolkit( + "TestKit", + [tool], + metadata, + null, + undefined, + { + previousToolkit, + } + ); + + const freshnessWarnings = result.warnings.filter((w) => + w.includes("[metadata-freshness]") + ); + expect(freshnessWarnings).toHaveLength(0); + }); +}); diff --git a/toolkit-docs-generator/tests/sources/engine-api.test.ts b/toolkit-docs-generator/tests/sources/engine-api.test.ts index a51fb3e44..3936eef5f 100644 --- a/toolkit-docs-generator/tests/sources/engine-api.test.ts +++ b/toolkit-docs-generator/tests/sources/engine-api.test.ts @@ -34,13 +34,13 @@ type ToolMetadataItem = { } | null; } | null; requirements?: { - authorization: { + authorization: Array<{ id?: string | null; provider_id: string | null; provider_type: string | null; scopes: string[]; - } | null; - secrets: Array<{ key: string }> | null; + }>; + secrets: Array<{ key: string }>; } | null; }; @@ -58,11 +58,13 @@ const createItems = (): ToolMetadataItem[] => [ input: { parameters: [] }, output: null, requirements: { - authorization: { - provider_id: "github", - provider_type: "oauth2", - scopes: ["repo"], - }, + authorization: [ + { + provider_id: "github", + provider_type: "oauth2", + scopes: ["repo"], + }, + ], secrets: [{ key: "GITHUB_API_KEY" }], }, }, @@ -82,12 +84,14 @@ const createItems = (): ToolMetadataItem[] => [ value_schema: null, }, requirements: { - authorization: { - provider_id: null, - provider_type: "oauth2", - scopes: ["chat:write"], - }, - secrets: null, + authorization: [ + { + provider_id: null, + provider_type: "oauth2", + scopes: ["chat:write"], + }, + ], + secrets: [], }, }, ]; @@ -103,16 +107,18 @@ const createFetchStub = const limit = Number(url.searchParams.get("limit") ?? items.length); const offset = Number(url.searchParams.get("offset") ?? 0); const toolkit = url.searchParams.get("toolkit"); - const providerId = url.searchParams.get("provider_id"); + const authProvider = url.searchParams.get("auth_provider"); const version = url.searchParams.get("version"); let filtered = items; if (toolkit) { filtered = filtered.filter((item) => item.toolkit.name === toolkit); } - if (providerId) { - filtered = filtered.filter( - (item) => item.requirements?.authorization?.provider_id === providerId + if (authProvider) { + filtered = filtered.filter((item) => + item.requirements?.authorization?.some( + (auth) => auth.provider_id === authProvider + ) ); } if (version) { @@ -253,12 +259,12 @@ describe("EngineApiSource", () => { await source.fetchAllTools(); }); - it("forces include_all_versions when filtering by version", async () => { + it("sets latest_only=false when filtering by version", async () => { const source = new EngineApiSource({ baseUrl: "https://api.arcade.dev", apiKey: "test", fetchFn: createInspectFetchStub((params) => { - expect(params.get("include_all_versions")).toBe("true"); + expect(params.get("latest_only")).toBe("false"); expect(params.get("version")).toBe("0.1.3"); }), }); diff --git a/toolkit-docs-generator/tests/sources/oauth-provider-resolver.test.ts b/toolkit-docs-generator/tests/sources/oauth-provider-resolver.test.ts new file mode 100644 index 000000000..d25695e55 --- /dev/null +++ b/toolkit-docs-generator/tests/sources/oauth-provider-resolver.test.ts @@ -0,0 +1,159 @@ +/** + * Tests for OAuth Provider ID Resolver + * + * Validates the resolution strategy: + * 1. Exact match + * 2. Strip "api" suffix + * 3. Longest-prefix match (with minimum length guard) + */ +import { describe, expect, it } from "vitest"; +import { + buildProviderIdResolver, + normalizeLookup, +} from "../../src/sources/oauth-provider-resolver.js"; + +// ============================================================================ +// Fixtures +// ============================================================================ + +/** Realistic set of known provider IDs (matches design system catalogue) */ +const KNOWN_PROVIDERS = new Set([ + "airtable", + "github", + "google", + "hubspot", + "salesforce", + "slack", + "ticktick", + "x", + "zendesk", + "zoho", + "squareup", +]); + +// ============================================================================ +// normalizeLookup +// ============================================================================ + +describe("normalizeLookup", () => { + it("lowercases and strips non-alphanumeric characters", () => { + expect(normalizeLookup("HubSpot")).toBe("hubspot"); + expect(normalizeLookup("GitHub-Api")).toBe("githubapi"); + expect(normalizeLookup("Tick_Tick_API")).toBe("ticktickapi"); + expect(normalizeLookup("SALESFORCE")).toBe("salesforce"); + }); + + it("handles already-normalized input", () => { + expect(normalizeLookup("salesforce")).toBe("salesforce"); + }); +}); + +// ============================================================================ +// buildProviderIdResolver +// ============================================================================ + +describe("buildProviderIdResolver", () => { + const resolve = buildProviderIdResolver(KNOWN_PROVIDERS); + + // ---------- 1. Exact match ---------- + + describe("exact match", () => { + it("resolves toolkit IDs that match a provider ID directly", () => { + expect(resolve("Salesforce")).toBe("salesforce"); + expect(resolve("Zendesk")).toBe("zendesk"); + expect(resolve("Hubspot")).toBe("hubspot"); + expect(resolve("Github")).toBe("github"); + }); + + it("is case-insensitive", () => { + expect(resolve("SALESFORCE")).toBe("salesforce"); + expect(resolve("salesforce")).toBe("salesforce"); + }); + }); + + // ---------- 2. Strip "api" suffix ---------- + + describe("strip api suffix", () => { + it("resolves *Api toolkit IDs by stripping the suffix", () => { + expect(resolve("TicktickApi")).toBe("ticktick"); + expect(resolve("AirtableApi")).toBe("airtable"); + expect(resolve("ZohoApi")).toBe("zoho"); + }); + + it("handles mixed casing of Api suffix", () => { + expect(resolve("SlackAPI")).toBe("slack"); + expect(resolve("GithubApi")).toBe("github"); + }); + }); + + // ---------- 3. Longest-prefix match ---------- + + describe("longest-prefix match", () => { + it("resolves HubSpot sub-toolkit IDs to hubspot", () => { + expect(resolve("HubspotCrmApi")).toBe("hubspot"); + expect(resolve("HubspotMarketingApi")).toBe("hubspot"); + expect(resolve("HubspotAutomationApi")).toBe("hubspot"); + expect(resolve("HubspotConversationsApi")).toBe("hubspot"); + }); + + it("picks the longest matching prefix", () => { + // Add a more specific provider to test longest-prefix + const providers = new Set(["hub", "hubspot"]); + const resolveCustom = buildProviderIdResolver(providers); + expect(resolveCustom("HubspotCrmApi")).toBe("hubspot"); + }); + }); + + // ---------- 4. Minimum prefix length guard ---------- + + describe("minimum prefix length", () => { + it("does NOT match short provider IDs as prefixes", () => { + // "xero" should NOT resolve to "x" (X/Twitter) + expect(resolve("XeroApi")).toBeNull(); + }); + + it("exact match still works for short IDs", () => { + // "X" as an exact toolkit ID should resolve to "x" + expect(resolve("X")).toBe("x"); + }); + + it("strip-api still works for short IDs", () => { + // This would need "x" + "api" = "xapi" which isn't a real case, + // but tests the direct strip path + const providers = new Set(["ab"]); + const resolveCustom = buildProviderIdResolver(providers); + expect(resolveCustom("AbApi")).toBe("ab"); + }); + }); + + // ---------- 5. No match ---------- + + describe("no match", () => { + it("returns null for unknown toolkit IDs", () => { + expect(resolve("UnknownToolkit")).toBeNull(); + expect(resolve("RandomApi")).toBeNull(); + }); + + it("returns null for empty string", () => { + expect(resolve("")).toBeNull(); + }); + }); + + // ---------- Edge cases ---------- + + describe("edge cases", () => { + it("handles toolkit ID that equals provider ID + api exactly", () => { + expect(resolve("GithubApi")).toBe("github"); + }); + + it("handles providers set with no entries", () => { + const empty = buildProviderIdResolver(new Set()); + expect(empty("Salesforce")).toBeNull(); + }); + + it("does not match when base is exactly the provider ID (strip path)", () => { + // "salesforceapi" -> base "salesforce" -> exact match in known set + expect(resolve("SalesforceApi")).toBe("salesforce"); + }); + }); +}); From 27896f5387ae34ebf26a46491321323891cc8f48 Mon Sep 17 00:00:00 2001 From: jottakka Date: Thu, 12 Feb 2026 16:22:02 -0300 Subject: [PATCH 02/15] fixing extra section --- .github/workflows/generate-toolkit-docs.yml | 2 +- .../references/auth-providers/figma/page.mdx | 8 +- app/en/resources/integrations/_meta.tsx | 3 - .../integrations/others/[toolkitId]/_meta.tsx | 13 -- .../others/[toolkitId]/page-impl.tsx | 10 -- .../integrations/others/[toolkitId]/page.mdx | 14 -- .../resources/integrations/others/_meta.tsx | 78 ----------- next.config.ts | 6 + .../en/references/auth-providers/figma.md | 8 +- .../scripts/sync-toolkit-sidebar.ts | 24 ++-- .../scripts/sync-toolkit-sidebar.test.ts | 128 ++++++++++++++++++ 11 files changed, 156 insertions(+), 138 deletions(-) delete mode 100644 app/en/resources/integrations/others/[toolkitId]/_meta.tsx delete mode 100644 app/en/resources/integrations/others/[toolkitId]/page-impl.tsx delete mode 100644 app/en/resources/integrations/others/[toolkitId]/page.mdx delete mode 100644 app/en/resources/integrations/others/_meta.tsx diff --git a/.github/workflows/generate-toolkit-docs.yml b/.github/workflows/generate-toolkit-docs.yml index ce92dc5c0..185d89286 100644 --- a/.github/workflows/generate-toolkit-docs.yml +++ b/.github/workflows/generate-toolkit-docs.yml @@ -62,7 +62,7 @@ jobs: OPENAI_MODEL: ${{ secrets.OPENAI_MODEL || 'gpt-4o-mini' }} - name: Sync toolkit sidebar navigation - run: pnpm dlx tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts + run: pnpm dlx tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --prune --verbose - name: Check for changes id: check-changes diff --git a/app/en/references/auth-providers/figma/page.mdx b/app/en/references/auth-providers/figma/page.mdx index 5b84f4a5f..1bc6d233d 100644 --- a/app/en/references/auth-providers/figma/page.mdx +++ b/app/en/references/auth-providers/figma/page.mdx @@ -6,7 +6,7 @@ The Figma auth provider enables tools and agents to call [Figma APIs](https://de Want to quickly get started with Figma in your agent or AI app? The pre-built - [Arcade Figma MCP Server](/resources/integrations/others/figma) is what you + [Arcade Figma MCP Server](/resources/integrations/development/figma) is what you want! @@ -16,13 +16,13 @@ This page describes how to use and configure Figma auth with Arcade. This auth provider is used by: -- The [Arcade Figma MCP Server](/resources/integrations/others/figma), which provides pre-built tools for interacting with Figma +- The [Arcade Figma MCP Server](/resources/integrations/development/figma), which provides pre-built tools for interacting with Figma - Your [app code](#using-figma-auth-in-app-code) that needs to call the Figma API - Or, your [custom tools](#using-figma-auth-in-custom-tools) that need to call the Figma API ### Required scopes for the Figma MCP Server -If you're using the [Arcade Figma MCP Server](/resources/integrations/others/figma), you'll need to configure these scopes based on which tools you plan to use: +If you're using the [Arcade Figma MCP Server](/resources/integrations/development/figma), you'll need to configure these scopes based on which tools you plan to use: - `file_content:read` - File structure, pages, nodes, and image exports - `library_content:read` - Published components, styles, and component sets from files @@ -288,7 +288,7 @@ const token = authResponse.context.token; ## Using Figma auth in custom tools -You can use the pre-built [Arcade Figma MCP Server](/resources/integrations/others/figma) to quickly build agents and AI apps that interact with Figma. +You can use the pre-built [Arcade Figma MCP Server](/resources/integrations/development/figma) to quickly build agents and AI apps that interact with Figma. If the pre-built tools in the Figma MCP Server don't meet your needs, you can author your own [custom tools](/guides/create-tools/tool-basics/build-mcp-server) that interact with the Figma API. diff --git a/app/en/resources/integrations/_meta.tsx b/app/en/resources/integrations/_meta.tsx index 9fe6b8071..32201d550 100644 --- a/app/en/resources/integrations/_meta.tsx +++ b/app/en/resources/integrations/_meta.tsx @@ -39,9 +39,6 @@ const meta: MetaRecord = { "customer-support": { title: "Customer Support", }, - others: { - title: "Others", - }, "-- Submit your Server": { type: "separator", title: "Submit your server", diff --git a/app/en/resources/integrations/others/[toolkitId]/_meta.tsx b/app/en/resources/integrations/others/[toolkitId]/_meta.tsx deleted file mode 100644 index ce7f0efde..000000000 --- a/app/en/resources/integrations/others/[toolkitId]/_meta.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "*": { - theme: { - breadcrumb: false, - toc: false, - copyPage: false, - }, - }, -}; - -export default meta; diff --git a/app/en/resources/integrations/others/[toolkitId]/page-impl.tsx b/app/en/resources/integrations/others/[toolkitId]/page-impl.tsx deleted file mode 100644 index e37332a5d..000000000 --- a/app/en/resources/integrations/others/[toolkitId]/page-impl.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { createToolkitDocsPage } from "@/app/en/resources/integrations/_lib/toolkit-docs-page"; - -export const dynamicParams = false; - -const { Page, generateMetadata, generateStaticParams } = - createToolkitDocsPage("others"); - -export { generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/others/[toolkitId]/page.mdx b/app/en/resources/integrations/others/[toolkitId]/page.mdx deleted file mode 100644 index ca97c319f..000000000 --- a/app/en/resources/integrations/others/[toolkitId]/page.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "MCP server" -description: "Generated MCP server documentation." ---- - -import Page, { - dynamicParams, - generateMetadata, - generateStaticParams, -} from "./page-impl"; - -export { dynamicParams, generateMetadata, generateStaticParams }; - -export default Page; diff --git a/app/en/resources/integrations/others/_meta.tsx b/app/en/resources/integrations/others/_meta.tsx deleted file mode 100644 index 232a6b2c1..000000000 --- a/app/en/resources/integrations/others/_meta.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import type { MetaRecord } from "nextra"; - -const meta: MetaRecord = { - "-- Optimized": { - type: "separator", - title: "Optimized", - }, - codesandbox: { - title: "Codesandbox", - href: "/en/resources/integrations/others/codesandbox", - }, - complextools: { - title: "ComplexTools", - href: "/en/resources/integrations/others/complextools", - }, - deepwiki: { - title: "Deepwiki", - href: "/en/resources/integrations/others/deepwiki", - }, - figma: { - title: "Figma", - href: "/en/resources/integrations/others/figma", - }, - google: { - title: "Google", - href: "/en/resources/integrations/others/google", - }, - math: { - title: "Math", - href: "/en/resources/integrations/others/math", - }, - microsoft: { - title: "Microsoft", - href: "/en/resources/integrations/others/microsoft", - }, - notiontoolkit: { - title: "Notion", - href: "/en/resources/integrations/others/notiontoolkit", - }, - pagerduty: { - title: "Pagerduty", - href: "/en/resources/integrations/others/pagerduty", - }, - pylon: { - title: "Pylon", - href: "/en/resources/integrations/others/pylon", - }, - search: { - title: "Search", - href: "/en/resources/integrations/others/search", - }, - test2: { - title: "Test2", - href: "/en/resources/integrations/others/test2", - }, - web: { - title: "Web", - href: "/en/resources/integrations/others/web", - }, - "-- Starter": { - type: "separator", - title: "Starter", - }, - upclickapi: { - title: "ClickUp API", - href: "/en/resources/integrations/others/upclickapi", - }, - mailchimpmarketingapi: { - title: "Mailchimp API", - href: "/en/resources/integrations/others/mailchimpmarketingapi", - }, - pylonapi: { - title: "PylonApi", - href: "/en/resources/integrations/others/pylonapi", - }, -}; - -export default meta; diff --git a/next.config.ts b/next.config.ts index 3b7c9fff8..f84c3c10d 100644 --- a/next.config.ts +++ b/next.config.ts @@ -23,6 +23,12 @@ const nextConfig: NextConfig = withLlmsTxt({ withNextra({ async redirects() { return [ + // "others" category removed — toolkits moved to proper categories + { + source: "/:locale/resources/integrations/others/:path*", + destination: "/:locale/resources/integrations", + permanent: false, + }, // Auto-added redirects for deleted pages { source: "/:locale/resources/integrations/preview", diff --git a/public/_markdown/en/references/auth-providers/figma.md b/public/_markdown/en/references/auth-providers/figma.md index 2390b3486..a701df83e 100644 --- a/public/_markdown/en/references/auth-providers/figma.md +++ b/public/_markdown/en/references/auth-providers/figma.md @@ -9,7 +9,7 @@ Figma The Figma enables tools and to call [Figma APIs](https://developers.figma.com/docs/rest-api/)  on behalf of a using OAuth 2.0 authentication. -Want to quickly get started with Figma in your or AI app? The pre-built [Arcade Figma MCP Server](/resources/integrations/others/figma.md) is what you want! +Want to quickly get started with Figma in your or AI app? The pre-built [Arcade Figma MCP Server](/resources/integrations/development/figma.md) is what you want! ### What’s documented here @@ -17,7 +17,7 @@ This page describes how to use and configure Figma auth with Arcade. This is used by: -- The [Arcade Figma MCP Server](/resources/integrations/others/figma.md) +- The [Arcade Figma MCP Server](/resources/integrations/development/figma.md) , which provides pre-built for interacting with Figma - Your [app code](#using-figma-auth-in-app-code) that needs to call the Figma API @@ -26,7 +26,7 @@ This is used by: ### Required scopes for the Figma MCP Server -If you’re using the [Arcade Figma MCP Server](/resources/integrations/others/figma.md), you’ll need to configure these scopes based on which you plan to use: +If you’re using the [Arcade Figma MCP Server](/resources/integrations/development/figma.md), you’ll need to configure these scopes based on which you plan to use: - `file_content:read` - File structure, pages, nodes, and image exports - `library_content:read` - Published components, styles, and component sets from files @@ -248,7 +248,7 @@ const token = authResponse.context.token; ## Using Figma auth in custom tools -You can use the pre-built [Arcade Figma MCP Server](/resources/integrations/others/figma.md) to quickly build and AI apps that interact with Figma. +You can use the pre-built [Arcade Figma MCP Server](/resources/integrations/development/figma.md) to quickly build and AI apps that interact with Figma. If the pre-built tools in the Figma Server don’t meet your needs, you can author your own [custom tools](/guides/create-tools/tool-basics/build-mcp-server.md) that interact with the Figma API. diff --git a/toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts b/toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts index 9baa3d812..a8e517eda 100644 --- a/toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts +++ b/toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts @@ -467,7 +467,7 @@ export default meta; export function syncToolkitSidebar( options: { dryRun?: boolean; verbose?: boolean; prune?: boolean } = {} ): SyncResult { - const { dryRun = false, verbose = false, prune = false } = options; + const { dryRun = false, verbose = false } = options; const result: SyncResult = { categoriesUpdated: [], @@ -548,17 +548,19 @@ export function syncToolkitSidebar( } } - // Remove empty categories (optional; off by default for safety). - if (prune) { - for (const existingDir of existingDirs) { - if (!activeCategories.includes(existingDir)) { - const categoryDir = join(CONFIG.integrationsDir, existingDir); - log(`Removing empty category: ${existingDir}`); - if (!dryRun) { - rmSync(categoryDir, { recursive: true }); - } - result.categoriesRemoved.push(existingDir); + // Remove category directories that no longer have any toolkits. + // This handles the case where toolkits move between categories (e.g. from + // "others" to "development") and the old category becomes empty. + // The --prune flag is accepted for backward compatibility but cleanup + // always runs to prevent stale sidebar entries and orphaned routes. + for (const existingDir of existingDirs) { + if (!activeCategories.includes(existingDir)) { + const categoryDir = join(CONFIG.integrationsDir, existingDir); + log(`Removing empty category: ${existingDir}`); + if (!dryRun) { + rmSync(categoryDir, { recursive: true }); } + result.categoriesRemoved.push(existingDir); } } diff --git a/toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts b/toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts index 1ad50c6f3..8c6b466f6 100644 --- a/toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts +++ b/toolkit-docs-generator/tests/scripts/sync-toolkit-sidebar.test.ts @@ -532,3 +532,131 @@ describe("syncToolkitSidebar", () => { }); }); }); + +// ============================================================================ +// Category move / cleanup logic +// ============================================================================ + +describe("category move cleanup logic", () => { + it("toolkit moved to a new category is removed from the old one", () => { + // Simulate: Pylon was in "others", now design system says "development" + const toolkits: ToolkitInfo[] = [ + { + id: "Pylon", + slug: "pylon", + label: "Pylon", + category: "development", + navGroup: "optimized", + }, + { + id: "Github", + slug: "github", + label: "GitHub", + category: "development", + navGroup: "optimized", + }, + ]; + + const grouped = groupByCategory(toolkits); + const activeCategories = Array.from(grouped.keys()); + + // "others" is no longer an active category + expect(activeCategories).not.toContain("others"); + expect(activeCategories).toContain("development"); + + // The development category contains Pylon + const devToolkits = grouped.get("development"); + expect(devToolkits?.some((t) => t.id === "Pylon")).toBe(true); + }); + + it("old category directory is identified for removal when empty", () => { + const toolkits: ToolkitInfo[] = [ + { + id: "Pylon", + slug: "pylon", + label: "Pylon", + category: "development", + navGroup: "optimized", + }, + ]; + + const grouped = groupByCategory(toolkits); + const activeCategories = Array.from(grouped.keys()); + + // Simulate existing directories on disk + const existingDirs = ["development", "others", "productivity"]; + + // Directories not in activeCategories should be removed + const toRemove = existingDirs.filter( + (dir) => !activeCategories.includes(dir) + ); + + expect(toRemove).toContain("others"); + expect(toRemove).toContain("productivity"); + expect(toRemove).not.toContain("development"); + }); + + it("category with remaining toolkits is NOT removed when one toolkit moves out", () => { + // "others" still has CustomTool, even though Pylon moved to "development" + const toolkits: ToolkitInfo[] = [ + { + id: "Pylon", + slug: "pylon", + label: "Pylon", + category: "development", + navGroup: "optimized", + }, + { + id: "CustomTool", + slug: "custom-tool", + label: "Custom Tool", + category: "others", + navGroup: "optimized", + }, + ]; + + const grouped = groupByCategory(toolkits); + const activeCategories = Array.from(grouped.keys()); + + expect(activeCategories).toContain("others"); + expect(activeCategories).toContain("development"); + + // "others" still has one toolkit so it won't be pruned + expect(grouped.get("others")).toHaveLength(1); + + // But Pylon is NOT in "others" anymore + const othersToolkits = grouped.get("others"); + expect(othersToolkits?.some((t) => t.id === "Pylon")).toBe(false); + }); + + it("generateCategoryMeta only includes current toolkits (not stale ones)", () => { + // After Pylon moves, the "others" _meta.tsx should only contain remaining toolkits + const othersToolkits: ToolkitInfo[] = [ + { + id: "CustomTool", + slug: "custom-tool", + label: "Custom Tool", + category: "others", + navGroup: "optimized", + }, + ]; + + const meta = generateCategoryMeta( + othersToolkits, + "others", + "/en/resources/integrations" + ); + + expect(meta).toContain("custom-tool"); + expect(meta).not.toContain("pylon"); + }); + + it("main _meta.tsx excludes categories with zero toolkits", () => { + const activeCategories = ["development", "productivity"]; + const mainMeta = generateMainMeta(activeCategories); + + expect(mainMeta).toContain("development:"); + expect(mainMeta).toContain("productivity:"); + expect(mainMeta).not.toContain("others:"); + }); +}); From a6d9fd30890e0c14c475d0bda082b1b267bab1b9 Mon Sep 17 00:00:00 2001 From: jottakka Date: Thu, 12 Feb 2026 16:23:13 -0300 Subject: [PATCH 03/15] merge --- .../agent-frameworks/google-adk/_meta.tsx | 3 +- .../google-adk/overview/page.mdx | 136 +--- .../page.mdx | 12 +- .../google-adk/setup-typescript/page.mdx | 497 ++++++++++++ .../agent-frameworks/langchain/_meta.tsx | 7 +- .../langchain/overview/page.mdx | 31 + next.config.ts | 12 +- .../agent-frameworks/google-adk/overview.md | 138 +--- .../google-adk/setup-python.md | 740 ++++++++++++++++++ .../google-adk/setup-typescript.md | 482 ++++++++++++ .../langchain/auth-langchain-tools.md | 6 +- .../agent-frameworks/langchain/overview.md | 41 + .../langchain/use-arcade-with-langchain-py.md | 7 +- .../langchain/use-arcade-with-langchain-ts.md | 8 +- .../en/get-started/agent-frameworks/mastra.md | 2 +- public/llms.txt | 5 +- scripts/generate-clean-markdown.ts | 194 ++++- 17 files changed, 2041 insertions(+), 280 deletions(-) rename app/en/get-started/agent-frameworks/google-adk/{use-arcade-tools => setup-python}/page.mdx (97%) create mode 100644 app/en/get-started/agent-frameworks/google-adk/setup-typescript/page.mdx create mode 100644 app/en/get-started/agent-frameworks/langchain/overview/page.mdx create mode 100644 public/_markdown/en/get-started/agent-frameworks/google-adk/setup-python.md create mode 100644 public/_markdown/en/get-started/agent-frameworks/google-adk/setup-typescript.md create mode 100644 public/_markdown/en/get-started/agent-frameworks/langchain/overview.md diff --git a/app/en/get-started/agent-frameworks/google-adk/_meta.tsx b/app/en/get-started/agent-frameworks/google-adk/_meta.tsx index f16667d5a..6b37f150a 100644 --- a/app/en/get-started/agent-frameworks/google-adk/_meta.tsx +++ b/app/en/get-started/agent-frameworks/google-adk/_meta.tsx @@ -1,4 +1,5 @@ export default { overview: "Overview", - "use-arcade-tools": "Setup Arcade with Google ADK (Python)", + "setup-python": "Setup (Python)", + "setup-typescript": "Setup (TypeScript)", }; diff --git a/app/en/get-started/agent-frameworks/google-adk/overview/page.mdx b/app/en/get-started/agent-frameworks/google-adk/overview/page.mdx index 963e91232..c6c2b0319 100644 --- a/app/en/get-started/agent-frameworks/google-adk/overview/page.mdx +++ b/app/en/get-started/agent-frameworks/google-adk/overview/page.mdx @@ -1,133 +1,27 @@ --- -title: "Arcade with Google ADK overview" -description: "Comprehensive guide to using Arcade with the Google ADK library" +title: "Arcade with Google ADK" +description: "Integrate Arcade tools with Google ADK agents" --- # Arcade with Google ADK -The `google-adk-arcade` package provides a seamless integration between -[Arcade](https://arcade.dev) and the [Google ADK](https://github.com/google/adk-python/). This integration allows you to enhance your AI agents with powerful Arcade tools including Google Mail, LinkedIn, GitHub, and many more. +[Google ADK](https://github.com/google/adk-python/) is a modular framework for building and deploying AI agents. It's optimized for Gemini and the Google ecosystem. Arcade integrates with both the Python and TypeScript versions, giving your agents access to Gmail, GitHub, Slack, and 100+ other tools. -## Installation +## Get started -Install the necessary packages to get started: +Choose your language to set up Arcade with Google ADK: -```bash -pip install google-adk-arcade -``` +- **[Python setup](/get-started/agent-frameworks/google-adk/setup-python)** - Build an agent with Arcade tools using the `google-adk-arcade` package +- **[TypeScript setup](/get-started/agent-frameworks/google-adk/setup-typescript)** - Build an agent with Arcade tools using `@arcadeai/arcadejs` -Make sure you have your Arcade API key ready. [Get an API key](/get-started/setup/api-keys) if you don't already have one. +## What you can build -## Key features +With Arcade and Google ADK, your agents can: -- **Easy integration** with the Google ADK framework -- **Access to all Arcade MCP Servers** including Google, GitHub, LinkedIn, X, and more -- **Create custom tools** with the Arcade Tool SDK -- **Manage user authentication** for tools that require it -- **Asynchronous support** compatible with Google's ADK framework +- Read and send emails via Gmail +- Post messages to Slack channels +- Create GitHub issues and pull requests +- Search the web and extract content +- Access 100+ other integrations -## Basic usage - -Here's a simple example of using Arcade tools with OpenAI Agents: - -```python -import asyncio - -from arcadepy import AsyncArcade -from google.adk import Agent, Runner -from google.adk.artifacts import InMemoryArtifactService -from google.adk.sessions import InMemorySessionService -from google.genai import types - -from google_adk_arcade.tools import get_arcade_tools - - -async def main(): - app_name = 'my_app' - user_id = '{arcade_user_id}' - session_service = InMemorySessionService() - artifact_service = InMemoryArtifactService() - client = AsyncArcade() - - google_tools = await get_arcade_tools(client, tools=["Gmail.ListEmails"]) - - # authorize the tools - for tool in google_tools: - result = await client.tools.authorize( - tool_name=tool.name, - user_id=user_id - ) - if result.status != "completed": - print(f"Click this link to authorize {tool.name}:\n{result.url}") - await client.auth.wait_for_completion(result) - - # create the agent - google_agent = Agent( - model="gemini-2.0-flash", - name="google_tool_agent", - instruction="I can use Google tools to manage an inbox!", - description="An agent equipped with tools to read Gmail emails." - tools=google_tools, - ) - - #create the session and pass the user ID to the state - session = await session_service.create_session( - app_name=app_name, user_id=user_id, state={ - "user_id": user_id, - } - ) - - runner = Runner( - app_name=app_name, - agent=google_agent, - artifact_service=artifact_service, - session_service=session_service, - ) - - user_input = "summarize my latest 3 emails" - content = types.Content( - role='user', parts=[types.Part.from_text(text=user_input)] - ) - for event in runner.run( - user_id=user_id, - session_id=session.id, - new_message=content, - ): - if event.content.parts and event.content.parts[0].text: - print(f'** {event.author}: {event.content.parts[0].text}') - -if __name__ == '__main__': - asyncio.run(main()) -``` - -## Handling authorization - -When a user needs to authorize access to a tool (like Google or GitHub), -the agent will reply with a URL for the user to visit, which will be displayed -to the user. - -After visiting the URL and authorizing access, the user can run the agent again -with the same `user_id`, and it will work without requiring re-authorization. - -Alternatively, you can authorize the tool before running the agent: - -```python -# authorize the tools -for tool in google_tools: - result = await client.tools.authorize( - tool_name=tool.name, - user_id=user_id - ) - if result.status != "completed": - print(f"Click this link to authorize {tool.name}:\n{result.url}") - await client.auth.wait_for_completion(result) -``` - -## Next steps - -Ready to start building with Arcade and OpenAI Agents? Check out these guides: - -- [Using Arcade tools](/get-started/agent-frameworks/google-adk/use-arcade-tools) - Learn the basics of using Arcade tools with Google ADK -- [Creating custom tools](/guides/create-tools/tool-basics/build-mcp-server) - Build your own tools with the Arcade Tool SDK - -Enjoy exploring Arcade and building powerful AI-enabled applications. +Browse the [full MCP server catalog](/resources/integrations) to see all available tools. diff --git a/app/en/get-started/agent-frameworks/google-adk/use-arcade-tools/page.mdx b/app/en/get-started/agent-frameworks/google-adk/setup-python/page.mdx similarity index 97% rename from app/en/get-started/agent-frameworks/google-adk/use-arcade-tools/page.mdx rename to app/en/get-started/agent-frameworks/google-adk/setup-python/page.mdx index e3ee34f1c..b6ff08ac7 100644 --- a/app/en/get-started/agent-frameworks/google-adk/use-arcade-tools/page.mdx +++ b/app/en/get-started/agent-frameworks/google-adk/setup-python/page.mdx @@ -1,15 +1,13 @@ --- -title: "Setup Arcade with Google ADK" -description: "Integrate Arcade tools into your Google ADK applications" +title: "Setup Arcade with Google ADK (Python)" +description: "Build an agent with Arcade tools using Google ADK" --- -import { Steps } from "nextra/components"; +import { Steps, Callout } from "nextra/components"; -## Setup Arcade with Google ADK +# Setup Arcade with Google ADK (Python) -Google ADK is a modular framework for building and deploying AI agents. It optimizes for Gemini and the Google Ecosystem, but supports any model and we built it with compatibility with other frameworks in mind. - -This guide shows developers familiar with Google ADK how to integrate Arcade tools into their agent applications. You'll build a ReAct-pattern agent that can use Arcade tools to help users with their requests. By the end, you'll understand how to retrieve Arcade tools, transform them for Google ADK compatibility, and manage tool authorization in your agentic flows. +Google ADK is a modular framework for building and deploying AI agents. It optimizes for Gemini and the Google Ecosystem, but supports any model. diff --git a/app/en/get-started/agent-frameworks/google-adk/setup-typescript/page.mdx b/app/en/get-started/agent-frameworks/google-adk/setup-typescript/page.mdx new file mode 100644 index 000000000..9113c0701 --- /dev/null +++ b/app/en/get-started/agent-frameworks/google-adk/setup-typescript/page.mdx @@ -0,0 +1,497 @@ +--- +title: "Setup Arcade with Google ADK (TypeScript)" +description: "Build an agent with Arcade tools using Google ADK for TypeScript" +--- + +import { Steps, Tabs, Callout } from "nextra/components"; + +# Setup Arcade with Google ADK (TypeScript) + +[Google ADK for TypeScript](https://github.com/google/adk-js) provides a framework for building AI agents in TypeScript. Arcade's `@arcadeai/arcadejs` library provides the tools integration, allowing your agents to access Gmail, GitHub, Slack, and 100+ other services. + + + + +Build an agent that uses Arcade tools to help users with Gmail and Slack + + + + + +- +- [Obtain an Arcade API key](/get-started/setup/api-keys) +- [Node.js](https://nodejs.org/) 18+ (includes npm) or [Bun](https://bun.sh/) +- A [Gemini API key](https://aistudio.google.com/apikey) + + + + + +- How to retrieve Arcade tools and convert them to Google ADK format +- How to build a Google ADK agent with Arcade tools +- How to handle tool authorization + + + + +## How Arcade integrates with Google ADK + +Google ADK uses `FunctionTool` for defining callable tools. Arcade's `@arcadeai/arcadejs` library provides `toZod` to convert tool definitions to Zod schemas, which Google ADK's `FunctionTool` accepts. The `Runner` class manages the agent's conversation loop, while `InMemorySessionService` handles session state. + +## Build the agent + + + +### Create a new project + +Create a new directory and install dependencies: + + + + + +```bash +mkdir google-adk-arcade-ts +cd google-adk-arcade-ts +npm init -y +npm install @google/adk @arcadeai/arcadejs dotenv +``` + + + + + +```bash +mkdir google-adk-arcade-ts +cd google-adk-arcade-ts +bun init -y +bun add @google/adk @arcadeai/arcadejs dotenv +``` + + + + + +Create a `.env` file with your API keys: + +```env filename=".env" +# Arcade API key from https://app.arcade.dev/api-keys +ARCADE_API_KEY=YOUR_ARCADE_API_KEY +# Your Arcade user ID (the email you used to sign up) +ARCADE_USER_ID={arcade_user_id} +# Google Gemini API key (get one at https://aistudio.google.com/apikey) +GEMINI_API_KEY=YOUR_GEMINI_API_KEY +``` + +### Create the agent file + +Create `index.ts` with the following imports and configuration: + +```typescript filename="index.ts" +import { + LlmAgent, + FunctionTool, + Runner, + InMemorySessionService, + setLogLevel, + LogLevel, +} from "@google/adk"; +import Arcade from "@arcadeai/arcadejs"; +import { toZod } from "@arcadeai/arcadejs/lib/zod/zod"; +import "dotenv/config"; +import readline from "node:readline/promises"; + +// Suppress verbose ADK logs (options: DEBUG, INFO, WARNING, ERROR) +setLogLevel(LogLevel.ERROR); + +// Configuration +const ARCADE_USER_ID = process.env.ARCADE_USER_ID || "default-user"; +const MCP_SERVERS = ["Slack"]; +const INDIVIDUAL_TOOLS = ["Gmail_ListEmails", "Gmail_SendEmail", "Gmail_WhoAmI"]; +const SYSTEM_PROMPT = "You are a helpful assistant that can assist with Gmail and Slack."; +const MODEL = "gemini-2.0-flash"; +const APP_NAME = "inbox_assistant"; +``` + +### Convert Arcade tools to Google ADK format + +Use Arcade's `toZod` to convert tool definitions to Zod schemas, then wrap them in Google ADK's `FunctionTool`: + +```typescript filename="index.ts" +async function getArcadeTools(client: Arcade, userId: string): Promise { + // Fetch tools from MCP servers + const mcpServerTools = await Promise.all( + MCP_SERVERS.map(async (serverName) => { + const response = await client.tools.list({ + toolkit: serverName, + limit: 30, + }); + return response.items; + }) + ); + + // Fetch individual tools by name + const individualToolDefs = await Promise.all( + INDIVIDUAL_TOOLS.map((toolName) => client.tools.get(toolName)) + ); + + // Combine and deduplicate + const allTools = [...mcpServerTools.flat(), ...individualToolDefs]; + const uniqueTools = Array.from( + new Map(allTools.map((t) => [t.qualified_name, t])).values() + ); + + // Convert Arcade tools to Zod format (with proper schemas) + const zodTools = toZod({ + tools: uniqueTools, + client, + userId, + executeFactory: ({ toolDefinition, client, userId }) => { + const toolName = toolDefinition.qualified_name; + return async (args: unknown) => { + // Handle authorization + const authResult = await client.tools.authorize({ + tool_name: toolName, + user_id: userId, + }); + + if (authResult.status !== "completed") { + console.log(`\nAuthorization required for ${toolName}`); + console.log(`Please visit: ${authResult.url}\n`); + await client.auth.waitForCompletion(authResult); + console.log("Authorization complete!\n"); + } + + // Execute the tool + const result = await client.tools.execute({ + tool_name: toolName, + input: args as Record, + user_id: userId, + }); + + if (!result.success) { + throw new Error(`Tool execution failed: ${result.output?.error?.message}`); + } + + return result.output?.value; + }; + }, + }); + + // Convert to Google ADK FunctionTool format + return zodTools.map((tool) => + new FunctionTool({ + name: tool.name, + description: tool.description, + parameters: tool.parameters, + execute: tool.execute, + }) + ); +} +``` + +**What's happening here:** +- `toZod` converts Arcade tool definitions to Zod schemas with proper parameter types +- `executeFactory` creates the execution function that handles authorization and tool calls +- Each Zod tool wraps in a Google ADK `FunctionTool` + +### Create and run the agent + +```typescript filename="index.ts" +async function main() { + const client = new Arcade(); + const sessionService = new InMemorySessionService(); + + // Get Arcade tools + const arcadeTools = await getArcadeTools(client, ARCADE_USER_ID); + + // Create the agent + const agent = new LlmAgent({ + name: APP_NAME, + description: "An assistant that helps with Gmail and Slack", + model: MODEL, + instruction: SYSTEM_PROMPT, + tools: arcadeTools, + }); + + // Create a session + const session = await sessionService.createSession({ + appName: APP_NAME, + userId: ARCADE_USER_ID, + state: { user_id: ARCADE_USER_ID }, + }); + + // Create the runner + const runner = new Runner({ + appName: APP_NAME, + agent, + sessionService, + }); + + // Set up interactive chat + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + console.log("Hello! I'm your Google ADK Agent with Arcade Tools."); + console.log("Try asking me to summarize your recent emails or send a Slack message!"); + console.log("Type 'exit' to quit.\n"); + + while (true) { + const input = await rl.question("> "); + if (input.toLowerCase() === "exit") { + break; + } + + try { + const events = runner.runAsync({ + userId: ARCADE_USER_ID, + sessionId: session.id, + newMessage: { role: "user", parts: [{ text: input }] }, + }); + + for await (const event of events) { + if (event.content?.parts?.[0]?.text) { + console.log(`\n${event.author}: ${event.content.parts[0].text}\n`); + } + } + } catch (error) { + console.error("Error:", error); + } + } + + console.log("Goodbye!"); + rl.close(); + process.exit(0); +} + +main().catch(console.error); +``` + +### Run the agent + + + + + +```bash +npx tsx index.ts +``` + + + + + +```bash +bun run index.ts +``` + + + + + + + + +Google ADK for TypeScript is still changing. Check the [official documentation](https://google.github.io/adk-docs) and [samples repository](https://github.com/google/adk-samples) for the latest API updates. + + +## Key takeaways + +- **`toZod`** from `@arcadeai/arcadejs` converts Arcade tools to Zod schemas with proper parameter types +- **`FunctionTool`** wraps the Zod tools for Google ADK +- **`Runner`** manages the agent's conversation loop with `runAsync()` +- **`InMemorySessionService`** handles session state between messages +- **Authorization** occurs in the execute factory. The auth URL appears when needed +- **`userId`** tracks authorization per user - use a consistent ID for each user in your application + +## Example code + +
+**index.ts** (full file) + +```typescript filename="index.ts" +import { + LlmAgent, + FunctionTool, + Runner, + InMemorySessionService, + setLogLevel, + LogLevel, +} from "@google/adk"; +import Arcade from "@arcadeai/arcadejs"; +import { toZod } from "@arcadeai/arcadejs/lib/zod/zod"; +import "dotenv/config"; +import readline from "node:readline/promises"; + +// Suppress verbose ADK logs (options: DEBUG, INFO, WARNING, ERROR) +setLogLevel(LogLevel.ERROR); + +// Configuration +const ARCADE_USER_ID = process.env.ARCADE_USER_ID || "default-user"; +const MCP_SERVERS = ["Slack"]; +const INDIVIDUAL_TOOLS = [ + "Gmail_ListEmails", + "Gmail_SendEmail", + "Gmail_WhoAmI", +]; +const SYSTEM_PROMPT = + "You are a helpful assistant that can assist with Gmail and Slack."; +const MODEL = "gemini-2.0-flash"; +const APP_NAME = "inbox_assistant"; + +async function getArcadeTools( + client: Arcade, + userId: string +): Promise { + // Fetch tools from MCP servers + const mcpServerTools = await Promise.all( + MCP_SERVERS.map(async (serverName) => { + const response = await client.tools.list({ + toolkit: serverName, + limit: 30, + }); + return response.items; + }) + ); + + // Fetch individual tools by name + const individualToolDefs = await Promise.all( + INDIVIDUAL_TOOLS.map((toolName) => client.tools.get(toolName)) + ); + + // Combine and deduplicate + const allTools = [...mcpServerTools.flat(), ...individualToolDefs]; + const uniqueTools = Array.from( + new Map(allTools.map((t) => [t.qualified_name, t])).values() + ); + + // Convert Arcade tools to Zod format (with proper schemas) + const zodTools = toZod({ + tools: uniqueTools, + client, + userId, + executeFactory: ({ toolDefinition, client, userId }) => { + const toolName = toolDefinition.qualified_name; + return async (args: unknown) => { + // Handle authorization + const authResult = await client.tools.authorize({ + tool_name: toolName, + user_id: userId, + }); + + if (authResult.status !== "completed") { + console.log(`\nAuthorization required for ${toolName}`); + console.log(`Please visit: ${authResult.url}\n`); + await client.auth.waitForCompletion(authResult); + console.log("Authorization complete!\n"); + } + + // Execute the tool + const result = await client.tools.execute({ + tool_name: toolName, + input: args as Record, + user_id: userId, + }); + + if (!result.success) { + throw new Error( + `Tool execution failed: ${result.output?.error?.message}` + ); + } + + return result.output?.value; + }; + }, + }); + + // Convert to Google ADK FunctionTool format + return zodTools.map((tool) => + new FunctionTool({ + name: tool.name, + description: tool.description, + parameters: tool.parameters, + execute: tool.execute, + }) + ); +} + +async function main() { + const client = new Arcade(); + const sessionService = new InMemorySessionService(); + + // Get Arcade tools + const arcadeTools = await getArcadeTools(client, ARCADE_USER_ID); + + // Create the agent + const agent = new LlmAgent({ + name: APP_NAME, + description: "An assistant that helps with Gmail and Slack", + model: MODEL, + instruction: SYSTEM_PROMPT, + tools: arcadeTools, + }); + + // Create a session + const session = await sessionService.createSession({ + appName: APP_NAME, + userId: ARCADE_USER_ID, + state: { user_id: ARCADE_USER_ID }, + }); + + // Create the runner + const runner = new Runner({ + appName: APP_NAME, + agent, + sessionService, + }); + + // Set up interactive chat + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + console.log("Hello! I'm your Google ADK Agent with Arcade Tools."); + console.log("Try asking me to summarize your recent emails or send a Slack message!"); + console.log("Type 'exit' to quit.\n"); + + while (true) { + const input = await rl.question("> "); + if (input.toLowerCase() === "exit") { + break; + } + + try { + const events = runner.runAsync({ + userId: ARCADE_USER_ID, + sessionId: session.id, + newMessage: { role: "user", parts: [{ text: input }] }, + }); + + for await (const event of events) { + if (event.content?.parts?.[0]?.text) { + console.log(`\n${event.author}: ${event.content.parts[0].text}\n`); + } + } + } catch (error) { + console.error("Error:", error); + } + } + + console.log("Goodbye!"); + rl.close(); + process.exit(0); +} + +main().catch(console.error); +``` + +
+ +## Next steps + +- Add more tools by modifying `MCP_SERVERS` and `INDIVIDUAL_TOOLS` +- Build multi-agent systems with different Arcade tools +- Explore [creating custom tools](/guides/create-tools/tool-basics/build-mcp-server) with the Arcade Tool SDK diff --git a/app/en/get-started/agent-frameworks/langchain/_meta.tsx b/app/en/get-started/agent-frameworks/langchain/_meta.tsx index c5fae73e0..cc2bbb33c 100644 --- a/app/en/get-started/agent-frameworks/langchain/_meta.tsx +++ b/app/en/get-started/agent-frameworks/langchain/_meta.tsx @@ -1,5 +1,6 @@ export default { - "use-arcade-with-langchain-py": "Setup Arcade with LangChain (Python)", - "use-arcade-with-langchain-ts": "Setup Arcade with LangChain (TypeScript)", - "auth-langchain-tools": "Authorizing existing tools", + overview: "Overview", + "use-arcade-with-langchain-py": "Setup (Python)", + "use-arcade-with-langchain-ts": "Setup (TypeScript)", + "auth-langchain-tools": "Authorizing Existing Tools", }; diff --git a/app/en/get-started/agent-frameworks/langchain/overview/page.mdx b/app/en/get-started/agent-frameworks/langchain/overview/page.mdx new file mode 100644 index 000000000..f6f31aec8 --- /dev/null +++ b/app/en/get-started/agent-frameworks/langchain/overview/page.mdx @@ -0,0 +1,31 @@ +--- +title: "Arcade with LangChain" +description: "Integrate Arcade tools with LangChain agents" +--- + +# Arcade with LangChain + +[LangChain](https://www.langchain.com/) is a popular framework for building AI agents that abstracts much of the complexity of agent development. Arcade integrates with both the Python and JavaScript versions, giving your agents access to Gmail, GitHub, Slack, and 100+ other tools. + +## Get started + +Choose your language to set up Arcade with LangChain: + +- **[Python setup](/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py)** - Build an agent with Arcade tools using `langchain-arcade` +- **[TypeScript setup](/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts)** - Build an agent with Arcade tools using `@arcadeai/arcadejs` + +## What you can build + +With Arcade and LangChain, your agents can: + +- Read and send emails via Gmail +- Post messages to Slack channels +- Create GitHub issues and pull requests +- Search the web and extract content +- Access 100+ other integrations + +Browse the [full MCP server catalog](/resources/integrations) to see all available tools. + +## Advanced topics + +- **[Authorizing existing tools](/get-started/agent-frameworks/langchain/auth-langchain-tools)** - Add Arcade authorization to your existing LangChain tools diff --git a/next.config.ts b/next.config.ts index f84c3c10d..b1a301972 100644 --- a/next.config.ts +++ b/next.config.ts @@ -29,6 +29,14 @@ const nextConfig: NextConfig = withLlmsTxt({ destination: "/:locale/resources/integrations", permanent: false, }, + // Google ADK tutorial consolidation - redirect old URL to new + { + source: + "/:locale/get-started/agent-frameworks/google-adk/use-arcade-tools", + destination: + "/:locale/get-started/agent-frameworks/google-adk/overview", + permanent: true, + }, // Auto-added redirects for deleted pages { source: "/:locale/resources/integrations/preview", @@ -541,7 +549,7 @@ const nextConfig: NextConfig = withLlmsTxt({ { source: "/:locale/home/google-adk/use-arcade-tools", destination: - "/:locale/get-started/agent-frameworks/google-adk/use-arcade-tools", + "/:locale/get-started/agent-frameworks/google-adk/setup-python", permanent: true, }, { @@ -754,7 +762,7 @@ const nextConfig: NextConfig = withLlmsTxt({ { source: "/:locale/guides/agent-frameworks/google-adk/python", destination: - "/:locale/get-started/agent-frameworks/google-adk/use-arcade-tools", + "/:locale/get-started/agent-frameworks/google-adk/setup-python", permanent: true, }, { diff --git a/public/_markdown/en/get-started/agent-frameworks/google-adk/overview.md b/public/_markdown/en/get-started/agent-frameworks/google-adk/overview.md index 217228ed7..3091acfa5 100644 --- a/public/_markdown/en/get-started/agent-frameworks/google-adk/overview.md +++ b/public/_markdown/en/get-started/agent-frameworks/google-adk/overview.md @@ -1,138 +1,36 @@ --- -title: "Arcade with Google ADK overview" -description: "Comprehensive guide to using Arcade with the Google ADK library" +title: "Arcade with Google ADK" +description: "Integrate Arcade tools with Google ADK agents" --- [Agent Frameworks](/en/get-started/agent-frameworks.md) Google ADKOverview # Arcade with Google ADK -The `google-adk-arcade` package provides a seamless integration between [Arcade](https://arcade.dev)  and the [Google ADK](https://github.com/google/adk-python/) . This integration allows you to enhance your AI with powerful Arcade including Google Mail, LinkedIn, GitHub, and many more. +[Google ADK](https://github.com/google/adk-python/)  is a modular framework for building and deploying AI . It’s optimized for Gemini and the Google ecosystem. Arcade integrates with both the Python and TypeScript versions, giving your agents access to Gmail, GitHub, Slack, and 100+ other . -## Installation +## Get started -Install the necessary packages to get started: +Choose your language to set up Arcade with Google ADK: -```bash -pip install google-adk-arcade -``` +- **[Python setup](/get-started/agent-frameworks/google-adk/setup-python.md) + ** - Build an with Arcade using the `google-adk-arcade` package +- **[TypeScript setup](/get-started/agent-frameworks/google-adk/setup-typescript.md) + ** - Build an with Arcade using `@arcadeai/arcadejs` -Make sure you have your key ready. [Get an API key](/get-started/setup/api-keys.md) if you don’t already have one. +## What you can build -## Key features +With Arcade and Google ADK, your can: -- **Easy integration** with the Google ADK framework -- **Access to all Arcade Servers** including Google, GitHub, LinkedIn, X, and more -- **Create custom ** with the Arcade Tool SDK -- **Manage authentication** for that require it -- **Asynchronous support** compatible with Google’s ADK framework +- Read and send emails via Gmail +- Post messages to Slack channels +- Create GitHub issues and pull requests +- Search the web and extract content +- Access 100+ other integrations -## Basic usage - -Here’s a simple example of using Arcade tools with OpenAI : - -```python -import asyncio - -from arcadepy import AsyncArcade -from google.adk import Agent, Runner -from google.adk.artifacts import InMemoryArtifactService -from google.adk.sessions import InMemorySessionService -from google.genai import types - -from google_adk_arcade.tools import get_arcade_tools - - -async def main(): - app_name = 'my_app' - user_id = '{arcade_user_id}' - session_service = InMemorySessionService() - artifact_service = InMemoryArtifactService() - client = AsyncArcade() - - google_tools = await get_arcade_tools(client, tools=["Gmail.ListEmails"]) - - # authorize the tools - for tool in google_tools: - result = await client.tools.authorize( - tool_name=tool.name, - user_id=user_id - ) - if result.status != "completed": - print(f"Click this link to authorize {tool.name}:\n{result.url}") - await client.auth.wait_for_completion(result) - - # create the agent - google_agent = Agent( - model="gemini-2.0-flash", - name="google_tool_agent", - instruction="I can use Google tools to manage an inbox!", - description="An agent equipped with tools to read Gmail emails." - tools=google_tools, - ) - - #create the session and pass the user ID to the state - session = await session_service.create_session( - app_name=app_name, user_id=user_id, state={ - "user_id": user_id, - } - ) - - runner = Runner( - app_name=app_name, - agent=google_agent, - artifact_service=artifact_service, - session_service=session_service, - ) - - user_input = "summarize my latest 3 emails" - content = types.Content( - role='user', parts=[types.Part.from_text(text=user_input)] - ) - for event in runner.run( - user_id=user_id, - session_id=session.id, - new_message=content, - ): - if event.content.parts and event.content.parts[0].text: - print(f'** {event.author}: {event.content.parts[0].text}') - -if __name__ == '__main__': - asyncio.run(main()) -``` - -## Handling authorization - -When a user needs to authorize access to a tool (like Google or GitHub), the will reply with a URL for the to visit, which will be displayed to the user. - -After visiting the URL and authorizing access, the user can run the again with the same `user_id`, and it will work without requiring re-authorization. - -Alternatively, you can authorize the tool before running the : - -```python -# authorize the tools -for tool in google_tools: - result = await client.tools.authorize( - tool_name=tool.name, - user_id=user_id - ) - if result.status != "completed": - print(f"Click this link to authorize {tool.name}:\n{result.url}") - await client.auth.wait_for_completion(result) -``` - -## Next steps - -Ready to start building with Arcade and OpenAI ? Check out these guides: - -- [Using Arcade tools](/get-started/agent-frameworks/google-adk/use-arcade-tools.md) - - Learn the basics of using Arcade with Google ADK -- [Creating custom tools](/guides/create-tools/tool-basics/build-mcp-server.md) - - Build your own tools with the Arcade SDK - -Enjoy exploring Arcade and building powerful AI-enabled applications. +Browse the [full MCP server catalog](/resources/integrations.md) to see all available . Last updated on February 11, 2026 [Custom auth flow](/en/get-started/agent-frameworks/crewai/custom-auth-flow.md) -[Setup Arcade with Google ADK (Python)](/en/get-started/agent-frameworks/google-adk/use-arcade-tools.md) +[Setup (Python)](/en/get-started/agent-frameworks/google-adk/setup-python.md) diff --git a/public/_markdown/en/get-started/agent-frameworks/google-adk/setup-python.md b/public/_markdown/en/get-started/agent-frameworks/google-adk/setup-python.md new file mode 100644 index 000000000..a37c8aef0 --- /dev/null +++ b/public/_markdown/en/get-started/agent-frameworks/google-adk/setup-python.md @@ -0,0 +1,740 @@ +--- +title: "Setup Arcade with Google ADK (Python)" +description: "Build an agent with Arcade tools using Google ADK" +--- +[Agent Frameworks](/en/get-started/agent-frameworks.md) +[Google ADK](/en/get-started/agent-frameworks/google-adk/overview.md) +Setup (Python) + +# Setup Arcade with Google ADK (Python) + +Google ADK is a modular framework for building and deploying AI . It optimizes for Gemini and the Google Ecosystem, but supports any model. + +## Outcomes + +Learn how to integrate Arcade using Google ADK primitives + +### You will Learn + +- How to retrieve Arcade and transform them into Google ADK tools +- How to build a Google ADK +- How to integrate Arcade into the agentic flow +- How to manage Arcade tool authorization for Google ADK + +### Prerequisites + +- [Arcade account](https://app.arcade.dev/register) + +- The [`uv` package manager](https://docs.astral.sh/uv/) + + +## The agent architecture you will build in this guide + +In this guide, you will build an that can use Arcade to help the with their requests. It will follow the ReAct pattern, where the agent thinks about what to do, plans the steps, and then executes the steps, calling tools as needed. + +### Create a new project + +Create a new directory for your and initialize a new virtual environment: + +```bash +mkdir google-adk-arcade-example +cd google-adk-arcade-example +uv init +uv venv +source .venv/bin/activate +``` + +Install the necessary packages: + +```bash +uv add arcadepy google-adk +``` + +### Configure API keys + +Provide your Arcade and Google . You can store it in environment variables or directly in your code: + +> Need an key? Visit the [Get an API key](/get-started/setup/api-keys.md) page to create one. + +Create a new file called `.env` and add the following environment variables: + +```bash +# .env +# Arcade API key +ARCADE_API_KEY=YOUR_ARCADE_API_KEY +# Arcade user ID (this is the email address you used to login to Arcade) +ARCADE_USER_ID={arcade_user_id} +# Google API key +GOOGLE_API_KEY=YOUR_GOOGLE_API_KEY +# Google GenAI use VertexAI +GOOGLE_GENAI_USE_VERTEXAI=FALSE +``` + +### Import the necessary packages + +Create a new file called `main.py` and add the following code: + +```python +# main.py +from arcadepy import AsyncArcade +from arcadepy.types import ToolDefinition +from arcadepy.types.execute_tool_response import ExecuteToolResponse +from google.adk import Agent, Runner +from google.adk.artifacts import InMemoryArtifactService +from google.adk.sessions import InMemorySessionService, Session +from google.adk.tools import ToolContext, FunctionTool +from google.adk.tools._automatic_function_calling_util import ( + _map_pydantic_type_to_property_schema +) +from google.genai import types +from pydantic import BaseModel, Field, create_model +from typing import Any +from typing_extensions import override +from dotenv import load_dotenv +import logging +import os +``` + +This includes multiple imports, here’s a breakdown: + +- Arcade imports: + - `AsyncArcade`: The , used to interact with the . + - `ToolDefinition`: The definition type, used to define the input and output of a tool. + - `ExecuteToolResponse`: The response type for the execute response. +- Google ADK imports: + - `Agent`: The Google ADK class, used to define an agent. + - `Runner`: The Google ADK runner, used to manage and run the agentic loop. + - `InMemoryArtifactService`: The in-memory artifact service, used to store and retrieve artifacts, such as the ’s state. + - `InMemorySessionService`: The in-memory session service, used to store and retrieve sessions, such as the conversation history. + - `Session`: The session type, used to define a session. + - `ToolContext`: The , used to provide contextual information, such as the ID. + - `FunctionTool`: The Google ADK function class, used to define a function tool. + - `_map_pydantic_type_to_property_schema`: A utility function that maps Pydantic types to Google ADK schemas. +- Google GenAI imports: + - `types`: The Google GenAI types, used to define the types for the Google GenAI API. +- Pydantic imports: + - `BaseModel`: The Pydantic base model, used to define a base model. + - `Field`: The Pydantic field, used to define a field. + - `create_model`: A Pydantic function used to create a model from a dictionary of fields. + - `typing` imports: Used to provide type hints for the code. + - `dotenv`: Used to load environment variables from a `.env` file. +- Other imports: + - `logging`: The logging module, used to log messages to the console. + - `os`: Used to retrieve loaded environment variables. + +### Configure the agent + +These variables set the configuration for the rest of the code to customize the and manage the . Feel free to configure them to your liking. Set the `google_genai.types` logging level to `ERROR` to avoid a lot of noise in the console. Load the environment variables from the `.env` file using `load_dotenv()`. + +```python +# main.py +logging.getLogger("google_genai.types").setLevel(logging.ERROR) + +load_dotenv() + +# The Arcade User ID identifies who is authorizing each service. +ARCADE_USER_ID = os.getenv("ARCADE_USER_ID") +# This determines which MCP server is providing the tools, you can customize this to make a Notion agent. All tools from the MCP servers defined in the array will be used. +MCP_SERVERS = ["Slack"] +# This determines individual tools. Useful to pick specific tools when you don't need all of them. +TOOLS = ["Gmail_ListEmails", "Gmail_SendEmail", "Gmail_WhoAmI"] +# This prompt defines the behavior of the agent. +MODEL = "gemini-2.5-flash" +# This determines which LLM model will be used inside the agent +SYSTEM_PROMPT = "You are a helpful assistant that can assist with Gmail and Slack." +# This determines the name of the agent. +AGENT_NAME = "AwesomeAgent" +``` + +### Write a utility function to transform Arcade tool definitions into Pydantic models + +In this utility function, you transform an Arcade definition into a Pydantic model. Later, you will transform these models to construct tools in the format expected by Google ADK. The `tool_definition_to_pydantic_model` function extracts the tools’ parameters, name, and description, and maps them to a Pydantic model. + +```python +# main.py +# Mapping of Arcade value types to Python types +TYPE_MAPPING = { + "string": str, + "number": float, + "integer": int, + "boolean": bool, + "array": list, + "json": dict, +} + + +def get_python_type(val_type: str) -> Any: + _type = TYPE_MAPPING.get(val_type) + if _type is None: + raise ValueError(f"Invalid value type: {val_type}") + return _type + + +def tool_definition_to_pydantic_model(tool_def: ToolDefinition) -> type[BaseModel]: + try: + fields: dict[str, Any] = {} + for param in tool_def.input.parameters or []: + param_type = get_python_type(param.value_schema.val_type) + if param_type == list and param.value_schema.inner_val_type: # noqa: E721 + inner_type: type[Any] = get_python_type(param.value_schema.inner_val_type) + param_type = list[inner_type] # type: ignore[valid-type] + param_description = param.description or "No description provided." + default = ... if param.required else None + fields[param.name] = ( + param_type, + Field(default=default, description=param_description), + ) + return create_model(f"{tool_def.name}Args", **fields) + except ValueError as e: + raise ValueError( + f"Error converting {tool_def.name} parameters into pydantic model: {e}" + ) +``` + +### Write a custom class that extends the Google ADK FunctionTool class + +Here, you define the `ArcadeTool` class that extends the Google ADK `FunctionTool` class to add the following capability: + +- Authorize the tool with the with the `_authorize_tool` helper function +- Execute the tool with the with the `_async_invoke_arcade_tool` helper function +- Map the Pydantic model to the Google ADK schema with the `_map_pydantic_type_to_property_schema` utility function + +You also define a `ToolError` class to handle errors from the Arcade . It wraps the `ExecuteToolResponse` and provides an informative error message that the system can handle in the agentic loop in case anything goes wrong. + +This class captures the authorization flow outside of the agent’s , which is a good practice for security and context engineering. By handling everything in the `ArcadeTool` class, you remove the risk of the LLM replacing the authorization URL or leaking it, and you keep the context free from any authorization-related traces, which reduces the risk of hallucinations, and reduces context bloat. + +```python +# main.py +class ToolError(ValueError): + def __init__(self, result: ExecuteToolResponse): + self.result = result + + @property + def message(self): + return self.result.output.error.message + + def __str__(self): + return f"Tool {self.result.tool_name} failed with error: {self.message}" + + +async def _authorize_tool(client: AsyncArcade, tool_context: ToolContext, tool_name: str): + if not tool_context.state.get("user_id"): + raise ValueError("No user ID and authorization required for tool") + + result = await client.tools.authorize( + tool_name=tool_name, + user_id=tool_context.state.get("user_id"), + ) + if result.status != "completed": + print(f"{tool_name} requires authorization to run, please open the following URL to authorize: {result.url}") + + await client.auth.wait_for_completion(result) + + +async def _async_invoke_arcade_tool( + tool_context: ToolContext, + tool_args: dict, + tool_name: str, + client: AsyncArcade, +) -> dict: + await _authorize_tool(client, tool_context, tool_name) + + print(f"Executing tool: {tool_name} with args: {tool_args}") + + result = await client.tools.execute( + tool_name=tool_name, + input=tool_args, + user_id=tool_context.state.get("user_id"), + ) + + if not result.success: + raise ToolError(result) + + print(f"{tool_name} called successfully, processing result...") + return result.output.value + + +class ArcadeTool(FunctionTool): + def __init__(self, + name: str, + description: str, + schema: BaseModel, + client: AsyncArcade): + + # define callable + async def func(tool_context: ToolContext, + **kwargs: Any) -> dict: + return await _async_invoke_arcade_tool( + tool_context=tool_context, + tool_args=kwargs, + tool_name=name, + client=client + ) + func.__name__ = name.lower() + func.__doc__ = description + + super().__init__(func) + schema = schema.model_json_schema() + _map_pydantic_type_to_property_schema(schema) + self.schema = schema + self.name = name.replace(".", "_") + self.description = description + self.client = client + + @override + async def run_async(self, *, args: dict[str, Any], + tool_context: ToolContext) -> Any: + return await _async_invoke_arcade_tool( + tool_context=tool_context, + tool_args=args, + tool_name=self.name, + client=self.client, + ) + + @override + def _get_declaration(self) -> types.FunctionDeclaration: + return types.FunctionDeclaration( + parameters=types.Schema( + type='OBJECT', + properties=self.schema["properties"], + ), + description=self.description, + name=self.name, + ) +``` + +### Retrieve Arcade tools and transform them into Google ADK tools + +Here you get the Arcade tools you want the agent to utilize, and transform them into Google ADK tools. The first step is to initialize the , and get the you want to work with. + +This helper function is long, here’s a breakdown of what it does for clarity: + +- retrieve tools from all configured servers (defined in the `MCP_SERVERS` variable) +- retrieve individual (defined in the `TOOLS` variable) +- transform the Arcade to Google ADK tools with the `ArcadeTool` class you defined earlier + +```python +# main.py +async def get_arcade_tools( + client: AsyncArcade | None = None, + tools: list[str] | None = None, + mcp_servers: list[str] | None = None, + **kwargs: dict[str, Any], +) -> list[ArcadeTool]: + if not client: + client = AsyncArcade() + + if not tools and not mcp_servers: + raise ValueError("No tools or toolkits provided to retrieve tool definitions") + + tool_formats: list[ToolDefinition] = [] + # Retrieve individual tools if specified + if tools: + tasks = [client.tools.get(name=tool_id) + for tool_id in tools] + responses = await asyncio.gather(*tasks) + for response in responses: + tool_formats.append(response) + + # Retrieve tools from specified toolkits + if mcp_servers: + tasks = [client.tools.list(toolkit=mcp_server) + for mcp_server in mcp_servers] + responses = await asyncio.gather(*tasks) + + # Combine the tool definitions from each response. + for response in responses: + tool_formats.extend(response.items) + + tool_functions = [] + for tool in tool_formats: + sanitized_name = tool.qualified_name.replace(".", "_") + tool_function = ArcadeTool( + name=sanitized_name, + description=tool.description, + schema=tool_definition_to_pydantic_model(tool), + client=client, + ) + tool_functions.append(tool_function) + + return tool_functions +``` + +### Create the main function + +The main function is where you: + +- Initialize the session and artifact services +- Get the Arcade tools from the configured servers +- Create an with the Arcade +- Initialize the conversation +- Run the loop + +Google ADK provides a `Runner` class that manages the agentic loop, and will employ the session and artifact services you created earlier to store the conversation history and state. Therefore, you don’t need to manually store the conversation history or agent state, and you can just pass the latest message to the runner. + +```python +# main.py +async def main(): + + session_service = InMemorySessionService() + artifact_service = InMemoryArtifactService() + client = AsyncArcade() + + arcade_tools = await get_arcade_tools(client, + tools=TOOLS, + mcp_servers=MCP_SERVERS) + + agent = Agent( + model=MODEL, + name=AGENT_NAME, + instruction=SYSTEM_PROMPT, + tools=arcade_tools, + ) + + session = await session_service.create_session( + app_name=AGENT_NAME, user_id=ARCADE_USER_ID, state={ + "user_id": ARCADE_USER_ID, + } + ) + + runner = Runner( + app_name=AGENT_NAME, + agent=agent, + artifact_service=artifact_service, + session_service=session_service, + ) + + async def run_prompt(session: Session, new_message: str): + content = types.Content( + role='user', parts=[types.Part.from_text(text=new_message)] + ) + async for event in runner.run_async( + user_id=ARCADE_USER_ID, + session_id=session.id, + new_message=content, + ): + if event.content.parts and event.content.parts[0].text: + print(f'** {event.author}: {event.content.parts[0].text}') + + while True: + user_input = input("User: ") + if user_input.lower() == "exit": + print("Goodbye!") + break + await run_prompt(session, user_input) + + +if __name__ == '__main__': + import asyncio + asyncio.run(main()) +``` + +### Run the agent + +```bash +uv run main.py +``` + +You should see the responding to your prompts like any model, as well as handling any calls and authorization requests. Here are some example prompts you can try: + +- “Send me an email with a random haiku about Google ADK” +- “Summarize my latest 3 emails” +- “summarize my latest 3 emails, then send me that summary on a Slack DM” + +## Tips for selecting tools + +- **Relevance**: Pick only the you need. Avoid utilizing all tools at once. +- ** identification**: Always provide a unique and consistent `user_id` for each user. Apply your internal or database user ID, not something entered by the user. + +## Next steps + +Now that you have integrated Arcade into your Google ADK application, you can: + +- Experiment with different servers, such as “Github” or “LinkedIn” +- Customize the ’s instructions for specific tasks +- Try out multi- systems with different Arcade +- Build your own custom tools with the Arcade SDK + +## Example code + +### **main.py** (full file) + +```python +# main.py +from arcadepy import AsyncArcade +from arcadepy.types import ToolDefinition +from arcadepy.types.execute_tool_response import ExecuteToolResponse +from google.adk import Agent, Runner +from google.adk.artifacts import InMemoryArtifactService +from google.adk.sessions import InMemorySessionService, Session +from google.adk.tools import ToolContext, FunctionTool +from google.adk.tools._automatic_function_calling_util import ( + _map_pydantic_type_to_property_schema +) +from google.genai import types +from pydantic import BaseModel, Field, create_model +from typing import Any +from typing_extensions import override +from dotenv import load_dotenv +import logging +import os + +logging.getLogger("google_genai.types").setLevel(logging.ERROR) + +load_dotenv() + +# The Arcade User ID identifies who is authorizing each service. +ARCADE_USER_ID = os.getenv("ARCADE_USER_ID") +# This determines which MCP server is providing the tools, you can customize this to make a Notion agent. All tools from the MCP servers defined in the array will be used. +MCP_SERVERS = ["Slack"] +# This determines individual tools. Useful to pick specific tools when you don't need all of them. +TOOLS = ["Gmail_ListEmails", "Gmail_SendEmail", "Gmail_WhoAmI"] +# This prompt defines the behavior of the agent. +MODEL = "gemini-2.5-flash" +# This determines which LLM model will be used inside the agent +SYSTEM_PROMPT = "You are a helpful assistant that can assist with Gmail and Slack." +# This determines the name of the agent. +AGENT_NAME = "AwesomeAgent" + + +# Mapping of Arcade value types to Python types +TYPE_MAPPING = { + "string": str, + "number": float, + "integer": int, + "boolean": bool, + "array": list, + "json": dict, +} + + +def get_python_type(val_type: str) -> Any: + _type = TYPE_MAPPING.get(val_type) + if _type is None: + raise ValueError(f"Invalid value type: {val_type}") + return _type + + +def tool_definition_to_pydantic_model(tool_def: ToolDefinition) -> type[BaseModel]: + try: + fields: dict[str, Any] = {} + for param in tool_def.input.parameters or []: + param_type = get_python_type(param.value_schema.val_type) + if param_type == list and param.value_schema.inner_val_type: # noqa: E721 + inner_type: type[Any] = get_python_type(param.value_schema.inner_val_type) + param_type = list[inner_type] # type: ignore[valid-type] + param_description = param.description or "No description provided." + default = ... if param.required else None + fields[param.name] = ( + param_type, + Field(default=default, description=param_description), + ) + return create_model(f"{tool_def.name}Args", **fields) + except ValueError as e: + raise ValueError( + f"Error converting {tool_def.name} parameters into pydantic model: {e}" + ) + + +class ToolError(ValueError): + def __init__(self, result: ExecuteToolResponse): + self.result = result + + @property + def message(self): + return self.result.output.error.message + + def __str__(self): + return f"Tool {self.result.tool_name} failed with error: {self.message}" + + +async def _authorize_tool(client: AsyncArcade, tool_context: ToolContext, tool_name: str): + if not tool_context.state.get("user_id"): + raise ValueError("No user ID and authorization required for tool") + + result = await client.tools.authorize( + tool_name=tool_name, + user_id=tool_context.state.get("user_id"), + ) + if result.status != "completed": + print(f"{tool_name} requires authorization to run, please open the following URL to authorize: {result.url}") + + await client.auth.wait_for_completion(result) + + +async def _async_invoke_arcade_tool( + tool_context: ToolContext, + tool_args: dict, + tool_name: str, + client: AsyncArcade, +) -> dict: + await _authorize_tool(client, tool_context, tool_name) + + print(f"Executing tool: {tool_name} with args: {tool_args}") + + result = await client.tools.execute( + tool_name=tool_name, + input=tool_args, + user_id=tool_context.state.get("user_id"), + ) + + if not result.success: + raise ToolError(result) + + print(f"{tool_name} called successfully, processing result...") + return result.output.value + + +class ArcadeTool(FunctionTool): + def __init__(self, + name: str, + description: str, + schema: BaseModel, + client: AsyncArcade): + + # define callable + async def func(tool_context: ToolContext, + **kwargs: Any) -> dict: + return await _async_invoke_arcade_tool( + tool_context=tool_context, + tool_args=kwargs, + tool_name=name, + client=client + ) + func.__name__ = name.lower() + func.__doc__ = description + + super().__init__(func) + schema = schema.model_json_schema() + _map_pydantic_type_to_property_schema(schema) + self.schema = schema + self.name = name.replace(".", "_") + self.description = description + self.client = client + + @override + async def run_async(self, *, args: dict[str, Any], + tool_context: ToolContext) -> Any: + return await _async_invoke_arcade_tool( + tool_context=tool_context, + tool_args=args, + tool_name=self.name, + client=self.client, + ) + + @override + def _get_declaration(self) -> types.FunctionDeclaration: + return types.FunctionDeclaration( + parameters=types.Schema( + type='OBJECT', + properties=self.schema["properties"], + ), + description=self.description, + name=self.name, + ) + + +async def get_arcade_tools( + client: AsyncArcade | None = None, + tools: list[str] | None = None, + mcp_servers: list[str] | None = None, + **kwargs: dict[str, Any], +) -> list[ArcadeTool]: + if not client: + client = AsyncArcade() + + if not tools and not mcp_servers: + raise ValueError("No tools or toolkits provided to retrieve tool definitions") + + tool_formats: list[ToolDefinition] = [] + # Retrieve individual tools if specified + if tools: + tasks = [client.tools.get(name=tool_id) + for tool_id in tools] + responses = await asyncio.gather(*tasks) + for response in responses: + tool_formats.append(response) + + # Retrieve tools from specified toolkits + if mcp_servers: + tasks = [client.tools.list(toolkit=mcp_server) + for mcp_server in mcp_servers] + responses = await asyncio.gather(*tasks) + + # Combine the tool definitions from each response. + for response in responses: + tool_formats.extend(response.items) + + tool_functions = [] + for tool in tool_formats: + sanitized_name = tool.qualified_name.replace(".", "_") + tool_function = ArcadeTool( + name=sanitized_name, + description=tool.description, + schema=tool_definition_to_pydantic_model(tool), + client=client, + ) + tool_functions.append(tool_function) + + return tool_functions + + +async def main(): + + session_service = InMemorySessionService() + artifact_service = InMemoryArtifactService() + client = AsyncArcade() + + arcade_tools = await get_arcade_tools(client, + tools=TOOLS, + mcp_servers=MCP_SERVERS) + + agent = Agent( + model=MODEL, + name=AGENT_NAME, + instruction=SYSTEM_PROMPT, + tools=arcade_tools, + ) + + session = await session_service.create_session( + app_name=AGENT_NAME, user_id=ARCADE_USER_ID, state={ + "user_id": ARCADE_USER_ID, + } + ) + + runner = Runner( + app_name=AGENT_NAME, + agent=agent, + artifact_service=artifact_service, + session_service=session_service, + ) + + async def run_prompt(session: Session, new_message: str): + content = types.Content( + role='user', parts=[types.Part.from_text(text=new_message)] + ) + async for event in runner.run_async( + user_id=ARCADE_USER_ID, + session_id=session.id, + new_message=content, + ): + if event.content.parts and event.content.parts[0].text: + print(f'** {event.author}: {event.content.parts[0].text}') + + while True: + user_input = input("User: ") + if user_input.lower() == "exit": + print("Goodbye!") + break + await run_prompt(session, user_input) + + +if __name__ == '__main__': + import asyncio + asyncio.run(main()) +``` + +Last updated on February 10, 2026 + +[Overview](/en/get-started/agent-frameworks/google-adk/overview.md) +[Setup (TypeScript)](/en/get-started/agent-frameworks/google-adk/setup-typescript.md) diff --git a/public/_markdown/en/get-started/agent-frameworks/google-adk/setup-typescript.md b/public/_markdown/en/get-started/agent-frameworks/google-adk/setup-typescript.md new file mode 100644 index 000000000..bc2f27c39 --- /dev/null +++ b/public/_markdown/en/get-started/agent-frameworks/google-adk/setup-typescript.md @@ -0,0 +1,482 @@ +--- +title: "Setup Arcade with Google ADK (TypeScript)" +description: "Build an agent with Arcade tools using Google ADK for TypeScript" +--- +[Agent Frameworks](/en/get-started/agent-frameworks.md) +[Google ADK](/en/get-started/agent-frameworks/google-adk/overview.md) +Setup (TypeScript) + +# Setup Arcade with Google ADK (TypeScript) + +[Google ADK for TypeScript](https://github.com/google/adk-js)  provides a framework for building AI in TypeScript. Arcade’s `@arcadeai/arcadejs` library provides the integration, allowing your agents to access Gmail, GitHub, Slack, and 100+ other services. + +## Outcomes + +Build an that uses Arcade to help with Gmail and Slack + +### You will Learn + +- How to retrieve Arcade and convert them to Google ADK format +- How to build a Google ADK with Arcade +- How to handle authorization + +### Prerequisites + +- [Arcade account](https://app.arcade.dev/register) + +- [Obtain an Arcade API key](/get-started/setup/api-keys.md) + +- [Node.js](https://nodejs.org/) +   18+ (includes npm) or [Bun](https://bun.sh/) +   +- A [Gemini API key](https://aistudio.google.com/apikey) +   + +## How Arcade integrates with Google ADK + +Google ADK uses `FunctionTool` for defining callable . Arcade’s `@arcadeai/arcadejs` library provides `toZod` to convert tool definitions to Zod schemas, which Google ADK’s `FunctionTool` accepts. The `Runner` class manages the ’s conversation loop, while `InMemorySessionService` handles session state. + +## Build the agent + +### Create a new project + +Create a new directory and install dependencies: + +### npm + +```bash +mkdir google-adk-arcade-ts +cd google-adk-arcade-ts +npm init -y +npm install @google/adk @arcadeai/arcadejs dotenv +``` + +### bun + +```bash +mkdir google-adk-arcade-ts +cd google-adk-arcade-ts +bun init -y +bun add @google/adk @arcadeai/arcadejs dotenv +``` + +Create a `.env` file with your : + +```bash +# .env +# Arcade API key from https://app.arcade.dev/api-keys +ARCADE_API_KEY=YOUR_ARCADE_API_KEY +# Your Arcade user ID (the email you used to sign up) +ARCADE_USER_ID={arcade_user_id} +# Google Gemini API key (get one at https://aistudio.google.com/apikey) +GEMINI_API_KEY=YOUR_GEMINI_API_KEY +``` + +### Create the agent file + +Create `index.ts` with the following imports and configuration: + +```typescript +// index.ts +import { + LlmAgent, + FunctionTool, + Runner, + InMemorySessionService, + setLogLevel, + LogLevel, +} from "@google/adk"; +import Arcade from "@arcadeai/arcadejs"; +import { toZod } from "@arcadeai/arcadejs/lib/zod/zod"; +import "dotenv/config"; +import readline from "node:readline/promises"; + +// Suppress verbose ADK logs (options: DEBUG, INFO, WARNING, ERROR) +setLogLevel(LogLevel.ERROR); + +// Configuration +const ARCADE_USER_ID = process.env.ARCADE_USER_ID || "default-user"; +const MCP_SERVERS = ["Slack"]; +const INDIVIDUAL_TOOLS = ["Gmail_ListEmails", "Gmail_SendEmail", "Gmail_WhoAmI"]; +const SYSTEM_PROMPT = "You are a helpful assistant that can assist with Gmail and Slack."; +const MODEL = "gemini-2.0-flash"; +const APP_NAME = "inbox_assistant"; +``` + +### Convert Arcade tools to Google ADK format + +Use Arcade’s `toZod` to convert definitions to Zod schemas, then wrap them in Google ADK’s `FunctionTool`: + +```typescript +// index.ts +async function getArcadeTools(client: Arcade, userId: string): Promise { + // Fetch tools from MCP servers + const mcpServerTools = await Promise.all( + MCP_SERVERS.map(async (serverName) => { + const response = await client.tools.list({ + toolkit: serverName, + limit: 30, + }); + return response.items; + }) + ); + + // Fetch individual tools by name + const individualToolDefs = await Promise.all( + INDIVIDUAL_TOOLS.map((toolName) => client.tools.get(toolName)) + ); + + // Combine and deduplicate + const allTools = [...mcpServerTools.flat(), ...individualToolDefs]; + const uniqueTools = Array.from( + new Map(allTools.map((t) => [t.qualified_name, t])).values() + ); + + // Convert Arcade tools to Zod format (with proper schemas) + const zodTools = toZod({ + tools: uniqueTools, + client, + userId, + executeFactory: ({ toolDefinition, client, userId }) => { + const toolName = toolDefinition.qualified_name; + return async (args: unknown) => { + // Handle authorization + const authResult = await client.tools.authorize({ + tool_name: toolName, + user_id: userId, + }); + + if (authResult.status !== "completed") { + console.log(`\nAuthorization required for ${toolName}`); + console.log(`Please visit: ${authResult.url}\n`); + await client.auth.waitForCompletion(authResult); + console.log("Authorization complete!\n"); + } + + // Execute the tool + const result = await client.tools.execute({ + tool_name: toolName, + input: args as Record, + user_id: userId, + }); + + if (!result.success) { + throw new Error(`Tool execution failed: ${result.output?.error?.message}`); + } + + return result.output?.value; + }; + }, + }); + + // Convert to Google ADK FunctionTool format + return zodTools.map((tool) => + new FunctionTool({ + name: tool.name, + description: tool.description, + parameters: tool.parameters, + execute: tool.execute, + }) + ); +} +``` + +**What’s happening here:** + +- `toZod` converts Arcade definitions to Zod schemas with proper parameter types +- `executeFactory` creates the execution function that handles authorization and calls +- Each Zod wraps in a Google ADK `FunctionTool` + +### Create and run the agent + +```typescript +// index.ts +async function main() { + const client = new Arcade(); + const sessionService = new InMemorySessionService(); + + // Get Arcade tools + const arcadeTools = await getArcadeTools(client, ARCADE_USER_ID); + + // Create the agent + const agent = new LlmAgent({ + name: APP_NAME, + description: "An assistant that helps with Gmail and Slack", + model: MODEL, + instruction: SYSTEM_PROMPT, + tools: arcadeTools, + }); + + // Create a session + const session = await sessionService.createSession({ + appName: APP_NAME, + userId: ARCADE_USER_ID, + state: { user_id: ARCADE_USER_ID }, + }); + + // Create the runner + const runner = new Runner({ + appName: APP_NAME, + agent, + sessionService, + }); + + // Set up interactive chat + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + console.log("Hello! I'm your Google ADK Agent with Arcade Tools."); + console.log("Try asking me to summarize your recent emails or send a Slack message!"); + console.log("Type 'exit' to quit.\n"); + + while (true) { + const input = await rl.question("> "); + if (input.toLowerCase() === "exit") { + break; + } + + try { + const events = runner.runAsync({ + userId: ARCADE_USER_ID, + sessionId: session.id, + newMessage: { role: "user", parts: [{ text: input }] }, + }); + + for await (const event of events) { + if (event.content?.parts?.[0]?.text) { + console.log(`\n${event.author}: ${event.content.parts[0].text}\n`); + } + } + } catch (error) { + console.error("Error:", error); + } + } + + console.log("Goodbye!"); + rl.close(); + process.exit(0); +} + +main().catch(console.error); +``` + +### Run the agent + +### npm + +```bash +npx tsx index.ts +``` + +### bun + +```bash +bun run index.ts +``` + +Google ADK for TypeScript is still changing. Check the [official documentation](https://google.github.io/adk-docs)  and [samples repository](https://github.com/google/adk-samples)  for the latest API updates. + +## Key takeaways + +- **`toZod`** from `@arcadeai/arcadejs` converts Arcade to Zod schemas with proper parameter types +- **`FunctionTool`** wraps the Zod for Google ADK +- **`Runner`** manages the ’s conversation loop with `runAsync()` +- **`InMemorySessionService`** handles session state between messages +- **Authorization** occurs in the execute factory. The auth URL appears when needed +- **`userId`** tracks authorization per - use a consistent ID for each user in your application + +## Example code + +### **index.ts** (full file) + +```typescript +// index.ts +import { + LlmAgent, + FunctionTool, + Runner, + InMemorySessionService, + setLogLevel, + LogLevel, +} from "@google/adk"; +import Arcade from "@arcadeai/arcadejs"; +import { toZod } from "@arcadeai/arcadejs/lib/zod/zod"; +import "dotenv/config"; +import readline from "node:readline/promises"; + +// Suppress verbose ADK logs (options: DEBUG, INFO, WARNING, ERROR) +setLogLevel(LogLevel.ERROR); + +// Configuration +const ARCADE_USER_ID = process.env.ARCADE_USER_ID || "default-user"; +const MCP_SERVERS = ["Slack"]; +const INDIVIDUAL_TOOLS = [ + "Gmail_ListEmails", + "Gmail_SendEmail", + "Gmail_WhoAmI", +]; +const SYSTEM_PROMPT = + "You are a helpful assistant that can assist with Gmail and Slack."; +const MODEL = "gemini-2.0-flash"; +const APP_NAME = "inbox_assistant"; + +async function getArcadeTools( + client: Arcade, + userId: string +): Promise { + // Fetch tools from MCP servers + const mcpServerTools = await Promise.all( + MCP_SERVERS.map(async (serverName) => { + const response = await client.tools.list({ + toolkit: serverName, + limit: 30, + }); + return response.items; + }) + ); + + // Fetch individual tools by name + const individualToolDefs = await Promise.all( + INDIVIDUAL_TOOLS.map((toolName) => client.tools.get(toolName)) + ); + + // Combine and deduplicate + const allTools = [...mcpServerTools.flat(), ...individualToolDefs]; + const uniqueTools = Array.from( + new Map(allTools.map((t) => [t.qualified_name, t])).values() + ); + + // Convert Arcade tools to Zod format (with proper schemas) + const zodTools = toZod({ + tools: uniqueTools, + client, + userId, + executeFactory: ({ toolDefinition, client, userId }) => { + const toolName = toolDefinition.qualified_name; + return async (args: unknown) => { + // Handle authorization + const authResult = await client.tools.authorize({ + tool_name: toolName, + user_id: userId, + }); + + if (authResult.status !== "completed") { + console.log(`\nAuthorization required for ${toolName}`); + console.log(`Please visit: ${authResult.url}\n`); + await client.auth.waitForCompletion(authResult); + console.log("Authorization complete!\n"); + } + + // Execute the tool + const result = await client.tools.execute({ + tool_name: toolName, + input: args as Record, + user_id: userId, + }); + + if (!result.success) { + throw new Error( + `Tool execution failed: ${result.output?.error?.message}` + ); + } + + return result.output?.value; + }; + }, + }); + + // Convert to Google ADK FunctionTool format + return zodTools.map((tool) => + new FunctionTool({ + name: tool.name, + description: tool.description, + parameters: tool.parameters, + execute: tool.execute, + }) + ); +} + +async function main() { + const client = new Arcade(); + const sessionService = new InMemorySessionService(); + + // Get Arcade tools + const arcadeTools = await getArcadeTools(client, ARCADE_USER_ID); + + // Create the agent + const agent = new LlmAgent({ + name: APP_NAME, + description: "An assistant that helps with Gmail and Slack", + model: MODEL, + instruction: SYSTEM_PROMPT, + tools: arcadeTools, + }); + + // Create a session + const session = await sessionService.createSession({ + appName: APP_NAME, + userId: ARCADE_USER_ID, + state: { user_id: ARCADE_USER_ID }, + }); + + // Create the runner + const runner = new Runner({ + appName: APP_NAME, + agent, + sessionService, + }); + + // Set up interactive chat + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + console.log("Hello! I'm your Google ADK Agent with Arcade Tools."); + console.log("Try asking me to summarize your recent emails or send a Slack message!"); + console.log("Type 'exit' to quit.\n"); + + while (true) { + const input = await rl.question("> "); + if (input.toLowerCase() === "exit") { + break; + } + + try { + const events = runner.runAsync({ + userId: ARCADE_USER_ID, + sessionId: session.id, + newMessage: { role: "user", parts: [{ text: input }] }, + }); + + for await (const event of events) { + if (event.content?.parts?.[0]?.text) { + console.log(`\n${event.author}: ${event.content.parts[0].text}\n`); + } + } + } catch (error) { + console.error("Error:", error); + } + } + + console.log("Goodbye!"); + rl.close(); + process.exit(0); +} + +main().catch(console.error); +``` + +## Next steps + +- Add more by modifying `MCP_SERVERS` and `INDIVIDUAL_TOOLS` +- Build multi- systems with different Arcade +- Explore [creating custom tools](/guides/create-tools/tool-basics/build-mcp-server.md) + with the Arcade SDK + +Last updated on February 10, 2026 + +[Setup (Python)](/en/get-started/agent-frameworks/google-adk/setup-python.md) +[Overview](/en/get-started/agent-frameworks/langchain/overview.md) diff --git a/public/_markdown/en/get-started/agent-frameworks/langchain/auth-langchain-tools.md b/public/_markdown/en/get-started/agent-frameworks/langchain/auth-langchain-tools.md index 712132156..8a82d575d 100644 --- a/public/_markdown/en/get-started/agent-frameworks/langchain/auth-langchain-tools.md +++ b/public/_markdown/en/get-started/agent-frameworks/langchain/auth-langchain-tools.md @@ -3,8 +3,8 @@ title: "Authorize Existing Tools" description: "Use Arcade to authorize existing tools" --- [Agent Frameworks](/en/get-started/agent-frameworks.md) -[LangChain](/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py.md) -Authorizing existing tools +[LangChain](/en/get-started/agent-frameworks/langchain/overview.md) +Authorizing Existing Tools ## Authorize Existing Tools @@ -215,5 +215,5 @@ Now you’re ready to explore more LangChain tools with Arcade. Try integrating Last updated on February 11, 2026 -[Setup Arcade with LangChain (TypeScript)](/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts.md) +[Setup (TypeScript)](/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts.md) [Mastra](/en/get-started/agent-frameworks/mastra.md) diff --git a/public/_markdown/en/get-started/agent-frameworks/langchain/overview.md b/public/_markdown/en/get-started/agent-frameworks/langchain/overview.md new file mode 100644 index 000000000..b335cdd13 --- /dev/null +++ b/public/_markdown/en/get-started/agent-frameworks/langchain/overview.md @@ -0,0 +1,41 @@ +--- +title: "Arcade with LangChain" +description: "Integrate Arcade tools with LangChain agents" +--- +[Agent Frameworks](/en/get-started/agent-frameworks.md) +LangChainOverview + +# Arcade with LangChain + +[LangChain](https://www.langchain.com/)  is a popular framework for building AI agents that abstracts much of the complexity of development. Arcade integrates with both the Python and JavaScript versions, giving your agents access to Gmail, GitHub, Slack, and 100+ other . + +## Get started + +Choose your language to set up Arcade with LangChain: + +- **[Python setup](/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py.md) + ** - Build an with Arcade using `langchain-arcade` +- **[TypeScript setup](/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts.md) + ** - Build an with Arcade using `@arcadeai/arcadejs` + +## What you can build + +With Arcade and LangChain, your can: + +- Read and send emails via Gmail +- Post messages to Slack channels +- Create GitHub issues and pull requests +- Search the web and extract content +- Access 100+ other integrations + +Browse the [full MCP server catalog](/resources/integrations.md) to see all available . + +## Advanced topics + +- **[Authorizing existing tools](/get-started/agent-frameworks/langchain/auth-langchain-tools.md) + ** - Add Arcade authorization to your existing LangChain + +Last updated on February 10, 2026 + +[Setup (TypeScript)](/en/get-started/agent-frameworks/google-adk/setup-typescript.md) +[Setup (Python)](/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py.md) diff --git a/public/_markdown/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py.md b/public/_markdown/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py.md index dcb305dc5..d92acb71c 100644 --- a/public/_markdown/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py.md +++ b/public/_markdown/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py.md @@ -3,7 +3,8 @@ title: "Setup Arcade with LangChain" description: "Learn how to use Arcade tools in LangChain agents" --- [Agent Frameworks](/en/get-started/agent-frameworks.md) -LangChainSetup Arcade with LangChain (Python) +[LangChain](/en/get-started/agent-frameworks/langchain/overview.md) +Setup (Python) # Setup Arcade with LangChain @@ -858,5 +859,5 @@ if __name__ == "__main__": Last updated on February 11, 2026 -[Setup Arcade with Google ADK (Python)](/en/get-started/agent-frameworks/google-adk/use-arcade-tools.md) -[Setup Arcade with LangChain (TypeScript)](/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts.md) +[Overview](/en/get-started/agent-frameworks/langchain/overview.md) +[Setup (TypeScript)](/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts.md) diff --git a/public/_markdown/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts.md b/public/_markdown/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts.md index f5360d81e..e6abb4d88 100644 --- a/public/_markdown/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts.md +++ b/public/_markdown/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts.md @@ -3,8 +3,8 @@ title: "Setup Arcade with LangChain" description: "Learn how to use Arcade tools in LangChain agents" --- [Agent Frameworks](/en/get-started/agent-frameworks.md) -[LangChain](/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py.md) -Setup Arcade with LangChain (TypeScript) +[LangChain](/en/get-started/agent-frameworks/langchain/overview.md) +Setup (TypeScript) # Setup Arcade with LangChain @@ -746,5 +746,5 @@ main().catch((err) => console.error(err)); Last updated on February 11, 2026 -[Setup Arcade with LangChain (Python)](/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py.md) -[Authorizing existing tools](/en/get-started/agent-frameworks/langchain/auth-langchain-tools.md) +[Setup (Python)](/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py.md) +[Authorizing Existing Tools](/en/get-started/agent-frameworks/langchain/auth-langchain-tools.md) diff --git a/public/_markdown/en/get-started/agent-frameworks/mastra.md b/public/_markdown/en/get-started/agent-frameworks/mastra.md index c552eef51..a08e5c4d9 100644 --- a/public/_markdown/en/get-started/agent-frameworks/mastra.md +++ b/public/_markdown/en/get-started/agent-frameworks/mastra.md @@ -962,5 +962,5 @@ export { emailDigestWorkflow }; Last updated on February 11, 2026 -[Authorizing existing tools](/en/get-started/agent-frameworks/langchain/auth-langchain-tools.md) +[Authorizing Existing Tools](/en/get-started/agent-frameworks/langchain/auth-langchain-tools.md) [Overview](/en/get-started/agent-frameworks/openai-agents/overview.md) diff --git a/public/llms.txt b/public/llms.txt index 8145d4091..9273bc548 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -1,4 +1,4 @@ - + # Arcade @@ -74,6 +74,7 @@ Arcade delivers three core capabilities: Deploy agents even your security team w - [Arcade Glossary](https://docs.arcade.dev/en/resources/glossary.md): The Arcade Glossary documentation provides definitions and explanations of key terms and concepts related to the Arcade platform, including agents, tools, and MCP servers. It aims to help users understand the components necessary for building, testing, and deploying applications that utilize large language - [Arcade with Agent Frameworks and MCP Clients](https://docs.arcade.dev/en/get-started/agent-frameworks.md): This documentation page provides developers with guidance on integrating Arcade with various agent frameworks and MCP clients to enhance their AI applications. It includes authentication procedures, tool loading, and execution instructions, along with code examples and configuration steps for quick implementation. Users can explore specific - [Arcade with Google ADK](https://docs.arcade.dev/en/get-started/agent-frameworks/google-adk/overview.md): This documentation page provides a comprehensive guide for integrating the `google-adk-arcade` package with the Google ADK library, enabling users to enhance their AI agents with various Arcade tools such as Google Mail and GitHub. It covers installation, key +- [Arcade with LangChain](https://docs.arcade.dev/en/get-started/agent-frameworks/langchain/overview.md): The "Arcade with LangChain" documentation page provides users with guidance on integrating Arcade tools with LangChain agents, enabling them to leverage various services like Gmail, GitHub, and Slack. It offers setup instructions for both Python and TypeScript, along - [Arcade with OpenAI Agents](https://docs.arcade.dev/en/get-started/agent-frameworks/openai-agents/overview.md): This documentation page provides a comprehensive guide for integrating Arcade with the OpenAI Agents library, enabling users to enhance their AI agents with various tools like Gmail, LinkedIn, and GitHub. It covers installation, key features, basic usage examples, and handling - [ArcadeEngineApi](https://docs.arcade.dev/en/resources/integrations/development/arcade-engine-api.md): The ArcadeEngineApi documentation provides users with a comprehensive guide to the tools available for interacting with the Arcade Engine API, focusing on managing authentication providers, secrets, and worker configurations. It outlines various actions users and LLMs can perform, such as retrieving - [Asana](https://docs.arcade.dev/en/resources/integrations/productivity/asana.md): This documentation page provides users with a comprehensive guide to the Arcade Asana MCP Server, enabling them to build agents and AI applications that interact with Asana tasks, projects, and workspaces. Users can learn how to manage teams, create and update tasks @@ -214,6 +215,8 @@ Arcade delivers three core capabilities: Deploy agents even your security team w - [Security Research Program](https://docs.arcade.dev/en/guides/security/security-research-program.md): The Security Research Program documentation page outlines how users can report security vulnerabilities in Arcade's tools and services, emphasizing the importance of community involvement in enhancing security. It details the types of vulnerabilities sought, the reporting process, and guidelines for responsible disclosure. Additionally, - [Server-Level vs Tool-Level Authorization](https://docs.arcade.dev/en/learn/server-level-vs-tool-level-auth.md): This documentation page explains the differences between server-level authorization (Resource Server auth) and tool-level authorization in Arcade MCP servers, highlighting their roles in securing access to the server and third-party APIs. It provides guidance on when to implement each type of authorization, - [Set your API key](https://docs.arcade.dev/en/get-started/agent-frameworks/openai-agents/user-auth-interrupts.md): This documentation page provides a comprehensive guide on managing user authorization for Arcade tools within OpenAI Agents applications. It outlines the steps to obtain an API key, configure the environment, handle authorization errors, and implement a complete authorization flow. Users will learn how to +- [Setup Arcade with Google ADK (Python)](https://docs.arcade.dev/en/get-started/agent-frameworks/google-adk/setup-python.md): This documentation page guides users through the process of setting up the Arcade framework with Google ADK using Python, enabling the creation of AI agents that leverage Arcade tools. Users will learn how to integrate Arcade functionalities, manage tool authorization, and build an agent that +- [Setup Arcade with Google ADK (TypeScript)](https://docs.arcade.dev/en/get-started/agent-frameworks/google-adk/setup-typescript.md): This documentation page provides a comprehensive guide for setting up an agent using Arcade tools with the Google ADK in TypeScript. Users will learn how to integrate Arcade's library with Google ADK, handle authorization, and build agents that can interact with various services - [Setup Arcade with LangChain](https://docs.arcade.dev/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-py.md): This documentation page provides a comprehensive guide on integrating Arcade tools with LangChain, an agentic framework for building AI agents. Users will learn how to transform Arcade tools into LangChain components, manage authorization, and configure their agents effectively. By following the instructions - [Setup Arcade with LangChain](https://docs.arcade.dev/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts.md): This documentation page provides a comprehensive guide on integrating Arcade tools with LangChain, an agentic framework for building AI agents. Users will learn how to transform Arcade tools into LangChain components, manage authorization through interrupts, and configure their agent setup effectively. By - [Setup Arcade with LangChain](https://docs.arcade.dev/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain.md): This documentation page guides users on how to integrate Arcade tools into LangChain agents, enabling them to leverage Arcade's capabilities within the LangChain framework. Users will learn to set up their environment, create a LangChain agent, and manage tool authorization and execution diff --git a/scripts/generate-clean-markdown.ts b/scripts/generate-clean-markdown.ts index e3d888334..6969a2ddf 100644 --- a/scripts/generate-clean-markdown.ts +++ b/scripts/generate-clean-markdown.ts @@ -1,4 +1,4 @@ -import { type ChildProcess, spawn } from "node:child_process"; +import { type ChildProcess, execSync, spawn } from "node:child_process"; import fs from "node:fs/promises"; import path, { dirname } from "node:path"; import { fileURLToPath } from "node:url"; @@ -58,6 +58,99 @@ const META_DESCRIPTION_ALT_PATTERN = / line.length > 0); + } catch (error) { + console.warn( + pc.yellow(`⚠ Could not get git diff, falling back to full rebuild: ${error}`) + ); + return []; + } +} + +/** + * Gets the list of deleted MDX files from git diff + */ +function getDeletedMdxFiles(): string[] { + try { + const output = execSync( + `git diff ${GIT_DIFF_BASE} --name-only --diff-filter=D -- "app/**/*.mdx"`, + { encoding: "utf-8", cwd: path.join(__dirname, "..") } + ); + return output + .trim() + .split("\n") + .filter((line) => line.length > 0); + } catch (error) { + console.warn(pc.yellow(`⚠ Could not get deleted files from git: ${error}`)); + return []; + } +} + +/** + * Converts an MDX file path to its corresponding markdown output path + * e.g., "app/en/home/quickstart/page.mdx" -> "public/_markdown/en/home/quickstart.md" + */ +function mdxPathToOutputPath(mdxPath: string): string { + // Remove "app/" prefix + let relativePath = mdxPath.replace(/^app\//, ""); + // Remove "/page.mdx" or ".mdx" suffix and add .md + relativePath = relativePath + .replace(/\/page\.mdx$/, ".md") + .replace(/\.mdx$/, ".md"); + return path.join(OUTPUT_DIR, relativePath); +} + +/** + * Converts an MDX file path to its route + * e.g., "app/en/home/quickstart/page.mdx" -> "/en/home/quickstart" + */ +function mdxPathToRoute(mdxPath: string): string { + // Remove "app/" prefix + let route = mdxPath.replace(/^app\//, ""); + // Remove "/page.mdx" or ".mdx" suffix + route = route.replace(/\/page\.mdx$/, "").replace(/\.mdx$/, ""); + return `/${route}`; +} + +/** + * Deletes markdown files for deleted MDX sources + */ +async function deleteMarkdownForDeletedMdx( + deletedMdxFiles: string[] +): Promise<{ deleted: number; paths: string[] }> { + const deletedPaths: string[] = []; + + for (const mdxPath of deletedMdxFiles) { + const outputPath = mdxPathToOutputPath(mdxPath); + try { + await fs.unlink(outputPath); + deletedPaths.push(outputPath); + } catch (error) { + // File may not exist, that's ok + } + } + + return { deleted: deletedPaths.length, paths: deletedPaths }; +} + // Initialize Turndown with options for clean markdown const turndown = new TurndownService({ headingStyle: "atx", @@ -948,33 +1041,106 @@ async function main() { let server: ChildProcess | null = null; try { - const pages = await discoverPages(); - console.log(pc.green(`✓ Found ${pages.length} pages to process`)); + // Determine which pages to process + let pagesToProcess: Array<{ + route: string; + language: string; + outputPath: string; + }>; + let deletedCount = 0; + let deletedPaths: string[] = []; + + if (FULL_REBUILD) { + console.log(pc.blue("📦 Full rebuild mode enabled\n")); + pagesToProcess = await discoverPages(); + console.log(pc.green(`✓ Found ${pagesToProcess.length} pages to process`)); + } else { + console.log(pc.blue(`📊 Diff-based mode (comparing to ${GIT_DIFF_BASE})\n`)); + + // Get changed and deleted MDX files + const changedMdxFiles = getChangedMdxFiles(); + const deletedMdxFiles = getDeletedMdxFiles(); + + console.log( + pc.gray(` Changed/added MDX files: ${changedMdxFiles.length}`) + ); + console.log(pc.gray(` Deleted MDX files: ${deletedMdxFiles.length}`)); + + // Delete markdown files for deleted MDX sources + if (deletedMdxFiles.length > 0) { + const result = await deleteMarkdownForDeletedMdx(deletedMdxFiles); + deletedCount = result.deleted; + deletedPaths = result.paths; + } + + // If no changes, we're done + if (changedMdxFiles.length === 0) { + console.log(pc.green("\n✓ No MDX changes detected, nothing to generate")); + if (deletedCount > 0) { + console.log(pc.yellow(` 🗑️ Deleted ${deletedCount} markdown files`)); + for (const deletedPath of deletedPaths) { + console.log(pc.gray(` - ${path.relative(OUTPUT_DIR, deletedPath)}`)); + } + } + console.log(pc.bold(pc.green("\n✨ Done!\n"))); + process.exit(0); + } + + // Filter to only changed pages (skip dynamic routes) + pagesToProcess = changedMdxFiles + .filter((mdxPath) => !mdxPath.includes("[")) + .map((mdxPath) => { + const route = mdxPathToRoute(mdxPath); + const language = mdxPath.split("/")[1] || "en"; + const outputPath = mdxPathToOutputPath(mdxPath); + return { route, language, outputPath }; + }); + + console.log( + pc.green(`\n✓ ${pagesToProcess.length} pages to regenerate`) + ); + } + + // Skip server startup if no pages to process + if (pagesToProcess.length === 0) { + console.log(pc.green("\n✓ No pages to process")); + console.log(pc.bold(pc.green("\n✨ Done!\n"))); + process.exit(0); + } server = startServer(); await waitForServer(`${SERVER_URL}/en/home`); console.log(pc.blue("\n📝 Converting pages to markdown...\n")); - const { successCount, errorCount } = await processAllPages(pages); + const { successCount, errorCount } = await processAllPages(pagesToProcess); console.log(pc.bold(pc.blue("\n📊 Results:"))); console.log(pc.green(` ✓ Successfully converted: ${successCount}`)); if (errorCount > 0) { console.log(pc.red(` ✗ Errors: ${errorCount}`)); } + if (deletedCount > 0) { + console.log(pc.yellow(` 🗑️ Deleted markdown files: ${deletedCount}`)); + for (const deletedPath of deletedPaths) { + console.log(pc.gray(` - ${path.relative(OUTPUT_DIR, deletedPath)}`)); + } + } console.log(pc.gray(` 📁 Output directory: ${OUTPUT_DIR}`)); - const validation = await validateGeneratedContent(); - if (!validation.passed) { - console.log(pc.bold(pc.red("\n⚠️ Validation errors:"))); - for (const error of validation.errors) { - console.log(pc.red(` • ${error}`)); + // Only run validation in full rebuild mode + if (FULL_REBUILD) { + const validation = await validateGeneratedContent(); + if (!validation.passed) { + console.log(pc.bold(pc.red("\n⚠️ Validation errors:"))); + for (const error of validation.errors) { + console.log(pc.red(` • ${error}`)); + } + console.log( + pc.yellow( + "\nNote: Some validation failures may indicate the HTML extraction needs adjustment." + ) + ); } - console.log( - pc.yellow( - "\nNote: Some validation failures may indicate the HTML extraction needs adjustment." - ) - ); } console.log(pc.bold(pc.green("\n✨ Done!\n"))); From 8e6325feff0d74858dd56cef81c6114611699b08 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 12 Feb 2026 19:34:08 +0000 Subject: [PATCH 04/15] Regenerate clean markdown files --- public/_markdown/en/references/auth-providers/figma.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/_markdown/en/references/auth-providers/figma.md b/public/_markdown/en/references/auth-providers/figma.md index a701df83e..148ccd8ba 100644 --- a/public/_markdown/en/references/auth-providers/figma.md +++ b/public/_markdown/en/references/auth-providers/figma.md @@ -283,7 +283,7 @@ async def get_figma_file( For a complete list of available Figma OAuth scopes and their descriptions, refer to the [Figma OAuth Scopes documentation](https://developers.figma.com/docs/rest-api/scopes/) . -Last updated on February 11, 2026 +Last updated on February 10, 2026 [Dropbox](/en/references/auth-providers/dropbox.md) [GitHub](/en/references/auth-providers/github.md) From 8ed38c06390bbc78d6a0ed9c12704dede4765d0f Mon Sep 17 00:00:00 2001 From: jottakka Date: Thu, 12 Feb 2026 16:48:30 -0300 Subject: [PATCH 05/15] removing pre commit stash+format --- .husky/pre-commit | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 76674e5f5..fc3ad8830 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -2,16 +2,6 @@ # Exit on any error set -e -# Detect merge/rebase state — used later to skip the stash+format block -# which conflicts with merge state and corrupts it. -GIT_DIR="$(git rev-parse --git-dir)" -IS_MERGING=false -if [ -f "$GIT_DIR/MERGE_HEAD" ] || \ - [ -d "$GIT_DIR/rebase-merge" ] || \ - [ -d "$GIT_DIR/rebase-apply" ]; then - IS_MERGING=true -fi - # Check if there are any staged files if [ -z "$(git diff --cached --name-only)" ]; then echo "No staged files to check" @@ -134,15 +124,6 @@ fi # --- Lint Staged (formatting) --- pnpm dlx lint-staged -# --- Stash + Format --- -# Skip this block during merge/rebase: git stash --keep-index destroys -# MERGE_HEAD and corrupts the merge state, causing repeated failures. -# lint-staged (above) already handles formatting for staged files safely. -if [ "$IS_MERGING" = true ]; then - echo "⏭️ Skipping stash+format (merge/rebase in progress)" - exit 0 -fi - # Store the hash of staged changes to detect modifications STAGED_HASH=$(git diff --cached | sha256sum | cut -d' ' -f1) From fa9da7a5654bb735cfdf3993a6c3f91a690fe069 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 13 Feb 2026 16:31:10 +0000 Subject: [PATCH 06/15] Regenerate clean markdown files --- .../en/references/auth-providers/figma.md | 103 +++++++++--------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/public/_markdown/en/references/auth-providers/figma.md b/public/_markdown/en/references/auth-providers/figma.md index e606baaa8..148ccd8ba 100644 --- a/public/_markdown/en/references/auth-providers/figma.md +++ b/public/_markdown/en/references/auth-providers/figma.md @@ -2,50 +2,49 @@ title: "Figma" description: "Arcade - AI platform for developers" --- - [Auth Providers](/en/references/auth-providers.md) Figma # Figma -The Figma enables tools and to call [Figma APIs](https://developers.figma.com/docs/rest-api/)  on behalf of a using OAuth 2.0 authentication. +The Figma enables tools and to call [Figma APIs](https://developers.figma.com/docs/rest-api/)  on behalf of a using OAuth 2.0 authentication. -Want to quickly get started with Figma in your or AI app? The pre-built [Arcade Figma MCP Server](/resources/integrations/development/figma.md) is what you want! +Want to quickly get started with Figma in your or AI app? The pre-built [Arcade Figma MCP Server](/resources/integrations/development/figma.md) is what you want! ### What’s documented here This page describes how to use and configure Figma auth with Arcade. -This is used by: +This is used by: -- The [Arcade Figma MCP Server](/resources/integrations/development/figma.md) - , which provides pre-built for interacting with Figma -- Your [app code](#using-figma-auth-in-app-code) - that needs to call the Figma API -- Or, your [custom tools](#using-figma-auth-in-custom-tools) - that need to call the Figma API +- The [Arcade Figma MCP Server](/resources/integrations/development/figma.md) + , which provides pre-built for interacting with Figma +- Your [app code](#using-figma-auth-in-app-code) + that needs to call the Figma API +- Or, your [custom tools](#using-figma-auth-in-custom-tools) + that need to call the Figma API ### Required scopes for the Figma MCP Server -If you’re using the [Arcade Figma MCP Server](/resources/integrations/development/figma.md), you’ll need to configure these scopes based on which you plan to use: +If you’re using the [Arcade Figma MCP Server](/resources/integrations/development/figma.md), you’ll need to configure these scopes based on which you plan to use: -- `file_content:read` - File structure, pages, nodes, and image exports -- `library_content:read` - Published components, styles, and component sets from files -- `team_library_content:read` - Team library content -- `library_assets:read` - Individual component, style, and component set metadata -- `file_comments:read` / `file_comments:write` - Reading and creating comments -- `current_user:read` - profile information -- `projects:read` - Team and files (**requires private OAuth app**) +- `file_content:read` - File structure, pages, nodes, and image exports +- `library_content:read` - Published components, styles, and component sets from files +- `team_library_content:read` - Team library content +- `library_assets:read` - Individual component, style, and component set metadata +- `file_comments:read` / `file_comments:write` - Reading and creating comments +- `current_user:read` - profile information +- `projects:read` - Team and files (**requires private OAuth app**) -The `projects:read` scope is **only available in private Figma OAuth apps**. If you need to access team and files, you must create a private OAuth app through your Figma organization. +The `projects:read` scope is **only available in private Figma OAuth apps**. If you need to access team and files, you must create a private OAuth app through your Figma organization. For detailed descriptions of all available Figma OAuth scopes, refer to the [Figma OAuth Scopes documentation](https://developers.figma.com/docs/rest-api/scopes/) . ## Configuring Figma auth -When using your own app credentials, make sure you configure your to use a [custom user verifier](/guides/user-facing-agents/secure-auth-production.md#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or in production. +When using your own app credentials, make sure you configure your to use a [custom user verifier](/guides/user-facing-agents/secure-auth-production.md#build-a-custom-user-verifier). Without this, your end-users will not be able to use your app or in production. -In a production environment, you will most likely want to use your own Figma app credentials. This way, your will see your application’s name requesting permission. +In a production environment, you will most likely want to use your own Figma app credentials. This way, your will see your application’s name requesting permission. Before showing how to configure your Figma app credentials, let’s go through the steps to create a Figma app. @@ -62,23 +61,23 @@ Navigate to the [Figma Developer Portal](https://www.figma.com/developers/)  an 1. Once logged in, go to your developer dashboard and select “My Apps” 2. Click on “Create a new app” 3. Fill in the required details: - - **App Name**: Choose a descriptive name for your application - - **Website**: Provide the URL of your application’s website - - **App Logo**: Upload a 100x100px PNG image representing your app + - **App Name**: Choose a descriptive name for your application + - **Website**: Provide the URL of your application’s website + - **App Logo**: Upload a 100x100px PNG image representing your app #### Set up OAuth configuration 1. After creating your app, you’ll receive a `client_id` and `client_secret` 2. Set the redirect URI to the redirect URL generated by Arcade (see configuration section below) -3. Configure the required scopes for your application based on the you need: - - `file_content:read` - Read access to file content and structure - - `library_content:read` - Read access to published library content - - `team_library_content:read` - Read access to team library content - - `library_assets:read` - Read access to individual library assets - - `file_comments:read` - Read access to file comments - - `file_comments:write` - Write access to file comments - - `current_user:read` - Read access to profile - - `projects:read` - Read access to team (private apps only) +3. Configure the required scopes for your application based on the you need: + - `file_content:read` - Read access to file content and structure + - `library_content:read` - Read access to published library content + - `team_library_content:read` - Read access to team library content + - `library_assets:read` - Read access to individual library assets + - `file_comments:read` - Read access to file comments + - `file_comments:write` - Write access to file comments + - `current_user:read` - Read access to profile + - `projects:read` - Read access to team (private apps only) For a complete list of available scopes, refer to the [Figma OAuth Scopes documentation](https://developers.figma.com/docs/rest-api/scopes/)  @@ -98,21 +97,21 @@ To access the Arcade Cloud dashboard, go to [api.arcade.dev/dashboard](https://a #### Navigate to the OAuth Providers page -- Under the **Connections** section of the Arcade Dashboard left-side menu, click **Connected Apps**. -- Click **Add OAuth Provider** in the top right corner. -- Select the **OAuth 2.0** tab at the top. +- Under the **Connections** section of the Arcade Dashboard left-side menu, click **Connected Apps**. +- Click **Add OAuth Provider** in the top right corner. +- Select the **OAuth 2.0** tab at the top. #### Enter the provider details -- Choose a unique **ID** for your provider (e.g. “figma”). -- Optionally enter a **Description**. -- Enter the **Client ID** and **Client Secret** from your Figma app. -- Configure the OAuth 2.0 endpoints: - - **Authorization URL**: `https://www.figma.com/oauth` - - **Token URL**: `https://api.figma.com/v1/oauth/token` - - **Scope Delimiter**: (space) - - **Use PKCE**: Enabled (S256) -- Note the **Redirect URL** generated by Arcade. This must be set as your Figma app’s redirect URI. +- Choose a unique **ID** for your provider (e.g. “figma”). +- Optionally enter a **Description**. +- Enter the **Client ID** and **Client Secret** from your Figma app. +- Configure the OAuth 2.0 endpoints: + - **Authorization URL**: `https://www.figma.com/oauth` + - **Token URL**: `https://api.figma.com/v1/oauth/token` + - **Scope Delimiter**: (space) + - **Use PKCE**: Enabled (S256) +- Note the **Redirect URL** generated by Arcade. This must be set as your Figma app’s redirect URI. #### Create the provider @@ -183,15 +182,15 @@ auth: response_content_type: application/json ``` -**Note on `projects:read` scope:** If you need access to the `projects:read` scope for team and files navigation, you must create a **private Figma OAuth app**. This scope is not available in public OAuth apps. Learn more in the [Figma OAuth Scopes documentation](https://developers.figma.com/docs/rest-api/scopes/) . +**Note on `projects:read` scope:** If you need access to the `projects:read` scope for team and files navigation, you must create a **private Figma OAuth app**. This scope is not available in public OAuth apps. Learn more in the [Figma OAuth Scopes documentation](https://developers.figma.com/docs/rest-api/scopes/) . -When you use tools that require Figma auth using your Arcade credentials, Arcade will automatically use this Figma OAuth provider. If you have multiple Figma providers, see [using multiple auth providers of the same type](/references/auth-providers.md#using-multiple-providers-of-the-same-type) for more information. +When you use tools that require Figma auth using your Arcade credentials, Arcade will automatically use this Figma OAuth provider. If you have multiple Figma providers, see [using multiple auth providers of the same type](/references/auth-providers.md#using-multiple-providers-of-the-same-type) for more information. ## Using Figma auth in app code -Use the Figma in your own and AI apps to get a token for the Figma API. See [authorizing agents with Arcade](/get-started/about-arcade.md) to understand how this works. +Use the Figma in your own and AI apps to get a token for the Figma API. See [authorizing agents with Arcade](/get-started/about-arcade.md) to understand how this works. -Use `client.auth.start()` to get a token for the Figma API: +Use `client.auth.start()` to get a token for the Figma API: ### Python @@ -249,11 +248,11 @@ const token = authResponse.context.token; ## Using Figma auth in custom tools -You can use the pre-built [Arcade Figma MCP Server](/resources/integrations/development/figma.md) to quickly build and AI apps that interact with Figma. +You can use the pre-built [Arcade Figma MCP Server](/resources/integrations/development/figma.md) to quickly build and AI apps that interact with Figma. -If the pre-built tools in the Figma Server don’t meet your needs, you can author your own [custom tools](/guides/create-tools/tool-basics/build-mcp-server.md) that interact with the Figma API. +If the pre-built tools in the Figma Server don’t meet your needs, you can author your own [custom tools](/guides/create-tools/tool-basics/build-mcp-server.md) that interact with the Figma API. -Use the `Figma()` auth class to specify that a requires authorization with Figma. The `context.authorization.token` field will be automatically populated with the ’s Figma token: +Use the `Figma()` auth class to specify that a requires authorization with Figma. The `context.authorization.token` field will be automatically populated with the ’s Figma token: ```python from typing import Annotated From adc0afd17051a1cbb21d231b406a545a21ecc918 Mon Sep 17 00:00:00 2001 From: "RL \"Nearest\" Nabors" <236306+nearestnabors@users.noreply.github.com> Date: Fri, 13 Feb 2026 17:19:03 +0000 Subject: [PATCH 07/15] fixing the links in the framework overview (#772) fixing the links in the framework overvieww to link to the appropriate pages Co-authored-by: Rachel Lee Nabors --- app/_components/agent-framework-tabs.tsx | 40 ++++++++++++------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/app/_components/agent-framework-tabs.tsx b/app/_components/agent-framework-tabs.tsx index a26eec9d0..dd022c53c 100644 --- a/app/_components/agent-framework-tabs.tsx +++ b/app/_components/agent-framework-tabs.tsx @@ -7,35 +7,35 @@ export function AgentFrameworkTabs() {
@@ -44,27 +44,27 @@ export function AgentFrameworkTabs() { From e3d0f00a3de3fc015062c2035d3af05007b9ec74 Mon Sep 17 00:00:00 2001 From: Evan Tahler Date: Fri, 13 Feb 2026 09:29:36 -0800 Subject: [PATCH 08/15] Add CLAUDE.md for Claude Code onboarding (#769) * Add CLAUDE.md for Claude Code onboarding Provides project overview, build commands, architecture map, content authoring guidelines, and key config file references so Claude Code can assist effectively in this repo. Co-Authored-By: Claude Opus 4.6 * Update CLAUDE.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Address PR review feedback on CLAUDE.md - Rename heading from redundant "CLAUDE.md" to "Arcade Docs" - Remove nonexistent app/es/ and app/pt-BR/ directories - Replace hardcoded framework/Node versions with references to source files - Add instruction to never bypass pre-commit hooks - Add Code Quality section forbidding lint/TS suppression comments - Remove LLM-Aware Features section (not useful for Claude Code) - Add missing config files (tsconfig, components.json, postcss) Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- CLAUDE.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..b09bc8fbe --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,59 @@ +# Arcade Docs + +Arcade documentation site built with Next.js + Nextra (App Router), using pnpm as the package manager. Content is authored in MDX with custom React components. Check `package.json` for current framework versions. + +## Commands + +```bash +pnpm dev # Local dev server (port 3000) +pnpm build # Full production build (toolkit-markdown → next build → pagefind) +pnpm lint # Lint with Ultracite (Biome-based) +pnpm format # Auto-format with Ultracite +pnpm test # Run all Vitest tests +pnpm test:watch # Watch mode +pnpm vale:check # Check docs against style rules +``` + +Run a single test: +```bash +pnpm vitest run tests/broken-link-check.test.ts +``` + +## Architecture + +- **`app/en/`** — English docs content (MDX pages). Nextra file-based routing with `_meta.tsx` for navigation order. i18n handled via `middleware.ts`. +- **`app/_components/`** — Shared React components (tabbed code blocks, toolkit docs, callouts, etc.). +- **`app/_lib/`** — Data-fetching utilities (toolkit catalog, slug generation, static params). +- **`app/api/`** — API routes (markdown export, toolkit-data, glossary). +- **`toolkit-docs-generator/`** — Generates MCP toolkit documentation from server metadata JSON files in `toolkit-docs-generator/data/toolkits/`. +- **`scripts/`** — Build/CI scripts (clean markdown export, Vale style fixes, redirect checking, pagefind indexing, i18n sync). +- **`tests/`** — Vitest tests (broken links, internal link validation, sitemap, smoke tests). +- **`lib/`** — Next.js utilities (glossary remark plugin, llmstxt plugin). +- **`next.config.ts`** — Contains ~138 redirect rules. + +## Content Authoring + +Follow **STYLEGUIDE.md** for writing standards and **AUTHORING.md** for formatting conventions. Key points: + +- Sentence case for headings. Active voice. Direct tone. +- Product is always "Arcade" (never abbreviated, never "Arcade AI"). +- Use "Arcade Engine" (capitalized), "MCP server" (lowercase server), "tool" (lowercase), "auth provider". +- Code snippets: 4 spaces for Python, 2 spaces for other languages. +- Run `pnpm vale:check` before submitting docs changes. + +## Pre-commit Hooks + +Husky runs on commit: Vale style checks on `.md/.mdx`, `_meta.tsx` key validation, redirect checking for deleted/renamed pages, internal link updates, and Ultracite formatting. You MUST fix any issues surfaced by the pre-commit hooks. NEVER bypass hooks with `--no-verify` or similar flags. + +## Key Config Files + +- `biome.jsonc` — Linter/formatter rules (Biome via Ultracite) +- `.vale.ini` — Vale style checker config (Google style base + Arcade vocabulary) +- `.nvmrc` — Required Node version +- `tsconfig.json` — TypeScript compiler configuration +- `components.json` — shadcn/ui component config +- `postcss.config.mjs` — PostCSS/Tailwind config + +## Code Quality + +NEVER add suppression comments (`// @ts-ignore`, `// @ts-expect-error`, `// biome-ignore`, `eslint-disable`, `{/* prettier-ignore */}`, etc.) to bypass TypeScript or linter errors. Fix the underlying issue instead. From 4d46d483090b5b8a44e90d7df13f44c92a63d4a8 Mon Sep 17 00:00:00 2001 From: Teal Larson Date: Fri, 13 Feb 2026 13:13:23 -0500 Subject: [PATCH 09/15] Preferred locale english only (#753) * Force preferred locale to English for now Co-authored-by: Teal Larson * Redirect non-English locale paths to English --------- Co-authored-by: Cursor Agent --- middleware.ts | 48 +++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/middleware.ts b/middleware.ts index 7314a86d4..7cd936828 100644 --- a/middleware.ts +++ b/middleware.ts @@ -58,40 +58,27 @@ function isAIAgent(request: NextRequest): boolean { return AI_AGENT_PATTERNS.some((pattern) => pattern.test(userAgent)); } -function parseAcceptLanguageHeader(acceptLanguage: string): string[] { - return acceptLanguage - .split(",") - .map((lang) => lang.split(";")[0].trim()) - .map((lang) => { - if (lang.startsWith("es")) { - return "es"; - } - if (lang.startsWith("pt")) { - return "pt-BR"; - } - return lang; - }); +function getPreferredLocale(_request: NextRequest): string { + // We don't currently support other translations, so always use English. + return "en"; } -function getPreferredLocale(request: NextRequest): string { - const cookieLocale = request.cookies.get("NEXT_LOCALE")?.value; +function getEnglishLocaleRedirectPath(pathname: string): string | null { + for (const locale of SUPPORTED_LOCALES) { + if (locale === "en") { + continue; + } - if (cookieLocale && SUPPORTED_LOCALES.includes(cookieLocale)) { - return cookieLocale; - } + if (pathname === `/${locale}` || pathname === `/${locale}/`) { + return "/en/home"; + } - const acceptLanguage = request.headers.get("accept-language"); - if (acceptLanguage) { - const languages = parseAcceptLanguageHeader(acceptLanguage); - const preferredLocale = languages.find((lang) => - SUPPORTED_LOCALES.includes(lang) - ); - if (preferredLocale) { - return preferredLocale; + if (pathname.startsWith(`/${locale}/`)) { + return `/en${pathname.slice(locale.length + 1)}`; } } - return "en"; + return null; } function pathnameIsMissingLocale(pathname: string): boolean { @@ -181,6 +168,13 @@ function handleContentNegotiation( export function middleware(request: NextRequest) { const pathname = request.nextUrl.pathname; + const englishLocaleRedirectPath = getEnglishLocaleRedirectPath(pathname); + if (englishLocaleRedirectPath) { + const url = request.nextUrl.clone(); + url.pathname = englishLocaleRedirectPath; + return NextResponse.redirect(url); + } + const contentNegotiationResponse = handleContentNegotiation( request, pathname From 4654042894fd60ea06d173b6a3a9426ba0cc3f0a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 13 Feb 2026 18:53:42 +0000 Subject: [PATCH 10/15] Regenerate clean markdown files --- public/_markdown/en/references/auth-providers/figma.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/_markdown/en/references/auth-providers/figma.md b/public/_markdown/en/references/auth-providers/figma.md index 148ccd8ba..8ee55cedb 100644 --- a/public/_markdown/en/references/auth-providers/figma.md +++ b/public/_markdown/en/references/auth-providers/figma.md @@ -283,7 +283,7 @@ async def get_figma_file( For a complete list of available Figma OAuth scopes and their descriptions, refer to the [Figma OAuth Scopes documentation](https://developers.figma.com/docs/rest-api/scopes/) . -Last updated on February 10, 2026 +Last updated on January 5, 2026 [Dropbox](/en/references/auth-providers/dropbox.md) [GitHub](/en/references/auth-providers/github.md) From dcf43fe4d0809f497afa9cac7bd198d799543a00 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 13 Feb 2026 18:57:52 +0000 Subject: [PATCH 11/15] Regenerate clean markdown files --- public/_markdown/en/references/auth-providers/figma.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/_markdown/en/references/auth-providers/figma.md b/public/_markdown/en/references/auth-providers/figma.md index 8ee55cedb..ba2c0a97a 100644 --- a/public/_markdown/en/references/auth-providers/figma.md +++ b/public/_markdown/en/references/auth-providers/figma.md @@ -283,7 +283,7 @@ async def get_figma_file( For a complete list of available Figma OAuth scopes and their descriptions, refer to the [Figma OAuth Scopes documentation](https://developers.figma.com/docs/rest-api/scopes/) . -Last updated on January 5, 2026 +Last updated on January 30, 2026 [Dropbox](/en/references/auth-providers/dropbox.md) [GitHub](/en/references/auth-providers/github.md) From e30eead59f8e1c52cd00cb08ecc86ffeead16447 Mon Sep 17 00:00:00 2001 From: jottakka Date: Fri, 13 Feb 2026 16:06:24 -0300 Subject: [PATCH 12/15] fix broken test --- tests/external-url-check.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/external-url-check.test.ts b/tests/external-url-check.test.ts index ba468f74d..44c2e4ec6 100644 --- a/tests/external-url-check.test.ts +++ b/tests/external-url-check.test.ts @@ -26,6 +26,7 @@ const SKIP_PATTERNS: RegExp[] = [ /reddit\.com/, /platform\.openai\.com/, /support\.google\.com/, + /developers\.google\.com/, /developer\.squareup\.com/, /developer\.ticktick\.com/, /api-console\.zoho\.com/, From fb283f60c5e79510dd0dcddfad2043d02370f1c2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 13 Feb 2026 19:10:47 +0000 Subject: [PATCH 13/15] Regenerate clean markdown files --- public/_markdown/en/references/auth-providers/figma.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/_markdown/en/references/auth-providers/figma.md b/public/_markdown/en/references/auth-providers/figma.md index ba2c0a97a..148ccd8ba 100644 --- a/public/_markdown/en/references/auth-providers/figma.md +++ b/public/_markdown/en/references/auth-providers/figma.md @@ -283,7 +283,7 @@ async def get_figma_file( For a complete list of available Figma OAuth scopes and their descriptions, refer to the [Figma OAuth Scopes documentation](https://developers.figma.com/docs/rest-api/scopes/) . -Last updated on January 30, 2026 +Last updated on February 10, 2026 [Dropbox](/en/references/auth-providers/dropbox.md) [GitHub](/en/references/auth-providers/github.md) From e7d637cdb6791f7a6c2b6cbf5405bfeda9df394e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 13 Feb 2026 19:15:07 +0000 Subject: [PATCH 14/15] Regenerate clean markdown files --- public/_markdown/en/references/auth-providers/figma.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/_markdown/en/references/auth-providers/figma.md b/public/_markdown/en/references/auth-providers/figma.md index 148ccd8ba..8ee55cedb 100644 --- a/public/_markdown/en/references/auth-providers/figma.md +++ b/public/_markdown/en/references/auth-providers/figma.md @@ -283,7 +283,7 @@ async def get_figma_file( For a complete list of available Figma OAuth scopes and their descriptions, refer to the [Figma OAuth Scopes documentation](https://developers.figma.com/docs/rest-api/scopes/) . -Last updated on February 10, 2026 +Last updated on January 5, 2026 [Dropbox](/en/references/auth-providers/dropbox.md) [GitHub](/en/references/auth-providers/github.md) From 6ccd81a891e11daaf8f2a943e70c8c9444965112 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 13 Feb 2026 19:19:27 +0000 Subject: [PATCH 15/15] Regenerate clean markdown files --- public/_markdown/en/references/auth-providers/figma.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/_markdown/en/references/auth-providers/figma.md b/public/_markdown/en/references/auth-providers/figma.md index 8ee55cedb..148ccd8ba 100644 --- a/public/_markdown/en/references/auth-providers/figma.md +++ b/public/_markdown/en/references/auth-providers/figma.md @@ -283,7 +283,7 @@ async def get_figma_file( For a complete list of available Figma OAuth scopes and their descriptions, refer to the [Figma OAuth Scopes documentation](https://developers.figma.com/docs/rest-api/scopes/) . -Last updated on January 5, 2026 +Last updated on February 10, 2026 [Dropbox](/en/references/auth-providers/dropbox.md) [GitHub](/en/references/auth-providers/github.md)