@jskit-ai/connectors-catalog 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (399) hide show
  1. package/README.md +192 -0
  2. package/docs/ai-upstream-notices.md +51 -0
  3. package/docs/ai.md +191 -0
  4. package/docs/airtable.md +130 -0
  5. package/docs/algolia.md +190 -0
  6. package/docs/amazon-redshift.md +229 -0
  7. package/docs/amplitude.md +210 -0
  8. package/docs/apify.md +162 -0
  9. package/docs/apollo-io.md +200 -0
  10. package/docs/asana.md +126 -0
  11. package/docs/ashby.md +188 -0
  12. package/docs/atlassian.md +224 -0
  13. package/docs/attention.md +162 -0
  14. package/docs/aws-athena.md +150 -0
  15. package/docs/aws-credentials.md +75 -0
  16. package/docs/aws-s3.md +193 -0
  17. package/docs/bigquery.md +146 -0
  18. package/docs/brevo.md +122 -0
  19. package/docs/calendly.md +158 -0
  20. package/docs/canva.md +191 -0
  21. package/docs/chargebee.md +192 -0
  22. package/docs/clay.md +194 -0
  23. package/docs/clickhouse.md +242 -0
  24. package/docs/confidence-exp.md +74 -0
  25. package/docs/confidence-flags.md +75 -0
  26. package/docs/confidence-oauth.md +137 -0
  27. package/docs/contentful.md +123 -0
  28. package/docs/databricks.md +180 -0
  29. package/docs/dbt-semantic-layer.md +175 -0
  30. package/docs/elevenlabs.md +102 -0
  31. package/docs/figma.md +132 -0
  32. package/docs/firebase-cloud-messaging.md +262 -0
  33. package/docs/firecrawl.md +137 -0
  34. package/docs/fireflies.md +95 -0
  35. package/docs/fireworks-ai.md +53 -0
  36. package/docs/gatewayapi.md +113 -0
  37. package/docs/gemini-enterprise.md +224 -0
  38. package/docs/github-api.md +133 -0
  39. package/docs/gitlab-api.md +138 -0
  40. package/docs/gmail.md +133 -0
  41. package/docs/gong.md +205 -0
  42. package/docs/google-ads.md +156 -0
  43. package/docs/google-analytics.md +202 -0
  44. package/docs/google-docs.md +105 -0
  45. package/docs/google-drive.md +108 -0
  46. package/docs/google-maps-platform.md +208 -0
  47. package/docs/google-oauth.md +123 -0
  48. package/docs/google-search-console.md +124 -0
  49. package/docs/google-sheets.md +94 -0
  50. package/docs/google-slides.md +95 -0
  51. package/docs/granola.md +178 -0
  52. package/docs/hex.md +161 -0
  53. package/docs/heygen.md +124 -0
  54. package/docs/hubspot.md +142 -0
  55. package/docs/incident-io.md +118 -0
  56. package/docs/inngest.md +178 -0
  57. package/docs/klipy.md +149 -0
  58. package/docs/lexware.md +144 -0
  59. package/docs/lightspeed.md +193 -0
  60. package/docs/linear.md +137 -0
  61. package/docs/linkedin.md +196 -0
  62. package/docs/logo-dev.md +152 -0
  63. package/docs/mailgun.md +153 -0
  64. package/docs/mapbox.md +174 -0
  65. package/docs/microsoft-excel.md +97 -0
  66. package/docs/microsoft-fabric.md +249 -0
  67. package/docs/microsoft-oauth.md +143 -0
  68. package/docs/microsoft-onedrive.md +76 -0
  69. package/docs/microsoft-onenote.md +75 -0
  70. package/docs/microsoft-outlook.md +90 -0
  71. package/docs/microsoft-powerpoint.md +119 -0
  72. package/docs/microsoft-sharepoint.md +75 -0
  73. package/docs/microsoft-teams.md +72 -0
  74. package/docs/microsoft-word.md +119 -0
  75. package/docs/miro.md +117 -0
  76. package/docs/n8n.md +169 -0
  77. package/docs/notion.md +137 -0
  78. package/docs/oura.md +161 -0
  79. package/docs/paddle.md +168 -0
  80. package/docs/perplexity.md +85 -0
  81. package/docs/pipedrive.md +120 -0
  82. package/docs/polar.md +63 -0
  83. package/docs/posthog.md +227 -0
  84. package/docs/prestashop.md +151 -0
  85. package/docs/replicate.md +60 -0
  86. package/docs/resend.md +153 -0
  87. package/docs/salesforce.md +234 -0
  88. package/docs/sanity.md +122 -0
  89. package/docs/semrush.md +276 -0
  90. package/docs/sentry.md +112 -0
  91. package/docs/sevdesk.md +107 -0
  92. package/docs/shopify.md +206 -0
  93. package/docs/slack.md +257 -0
  94. package/docs/snowflake.md +271 -0
  95. package/docs/storyblok.md +130 -0
  96. package/docs/stripe.md +77 -0
  97. package/docs/supabase.md +51 -0
  98. package/docs/tally.md +105 -0
  99. package/docs/telegram.md +165 -0
  100. package/docs/tiktok.md +201 -0
  101. package/docs/twilio.md +284 -0
  102. package/docs/twitch.md +428 -0
  103. package/docs/wave.md +440 -0
  104. package/docs/wix.md +1249 -0
  105. package/docs/wiz.md +177 -0
  106. package/docs/woocommerce.md +400 -0
  107. package/docs/wordpress-com.md +358 -0
  108. package/docs/wordpress-self-hosted.md +252 -0
  109. package/docs/workday.md +292 -0
  110. package/docs/x-twitter.md +188 -0
  111. package/docs/xero.md +429 -0
  112. package/docs/zoho-books.md +214 -0
  113. package/docs/zoho-crm.md +192 -0
  114. package/package.json +153 -0
  115. package/patterns/ai-connections/PATTERN.md +84 -0
  116. package/patterns/ai-connections/example/ai-model.js +11 -0
  117. package/patterns/ai-connections/example/perplexity-answer.js +29 -0
  118. package/patterns/api-key-connection/PATTERN.md +368 -0
  119. package/patterns/api-key-connection/example/connections.js +18 -0
  120. package/patterns/api-key-connection/example/integrations.json +19 -0
  121. package/patterns/assistant-mcp/PATTERN.md +110 -0
  122. package/patterns/assistant-mcp/example/integrations.json +13 -0
  123. package/patterns/assistant-mcp-oauth/PATTERN.md +235 -0
  124. package/patterns/assistant-mcp-oauth/example/integrations.json +102 -0
  125. package/patterns/aws-storage-queries/PATTERN.md +169 -0
  126. package/patterns/aws-storage-queries/example/formats/data-formats.js +35 -0
  127. package/patterns/aws-storage-queries/example/formats/package-lock.json +49 -0
  128. package/patterns/aws-storage-queries/example/formats/package.json +12 -0
  129. package/patterns/aws-storage-queries/example/formats/verify-formats.mjs +37 -0
  130. package/patterns/aws-storage-queries/example/integrations.json +38 -0
  131. package/patterns/aws-storage-queries/example/s3-transfer.js +34 -0
  132. package/patterns/event-delivery/PATTERN.md +151 -0
  133. package/patterns/event-delivery/example/integrations.json +17 -0
  134. package/patterns/firebase-messaging/PATTERN.md +156 -0
  135. package/patterns/firebase-messaging/example/server/notifications.js +23 -0
  136. package/patterns/google-ads-search/PATTERN.md +92 -0
  137. package/patterns/google-ads-search/example/ads-setup.js +25 -0
  138. package/patterns/oauth-connection/PATTERN.md +724 -0
  139. package/patterns/oauth-connection/example/integrations.json +20 -0
  140. package/patterns/paddle-catalogue/PATTERN.md +78 -0
  141. package/patterns/paddle-catalogue/example/create-products.js +38 -0
  142. package/patterns/public-image/PATTERN.md +84 -0
  143. package/patterns/public-image/example/integrations.json +13 -0
  144. package/patterns/public-image/example/logo-url.js +16 -0
  145. package/patterns/redshift-queries/PATTERN.md +150 -0
  146. package/patterns/redshift-queries/example/integrations.json +33 -0
  147. package/patterns/source-scanning/PATTERN.md +83 -0
  148. package/patterns/source-scanning/example/source-scanner.js +13 -0
  149. package/src/client/firebase-cloud-messaging.js +10 -0
  150. package/src/client/google-analytics.js +72 -0
  151. package/src/client/google-maps-platform.js +39 -0
  152. package/src/client/logo-dev.js +36 -0
  153. package/src/server/ai.js +46 -0
  154. package/src/server/airtable.js +67 -0
  155. package/src/server/algolia.js +63 -0
  156. package/src/server/amazon-redshift.js +96 -0
  157. package/src/server/amplitude.js +31 -0
  158. package/src/server/apify.js +68 -0
  159. package/src/server/apollo-io.js +85 -0
  160. package/src/server/asana.js +64 -0
  161. package/src/server/ashby.js +61 -0
  162. package/src/server/atlassian.js +32 -0
  163. package/src/server/attention.js +93 -0
  164. package/src/server/aws-athena.js +82 -0
  165. package/src/server/aws-s3.js +49 -0
  166. package/src/server/awsTransport.js +75 -0
  167. package/src/server/bigquery.js +97 -0
  168. package/src/server/brevo.js +97 -0
  169. package/src/server/calendly.js +77 -0
  170. package/src/server/canva.js +13 -0
  171. package/src/server/chargebee.js +88 -0
  172. package/src/server/clay.js +78 -0
  173. package/src/server/clickhouse.js +66 -0
  174. package/src/server/confidence.js +29 -0
  175. package/src/server/contentful.js +40 -0
  176. package/src/server/databricks.js +55 -0
  177. package/src/server/dbt-semantic-layer.js +95 -0
  178. package/src/server/elevenlabs.js +81 -0
  179. package/src/server/figma.js +24 -0
  180. package/src/server/firebase-cloud-messaging.js +111 -0
  181. package/src/server/firecrawl.js +70 -0
  182. package/src/server/fireflies.js +31 -0
  183. package/src/server/fireworks-ai.js +16 -0
  184. package/src/server/gatewayapi.js +48 -0
  185. package/src/server/gemini-enterprise.js +47 -0
  186. package/src/server/github-api.js +90 -0
  187. package/src/server/gitlab-api.js +67 -0
  188. package/src/server/gmail.js +60 -0
  189. package/src/server/gong.js +52 -0
  190. package/src/server/google-ads-search.js +134 -0
  191. package/src/server/google-ads.js +80 -0
  192. package/src/server/google-docs.js +36 -0
  193. package/src/server/google-drive.js +71 -0
  194. package/src/server/google-maps-platform.js +90 -0
  195. package/src/server/google-search-console.js +31 -0
  196. package/src/server/google-sheets.js +60 -0
  197. package/src/server/google-slides.js +36 -0
  198. package/src/server/google.js +61 -0
  199. package/src/server/granola.js +115 -0
  200. package/src/server/graphqlOperation.js +15 -0
  201. package/src/server/hex.js +29 -0
  202. package/src/server/heygen.js +54 -0
  203. package/src/server/hubspot.js +85 -0
  204. package/src/server/incident-io.js +68 -0
  205. package/src/server/inngest.js +86 -0
  206. package/src/server/jsonOperation.js +34 -0
  207. package/src/server/klipy.js +34 -0
  208. package/src/server/lexware.js +59 -0
  209. package/src/server/lightspeed.js +82 -0
  210. package/src/server/linear.js +52 -0
  211. package/src/server/linkedin.js +74 -0
  212. package/src/server/mailgun.js +76 -0
  213. package/src/server/mapbox.js +46 -0
  214. package/src/server/mcpRegistration.js +41 -0
  215. package/src/server/mcpTransport.js +60 -0
  216. package/src/server/microsoft-excel.js +67 -0
  217. package/src/server/microsoft-fabric.js +81 -0
  218. package/src/server/microsoft-onedrive.js +58 -0
  219. package/src/server/microsoft-onenote.js +52 -0
  220. package/src/server/microsoft-outlook.js +58 -0
  221. package/src/server/microsoft-powerpoint.js +5 -0
  222. package/src/server/microsoft-sharepoint.js +46 -0
  223. package/src/server/microsoft-teams.js +29 -0
  224. package/src/server/microsoft-word.js +5 -0
  225. package/src/server/microsoft.js +88 -0
  226. package/src/server/miro.js +29 -0
  227. package/src/server/n8n.js +78 -0
  228. package/src/server/notion.js +76 -0
  229. package/src/server/oura.js +48 -0
  230. package/src/server/paddle.js +83 -0
  231. package/src/server/perplexity.js +15 -0
  232. package/src/server/pipedrive.js +95 -0
  233. package/src/server/polar.js +20 -0
  234. package/src/server/posthog.js +44 -0
  235. package/src/server/prestashop.js +42 -0
  236. package/src/server/replicate.js +42 -0
  237. package/src/server/resend.js +93 -0
  238. package/src/server/salesforce.js +84 -0
  239. package/src/server/sanity.js +31 -0
  240. package/src/server/semrush.js +359 -0
  241. package/src/server/sentry.js +30 -0
  242. package/src/server/sevdesk.js +20 -0
  243. package/src/server/shopify.js +99 -0
  244. package/src/server/slack.js +143 -0
  245. package/src/server/snowflake.js +139 -0
  246. package/src/server/storyblok.js +49 -0
  247. package/src/server/stripe.js +13 -0
  248. package/src/server/supabase.js +13 -0
  249. package/src/server/tally.js +78 -0
  250. package/src/server/telegram.js +55 -0
  251. package/src/server/tiktok.js +86 -0
  252. package/src/server/twilio.js +90 -0
  253. package/src/server/twitch.js +409 -0
  254. package/src/server/wave.js +520 -0
  255. package/src/server/wix.js +1048 -0
  256. package/src/server/wiz.js +117 -0
  257. package/src/server/woocommerce.js +412 -0
  258. package/src/server/wordpress-com.js +236 -0
  259. package/src/server/wordpress-self-hosted.js +134 -0
  260. package/src/server/workday.js +136 -0
  261. package/src/server/x-twitter.js +110 -0
  262. package/src/server/xero.js +397 -0
  263. package/src/server/zoho-books.js +90 -0
  264. package/src/server/zoho-crm.js +89 -0
  265. package/src/server/zohoOAuth.js +21 -0
  266. package/src/shared/ai-models.json +8049 -0
  267. package/src/shared/ai.js +63 -0
  268. package/src/shared/algolia.js +29 -0
  269. package/src/shared/amazon-redshift.js +51 -0
  270. package/src/shared/amplitude.js +28 -0
  271. package/src/shared/atlassian.js +43 -0
  272. package/src/shared/aws.js +72 -0
  273. package/src/shared/canva.js +66 -0
  274. package/src/shared/chargebee.js +26 -0
  275. package/src/shared/clickhouse.js +38 -0
  276. package/src/shared/confidence.js +28 -0
  277. package/src/shared/databricks.js +33 -0
  278. package/src/shared/dbt-semantic-layer.js +32 -0
  279. package/src/shared/definitions.js +140 -0
  280. package/src/shared/figma.js +21 -0
  281. package/src/shared/firebase-cloud-messaging.js +45 -0
  282. package/src/shared/gemini-enterprise.js +33 -0
  283. package/src/shared/gong.js +33 -0
  284. package/src/shared/google-ads-search.js +31 -0
  285. package/src/shared/google-ads.js +28 -0
  286. package/src/shared/google-maps-platform.js +28 -0
  287. package/src/shared/google.js +133 -0
  288. package/src/shared/granola.js +25 -0
  289. package/src/shared/hex.js +28 -0
  290. package/src/shared/inngest.js +35 -0
  291. package/src/shared/lightspeed.js +75 -0
  292. package/src/shared/linkedin.js +27 -0
  293. package/src/shared/logo-dev.js +21 -0
  294. package/src/shared/mapbox.js +38 -0
  295. package/src/shared/mcp.js +92 -0
  296. package/src/shared/microsoft-fabric.js +47 -0
  297. package/src/shared/microsoft.js +231 -0
  298. package/src/shared/miro.js +27 -0
  299. package/src/shared/oura.js +23 -0
  300. package/src/shared/posthog.js +33 -0
  301. package/src/shared/prestashop.js +23 -0
  302. package/src/shared/salesforce.js +36 -0
  303. package/src/shared/semrush.js +24 -0
  304. package/src/shared/shopify.js +51 -0
  305. package/src/shared/siteUrl.js +12 -0
  306. package/src/shared/slack.js +88 -0
  307. package/src/shared/snowflake.js +55 -0
  308. package/src/shared/tiktok.js +22 -0
  309. package/src/shared/tokens.js +747 -0
  310. package/src/shared/twilio.js +41 -0
  311. package/src/shared/twitch.js +49 -0
  312. package/src/shared/wave.js +52 -0
  313. package/src/shared/wix.js +40 -0
  314. package/src/shared/wiz.js +41 -0
  315. package/src/shared/wordpress-com.js +30 -0
  316. package/src/shared/wordpress.js +69 -0
  317. package/src/shared/workday.js +50 -0
  318. package/src/shared/x-twitter.js +19 -0
  319. package/src/shared/xero.js +46 -0
  320. package/src/shared/zoho-books.js +39 -0
  321. package/src/shared/zoho-crm.js +43 -0
  322. package/src/shared/zoho-regions.js +12 -0
  323. package/test/ai.test.js +136 -0
  324. package/test/airtable.test.js +125 -0
  325. package/test/algolia.test.js +207 -0
  326. package/test/amazon-redshift.test.js +272 -0
  327. package/test/amplitude.test.js +239 -0
  328. package/test/apify.test.js +93 -0
  329. package/test/apollo-io.test.js +125 -0
  330. package/test/asana.test.js +99 -0
  331. package/test/ashby.test.js +97 -0
  332. package/test/atlassian.test.js +249 -0
  333. package/test/attention.test.js +79 -0
  334. package/test/aws.test.js +405 -0
  335. package/test/brevo.test.js +71 -0
  336. package/test/calendly.test.js +136 -0
  337. package/test/canva.test.js +258 -0
  338. package/test/chargebee.test.js +195 -0
  339. package/test/clickhouse.test.js +278 -0
  340. package/test/confidence.test.js +205 -0
  341. package/test/databricks.test.js +253 -0
  342. package/test/dbt-semantic-layer.test.js +221 -0
  343. package/test/designMcp.test.js +336 -0
  344. package/test/elevenlabs.test.js +52 -0
  345. package/test/firebase-cloud-messaging.test.js +242 -0
  346. package/test/firecrawl.test.js +66 -0
  347. package/test/gemini-enterprise.test.js +218 -0
  348. package/test/github-api.test.js +167 -0
  349. package/test/gitlab-api.test.js +160 -0
  350. package/test/gong.test.js +192 -0
  351. package/test/google-ads.test.js +350 -0
  352. package/test/google-maps-platform.test.js +191 -0
  353. package/test/google.test.js +710 -0
  354. package/test/granola.test.js +240 -0
  355. package/test/hex.test.js +253 -0
  356. package/test/heygen.test.js +121 -0
  357. package/test/hubspot.test.js +103 -0
  358. package/test/inngest.test.js +219 -0
  359. package/test/lightspeed.test.js +250 -0
  360. package/test/linear.test.js +146 -0
  361. package/test/linkedin.test.js +243 -0
  362. package/test/logo-dev.test.js +79 -0
  363. package/test/mailgun.test.js +107 -0
  364. package/test/mapbox.test.js +181 -0
  365. package/test/mcp.test.js +227 -0
  366. package/test/microsoft-fabric.test.js +258 -0
  367. package/test/microsoft.test.js +554 -0
  368. package/test/n8nOAuth.test.js +188 -0
  369. package/test/notionOAuth.test.js +183 -0
  370. package/test/oura.test.js +211 -0
  371. package/test/paddleCatalogue.test.js +51 -0
  372. package/test/paddlePermissions.test.js +101 -0
  373. package/test/perplexityComposition.test.js +39 -0
  374. package/test/pipedriveOAuth.test.js +140 -0
  375. package/test/posthog.test.js +200 -0
  376. package/test/prestashop.test.js +207 -0
  377. package/test/resend.test.js +163 -0
  378. package/test/salesforce.test.js +276 -0
  379. package/test/semrush.test.js +479 -0
  380. package/test/sentry.test.js +35 -0
  381. package/test/shopify.test.js +247 -0
  382. package/test/slack.test.js +320 -0
  383. package/test/snowflake.test.js +391 -0
  384. package/test/tally.test.js +75 -0
  385. package/test/telegram.test.js +58 -0
  386. package/test/tiktok.test.js +243 -0
  387. package/test/tokens.test.js +1079 -0
  388. package/test/twilio.test.js +221 -0
  389. package/test/twitch.test.js +573 -0
  390. package/test/wave.test.js +678 -0
  391. package/test/wix.test.js +1099 -0
  392. package/test/wiz.test.js +185 -0
  393. package/test/wordpress-com.test.js +469 -0
  394. package/test/wordpress.test.js +660 -0
  395. package/test/workday.test.js +314 -0
  396. package/test/x-twitter.test.js +256 -0
  397. package/test/xero.test.js +700 -0
  398. package/test/zoho-books.test.js +253 -0
  399. package/test/zoho-crm.test.js +223 -0
