@okostream/mcp 0.18.0 → 0.18.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.
package/README.md CHANGED
@@ -101,7 +101,8 @@ A new page is a draft: viewers of its public URL see "not published".
101
101
  the ledger of the organizer behind the key (scope `wallet:read`, included in
102
102
  the `operator` and `full` presets, not in `readOnly`). Keys created before that scope existed get `SCOPE_REQUIRED` and
103
103
  need a new key. A page in a shared workspace is paid from the workspace
104
- owner's wallet, which is theirs to read.
104
+ owner's wallet: its balance is told, in the previews and refusals, to whoever
105
+ may spend from it; its grants and ledger are the owner's to read.
105
106
  - **Payment and terms are web-only.** Plans and lumens are bought, and terms
106
107
  accepted, only in the OKO web cabinet, and no MCP tool collects a password,
107
108
  card or bank details, or agreement to terms on anyone's behalf.
package/dist/server.js CHANGED
@@ -116320,7 +116320,7 @@ function sanitizeScopes(scopes) {
116320
116320
  }
116321
116321
 
116322
116322
  // build/version.js
116323
- var OKO_MCP_VERSION = "0.18.0";
116323
+ var OKO_MCP_VERSION = "0.18.2";
116324
116324
 
116325
116325
  // build/auth.js
116326
116326
  var state = null;
