@zackbart/connecta 0.28.1 → 0.30.0

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 (332) hide show
  1. package/CHANGELOG.md +855 -5
  2. package/README.md +125 -116
  3. package/bin/connecta.mjs +121 -139
  4. package/bin/meta-tool-names.d.mts +2 -0
  5. package/bin/meta-tool-names.mjs +9 -0
  6. package/bin/version.mjs +3 -0
  7. package/dist/access-tokens.js +53 -63
  8. package/dist/activity-request.js +8 -0
  9. package/dist/activity.d.ts +76 -42
  10. package/dist/activity.js +110 -42
  11. package/dist/argument-redaction.js +230 -0
  12. package/dist/auth/clerk-sdk/client.d.ts +65 -0
  13. package/dist/auth/clerk-sdk/client.js +5 -0
  14. package/dist/auth/clerk-sdk/generated.js +1845 -0
  15. package/dist/auth/clerk-transport.js +19 -0
  16. package/dist/auth/clerk.d.ts +17 -7
  17. package/dist/auth/clerk.js +189 -107
  18. package/dist/auth/cloudflare-access.js +25 -26
  19. package/dist/auth/downstream-client-metadata.d.ts +29 -0
  20. package/dist/auth/downstream-client-metadata.js +133 -0
  21. package/dist/auth/downstream-oauth.js +1584 -1580
  22. package/dist/auth/downstream-revocation.js +106 -0
  23. package/dist/auth/oauth-token-response.js +231 -0
  24. package/dist/auth/oauth-v2-migration.js +116 -0
  25. package/dist/auth/static-oauth.js +93 -71
  26. package/dist/auth-elicitation.js +288 -0
  27. package/dist/authorization-handoff.js +111 -0
  28. package/dist/branding.d.ts +8 -16
  29. package/dist/branding.js +26 -59
  30. package/dist/byte-read-response.js +39 -0
  31. package/dist/call-admission.js +12 -16
  32. package/dist/catalog-cache.js +739 -0
  33. package/dist/catalog-drift.d.ts +50 -15
  34. package/dist/catalog-drift.js +365 -128
  35. package/dist/catalog-fingerprint.js +2 -4
  36. package/dist/catalog-freshness.js +13 -0
  37. package/dist/catalog-limits.js +2 -2
  38. package/dist/catalog-service.js +409 -225
  39. package/dist/catalog.js +43 -84
  40. package/dist/concurrency.js +3 -4
  41. package/dist/config-defaults.d.ts +39 -0
  42. package/dist/config-defaults.js +46 -0
  43. package/dist/config-schema.d.ts +181 -0
  44. package/dist/config-schema.js +435 -0
  45. package/dist/config-value-sources.js +102 -0
  46. package/dist/config.d.ts +633 -0
  47. package/dist/config.js +508 -0
  48. package/dist/connector-access.d.ts +1 -0
  49. package/dist/connector-access.js +13 -15
  50. package/dist/connector-caller.d.ts +8 -0
  51. package/dist/connector-caller.js +15 -6
  52. package/dist/connector-scope.js +1 -1
  53. package/dist/connectors/api-connector.d.ts +59 -7
  54. package/dist/connectors/api-connector.js +106 -19
  55. package/dist/connectors/api.js +4 -1
  56. package/dist/connectors/guarded-fetch.d.ts +144 -0
  57. package/dist/connectors/guarded-fetch.js +87 -38
  58. package/dist/connectors/negotiation-cache.js +46 -0
  59. package/dist/connectors/option-shapes.js +27 -0
  60. package/dist/connectors/remote-mcp.d.ts +30 -4
  61. package/dist/connectors/remote-mcp.js +1774 -320
  62. package/dist/connectors/resource-uri.js +335 -0
  63. package/dist/credential-contract.d.ts +10 -0
  64. package/dist/credential-rules.js +1 -3
  65. package/dist/credential-url.js +108 -0
  66. package/dist/credentials.d.ts +6 -1
  67. package/dist/credentials.js +27 -18
  68. package/dist/d1.d.ts +130 -0
  69. package/dist/d1.js +290 -0
  70. package/dist/describe-config.d.ts +165 -0
  71. package/dist/describe-config.js +315 -0
  72. package/dist/described.js +106 -0
  73. package/dist/downstream-input-context.js +128 -0
  74. package/dist/downstream-input.js +325 -0
  75. package/dist/errors.d.ts +132 -31
  76. package/dist/errors.js +175 -87
  77. package/dist/execute.js +798 -318
  78. package/dist/executor-admission.d.ts +1 -0
  79. package/dist/executor-admission.js +11 -15
  80. package/dist/executor-contract.d.ts +20 -0
  81. package/dist/executor-contract.js +48 -0
  82. package/dist/executor-result.js +30 -13
  83. package/dist/executors/quickjs-child.js +15 -9
  84. package/dist/executors/quickjs-protocol.js +5 -6
  85. package/dist/executors/quickjs-runtime.js +152 -59
  86. package/dist/executors/quickjs.js +49 -39
  87. package/dist/favicon.js +1 -2
  88. package/dist/fix-prompt.js +1 -3
  89. package/dist/guest-runtime.js +238 -0
  90. package/dist/guest-types.d.ts +218 -0
  91. package/dist/guest-types.js +1 -0
  92. package/dist/html-security.js +24 -0
  93. package/dist/identity.js +1 -3
  94. package/dist/inbound-credential.js +52 -0
  95. package/dist/index.d.ts +24 -256
  96. package/dist/index.js +65 -478
  97. package/dist/invocation-auth.js +69 -0
  98. package/dist/invocation.js +262 -79
  99. package/dist/json-schema.d.ts +1 -1
  100. package/dist/mcp-client-context.js +35 -0
  101. package/dist/mcp-result.js +11 -7
  102. package/dist/meta-output.js +61 -0
  103. package/dist/meta-tool-names.d.ts +2 -0
  104. package/dist/meta-tool-names.js +3 -0
  105. package/dist/meta-tools.js +419 -272
  106. package/dist/module-contracts.d.ts +8 -23
  107. package/dist/node.d.ts +0 -2
  108. package/dist/node.js +2 -2
  109. package/dist/oauth-callback-outcome.js +1 -3
  110. package/dist/oauth-handoff.js +194 -0
  111. package/dist/oauth-sealing.js +20 -0
  112. package/dist/operator-record.d.ts +137 -0
  113. package/dist/operator-record.js +420 -0
  114. package/dist/operator-ui/assets.js +31 -0
  115. package/dist/operator-ui/contract.d.ts +72 -0
  116. package/dist/operator-ui/contract.js +1 -0
  117. package/dist/operator-ui/fix-prompts.d.ts +18 -0
  118. package/dist/operator-ui/generated.js +5 -4
  119. package/dist/operator-ui/model.d.ts +12 -87
  120. package/dist/operator-ui/model.js +2 -9
  121. package/dist/operator-ui/setup-commands.js +1 -3
  122. package/dist/operator-ui/view.d.ts +323 -0
  123. package/dist/operator-ui/view.js +62 -189
  124. package/dist/page-styles.js +2 -3
  125. package/dist/program-result.js +5 -0
  126. package/dist/program-source.js +14 -2
  127. package/dist/{exempt-writes.js → program-writes.js} +60 -19
  128. package/dist/provider.d.ts +142 -0
  129. package/dist/provider.js +187 -0
  130. package/dist/providers/_shared/google/result-size.js +57 -0
  131. package/dist/providers/_shared/google/service-account.d.ts +58 -0
  132. package/dist/providers/_shared/google/service-account.js +500 -0
  133. package/dist/providers/_shared/google/workspace.d.ts +276 -0
  134. package/dist/providers/_shared/google/workspace.js +518 -0
  135. package/dist/providers/_shared/request-token.js +83 -0
  136. package/dist/providers/{basecamp.d.ts → basecamp/index.d.ts} +6 -28
  137. package/dist/providers/basecamp/index.js +997 -0
  138. package/dist/providers/basecamp/skill.generated.js +9 -0
  139. package/dist/providers/{breeze.d.ts → breeze/index.d.ts} +2 -2
  140. package/dist/providers/{breeze.js → breeze/index.js} +184 -140
  141. package/dist/providers/breeze/skill.generated.js +9 -0
  142. package/dist/providers/{ccb.d.ts → ccb/index.d.ts} +2 -2
  143. package/dist/providers/{ccb.js → ccb/index.js} +271 -116
  144. package/dist/providers/ccb/skill.generated.js +9 -0
  145. package/dist/providers/{cloudflare.d.ts → cloudflare/index.d.ts} +8 -7
  146. package/dist/providers/{cloudflare.js → cloudflare/index.js} +171 -738
  147. package/dist/providers/cloudflare/skill.generated.js +10 -0
  148. package/dist/providers/docs/index.d.ts +54 -0
  149. package/dist/providers/docs/index.js +1237 -0
  150. package/dist/providers/docs/skill.generated.js +9 -0
  151. package/dist/providers/drive/index.d.ts +53 -0
  152. package/dist/providers/drive/index.js +1523 -0
  153. package/dist/providers/drive/skill.generated.js +9 -0
  154. package/dist/providers/forms/index.d.ts +54 -0
  155. package/dist/providers/forms/index.js +1217 -0
  156. package/dist/providers/forms/skill.generated.js +11 -0
  157. package/dist/providers/github/auth.d.ts +32 -0
  158. package/dist/providers/github/auth.js +361 -0
  159. package/dist/providers/github/hosted.js +218 -0
  160. package/dist/providers/github/index.d.ts +13 -0
  161. package/dist/providers/github/index.js +140 -0
  162. package/dist/providers/github/key.js +100 -0
  163. package/dist/providers/github/rest.js +321 -0
  164. package/dist/providers/github/scope.d.ts +29 -0
  165. package/dist/providers/github/scope.js +124 -0
  166. package/dist/providers/github/skill.generated.js +8 -0
  167. package/dist/providers/gmail/index.d.ts +51 -0
  168. package/dist/providers/gmail/index.js +1436 -0
  169. package/dist/providers/gmail/skill.generated.js +9 -0
  170. package/dist/providers/infisical/index.d.ts +7 -0
  171. package/dist/providers/infisical/index.js +713 -0
  172. package/dist/providers/infisical/skill.generated.js +8 -0
  173. package/dist/providers/{linear.d.ts → linear/index.d.ts} +11 -14
  174. package/dist/providers/linear/index.js +352 -0
  175. package/dist/providers/linear/skill.generated.js +8 -0
  176. package/dist/providers/{mixpanel.d.ts → mixpanel/index.d.ts} +5 -10
  177. package/dist/providers/mixpanel/index.js +416 -0
  178. package/dist/providers/mixpanel/skill.generated.js +9 -0
  179. package/dist/providers/{notion.d.ts → notion/index.d.ts} +8 -8
  180. package/dist/providers/{notion.js → notion/index.js} +226 -239
  181. package/dist/providers/notion/skill.generated.js +10 -0
  182. package/dist/providers/{overflow.d.ts → overflow/index.d.ts} +2 -2
  183. package/dist/providers/{overflow.js → overflow/index.js} +74 -111
  184. package/dist/providers/overflow/skill.generated.js +8 -0
  185. package/dist/providers/{planning-center.d.ts → planning-center/index.d.ts} +2 -2
  186. package/dist/providers/{planning-center.js → planning-center/index.js} +264 -104
  187. package/dist/providers/planning-center/skill.generated.js +10 -0
  188. package/dist/providers/{revenuecat.d.ts → revenuecat/index.d.ts} +5 -10
  189. package/dist/providers/revenuecat/index.js +557 -0
  190. package/dist/providers/revenuecat/skill.generated.js +11 -0
  191. package/dist/providers/sheets/index.d.ts +54 -0
  192. package/dist/providers/sheets/index.js +1237 -0
  193. package/dist/providers/sheets/skill.generated.js +10 -0
  194. package/dist/providers/slides/index.d.ts +53 -0
  195. package/dist/providers/slides/index.js +2645 -0
  196. package/dist/providers/slides/skill.generated.js +9 -0
  197. package/dist/providers/{stripe.d.ts → stripe/index.d.ts} +5 -12
  198. package/dist/providers/stripe/index.js +266 -0
  199. package/dist/providers/stripe/skill.generated.js +11 -0
  200. package/dist/providers/{tithely.d.ts → tithely/index.d.ts} +2 -2
  201. package/dist/providers/{tithely.js → tithely/index.js} +60 -91
  202. package/dist/providers/tithely/skill.generated.js +8 -0
  203. package/dist/providers/{vercel.d.ts → vercel/index.d.ts} +8 -8
  204. package/dist/providers/vercel/index.js +1038 -0
  205. package/dist/providers/vercel/mcp-ownership.js +212 -0
  206. package/dist/providers/vercel/skill.generated.js +11 -0
  207. package/dist/registry.d.ts +110 -171
  208. package/dist/registry.js +588 -927
  209. package/dist/request-state.js +21 -0
  210. package/dist/resource-read.js +103 -0
  211. package/dist/result-shapes.js +17 -49
  212. package/dist/retired-toolkits.js +1 -1
  213. package/dist/routes/access-tokens.js +3 -6
  214. package/dist/routes/activity-context.js +21 -0
  215. package/dist/routes/activity-disclosure.js +40 -0
  216. package/dist/routes/activity.js +59 -21
  217. package/dist/routes/actor-labels.js +3 -50
  218. package/dist/routes/connect.js +221 -0
  219. package/dist/routes/credentials.js +15 -27
  220. package/dist/routes/mcp.js +365 -130
  221. package/dist/routes/oauth-client-metadata.js +15 -0
  222. package/dist/routes/oauth-management.js +45 -107
  223. package/dist/routes/oauth.js +110 -57
  224. package/dist/routes/operator.js +3 -3
  225. package/dist/routes/shared.d.ts +25 -64
  226. package/dist/routes/shared.js +160 -80
  227. package/dist/routes/skills.js +30 -0
  228. package/dist/routes/ui-config.js +225 -0
  229. package/dist/routes/ui-data.js +40 -35
  230. package/dist/routes/ui.js +66 -56
  231. package/dist/runtime/admission.js +4 -2
  232. package/dist/runtime/call-admission.js +3 -1
  233. package/dist/runtime/connector-scope.js +1 -4
  234. package/dist/runtime/run.js +10 -39
  235. package/dist/runtime/services.js +12 -117
  236. package/dist/runtime/shared-read.js +1 -3
  237. package/dist/runtime/storage.js +8 -1
  238. package/dist/sent-secrets.d.ts +93 -0
  239. package/dist/sent-secrets.js +1013 -0
  240. package/dist/server.js +28 -71
  241. package/dist/skills.js +416 -174
  242. package/dist/sqlite.d.ts +48 -0
  243. package/dist/sqlite.js +181 -0
  244. package/dist/storage/keys.js +443 -0
  245. package/dist/storage/memory.js +16 -4
  246. package/dist/storage/sql.d.ts +66 -0
  247. package/dist/storage/sql.js +543 -0
  248. package/dist/timeout.js +4 -2
  249. package/dist/tool-name.js +17 -0
  250. package/dist/tool-safety.d.ts +9 -35
  251. package/dist/tool-safety.js +20 -40
  252. package/dist/types.d.ts +336 -56
  253. package/dist/types.js +0 -1
  254. package/dist/ui.d.ts +7 -19
  255. package/dist/ui.js +76 -90
  256. package/dist/url-safety.js +3 -6
  257. package/dist/usage-guide.js +146 -0
  258. package/dist/validate.d.ts +7 -0
  259. package/dist/validate.js +309 -47
  260. package/dist/version.d.ts +1 -7
  261. package/dist/version.js +3 -7
  262. package/dist/worker.d.ts +11 -0
  263. package/dist/worker.js +435 -0
  264. package/documentation/README.md +63 -0
  265. package/documentation/architecture.md +696 -231
  266. package/documentation/auth.md +1069 -546
  267. package/documentation/code-mode.md +662 -356
  268. package/documentation/deploying.md +146 -0
  269. package/documentation/integrating.md +159 -0
  270. package/documentation/meta-tools.md +570 -216
  271. package/documentation/operating.md +135 -0
  272. package/documentation/operator-tests.md +97 -0
  273. package/documentation/operator-ui.md +132 -0
  274. package/documentation/provider-migration-0.29.md +65 -0
  275. package/examples/worker/AGENTS.md +34 -12
  276. package/examples/worker/README.md +293 -255
  277. package/examples/worker/kv-to-d1.wrangler.jsonc +26 -0
  278. package/examples/worker/scripts/copy-kv-to-d1.mjs +162 -0
  279. package/examples/worker/src/connecta.config.ts +151 -0
  280. package/examples/worker/src/index.ts +14 -197
  281. package/examples/worker/wrangler.jsonc +15 -35
  282. package/package.json +126 -54
  283. package/templates/node/.dockerignore +1 -2
  284. package/templates/node/.env.example +20 -21
  285. package/templates/node/AGENTS.md +21 -17
  286. package/templates/node/Dockerfile +3 -5
  287. package/templates/node/README.md +140 -58
  288. package/templates/node/docker-compose.yml +11 -12
  289. package/templates/node/package.json +4 -2
  290. package/templates/node/src/connecta.config.ts +119 -0
  291. package/templates/node/src/index.ts +9 -154
  292. package/templates/node/src/provision-token.ts +17 -0
  293. package/AGENTS.md +0 -170
  294. package/dist/artifacts/connector.d.ts +0 -58
  295. package/dist/artifacts/connector.js +0 -730
  296. package/dist/artifacts/document.js +0 -86
  297. package/dist/artifacts/guide.js +0 -140
  298. package/dist/artifacts/html-scan.js +0 -329
  299. package/dist/artifacts/json.js +0 -78
  300. package/dist/artifacts/kv-store.d.ts +0 -20
  301. package/dist/artifacts/kv-store.js +0 -140
  302. package/dist/artifacts/markdown.js +0 -478
  303. package/dist/artifacts/module.d.ts +0 -37
  304. package/dist/artifacts/module.js +0 -66
  305. package/dist/artifacts/operations.d.ts +0 -197
  306. package/dist/artifacts/operations.js +0 -1002
  307. package/dist/artifacts/refresh.d.ts +0 -55
  308. package/dist/artifacts/refresh.js +0 -236
  309. package/dist/artifacts/routes.js +0 -206
  310. package/dist/artifacts/types.d.ts +0 -214
  311. package/dist/artifacts/types.js +0 -22
  312. package/dist/artifacts/validate.d.ts +0 -62
  313. package/dist/artifacts/validate.js +0 -651
  314. package/dist/artifacts.d.ts +0 -6
  315. package/dist/artifacts.js +0 -9
  316. package/dist/auth/bearer.d.ts +0 -11
  317. package/dist/auth/bearer.js +0 -50
  318. package/dist/providers/basecamp.js +0 -445
  319. package/dist/providers/linear.js +0 -217
  320. package/dist/providers/mixpanel.js +0 -247
  321. package/dist/providers/revenuecat.js +0 -317
  322. package/dist/providers/stripe.js +0 -253
  323. package/dist/providers/vercel.js +0 -1253
  324. package/dist/storage/file.d.ts +0 -15
  325. package/dist/storage/file.js +0 -455
  326. package/ethos.md +0 -93
  327. package/examples/worker/src/cloudflare-kv.ts +0 -43
  328. package/examples/worker/src/d1-activity-row.ts +0 -155
  329. package/examples/worker/src/d1-activity.ts +0 -147
  330. package/examples/worker/src/d1-storage.ts +0 -138
  331. package/examples/worker/src/r2-artifact-blobs.ts +0 -29
  332. package/templates/node/src/file-activity.ts +0 -182
