@okostream/mcp 0.7.1 → 0.7.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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.2 - 2026-07-23
4
+
5
+ - Replaced the maintainer-oriented npm README with a concise organizer setup,
6
+ capability, safety, image-access, and troubleshooting guide.
7
+ - Removed the private repository link and added public homepage, author, and
8
+ discovery metadata.
9
+ - Disabled local image reads until `OKO_IMAGE_ROOTS` explicitly lists trusted
10
+ directories.
11
+ - Added the MCP client version to API-key exchange requests so the platform can
12
+ introduce a controlled minimum-version gate in a separate auth release.
13
+
3
14
  ## 0.7.1 - 2026-07-22
4
15
 
5
16
  - Refreshed transitive dependencies after new npm advisories without changing the MCP tool/resource/prompt surface.
package/README.md CHANGED
@@ -1,20 +1,25 @@
1
- # OKO MCP Server
1
+ # OKO MCP
2
2
 
3
- MCP server for managing OKO Stream webinars as an organizer.
3
+ Control OKO Stream webinars from Claude, Codex, or another stdio MCP client.
4
+ Create webinar pages, configure audience blocks, manage live interactions, and
5
+ read analytics without switching to the organizer dashboard.
4
6
 
5
- ## Quick Setup
7
+ ## Connect
6
8
 
7
- 1. Create an API key in OKO: `/app/mcp` -> access keys.
8
- 2. Add the server to your MCP client config.
9
+ You need an OKO organizer account and Node.js 20.18.1 or newer.
9
10
 
