@okostream/mcp 0.20.0 → 0.21.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 (3) hide show
  1. package/README.md +7 -1
  2. package/dist/server.js +100 -11
  3. package/package.json +7 -2
package/README.md CHANGED
@@ -141,7 +141,9 @@ Refusals are explicit and never require guessing:
141
141
  window it bought (the result carries `windowStart`, `windowEnd` and the next
142
142
  steps); a date outside the window, a cleared date, or any date on a page whose
143
143
  show is over is refused. `oko_update_event` may also return `warnings` (advice,
144
- never a refusal).
144
+ never a refusal). It also sets the page's look: `pageTheme` (`auto`, `light`, `dark`) and `pageAccent`,
145
+ the color of buttons and links — nine palette colors are free on every plan; a custom `#rrggbb` is a Pro
146
+ and Max right (on other plans it is stored and the page shows the default color until the plan allows it).
145
147
  - `WORKSPACE_LIMIT`, `SEATS_FULL`, `ENTITLEMENT_REQUIRED` — the plan of the
146
148
  workspace owner does not hold another shared workspace, another member, or
147
149
  the capability at all. A refused invitation stays valid. Plans are bought only
@@ -184,6 +186,10 @@ always works. The result's `sourceVia` says who fetched the image.
184
186
  costs 10 lumens each for that show: the tool answers with the quote first and
185
187
  writes only after `confirm=true`; unpublishing later does not return these
186
188
  top-ups.
189
+ A block exists on the page only while it is enabled; `visibility: "hidden"` is
190
+ the default an enabled block starts in, and the live switch shows or hides an
191
+ enabled block during the show. To prepare a banner and reveal it later, enable
192
+ it hidden (`{"promoBanner": {"enabled": true, "visibility": "hidden"}}`).
187
193
  The live control room is a paid option of a published page (50 lumens, Pro
188
194
  and above) bought in the analytics screen, not through MCP.
189
195
  - Operate live audience controls, chat, moderation, and analytics sessions.
package/dist/server.js CHANGED
@@ -119137,7 +119137,7 @@ function sanitizeScopes(scopes) {
119137
119137
  }
119138
119138
 
119139
119139
  // build/version.js
119140
- var OKO_MCP_VERSION = "0.20.0";
119140
+ var OKO_MCP_VERSION = "0.21.0";
119141
119141
 
119142
119142
  // build/auth.js
119143
119143
  var state = null;
@@ -122308,6 +122308,19 @@ function registerChatTools(server2) {
122308
122308
  });
122309
122309
  }
122310
122310
 
122311
+ // build/pageAccent.js
122312
+ var PAGE_THEMES = ["auto", "light", "dark"];
122313
+ var PAGE_ACCENT_KEYS = ["sky", "blue", "violet", "pink", "orange", "amber", "green", "teal", "graphite"];
122314
+ var HEX6 = /^#[0-9a-fA-F]{6}$/;
122315
+ function parsePageAccent(value) {
122316
+ const text = value.trim();
122317
+ if (PAGE_ACCENT_KEYS.includes(text))
122318
+ return text;
122319
+ if (HEX6.test(text))
122320
+ return text.toLowerCase();
122321
+ throw new Error(`pageAccent must be one of ${PAGE_ACCENT_KEYS.join(", ")} or a color like "#0a7fa3" (six hex digits); got ${JSON.stringify(value)}.`);
122322
+ }
122323
+
122311
122324
  // build/tools/publication.js
122312
122325
  var SERVER_REFUSAL_CODES = /* @__PURE__ */ new Set(["ENTITLEMENT_REQUIRED", "INSUFFICIENT_TOKENS", "WORKSPACE_INACTIVE"]);
