@okostream/mcp 0.13.0 → 0.13.2

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 +24 -19
  2. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -116230,6 +116230,15 @@ if (!env.firebase.chatDatabaseURL) {
116230
116230
  throw new Error("OKO MCP: chat database URL is not configured. Set OKO_FIREBASE_CHAT_DB_URL (or keep the bundled default) \u2014 chat paths do not exist in the main database.");
116231
116231
  }
116232
116232
  var chatRtdb = getDatabase(firebaseApp, env.firebase.chatDatabaseURL);
116233
+ async function serverNowMs(db = chatRtdb) {
116234
+ const offset = await new Promise((resolve3) => {
116235
+ onValue(ref(db, ".info/serverTimeOffset"), (snap) => {
116236
+ const v = snap.val();
116237
+ resolve3(typeof v === "number" && Number.isFinite(v) ? v : 0);
116238
+ }, () => resolve3(0), { onlyOnce: true });
116239
+ });
116240
+ return Date.now() + offset;
116241
+ }
116233
116242
  var functions = getFunctions(firebaseApp, "us-central1");
116234
116243
 
116235
116244
  // build/scopes.js
@@ -116291,7 +116300,7 @@ function sanitizeScopes(scopes) {
116291
116300
  }
116292
116301
 
116293
116302
  // build/version.js
116294
- var OKO_MCP_VERSION = "0.13.0";
116303
+ var OKO_MCP_VERSION = "0.13.2";
116295
116304
 
116296
116305
  // build/auth.js
116297
116306
  var state = null;
@@ -118346,7 +118355,7 @@ function registerChatTools(server2) {
118346
118355
  const { uid } = await authed(["chat:moderate"]);
118347
118356
  await readEvent(eventId, uid, "moderate");
118348
118357
  requireConfirm(confirm, "oko_delete_chat_message", { eventId, messageId });
118349
- await set(ref(chatRtdb, `mod/deleted/${eventId}/${messageId}`), { deletedAt: serverTimestamp(), ts: Date.now(), by: uid, reason: "manual" });
118358
+ await set(ref(chatRtdb, `mod/deleted/${eventId}/${messageId}`), { deletedAt: serverTimestamp(), ts: await serverNowMs(), by: uid, reason: "manual" });
118350
118359
  return textResult({ eventId, messageId, deleted: true });
118351
118360
  });