@@ -1,8 +1,12 @@
1
- import { apiConnector as api, defined } from "../connectors/api-connector.js";
2
- import { remoteMcp } from "../connectors/remote-mcp.js";
3
- import { vettedCatalog, withVettedCatalog } from "../catalog-drift.js";
4
- import { guardedFetch, retryAfterMs, } from "../connectors/guarded-fetch.js";
5
- import { ConnectorCallError } from "../errors.js";
1
+ import { skill } from "./skill.generated.js";
2
+ import { apiConnector as api } from "../../connectors/api-connector.js";
3
+ import { remoteMcp } from "../../connectors/remote-mcp.js";
4
+ import { reviewedCatalog } from "../../catalog-drift.js";
5
+ import { guardedFetch, retryAfterMs } from "../../connectors/guarded-fetch.js";
6
+ import { ConnectorCallError } from "../../errors.js";
7
+ import { keys, optionsOf, variants } from "../../config-schema.js";
8
+ import { PROVIDER_COMMON } from "../../connectors/option-shapes.js";
9
+ import { defineProvider } from "../../provider.js";
6
10
  /** Notion's REST origin. Every tool below speaks to exactly this host. */
7
11
  export const NOTION_API_BASE_URL = "https://api.notion.com";
8
12
  /** Notion's official hosted MCP endpoint. */