@@ -0,0 +1,77 @@
1
+ import { ConnectorError } from "@jskit-ai/connectors-core/server";
2
+ import { createSchema } from "json-rest-schema";
3
+ import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
4
+ import { calendlyDefinition } from "../shared/tokens.js";
5
+ import { jsonOperation } from "./jsonOperation.js";
6
+
7
+ const id = { type: "string", required: true, minLength: 1, maxLength: 100, pattern: "^[A-Za-z0-9_-]+$" };
8
+ const text = { type: "string", minLength: 1, maxLength: 1000 };
9
+ const email = { ...text, maxLength: 320, pattern: "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$" };
10
+ const date = { ...text, validator: value => /^\d{4}-\d{2}-\d{2}T.*Z$/.test(value) && Number.isFinite(Date.parse(value)) || "Use a UTC ISO timestamp ending in Z." };
11
+ const page = { count: { type: "integer", min: 1, max: 100, defaultTo: 20 }, page_token: { type: "string", maxLength: 4096 } };
12
+ const status = { type: "string", enum: ["active", "canceled"] };
13
+ const record = result => typeof result?.resource?.uri === "string";
14
+ const collection = result => Array.isArray(result?.collection) && Number.isInteger(result?.pagination?.count);
15
+ function uri(kind) { return { type: "string", maxLength: 2048, pattern: `^https://api\\.calendly\\.com/${kind}/[A-Za-z0-9_-]+$` }; }
16
+ function operation(method, endpoint, fields, scope, validateResult, prepare = value => value) {
17
+ const schema = createSchema(fields);
18
+ return { scopes: [scope], request(input) {
19
+ const { uuid, invitee_uuid, ...values } = prepare(validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 }));
20
+ const url = new URL(`https://api.calendly.com/${endpoint.replace("{uuid}", uuid || "").replace("{invitee_uuid}", invitee_uuid || "")}`);
21
+ if (method === "GET") { for (const [key, value] of Object.entries(values)) url.searchParams.set(key, String(value)); return { method, url: url.href }; }
22
+ return { method, url: url.href, ...(method === "POST" ? { body: values } : {}) };
23
+ }, validateResult };
24
+ }
25
+ const calendlyProvider = Object.freeze({
26
+ ...calendlyDefinition, apiOrigins: ["https://api.calendly.com"],
27
+ apiKey: { headers: (key) => ({ Authorization: `Bearer ${key}` }) },
28
+ oauth: {
29
+ issuer: "https://auth.calendly.com",
30
+ authorization_endpoint: "https://auth.calendly.com/oauth/authorize",
31
+ token_endpoint: "https://auth.calendly.com/oauth/token"
32
+ },
33
+ checkOperation: "profile.read",
34
+ operations: {
35
+ "eventTypes.get": operation("GET", "event_types/{uuid}", { uuid: id }, "event_types:read", record),
36
+ "availability.list": operation("GET", "event_type_available_times", { event_type: { ...uri("event_types"), required: true },
37
+ start_time: { ...date, required: true }, end_time: { ...date, required: true }
38
+ }, "availability:read", result => Array.isArray(result?.collection), value => {
39
+ const span = Date.parse(value.end_time) - Date.parse(value.start_time);
40
+ if (span <= 0 || span > 31 * 86400000) throw new ConnectorError("connector_input_invalid", "Choose a positive range of at most 31 days.", { statusCode: 422 }); return value;
41
+ }),
42
+ "events.list": operation("GET", "scheduled_events", { user: uri("users"), organization: uri("organizations"), group: uri("groups"),
43
+ invitee_email: email, status, ...page, sort: { type: "string", enum: ["start_time:asc", "start_time:desc"] }, min_start_time: date, max_start_time: date
44
+ }, "scheduled_events:read", collection, value => {
45
+ if (!(value.user || value.organization || value.group)) throw new ConnectorError("connector_input_invalid", "Choose the user, organization or group whose events to read.", { statusCode: 422 }); return value;
46
+ }),
47
+ "events.get": operation("GET", "scheduled_events/{uuid}", { uuid: id }, "scheduled_events:read", record),
48
+ "events.cancel": operation("POST", "scheduled_events/{uuid}/cancellation", { uuid: id, reason: { ...text, maxLength: 10000 } },
49
+ "scheduled_events:write", result => typeof result?.resource?.canceled_by === "string" && typeof result.resource.created_at === "string"),
50
+ "invitees.list": operation("GET", "scheduled_events/{uuid}/invitees", { uuid: id, ...page, status, email,
51
+ sort: { type: "string", enum: ["created_at:asc", "created_at:desc"] } }, "scheduled_events:read", collection),
52
+ "invitees.get": operation("GET", "scheduled_events/{uuid}/invitees/{invitee_uuid}", { uuid: id, invitee_uuid: id }, "scheduled_events:read", record),
53
+ "invitees.create": operation("POST", "invitees", { event_type: { ...uri("event_types"), required: true }, start_time: { ...date, required: true },
54
+ invitee: { type: "object", required: true, schema: createSchema({ email: { ...email, required: true }, name: text,
55
+ timezone: { ...text, required: true, validator: value => { try { new Intl.DateTimeFormat("en", { timeZone: value }); return true; } catch { return "Choose an IANA timezone."; } } } }) },
56
+ questions_and_answers: { type: "array", items: { type: "object", schema: createSchema({ question: { ...text, required: true }, answer: { ...text, required: true }, position: { type: "integer", required: true, min: 0 } }) },
57
+ validator: value => value.length <= 100 || "Use at most 100 answers." },
58
+ event_guests: { type: "array", items: email, validator: value => value.length <= 10 || "Use at most 10 guests." }
59
+ }, "scheduled_events:write", record),
60
+ "schedulingLinks.create": operation("POST", "scheduling_links", { owner: { ...uri("event_types"), required: true },
61
+ owner_type: { type: "string", enum: ["EventType"], defaultTo: "EventType" }, max_event_count: { type: "integer", enum: [1], defaultTo: 1 }
62
+ }, "scheduling_links:write", result => typeof result?.resource?.booking_url === "string" && typeof result.resource.owner === "string"),
63
+ "noShows.create": operation("POST", "invitee_no_shows", { invitee: { type: "string", required: true,
64
+ pattern: "^https://api\\.calendly\\.com/scheduled_events/[A-Za-z0-9_-]+/invitees/[A-Za-z0-9_-]+$" } }, "scheduled_events:write", record),
65
+ "noShows.get": operation("GET", "invitee_no_shows/{uuid}", { uuid: id }, "scheduled_events:read", record),
66
+ "noShows.delete": operation("DELETE", "invitee_no_shows/{uuid}", { uuid: id }, "scheduled_events:write", result => result === null),
67
+ "profile.read": { ...jsonOperation("https://api.calendly.com/users/me", {},
68
+ (result) => typeof result?.resource?.uri === "string" && typeof result.resource.name === "string"), scopes: ["users:read"] },
69
+ "eventTypes.list": { ...jsonOperation("https://api.calendly.com/event_types", {
70
+ user: { type: "string", required: true, minLength: 1, maxLength: 2048 },
71
+ count: { type: "integer", min: 1, max: 100, defaultTo: 20 },
72
+ page_token: { type: "string", maxLength: 4096 },
73
+ active: { type: "boolean" }
74
+ }, (result) => Array.isArray(result?.collection) && Number.isInteger(result.pagination?.count)), scopes: ["event_types:read"] }
75
+ }
76
+ });
77
+ export { calendlyProvider };
@@ -0,0 +1,13 @@
1
+ import { canvaDefinition } from "../shared/canva.js";
2
+ import { mcpOperations, exchangeMcp } from "./mcpTransport.js";
3
+
4
+ const canvaProvider = Object.freeze({
5
+ ...canvaDefinition,
6
+ oauth: { issuer: "https://mcp.canva.com", authorization_endpoint: "https://mcp.canva.com/authorize",
7
+ token_endpoint: "https://mcp.canva.com/token", response_types_supported: ["code"] },
8
+ oauthResource: "https://mcp.canva.com", apiOrigins: ["https://mcp.canva.com"],
9
+ requestTimeoutMs: 60_000, checkOperation: "tools.list", exchange: exchangeMcp,
10
+ operations: mcpOperations(() => "https://mcp.canva.com/mcp")
11
+ });
12
+
13
+ export { canvaProvider };
@@ -0,0 +1,88 @@
1
+ import { ConnectorError } from "@jskit-ai/connectors-core/server";
2
+ import { createSchema } from "json-rest-schema";
3
+ import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
4
+ import { chargebeeDefinition } from "../shared/chargebee.js";
5
+ import { jsonOperation } from "./jsonOperation.js";
6
+
7
+ const origin = ({ siteName }) => `https://${siteName.toLowerCase()}.chargebee.com`;
8
+ const text = { type: "string", minLength: 1, maxLength: 250 };
9
+ const resource = { ...text, required: true, maxLength: 100, pattern: "^[A-Za-z0-9_-]+$" };
10
+ const pagination = { limit: { type: "integer", min: 1, max: 100, defaultTo: 20 }, offset: { ...text, maxLength: 1000 } };
11
+ const itemList = { type: "array", required: true, items: { type: "object", schema: createSchema({
12
+ item_price_id: { ...resource }, quantity: { type: "integer", min: 1, required: true }
13
+ }) }, validator: values => values.length > 0 && values.length <= 100 || "Choose between 1 and 100 item prices." };
14
+ const customerFields = { email: { ...text, pattern: "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$" }, first_name: text, last_name: text, company: text };
15
+ const returnUrl = { ...text, required: true, validator: value => {
16
+ try { const u = new URL(value); return !u.username && !u.password && (u.protocol === "https:" || u.protocol === "http:" && ["localhost", "127.0.0.1"].includes(u.hostname)) || "Use an HTTPS or localhost return URL."; }
17
+ catch { return "Use an absolute return URL."; }
18
+ } };
19
+ const record = key => result => typeof result?.[key]?.id === "string" && result[key].id.length > 0;
20
+ const collection = key => result => Array.isArray(result?.list) && result.list.every(entry => record(key)(entry)) &&
21
+ (result.next_offset === undefined || typeof result.next_offset === "string");
22
+ function operation(method, endpoint, fields, validateResult, defaults = {}) {
23
+ const schema = createSchema({ ...fields, ...(method === "POST" ? { idempotencyKey: { ...resource } } : {}) });
24
+ return { scopes: [], request(input, settings) {
25
+ const { resource: id, idempotencyKey, ...values } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
26
+ const body = new URLSearchParams();
27
+ for (const [key, value] of Object.entries({ ...values, ...defaults })) {
28
+ if (Array.isArray(value)) value.forEach((entry, index) => { for (const [field, child] of Object.entries(entry)) body.set(`${key}[${field}][${index}]`, String(child)); });
29
+ else if (value && typeof value === "object") { for (const [field, child] of Object.entries(value)) body.set(`${key}[${field}]`, String(child)); }
30
+ else body.set(key, String(value));
31
+ }
32
+ const url = new URL(`${origin(settings)}/api/v2/${endpoint.replace("{resource}", id || "")}`);
33
+ if (method === "GET") { url.search = body.toString(); return { method, url: url.href }; }
34
+ return { method, url: url.href, body: body.toString(), headers: { "Content-Type": "application/x-www-form-urlencoded", "chargebee-idempotency-key": idempotencyKey } };
35
+ }, validateResult };
36
+ }
37
+ const chargebeeProvider = Object.freeze({
38
+ ...chargebeeDefinition,
39
+ apiOrigins: (settings) => [origin(settings)],
40
+ apiKey: { headers: (key) => {
41
+ if (/[:\s\p{Cc}]/u.test(key)) {
42
+ throw new ConnectorError("connector_binding_missing", "Use the Chargebee API key without spaces, control characters or colons.");
43
+ }
44
+ return { Authorization: `Basic ${Buffer.from(`${key}:`).toString("base64")}` };
45
+ } },
46
+ checkOperation: "customers.list",
47
+ operations: {
48
+ "customers.get": operation("GET", "customers/{resource}", { resource }, record("customer")),
49
+ "customers.create": operation("POST", "customers", { ...customerFields, email: { ...customerFields.email, required: true }, id: { ...resource, required: false } }, record("customer")),
50
+ "customers.update": operation("POST", "customers/{resource}", { resource, ...customerFields }, record("customer")),
51
+ "itemFamilies.list": operation("GET", "item_families", pagination, collection("item_family")),
52
+ "items.list": operation("GET", "items", pagination, collection("item")),
53
+ "items.get": operation("GET", "items/{resource}", { resource }, record("item")),
54
+ "itemPrices.list": operation("GET", "item_prices", pagination, collection("item_price")),
55
+ "itemPrices.get": operation("GET", "item_prices/{resource}", { resource }, record("item_price")),
56
+ "subscriptions.list": operation("GET", "subscriptions", { ...pagination, "customer_id[is]": text }, collection("subscription")),
57
+ "subscriptions.get": operation("GET", "subscriptions/{resource}", { resource }, record("subscription")),
58
+ "subscriptions.create": operation("POST", "customers/{resource}/subscription_for_items", { resource, subscription_items: itemList,
59
+ auto_collection: { type: "string", required: true, enum: ["on", "off"] }, invoice_immediately: { type: "boolean", required: true },
60
+ id: { ...resource, required: false }, trial_end: { type: "integer", min: 0 }
61
+ }, record("subscription")),
62
+ "subscriptions.updateAtTermEnd": operation("POST", "subscriptions/{resource}/update_for_items", { resource, subscription_items: itemList,
63
+ replace_items_list: { type: "boolean", required: true }
64
+ }, record("subscription"), { end_of_term: true }),
65
+ "subscriptions.cancelAtTermEnd": operation("POST", "subscriptions/{resource}/cancel_for_items", { resource, cancel_reason_code: text }, record("subscription"), { cancel_option: "end_of_term" }),
66
+ "invoices.list": operation("GET", "invoices", { ...pagination, "customer_id[is]": text, "subscription_id[is]": text }, collection("invoice")),
67
+ "invoices.get": operation("GET", "invoices/{resource}", { resource, line_items_limit: { type: "integer", min: 1, max: 300 }, line_items_offset: { ...text, maxLength: 1000 } }, record("invoice")),
68
+ "invoices.pdf": operation("POST", "invoices/{resource}/pdf", { resource, disposition_type: { type: "string", enum: ["inline", "attachment"], defaultTo: "attachment" } }, result => typeof result?.download?.download_url === "string"),
69
+ "hostedPages.checkoutNew": operation("POST", "hosted_pages/checkout_new_for_items", { subscription_items: itemList,
70
+ customer: { type: "object", required: true, schema: createSchema({ id: { ...resource } }) }, redirect_url: returnUrl, cancel_url: returnUrl
71
+ }, record("hosted_page")),
72
+ "hostedPages.checkoutExisting": operation("POST", "hosted_pages/checkout_existing_for_items", { subscription_items: itemList,
73
+ subscription: { type: "object", required: true, schema: createSchema({ id: { ...resource } }) }, redirect_url: returnUrl, cancel_url: returnUrl
74
+ }, record("hosted_page")),
75
+ "hostedPages.get": operation("GET", "hosted_pages/{resource}", { resource }, record("hosted_page")),
76
+ "portalSessions.create": operation("POST", "portal_sessions", { customer: { type: "object", required: true, schema: createSchema({ id: { ...resource } }) }, redirect_url: returnUrl }, record("portal_session")),
77
+ "events.list": operation("GET", "events", pagination, collection("event")),
78
+ "events.get": operation("GET", "events/{resource}", { resource }, record("event")),
79
+ "customers.list": jsonOperation((settings) => `${origin(settings)}/api/v2/customers`, {
80
+ limit: { type: "integer", min: 1, max: 100, defaultTo: 10 },
81
+ offset: { type: "string", minLength: 1, maxLength: 1000 },
82
+ include_deleted: { type: "boolean", defaultTo: false }
83
+ }, (result) => Array.isArray(result?.list) && result.list.every((entry) =>
84
+ entry?.customer && typeof entry.customer.id === "string" && entry.customer.id.length > 0) &&
85
+ (result.next_offset === undefined || typeof result.next_offset === "string" && result.next_offset.length > 0 && result.next_offset.length <= 1000))
86
+ }
87
+ });
88
+ export { chargebeeProvider };
@@ -0,0 +1,78 @@
1
+ import { createSchema } from "json-rest-schema";
2
+ import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
3
+ import { clayDefinition } from "../shared/tokens.js";
4
+ import { jsonOperation } from "./jsonOperation.js";
5
+
6
+ const endpoint = "https://api.clay.com/public/v0";
7
+ const pageSchema = createSchema({
8
+ searchId: { type: "string", required: true, minLength: 1, maxLength: 1024,
9
+ validator: (value) => /^[a-zA-Z0-9_-]+$/u.test(value) || "Use the search ID returned by Clay." },
10
+ limit: { type: "integer", min: 1, max: 500, defaultTo: 20 }
11
+ });
12
+
13
+ const routineId = { type: "string", required: true, minLength: 1, maxLength: 64,
14
+ validator: value => /^[a-zA-Z0-9_:-]+$/u.test(value) || "Use the routine ID from Clay, including function: for custom functions." };
15
+ const object = { type: "object", additionalProperties: true };
16
+ const array = (items, maximum, minimum = 0) => ({ type: "array", items,
17
+ validator: value => value.length >= minimum && value.length <= maximum || `Use ${minimum}–${maximum} entries.` });
18
+ const text = { type: "string", minLength: 1 };
19
+
20
+ function routineOperation(method, suffix, fields, validateResult) {
21
+ const schema = createSchema({ routineId, ...fields });
22
+ return { scopes: [], request(input) {
23
+ const { routineId: id, ...values } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
24
+ const url = new URL(`${endpoint}/routines/${suffix.replace("{id}", encodeURIComponent(id))}`);
25
+ if (method === "POST") return { method, url: url.href, body: values };
26
+ for (const [key, value] of Object.entries(values)) url.searchParams.set(key, String(value));
27
+ return { method, url: url.href };
28
+ }, validateResult };
29
+ }
30
+
31
+ const clayProvider = Object.freeze({
32
+ ...clayDefinition,
33
+ apiOrigins: ["https://api.clay.com"],
34
+ apiKey: { headers: (key) => ({ "clay-api-key": key }) },
35
+ checkOperation: "identity.read",
36
+ operations: {
37
+ "identity.read": jsonOperation(`${endpoint}/me`, {}, (result) =>
38
+ typeof result?.user?.id === "string" && result.user.id.length > 0 &&
39
+ typeof result?.workspace?.id === "string" && result.workspace.id.length > 0),
40
+ "credits.balance": jsonOperation(`${endpoint}/credits/balance`, {}, result => typeof result?.balance === "number"),
41
+ "searches.reference": jsonOperation(`${endpoint}/search/query-mode/reference`, {}, result => typeof result?.reference === "string"),
42
+ "routines.run": routineOperation("POST", "{id}/run", {
43
+ items: { ...array({ type: "object", schema: createSchema({
44
+ id: { ...text, required: true, maxLength: 64 }, inputs: { ...object, required: true }
45
+ }) }, 100, 1), required: true },
46
+ webhook_id: { ...text, maxLength: 64 }
47
+ }, result => typeof result?.routine_run_id === "string" && result.routine_run_id.length > 0 && result.status === "in_progress"),
48
+ "routines.results": routineOperation("GET", "run/{id}/results", {
49
+ cursor: text, limit: { type: "integer", min: 1, max: 100, defaultTo: 20 }
50
+ }, result => typeof result?.routine_run_id === "string" && typeof result.total === "number" && typeof result.finished === "number" &&
51
+ (result.status === "in_progress" || result.status === "complete" && Array.isArray(result.data))),
52
+ "tables.query": jsonOperation(`${endpoint}/tables/query`, {
53
+ query: { type: "object", required: true, schema: createSchema({
54
+ tables: { ...array({ type: "object", schema: createSchema({ id: { ...text, required: true }, alias: { type: "string" } }) }, 5, 1), required: true },
55
+ field_mode: { type: "string", enum: ["names", "ids"] },
56
+ filter: object, select: array(object, 20), join: array(object, 4),
57
+ group_by: array(text, 5), order_by: array(object, 3)
58
+ }) },
59
+ cursor: text, limit: { type: "integer", min: 1, max: 100, defaultTo: 50 }
60
+ }, result => Array.isArray(result?.data), "POST"),
61
+ "searches.create": jsonOperation(`${endpoint}/search/query-mode`, {
62
+ query: { type: "string", required: true, minLength: 1, maxLength: 16000,
63
+ validator: (value) => value.trim().length > 0 || "Enter a Clay search query." }
64
+ }, (result) => typeof result?.search_id === "string" && result.search_id.length > 0 &&
65
+ ["people", "companies"].includes(result.source_type), "POST"),
66
+ "searches.next": {
67
+ scopes: [],
68
+ request(input) {
69
+ const { searchId, limit } = validateSchemaPayload({ schema: pageSchema, mode: "replace" }, input, { statusCode: 422 });
70
+ return { method: "POST", url: `${endpoint}/search/query-mode/${encodeURIComponent(searchId)}/run`, body: { limit } };
71
+ },
72
+ validateResult: (result) => Array.isArray(result?.data) && typeof result.has_more === "boolean" &&
73
+ ["people", "companies"].includes(result.source_type)
74
+ }
75
+ }
76
+ });
77
+
78
+ export { clayProvider };
@@ -0,0 +1,66 @@
1
+ import { createSchema } from "json-rest-schema";
2
+ import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
3
+ import { clickhouseDefinition } from "../shared/clickhouse.js";
4
+
5
+ const name = { type: "string", required: true, minLength: 1, maxLength: 256,
6
+ validator: (value) => !/[\p{Cc}]/u.test(value) || "Use a name without control characters." };
7
+ const pagination = {
8
+ limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
9
+ offset: { type: "integer", min: 0, max: 1000000, defaultTo: 0 }
10
+ };
11
+
12
+ function queryOperation(query, fields, validateData, parameters = values => values) {
13
+ const schema = createSchema(fields);
14
+ return {
15
+ scopes: [],
16
+ request(input, settings) {
17
+ const values = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
18
+ const url = new URL(settings.httpUrl);
19
+ const sql = typeof query === "function" ? query(values) : query;
20
+ url.searchParams.set("query", `${sql} FORMAT JSON`);
21
+ url.searchParams.set("readonly", "1");
22
+ url.searchParams.set("wait_end_of_query", "1");
23
+ url.searchParams.set("max_execution_time", "10");
24
+ url.searchParams.set("max_result_rows", "100");
25
+ url.searchParams.set("max_result_bytes", "5242880");
26
+ url.searchParams.set("result_overflow_mode", "throw");
27
+ for (const [key, value] of Object.entries(parameters(values))) url.searchParams.set(`param_${key}`, String(value).replaceAll("\\", "\\\\"));
28
+ return { method: "GET", url: url.href };
29
+ },
30
+ validateResult(result) {
31
+ return Boolean(result && !result.exception && !result.error && Array.isArray(result.meta) &&
32
+ result.meta.every((column) => typeof column?.name === "string" && typeof column.type === "string") &&
33
+ Array.isArray(result.data) && result.data.length <= 100 && Number.isInteger(result.rows) && result.rows === result.data.length &&
34
+ result.data.every((row) => row && typeof row === "object" && !Array.isArray(row)) && (!validateData || validateData(result.data)));
35
+ }
36
+ };
37
+ }
38
+
39
+ const clickhouseProvider = Object.freeze({
40
+ ...clickhouseDefinition,
41
+ apiOrigins: (settings) => [new URL(settings.httpUrl).origin],
42
+ apiKey: { headers: (password, settings) => ({ Authorization: `Basic ${Buffer.from(`${settings.username || "default"}:${password}`).toString("base64")}` }) },
43
+ checkOperation: "connection.check",
44
+ operations: {
45
+ "connection.check": queryOperation("SELECT 1 AS ok, currentUser() AS user", {},
46
+ (rows) => rows.length === 1 && rows[0].ok === 1 && typeof rows[0].user === "string" && rows[0].user.length > 0),
47
+ "queries.read": queryOperation(values => values.sql, {
48
+ sql: { type: "string", required: true, minLength: 1, maxLength: 16000,
49
+ // eslint-disable-next-line no-control-regex -- Reject literal control characters in provider input.
50
+ validator: value => /^\s*(SELECT|WITH)\b/iu.test(value) && !/[;\u0000]/u.test(value) || "Use one SELECT/WITH query without a terminating semicolon; pass values as typed parameters." },
51
+ parameters: { type: "object", additionalProperties: true,
52
+ validator: value => Object.keys(value).length <= 100 && Object.entries(value).every(([key, item]) =>
53
+ /^[a-zA-Z_][a-zA-Z0-9_]*$/u.test(key) && typeof item === "string" && item.length <= 16000 && !/[\p{Cc}]/u.test(item)) || "Use up to 100 named string parameters without control characters." }
54
+ }, undefined, values => values.parameters || {}),
55
+ "tables.list": queryOperation((values) => `SELECT database, name, engine FROM system.tables${values.database === undefined ? "" : " WHERE database = {database:String}"} ORDER BY database, name LIMIT {limit:UInt32} OFFSET {offset:UInt64}`,
56
+ { ...pagination, database: { ...name, required: false } },
57
+ (rows) => rows.every((row) => typeof row.database === "string" && typeof row.name === "string" && typeof row.engine === "string")),
58
+ "columns.list": queryOperation("SELECT name, type, position, default_kind, default_expression FROM system.columns WHERE database = {database:String} AND table = {table:String} ORDER BY position LIMIT {limit:UInt32} OFFSET {offset:UInt64}",
59
+ { database: name, table: name, ...pagination },
60
+ (rows) => rows.every((row) => typeof row.name === "string" && typeof row.type === "string")),
61
+ "rows.list": queryOperation((values) => `SELECT * FROM {database:Identifier}.{table:Identifier}${values.orderBy === undefined ? "" : " ORDER BY {orderBy:Identifier}"} LIMIT {limit:UInt32} OFFSET {offset:UInt64}`,
62
+ { database: name, table: name, orderBy: { ...name, required: false }, ...pagination })
63
+ }
64
+ });
65
+
66
+ export { clickhouseProvider };
@@ -0,0 +1,29 @@
1
+ import { createSchema } from "json-rest-schema";
2
+ import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
3
+ import { confidenceFlagsDefinition, confidenceExpDefinition } from "../shared/confidence.js";
4
+ import { mcpOperations, exchangeMcp } from "./mcpTransport.js";
5
+ import { mcpRegistrationFields, registerMcpClient } from "./mcpRegistration.js";
6
+
7
+ const origin = "https://mcp.confidence.dev";
8
+ const oauth = Object.freeze({ issuer: origin, authorization_endpoint: `${origin}/authorize`, token_endpoint: `${origin}/token`,
9
+ registration_endpoint: `${origin}/register`, response_types_supported: ["code"], code_challenge_methods_supported: ["S256"] });
10
+
11
+ function provider(definition, path) {
12
+ return Object.freeze({
13
+ ...definition, oauth, oauthResource: `${origin}/mcp`, apiOrigins: [origin], checkOperation: "tools.list", exchange: exchangeMcp,
14
+ operations: Object.fromEntries(Object.entries(mcpOperations(() => `${origin}/mcp/${path}`)).map(([name, operation]) => [name, { ...operation, scopes: ["openid"] }]))
15
+ });
16
+ }
17
+ const confidenceFlagsProvider = provider(confidenceFlagsDefinition, "flags");
18
+ const confidenceExpProvider = provider(confidenceExpDefinition, "experiments");
19
+ const registrationSchema = createSchema({ ...mcpRegistrationFields,
20
+ scopes: { type: "array", required: true, minLength: 1, items: { type: "string", enum: confidenceFlagsDefinition.scopes.map((scope) => scope.value) },
21
+ validator: (scopes) => scopes.includes("openid") && new Set(scopes).size === scopes.length || "Select account identity and include each permission once." }
22
+ });
23
+
24
+ async function registerConfidenceClient(input, options) {
25
+ const values = validateSchemaPayload({ schema: registrationSchema, mode: "replace" }, input, { statusCode: 422 });
26
+ return registerMcpClient("Confidence", oauth, values, options);
27
+ }
28
+
29
+ export { confidenceFlagsProvider, confidenceExpProvider, registerConfidenceClient };
@@ -0,0 +1,40 @@
1
+ import { createSchema } from "json-rest-schema";
2
+ import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
3
+ import { contentfulDefinition } from "../shared/tokens.js";
4
+ import { jsonOperation } from "./jsonOperation.js";
5
+
6
+ const text = { type: "string", minLength: 1, maxLength: 256 };
7
+ const page = { limit: { type: "integer", min: 1, max: 1000, defaultTo: 1 }, skip: { type: "integer", min: 0, defaultTo: 0 } };
8
+ const localized = { locale: text };
9
+ const resource = type => result => result?.sys?.type === type && typeof result.sys.id === "string" && result.sys.id.length > 0;
10
+ const collection = type => result => result?.sys?.type === "Array" && Array.isArray(result.items) && result.items.every(resource(type)) &&
11
+ Number.isInteger(result.total) && result.total >= 0 && Number.isInteger(result.skip) && result.skip >= 0 && Number.isInteger(result.limit) && result.limit >= 0;
12
+ const endpoint = (settings, path) => `https://${settings.region === "eu" ? "cdn.eu.contentful.com" : "cdn.contentful.com"}/spaces/${encodeURIComponent(settings.spaceId)}/environments/${encodeURIComponent(settings.environmentId || "master")}/${path}`;
13
+ function getOperation(path, type, fields = {}) {
14
+ const schema = createSchema({ id: { type: "string", required: true, minLength: 1, maxLength: 64,
15
+ validator: value => /^[a-zA-Z0-9_-]+$/u.test(value) || "Use the Contentful resource ID." }, ...fields });
16
+ return { scopes: [], request(input, settings) {
17
+ const { id, ...values } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
18
+ const url = new URL(endpoint(settings, `${path}/${encodeURIComponent(id)}`));
19
+ for (const [name, value] of Object.entries(values)) url.searchParams.set(name, String(value));
20
+ return { method: "GET", url: url.href };
21
+ }, validateResult: resource(type) };
22
+ }
23
+ const contentfulProvider = Object.freeze({
24
+ ...contentfulDefinition, apiOrigins: ["https://cdn.contentful.com", "https://cdn.eu.contentful.com"],
25
+ apiKey: { headers: key => ({ Authorization: `Bearer ${key}` }) }, checkOperation: "entries.list",
26
+ operations: {
27
+ "entries.list": jsonOperation(settings => endpoint(settings, "entries"), {
28
+ ...page, ...localized, content_type: { ...text, maxLength: 64 },
29
+ include: { type: "integer", min: 0, max: 10 }, order: text, query: { ...text, maxLength: 1000 },
30
+ "sys.id": { ...text, maxLength: 64 }
31
+ }, collection("Entry")),
32
+ "entries.get": getOperation("entries", "Entry", localized),
33
+ "assets.list": jsonOperation(settings => endpoint(settings, "assets"), { ...page, ...localized, order: text, query: { ...text, maxLength: 1000 } }, collection("Asset")),
34
+ "assets.get": getOperation("assets", "Asset", localized),
35
+ "contentTypes.list": jsonOperation(settings => endpoint(settings, "content_types"), page, collection("ContentType")),
36
+ "contentTypes.get": getOperation("content_types", "ContentType"),
37
+ "locales.list": jsonOperation(settings => endpoint(settings, "locales"), page, collection("Locale"))
38
+ }
39
+ });
40
+ export { contentfulProvider };
@@ -0,0 +1,55 @@
1
+ import { ConnectorError } from "@jskit-ai/connectors-core/server";
2
+ import { databricksDefinition } from "../shared/databricks.js";
3
+ import { jsonOperation } from "./jsonOperation.js";
4
+
5
+ const origin = (settings) => new URL(settings.workspaceUrl).origin;
6
+ const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
7
+ const identifier = (value) => Number.isSafeInteger(value) && value > 0;
8
+ const pageToken = { type: "string", minLength: 1, maxLength: 8192 };
9
+ const pagination = (value) => ["next_page_token", "prev_page_token"].every((key) => value[key] === undefined ||
10
+ typeof value[key] === "string" && value[key].length > 0 && value[key].length <= 8192);
11
+ const job = (value) => object(value) && identifier(value.job_id) &&
12
+ (value.settings === undefined || object(value.settings)) && (value.has_more === undefined || typeof value.has_more === "boolean");
13
+ const jobsList = jsonOperation((settings) => `${origin(settings)}/api/2.2/jobs/list`, {
14
+ limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
15
+ expand_tasks: { type: "boolean", defaultTo: false }, name: { type: "string", minLength: 1, maxLength: 4096 }, page_token: pageToken
16
+ }, (value) => object(value) && pagination(value) && (value.jobs === undefined || Array.isArray(value.jobs) && value.jobs.every(job)));
17
+ const jobsGet = jsonOperation((settings) => `${origin(settings)}/api/2.2/jobs/get`, {
18
+ job_id: { type: "integer", required: true, min: 1, max: Number.MAX_SAFE_INTEGER },
19
+ include_trigger_state: { type: "boolean", defaultTo: false }, page_token: pageToken
20
+ }, (value) => job(value) && pagination(value));
21
+
22
+ const databricksProvider = Object.freeze({
23
+ ...databricksDefinition,
24
+ oauth: (settings) => ({ issuer: `${origin(settings)}/oidc`, authorization_endpoint: `${origin(settings)}/oidc/v1/authorize`,
25
+ token_endpoint: `${origin(settings)}/oidc/v1/token` }),
26
+ apiOrigins: (settings) => [origin(settings)], checkOperation: "jobs.list", tokenRefreshLeewayMs: 40_000,
27
+ async normalizeTokenResponse(response) {
28
+ if (response.ok) {
29
+ let value;
30
+ try { value = await response.clone().json(); } catch { /* reported below */ }
31
+ if (!Number.isSafeInteger(value?.expires_in) || value.expires_in <= 40 ||
32
+ (value.scope !== undefined && (typeof value.scope !== "string" || !/^[^\s]+(?: [^\s]+)*$/u.test(value.scope))) ||
33
+ (value.refresh_token !== undefined && (typeof value.refresh_token !== "string" || !value.refresh_token.trim()))) {
34
+ throw new ConnectorError("connector_response_invalid", "Databricks returned an invalid token grant.", { statusCode: 502 });
35
+ }
36
+ }
37
+ return response;
38
+ },
39
+ operations: { "jobs.list": { ...jobsList, scopes: ["all-apis", "jobs"] }, "jobs.get": { ...jobsGet, scopes: ["all-apis", "jobs"] } },
40
+ async exchange(address, options, { fetchImpl }) {
41
+ const url = new URL(address);
42
+ // An empty jobs list is {}, but JSON null or an unparseable body is not a verified list.
43
+ const response = await fetchImpl(address, { ...options, credentials: "omit" });
44
+ if (!response.ok) throw Object.assign(new Error("Databricks request failed."), { status: response.status });
45
+ let result;
46
+ try { result = await response.json(); } catch { /* checked below */ }
47
+ if (response.status !== 200 || !object(result) || result.error_code || result.error ||
48
+ (url.pathname.endsWith("/list") && result?.jobs?.length > Number(url.searchParams.get("limit"))) ||
49
+ (url.pathname.endsWith("/get") && result?.job_id !== Number(url.searchParams.get("job_id")))) {
50
+ throw new ConnectorError("connector_response_invalid", "Databricks returned an unexpected job response.", { statusCode: 502 });
51
+ }
52
+ return result;
53
+ }
54
+ });
55
+ export { databricksProvider };
@@ -0,0 +1,95 @@
1
+ import { createSchema } from "json-rest-schema";
2
+ import { ConnectorError } from "@jskit-ai/connectors-core/server";
3
+ import { dbtSemanticLayerDefinition } from "../shared/dbt-semantic-layer.js";
4
+ import { graphqlOperation } from "./graphqlOperation.js";
5
+
6
+ const origin = (settings) => `https://${settings.host}`;
7
+ const endpoint = (settings) => `${origin(settings)}/api/graphql`;
8
+ const text = (value) => typeof value === "string" && value.length > 0;
9
+ const description = (value) => value === null || typeof value === "string";
10
+ const paging = {
11
+ pageNum: { type: "integer", min: 1, max: 2_147_483_647, defaultTo: 1 },
12
+ pageSize: { type: "integer", min: 1, max: 100, defaultTo: 20 },
13
+ search: { type: "string", maxLength: 512 }
14
+ };
15
+ const pageFields = "pageNum pageSize totalItems totalPages items { name description type }";
16
+ const validPage = (page, typed = true) => Array.isArray(page?.items) && page.items.length <= 100 &&
17
+ page.items.every((item) => text(item?.name) && description(item.description) && (!typed || text(item.type))) &&
18
+ Number.isSafeInteger(page.pageNum) && page.pageNum >= 1 && Number.isSafeInteger(page.pageSize) && page.pageSize >= 1 && page.pageSize <= 100 &&
19
+ Number.isSafeInteger(page.totalItems) && page.totalItems >= page.items.length && Number.isSafeInteger(page.totalPages) && page.totalPages >= 0;
20
+
21
+ function environmentOperation(query, fields, validateData) {
22
+ const operation = graphqlOperation(endpoint, query, fields, validateData);
23
+ return { ...operation, request(input, settings) {
24
+ const request = operation.request(input, settings);
25
+ request.body.variables.environmentId = settings.environmentId;
26
+ return request;
27
+ } };
28
+ }
29
+ const dbtSemanticLayerProvider = Object.freeze({
30
+ ...dbtSemanticLayerDefinition,
31
+ apiOrigins: (settings) => [origin(settings)],
32
+ apiKey: { headers: (key) => {
33
+ if (/[\s\p{Cc}]/u.test(key)) throw new ConnectorError("connector_binding_missing", "Use a dbt service token without spaces or control characters.");
34
+ return { Authorization: `Bearer ${key}` };
35
+ } },
36
+ checkOperation: "environment.read",
37
+ operations: {
38
+ "queries.create": environmentOperation("mutation ConnectorDbtQuery($environmentId: BigInt!, $metrics: [MetricInput!]!, $groupBy: [GroupByInput!], $where: [WhereInput!], $limit: Int!) { createQuery(environmentId: $environmentId, metrics: $metrics, groupBy: $groupBy, where: $where, limit: $limit) { queryId } }", {
39
+ metrics: { type: "array", required: true, validator: (value) => (value.length >= 1 && value.length <= 100) || "Choose between 1 and 100 metrics.",
40
+ items: { type: "object", schema: createSchema({ name: { type: "string", required: true, minLength: 1, maxLength: 512 }, alias: { type: "string", minLength: 1, maxLength: 512 } }) } },
41
+ groupBy: { type: "array", validator: (value) => value.length <= 100 || "Choose at most 100 dimensions.",
42
+ items: { type: "object", schema: createSchema({ name: { type: "string", required: true, minLength: 1, maxLength: 512 }, grain: { type: "string", enum: ["DAY", "WEEK", "MONTH", "QUARTER", "YEAR"] } }) } },
43
+ where: { type: "array", validator: (value) => value.length <= 20 || "Use at most 20 filters.",
44
+ items: { type: "object", schema: createSchema({ sql: { type: "string", required: true, minLength: 1, maxLength: 4000 } }) } },
45
+ limit: { type: "integer", min: 1, max: 10000, defaultTo: 100 }
46
+ }, (data) => text(data?.createQuery?.queryId)),
47
+ "queries.get": environmentOperation("query ConnectorDbtQueryResult($environmentId: BigInt!, $queryId: String!, $pageNum: Int!) { query(environmentId: $environmentId, queryId: $queryId, pageNum: $pageNum) { status error totalPages jsonResult(orient: TABLE, encoded: false) } }", {
48
+ queryId: { type: "string", required: true, minLength: 1, maxLength: 512 },
49
+ pageNum: { type: "integer", min: 1, max: 2_147_483_647, defaultTo: 1 }
50
+ }, (data) => text(data?.query?.status) && description(data.query.error) &&
51
+ (data.query.totalPages === null || (Number.isSafeInteger(data.query.totalPages) && data.query.totalPages >= 0)) &&
52
+ (data.query.jsonResult === null || (typeof data.query.jsonResult === "object" && Array.isArray(data.query.jsonResult?.data) && Array.isArray(data.query.jsonResult?.schema?.fields)))),
53
+ "environment.read": environmentOperation("query ConnectorDbtEnvironment($environmentId: BigInt!) { environmentInfo(environmentId: $environmentId) { dialect } }", {},
54
+ (data) => text(data?.environmentInfo?.dialect)),
55
+ "metrics.list": environmentOperation(`query ConnectorDbtMetrics($environmentId: BigInt!, $pageNum: Int!, $pageSize: Int!, $search: String) { metricsPaginated(environmentId: $environmentId, pageNum: $pageNum, pageSize: $pageSize, search: $search) { ${pageFields} } }`, paging,
56
+ (data) => validPage(data?.metricsPaginated)),
57
+ "dimensions.list": environmentOperation(`query ConnectorDbtDimensions($environmentId: BigInt!, $metrics: [MetricInput!]!, $pageNum: Int!, $pageSize: Int!, $search: String) { dimensionsPaginated(environmentId: $environmentId, metrics: $metrics, pageNum: $pageNum, pageSize: $pageSize, search: $search) { ${pageFields} } }`, {
58
+ ...paging, metrics: { type: "array", required: true, validator: (value) => (value.length >= 1 && value.length <= 100) || "Choose between 1 and 100 metrics.",
59
+ items: { type: "object", schema: createSchema({ name: { type: "string", required: true, minLength: 1, maxLength: 512 } }) } }
60
+ }, (data) => validPage(data?.dimensionsPaginated)),
61
+ "savedQueries.list": environmentOperation(`query ConnectorDbtSavedQueries($environmentId: BigInt!, $pageNum: Int!, $pageSize: Int!, $search: String) { savedQueriesPaginated(environmentId: $environmentId, pageNum: $pageNum, pageSize: $pageSize, search: $search) { pageNum pageSize totalItems totalPages items { name description } } }`, paging,
62
+ (data) => validPage(data?.savedQueriesPaginated, false))
63
+ },
64
+ async exchange(address, options, { request }) {
65
+ const result = await request(address, options);
66
+ if (Array.isArray(result?.errors) && result.errors.length > 0) {
67
+ throw new ConnectorError("connector_provider_failed", "dbt could not complete the requested operation. Check the environment and service token permissions.", { statusCode: 502 });
68
+ }
69
+ const queryResult = result?.data?.query;
70
+ if (queryResult) {
71
+ if (queryResult.status === "FAILED") {
72
+ throw new ConnectorError("connector_provider_failed", "dbt query failed. Inspect its status in dbt; do not automatically submit it again.", { statusCode: 502 });
73
+ }
74
+ if (queryResult.jsonResult !== null && queryResult.jsonResult !== undefined) {
75
+ if (typeof queryResult.jsonResult !== "string" || Buffer.byteLength(queryResult.jsonResult) > 5 * 1024 * 1024) {
76
+ throw new ConnectorError("connector_response_invalid", "dbt returned an invalid or oversized JSON result page.", { statusCode: 502 });
77
+ }
78
+ try { queryResult.jsonResult = JSON.parse(queryResult.jsonResult); }
79
+ catch { throw new ConnectorError("connector_response_invalid", "dbt returned an invalid JSON result page.", { statusCode: 502 }); }
80
+ if (!Array.isArray(queryResult.jsonResult?.data) || queryResult.jsonResult.data.length > 1024 || !Array.isArray(queryResult.jsonResult?.schema?.fields)) {
81
+ throw new ConnectorError("connector_response_invalid", "dbt returned an invalid JSON table.", { statusCode: 502 });
82
+ }
83
+ } else if (queryResult.status === "SUCCESSFUL") {
84
+ throw new ConnectorError("connector_response_invalid", "dbt completed without a JSON result table.", { statusCode: 502 });
85
+ }
86
+ }
87
+ const page = result?.data?.metricsPaginated || result?.data?.dimensionsPaginated || result?.data?.savedQueriesPaginated;
88
+ if (page && (page.pageNum !== options.body.variables.pageNum || page.pageSize !== options.body.variables.pageSize || page.items?.length > options.body.variables.pageSize)) {
89
+ throw new ConnectorError("connector_response_invalid", "dbt returned an unexpected metadata page.", { statusCode: 502 });
90
+ }
91
+ return result;
92
+ }
93
+ });
94
+
95
+ export { dbtSemanticLayerProvider };