10
- Claude Desktop / Claude Code:
11
+ 1. Open [OKO MCP settings](https://okostream.app/app/mcp).
12
+ 2. Create an access key and select only the permissions you need.
13
+ 3. Add OKO to your MCP client.
14
+
15
+ ### Claude Desktop or Claude Code
11
16
 
12
17
  ```json
13
18
  {
14
19
  "mcpServers": {
15
20
  "oko": {
16
21
  "command": "npx",
17
- "args": ["-y", "@okostream/mcp"],
22
+ "args": ["-y", "@okostream/mcp@latest"],
18
23
  "env": {
19
24
  "OKO_API_KEY": "oko_sk_..."
20
25
  }
@@ -23,160 +28,106 @@ Claude Desktop / Claude Code:
23
28
  }
24
29
  ```
25
30
 
26
- Codex:
31
+ ### Codex
27
32
 
28
33
  ```toml
29
34
  [mcp_servers.oko]
30
35
  command = "npx"
31
- args = ["-y", "@okostream/mcp"]
36
+ args = ["-y", "@okostream/mcp@latest"]
32
37
 
33
38
  [mcp_servers.oko.env]
34
39
  OKO_API_KEY = "oko_sk_..."
35
40
  ```
36
41
 
37
- The package includes the public Firebase config for `okostreamcore`, so `OKO_API_KEY` is the only required variable. For staging or local experiments, override these optional variables:
38
-
39
- ```bash
40
- OKO_EXCHANGE_URL=...
41
- OKO_FIREBASE_API_KEY=...
42
- OKO_FIREBASE_AUTH_DOMAIN=...
43
- OKO_FIREBASE_DATABASE_URL=...
44
- OKO_FIREBASE_CHAT_DB_URL=...
45
- OKO_FIREBASE_PROJECT_ID=...
46
- OKO_FIREBASE_STORAGE_BUCKET=...
47
- OKO_FIREBASE_MESSAGING_SENDER_ID=...
48
- OKO_FIREBASE_APP_ID=...
49
- # Path-delimited allowlist for local image reads; defaults to the process cwd.
50
- OKO_IMAGE_ROOTS=/trusted/assets:/another/trusted/folder
51
- ```
52
-
53
- Node.js 20.18.1 or newer is required.
54
-
55
- ## What OKO Does
56
-
57
- OKO is a webinar experience and operations layer, not a video-streaming host. The organizer supplies a YouTube, iframe, or other player URL; OKO adds the event page, configurable audience blocks, chat, analytics, and NMO attendance controls around it. Start/End tools control analytics sessions only, never the underlying video stream.
58
-
59
- Standard webinars are the default supported workflow. NMO is a separate unfinished MVP mode and must be enabled only on an explicit organizer request after reviewing `oko://guide/nmo`; it is not a normal option inferred from a medical topic or audience.
42
+ Restart the MCP client, then ask:
60
43
 
61
- ## Security Model
44
+ > Check my OKO connection with `oko_whoami`, then show my five most recent
45
+ > webinars. Do not change anything.
62
46
 
63
- - Each API key exchanges into its own Firebase identity `mcp_<keyId>`; it never inherits platform-owner claims from the organizer account.
64
- - Every protected operation is checked independently by MCP scope, active key state, organizer identity, and event/workspace capability in Cloud Functions and Firebase Rules.
65
- - Revocation disables the synthetic identity, revokes refresh tokens, and invalidates Firestore, main RTDB, and chat RTDB access immediately.
66
- - API-key creation and revocation are browser-only callables. Raw secrets are hashed at rest and shown once.
67
- - MCP image uploads go through a scoped callable; MCP sessions cannot write Firebase Storage directly.
68
- - Local files are constrained to `OKO_IMAGE_ROOTS`, symlink escapes are rejected, and remote images use HTTPS with DNS pinning, redirect revalidation, and byte/pixel limits.
69
- - Every non-read-only tool writes a compact server-owned audit event. Message bodies, captions, lead values, and file contents are not copied into the audit record.
70
- - Destructive, live, public-upload, claim, chat-send, and Telegram actions require `confirm:true`.
71
- - Event, lead, participant, chat, URL, and remote-image content is untrusted data, never instructions for the MCP client.
47
+ ## What You Can Do
72
48
 
73
- ## Response Contract
49
+ - Create, duplicate, move, restore, and delete webinar events.
50
+ - Configure video, metadata, forms, calls to action, cards, banners, and images.
51
+ - Operate webinar chat, moderation, checkpoints, and live block visibility.
52
+ - Start and finish analytics sessions and prepare session reports.
53
+ - Manage workspaces, invitations, access rights, and notifications.
54
+ - Connect Telegram channels and schedule or publish event posts.
74
55
 
75
- Every tool returns JSON text plus MCP `structuredContent`. List-like tools use bounded pages:
56
+ Standard webinars are the default workflow. NMO is a separate beta mode and is
57
+ used only when the organizer explicitly requests it.
76
58
 
77
- - `limit` defaults to 20-50 depending on the tool.
78
- - Cursor tools return both `hasMore`/`nextCursor` and `has_more`/`next_cursor`.
79
- - `oko_list_my_events`, `oko_get_analytics`, `oko_list_recent_chat`, `oko_list_participants`, `oko_list_leads`, `oko_list_notifications`, `oko_list_workspace_access`, `oko_list_workspace_invites`, and `oko_list_telegram_posts` support pagination.
80
- - `oko_get_analytics` omits heavy `timeline`, baseline/final snapshots, and NMO participant rows by default. Request `includeTimeline`, `includeSnapshots`, or `includeNmoRows` only when needed.
81
- - `oko_get_session_report` returns a compact structured session report plus markdown suitable for a human handoff.
82
- - Standard webinar Start/End uses server-owned Analytics v3 callables shared with the dashboard; completed reports include explicit data-quality markers. NMO remains a separate unfinished special-mode workflow.
83
- - Oversized responses are truncated with `truncated: true` and a `truncationMessage`.
59
+ ## Images
84
60
 
85
- The server also exposes MCP-native onboarding:
61
+ Public HTTPS image URLs work without additional configuration.
86
62
 
87
- - `instructions` on initialize with the supported webinar creation flow and product invariants.
88
- - Resources: `oko://guide/create-webinar`, `oko://guide/platform`, `oko://guide/security`, `oko://guide/nmo`, `oko://schema/event`, `oko://guide/telegram`, `oko://capabilities`.
89
- - Prompts: `create_webinar_from_brief`, `audit_event_setup`, `prepare_session_report`.
63
+ Local file access is disabled by default. To let OKO MCP upload images from your
64
+ computer, add an explicit trusted-directory allowlist to the client config.
65
+ Use a dedicated folder rather than your home directory.
90
66
 
91
- This package is published as source-available runtime distribution for OKO customers. `license: UNLICENSED` means all rights reserved unless a separate agreement says otherwise.
92
-
93
- ## Local Development
94
-
95
- ```bash
96
- cd mcp
97
- npm install
98
- npm run build
99
- npm start
100
- ```
101
-
102
- Local config without npm publishing:
67
+ ### Claude with Local Images
103
68
 
104
69
  ```json
105
70
  {
106
71
  "mcpServers": {
107
72
  "oko": {
108
- "command": "node",
109
- "args": ["/absolute/path/to/okostream/mcp/dist/server.js"],
73
+ "command": "npx",
74
+ "args": ["-y", "@okostream/mcp@latest"],
110
75
  "env": {
111
- "OKO_API_KEY": "oko_sk_..."
76
+ "OKO_API_KEY": "oko_sk_...",
77
+ "OKO_IMAGE_ROOTS": "/Users/you/Documents/OKO"
112
78
  }
113
79
  }
114
80
  }
115
81
  }
116
82
  ```
117
83
 
118
- ## Tools
84
+ ### Codex with Local Images
119
85
 
120
- All tools are prefixed with `oko_` for safe discovery next to other MCP servers.
121
-
122
- Core discovery/read tools:
123
-
124
- - `oko_whoami`, `oko_list_workspaces`, `oko_list_my_events`, `oko_get_event`
125
- - `oko_validate_event_setup`
126
- - `oko_update_profile`, `oko_list_notifications`, `oko_mark_notifications_read`
127
- - `oko_get_live_state`, `oko_get_analytics`, `oko_get_session_report`, `oko_list_recent_chat`, `oko_list_participants`, `oko_list_leads`, `oko_delete_lead`
128
-
129
- Event/setup/media tools:
130
-
131
- - `oko_create_webinar`, `oko_update_event`, `oko_delete_event` (30-day trash compatibility alias)
132
- - `oko_trash_event`, `oko_restore_event`, `oko_delete_event_permanently`
133
- - `oko_duplicate_event`, `oko_move_event`
134
- - `oko_configure_blocks`, `oko_configure_cards`
135
- - `oko_upload_image`, `oko_upload_card_images_batch`, `oko_wait_for_image_variants`, `oko_delete_image`
136
-
137
- Live/session/chat/NMO tools:
86
+ ```toml
87
+ [mcp_servers.oko]
88
+ command = "npx"
89
+ args = ["-y", "@okostream/mcp@latest"]
138
90
 
139
- - `oko_set_block_visibility`, `oko_refresh_viewers`
140
- - `oko_start_session`, `oko_end_session`, `oko_fix_orphan_session`, `oko_delete_session`
141
- - `oko_send_chat_message`, `oko_delete_chat_message`, `oko_mute_user`, `oko_unmute_user`, `oko_ban_user`, `oko_unban_user`, `oko_set_chat_settings`
142
- - `oko_launch_checkpoint`, `oko_close_checkpoint`
143
- - `oko_add_nmo_participant`, `oko_import_nmo_participants`, `oko_remove_nmo_participant`
91
+ [mcp_servers.oko.env]
92
+ OKO_API_KEY = "oko_sk_..."
93
+ OKO_IMAGE_ROOTS = "/Users/you/Documents/OKO"
94
+ ```
144
95
 
145
- Workspace and Telegram tools:
96
+ Use `:` between multiple directories on macOS and Linux, or `;` on Windows.
97
+ Paths must be absolute. Filesystem roots are rejected, and symlinks cannot
98
+ escape the configured directories.
146
99
 
147
- - `oko_create_workspace`, `oko_update_workspace`, `oko_delete_workspace`
148
- - `oko_list_workspace_access`, `oko_update_workspace_member`, `oko_remove_workspace_member`
149
- - `oko_create_workspace_invite`, `oko_list_workspace_invites`, `oko_revoke_workspace_invite`, `oko_claim_workspace_invite`
150
- - `oko_create_telegram_link`, `oko_list_telegram_channels`, `oko_claim_telegram_channel`
151
- - `oko_set_telegram_channel_workspaces`, `oko_disconnect_telegram_channel`
152
- - `oko_list_telegram_posts`, `oko_cancel_telegram_post`, `oko_publish_telegram_post`
100
+ ## Access and Safety
153
101
 
154
- Dangerous or external side-effect tools require `confirm: true`.
102
+ - The access key acts only as the organizer and permissions selected for that
103
+ key. It does not inherit platform-administrator access.
104
+ - Revoking a key in OKO immediately disables its Firebase and MCP access.
105
+ - Destructive, live, public-upload, chat-send, claim, and Telegram actions
106
+ require explicit confirmation.
107
+ - Keys stay in the local MCP client environment. The npm package contains no
108
+ organizer key or other private credential.
109
+ - Event content, chat messages, URLs, and remote images are treated as data,
110
+ never as instructions for the AI client.
155
111
 
156
- ## Smoke
112
+ The Firebase web configuration bundled with the package is public project
113
+ metadata. Access to OKO data is enforced separately by the access-key identity,
114
+ scopes, organizer permissions, Cloud Functions, and Firebase Security Rules.
157
115
 
158
- 1. `oko_whoami` should return the organizer UID and granted scopes.
159
- 2. `oko_list_my_events` should return only events visible to that UID.
160
- 3. `oko_create_webinar` should create a test webinar and RTDB owner/workspace mirror.
161
- 4. `oko_configure_cards` should create/update `blocks.cards.items` and preserve card image fields by stable card id.
162
- 5. `oko_upload_card_images_batch` should upload multiple card images, then return ready `imageVariants`.
163
- 6. `oko_validate_event_setup` should catch missing images/forms/buttons/video/NMO/Telegram/variants on a deliberately broken event.
164
- 7. `oko_start_session` -> `oko_fix_orphan_session` -> `oko_delete_session` should leave no active session pointers on a disposable test event.
165
- 8. `oko_duplicate_event`, workspace tools, and `oko_move_event` should preserve Firestore access rules and RTDB owner/workspace mirrors.
166
- 9. `oko_import_nmo_participants` should call CF24 and return per-row results; clean up test participants after import.
167
- 10. `oko_get_session_report` should return markdown and structured session data for a completed session.
168
- 11. `oko_list_notifications` -> `oko_mark_notifications_read` should read and clear an own disposable notification.
169
- 12. `oko_get_analytics` should read an existing event with analytics sessions.
170
- 13. MCP protocol smoke should report 65 tools, 7 resources, 3 prompts, and non-empty initialize instructions.
116
+ ## Troubleshooting
171
117
 
172
- CLI help:
118
+ Check the installed package:
173
119
 
174
120
  ```bash
175
- npx -y @okostream/mcp --help
121
+ npx -y @okostream/mcp@latest --help
176
122
  ```
177
123
 
178
- ## Troubleshooting
179
-
180
- If Claude shows `Unexpected token '◇'`, the MCP server printed a non-JSON line to stdout. Use the current version with `dotenv quiet:true`, then fully restart the MCP client.
124
+ - **Invalid or revoked key:** create a new key in
125
+ [OKO MCP settings](https://okostream.app/app/mcp) and replace
126
+ `OKO_API_KEY`.
127
+ - **Claude reports `Unexpected token`:** make sure the config uses
128
+ `@okostream/mcp@latest`, fully quit the MCP client, and open it again.
129
+ - **A local image is rejected:** add its directory to `OKO_IMAGE_ROOTS`, then
130
+ restart the client.
181
131
 
182
- For stdio MCP, stdout must contain only JSON-RPC. Diagnostics belong on stderr.
132
+ Settings, key management, and installation snippets are always available at
133
+ [okostream.app/app/mcp](https://okostream.app/app/mcp).
package/dist/auth.js CHANGED
@@ -2,18 +2,27 @@ import { signInWithCustomToken } from "firebase/auth";
2
2
  import { auth } from "./firebase.js";
3
3
  import { env } from "./env.js";
4
4
  import { sanitizeScopes } from "./scopes.js";
5
+ import { OKO_MCP_VERSION } from "./version.js";
5
6
  let state = null;
6
7
  async function exchangeApiKey() {
7
8
  const response = await fetch(env.exchangeUrl, {
8
9
  method: "POST",
9
10
  headers: { "content-type": "application/json" },
10
- body: JSON.stringify({ apiKey: env.apiKey }),
11
+ body: JSON.stringify({ apiKey: env.apiKey, clientVersion: OKO_MCP_VERSION }),
11
12
  });
12
13
  if (!response.ok) {
13
14
  const status = response.status;
15
+ const errorBody = await response.json().catch(() => ({}));
16
+ if (status === 426) {
17
+ const minimumVersion = typeof errorBody.minimumVersion === "string"
18
+ ? errorBody.minimumVersion
19
+ : "the current release";
20
+ throw new Error(`OKO MCP ${OKO_MCP_VERSION} is no longer supported. Update to @okostream/mcp@latest ` +
21
+ `(minimum ${minimumVersion}) and restart the MCP client.`);
22
+ }
14
23
  throw new Error(status === 401
15
24
  ? "OKO API key is invalid or revoked. Create a fresh key in OKO Settings and set OKO_API_KEY."
16
- : `OKO API key exchange failed with HTTP ${status}. Check OKO_EXCHANGE_URL and try again.`);
25
+ : `OKO API key exchange failed with HTTP ${status}. Check your connection and try again.`);
17
26
  }
18
27
  const data = await response.json();
19
28
  if (typeof data.customToken !== "string") {
package/dist/env.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { config } from "dotenv";
2
2
  import { homedir } from "node:os";
3
- import { delimiter, join, resolve } from "node:path";
3
+ import { delimiter, isAbsolute, join, parse, resolve } from "node:path";
4
4
  import { DEFAULT_EXCHANGE_URL, DEFAULT_FIREBASE_CONFIG } from "./firebaseDefaults.js";
5
5
  config({ path: join(homedir(), ".env"), quiet: true });
6
6
  function readEnv(...names) {
@@ -19,15 +19,31 @@ function readEnvWithDefault(defaultValue, ...names) {
19
19
  }
20
20
  return defaultValue;
21
21
  }
22
+ function readImageRoots() {
23
+ const rawRoots = process.env.OKO_IMAGE_ROOTS?.trim();
24
+ if (!rawRoots)
25
+ return [];
26
+ return rawRoots.split(delimiter).map((value) => {
27
+ const root = value.trim();
28
+ if (!root) {
29
+ throw new Error("OKO_IMAGE_ROOTS contains an empty directory entry.");
30
+ }
31
+ const expanded = root === "~" || root.startsWith("~/")
32
+ ? join(homedir(), root.slice(2))
33
+ : root;
34
+ if (!isAbsolute(expanded)) {
35
+ throw new Error(`OKO_IMAGE_ROOTS entries must be absolute paths: ${root}`);
36
+ }
37
+ const resolved = resolve(expanded);
38
+ if (resolved === parse(resolved).root) {
39
+ throw new Error("OKO_IMAGE_ROOTS must not allow an entire filesystem root.");
40
+ }
41
+ return resolved;
42
+ });
43
+ }
22
44
  export const env = {
23
45
  apiKey: readEnv("OKO_API_KEY"),
24
- imageRoots: (process.env.OKO_IMAGE_ROOTS?.trim() || process.cwd())
25
- .split(delimiter)
26
- .map((root) => root.trim())
27
- .map((root) => root === "~" || root.startsWith("~/")
28
- ? resolve(join(homedir(), root.slice(2)))
29
- : resolve(root))
30
- .filter(Boolean),
46
+ imageRoots: readImageRoots(),
31
47
  exchangeUrl: readEnvWithDefault(DEFAULT_EXCHANGE_URL, "OKO_EXCHANGE_URL"),
32
48
  firebase: {
33
49
  apiKey: readEnvWithDefault(DEFAULT_FIREBASE_CONFIG.apiKey, "OKO_FIREBASE_API_KEY", "NEXT_PUBLIC_FIREBASE_API_KEY"),
package/dist/guides.js CHANGED
@@ -112,7 +112,7 @@ const GUIDE_RESOURCES = [
112
112
  "- MCP image bytes cross a scoped callable; MCP sessions cannot write Firebase Storage directly.",
113
113
  "- Every write tool emits a server-owned audit record without storing message/caption/body content.",
114
114
  "- Destructive, live, or external side-effect tools require explicit `confirm:true`.",
115
- "- Local image paths are limited to `OKO_IMAGE_ROOTS` (path-delimited; current working directory by default). Symlink escapes are blocked.",
115
+ "- Local image reads are disabled by default. `OKO_IMAGE_ROOTS` enables a path-delimited allowlist; symlink escapes are blocked.",
116
116
  "- Remote image downloads require public HTTPS, pin the validated DNS address, revalidate redirects, and enforce byte/pixel limits.",
117
117
  "- Wrong API-key exchanges are counted per keyId; after 10 failures the keyId is locked for 15 minutes.",
118
118
  "",
package/dist/server.js CHANGED
@@ -3,19 +3,21 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { OKO_MCP_INSTRUCTIONS, registerGuides } from "./guides.js";
5
5
  import { registerTools } from "./tools.js";
6
+ import { OKO_MCP_VERSION } from "./version.js";
6
7
  if (process.argv.includes("--help")) {
7
8
  process.stdout.write([
8
9
  "OKO MCP Server",
9
10
  "",
10
11
  "Usage:",
11
12
  " oko-mcp",
12
- " npx -y @okostream/mcp",
13
+ " npx -y @okostream/mcp@latest",
13
14
  "",
14
15
  "Required env:",
15
16
  " OKO_API_KEY=oko_sk_...",
16
17
  "",
17
- "Optional env overrides:",
18
- " OKO_EXCHANGE_URL, OKO_FIREBASE_*",
18
+ "Optional local image access:",
19
+ " OKO_IMAGE_ROOTS=/trusted/image/directory",
20
+ " Local file reads are disabled unless this allowlist is set.",
19
21
  "",
20
22
  "The server speaks MCP over stdio. Do not run it manually before connecting a client.",
21
23
  ].join("\n") + "\n");
@@ -23,7 +25,7 @@ if (process.argv.includes("--help")) {
23
25
  }
24
26
  const server = new McpServer({
25
27
  name: "oko-mcp-server",
26
- version: "0.7.1",
28
+ version: OKO_MCP_VERSION,
27
29
  }, {
28
30
  instructions: OKO_MCP_INSTRUCTIONS,
29
31
  });
package/dist/tools.js CHANGED
@@ -499,6 +499,9 @@ async function readRemoteImage(rawUrl, redirects = 0) {
499
499
  }
500
500
  }
501
501
  async function readLocalImage(inputPath) {
502
+ if (env.imageRoots.length === 0) {
503
+ throw new Error("Local image reads are disabled. Set OKO_IMAGE_ROOTS to an explicit trusted directory and restart the MCP client.");
504
+ }
502
505
  const filePath = await realpath(resolveLocalImagePath(inputPath));
503
506
  const roots = await Promise.all(env.imageRoots.map(async (root) => {
504
507
  try {
@@ -0,0 +1 @@
1
+ export const OKO_MCP_VERSION = "0.7.2";
package/package.json CHANGED
@@ -1,15 +1,24 @@
1
1
  {
2
2
  "name": "@okostream/mcp",
3
- "version": "0.7.1",
4
- "description": "MCP server for managing OKO Stream webinars as an organizer.",
3
+ "version": "0.7.2",
4
+ "description": "Control OKO Stream webinars from Claude, Codex, and other MCP clients.",
5
5
  "license": "UNLICENSED",
6
+ "author": {
7
+ "name": "OKO Stream",
8
+ "url": "https://okostream.app"
9
+ },
10
+ "homepage": "https://okostream.app/app/mcp",
11
+ "keywords": [
12
+ "mcp",
13
+ "model-context-protocol",
14
+ "webinar",
15
+ "webinars",
16
+ "claude",
17
+ "codex",
18
+ "okostream"
19
+ ],
6
20
  "type": "module",
7
21
  "main": "dist/server.js",
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/BelfanioXL/okostreamcore.git",
11
- "directory": "mcp"
12
- },
13
22
  "publishConfig": {
14
23
  "access": "public"
15
24
  },