118352
118361
  server2.registerTool("oko_mute_user", {
@@ -118364,7 +118373,7 @@ function registerChatTools(server2) {
118364
118373
  const actor = await authed(["chat:moderate"]);
118365
118374
  await readEvent(eventId, actor.uid, "moderate");
118366
118375
  requireConfirm(confirm, "oko_mute_user", { eventId, targetUid, minutes });
118367
- await set(ref(chatRtdb, `mutes/${eventId}/${targetUid}`), { until: Date.now() + minutes * 6e4, by: actor.uid, reason: "manual" });
118376
+ await set(ref(chatRtdb, `mutes/${eventId}/${targetUid}`), { until: await serverNowMs() + minutes * 6e4, by: actor.uid, reason: "manual" });
118368
118377
  return textResult({ eventId, targetUid, mutedMinutes: minutes });
118369
118378
  });
118370
118379
  server2.registerTool("oko_ban_user", {
@@ -118386,9 +118395,10 @@ function registerChatTools(server2) {
118386
118395
  const messagesSnap = await get(query(ref(chatRtdb, `messages/${eventId}`), limitToLast(500)));
118387
118396
  const messages = messagesSnap.val() ?? {};
118388
118397
  const deletes = {};
118398
+ const nowMs = await serverNowMs();
118389
118399
  for (const [messageId, message] of Object.entries(messages)) {
118390
118400
  if (message.uid === targetUid)
118391
- deletes[`mod/deleted/${eventId}/${messageId}`] = { deletedAt: serverTimestamp(), ts: Date.now(), by: actor.uid, reason: "ban" };
118401
+ deletes[`mod/deleted/${eventId}/${messageId}`] = { deletedAt: serverTimestamp(), ts: nowMs, by: actor.uid, reason: "ban" };
118392
118402
  }
118393
118403
  if (Object.keys(deletes).length > 0)
118394
118404
  await update(ref(chatRtdb), deletes);
@@ -119908,13 +119918,16 @@ function registerLifecycleTools(server2) {
119908
119918
 
119909
119919
  // build/tools/live.js
119910
119920
  var CHAT_LIVE_SETTINGS = ["mode", "slowModeMs", "automodEnabled", "allowViewerLinks"];
119921
+ async function writeChatControl(eventId, mode) {
119922
+ await update(ref(rtdb, `events/${eventId}/ctrl`), { chatMode: mode, chatVisible: null });
119923
+ }
119911
119924
  function registerLiveTools(server2) {
119912
119925
  server2.registerTool("oko_get_live_state", {
119913
119926
  title: "Get live state",
119914
119927
  description: toolDescription({
119915
119928
  summary: "Read main RTDB live controls, aggregate analytics, and chat settings for an event.",
119916
119929
  args: ["eventId: event to inspect."],
119917
- 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).",
119930
+ 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).",
119918
119931
  examples: ["Use during a webinar before changing live block visibility or starting/ending sessions."],
119919
119932
  notes: [
119920
119933
  "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."
@@ -119949,7 +119962,7 @@ function registerLiveTools(server2) {
119949
119962
  returns: "eventId, block, and effective override value.",
119950
119963
  examples: ["Use visible=false to hide the CTA live; use visible=null to return to setup defaults."],
119951
119964
  notes: [
119952
- "block=chat with visible=false PAUSES message input: viewers keep seeing the chat history but cannot send. It does not hide the chat panel.",
119965
+ "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.",
119953
119966
  "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.",
119954
119967
  "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."
119955
119968
  ]
@@ -119974,7 +119987,8 @@ function registerLiveTools(server2) {
119974
119987
  cards: setupBlocks.cards?.enabled === true,
119975
119988
  placeholder: setupBlocks.video?.placeholder?.enabled === true
119976
119989
  };
119977
- 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"}).`];
119990
+ 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.";
119991
+ 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.`];
119978
119992
  const paths = {
119979
119993
  chat: "chatVisible",
119980
119994
  banner: "bannerVisible",
@@ -119983,16 +119997,7 @@ function registerLiveTools(server2) {
119983
119997
  placeholder: "placeholderVisible"
119984
119998
  };
119985
119999
  if (block === "chat") {
119986
- const modeRef = ref(rtdb, `events/${eventId}/ctrl/chatMode`);
119987
- const patch = { chatVisible: null };
119988
- if (visible === null || visible === false) {
119989
- patch.chatMode = visible === false ? "off" : null;
119990
- } else {
119991
- const current = await get(modeRef);
119992
- if (current.val() === "off")
119993
- patch.chatMode = null;
119994
- }
119995
- await update(ref(rtdb, `events/${eventId}/ctrl`), patch);
120000
+ await writeChatControl(eventId, visible === null ? null : visible ? "public" : "off");
119996
120001
  return textResult({ eventId, block, visible, hints });
119997
120002
  }
119998
120003
  const target = ref(rtdb, `events/${eventId}/ctrl/${paths[block]}`);
@@ -120077,7 +120082,7 @@ function registerLiveTools(server2) {
120077
120082
  examples: ['Use mode="off" to pause a chat storm; use mode="public" to reopen.'],
120078
120083
  notes: [
120079
120084
  '\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.',
120080
- 'This is the full replacement for the legacy pause via oko_set_block_visibility(block="chat"): prefer oko_set_chat_mode.',
120085
+ '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.',
120081
120086
  "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."
120082
120087
  ]
120083
120088
  }),
@@ -120095,7 +120100,7 @@ function registerLiveTools(server2) {
120095
120100
  const { uid } = await authed(["live:control"]);
120096
120101
  await readEvent(eventId, uid, "live");
120097
120102
  requireConfirm(confirm, "oko_set_chat_mode", { eventId, mode });
120098
- await update(ref(rtdb, `events/${eventId}/ctrl`), { chatMode: mode, chatVisible: null });
120103
+ await writeChatControl(eventId, mode);
120099
120104
  return textResult({ eventId, mode });
120100
120105
  });
120101
120106
  server2.registerTool("oko_force_viewer_reload", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okostream/mcp",
3
- "version": "0.13.0",
3
+ "version": "0.13.2",
4
4
  "description": "Control OKO Stream webinars from Claude, Codex, and other MCP clients.",
5
5
  "license": "UNLICENSED",
6
6
  "author": {