@okostream/mcp 0.12.16 → 0.13.1

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 +38 -28
  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.16";
116294
+ var OKO_MCP_VERSION = "0.13.1";
116295
116295
 
116296
116296
  // build/auth.js
116297
116297
  var state = null;
@@ -118496,7 +118496,15 @@ function buildCreateWebinarDocument(input) {
118496
118496
  description: input.description
118497
118497
  },
118498
118498
  blocks: {
118499
- chat: { enabled: input.chatEnabled, visibility: "always" },
118499
+ // Starting state closed, session triggers off (owner decision 10.09):
118500
+ // the chat exists, and the organizer opens it in analytics.
118501
+ chat: {
118502
+ enabled: input.chatEnabled,
118503
+ visibility: "always",
118504
+ intake: "closed",
118505
+ openOnSessionStart: false,
118506
+ closeOnSessionEnd: false
118507
+ },
118500
118508
  promoBanner: { enabled: false, text: "", link: "", visibility: "always" }
118501
118509
  }
118502
118510
  };
@@ -118812,7 +118820,7 @@ function registerEventTools(server2) {
118812
118820
  returns: "event document fields plus id and public event URL. The event's access composition (the historical uid arrays) is never returned; ask oko_list_workspace_access for who has access.",
118813
118821
  examples: ["Use after oko_create_webinar to verify setup and return the event URL to the user."],
118814
118822
  notes: [
118815
- "blocks.chat: the panel exists exactly when blocks.chat.enabled is true. blocks.chat.intake says whether it ACCEPTS MESSAGES before anyone touches the live control \u2014 closed (default), open, schedule (at the event time), or session (while an analytics session runs); oko_set_chat_mode overrides it live. blocks.chat.mode and blocks.chat.visibility are legacy and read by nobody \u2014 ignore those two."
118823
+ "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."
118816
118824
  ]
118817
118825
  }),
118818
118826
  inputSchema: { eventId: eventIdField },
@@ -118857,7 +118865,7 @@ function registerEventTools(server2) {
118857
118865
  "workspaceId: optional workspace id; omitted means personal workspace.",
118858
118866
  "videoUrl: optional iframe/source/player URL; normalized by OKO.",
118859
118867
  "eventDate: optional ISO datetime.",
118860
- "chatEnabled: whether the chat block is on the page at creation. It does NOT open message sending: a new event starts with intake closed, so the chat is visible and silent until the organizer opens it (oko_set_chat_mode) or sets blocks.chat.intake."
118868
+ "chatEnabled: whether the chat block is on the page at creation (default true \u2014 the chat exists practically always). It does NOT open message sending: a new event starts closed, so the chat is visible and silent until the organizer opens it (oko_set_chat_mode, or the analytics switch), sets blocks.chat.intake to open, or enables the session triggers in oko_configure_blocks."
118861
118869
  ],
118862
118870
  returns: "new event id, public event URL, and workspaceId.",
118863
118871
  examples: ["Use for a complete create flow, then call oko_configure_blocks and oko_get_event."],
@@ -118870,7 +118878,7 @@ function registerEventTools(server2) {
118870
118878
  eventDate: external_exports.string().datetime().nullable().optional().describe("Optional webinar datetime in ISO format."),
118871
118879
  speakerName: external_exports.string().max(160).nullable().optional().describe("Optional speaker display name."),
118872
118880
  description: external_exports.string().max(3e3).nullable().optional().describe('Optional event description shown under the speaker name. Omit or pass "" for none; the literal text "null" is rejected.'),
118873
- chatEnabled: external_exports.boolean().default(false).describe("Whether the chat block is on the page at creation; message sending stays closed until opened.")
118881
+ chatEnabled: external_exports.boolean().default(true).describe("Whether the chat block is on the page at creation (default true); message sending stays closed until opened.")
118874
118882
  },
118875
118883
  outputSchema: createWebinarOutputSchema,
118876
118884
  annotations: writeTool