@@ -119581,7 +119581,7 @@ function sessionOutlookWarnings(outlook) {
119581
119581
  if (outlook.lumens.enoughForPlan === false && outlook.lumens.neededForPlan !== null) {
119582
119582
  warnings.push({
119583
119583
  code: "session_lumens_not_enough_for_plan",
119584
- message: `A session that runs to the end of its plan costs about ${outlook.lumens.neededForPlan} lumens (1 per minute, taken when it ends). The wallet that pays for this workspace holds less: the session would be stopped when the lumens run out. Its owner can top it up. Lumens are checked at Start (a wallet at zero refuses it) and every minute while it runs.`
119584
+ message: `A session that runs to the end of its plan costs about ${outlook.lumens.neededForPlan} lumens (1 per minute, taken when it ends). ` + (typeof outlook.lumens.available === "number" ? `The wallet that pays for this workspace holds ${outlook.lumens.available}, which is less: ` : "The wallet that pays for this workspace holds less: ") + "the session would be stopped when the lumens run out. Its owner can top it up. Lumens are checked at Start (a wallet at zero refuses it) and every minute while it runs."
119585
119585
  });
119586
119586
  }
119587
119587
  return warnings;
@@ -122386,8 +122386,9 @@ function telegramPostRefusalResult(eventId, postId, error3, callerUid) {
122386
122386
  eventId,
122387
122387
  postId,
122388
122388
  needed,
122389
- // The wallet belongs to the payer: its balance is told to the payer only.
122390
- ...callerIsPayer && typeof info.available === "number" ? { available: info.available } : {},
122389
+ // The server tells the balance to whoever may spend from the wallet (the payer and a member who may send: decision
122390
+ // 2026-09-30) — this side relays what it was told, whoever the caller is.
122391
+ ...typeof info.available === "number" ? { available: info.available } : {},
122391
122392
  callerIsPayer,
122392
122393
  message: text || `OKO refused to send the Telegram post: the wallet that pays for this event holds fewer than ${needed} lumens. The post was not sent and nothing was charged.`,
122393
122394
  whatToDo: [
@@ -122853,7 +122854,7 @@ function registerWalletTools(server2) {
122853
122854
  ],
122854
122855
  notes: [
122855
122856
  "Needs the wallet:read scope. A key created before that scope existed gets SCOPE_REQUIRED and its scopes cannot be changed: the organizer creates a new key in the OKO cabinet.",
122856
- "Shows only the wallet of the organizer behind the key. A page in a shared workspace is paid from the WORKSPACE OWNER's wallet: the publish, session and Telegram previews name that payer, and the owner's balance is theirs to read, not this key's.",
122857
+ "Shows only the wallet of the organizer behind the key. A page in a shared workspace is paid from the WORKSPACE OWNER's wallet: the publish, session and Telegram previews and refusals name that payer and tell its balance to whoever may spend from it (publish, start a session, send posts); the owner's grants and ledger are theirs to read, not this key's.",
122857
122858
  "This tool cannot buy, grant, top up or spend lumens. Plans, lumens and payment details belong to the OKO web cabinet only; never ask the organizer for card data or a password.",
122858
122859
  "Refusals of the paid tools use the same codes as the web: ENTITLEMENT_REQUIRED (the plan does not include the feature) and INSUFFICIENT_TOKENS (not enough lumens); PLATFORM_REJECTED_READ here means the platform refused the read."
122859
122860
  ]
@@ -122957,6 +122958,7 @@ function workspaceRefusalResult(tool, error3, subject = {}) {
122957
122958
  }
122958
122959
 
122959
122960
  // build/tools/workspaces.js
122961
+ var INVITE_EMAIL_PATTERN = /^[A-Za-z0-9.!#$%&'*+/=?^_`{|}~-]{1,64}@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)+$/;
122960
122962
  function registerWorkspaceTools(server2) {
122961
122963
  server2.registerTool("oko_list_workspaces", {
122962
122964
  title: "List workspaces",
@@ -123197,8 +123199,8 @@ function registerWorkspaceTools(server2) {
123197
123199
  description: toolDescription({
123198
123200
  summary: "List workspace invite links for a workspace owned by the organizer.",
123199
123201
  args: ["workspaceId: workspace owned by the organizer."],
123200
- returns: "invites array with code, claim status, permissions, and inviteUrl.",
123201
- examples: ["Use before revoking or sharing an invite code."]
123202
+ returns: "invites array with code, claim status, permissions, and inviteUrl; for an invite created with an email also mailStatus (queued | sent | not_sent) and, when not_sent, mailReason (rate_limited | address | not_allowed | undeliverable) \u2014 what the platform did with the letter. No mailStatus on an invite with an email means the platform has not reported yet: do not read it as sent. sent means the provider accepted the letter, not that it was delivered.",
123203
+ examples: ["Use before revoking or sharing an invite code, and to check whether the platform's letter went out."]
123202
123204
  }),
123203
123205
  inputSchema: { workspaceId: external_exports.string().min(1) },
123204
123206
  outputSchema: external_exports.object({ workspaceId: external_exports.string(), invites: external_exports.array(unknownRecordSchema), count: external_exports.number().int().nonnegative() }).passthrough(),
@@ -123270,20 +123272,23 @@ function registerWorkspaceTools(server2) {
123270
123272
  server2.registerTool("oko_create_workspace_invite", {
123271
123273
  title: "Create workspace invite",
123272
123274
  description: toolDescription({
123273
- summary: "Create a workspace invite for a known OKO workspace.",
123275
+ summary: "Create a workspace invite for a known OKO workspace. When `email` is given, OKO Stream itself emails the invitation to that address: an external effect, not a note.",
123274
123276
  args: [
123275
123277
  "workspaceId: workspace owned by the authenticated organizer.",
123276
123278
  "code: optional invite code, 16-64 chars; omitted means generated (recommended).",
123277
- "email: optional intended recipient email.",
123279
+ "email: optional email of the invitee, one plain address (`name@domain`). When given, the PLATFORM sends the invitation letter to it \u2014 the workspace name and the invite link, in the language of the account that owns that address when there is one and it has a language, otherwise in the inviter's language, otherwise English; the `comment` is never put into the letter. A letter cannot be recalled: confirm the exact address with the workspace owner first. Omit it to get only the link.",
123278
123280
  // The schema is `workspacePermissionsSchema`: nine booleans, all
123279
123281
  // required, no extra keys. "optional whitelist" read as "send the
123280
123282
  // ones you want", and every such call was rejected by validation
123281
123283
  // before it reached the platform.
123282
123284
  "permissions: required object of all nine workspace permission booleans (createEvents, editSetup, deleteEvents, controlLive, moderateChat, startSession, viewAnalytics, downloadReports, sendNotifications) \u2014 every key must be present, extra keys are rejected; set false for the ones the invitee should not get.",
123283
- "confirm: must be true."
123285
+ "confirm: must be true. Without it the call is refused with a preview that names the address the platform would email."
123284
123286
  ],
123285
- returns: "workspaceId, invite code, inviteUrl, and created=true.",
123286
- examples: ["Use only after confirming the recipient and permission set with the workspace owner."]
123287
+ returns: "workspaceId, invite code, inviteUrl, created=true, and emailRequested=true when an address was given: the platform queues the letter AFTER the invite exists, and this result does not say the letter was delivered \u2014 what became of it is mailStatus on the invite, read with oko_list_workspace_invites a moment later.",
123288
+ examples: ["Use only after confirming the recipient and permission set with the workspace owner."],
123289
+ notes: [
123290
+ "The invite itself and its inviteUrl are always created and always work. The LETTER is held to limits and is checked again just before it goes out: at most 1 letter an hour for one workspace and address, 3 a day to one address, 20 a day per inviter; and none if the invite was revoked or accepted meanwhile, or the plan has no free seat. When no letter goes out the invite stays valid: pass inviteUrl on yourself \u2014 mailStatus not_sent says so (mailReason not_allowed means the invite cannot be accepted right now, so passing the link on would not help either)."
123291
+ ]
123287
123292
  }),
123288
123293
  inputSchema: {
123289
123294
  workspaceId: external_exports.string().min(1),
@@ -123295,25 +123300,31 @@ function registerWorkspaceTools(server2) {
123295
123300
  // deliberately stays 4: they accept codes that ALREADY exist, including
123296
123301
  // the historical six-character ones.
123297
123302
  code: external_exports.string().min(16).max(MAX_WORKSPACE_INVITE_CODE_LENGTH).optional(),
123298
- email: external_exports.string().email().optional(),
123303
+ email: external_exports.string().max(254).regex(INVITE_EMAIL_PATTERN, "One plain email address (name@domain), no names, lists or spaces.").optional().describe("Optional. When given, OKO Stream emails the invitation to this address."),
123299
123304
  comment: external_exports.string().max(500).default(""),
123300
123305
  permissions: workspacePermissionsSchema,
123301
- confirm: external_exports.boolean().optional()
123306
+ confirm: external_exports.boolean().optional().describe("Required true. With an email, true also means the workspace owner has confirmed that address.")
123302
123307
  },
123303
- outputSchema: external_exports.object({ workspaceId: external_exports.string(), code: external_exports.string(), inviteUrl: external_exports.string().url(), created: external_exports.boolean() }).passthrough(),
123308
+ outputSchema: external_exports.object({ workspaceId: external_exports.string(), code: external_exports.string(), inviteUrl: external_exports.string().url(), created: external_exports.boolean(), emailRequested: external_exports.boolean().optional() }).passthrough(),
123304
123309
  annotations: writeTool
123305
123310
  }, async (args) => {
123306
123311
  const { uid } = await authed(["workspace:write"]);
123307
123312
  const workspace = await readWorkspaceForOwner(args.workspaceId, uid);
123308
123313
  if (!isSharedWorkspaceId(args.workspaceId))
123309
123314
  throw new Error(personalWorkspaceLimitMessage("creating invites"));
123310
- requireConfirm(args.confirm, "oko_create_workspace_invite", { workspaceId: args.workspaceId, code: args.code, email: args.email });
123315
+ requireConfirm(args.confirm, "oko_create_workspace_invite", {
123316
+ workspaceId: args.workspaceId,
123317
+ code: args.code,
123318
+ email: args.email,
123319
+ ...args.email ? { platformEmailsThisAddress: true } : {}
123320
+ });
123311
123321
  const code = (args.code?.trim() || randomInviteCode()).toUpperCase();
123312
123322
  const url = inviteUrl(code);
123323
+ const email2 = args.email?.trim() ?? "";
123313
123324
  await setDoc(doc(firestore, "workspaceInvites", code), {
123314
123325
  workspaceId: args.workspaceId,
123315
123326
  workspaceName: safeString(workspace.name, "Workspace"),
123316
- email: args.email ?? "",
123327
+ email: email2,
123317
123328
  comment: args.comment,
123318
123329
  permissions: normalizeWorkspacePermissions(args.permissions),
123319
123330
  createdBy: uid,
@@ -123321,7 +123332,7 @@ function registerWorkspaceTools(server2) {
123321
123332
  claimed: false,
123322
123333
  claimedBy: null
123323
123334
  });
123324
- return textResult({ workspaceId: args.workspaceId, code, inviteUrl: url, created: true });
123335
+ return textResult({ workspaceId: args.workspaceId, code, inviteUrl: url, created: true, ...email2 ? { emailRequested: true } : {} });
123325
123336
  });
123326
123337
  }
123327
123338
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okostream/mcp",
3
- "version": "0.18.0",
3
+ "version": "0.18.2",
4
4
  "description": "Control OKO Stream webinars from Claude, Codex, and other MCP clients.",
5
5
  "license": "UNLICENSED",
6
6
  "author": {