122313
122326
  function refusal2(eventId, body, tool = "oko_publish_event") {
@@ -123030,6 +123043,71 @@ async function readLiveLayer(eventId, data, nowMs) {
123030
123043
  return { live, hints };
123031
123044
  }
123032
123045
 
123046
+ // build/tools/blockOffAdvice.js
123047
+ var BLOCK_LEVELS_SENTENCE = "Three levels: `enabled` decides whether the block exists on the page at all; `visibility` (always | hidden) is the default it starts in; the live override (oko_set_block_visibility, the analytics buttons) shows or hides an ENABLED block during the show \u2014 on a disabled block it does nothing.";
123048
+ var PREPARING_FIELDS = {
123049
+ promoBanner: ["visibility", "link", "formId", "pollId"],
123050
+ primaryCta: ["visibility", "title", "text", "buttonLabel", "buttonUrl", "formId", "pollId"]
123051
+ };
123052
+ function touches(patch, fields) {
123053
+ return Boolean(patch) && fields.some((field2) => patch[field2] !== void 0);
123054
+ }
123055
+ function disabledBlockHints(args, storedBlocks) {
123056
+ const hints = [];
123057
+ const names = { promoBanner: "promo banner", primaryCta: "CTA" };
123058
+ for (const key of ["promoBanner", "primaryCta"]) {
123059
+ const patch = args[key];
123060
+ if (!touches(patch, PREPARING_FIELDS[key]))
123061
+ continue;
123062
+ const enabledAfter = patch?.enabled !== void 0 ? patch.enabled === true : asRecord(storedBlocks[key]).enabled === true;
123063
+ if (enabledAfter)
123064
+ continue;
123065
+ hints.push(`The ${names[key]} is still OFF in setup (enabled is not true), so viewers will not see it at all and a live show from analytics cannot reveal it \u2014 its visibility is only the default of an enabled block. To keep it on the page but hidden until the moment, set enabled together with visibility: oko_configure_blocks {"${key}": {"enabled": true, "visibility": "hidden"}}, then show it live with oko_set_block_visibility. On a published page turning a paid block on is charged first and needs confirm \u2014 quote it to the organizer.`);
123066
+ }
123067
+ return hints;
123068
+ }
123069
+ function disabledCardsHint(args, storedEnabled) {
123070
+ const preparing = args.visibility !== void 0 || Array.isArray(args.items) && args.items.length > 0;
123071
+ const enabledAfter = args.enabled !== void 0 ? args.enabled === true : storedEnabled;
123072
+ if (!preparing || enabledAfter)
123073
+ return [];
123074
+ return [
123075
+ 'The cards block is still OFF in setup (enabled is not true), so viewers will not see the cards at all and a live show from analytics cannot reveal them \u2014 visibility is only the default of an enabled block. To keep them on the page but hidden until the moment, call oko_configure_cards {"enabled": true, "visibility": "hidden"}, then show them live with oko_set_block_visibility block "cards". On a published page turning the block on is a paid top-up and needs confirm \u2014 quote it to the organizer.'
123076
+ ];
123077
+ }
123078
+ function disabledOverrideHint(block, visible) {
123079
+ const enable = block === "cards" ? 'oko_configure_cards {"enabled": true, "visibility": "hidden"}' : block === "banner" ? 'oko_configure_blocks {"promoBanner": {"enabled": true, "visibility": "hidden"}}' : block === "cta" ? 'oko_configure_blocks {"primaryCta": {"enabled": true, "visibility": "hidden"}}' : block === "placeholder" ? 'oko_configure_blocks {"videoPlaceholder": {"enabled": true}}' : 'oko_configure_blocks {"chat": {"enabled": true}}';
123080
+ return `The ${block} block is OFF in setup: it does not exist on the page, so ${visible === null ? "there is nothing for this override to reset" : "this live override was stored but viewers see nothing"}. Enable it first (${enable} \u2014 hidden by default if it should appear later), then use this tool at the moment of the show.`;
123081
+ }
123082
+ function preparedButOffFindings(blocks, cards) {
123083
+ const out = [];
123084
+ const tail = " Viewers will not see it, and it cannot be shown live; enable it (hidden by default if it should appear later): ";
123085
+ const promo = asRecord(blocks.promoBanner);
123086
+ if (promo.enabled !== true && (safeString(promo.imageUrl).trim() || safeString(promo.imageMasterUrl).trim() || safeString(promo.link).trim())) {
123087
+ out.push({
123088
+ code: "promo_prepared_but_off",
123089
+ path: "blocks.promoBanner.enabled",
123090
+ message: "The promo banner has an image or link prepared but is OFF in setup." + tail + 'oko_configure_blocks {"promoBanner": {"enabled": true, "visibility": "hidden"}}.'
123091
+ });
123092
+ }
123093
+ const cta = asRecord(blocks.primaryCta);
123094
+ if (cta.enabled !== true && (safeString(cta.title).trim() || safeString(cta.text).trim() || safeString(cta.buttonUrl).trim())) {
123095
+ out.push({
123096
+ code: "cta_prepared_but_off",
123097
+ path: "blocks.primaryCta.enabled",
123098
+ message: "The CTA has a title, text or button URL prepared but is OFF in setup." + tail + 'oko_configure_blocks {"primaryCta": {"enabled": true, "visibility": "hidden"}}.'
123099
+ });
123100
+ }
123101
+ if (!cards.enabled && cards.items.length > 0) {
123102
+ out.push({
123103
+ code: "cards_prepared_but_off",
123104
+ path: "blocks.cards.enabled",
123105
+ message: "The cards block has cards prepared but is OFF in setup." + tail + 'oko_configure_cards {"enabled": true, "visibility": "hidden"}.'
123106
+ });
123107
+ }
123108
+ return out;
123109
+ }
123110
+
123033
123111
  // build/tools/setupValidation.js
123034
123112
  function scheduleWindowPassedByPlan(event, eventDateMs, rawDurationMinutes, nowMs) {
123035
123113
  if (event.analyticsSessionMode !== "schedule")
@@ -123142,9 +123220,10 @@ function validateEventSetup(eventId, event, nowMs = Date.now()) {
123142
123220
  } else if (safeString(promo.imageMasterUrl).trim() && !readImageVariants(promo.imageVariants)) {
123143
123221
  add(warnings, "promo_variants_pending", "blocks.promoBanner.imageVariants", "Promo banner image variants are not ready yet.");
123144
123222
  }
123145
- } else if (safeString(promo.imageUrl).trim() || safeString(promo.imageMasterUrl).trim()) {
123146
- add(suggestions, "promo_disabled_with_image", "blocks.promoBanner.enabled", 'Promo banner has an uploaded image but is disabled in setup, so viewers never see it. Enable it with oko_configure_blocks promoBanner.enabled=true; to hide it only during the live show, keep it enabled and use oko_set_block_visibility block "banner" visible=false.');
123147
123223
  }
123224
+ const cardsForLint = readCardsConfig(event);
123225
+ for (const finding of preparedButOffFindings(blocks, cardsForLint))
123226
+ add(warnings, finding.code, finding.path, finding.message);
123148
123227
  const cta = asRecord(blocks.primaryCta);
123149
123228
  if (cta.enabled === true) {
123150
123229
  checkFormBinding("blocks.primaryCta.formId", cta.formId);
@@ -123562,6 +123641,7 @@ function registerEventTools(server2) {
123562
123641
  notes: [
123563
123642
  "billing (server-owned) is the publication record. Blocks and the control room paid AFTER publication are listed in billing.additions (one element per block or per control room, with the source payEventExtras or reconcile) and billing.controlRoom: true once the control room is paid; the show's paid set is billing.invoice plus billing.additions. Never write it.",
123564
123643
  "blocks.promoBanner.visibility, blocks.primaryCta.visibility, blocks.cards.visibility and blocks.video.placeholder.enabled are SETUP values \u2014 the default a block starts with. A live override (oko_set_block_visibility, or the organizer's switch in analytics) replaces it without touching setup, so these fields do not say what viewers see now: `live.<block>.visibleNow` does, with `decidedBy` naming the layer that decided. oko_get_live_state shows the raw overrides (ctrl.bannerVisible and so on).",
123644
+ "meta.pageTheme and meta.pageAccent are the page's look AS STORED (absent = auto and sky). A custom hex in meta.pageAccent is painted only while the plan that pays for the page holds the right (Pro and Max); otherwise the page shows sky \u2014 this tool does not say which, so for a custom color on an unknown plan read oko_wallet.",
123565
123645
  "blocks.chat: the panel exists exactly when blocks.chat.enabled is true (false HIDES the panel; messages are kept for 90 days and come back when it is enabled again). Message intake is ONE live state \u2014 oko_set_chat_mode reads and writes it; blocks.chat.intake is only the STARTING state an untouched event has (closed by default, or open), and blocks.chat.openOnSessionStart / closeOnSessionEnd are two triggers that flip that same state when an analytics session starts or ends. blocks.chat.mode and blocks.chat.visibility are legacy and read by nobody \u2014 ignore those two."
123566
123646
  ]
123567
123647
  }),
@@ -123681,7 +123761,7 @@ function registerEventTools(server2) {
123681
123761
  description: toolDescription({
123682
123762
  summary: "Update safe top-level event/setup metadata fields.",
123683
123763
  exampleCall: { "eventId": "Kx9pQ2mR7tL4vB8nW3cY", "title": "Autumn product webinar (rescheduled)", "eventDate": "2026-09-12T07:00:00.000Z", "speakerName": "Jane Doe", "description": "", "durationMinutes": 60 },
123684
- args: ["eventId plus any subset of title, videoUrl, eventDate, speakerName, description, pinned, sessionMode, durationMinutes, sessionStartAt, sessionEndAt."],
123764
+ args: ["eventId plus any subset of title, videoUrl, eventDate, speakerName, description, pinned, sessionMode, durationMinutes, sessionStartAt, sessionEndAt, pageTheme, pageAccent."],
123685
123765
  returns: "event id, the list of Firestore fields updated, and session warnings (plan versus event time, lumens versus plan, a plan with no sessions).",
123686
123766
  examples: ["Use for title/date/speaker/video edits; use oko_configure_blocks for blocks and forms."],
123687
123767
  notes: [
@@ -123690,6 +123770,7 @@ function registerEventTools(server2) {
123690
123770
  "sessionStartAt/sessionEndAt: pass BOTH ISO datetimes to set a session time different from the show's, or pass null for both to follow the event time again. A session plan that differs from the event time is reported as a warning (moving eventDate does not move an explicit plan).",
123691
123771
  "Setting sessionMode 'schedule' on a page with no date, or clearing the date of a page in 'schedule', in one call is refused (nothing could start from it); a page born with the auto start on and no date yet is normal and can be edited freely.",
123692
123772
  'On a published page eventDate moves only INSIDE the show window bought at publication (from a day before the event date to two days after it): a date inside is written; a date outside, a cleared date, or any date on a page that shows "held"/\u041F\u0440\u043E\u0432\u0435\u0434\u0435\u043D\u043E is refused with errorCode EVENT_DATE_LOCKED, windowStart/windowEnd and whatToDo \u2014 every other field in the same call still goes through if eventDate is dropped from it. For a date outside the window: oko_unpublish_event (refunds, only before the window opens and only for a page that was charged), change the date, oko_publish_event (new charge); or duplicate the page with oko_duplicate_event for the new date. A draft or an unpublished page moves freely.',
123773
+ `pageTheme (auto | light | dark) is how the viewer page is painted: auto follows each viewer's system, light and dark look the same for everyone. pageAccent is the color of the page's buttons and links: one of the nine palette colors (sky, blue, violet, pink, orange, amber, green, teal, graphite) or a custom "#rrggbb". The nine palette colors are free on every plan. A custom hex is a right of the Pro and Max plans (of the plan that pays for the page): on a plan without it the value is still stored, but the page shows the default color (sky) until the plan allows it \u2014 the choice is not lost. The color applies to buttons and links in both the light and the dark theme and is adjusted for readability (a custom color is lightened or darkened as needed, keeping its hue). Both are read back as stored in oko_get_event, meta.pageTheme and meta.pageAccent; "sky" is the default.`,
123693
123774
  "The result may carry warnings (never a refusal), each { code, message }: about the date \u2014 the show would start inside the window but not fit into it, or the date is already behind us while sessionMode is 'schedule' (the scheduler then starts the analytics session right away) \u2014 and about the session (see above)."
123694
123775
  ]
123695
123776
  }),
@@ -123704,7 +123785,9 @@ function registerEventTools(server2) {
123704
123785
  sessionMode: external_exports.enum(["manual", "schedule"]).optional().describe("Automatic start of the analytics session: 'schedule' starts it by itself at the start of the session plan (the default of a new page), 'manual' leaves the start to oko_start_session. The session always ends at the end of its plan."),
123705
123786
  durationMinutes: external_exports.number().int().min(15).max(1440).optional().describe("Event duration in minutes (15-1440); with eventDate it is the default session plan."),
123706
123787
  sessionStartAt: external_exports.string().datetime().nullable().optional().describe("Start of the session plan (ISO datetime), or null to follow the event time. Pass together with sessionEndAt."),
123707
- sessionEndAt: external_exports.string().datetime().nullable().optional().describe("End of the session plan (ISO datetime), or null to follow the event time. Pass together with sessionStartAt.")
123788
+ sessionEndAt: external_exports.string().datetime().nullable().optional().describe("End of the session plan (ISO datetime), or null to follow the event time. Pass together with sessionStartAt."),
123789
+ pageTheme: external_exports.enum(PAGE_THEMES).optional().describe("How the viewer page is painted: auto (follows each viewer's system, the default), light or dark."),
123790
+ pageAccent: external_exports.string().max(16).optional().describe(`Color of the page's buttons and links: sky (the default), blue, violet, pink, orange, amber, green, teal, graphite \u2014 free on every plan \u2014 or a custom "#rrggbb" (Pro and Max; on other plans stored, page shows sky).`)
123708
123791
  },
123709
123792
  outputSchema: updateOutputSchema,
123710
123793
  annotations: writeTool
@@ -123712,7 +123795,7 @@ function registerEventTools(server2) {
123712
123795
  const requiredScopes = /* @__PURE__ */ new Set();
123713
123796
  if (args.title !== void 0 || args.pinned !== void 0)
123714
123797
  requiredScopes.add("events:write");
123715
- if (args.videoUrl !== void 0 || args.eventDate !== void 0 || args.speakerName !== void 0 || args.description !== void 0 || args.sessionMode !== void 0 || args.durationMinutes !== void 0 || args.sessionStartAt !== void 0 || args.sessionEndAt !== void 0) {
123798
+ if (args.videoUrl !== void 0 || args.eventDate !== void 0 || args.speakerName !== void 0 || args.description !== void 0 || args.sessionMode !== void 0 || args.durationMinutes !== void 0 || args.sessionStartAt !== void 0 || args.sessionEndAt !== void 0 || args.pageTheme !== void 0 || args.pageAccent !== void 0) {
123716
123799
  requiredScopes.add("setup:write");
123717
123800
  }
123718
123801
  if (requiredScopes.size === 0)
@@ -123739,6 +123822,10 @@ function registerEventTools(server2) {
123739
123822
  }
123740
123823
  if (args.pinned !== void 0)
123741
123824
  patch.pinned = args.pinned;
123825
+ if (args.pageTheme !== void 0)
123826
+ patch["meta.pageTheme"] = args.pageTheme;
123827
+ if (args.pageAccent !== void 0)
123828
+ patch["meta.pageAccent"] = parsePageAccent(args.pageAccent);
123742
123829
  if (args.sessionMode !== void 0)
123743
123830
  patch.analyticsSessionMode = args.sessionMode;
123744
123831
  if (args.durationMinutes !== void 0)
@@ -123772,7 +123859,7 @@ function registerEventTools(server2) {
123772
123859
  server2.registerTool("oko_configure_blocks", {
123773
123860
  title: "Configure event blocks",
123774
123861
  description: toolDescription({
123775
- summary: "Configure supported event setup blocks, forms, polls, and Telegram channel ids.",
123862
+ summary: "Configure supported event setup blocks, forms, polls, and Telegram channel ids. " + BLOCK_LEVELS_SENTENCE + ' To upload now and show later, set {"promoBanner": {"enabled": true, "visibility": "hidden"}} (visibility alone leaves a disabled block off the page), then call oko_set_block_visibility at the moment.',
123776
123863
  exampleCall: { "eventId": "Kx9pQ2mR7tL4vB8nW3cY", "chat": { "enabled": true }, "promoBanner": { "enabled": false }, "primaryCta": { "enabled": true, "title": "Get the slides", "buttonLabel": "Download", "buttonUrl": "https://example.com/slides" }, "videoPlaceholder": { "enabled": true, "mode": "glass", "showTimer": true, "title": "The webinar starts at 14:00", "subtitle": "Moscow time" } },
123777
123864
  args: [
123778
123865
  "eventId: event to edit.",
@@ -123927,6 +124014,7 @@ function registerEventTools(server2) {
123927
124014
  if (args.promoBanner?.enabled === true && !bannerHasImage) {
123928
124015
  hints.push(`The promo banner is enabled but has no image yet: upload one with oko_upload_image kind "promoBanner". ${IMAGE_ADVICE.promoBanner}`);
123929
124016
  }
124017
+ hints.push(...disabledBlockHints(args, currentBlocks));
123930
124018
  if (args.chat?.enabled !== void 0 || args.promoBanner?.enabled !== void 0 || args.primaryCta?.enabled !== void 0 || args.videoPlaceholder?.enabled !== void 0) {
123931
124019
  hints.push("Setup toggles (enabled) decide whether a block exists on the page and persist; live overrides (oko_set_block_visibility) show or hide an enabled block temporarily during the show. Uploading an image never enables a block.");
123932
124020
  }
@@ -124059,6 +124147,7 @@ function registerEventTools(server2) {
124059
124147
  warnings.push("The ids shown for those positions are provisional (generated on read) and were NOT stored: oko_upload_image and oko_delete_image will not accept them. Save the cards first by passing items to oko_configure_cards \u2014 the saved ids are the ones to use.");
124060
124148
  }
124061
124149
  }
124150
+ const hints = [...paid.hints ?? [], ...disabledCardsHint(args, readCardsConfig(event).enabled)];
124062
124151
  return textResult({
124063
124152
  eventId: args.eventId,
124064
124153
  updated: Object.keys(patch),
@@ -124069,7 +124158,7 @@ function registerEventTools(server2) {
124069
124158
  preservedImageCardIds,
124070
124159
  warnings,
124071
124160
  ...paid.payment ? { payment: paid.payment } : {},
124072
- ...paid.hints ? { hints: paid.hints } : {}
124161
+ ...hints.length ? { hints } : {}
124073
124162
  });
124074
124163
  });
124075
124164
  }
@@ -125165,7 +125254,7 @@ function registerLiveTools(server2) {
125165
125254
  server2.registerTool("oko_set_block_visibility", {
125166
125255
  title: "Set live block visibility",
125167
125256
  description: toolDescription({
125168
- summary: "Set or reset a live RTDB visibility override for a public event block.",
125257
+ summary: "Set or reset a live RTDB visibility override for a public event block. " + BLOCK_LEVELS_SENTENCE + ' For "upload now, show later": oko_configure_blocks {"promoBanner": {"enabled": true, "visibility": "hidden"}} first, then this tool with block "banner", visible true at the moment.',
125169
125258
  exampleCall: { "eventId": "Kx9pQ2mR7tL4vB8nW3cY", "block": "placeholder", "visible": true, "confirm": true },
125170
125259
  args: ["eventId: event to control.", "block: chat, banner, cta, cards, or placeholder.", "visible: true/false, or null to reset override.", "confirm: must be true."],
125171
125260
  returns: "eventId, block, and effective override value.",
@@ -125199,7 +125288,7 @@ function registerLiveTools(server2) {
125199
125288
  placeholder: setupBlocks.video?.placeholder?.enabled === true
125200
125289
  };
125201
125290
  const chatHint = visible === null ? "Live control cleared: message intake follows the starting state (blocks.chat.intake)." : visible ? "Message sending is open (chat mode public); the panel was not touched." : "Message sending is paused (chat mode off); history stays visible, the panel was not touched.";
125202
- const hints = !setupEnabled[block] ? [`The ${block} block is disabled in setup, so this live override has no visible effect until it is enabled (${block === "cards" ? "oko_configure_cards enabled=true" : "oko_configure_blocks"}).`] : block === "chat" ? [chatHint] : [visible === null ? "Override removed: the block follows its setup visibility again." : `Live override applied on top of setup; visible=null returns the ${block} block to its setup default.`];
125291
+ const hints = !setupEnabled[block] ? [disabledOverrideHint(block, visible)] : block === "chat" ? [chatHint] : [visible === null ? "Override removed: the block follows its setup visibility again." : `Live override applied on top of setup; visible=null returns the ${block} block to its setup default.`];
125203
125292
  if (controlRoom)
125204
125293
  hints.push(String(controlRoom.note));
125205
125294
  const paths = {
@@ -126833,7 +126922,7 @@ function registerWalletTools(server2) {
126833
126922
  "limit: ledger rows per page, default 20, max 50, newest first.",
126834
126923
  "cursor: nextCursor returned by the previous page. The plan and the grants come with the first page only; the balance with every page."
126835
126924
  ],
126836
- returns: `billingUrl (the cabinet's Billing page), plan on the first page (tier core|pro|max|legacy, status active|past_due|canceled_at_period_end|expired, source lava|operator|legacy-exit|transition-b|smoke or null, periodEnd and graceUntil as ISO times or null; plan is null when the organizer has no plan) with planRead ok|unreadable (unreadable: the plan could not be read, so null means unknown, not no plan), hasWallet, balance (whole lumens, null when the organizer has no wallet yet), unit "lumens", grants on the first page (id, bucket plan|rollover|topup, amount, remaining, expiresAt, createdAt), ledger rows (id, kind grant|charge|refund|expire|settlement, amount, delta, balanceAfter, by, at, eventId, showId, workspaceId, postId, sessionId, minutes, forgiven), unreadableGrants/unreadableLedgerRows (rows that did not read: say so, never hide them) and cursor pagination metadata.`,
126925
+ returns: `billingUrl (the cabinet's Billing page), plan on the first page (tier core|pro|max|legacy, status active|past_due|canceled_at_period_end|expired, source lava|tribute|operator|legacy-exit|transition-b|smoke or null, periodEnd and graceUntil as ISO times or null; plan is null when the organizer has no plan) with planRead ok|unreadable (unreadable: the plan could not be read, so null means unknown, not no plan), hasWallet, balance (whole lumens, null when the organizer has no wallet yet), unit "lumens", grants on the first page (id, bucket plan|rollover|topup, amount, remaining, expiresAt, createdAt), ledger rows (id, kind grant|charge|refund|expire|settlement, amount, delta, balanceAfter, by, at, eventId, showId, workspaceId, postId, sessionId, minutes, forgiven), unreadableGrants/unreadableLedgerRows (rows that did not read: say so, never hide them) and cursor pagination metadata.`,
126837
126926
  examples: [
126838
126927
  "Use before publishing a page or starting a session when the organizer asks how many lumens are left or what was charged.",
126839
126928
  "Use to explain a charge or a refund: every ledger row names what paid (eventId, sessionId, postId) and the balance after it."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okostream/mcp",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "Control OKO Stream webinars from Claude, Codex, and other MCP clients.",
5
5
  "license": "UNLICENSED",
6
6
  "author": {
@@ -55,8 +55,13 @@
55
55
  "firebase": "^12.16.0",
56
56
  "ipaddr.js": "2.4.0",
57
57
  "openai": "^6.46.0",
58
- "typescript": "^5.9.3",
58
+ "typescript": "~6.0.3",
59
59
  "undici": "7.30.1",
60
60
  "zod": "^3.25.76"
61
+ },
62
+ "allowScripts": {
63
+ "@firebase/util@1.15.3": true,
64
+ "esbuild@0.24.2": true,
65
+ "protobufjs@7.6.6": true
61
66
  }
62
67
  }