@@ -23,7 +27,7 @@ const DEFAULT_PAGE_SIZE = 25;
23
27
  /** Notion's cap on `children` per append, and on blocks per page create. */
24
28
  const MAX_CHILDREN_PER_REQUEST = 100;
25
29
  /**
26
- * Ceiling on the fetches one `get_page_content` walk may spend. Call admission
30
+ * Ceiling on the fetches one `integration_get_page_content` walk may spend. Call admission
27
31
  * meters tool calls, not the requests inside them, so a deep `depth` would
28
32
  * otherwise drain the budget invisibly; the walk stops here and reports
29
33
  * `truncated: true` instead.
@@ -86,13 +90,15 @@ function notionFailure(status, body, headers) {
86
90
  }
87
91
  if (status === 529) {
88
92
  // Notion documents 529 alongside 429: back off and respect Retry-After.
89
- return new ConnectorCallError("unavailable", `${labelled} Notion is overloaded; retry after the reported window.`, { retryAfterMs: retryAfter ?? 5_000 });
93
+ return new ConnectorCallError("unavailable", `${labelled} Notion is overloaded; retry after the reported window.`, {
94
+ retryAfterMs: retryAfter ?? 5_000,
95
+ });
90
96
  }
91
97
  if (status === 401) {
92
98
  return new ConnectorCallError("auth_required", `${labelled} The Notion integration token is missing or invalid. Call authorize_connector for recovery options. When available, an operator can set a valid token in this connection in the operator UI.`);
93
99
  }
94
100
  if (status === 403) {
95
- return new ConnectorCallError("connector_call_failed", `${labelled} The token is valid but this integration is not allowed to perform this operation. An operator must enable the matching capability on the Notion integration (comment capabilities are off by default) or share the object with it. Re-authorizing will not help.`, { retryable: false });
101
+ return new ConnectorCallError("provider_permission_denied", `${labelled} The token is valid but this integration is not allowed to perform this operation. An operator must enable the matching capability on the Notion integration (comment capabilities are off by default) or share the object with it. Re-authorizing will not help.`, { retryable: false });
96
102
  }
97
103
  if (status === 404) {
98
104
  return new ConnectorCallError("connector_call_failed", `${labelled} Notion returns this both for an object that does not exist and for one that exists but has not been shared with this integration — do not treat it as proof of deletion. Confirm the id, then confirm the page or database is shared with the integration in Notion.`, { retryable: false });
@@ -141,9 +147,7 @@ const send = guardedFetch({
141
147
  async function notionRequest(ctx, request) {
142
148
  return await send(request, ctx, async (response) => {
143
149
  const parsed = await response.jsonResult();
144
- const payload = "value" in parsed
145
- ? parsed.value
146
- : undefined;
150
+ const payload = "value" in parsed ? parsed.value : undefined;
147
151
  if (!response.ok) {
148
152
  throw notionFailure(response.status, payload, response.headers);
149
153
  }
@@ -167,9 +171,7 @@ async function notionRequest(ctx, request) {
167
171
  function plainText(value) {
168
172
  if (!Array.isArray(value))
169
173
  return "";
170
- return value
171
- .map((run) => (typeof run?.plain_text === "string" ? run.plain_text : ""))
172
- .join("");
174
+ return value.map((run) => (typeof run?.plain_text === "string" ? run.plain_text : "")).join("");
173
175
  }
174
176
  /** Wrap a plain string as the single-run rich-text array Notion expects. */
