@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,3 +1,4 @@
1
+ import { skill } from "./skill.generated.js";
1
2
  /**
2
3
  * Planning Center Online as a hand-written `api()` provider: named tools for
3
4
  * the jobs a church staff agent actually does, plus two hatches split by safety
@@ -19,16 +20,19 @@
19
20
  * Drift. Planning Center publishes an OpenAPI 3.1 document per product and
20
21
  * dated version (`/<app>/v2/open_api/<version>`) and a credential-free
21
22
  * documentation graph listing every version (`/<app>/v2/documentation`).
22
- * `scripts/drift/planning-center-endpoints.json` records each endpoint a named
23
+ * `src/providers/planning-center/drift.json` records each endpoint a named
23
24
  * tool calls, digested at the version pinned below, and
24
25
  * `npm run providers:check -- --provider planning-center` reports a touched
25
26
  * contract that moved and a newly published version of any pinned product.
26
27
  * Both are evidence only: no tool here is generated from either document.
27
28
  */
28
- import { apiConnector as api } from "../connectors/api-connector.js";
29
- import { guardedFetch, retryAfterMs, } from "../connectors/guarded-fetch.js";
30
- import { ConnectorCallError } from "../errors.js";
31
- import { CONNECTA_VERSION } from "../version.js";
29
+ import { apiConnector as api } from "../../connectors/api-connector.js";
30
+ import { guardedFetch, retryAfterMs, } from "../../connectors/guarded-fetch.js";
31
+ import { ConnectorCallError } from "../../errors.js";
32
+ import { CONNECTA_VERSION } from "../../version.js";
33
+ import { keys, optionsOf } from "../../config-schema.js";
34
+ import { PROVIDER_COMMON } from "../../connectors/option-shapes.js";
35
+ import { asProviderFactory } from "../../provider.js";
32
36
  /** Planning Center's REST origin. Override only for a proxy or test double. */
33
37
  export const PLANNING_CENTER_API_BASE_URL = "https://api.planningcenteronline.com";
34
38
  /** The products behind `/<app>/v2`, in the order their guides list them. */
@@ -148,9 +152,7 @@ const PERSONAL_ACCESS_TOKEN = {
148
152
  },
149
153
  };
150
154
  function asRecord(value) {
151
- return value && typeof value === "object" && !Array.isArray(value)
152
- ? value
153
- : {};
155
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
154
156
  }
155
157
  function asArray(value) {
156
158
  return Array.isArray(value) ? value : [];
@@ -267,9 +269,7 @@ function errorDetail(payload, status) {
267
269
  if (text)
268
270
  parts.push(`${text}${at}`);
269
271
  }
270
- return parts.length > 0
271
- ? `Planning Center: ${parts.join("; ")}`
272
- : `Planning Center returned HTTP ${status}.`;
272
+ return parts.length > 0 ? `Planning Center: ${parts.join("; ")}` : `Planning Center returned HTTP ${status}.`;
273
273
  }
