@okostream/mcp 0.12.10 → 0.12.11

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 (2) hide show
  1. package/dist/server.js +56 -23
  2. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -116291,7 +116291,7 @@ function sanitizeScopes(scopes) {
116291
116291
  }
116292
116292
 
116293
116293
  // build/version.js
116294
- var OKO_MCP_VERSION = "0.12.10";
116294
+ var OKO_MCP_VERSION = "0.12.11";
116295
116295
 
116296
116296
  // build/auth.js
116297
116297
  var state = null;
@@ -117195,16 +117195,43 @@ async function updateCardImageInTransaction(eventId, cardId, patch) {
117195
117195
  });
117196
117196
  });
117197
117197
  }
117198
- async function writeEventPatchWithCards(eventId, basePatch, buildItems) {
117198
+ function descendPath(root, segments) {
117199
+ let current = root;
117200
+ for (const segment of segments) {
117201
+ if (current === null || typeof current !== "object")
117202
+ return void 0;
117203
+ current = current[segment];
117204
+ }
117205
+ return current;
117206
+ }
117207
+ function valueAfterPatch(fresh, patch, path) {
117208
+ if (Object.prototype.hasOwnProperty.call(patch, path))
117209
+ return patch[path];
117210
+ const segments = path.split(".");
117211
+ for (let cut = segments.length - 1; cut > 0; cut--) {
117212
+ const ancestor = segments.slice(0, cut).join(".");
117213
+ if (!Object.prototype.hasOwnProperty.call(patch, ancestor))
117214
+ continue;
117215
+ return descendPath(patch[ancestor], segments.slice(cut));
117216
+ }
117217
+ return descendPath(fresh, segments);
117218
+ }
117219
+ function scheduleMissesDate(sessionMode, eventDate) {
117220
+ return sessionMode === "schedule" && !eventDate;
117221
+ }
117222
+ async function writeEventPatchWithCards(eventId, basePatch, buildItems, validate = null) {
117199
117223
  const eventRef = doc(firestore, "events", eventId);
117200
117224
  await runTransaction2(firestore, async (transaction) => {
117201
117225
  const snap = await transaction.get(eventRef);
117202
117226
  if (!snap.exists())
117203
117227
  throw new Error("Event not found.");
117228
+ const fresh = snap.data();
117204
117229
  const patch = { ...basePatch };
117205
117230
  if (buildItems) {
117206
- patch["blocks.cards.items"] = buildItems(snap.data());
117231
+ patch["blocks.cards.items"] = buildItems(fresh);
117207
117232
  }
117233
+ if (validate)
117234
+ validate(fresh, patch);
117208
117235
  if (Object.keys(patch).length === 0)
117209
117236
  return;
117210
117237
  transaction.update(eventRef, patch);
@@ -118425,6 +118452,9 @@ function validateEventSetup(eventId, event) {
118425
118452
  } else if (eventDateMs < Date.now() - 60 * 60 * 1e3) {
118426
118453
  add(warnings, "date_in_past", "meta.eventDate", "Configured webinar date appears to be in the past.");
118427
118454
  }
118455
+ if (scheduleMissesDate(event.analyticsSessionMode, meta2.eventDate)) {
118456
+ add(errors, "schedule_missing_date", "analyticsSessionMode", "Session mode is 'schedule' but meta.eventDate is empty, so OKO can never start or end the session automatically and the reminder is never sent. Set eventDate, or switch sessionMode to 'manual' \u2014 oko_update_event accepts both fields in one call.");
118457
+ }
118428
118458
  const duplicateFormIds = /* @__PURE__ */ new Set();
118429
118459
  const seenFormIds = /* @__PURE__ */ new Set();
118430
118460
  forms.forEach((form, index) => {
@@ -118540,6 +118570,24 @@ function telegramBlocksPatch(args) {
118540
118570
  "telegram.channelIds": args.telegramChannelIds
118541
118571
  };
118542
118572
  }
118573
+ function assertScheduleHasDate(fresh, patch) {
118574
+ const missing = scheduleMissesDate(valueAfterPatch(fresh, patch, "analyticsSessionMode"), valueAfterPatch(fresh, patch, "meta.eventDate"));
118575
+ if (!missing)
118576
+ return;
118577
+ throw new Error("sessionMode 'schedule' requires meta.eventDate: without a date OKO can never start the session automatically. Set eventDate in the same call, or switch sessionMode to 'manual' in the same call to clear the date.");
118578
+ }
118579
+ function assertFormBindingsExist(fresh, patch) {
118580
+ const formsAfterPatch = valueAfterPatch(fresh, patch, "forms");
118581
+ const knownFormIds = new Set((Array.isArray(formsAfterPatch) ? formsAfterPatch : []).map((form) => typeof form?.id === "string" ? form.id : null).filter((id) => Boolean(id)));
118582
+ for (const field2 of ["blocks.promoBanner.formId", "blocks.primaryCta.formId"]) {
118583
+ if (!Object.prototype.hasOwnProperty.call(patch, field2))
118584
+ continue;
118585
+ const value = patch[field2];
118586
+ if (typeof value === "string" && value && !knownFormIds.has(value)) {
118587
+ throw new Error(`${field2}: form "${value}" not found in event.forms. Create the form in the same call (or beforehand) \u2014 a button bound to a missing form does nothing for viewers.`);
118588
+ }
118589
+ }
118590
+ }
118543
118591
  function configureBlocksScopes(args) {
118544
118592
  return Object.keys(telegramBlocksPatch(args)).length > 0 ? ["setup:write", "telegram:write"] : ["setup:write"];
118545
118593
  }
@@ -118719,7 +118767,8 @@ function registerEventTools(server2) {
118719
118767
  returns: "event id and the list of Firestore fields updated.",
118720
118768
  examples: ["Use for title/date/speaker/video edits; use oko_configure_blocks for blocks and forms."],
118721
118769
  notes: [
118722
- "sessionMode 'schedule' makes OKO start and end the analytics session automatically at eventDate for durationMinutes; 'manual' keeps the Start/End buttons and a Telegram reminder 10 minutes before eventDate. This is still an analytics session, not an event status."
118770
+ "sessionMode 'schedule' makes OKO start and end the analytics session automatically at eventDate for durationMinutes; 'manual' keeps the Start/End buttons and a Telegram reminder 10 minutes before eventDate. This is still an analytics session, not an event status.",
118771
+ "sessionMode 'schedule' and eventDate travel together: setting the mode without a date, or clearing the date while the mode stays, is refused. Change both in one call."
118723
118772
  ]
118724
118773
  }),
118725
118774
  inputSchema: {
@@ -118745,13 +118794,7 @@ function registerEventTools(server2) {
118745
118794
  if (requiredScopes.size === 0)
118746
118795
  throw new Error("Provide at least one field to update.");
118747
118796
  const { uid } = await authed([...requiredScopes]);
118748
- const current = await readEvent(args.eventId, uid, "edit");
118749
- if (args.sessionMode === "schedule") {
118750
- const dateAfterPatch = args.eventDate !== void 0 ? args.eventDate : current.meta?.eventDate ?? null;
118751
- if (!dateAfterPatch) {
118752
- throw new Error("sessionMode 'schedule' requires meta.eventDate: without a date OKO can never start the session automatically. Set eventDate in the same call, or keep sessionMode 'manual'.");
118753
- }
118754
- }
118797
+ await readEvent(args.eventId, uid, "edit");
118755
118798
  const patch = {};
118756
118799
  if (args.title !== void 0)
118757
118800
  patch.title = args.title.trim();
@@ -118776,7 +118819,7 @@ function registerEventTools(server2) {
118776
118819
  patch.analyticsSessionMode = args.sessionMode;
118777
118820
  if (args.durationMinutes !== void 0)
118778
118821
  patch["meta.durationMinutes"] = args.durationMinutes;
118779
- await updateDoc(doc(firestore, "events", args.eventId), patch);
118822
+ await writeEventPatchWithCards(args.eventId, patch, null, assertScheduleHasDate);
118780
118823
  return textResult({ id: args.eventId, updated: Object.keys(patch) });
118781
118824
  });
118782
118825
  server2.registerTool("oko_configure_blocks", {
@@ -118874,17 +118917,7 @@ function registerEventTools(server2) {
118874
118917
  }))
118875
118918
  }));
118876
118919
  Object.assign(patch, telegramPatch);
118877
- const formsAfterPatch = patch.forms ?? current.forms;
118878
- const knownFormIds = new Set((Array.isArray(formsAfterPatch) ? formsAfterPatch : []).map((form) => typeof form?.id === "string" ? form.id : null).filter((id) => Boolean(id)));
118879
- for (const [field2, value] of [
118880
- ["blocks.promoBanner.formId", patch["blocks.promoBanner.formId"]],
118881
- ["blocks.primaryCta.formId", patch["blocks.primaryCta.formId"]]
118882
- ]) {
118883
- if (typeof value === "string" && value && !knownFormIds.has(value)) {
118884
- throw new Error(`${field2}: form "${value}" not found in event.forms. Create the form in the same call (or beforehand) \u2014 a button bound to a missing form does nothing for viewers.`);
118885
- }
118886
- }
118887
- await updateDoc(doc(firestore, "events", args.eventId), patch);
118920
+ await writeEventPatchWithCards(args.eventId, patch, null, assertFormBindingsExist);
118888
118921
  const hints = [];
118889
118922
  if (args.chat?.visibility !== void 0) {
118890
118923
  hints.push("blocks.chat.visibility was stored but the event page does not read it: the chat panel exists exactly when blocks.chat.enabled is true, and sending is opened or closed live with oko_set_chat_mode. Drop the field.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okostream/mcp",
3
- "version": "0.12.10",
3
+ "version": "0.12.11",
4
4
  "description": "Control OKO Stream webinars from Claude, Codex, and other MCP clients.",
5
5
  "license": "UNLICENSED",
6
6
  "author": {