@bettercms-ai/mcp 0.80.1 → 0.81.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.
package/dist/index.js CHANGED
@@ -2915,6 +2915,11 @@ var LISTING_TAIL = /* @__PURE__ */ new Set([
2915
2915
  "delete_integration",
2916
2916
  "test_integration_fetch",
2917
2917
  "run_integration_sync",
2918
+ "run_integration_function",
2919
+ "search_integration_reference",
2920
+ "start_integration_signin",
2921
+ "save_integration_template",
2922
+ "install_integration_template",
2918
2923
  // The ABM campaign lane — also hidden from the default listing (ABM_TOOLS below).
2919
2924
  "research_account",
2920
2925
  "build_abm_page",
@@ -2999,12 +3004,17 @@ var NEW_TOOL_TEXT = {
2999
3004
  editor_setup: "Make the live build editable: derives pages, collections, forms, bindings and components from the live release (skill bindings). Run it after an import or a rebuild, before the conversion brief. Idempotent: `ready` when it already ran for the live sha, else it starts (`running`; poll with `check: true`). `state`: never or stale (run it), running, ready, failed. 409 = nothing is live yet: deploy first. Then get_next_steps.",
3000
3005
  transform_image: "Edit an existing Media Library image with an instruction (e.g. 'remove the background', 'make it dusk'); the result is saved as a NEW asset and the original is kept (5 AI credits). Returns the new asset (id, url, altText).",
3001
3006
  write_alt_text: "Alt text for a Media Library image (mediaId) or an https image URL (url), written from its pixels (1 AI credit). A suggestion: save it with update_media.",
3002
- list_integrations: "List the project's custom integrations (any vendor with a public API: jobs boards, stores, CRMs, video). Each has its allowed hosts, secret NAMES with `secretsPresent` (is the env var set; never the value), `inject` (where the secrets go), syncs, panel and `approved`. Read bettercms://skill/integrations before building one.",
3003
- get_integration: "One integration (`integration`: id or slug) with its last 20 sync runs (status, counts, error).",
3004
- create_integration: "Declare a custom integration: `allowedHosts` (exact hostnames the server may call), `secrets` (template name -> project env var KEY), `inject` (the headers / query parameters that carry them on every request, e.g. {headers: {authorization: 'Bearer {{secret.TOKEN}}'}}), `allowPost` (GET only unless true), optional `auth` token exchange (inject {{token}}), `syncs` (vendor list -> collection) and `panel`. Secret VALUES never pass through here: the user sets them under Settings \u2192 Environment variables (production); list_env_vars shows the names. Until an admin approves it, vendor calls and syncs are refused: give the user `approval.approveUrl` and the `approval.changes` in plain words. You cannot approve it yourself.",
3005
- update_integration: "Change an integration (`integration`: id or slug); keys you leave out are kept, and `secrets`, `inject` and `syncs` replace the stored ones whole. Changing `allowedHosts`, `secrets`, `inject`, `allowPost` or `auth` withdraws its approval until an admin approves again (`approval.approveUrl`). Name, syncs, settings and panel never need approval.",
3007
+ list_integrations: "List the project's custom integrations (any vendor with a public API) and the workspace's integration `templates`. Each has its hosts, secret NAMES with `secretsPresent` (never a value), syncs, hooks, endpoints, functions and `approved`. Read bettercms://skill/integrations first.",
3008
+ get_integration: "One integration (`integration`: id or slug): `hookUrls`, `endpointUrls`, `oauthStatus`, its last sync runs, write-backs (`pushes`), function runs, webhook deliveries and vendor calls (the call log).",
3009
+ create_integration: "Declare a custom integration. Shapes: bettercms://skill/integrations. Secret VALUES never pass through here (the user sets env vars; list_env_vars shows names). Until an admin approves it, every vendor call, sync, webhook, endpoint and function is refused: give the user `approval.approveUrl` and `approval.changes` in plain words. You cannot approve it.",
3010
+ update_integration: "Change an integration (`integration`: id or slug); parameters as create_integration, keys left out are kept, lists replace the stored ones whole. A change to hosts, secrets, inject, allowPost, auth, oauth, hooks, endpoints, references or functions withdraws its approval (`approval.approveUrl`); name, syncs, settings and panel never need it.",
3006
3011
  delete_integration: "Delete an integration and its sync runs. Entries it synced stay (their provenance still names it); the env vars stay.",
3007
3012
  run_integration_sync: "Run one of an integration's syncs now (`integration`, `sync`: its key): every page of the vendor list is fetched and each item is created or updated as a DRAFT entry, matched on its externalKey, so a re-run only changes what changed (`unchanged` counts the rest); `publish: true` on the sync publishes what changed. Answers with the run: queued, running, done (counts: created, updated, unchanged, failed, pages, items) or failed (error). `waitSeconds` (\u2264 20) waits for it to finish; else follow it with get_integration. A sync with `schedule` 'hourly' or 'daily' also runs on its own.",
3013
+ run_integration_function: "Run an integration's function (bettercms/functions/<name>.ts, trigger 'manual') with `input`. Its fetch() reaches only the integration's approved hosts and the server adds the secrets: the code never sees them. `waitSeconds` (\u2264 20) answers with output, logs or error.",
3014
+ search_integration_reference: "Search an integration's live reference (`reference`: its key) at the vendor: up to 20 {id, title, image}. Store the pick in a json field (skill \xA712); the site resolves it live.",
3015
+ start_integration_signin: "Start the vendor sign-in of an integration with `oauth`: a `url` the USER opens to sign in with their own OAuth app (register `redirectUri` in it first). Tokens stay on the server.",
3016
+ save_integration_template: "Save an integration as a workspace template: its shape, never a secret value or its settings, with copies of the collections its syncs write into.",
3017
+ install_integration_template: "Install a workspace template on this project: missing collections are created and the integration waits for approval (`approval.approveUrl`); `secretsNeeded` lists the env vars to set.",
3008
3018
  test_integration_fetch: "Call the vendor once through the server, as the integration: an approved host only, with the integration's `inject` secrets added server-side and every secret redacted from the answer. The request itself takes no {{...}} templates, and the token endpoint cannot be called. Use it to read the response shape before writing a sync: `items`, `externalKey` and `map` are JSON Pointers into it. Bodies over 1 MB come back truncated. 409 integration_not_approved: an admin must approve it first."
3009
3019
  };
3010
3020
  var DELETE_PAGE_RUNTIME_BUNDLE = " Or, with no pageId, `runtimeBundle`: 'report' lists the duplicate pages derived from a server app's own bundle (`app/` folders); 'apply' retires them and their empty folders.";
@@ -3063,9 +3073,17 @@ var INTEGRATION_PARAM_TEXT = {
3063
3073
  secrets: "template name (UPPER_SNAKE) -> project env var KEY, e.g. {TOKEN: 'SHOPIFY_ADMIN_TOKEN'}; never a value",
3064
3074
  inject: "where the secrets go on every vendor request: {headers?: {name: template}, query?: {name: template}}, templates {{secret.NAME}} or {{token}}",
3065
3075
  allowPost: "true lets requests other than GET through (vendor writes, POST search APIs such as Apollo's); GET only when omitted",
3066
- auth: "optional token exchange: {request:{method,url,headers?,query?,form?}, tokenPath, expiresInPath?, ttlSeconds?}; {{secret.NAME}} in its values; its result is {{token}}",
3076
+ auth: "optional token exchange (skill \xA75); its result is {{token}}",
3067
3077
  settings: "non-secret JSON the panel reads and writes (store id, toggles); at most 16 KB",
3068
- syncs: "[{key, contentModelId, request:{method,url,headers?,query?}, pagination?:{style:'none'|'page'|'cursor'|'link', param?, cursorPath?, maxPages?}, items, externalKey, slug?, map:{fieldKey: pointer | {path, transform?:'string'|'number'|'boolean'|'join'}}, publish?, schedule?:'hourly'|'daily'|null}]; pointers are JSON Pointers",
3078
+ syncs: "vendor list -> collection [{key, contentModelId, request, items, externalKey, map, item?, writeBack?, publish?, schedule?}] (skill \xA76; two-way \xA715)",
3079
+ hooks: "vendor webhooks [{key, verify, action}] (skill \xA79); the vendor posts to `hookUrls`",
3080
+ endpoints: "public reads for the site [{key, request, params, pick, cacheSeconds?}] (skill \xA711); at `endpointUrls`",
3081
+ references: "live references [{key, label, search, lookup, items, id, title}] (skill \xA712)",
3082
+ functions: "[{name, triggers?, cpuMs?}]: bettercms/functions/<name>.ts (skill \xA710)",
3083
+ oauth: "sign-in with the customer's own OAuth app {authorizeUrl, tokenUrl, scopes, clientId, clientSecret} (secret NAMES; skill \xA713)",
3084
+ alertAfter: "tell admins after this many failed runs in a row (0 = never; 3 when omitted)",
3085
+ reference: "the reference's key (from the integration's references)",
3086
+ template: "the template's slug (from list_integrations `templates`)",
3069
3087
  panel: "the custom panel's slug: bettercms/panels/<panel>.tsx in the site repo; null for the stock page",
3070
3088
  url: "the full https URL on an allowed host (no templates in the URL)",
3071
3089
  sync: "the sync's key (from the integration's syncs)",
@@ -3359,6 +3377,7 @@ var uiObject = z.strictObject({
3359
3377
  placeholder: z.string().min(1).max(120).optional().describe("ghost text, a field of any type. A prop uses its own `placeholder`."),
3360
3378
  synced: z.strictObject({ from: z.string().min(1).max(64) }).optional().describe("the value comes from this global; edits apply everywhere."),
3361
3379
  valueShape: z.enum(["string", "object"]).optional().describe("a link / image / button writes a plain string or the object."),
3380
+ reference: z.object({ integration: z.string(), key: z.string() }).strict().optional().describe("a json field holding a live vendor item: the integration's slug and reference key."),
3362
3381
  access: z.strictObject({ view: z.literal("schema.view").optional(), edit: z.literal("schema.manage").optional() }).optional().describe("who sees (view: developer-only) and who changes (edit) it, gated in the editor. The enforced lock is readOnly:'developer'."),
3363
3382
  hidden: z.boolean().optional().describe("true: never shown in the form (a system value). A conditional hide is showIf."),
3364
3383
  blocks: z.record(z.string(), z.strictObject({ icon: dockIconArg.optional(), preview: z.record(z.string(), z.string()).optional() })).optional().describe("modular only: per block slug, {icon?, preview?} for its rows."),
@@ -3551,8 +3570,17 @@ var integrationShape = {
3551
3570
  auth: z.record(z.string(), z.unknown()).nullable().optional().describe(INTEGRATION_PARAM_TEXT.auth),
3552
3571
  settings: z.record(z.string(), z.unknown()).optional().describe(INTEGRATION_PARAM_TEXT.settings),
3553
3572
  syncs: z.array(z.record(z.string(), z.unknown())).max(20).optional().describe(INTEGRATION_PARAM_TEXT.syncs),
3554
- panel: z.string().max(48).nullable().optional().describe(INTEGRATION_PARAM_TEXT.panel)
3573
+ panel: z.string().max(48).nullable().optional().describe(INTEGRATION_PARAM_TEXT.panel),
3574
+ hooks: z.array(z.record(z.string(), z.unknown())).max(20).optional().describe(INTEGRATION_PARAM_TEXT.hooks),
3575
+ endpoints: z.array(z.record(z.string(), z.unknown())).max(20).optional().describe(INTEGRATION_PARAM_TEXT.endpoints),
3576
+ references: z.array(z.record(z.string(), z.unknown())).max(10).optional().describe(INTEGRATION_PARAM_TEXT.references),
3577
+ functions: z.array(z.record(z.string(), z.unknown())).max(20).optional().describe(INTEGRATION_PARAM_TEXT.functions),
3578
+ oauth: z.record(z.string(), z.unknown()).nullable().optional().describe(INTEGRATION_PARAM_TEXT.oauth),
3579
+ alertAfter: z.number().int().min(0).max(50).optional().describe(INTEGRATION_PARAM_TEXT.alertAfter)
3555
3580
  };
3581
+ var integrationShapeBare = Object.fromEntries(
3582
+ Object.entries(integrationShape).map(([k, v]) => [k, v.describe(void 0)])
3583
+ );
3556
3584
  var DRY_RUN_SUMMARY = "Dry run: nothing was written.";
3557
3585
  var dryRunOf = async (client, method, path, body, headers = {}) => ok(DRY_RUN_SUMMARY, await client.fetchJSON(client.url(path), { method, body: JSON.stringify({ ...body, dryRun: true }), ...withHeaders(headers) }));
3558
3586
  function toField(f) {
@@ -5227,7 +5255,8 @@ ${d.outline}` : "Proposed Content structure.", d);
5227
5255
  "List custom integrations",
5228
5256
  NEW_TOOL_TEXT.list_integrations,
5229
5257
  z.object({}).shape,
5230
- async (c) => ok("Integrations.", await data(c, "GET", `/management/integrations`))
5258
+ // The whole body: the workspace's `templates` ride beside `data`.
5259
+ async (c) => ok("Integrations.", await c.fetchJSON(c.url(`/management/integrations`), { method: "GET" }))
5231
5260
  ),
5232
5261
  def(
5233
5262
  "get_integration",
@@ -5255,9 +5284,9 @@ ${d.outline}` : "Proposed Content structure.", d);
5255
5284
  NEW_TOOL_TEXT.update_integration,
5256
5285
  z.object({
5257
5286
  integration: z.string().min(1).describe(INTEGRATION_PARAM_TEXT.integration),
5258
- name: z.string().min(1).max(120).optional().describe("a label, 1-120 chars"),
5259
- vendor: z.string().max(60).nullable().optional().describe("the vendor's name"),
5260
- ...integrationShape
5287
+ name: z.string().min(1).max(120).optional(),
5288
+ vendor: z.string().max(60).nullable().optional(),
5289
+ ...integrationShapeBare
5261
5290
  }).shape,
5262
5291
  async (c, a) => {
5263
5292
  const { integration, ...body } = a;
@@ -5282,6 +5311,66 @@ ${d.outline}` : "Proposed Content structure.", d);
5282
5311
  }).shape,
5283
5312
  async (c, a) => ok("Sync run.", await data(c, "POST", withWait(`/management/integrations/${s(a.integration)}/syncs/${s(a.sync)}/run`, a.waitSeconds)))
5284
5313
  ),
5314
+ def(
5315
+ "run_integration_function",
5316
+ "Run an integration's function",
5317
+ NEW_TOOL_TEXT.run_integration_function,
5318
+ z.object({
5319
+ integration: z.string().min(1).describe(INTEGRATION_PARAM_TEXT.integration),
5320
+ name: z.string().min(1).describe("the function's name"),
5321
+ input: z.unknown().optional().describe("the function's `event` (JSON)"),
5322
+ waitSeconds: z.number().min(0).max(50).optional().describe(PARAM_TEXT.waitSeconds)
5323
+ }).shape,
5324
+ async (c, a) => ok("Function run.", await data(c, "POST", withWait(`/management/integrations/${s(a.integration)}/functions/${s(a.name)}/run`, a.waitSeconds), { input: a.input }))
5325
+ ),
5326
+ def(
5327
+ "search_integration_reference",
5328
+ "Search an integration's live reference",
5329
+ NEW_TOOL_TEXT.search_integration_reference,
5330
+ z.object({
5331
+ integration: z.string().min(1).describe(INTEGRATION_PARAM_TEXT.integration),
5332
+ reference: z.string().min(1).describe(INTEGRATION_PARAM_TEXT.reference),
5333
+ query: z.string().max(200).optional().describe("what to search for")
5334
+ }).shape,
5335
+ async (c, a) => ok("Vendor items.", await data(c, "POST", `/management/integrations/${s(a.integration)}/references/${s(a.reference)}/search`, { query: a.query ?? "" }))
5336
+ ),
5337
+ def(
5338
+ "start_integration_signin",
5339
+ "Start an integration's vendor sign-in",
5340
+ NEW_TOOL_TEXT.start_integration_signin,
5341
+ z.object({ integration: z.string().min(1).describe(INTEGRATION_PARAM_TEXT.integration) }).shape,
5342
+ async (c, a) => ok("Sign-in URL: give it to the user to open.", await data(c, "POST", `/management/integrations/${s(a.integration)}/oauth/start`))
5343
+ ),
5344
+ def(
5345
+ "save_integration_template",
5346
+ "Save an integration as a template",
5347
+ NEW_TOOL_TEXT.save_integration_template,
5348
+ z.object({
5349
+ integration: z.string().min(1).describe(INTEGRATION_PARAM_TEXT.integration),
5350
+ slug: z.string().min(1).max(48).optional().describe(INTEGRATION_PARAM_TEXT.slug),
5351
+ name: z.string().min(1).max(120).optional().describe("a label, 1-120 chars"),
5352
+ description: z.string().max(500).optional().describe("what it connects, for the people installing it")
5353
+ }).shape,
5354
+ async (c, a) => {
5355
+ const { integration, ...body } = a;
5356
+ return ok("Template saved.", await data(c, "POST", `/management/integrations/${s(integration)}/template`, body));
5357
+ }
5358
+ ),
5359
+ def(
5360
+ "install_integration_template",
5361
+ "Install an integration template",
5362
+ NEW_TOOL_TEXT.install_integration_template,
5363
+ z.object({
5364
+ template: z.string().min(1).describe(INTEGRATION_PARAM_TEXT.template),
5365
+ slug: z.string().min(1).max(48).optional().describe(INTEGRATION_PARAM_TEXT.slug),
5366
+ name: z.string().min(1).max(120).optional().describe("a label, 1-120 chars")
5367
+ }).shape,
5368
+ // The whole body: `approval`, `secretsNeeded` and `collectionsCreated` ride beside `data`.
5369
+ async (c, a) => {
5370
+ const { template, ...body } = a;
5371
+ return ok("Template installed.", await c.fetchJSON(c.url(`/management/integrations/templates/${s(template)}/install`), { method: "POST", body: JSON.stringify(body) }));
5372
+ }
5373
+ ),
5285
5374
  def(
5286
5375
  "test_integration_fetch",
5287
5376
  "Call a vendor through an integration",