274
274
  /**
275
275
  * Map by the caller's next move.
@@ -293,7 +293,7 @@ function planningCenterFailure(status, headers, payload) {
293
293
  return new ConnectorCallError("auth_required", `${detail} The personal access token was rejected: revoked, mistyped, or an Application ID and Secret from different tokens. An operator must replace it.`);
294
294
  }
295
295
  if (status === 403) {
296
- return new ConnectorCallError("connector_call_failed", `${detail} The token's Planning Center user lacks permission for this product or record. Permissions belong to that user; an operator must raise them or use another user's token. Retrying will not help.`, { retryable: false });
296
+ return new ConnectorCallError("provider_permission_denied", `${detail} The token's Planning Center user lacks permission for this product or record. Permissions belong to that user; an operator must raise them or use another user's token. Retrying will not help.`, { retryable: false });
297
297
  }
298
298
  if (status === 404) {
299
299
  return new ConnectorCallError("not_found", `${detail} Confirm the id with its list tool. A person id that used to resolve was probably merged: look it up in /people/v2/person_mergers with where[person_to_remove_id].`);
@@ -471,8 +471,20 @@ const PERSON_SCHEMA = record({
471
471
  addresses: ARRAY,
472
472
  households: ARRAY,
473
473
  });
474
- const EMAIL_SCHEMA = record({ id: STRING, address: STRING, location: NULLABLE_STRING, primary: BOOLEAN, person_id: STRING });
475
- const PHONE_SCHEMA = record({ id: STRING, number: STRING, location: NULLABLE_STRING, primary: BOOLEAN, person_id: STRING });
474
+ const EMAIL_SCHEMA = record({
475
+ id: STRING,
476
+ address: STRING,
477
+ location: NULLABLE_STRING,
478
+ primary: BOOLEAN,
479
+ person_id: STRING,
480
+ });
481
+ const PHONE_SCHEMA = record({
482
+ id: STRING,
483
+ number: STRING,
484
+ location: NULLABLE_STRING,
485
+ primary: BOOLEAN,
486
+ person_id: STRING,
487
+ });
476
488
  const CARD_SCHEMA = record({
477
489
  id: STRING,
478
490
  stage: STRING,
@@ -560,7 +572,10 @@ function personSummary(person, index) {
560
572
  };
561
573
  }
562
574
  function emailRecord(email) {
563
- return compact({ ...pick(email, ["address", "location", "primary"]), person_id: relId(email, "person") ?? undefined });
575
+ return compact({
576
+ ...pick(email, ["address", "location", "primary"]),
577
+ person_id: relId(email, "person") ?? undefined,
578
+ });
564
579
  }
565
580
  function phoneRecord(phone) {
566
581
  return compact({ ...pick(phone, ["number", "location", "primary"]), person_id: relId(phone, "person") ?? undefined });
@@ -738,10 +753,21 @@ const PERSON_WRITE_PROPERTIES = {
738
753
  lastName: { type: "string", minLength: 1, description: "Last name." },
739
754
  nickname: { type: ["string", "null"], description: "Nickname; null clears it." },
740
755
  middleName: { type: ["string", "null"], description: "Middle name; null clears it." },
741
- birthdate: { type: ["string", "null"], pattern: "^\\d{4}-\\d{2}-\\d{2}$", description: "Birthdate, YYYY-MM-DD; null clears it." },
742
- anniversary: { type: ["string", "null"], pattern: "^\\d{4}-\\d{2}-\\d{2}$", description: "Anniversary, YYYY-MM-DD; null clears it." },
756
+ birthdate: {
757
+ type: ["string", "null"],
758
+ pattern: "^\\d{4}-\\d{2}-\\d{2}$",
759
+ description: "Birthdate, YYYY-MM-DD; null clears it.",
760
+ },
761
+ anniversary: {
762
+ type: ["string", "null"],
763
+ pattern: "^\\d{4}-\\d{2}-\\d{2}$",
764
+ description: "Anniversary, YYYY-MM-DD; null clears it.",
765
+ },
743
766
  gender: { type: ["string", "null"], description: "Gender as the organization records it, e.g. Male or Female." },
744
- membership: { type: ["string", "null"], description: "Membership type, one of the organization's own values such as Member or Visitor." },
767
+ membership: {
768
+ type: ["string", "null"],
769
+ description: "Membership type, one of the organization's own values such as Member or Visitor.",
770
+ },
745
771
  child: { type: "boolean", description: "Whether the person is a child." },
746
772
  primaryCampusId: { type: "string", pattern: "^[0-9]+$", description: "Campus id from /people/v2/campuses." },
747
773
  };
@@ -760,15 +786,7 @@ function personAttributes(args) {
760
786
  status: args["status"],
761
787
  };
762
788
  }
763
- const WORKFLOW_CARD_ACTIONS = [
764
- "promote",
765
- "go_back",
766
- "skip_step",
767
- "snooze",
768
- "unsnooze",
769
- "remove",
770
- "restore",
771
- ];
789
+ const WORKFLOW_CARD_ACTIONS = ["promote", "go_back", "skip_step", "snooze", "unsnooze", "remove", "restore"];
772
790
  function tools(call, defaultPageSize) {
773
791
  const perPage = (args) => args["perPage"] ?? defaultPageSize;
774
792
  const list = (spec) => listTool(call, defaultPageSize, spec);
@@ -909,7 +927,11 @@ function tools(call, defaultPageSize) {
909
927
  inputSchema: input({
910
928
  personId: PERSON_ID,
911
929
  ...PERSON_WRITE_PROPERTIES,
912
- status: { type: "string", enum: ["active", "inactive"], description: "inactive archives the profile; active restores it." },
930
+ status: {
931
+ type: "string",
932
+ enum: ["active", "inactive"],
933
+ description: "inactive archives the profile; active restores it.",
934
+ },
913
935
  }, ["personId"]),
914
936
  outputSchema: PERSON_SUMMARY_SCHEMA,
915
937
  handler: async (args, ctx) => {
@@ -953,7 +975,11 @@ function tools(call, defaultPageSize) {
953
975
  inputSchema: input({
954
976
  personId: PERSON_ID,
955
977
  number: { type: "string", minLength: 3, description: "Phone number as dialed; Planning Center formats it." },
956
- location: { type: "string", minLength: 1, description: "Label such as Mobile, Home, or Work. Defaults to Mobile." },
978
+ location: {
979
+ type: "string",
980
+ minLength: 1,
981
+ description: "Label such as Mobile, Home, or Work. Defaults to Mobile.",
982
+ },
957
983
  primary: { type: "boolean", description: "Make this the primary number." },
958
984
  }, ["personId", "number"]),
959
985
  outputSchema: PHONE_SCHEMA,
@@ -987,7 +1013,15 @@ function tools(call, defaultPageSize) {
987
1013
  },
988
1014
  path: () => "/people/v2/lists",
989
1015
  query: (args) => ({ "where[name]": args["name"], order: args["order"] }),
990
- project: (row) => pick(row, ["name", "description", "total_people", "status", "refreshed_at", "auto_refresh", "automations_active"]),
1016
+ project: (row) => pick(row, [
1017
+ "name",
1018
+ "description",
1019
+ "total_people",
1020
+ "status",
1021
+ "refreshed_at",
1022
+ "auto_refresh",
1023
+ "automations_active",
1024
+ ]),
991
1025
  }),
992
1026
  list({
993
1027
  name: "list_list_people",
@@ -1058,7 +1092,11 @@ function tools(call, defaultPageSize) {
1058
1092
  item: CARD_SCHEMA,
1059
1093
  properties: {
1060
1094
  workflowId: idProperty("Workflow id from list_workflows."),
1061
- stage: { type: "string", minLength: 1, description: "Card stage as Planning Center reports it, e.g. ready, snoozed, completed, removed." },
1095
+ stage: {
1096
+ type: "string",
1097
+ minLength: 1,
1098
+ description: "Card stage as Planning Center reports it, e.g. ready, snoozed, completed, removed.",
1099
+ },
1062
1100
  stepId: idProperty("Only cards on this step (list_workflows steps[].id)."),
1063
1101
  assigneeId: idProperty("Only cards assigned to this person id."),
1064
1102
  overdue: { type: "boolean", description: "Only overdue (true) or on-time (false) cards." },
@@ -1104,7 +1142,12 @@ function tools(call, defaultPageSize) {
1104
1142
  enum: [...WORKFLOW_CARD_ACTIONS],
1105
1143
  description: "promote completes the step; skip_step moves on without completing it; go_back returns to the previous step.",
1106
1144
  },
1107
- snoozeDays: { type: "integer", minimum: 1, maximum: 365, description: "Days to snooze; required with action snooze and refused otherwise." },
1145
+ snoozeDays: {
1146
+ type: "integer",
1147
+ minimum: 1,
1148
+ maximum: 365,
1149
+ description: "Days to snooze; required with action snooze and refused otherwise.",
1150
+ },
1108
1151
  }, ["personId", "cardId", "action"]),
1109
1152
  outputSchema: record({ cardId: STRING, action: STRING, applied: BOOLEAN }, ["cardId", "action", "applied"]),
1110
1153
  handler: async (args, ctx) => {
@@ -1254,7 +1297,13 @@ function tools(call, defaultPageSize) {
1254
1297
  name: "list_service_types",
1255
1298
  description: "List Services service types — the recurring services such as Sunday Morning. Supplies serviceTypeId for plans and teams.",
1256
1299
  key: "serviceTypes",
1257
- item: record({ id: STRING, name: STRING, frequency: NULLABLE_STRING, sequence: NULLABLE_INTEGER, parent_id: NULLABLE_STRING }),
1300
+ item: record({
1301
+ id: STRING,
1302
+ name: STRING,
1303
+ frequency: NULLABLE_STRING,
1304
+ sequence: NULLABLE_INTEGER,
1305
+ parent_id: NULLABLE_STRING,
1306
+ }),
1258
1307
  path: () => "/services/v2/service_types",
1259
1308
  query: () => ({ order: "sequence" }),
1260
1309
  project: (row) => ({ ...pick(row, ["name", "frequency", "sequence"]), parent_id: relId(row, "parent") ?? null }),
@@ -1276,7 +1325,11 @@ function tools(call, defaultPageSize) {
1276
1325
  }),
1277
1326
  properties: {
1278
1327
  serviceTypeId: SERVICE_TYPE_ID,
1279
- when: { type: "string", enum: ["future", "past", "all"], description: "future (default) sorts soonest first; past sorts most recent first." },
1328
+ when: {
1329
+ type: "string",
1330
+ enum: ["future", "past", "all"],
1331
+ description: "future (default) sorts soonest first; past sorts most recent first.",
1332
+ },
1280
1333
  },
1281
1334
  required: ["serviceTypeId"],
1282
1335
  path: (args) => `/services/v2/service_types/${segment(args["serviceTypeId"])}/plans`,
@@ -1302,7 +1355,10 @@ function tools(call, defaultPageSize) {
1302
1355
  name: "get_plan",
1303
1356
  description: "Get one plan with its times, order of service, and scheduled team in a single call. Flags a section past 100 rows rather than dropping it silently.",
1304
1357
  annotations: READ,
1305
- inputSchema: input({ serviceTypeId: SERVICE_TYPE_ID, planId: PLAN_ID, raw: RAW_PROPERTY }, ["serviceTypeId", "planId"]),
1358
+ inputSchema: input({ serviceTypeId: SERVICE_TYPE_ID, planId: PLAN_ID, raw: RAW_PROPERTY }, [
1359
+ "serviceTypeId",
1360
+ "planId",
1361
+ ]),
1306
1362
  outputSchema: {
1307
1363
  type: "object",
1308
1364
  properties: {
@@ -1340,12 +1396,18 @@ function tools(call, defaultPageSize) {
1340
1396
  const teamIndex = indexIncluded(teamDocument);
1341
1397
  return {
1342
1398
  plan: {
1343
- ...pick(plan, ["title", "series_title", "dates", "sort_date", "total_length", "needed_positions_count", "planning_center_url"]),
1399
+ ...pick(plan, [
1400
+ "title",
1401
+ "series_title",
1402
+ "dates",
1403
+ "sort_date",
1404
+ "total_length",
1405
+ "needed_positions_count",
1406
+ "planning_center_url",
1407
+ ]),
1344
1408
  times: relatedMany(plan, "plan_times", planIndex).map((time) => pick(time, ["name", "time_type", "starts_at", "ends_at"])),
1345
1409
  },
1346
- items: items
1347
- .map(itemRecord)
1348
- .sort((a, b) => Number(a["sequence"] ?? 0) - Number(b["sequence"] ?? 0)),
1410
+ items: items.map(itemRecord).sort((a, b) => Number(a["sequence"] ?? 0) - Number(b["sequence"] ?? 0)),
1349
1411
  teamMembers: members.map((member) => planPersonRecord(member, teamIndex)),
1350
1412
  ...truncation,
1351
1413
  };
@@ -1386,9 +1448,20 @@ function tools(call, defaultPageSize) {
1386
1448
  },
1387
1449
  required: ["personId"],
1388
1450
  path: (args) => `/services/v2/people/${segment(args["personId"])}/schedules`,
1389
- query: (args) => ({ filter: args["when"] ?? "future", order: args["when"] === "past" ? "-starts_at" : "starts_at" }),
1451
+ query: (args) => ({
1452
+ filter: args["when"] ?? "future",
1453
+ order: args["when"] === "past" ? "-starts_at" : "starts_at",
1454
+ }),
1390
1455
  project: (row) => ({
1391
- ...pick(row, ["dates", "sort_date", "service_type_name", "team_name", "team_position_name", "status", "decline_reason"]),
1456
+ ...pick(row, [
1457
+ "dates",
1458
+ "sort_date",
1459
+ "service_type_name",
1460
+ "team_name",
1461
+ "team_position_name",
1462
+ "status",
1463
+ "decline_reason",
1464
+ ]),
1392
1465
  plan_id: relId(row, "plan") ?? null,
1393
1466
  }),
1394
1467
  }),
@@ -1401,9 +1474,16 @@ function tools(call, defaultPageSize) {
1401
1474
  planId: PLAN_ID,
1402
1475
  personId: PERSON_ID,
1403
1476
  teamId: idProperty("Team id from list_teams."),
1404
- teamPositionName: { type: "string", minLength: 1, description: "Position name from list_teams positions[].name." },
1477
+ teamPositionName: {
1478
+ type: "string",
1479
+ minLength: 1,
1480
+ description: "Position name from list_teams positions[].name.",
1481
+ },
1405
1482
  status: { type: "string", enum: ["U", "C"], description: "U unconfirmed (default) or C confirmed." },
1406
- prepareNotification: { type: "boolean", description: "Queue Planning Center's scheduling notification for this person." },
1483
+ prepareNotification: {
1484
+ type: "boolean",
1485
+ description: "Queue Planning Center's scheduling notification for this person.",
1486
+ },
1407
1487
  notes: { type: "string", description: "Note shown to the person." },
1408
1488
  }, ["serviceTypeId", "planId", "personId", "teamId"]),
1409
1489
  outputSchema: PLAN_PERSON_SCHEMA,
@@ -1438,7 +1518,11 @@ function tools(call, defaultPageSize) {
1438
1518
  keyId: idProperty("Key id from list_song_arrangements keys[].id."),
1439
1519
  length: { type: "integer", minimum: 0, maximum: 86_400, description: "Length in seconds." },
1440
1520
  sequence: { type: "integer", minimum: 0, description: "Position in the order; omit to append." },
1441
- servicePosition: { type: "string", enum: ["pre", "during", "post"], description: "Before, during, or after the service. Defaults to during." },
1521
+ servicePosition: {
1522
+ type: "string",
1523
+ enum: ["pre", "during", "post"],
1524
+ description: "Before, during, or after the service. Defaults to during.",
1525
+ },
1442
1526
  description: { type: "string", description: "Item description." },
1443
1527
  }, ["serviceTypeId", "planId", "title"]),
1444
1528
  outputSchema: ITEM_SCHEMA,
@@ -1530,7 +1614,11 @@ function tools(call, defaultPageSize) {
1530
1614
  properties: {
1531
1615
  name: { type: "string", minLength: 1, description: "Exact group name; end it with % for a prefix match." },
1532
1616
  groupTypeId: idProperty("Only groups of this type (/groups/v2/group_types)."),
1533
- archived: { type: "string", enum: ["exclude", "include", "only"], description: "Archived groups: exclude (default), include, or only." },
1617
+ archived: {
1618
+ type: "string",
1619
+ enum: ["exclude", "include", "only"],
1620
+ description: "Archived groups: exclude (default), include, or only.",
1621
+ },
1534
1622
  order: orderProperty(["name", "created_at", "memberships_count"]),
1535
1623
  },
1536
1624
  path: () => "/groups/v2/groups",
@@ -1541,7 +1629,14 @@ function tools(call, defaultPageSize) {
1541
1629
  order: args["order"],
1542
1630
  }),
1543
1631
  project: (row) => ({
1544
- ...pick(row, ["name", "memberships_count", "schedule", "contact_email", "archived_at", "public_church_center_web_url"]),
1632
+ ...pick(row, [
1633
+ "name",
1634
+ "memberships_count",
1635
+ "schedule",
1636
+ "contact_email",
1637
+ "archived_at",
1638
+ "public_church_center_web_url",
1639
+ ]),
1545
1640
  group_type_id: relId(row, "group_type") ?? null,
1546
1641
  }),
1547
1642
  }),
@@ -1606,7 +1701,7 @@ function tools(call, defaultPageSize) {
1606
1701
  startsBefore: dateProperty("Only events starting before this time."),
1607
1702
  includeCanceled: { type: "boolean", description: "Include canceled events." },
1608
1703
  },
1609
- path: (args) => args["groupId"] ? `/groups/v2/groups/${segment(args["groupId"])}/events` : "/groups/v2/events",
1704
+ path: (args) => (args["groupId"] ? `/groups/v2/groups/${segment(args["groupId"])}/events` : "/groups/v2/events"),
1610
1705
  query: (args) => ({
1611
1706
  ...range(["starts_at"], args["startsAfter"], args["startsBefore"]),
1612
1707
  filter: args["includeCanceled"] === true ? undefined : "not_canceled",
@@ -1628,7 +1723,10 @@ function tools(call, defaultPageSize) {
1628
1723
  includeArchived: { type: "boolean", description: "Include archived events." },
1629
1724
  },
1630
1725
  path: () => "/check-ins/v2/events",
1631
- query: (args) => ({ "where[name]": args["name"], filter: args["includeArchived"] === true ? undefined : "not_archived" }),
1726
+ query: (args) => ({
1727
+ "where[name]": args["name"],
1728
+ filter: args["includeArchived"] === true ? undefined : "not_archived",
1729
+ }),
1632
1730
  project: (row) => pick(row, ["name", "frequency", "archived_at"]),
1633
1731
  }),
1634
1732
  list({
@@ -1653,7 +1751,15 @@ function tools(call, defaultPageSize) {
1653
1751
  required: ["eventId"],
1654
1752
  path: (args) => `/check-ins/v2/events/${segment(args["eventId"])}/event_periods`,
1655
1753
  query: (args) => ({ ...range(["starts_at"], args["startsAfter"], args["startsBefore"]), order: "-starts_at" }),
1656
- project: (row) => pick(row, ["starts_at", "ends_at", "regular_count", "guest_count", "volunteer_count", "unique_total_count", "note"]),
1754
+ project: (row) => pick(row, [
1755
+ "starts_at",
1756
+ "ends_at",
1757
+ "regular_count",
1758
+ "guest_count",
1759
+ "volunteer_count",
1760
+ "unique_total_count",
1761
+ "note",
1762
+ ]),
1657
1763
  }),
1658
1764
  list({
1659
1765
  name: "list_check_ins",
@@ -1686,7 +1792,16 @@ function tools(call, defaultPageSize) {
1686
1792
  order: args["order"],
1687
1793
  }),
1688
1794
  project: (row) => ({
1689
- ...pick(row, ["first_name", "last_name", "kind", "number", "security_code", "created_at", "checked_out_at", "one_time_guest"]),
1795
+ ...pick(row, [
1796
+ "first_name",
1797
+ "last_name",
1798
+ "kind",
1799
+ "number",
1800
+ "security_code",
1801
+ "created_at",
1802
+ "checked_out_at",
1803
+ "one_time_guest",
1804
+ ]),
1690
1805
  person_id: relId(row, "person") ?? null,
1691
1806
  event_period_id: relId(row, "event_period") ?? null,
1692
1807
  }),
@@ -1720,7 +1835,16 @@ function tools(call, defaultPageSize) {
1720
1835
  order: "starts_at",
1721
1836
  }),
1722
1837
  project: (row) => ({
1723
- ...pick(row, ["name", "starts_at", "ends_at", "all_day_event", "location", "kind", "recurrence_description", "church_center_url"]),
1838
+ ...pick(row, [
1839
+ "name",
1840
+ "starts_at",
1841
+ "ends_at",
1842
+ "all_day_event",
1843
+ "location",
1844
+ "kind",
1845
+ "recurrence_description",
1846
+ "church_center_url",
1847
+ ]),
1724
1848
  event_id: relId(row, "event") ?? null,
1725
1849
  }),
1726
1850
  }),
@@ -1742,11 +1866,25 @@ function tools(call, defaultPageSize) {
1742
1866
  new_registration_url: NULLABLE_STRING,
1743
1867
  }),
1744
1868
  properties: {
1745
- archived: { type: "string", enum: ["unarchived", "archived", "all"], description: "unarchived (default), archived, or all." },
1869
+ archived: {
1870
+ type: "string",
1871
+ enum: ["unarchived", "archived", "all"],
1872
+ description: "unarchived (default), archived, or all.",
1873
+ },
1746
1874
  },
1747
1875
  path: () => "/registrations/v2/signups",
1748
1876
  query: (args) => ({ filter: args["archived"] === "all" ? undefined : (args["archived"] ?? "unarchived") }),
1749
- project: (row) => pick(row, ["name", "open", "closed", "open_at", "close_at", "maximum_capacity", "at_maximum_capacity", "archived", "new_registration_url"]),
1877
+ project: (row) => pick(row, [
1878
+ "name",
1879
+ "open",
1880
+ "closed",
1881
+ "open_at",
1882
+ "close_at",
1883
+ "maximum_capacity",
1884
+ "at_maximum_capacity",
1885
+ "archived",
1886
+ "new_registration_url",
1887
+ ]),
1750
1888
  }),
1751
1889
  list({
1752
1890
  name: "list_signup_attendees",
@@ -1764,7 +1902,11 @@ function tools(call, defaultPageSize) {
1764
1902
  }),
1765
1903
  properties: {
1766
1904
  signupId: idProperty("Signup id from list_signups."),
1767
- status: { type: "string", enum: ["active", "waitlist", "canceled"], description: "Only attendees in this state." },
1905
+ status: {
1906
+ type: "string",
1907
+ enum: ["active", "waitlist", "canceled"],
1908
+ description: "Only attendees in this state.",
1909
+ },
1768
1910
  },
1769
1911
  required: ["signupId"],
1770
1912
  path: (args) => `/registrations/v2/signups/${segment(args["signupId"])}/attendees`,
@@ -1803,7 +1945,14 @@ function tools(call, defaultPageSize) {
1803
1945
  name: "list_funds",
1804
1946
  description: "List Giving funds with ledger codes and visibility. Supplies fundId for donation filters.",
1805
1947
  key: "funds",
1806
- item: record({ id: STRING, name: STRING, ledger_code: NULLABLE_STRING, visibility: STRING, default: BOOLEAN, description: NULLABLE_STRING }),
1948
+ item: record({
1949
+ id: STRING,
1950
+ name: STRING,
1951
+ ledger_code: NULLABLE_STRING,
1952
+ visibility: STRING,
1953
+ default: BOOLEAN,
1954
+ description: NULLABLE_STRING,
1955
+ }),
1807
1956
  path: () => "/giving/v2/funds",
1808
1957
  project: (row) => pick(row, ["name", "ledger_code", "visibility", "default", "description"]),
1809
1958
  }),
@@ -1823,12 +1972,24 @@ function tools(call, defaultPageSize) {
1823
1972
  batch_group_id: NULLABLE_STRING,
1824
1973
  }),
1825
1974
  properties: {
1826
- status: { type: "string", enum: ["committed", "in_progress"], description: "Only committed or in-progress batches." },
1975
+ status: {
1976
+ type: "string",
1977
+ enum: ["committed", "in_progress"],
1978
+ description: "Only committed or in-progress batches.",
1979
+ },
1827
1980
  },
1828
1981
  path: () => "/giving/v2/batches",
1829
1982
  query: (args) => ({ filter: args["status"], order: "-updated_at" }),
1830
1983
  project: (row) => ({
1831
- ...pick(row, ["description", "status", "donations_count", "total_cents", "total_currency", "committed_at", "created_at"]),
1984
+ ...pick(row, [
1985
+ "description",
1986
+ "status",
1987
+ "donations_count",
1988
+ "total_cents",
1989
+ "total_currency",
1990
+ "committed_at",
1991
+ "created_at",
1992
+ ]),
1832
1993
  batch_group_id: relId(row, "batch_group") ?? null,
1833
1994
  }),
1834
1995
  }),
@@ -1895,12 +2056,21 @@ function tools(call, defaultPageSize) {
1895
2056
  person_id: NULLABLE_STRING,
1896
2057
  designations: DESIGNATIONS,
1897
2058
  }),
1898
- properties: { status: { type: "string", minLength: 1, description: "Status as Planning Center reports it, e.g. active." } },
2059
+ properties: {
2060
+ status: { type: "string", minLength: 1, description: "Status as Planning Center reports it, e.g. active." },
2061
+ },
1899
2062
  path: () => "/giving/v2/recurring_donations",
1900
2063
  query: (args) => ({ "where[status]": args["status"] }),
1901
2064
  include: "designations,designations.fund",
1902
2065
  project: (row, index) => ({
1903
- ...pick(row, ["amount_cents", "amount_currency", "schedule", "status", "next_occurrence", "last_donation_received_at"]),
2066
+ ...pick(row, [
2067
+ "amount_cents",
2068
+ "amount_currency",
2069
+ "schedule",
2070
+ "status",
2071
+ "next_occurrence",
2072
+ "last_donation_received_at",
2073
+ ]),
1904
2074
  person_id: relId(row, "person") ?? null,
1905
2075
  designations: designations(row, index),
1906
2076
  }),
@@ -1911,9 +2081,20 @@ function tools(call, defaultPageSize) {
1911
2081
  description: "List the organization's webhook subscriptions: event name, URL, and active state. Never returns the signing secret; that needs pco_api_get.",
1912
2082
  annotations: READ,
1913
2083
  inputSchema: input({ perPage: perPageProperty(defaultPageSize), offset: OFFSET_PROPERTY }),
1914
- outputSchema: listOutput("subscriptions", record({ id: STRING, name: STRING, url: STRING, active: BOOLEAN, application_id: NULLABLE_STRING, created_at: STRING })),
2084
+ outputSchema: listOutput("subscriptions", record({
2085
+ id: STRING,
2086
+ name: STRING,
2087
+ url: STRING,
2088
+ active: BOOLEAN,
2089
+ application_id: NULLABLE_STRING,
2090
+ created_at: STRING,
2091
+ })),
1915
2092
  handler: async (args, ctx) => {
1916
- const document = asRecord(await call({ method: "GET", path: "/webhooks/v2/webhook_subscriptions", query: { per_page: perPage(args), offset: args["offset"] } }, ctx));
2093
+ const document = asRecord(await call({
2094
+ method: "GET",
2095
+ path: "/webhooks/v2/webhook_subscriptions",
2096
+ query: { per_page: perPage(args), offset: args["offset"] },
2097
+ }, ctx));
1917
2098
  // `authenticity_secret` verifies deliveries. A projection is the
1918
2099
  // place it never appears, so this read has no `raw` mode.
1919
2100
  return {
@@ -1929,50 +2110,29 @@ function tools(call, defaultPageSize) {
1929
2110
  // ---------------------------------------------------------------------------
1930
2111
  function usageGuide(purpose, instructions) {
1931
2112
  const organizationInstructions = instructions?.trim();
1932
- return `# Planning Center usage
1933
-
1934
- Planning Center Online church management — People, Services, Groups, Check-Ins, Calendar, Registrations, Giving, Publishing, Webhooks — for: ${purpose}
1935
-
1936
- ## Identity first
1937
-
1938
- - A person id is the join key across every product: the id \`search_people\` returns is the \`personId\` for Services schedules, Groups memberships, Giving donations, and Check-Ins. Resolve ids with list tools; never guess one.
1939
- - A person id that used to resolve and now answers \`not_found\` was most likely merged. Read \`/people/v2/person_mergers\` with \`where[person_to_remove_id]\` and follow \`person_to_keep_id\`.
1940
- - \`get_me\` names the user this token acts as. Every read and write runs with that user's permissions, product by product; a \`connector_call_failed\` naming permission means the user cannot, not that the call was wrong.
1941
-
1942
- ## Results and paging
1943
-
1944
- - Records keep Planning Center's own snake_case attribute names; a relationship appears as \`<relationship>_id\`. Pass \`raw: true\` for the untouched JSON:API resources and \`included\`.
1945
- - Lists page by offset: \`perPage\` up to 100, then pass \`page.nextOffset\` back as \`offset\` while \`page.hasMore\` is true. \`page.totalCount\` answers "how many" without paging — request \`perPage: 1\`.
1946
- - A bare date in a date filter is the organization's local day; pass a full timestamp for exact bounds. Name filters are exact; end one with \`%\` for a prefix match.
1947
- - \`get_plan\` fetches the plan, its items, and its team in one call and sets \`itemsTruncated\`/\`teamMembersTruncated\` past 100 rows; page the rest with \`pco_api_get\`.
1948
-
1949
- ## What each product lets the API write
1950
-
1951
- - People: full read/write. \`update_person\` with \`status: inactive\` archives reversibly; a DELETE is permanent and only reachable through \`pco_api_mutate\`. \`run_list\` can fire list automations.
1952
- - Services: full read/write. A plan's dates come from its plan times. Scheduling needs a team.
1953
- - Groups: memberships and group settings are writable; groups cannot be created, and events and attendance are read-only.
1954
- - Check-Ins and Registrations: read-only. Nobody can be checked in or registered through the API.
1955
- - Calendar: events, instances, and times are read-only; tags, resources, rooms, and folders are writable through \`pco_api_mutate\`.
1956
- - Giving: the named tools only read, amounts in integer cents. Creating or editing donations, refunds, and committing a batch move money and go through \`pco_api_mutate\`.
1957
- - Publishing and the \`api\` product (organization, OAuth applications, personal access tokens) have no named tools; use the hatches.
1958
-
1959
- ## The hatches
1960
-
1961
- - \`pco_api_get\` reaches any GET; \`pco_api_mutate\` any POST, PATCH, or DELETE, always approval-gated. Paths start \`/<app>/v2\`, exactly as Planning Center documents them; the connector owns host, auth, content type, and the \`X-PCO-API-Version\` header.
1962
- - Bodies are JSON:API documents: \`{"data":{"type":"Email","attributes":{...}}}\`. \`null\` clears an attribute; an omitted key is left alone.
1963
- - Each product is pinned to a reviewed API version: ${PLANNING_CENTER_APPS.map((app) => `${app} ${PLANNING_CENTER_API_VERSIONS[app]}`).join(", ")}. Pass \`version\` only for an endpoint those versions lack.
1964
- - Query parameters are name/value pairs with literal brackets: \`where[status]\`, \`include\`, \`order\`, \`filter\`, \`fields[Person]\`. Every list response's \`meta\` names the \`can_query_by\`, \`can_order_by\`, and \`can_include\` values for that endpoint; an unknown \`where\` key is ignored rather than refused.
1965
- - File uploads go to a separate host (upload.planningcenteronline.com), which this connection does not reach.
1966
-
1967
- ## Rate limits
1968
-
1969
- Planning Center allows 100 requests per 20 seconds per user, shared by every integration running as that user. This connection admits 100 calls per 20 seconds, five at a time, per runtime — an approximation, not a guarantee. A \`rate_limited\` failure carries Planning Center's wait. Filter server-side and use \`totalCount\` rather than paging to count.
1970
- ${organizationInstructions
1971
- ? `\n## Organization instructions\n\n${organizationInstructions}\n`
1972
- : ""}`;
2113
+ return `${skill.fragments.guide_0}${purpose}${skill.fragments.guide_1}${PLANNING_CENTER_APPS.map((app) => `${app} ${PLANNING_CENTER_API_VERSIONS[app]}`).join(", ")}${skill.fragments.guide_2}${organizationInstructions ? `\n## ${skill.instructionsHeading}\n\n${organizationInstructions}\n` : ""}`;
1973
2114
  }
2115
+ /** The closed options planningCenter() accepts; see `assertKnownOptions`. */
2116
+ const PLANNING_CENTER_OPTIONS = optionsOf()({
2117
+ ...PROVIDER_COMMON,
2118
+ ...keys("userAgent", "defaultPageSize", "baseUrl"),
2119
+ });
1974
2120
  /** A maintained Planning Center Online connection over the REST API. */