@@ -118984,7 +118992,7 @@ function registerEventTools(server2) {
118984
118992
  returns: "event id and the list of Firestore fields updated.",
118985
118993
  examples: ["Use after oko_create_webinar to enable chat, bind a CTA/form, and configure other audience blocks."],
118986
118994
  notes: [
118987
- '\u{1F534} "Turn off the chat" is ambiguous, and the two meanings are not close. chat.enabled=false REMOVES the chat block from the page together with every message people wrote \u2014 irreversible for the audience, and almost never what someone asks for mid-webinar. Stopping new messages is a different tool: oko_set_chat_mode with mode="off" leaves the block and the whole history in place. When the organizer says "turn off the chat", "close the chat", "stop the chat", "disable chat", assume they mean the mode, and ASK before removing the block. The same trap in reverse: "turn the chat back on" after a pause means mode="public", not this field.',
118995
+ '\u{1F534} "Turn off the chat" is ambiguous, and the two meanings are different actions. chat.enabled=false HIDES the chat panel from the page for everyone \u2014 the history is not deleted (messages are kept for 90 days) and comes back when the block is enabled again \u2014 but during or after a webinar that is almost never what is asked for. Stopping new messages is a different tool: oko_set_chat_mode with mode="off" leaves the panel and the history on the page and only closes the input. When the organizer says "turn off the chat", "close the chat", "stop the chat", "mute the chat", "disable chat", assume they mean CLOSING INTAKE (oko_set_chat_mode off), and ask before hiding the panel. The same trap in reverse: "turn the chat back on" after a pause means mode="public", not this field.',
118988
118996
  "A block toggle and its image are independent: enabling a block here works with an image already uploaded via oko_upload_image, and uploading an image never enables the block by itself.",
118989
118997
  "promoBanner has no text field by design: the banner is image+link. blocks.promoBanner.text seen in event documents is legacy and is never rendered on the event page.",
118990
118998
  'videoPlaceholder.enabled only allows the placeholder; viewers see it after oko_set_block_visibility with block "placeholder" and visible true (a live override, stored outside the event document). Chat, CTA, banner and cards appear as soon as they are enabled here.',
@@ -118996,8 +119004,10 @@ function registerEventTools(server2) {
118996
119004
  inputSchema: {
118997
119005
  eventId: eventIdField,
118998
119006
  chat: external_exports.object({
118999
- enabled: external_exports.boolean().optional().describe("Whether the chat block is ON THE PAGE. false removes it together with the message history \u2014 see the note above before using it."),
119000
- intake: external_exports.enum(["closed", "open", "schedule", "session"]).optional().describe("When the chat starts accepting messages before any live override: closed (default \u2014 visible and silent until opened), open, schedule (at the event time), session (while an analytics session runs)."),
119007
+ enabled: external_exports.boolean().optional().describe("Whether the chat panel is ON THE PAGE. false hides it for everyone (history kept for 90 days, back when re-enabled) \u2014 see the note above: to stop new messages use oko_set_chat_mode instead."),
119008
+ intake: external_exports.enum(["closed", "open"]).optional().describe("STARTING state of message intake \u2014 what the chat does while nobody has touched the live control: closed (default \u2014 on the page, history visible, input closed) or open. The live control (oko_set_chat_mode) is the state itself; this field does not override it."),
119009
+ openOnSessionStart: external_exports.boolean().optional().describe("Session trigger: open message intake when an analytics session starts (default false)."),
119010
+ closeOnSessionEnd: external_exports.boolean().optional().describe("Session trigger: close message intake when the analytics session ends (default false)."),
119001
119011
  visibility: external_exports.enum(["always", "hidden"]).optional()
119002
119012
  }).optional(),
119003
119013
  promoBanner: external_exports.object({ enabled: external_exports.boolean().optional(), link: external_exports.string().max(1e3).optional(), visibility: external_exports.enum(["always", "hidden"]).optional(), formId: external_exports.string().nullable().optional() }).optional(),
@@ -119020,6 +119030,10 @@ function registerEventTools(server2) {
119020
119030
  patch["blocks.chat.enabled"] = args.chat.enabled;
119021
119031
  if (args.chat?.intake !== void 0)
119022
119032
  patch["blocks.chat.intake"] = args.chat.intake;
119033
+ if (args.chat?.openOnSessionStart !== void 0)
119034
+ patch["blocks.chat.openOnSessionStart"] = args.chat.openOnSessionStart;
119035
+ if (args.chat?.closeOnSessionEnd !== void 0)
119036
+ patch["blocks.chat.closeOnSessionEnd"] = args.chat.closeOnSessionEnd;
119023
119037
  if (args.chat?.visibility !== void 0)
119024
119038
  patch["blocks.chat.visibility"] = args.chat.visibility;
119025
119039
  if (args.promoBanner?.enabled !== void 0)
@@ -119079,10 +119093,10 @@ function registerEventTools(server2) {
119079
119093
  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.");
119080
119094
  }
119081
119095
  if (args.chat?.enabled === false) {
119082
- hints.push('The chat block is now off in setup: it is gone from the page for everyone, together with every message the audience wrote. If the goal was only to stop NEW messages \u2014 which is what "turn off the chat" usually means during or after a webinar \u2014 re-enable it here and use oko_set_chat_mode with mode "off" instead: the block and the whole history stay in place. Worth confirming with the organizer which of the two they meant.');
119096
+ hints.push('The chat panel is now hidden from the page for everyone. Nothing was deleted: messages are kept for 90 days and come back when the block is enabled again. If the goal was only to stop NEW messages \u2014 which is what "turn off the chat" usually means during or after a webinar \u2014 re-enable the block here and use oko_set_chat_mode with mode "off" instead: the panel and the history stay visible, only the input closes. Worth confirming with the organizer which of the two they meant.');
119083
119097
  }
119084
119098
  if (args.chat?.enabled === true && args.chat?.intake === void 0) {
119085
- hints.push('The chat block is on the page, but message sending stays closed until it is opened: a chat nobody opened is silent by default. Open it now with oko_set_chat_mode mode "public", or set blocks.chat.intake to open / schedule / session so it opens by itself.');
119099
+ hints.push('The chat block is on the page, but message intake stays closed until it is opened: a chat nobody opened is silent by default. Open it now with oko_set_chat_mode mode "public", set blocks.chat.intake to "open" so new events of this kind start open, or enable openOnSessionStart / closeOnSessionEnd so the analytics session opens and closes it by itself.');
119086
119100
  }
119087
119101
  const currentBlocks = current.blocks && typeof current.blocks === "object" ? current.blocks : {};
119088
119102
  const bannerHasImage = Boolean(safeString(currentBlocks.promoBanner?.imageUrl) || safeString(currentBlocks.promoBanner?.imageMasterUrl));
@@ -119894,13 +119908,16 @@ function registerLifecycleTools(server2) {
119894
119908
 
119895
119909
  // build/tools/live.js
119896
119910
  var CHAT_LIVE_SETTINGS = ["mode", "slowModeMs", "automodEnabled", "allowViewerLinks"];
119911
+ async function writeChatControl(eventId, mode) {
119912
+ await update(ref(rtdb, `events/${eventId}/ctrl`), { chatMode: mode, chatVisible: null });
119913
+ }
119897
119914
  function registerLiveTools(server2) {
119898
119915
  server2.registerTool("oko_get_live_state", {
119899
119916
  title: "Get live state",
119900
119917
  description: toolDescription({
119901
119918
  summary: "Read main RTDB live controls, aggregate analytics, and chat settings for an event.",
119902
119919
  args: ["eventId: event to inspect."],
119903
- returns: "eventId, agg metrics, ctrl live overrides, and chat settings (mode, slowModeMs, automodEnabled, allowViewerLinks).",
119920
+ returns: "eventId, agg metrics, ctrl live overrides, and chat settings (mode, slowModeMs, automodEnabled, allowViewerLinks, plus accepting \u2014 whether message intake is open right now, as the server enforces it). ctrl.chatMode is the live control for message intake: public, off, or absent (then the event's starting state blocks.chat.intake applies and accepting shows the result).",
119904
119921
  examples: ["Use during a webinar before changing live block visibility or starting/ending sessions."],
119905
119922
  notes: [
119906
119923
  "Live state does not carry the event team: to see who has access to an event and with which permissions, use oko_list_workspace_access on the event's workspace."
@@ -119923,6 +119940,7 @@ function registerLiveTools(server2) {
119923
119940
  if (value !== null && value !== void 0)
119924
119941
  chat[field2] = value;
119925
119942
  });
119943
+ chat.accepting = chat.mode !== "off";
119926
119944
  return textResult({ eventId, agg: aggSnap.val() ?? {}, ctrl: ctrlSnap.val() ?? {}, chat });
119927
119945
  });
119928
119946
  server2.registerTool("oko_set_block_visibility", {
@@ -119934,7 +119952,7 @@ function registerLiveTools(server2) {
119934
119952
  returns: "eventId, block, and effective override value.",
119935
119953
  examples: ["Use visible=false to hide the CTA live; use visible=null to return to setup defaults."],
119936
119954
  notes: [
119937
- "block=chat with visible=false PAUSES message input: viewers keep seeing the chat history but cannot send. It does not hide the chat panel.",
119955
+ "block=chat controls MESSAGE SENDING, never the panel: visible=true OPENS sending (same as oko_set_chat_mode mode=public), visible=false PAUSES it (mode=off \u2014 viewers keep seeing the history but cannot send), visible=null returns the chat to its starting state (blocks.chat.intake). The panel itself is blocks.chat.enabled in oko_configure_blocks.",
119938
119956
  "Chat is the only block whose override the SERVER enforces: it is written as the chat mode and mirrored into the chat security rules, so a paused chat refuses messages from every client. The other four overrides are page presentation only \u2014 they change what viewers see, not what the server accepts.",
119939
119957
  "Live overrides sit on top of setup: a block disabled in setup (oko_configure_blocks enabled=false) stays absent whatever the override; visible=null returns an enabled block to its setup default. Use overrides for the live show, setup toggles for permanent changes."
119940
119958
  ]
@@ -119959,7 +119977,8 @@ function registerLiveTools(server2) {
119959
119977
  cards: setupBlocks.cards?.enabled === true,
119960
119978
  placeholder: setupBlocks.video?.placeholder?.enabled === true
119961
119979
  };
119962
- const hints = setupEnabled[block] ? [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.`] : [`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"}).`];
119980
+ 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.";
119981
+ 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.`];
119963
119982
  const paths = {
119964
119983
  chat: "chatVisible",
119965
119984
  banner: "bannerVisible",
@@ -119968,16 +119987,7 @@ function registerLiveTools(server2) {
119968
119987
  placeholder: "placeholderVisible"
119969
119988
  };
119970
119989
  if (block === "chat") {
119971
- const modeRef = ref(rtdb, `events/${eventId}/ctrl/chatMode`);
119972
- const patch = { chatVisible: null };
119973
- if (visible === null || visible === false) {
119974
- patch.chatMode = visible === false ? "off" : null;
119975
- } else {
119976
- const current = await get(modeRef);
119977
- if (current.val() === "off")
119978
- patch.chatMode = null;
119979
- }
119980
- await update(ref(rtdb, `events/${eventId}/ctrl`), patch);
119990
+ await writeChatControl(eventId, visible === null ? null : visible ? "public" : "off");
119981
119991
  return textResult({ eventId, block, visible, hints });
119982
119992
  }
119983
119993
  const target = ref(rtdb, `events/${eventId}/ctrl/${paths[block]}`);
@@ -120055,14 +120065,14 @@ function registerLiveTools(server2) {
120055
120065
  exampleCall: { "eventId": "Kx9pQ2mR7tL4vB8nW3cY", "mode": "off", "confirm": true },
120056
120066
  args: [
120057
120067
  "eventId: event to control.",
120058
- "mode: public (anyone can send), off (input closed, history stays visible), or null to fall back to the event's own setting (blocks.chat.intake, closed unless the organizer chose otherwise).",
120068
+ "mode: public (anyone can send), off (input closed, history stays visible), or null to clear the live control so the event's STARTING state applies (blocks.chat.intake \u2014 closed unless the organizer chose open). Message intake is one live state; this tool, the analytics switch and the session triggers all write it.",
120059
120069
  "confirm: must be true."
120060
120070
  ],
120061
120071
  returns: "eventId and the effective chat mode.",
120062
120072
  examples: ['Use mode="off" to pause a chat storm; use mode="public" to reopen.'],
120063
120073
  notes: [
120064
- '\u{1F534} "Turn off the chat" means one of TWO different things, and picking the wrong one destroys what people wrote. THIS tool stops new messages: the block stays on the page, history stays readable, nothing is lost. The other one \u2014 oko_configure_blocks with chat.enabled=false \u2014 removes the block from the page together with the whole conversation. When the organizer says "turn off the chat", "close the chat", "stop the chat", "mute the chat" during or after a webinar, they almost always mean THIS tool. Ask before removing the block.',
120065
- 'This is the full replacement for the legacy pause via oko_set_block_visibility(block="chat"): prefer oko_set_chat_mode.',
120074
+ '\u{1F534} "Turn off the chat" means one of TWO different things. THIS tool stops new messages: the panel stays on the page, history stays readable, nothing changes for the audience except the input. The other one \u2014 oko_configure_blocks with chat.enabled=false \u2014 HIDES the whole panel (history is kept for 90 days and returns when re-enabled), which is a page-layout decision, not a live-show one. When the organizer says "turn off the chat", "close the chat", "stop the chat", "mute the chat", "disable chat" during or after a webinar, they almost always mean THIS tool. Ask before hiding the panel.',
120075
+ 'oko_set_block_visibility(block="chat") writes this same state (visible=true \u2192 public, false \u2192 off, null \u2192 starting state); prefer this tool for the chat, it says what it does.',
120066
120076
  "There are no other levels: the server has exactly two states, sending open or closed. History is always visible; the mode never restricts who can read or hides anyone's messages."
120067
120077
  ]
120068
120078
  }),
@@ -120071,7 +120081,7 @@ function registerLiveTools(server2) {
120071
120081
  // LD5a-2 / A8 (owner decision 03.09): "auth" and "owner" were promised
120072
120082
  // here and enforced nowhere — the rule and every client only check
120073
120083
  // `mode !== "off"`. The contract now says what the server does.
120074
- mode: external_exports.enum(["public", "off"]).nullable().describe("Chat mode: public or off; null drops the live override and falls back to the event setting."),
120084
+ mode: external_exports.enum(["public", "off"]).nullable().describe("Chat mode: public or off; null clears the live control so the event's starting state (blocks.chat.intake) applies."),
120075
120085
  confirm: external_exports.boolean().optional().describe("Required true to change the live chat mode.")
120076
120086
  },
120077
120087
  outputSchema: basicEventOutputSchema,
@@ -120080,7 +120090,7 @@ function registerLiveTools(server2) {
120080
120090
  const { uid } = await authed(["live:control"]);
120081
120091
  await readEvent(eventId, uid, "live");
120082
120092
  requireConfirm(confirm, "oko_set_chat_mode", { eventId, mode });
120083
- await update(ref(rtdb, `events/${eventId}/ctrl`), { chatMode: mode, chatVisible: null });
120093
+ await writeChatControl(eventId, mode);
120084
120094
  return textResult({ eventId, mode });
120085
120095
  });
120086
120096
  server2.registerTool("oko_force_viewer_reload", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okostream/mcp",
3
- "version": "0.12.16",
3
+ "version": "0.13.1",
4
4
  "description": "Control OKO Stream webinars from Claude, Codex, and other MCP clients.",
5
5
  "license": "UNLICENSED",
6
6
  "author": {