175
177
  function richText(value) {
@@ -349,7 +351,7 @@ function projectPage(page, select) {
349
351
  /**
350
352
  * Identity fields only, no properties. A 25-result search across a populated
351
353
  * database would otherwise drag back hundreds of flattened values for results
352
- * the agent is about to discard; `get_page` fetches them for the one that hit.
354
+ * the agent is about to discard; `integration_get_page` fetches them for the one that hit.
353
355
  */
354
356
  function projectSearchHit(hit) {
355
357
  if (hit?.object === "data_source") {
@@ -492,7 +494,7 @@ function projectSchemaProperty(property) {
492
494
  // Shared schema fragments
493
495
  // ---------------------------------------------------------------------------
494
496
  // The compact renderer inlines every property description, so these three
495
- // shared strings are paid for once per tool that uses them. `query_data_source`
497
+ // shared strings are paid for once per tool that uses them. `integration_query_data_source`
496
498
  // carries all three plus a filter grammar and rendered past the 1,024-byte
497
499
  // compact budget until they were cut to the fact each one actually adds
498
500
  // ([#342](https://github.com/zackbart/connecta/issues/342)); the long versions
@@ -568,12 +570,12 @@ const PAGE_OUTPUT_SCHEMA = {
568
570
  name: { type: "string" },
569
571
  id: {
570
572
  type: ["string", "null"],
571
- description: "Pass as property_id to get_page_property.",
573
+ description: "Pass as property_id to integration_get_page_property.",
572
574
  },
573
575
  },
574
576
  required: ["name", "id"],
575
577
  },
576
- description: "Properties Notion truncated at 25 entries. Each carries the property_id get_page_property needs to read the complete value.",
578
+ description: "Properties Notion truncated at 25 entries. Each carries the property_id integration_get_page_property needs to read the complete value.",
577
579
  },
578
580
  },
579
581
  required: ["id", "title", "properties"],
@@ -656,9 +658,7 @@ function pagination(args, defaultPageSize) {
656
658
  function requireExactlyOne(provided, hint) {
657
659
  const present = provided.filter(([, value]) => value !== undefined && value !== null && value !== "");
658
660
  if (present.length !== 1) {
659
- throw new ConnectorCallError("invalid_args", `Provide exactly one of ${provided
660
- .map(([name]) => name)
661
- .join(", ")}. ${hint}`);
661
+ throw new ConnectorCallError("invalid_args", `Provide exactly one of ${provided.map(([name]) => name).join(", ")}. ${hint}`);
662
662
  }
663
663
  return present[0];
664
664
  }
@@ -666,8 +666,8 @@ function buildTools(defaultPageSize) {
666
666
  return [
667
667
  // ---------------------------------------------------------------- reads
668
668
  {
669
- name: "search",
670
- description: "Find pages and data sources by title across everything shared with this integration. Never searches page content — use query_data_source for rows inside a database. Returns identity fields only.",
669
+ name: "integration_search",
670
+ description: "Find pages and data sources by title across everything shared with this integration. Never searches page content — use integration_query_data_source for rows inside a database. Returns identity fields only.",
671
671
  annotations: { readOnlyHint: true },
672
672
  inputSchema: {
673
673
  type: "object",
@@ -736,8 +736,8 @@ function buildTools(defaultPageSize) {
736
736
  },
737
737
  },
738
738
  {
739
- name: "get_page",
740
- description: "Fetch one page's metadata and flattened property values by id. Returns the page's properties, not its body content — use get_page_content for the blocks.",
739
+ name: "integration_get_page",
740
+ description: "Fetch one page's metadata and flattened property values by id. Returns the page's properties, not its body content — use integration_get_page_content for the blocks.",
741
741
  annotations: { readOnlyHint: true },
742
742
  inputSchema: {
743
743
  type: "object",
@@ -764,7 +764,7 @@ function buildTools(defaultPageSize) {
764
764
  },
765
765
  },
766
766
  {
767
- name: "get_page_content",
767
+ name: "integration_get_page_content",
768
768
  // `raw: true` returns the requested level exactly as Notion sent it and
769
769
  // does not walk nested children, so `depth` is ignored alongside it: a
770
770
  // raw read of a deep page yields one level.
@@ -847,8 +847,8 @@ function buildTools(defaultPageSize) {
847
847
  },
848
848
  },
849
849
  {
850
- name: "get_page_property",
851
- description: "Fetch one page property completely, paginating past the 25-entry limit that get_page reports in truncated_properties. Use for title, rich_text, relation, and people properties — the four Notion paginates.",
850
+ name: "integration_get_page_property",
851
+ description: "Fetch one page property completely, paginating past the 25-entry limit that integration_get_page reports in truncated_properties. Use for title, rich_text, relation, and people properties — the four Notion paginates.",
852
852
  annotations: { readOnlyHint: true },
853
853
  inputSchema: {
854
854
  type: "object",
@@ -856,7 +856,7 @@ function buildTools(defaultPageSize) {
856
856
  page_id: { type: "string", description: "Notion page id." },
857
857
  property_id: {
858
858
  type: "string",
859
- description: "The property's id, from get_data_source_schema or get_page's truncated_properties — not its name.",
859
+ description: "The property's id, from integration_get_data_source_schema or integration_get_page's truncated_properties — not its name.",
860
860
  },
861
861
  page_size: PAGE_SIZE_PROPERTY,
862
862
  start_cursor: START_CURSOR_PROPERTY,
@@ -871,7 +871,7 @@ function buildTools(defaultPageSize) {
871
871
  properties: {
872
872
  type: {
873
873
  type: ["string", "null"],
874
- description: "The property's own type, never the \"property_item\" envelope.",
874
+ description: 'The property\'s own type, never the "property_item" envelope.',
875
875
  },
876
876
  value: { description: "Flattened value for a single-value property." },
877
877
  results: {
@@ -906,8 +906,8 @@ function buildTools(defaultPageSize) {
906
906
  },
907
907
  },
908
908
  {
909
- name: "get_database",
910
- description: "Fetch a database container and list the data sources inside it. A database id cannot be queried directly — start here to get the data_source_id that query_data_source and get_data_source_schema need.",
909
+ name: "integration_get_database",
910
+ description: "Fetch a database container and list the data sources inside it. A database id cannot be queried directly — start here to get the data_source_id that integration_query_data_source and integration_get_data_source_schema need.",
911
911
  annotations: { readOnlyHint: true },
912
912
  inputSchema: {
913
913
  type: "object",
@@ -958,7 +958,7 @@ function buildTools(defaultPageSize) {
958
958
  },
959
959
  },
960
960
  {
961
- name: "get_data_source_schema",
961
+ name: "integration_get_data_source_schema",
962
962
  description: "List a data source's properties with their ids, types, and select/status options. Read this before filtering, sorting, or writing — filters and property names that do not match the schema exactly are rejected.",
963
963
  annotations: { readOnlyHint: true },
964
964
  inputSchema: {
@@ -966,7 +966,7 @@ function buildTools(defaultPageSize) {
966
966
  properties: {
967
967
  data_source_id: {
968
968
  type: "string",
969
- description: "Data source id from get_database or search, not a database id.",
969
+ description: "Data source id from integration_get_database or integration_search, not a database id.",
970
970
  },
971
971
  raw: RAW_PROPERTY,
972
972
  },
@@ -981,7 +981,7 @@ function buildTools(defaultPageSize) {
981
981
  database_id: { type: ["string", "null"] },
982
982
  title_property: {
983
983
  type: ["string", "null"],
984
- description: "Name of the title-typed property. create_page needs this to title a row.",
984
+ description: "Name of the title-typed property. integration_create_page needs this to title a row.",
985
985
  },
986
986
  properties: {
987
987
  type: "object",
@@ -1014,7 +1014,7 @@ function buildTools(defaultPageSize) {
1014
1014
  },
1015
1015
  },
1016
1016
  {
1017
- name: "query_data_source",
1017
+ name: "integration_query_data_source",
1018
1018
  description: "List rows in a data source with optional filtering and sorting, returning each row's properties already flattened. Requires a data_source_id, never a database_id. Narrow with the properties argument to keep results small.",
1019
1019
  annotations: { readOnlyHint: true },
1020
1020
  inputSchema: {
@@ -1022,11 +1022,11 @@ function buildTools(defaultPageSize) {
1022
1022
  properties: {
1023
1023
  data_source_id: {
1024
1024
  type: "string",
1025
- description: "Data source id from get_database or search.",
1025
+ description: "Data source id from integration_get_database or integration_search.",
1026
1026
  },
1027
1027
  filter: {
1028
1028
  type: "object",
1029
- description: 'Notion filter object, passed through unchanged. Single condition: {"property":"Status","status":{"equals":"Done"}}. Compound: {"and":[...]} or {"or":[...]}. Property names must match get_data_source_schema exactly.',
1029
+ description: 'Notion filter object, passed through unchanged. Single condition: {"property":"Status","status":{"equals":"Done"}}. Compound: {"and":[...]} or {"or":[...]}. Property names must match integration_get_data_source_schema exactly.',
1030
1030
  },
1031
1031
  sorts: {
1032
1032
  type: "array",
@@ -1078,7 +1078,7 @@ function buildTools(defaultPageSize) {
1078
1078
  },
1079
1079
  },
1080
1080
  {
1081
- name: "list_users",
1081
+ name: "integration_list_users",
1082
1082
  description: "List workspace users and bots with their ids, for assigning people properties or attributing edits. Requires the integration's user-information capability.",
1083
1083
  annotations: { readOnlyHint: true },
1084
1084
  inputSchema: {
@@ -1101,7 +1101,7 @@ function buildTools(defaultPageSize) {
1101
1101
  },
1102
1102
  },
1103
1103
  {
1104
- name: "get_self",
1104
+ name: "integration_get_self",
1105
1105
  description: "Identify the integration this connector authenticates as, and the workspace it is installed in. The cheapest way to confirm the token works before a longer sequence.",
1106
1106
  annotations: { readOnlyHint: true },
1107
1107
  inputSchema: {
@@ -1132,7 +1132,7 @@ function buildTools(defaultPageSize) {
1132
1132
  },
1133
1133
  },
1134
1134
  {
1135
- name: "list_comments",
1135
+ name: "integration_list_comments",
1136
1136
  description: "List unresolved comments on a page or block as plain text with their discussion ids. Requires the integration's read-comment capability, which is off by default.",
1137
1137
  annotations: { readOnlyHint: true },
1138
1138
  inputSchema: {
@@ -1166,7 +1166,7 @@ function buildTools(defaultPageSize) {
1166
1166
  },
1167
1167
  // --------------------------------------------------------------- writes
1168
1168
  {
1169
- name: "create_page",
1169
+ name: "integration_create_page",
1170
1170
  description: "Create a page, either as a child of another page or as a row in a data source. Notion has no idempotency key: a retried create makes a second page, so confirm with search before repeating one.",
1171
1171
  annotations: { readOnlyHint: false },
1172
1172
  inputSchema: {
@@ -1189,11 +1189,11 @@ function buildTools(defaultPageSize) {
1189
1189
  title: { type: "string", description: "Plain-text title." },
1190
1190
  title_property: {
1191
1191
  type: "string",
1192
- description: 'Name of the title-typed property, from get_data_source_schema. Required in practice for a data-source parent, whose title column is rarely called "title". Defaults to "title", which is the only valid key under a page parent.',
1192
+ description: 'Name of the title-typed property, from integration_get_data_source_schema. Required in practice for a data-source parent, whose title column is rarely called "title". Defaults to "title", which is the only valid key under a page parent.',
1193
1193
  },
1194
1194
  properties: {
1195
1195
  type: "object",
1196
- description: "Additional Notion property values, keyed by property name and in Notion's own wrapped form, e.g. {\"Status\":{\"status\":{\"name\":\"Todo\"}}}. Read get_data_source_schema first.",
1196
+ description: 'Additional Notion property values, keyed by property name and in Notion\'s own wrapped form, e.g. {"Status":{"status":{"name":"Todo"}}}. Read integration_get_data_source_schema first.',
1197
1197
  },
1198
1198
  markdown: {
1199
1199
  type: "string",
@@ -1214,7 +1214,7 @@ function buildTools(defaultPageSize) {
1214
1214
  const [parentKey, parentValue] = requireExactlyOne([
1215
1215
  ["parent_page_id", args.parent_page_id],
1216
1216
  ["parent_data_source_id", args.parent_data_source_id],
1217
- ], "A page needs exactly one parent, and a data source is addressed by its data_source_id from get_database — never by a database_id.");
1217
+ ], "A page needs exactly one parent, and a data source is addressed by its data_source_id from integration_get_database — never by a database_id.");
1218
1218
  if (args.markdown !== undefined && args.children !== undefined) {
1219
1219
  throw new ConnectorCallError("invalid_args", "Provide either markdown or children for the page body, not both.");
1220
1220
  }
@@ -1245,7 +1245,7 @@ function buildTools(defaultPageSize) {
1245
1245
  },
1246
1246
  },
1247
1247
  {
1248
- name: "append_blocks",
1248
+ name: "integration_append_blocks",
1249
1249
  description: "Append content to the end of a page or block, or insert it at a chosen position. Appending only adds: existing blocks are never moved or replaced, and an appended block cannot be relocated later through the API.",
1250
1250
  annotations: { readOnlyHint: false },
1251
1251
  inputSchema: {
@@ -1329,7 +1329,7 @@ function buildTools(defaultPageSize) {
1329
1329
  },
1330
1330
  },
1331
1331
  {
1332
- name: "update_page_properties",
1332
+ name: "integration_update_page_properties",
1333
1333
  // Deliberately no locking, templates, or `erase_content` (#409): locking
1334
1334
  // is coordination state, templates finish asynchronously, and
1335
1335
  // `erase_content` permanently deletes every child block. None belongs
@@ -1347,11 +1347,11 @@ function buildTools(defaultPageSize) {
1347
1347
  },
1348
1348
  title_property: {
1349
1349
  type: "string",
1350
- description: 'Name of the title-typed property, from get_data_source_schema. Defaults to "title".',
1350
+ description: 'Name of the title-typed property, from integration_get_data_source_schema. Defaults to "title".',
1351
1351
  },
1352
1352
  properties: {
1353
1353
  type: "object",
1354
- description: "Notion property values keyed by property name, in Notion's wrapped form. Read get_data_source_schema for names, types, and valid option names.",
1354
+ description: "Notion property values keyed by property name, in Notion's wrapped form. Read integration_get_data_source_schema for names, types, and valid option names.",
1355
1355
  },
1356
1356
  icon: { type: "string", description: "Replacement emoji icon." },
1357
1357
  },
@@ -1383,7 +1383,7 @@ function buildTools(defaultPageSize) {
1383
1383
  },
1384
1384
  },
1385
1385
  {
1386
- name: "trash_page",
1386
+ name: "integration_trash_page",
1387
1387
  description: "Move a page to the workspace trash, or restore one from it. Trashing hides the page and its content from reads; it is reversible through this same tool with restore: true.",
1388
1388
  annotations: { readOnlyHint: false, destructiveHint: true },
1389
1389
  inputSchema: {
@@ -1421,7 +1421,7 @@ function buildTools(defaultPageSize) {
1421
1421
  },
1422
1422
  },
1423
1423
  {
1424
- name: "add_comment",
1424
+ name: "integration_add_comment",
1425
1425
  description: "Start a comment discussion on a page, or reply to an existing discussion. Requires the integration's insert-comment capability, which is off by default.",
1426
1426
  annotations: { readOnlyHint: false },
1427
1427
  inputSchema: {
@@ -1433,7 +1433,7 @@ function buildTools(defaultPageSize) {
1433
1433
  },
1434
1434
  discussion_id: {
1435
1435
  type: "string",
1436
- description: "Reply to this existing discussion, from list_comments.",
1436
+ description: "Reply to this existing discussion, from integration_list_comments.",
1437
1437
  },
1438
1438
  text: { type: "string", description: "Comment body as plain text." },
1439
1439
  },
@@ -1451,9 +1451,7 @@ function buildTools(defaultPageSize) {
1451
1451
  }
1452
1452
  const body = {
1453
1453
  rich_text: richText(args.text),
1454
- ...(kind === "page_id"
1455
- ? { parent: { type: "page_id", page_id: value } }
1456
- : { discussion_id: value }),
1454
+ ...(kind === "page_id" ? { parent: { type: "page_id", page_id: value } } : { discussion_id: value }),
1457
1455
  };
1458
1456
  return projectComment(await notionRequest(ctx, {
1459
1457
  method: "POST",
@@ -1470,205 +1468,182 @@ function buildTools(defaultPageSize) {
1470
1468
  /**
1471
1469
  * Marked `required` for one reason: a Notion database is a container, and the
1472
1470
  * rows and schema live in a data source inside it. The id in a database URL is a
1473
- * database id, and passing it to `query_data_source` or `create_page` fails —
1471
+ * database id, and passing it to `integration_query_data_source` or `integration_create_page` fails —
1474
1472
  * a trap no input schema can teach, so the guide has to.
1475
1473
  */
1476
1474
  function apiUsageGuide(purpose, instructions) {
1477
1475
  const accountInstructions = instructions?.trim();
1478
1476
  return `# Notion usage
1479
1477
 
1480
- Workspace purpose: ${purpose}
1481
-
1482
- ## Databases contain data sources
1483
-
1484
- A Notion database is a container; the rows and the schema live in a *data
1485
- source* inside it. The two ids are not interchangeable and Notion rejects the
1486
- wrong one.
1487
-
1488
- - The id in a Notion database URL is a **database id**.
1489
- - \`get_database\` turns it into the \`data_sources\` list — usually one entry.
1490
- - \`get_data_source_schema\` and \`query_data_source\` take that
1491
- **data_source_id**, and so does \`create_page\` when adding a row.
1492
-
1493
- So the sequence for "find rows in this database" is \`get_database\` →
1494
- \`get_data_source_schema\` → \`query_data_source\`. \`search\` returns data
1495
- sources directly and skips the first step.
1496
-
1497
- ## Property quirks that break writes
1498
-
1499
- - Property names in filters, sorts, and writes must match the schema exactly,
1500
- including case. Read \`get_data_source_schema\` before composing one.
1501
- - \`select\` and \`status\` writes must use an existing option name; inventing
1502
- one fails. The schema lists the valid options.
1503
- - Writes **replace** a property. Sending one item to a \`multi_select\` or
1504
- \`relation\` drops the others, so send the complete intended value.
1505
- - \`rollup\`, \`formula\`, \`unique_id\`, and the created/edited fields are
1506
- computed. They cannot be written.
1507
- - A page's title column is rarely called "title" — pass \`title_property\` from
1508
- the schema when creating or updating a row.
1509
- - Notion truncates \`title\`, \`rich_text\`, \`relation\`, and \`people\` at 25
1510
- entries. \`get_page\` reports each one in \`truncated_properties\` as
1511
- \`{ name, id }\`; pass that \`id\` as \`property_id\` to
1512
- \`get_page_property\` for the complete value.
1513
-
1514
- ## Reading page content
1515
-
1516
- \`get_page\` returns properties. \`get_page_content\` returns the body as flat
1517
- blocks. Nested blocks (toggles, list children, table rows) need \`depth\`, and
1518
- each level multiplies requests — a deep read stops at an internal ceiling and
1519
- reports \`truncated: true\` rather than spending the whole rate-limit budget.
1520
-
1521
- ## Appending is append-only
1522
-
1523
- \`append_blocks\` adds children and nothing else. It cannot move, reorder, or
1524
- replace an existing block, and a block appended through the API can never be
1525
- relocated by it afterwards. Get the position right the first time with
1526
- \`position\` and \`after_block_id\`. Notion caps one call at 100 blocks.
1527
-
1528
- ## Lean by default, raw on request
1529
-
1530
- Every read projects Notion's payload down to ids, plain text, and flattened
1531
- property values. Where the dropped detail can matter — \`search\`, \`get_page\`,
1532
- \`get_page_content\`, \`get_page_property\`, \`get_data_source_schema\`,
1533
- \`query_data_source\`, \`list_comments\` — pass \`raw: true\` to get Notion's
1534
- untouched response instead. It is much larger; reach for it only when a
1535
- specific field is missing. Narrow \`query_data_source\` and \`get_page\` with
1536
- \`properties\` instead whenever the goal is fewer fields, not more.
1537
-
1538
- \`get_page_content\` with \`raw: true\` returns one level exactly as Notion
1539
- sent it and does not walk nested children, so \`depth\` is ignored alongside
1540
- it. A block type this projection does not model keeps its payload under
1541
- \`raw\` on the block itself, so nothing silently flattens to an empty string.
1542
-
1543
- ## Failures worth reading carefully
1544
-
1545
- - **404** means "no such object" *or* "not shared with this integration", and
1546
- Notion will not say which. Never conclude a page was deleted from it; check
1547
- that the page is shared with the integration in Notion.
1548
- - **403** is not an expired token. The integration is missing a capability
1549
- (comment capabilities are off by default) or the object was never shared.
1550
- Re-authorizing cannot fix it; an operator must change it in Notion.
1551
- - **429** carries a retry window. Notion allows roughly three requests per
1552
- second per integration, so wait it out rather than retrying immediately.
1553
-
1554
- ## No escape hatch
1555
-
1556
- This connection has no guarded raw-REST tool, deliberately. Notion's public
1557
- API is small and slow-moving enough for the named surface to cover it, so
1558
- there is no \`notion_api_*\` to reach for — an operation absent from the tool
1559
- list is absent from this connection, not hidden behind a generic call.
1560
-
1561
- ## Writes and pagination
1562
-
1563
- - Notion has **no idempotency key**. A retried \`create_page\` or
1564
- \`add_comment\` creates a duplicate. Confirm with \`search\` before repeating
1565
- a write that may have partially succeeded.
1566
- - List tools take \`page_size\` (max 100) and return \`has_more\` with
1567
- \`next_cursor\`. Pass a cursor back verbatim — it is opaque and must never be
1568
- parsed or constructed. Follow pages inside \`execute_code\` and reduce there.
1569
- ${accountInstructions
1570
- ? `\n## Workspace instructions\n\n${accountInstructions}\n`
1571
- : ""}`;
1478
+ Workspace purpose: ${purpose}${skill.fragments.guide_0}${accountInstructions ? `\n## ${skill.instructionsHeading}\n\n${accountInstructions}\n` : ""}`;
1572
1479
  }
1573
- /** Release-reviewed Notion MCP inventory and safety verdicts. */
1574
- export const NOTION_MCP_VETTED_CATALOG = vettedCatalog({
1575
- reads: new Set([
1576
- "notion-search",
1577
- "notion-search-skills",
1578
- "notion-fetch",
1579
- "notion-download-attachment",
1580
- "notion-query-data-sources",
1581
- "notion-query-meeting-notes",
1582
- "notion-search-agents",
1583
- "notion-list-agents",
1584
- "notion-query-sessions",
1585
- "notion-search-sessions",
1586
- "notion-get-session-status",
1587
- "notion-wait-session",
1588
- "notion-list-session-events",
1589
- "notion-read-session-event",
1590
- "notion-get-comments",
1591
- "notion-get-teams",
1592
- "notion-get-users",
1593
- "notion-get-async-task",
1594
- ]),
1595
- writes: new Map([
1596
- ["notion-create-file-upload", "additive"],
1597
- ["notion-create-attachment", "additive"],
1598
- ["notion-create-pages", "additive"],
1599
- ["notion-duplicate-page", "additive"],
1600
- ["notion-create-database", "additive"],
1601
- ["notion-create-folder", "additive"],
1602
- ["notion-create-view", "additive"],
1603
- ["notion-spawn-session", "additive"],
1604
- ["notion-send-message-to-session", "additive"],
1605
- ["notion-create-comment", "additive"],
1606
- ["notion-update-page", "destructive"],
1607
- ["notion-convert-page-to-skill", "destructive"],
1608
- ["notion-move-pages", "destructive"],
1609
- ["notion-update-data-source", "destructive"],
1610
- ["notion-update-view", "destructive"],
1611
- ["notion-stop-session", "destructive"],
1612
- ]),
1613
- });
1480
+ /**
1481
+ * Reviewed in #705's provider audit against https://developers.notion.com/guides/mcp/mcp-supported-tools.
1482
+ * Retains the release-reviewed inventory, including names absent from today's
1483
+ * public reference. Live annotations were not reverified without credentials.
1484
+ * No schema digest is asserted without a captured schema to review.
1485
+ */
1486
+ const NOTION_MCP_CLASSIFICATION = {
1487
+ tools: {
1488
+ "notion-search": { "verdict": "read", "reason": "Retrieves Notion information without changing vendor state." },
1489
+ "notion-search-skills": {
1490
+ "verdict": "read",
1491
+ "reason": "Retrieves Notion skills information without changing vendor state.",
1492
+ },
1493
+ "notion-fetch": {
1494
+ "verdict": "read",
1495
+ "reason": "Retrieves Notion fetch information without changing vendor state.",
1496
+ },
1497
+ "notion-download-attachment": {
1498
+ "verdict": "read",
1499
+ "reason": "Reads attachment content; creating uploads and attachments uses separate writes.",
1500
+ },
1501
+ "notion-query-data-sources": {
1502
+ "verdict": "read",
1503
+ "reason": "Retrieves Notion query data sources information without changing vendor state.",
1504
+ },
1505
+ "notion-query-meeting-notes": {
1506
+ "verdict": "read",
1507
+ "reason": "Retrieves Notion query meeting notes information without changing vendor state.",
1508
+ },
1509
+ "notion-search-agents": {
1510
+ "verdict": "read",
1511
+ "reason": "Retrieves Notion agents information without changing vendor state.",
1512
+ },
1513
+ "notion-list-agents": {
1514
+ "verdict": "read",
1515
+ "reason": "Retrieves Notion agents information without changing vendor state.",
1516
+ },
1517
+ "notion-query-sessions": {
1518
+ "verdict": "read",
1519
+ "reason": "Retrieves Notion query sessions information without changing vendor state.",
1520
+ },
1521
+ "notion-search-sessions": {
1522
+ "verdict": "read",
1523
+ "reason": "Retrieves Notion sessions information without changing vendor state.",
1524
+ },
1525
+ "notion-get-session-status": {
1526
+ "verdict": "read",
1527
+ "reason": "Retrieves Notion session status information without changing vendor state.",
1528
+ },
1529
+ "notion-wait-session": {
1530
+ "verdict": "read",
1531
+ "reason": "Waits for existing session state; it does not start or message a session.",
1532
+ },
1533
+ "notion-list-session-events": {
1534
+ "verdict": "read",
1535
+ "reason": "Retrieves Notion session events information without changing vendor state.",
1536
+ },
1537
+ "notion-read-session-event": {
1538
+ "verdict": "read",
1539
+ "reason": "Retrieves Notion read session event information without changing vendor state.",
1540
+ },
1541
+ "notion-get-comments": {
1542
+ "verdict": "read",
1543
+ "reason": "Retrieves Notion comments information without changing vendor state.",
1544
+ },
1545
+ "notion-get-teams": {
1546
+ "verdict": "read",
1547
+ "reason": "Retrieves Notion teams information without changing vendor state.",
1548
+ },
1549
+ "notion-get-users": {
1550
+ "verdict": "read",
1551
+ "reason": "Retrieves Notion users information without changing vendor state.",
1552
+ },
1553
+ "notion-get-async-task": {
1554
+ "verdict": "read",
1555
+ "reason": "Retrieves Notion async task information without changing vendor state.",
1556
+ },
1557
+ "notion-create-file-upload": { "verdict": "write", "reason": "Allocates upload state for a workspace file." },
1558
+ "notion-create-attachment": {
1559
+ "verdict": "write",
1560
+ "reason": "create attachment creates or appends Notion state; it has side effects.",
1561
+ },
1562
+ "notion-create-pages": {
1563
+ "verdict": "write",
1564
+ "reason": "create pages creates or appends Notion state; it has side effects.",
1565
+ },
1566
+ "notion-duplicate-page": {
1567
+ "verdict": "write",
1568
+ "reason": "duplicate page creates or appends Notion state; it has side effects.",
1569
+ },
1570
+ "notion-create-database": {
1571
+ "verdict": "write",
1572
+ "reason": "create database creates or appends Notion state; it has side effects.",
1573
+ },
1574
+ "notion-create-folder": {
1575
+ "verdict": "write",
1576
+ "reason": "create folder creates or appends Notion state; it has side effects.",
1577
+ },
1578
+ "notion-create-view": {
1579
+ "verdict": "write",
1580
+ "reason": "create view creates or appends Notion state; it has side effects.",
1581
+ },
1582
+ "notion-spawn-session": {
1583
+ "verdict": "write",
1584
+ "reason": "Starts asynchronous agent work and creates session state.",
1585
+ },
1586
+ "notion-send-message-to-session": {
1587
+ "verdict": "write",
1588
+ "reason": "Appends a message to a running session and may trigger further work.",
1589
+ },
1590
+ "notion-create-comment": {
1591
+ "verdict": "write",
1592
+ "reason": "create comment creates or appends Notion state; it has side effects.",
1593
+ },
1594
+ "notion-update-page": {
1595
+ "verdict": "destructive",
1596
+ "reason": "update page changes existing Notion state or removes it.",
1597
+ },
1598
+ "notion-convert-page-to-skill": {
1599
+ "verdict": "destructive",
1600
+ "reason": "Changes the role and content of an existing workspace page.",
1601
+ },
1602
+ "notion-move-pages": {
1603
+ "verdict": "destructive",
1604
+ "reason": "move pages changes existing Notion state or removes it.",
1605
+ },
1606
+ "notion-update-data-source": {
1607
+ "verdict": "destructive",
1608
+ "reason": "update data source changes existing Notion state or removes it.",
1609
+ },
1610
+ "notion-update-view": {
1611
+ "verdict": "destructive",
1612
+ "reason": "update view changes existing Notion state or removes it.",
1613
+ },
1614
+ "notion-stop-session": { "verdict": "destructive", "reason": "Stops an existing session and interrupts its work." },
1615
+ },
1616
+ };
1614
1617
  function mcpUsageGuide(purpose, instructions) {
1615
1618
  const accountInstructions = instructions?.trim();
1616
- return `# Notion MCP usage
1617
-
1618
- Official MCP interface: tool names, descriptions, argument schemas, and result
1619
- schemas come from Notion's live server. Connecta preserves that catalog and
1620
- only fills in release-reviewed safety annotations when Notion leaves them out.
1621
-
1622
- Workspace purpose: ${purpose}
1623
-
1624
- - Discover the live catalog before assuming a tool exists. Notion can gate
1625
- tools by workspace, account, client, and rollout independently of Connecta.
1626
- - Start broad discovery with \`notion-search\`, then use \`notion-fetch\` on
1627
- the exact page, database, data source, or object before changing it.
1628
- - Use the live input schema as the contract. Notion owns these MCP schemas;
1629
- the REST schemas in Connecta's API interface do not apply to MCP tools with
1630
- similar names.
1631
- - Session and agent tools can launch asynchronous work. Read session state and
1632
- events before sending another message, waiting, or stopping a session.
1633
- - File uploads and attachment tools create durable workspace state. Keep
1634
- downloads inside the requested task and do not expose signed attachment URLs.
1635
- - An \`auth_required\` failure means this connector's OAuth grant is missing or
1636
- expired. Run \`authorize_connector\` for this connector id, then retry.
1637
- ${accountInstructions
1638
- ? `\n## Workspace instructions\n\n${accountInstructions}\n`
1639
- : ""}`;
1619
+ return `${skill.fragments.guide_1}${purpose}${skill.fragments.guide_2}${accountInstructions ? `\n## ${skill.instructionsHeading}\n\n${accountInstructions}\n` : ""}`;
1640
1620
  }
1641
- function notionMcp(id, purpose, options) {
1621
+ function notionMcp(id, purpose, options, provider) {
1642
1622
  const connector = remoteMcp(id, {
1643
1623
  url: NOTION_MCP_ENDPOINT,
1644
- ...defined({
1645
- authScope: options.authScope,
1646
- callAdmission: options.callAdmission,
1647
- maxResultBytes: options.maxResultBytes,
1648
- }),
1624
+ ...provider.connectorOptions,
1649
1625
  title: options.title ?? "Notion (MCP)",
1650
1626
  description: `Notion's official hosted MCP interface: ${purpose}`,
1651
1627
  auth: { type: "oauth" },
1652
1628
  requireHttps: true,
1629
+ classify: provider.classify,
1653
1630
  usageGuide: {
1654
1631
  content: mcpUsageGuide(purpose, options.instructions),
1655
1632
  summary: "Official MCP. Live Notion schemas, object discovery, sessions, agents, attachments, and OAuth ownership.",
1656
1633
  required: true,
1657
1634
  },
1658
1635
  });
1659
- return withVettedCatalog(connector, NOTION_MCP_VETTED_CATALOG);
1636
+ return connector;
1660
1637
  }
1661
1638
  function notionApi(id, purpose, options) {
1662
1639
  const defaultPageSize = options.defaultPageSize ?? DEFAULT_PAGE_SIZE;
1663
- if (!Number.isInteger(defaultPageSize) ||
1664
- defaultPageSize < 1 ||
1665
- defaultPageSize > MAX_PAGE_SIZE) {
1640
+ if (!Number.isInteger(defaultPageSize) || defaultPageSize < 1 || defaultPageSize > MAX_PAGE_SIZE) {
1666
1641
  throw new Error(`notion() defaultPageSize must be a whole number between 1 and ${MAX_PAGE_SIZE}.`);
1667
1642
  }
1668
1643
  return api(id, {
1669
1644
  ...(options.authScope ? { authScope: options.authScope } : {}),
1670
- title: options.title ?? "Notion",
1671
- description: `Notion workspace — ${purpose}`,
1645
+ title: options.title ?? "Notion integration",
1646
+ description: `Notion internal integration: ${purpose}`,
1672
1647
  credential: {
1673
1648
  label: options.credentialLabel ?? "Notion integration token",
1674
1649
  description: "Internal integration token from notion.so/profile/integrations. Every page or database the agent should reach must also be shared with that integration, and its capabilities decide which tools succeed — comment capabilities are off by default.",
@@ -1685,31 +1660,43 @@ function notionApi(id, purpose, options) {
1685
1660
  catch (error) {
1686
1661
  return {
1687
1662
  ok: false,
1688
- message: error instanceof ConnectorCallError
1689
- ? error.message
1690
- : "Notion rejected the token.",
1663
+ message: error instanceof ConnectorCallError ? error.message : "Notion rejected the token.",
1691
1664
  };
1692
1665
  }
1693
1666
  },
1694
1667
  callAdmission: NOTION_ADMISSION,
1695
1668
  usageGuide: {
1696
1669
  content: apiUsageGuide(purpose, options.instructions),
1697
- summary: "Database-to-data-source lookup, property write rules, lean-vs-raw results, and Notion's overloaded 403/404.",
1670
+ summary: "Internal integration identity. Exact REST queries, JSON blocks, properties, paging and bot-scoped content writes.",
1698
1671
  required: true,
1699
1672
  },
1700
1673
  tools: buildTools(defaultPageSize),
1701
- ...(options.maxResultBytes !== undefined
1702
- ? { maxResultBytes: options.maxResultBytes }
1703
- : {}),
1674
+ ...(options.maxResultBytes !== undefined ? { maxResultBytes: options.maxResultBytes } : {}),
1704
1675
  });
1705
1676
  }
1677
+ /** The closed options notion() accepts; see `assertKnownOptions`. */
1678
+ const NOTION_OPTIONS = variants("surface", {
1679
+ api: optionsOf()({
1680
+ ...keys("title", "authScope", "purpose", "instructions", "maxResultBytes"),
1681
+ ...keys("surface", "credentialLabel", "defaultPageSize"),
1682
+ }).shape,
1683
+ mcp: optionsOf()({ ...PROVIDER_COMMON, ...keys("surface") }).shape,
1684
+ }, "mcp");
1706
1685
  /** A maintained Notion connection using the selected provider interface. */
1707
- export function notion(id, options) {
1686
+ export const notion = defineProvider({
1687
+ name: "notion",
1688
+ title: "Notion",
1689
+ kind: "composed",
1690
+ readme: "Notion",
1691
+ bundle: { "baselineGzip": 145300, "maxGzip": 205300 },
1692
+ skill,
1693
+ options: NOTION_OPTIONS,
1694
+ classify: NOTION_MCP_CLASSIFICATION,
1695
+ create: notionConnector,
1696
+ });
1697
+ function notionConnector(id, options, provider) {
1708
1698
  const purpose = options.purpose.trim();
1709
- if (!purpose) {
1710
- throw new Error("notion() requires a non-empty workspace purpose.");
1711
- }
1712
- return options.surface === "mcp"
1713
- ? notionMcp(id, purpose, options)
1714
- : notionApi(id, purpose, options);
1699
+ return options.surface === "api" ? notionApi(id, purpose, options) : notionMcp(id, purpose, options, provider);
1715
1700
  }
1701
+ /** @deprecated Read `notion.definition.classify` instead. Kept for existing imports. */
1702
+ export const NOTION_MCP_VETTED_CATALOG = reviewedCatalog(notion.definition.classify, 'defineProvider("notion")');