1975
- export function planningCenter(id, options) {
2121
+ export const planningCenter = asProviderFactory({
2122
+ name: "planning-center",
2123
+ title: "Planning Center",
2124
+ kind: "api",
2125
+ readme: "Planning Center",
2126
+ bundle: {
2127
+ "baselineGzip": 26970,
2128
+ "maxGzip": 86970,
2129
+ "note": "./providers/planning-center starts at 26,970 B gzip. It is api() over the guarded transport with no hosted-MCP surface, so neither the MCP client nor the Effect core is in its graph; the cap uses the existing baseline + 60,000 B policy.",
2130
+ },
2131
+ skill,
2132
+ options: PLANNING_CENTER_OPTIONS,
2133
+ create: planningCenterConnector,
2134
+ });
2135
+ function planningCenterConnector(id, options) {
1976
2136
  const purpose = options.purpose.trim();
1977
2137
  if (!purpose) {
1978
2138
  throw new Error("planningCenter() requires a non-empty organization purpose.");
@@ -2004,7 +2164,7 @@ export function planningCenter(id, options) {
2004
2164
  const document = asRecord(await call({ method: "GET", path: "/people/v2/me", query: { include: "organization" } }, {
2005
2165
  ...ctx,
2006
2166
  credential: {
2007
- get: async (field) => (field ? values[field] ?? null : null),
2167
+ get: async (field) => (field ? (values[field] ?? null) : null),
2008
2168
  getAll: async () => values,
2009
2169
  },
2010
2170
  }));