@okostream/mcp 0.12.6 → 0.12.8
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 +12 -2
- package/dist/server.js +111 -41
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -82,8 +82,9 @@ published version. The server reports the latest version during the key
|
|
|
82
82
|
exchange: `oko_whoami` and `oko_connection_status` return `update`, and the
|
|
83
83
|
first results of a session carry an `UPDATE AVAILABLE` block while the client
|
|
84
84
|
is behind, with the exact update step. Global installs update with
|
|
85
|
-
`npm install -g @okostream/mcp@latest
|
|
86
|
-
|
|
85
|
+
`npm install -g @okostream/mcp@latest`. Claude Desktop is not offered for
|
|
86
|
+
now (the extension bundle is discontinued; the Desktop path returns when the
|
|
87
|
+
package ships through a directory).
|
|
87
88
|
|
|
88
89
|
## When a tool refuses
|
|
89
90
|
|
|
@@ -96,6 +97,15 @@ Refusals are explicit and never require guessing:
|
|
|
96
97
|
OKO team.
|
|
97
98
|
- `PLATFORM_REJECTED_READ` — no access to that resource, or it does not exist.
|
|
98
99
|
|
|
100
|
+
**One install path.** Every supported client — Claude Code, Codex, and any
|
|
101
|
+
MCP client that takes a command with arguments and env — runs the package
|
|
102
|
+
through `npx -y @okostream/mcp@latest` (Node.js required): the latest version
|
|
103
|
+
is installed on each restart, so the server never falls behind. The `.mcpb`
|
|
104
|
+
extension bundle for Claude Desktop is no longer distributed — it never
|
|
105
|
+
updated itself — and the Claude Desktop path is archived until the package
|
|
106
|
+
ships through a directory. The key exchange refuses versions behind the
|
|
107
|
+
latest published one and tells the assistant how to update.
|
|
108
|
+
|
|
99
109
|
Image URLs are fetched by the OKO server first, so a VPN, proxy or captive
|
|
100
110
|
portal on the organizer's machine is not in the way. If the server cannot
|
|
101
111
|
reach the URL (a private host, a login wall, a timeout), the MCP client
|
package/dist/server.js
CHANGED
|
@@ -70503,7 +70503,7 @@ var OKO_CONNECTION_STATUSES = [
|
|
|
70503
70503
|
"rate_limited",
|
|
70504
70504
|
"unavailable"
|
|
70505
70505
|
];
|
|
70506
|
-
function nextSteps(status, minimumVersion) {
|
|
70506
|
+
function nextSteps(status, minimumVersion, latestVersion) {
|
|
70507
70507
|
switch (status) {
|
|
70508
70508
|
case "connected":
|
|
70509
70509
|
return [{
|
|
@@ -70543,7 +70543,7 @@ function nextSteps(status, minimumVersion) {
|
|
|
70543
70543
|
return [
|
|
70544
70544
|
{
|
|
70545
70545
|
id: "update_package",
|
|
70546
|
-
label: `Update to @okostream/mcp@latest${
|
|
70546
|
+
label: `Update to @okostream/mcp@latest${latestVersion ? ` (${latestVersion} is published` : ""}${minimumVersion ? `${latestVersion ? "; " : " ("}minimum ${minimumVersion}` : ""}${latestVersion || minimumVersion ? ")" : ""}.`,
|
|
70547
70547
|
command: "npx -y @okostream/mcp@latest"
|
|
70548
70548
|
},
|
|
70549
70549
|
{
|
|
@@ -70566,7 +70566,7 @@ function nextSteps(status, minimumVersion) {
|
|
|
70566
70566
|
function buildConnectionStatus(status, options2 = {}) {
|
|
70567
70567
|
const connected = status === "connected";
|
|
70568
70568
|
const errorCode = connected ? null : status === "update_required" ? "CLIENT_UPGRADE_REQUIRED" : status === "rate_limited" ? "RATE_LIMITED" : status === "unavailable" ? "SERVICE_UNAVAILABLE" : "AUTH_REQUIRED";
|
|
70569
|
-
const message = connected ? "OKO MCP is connected to an active organizer account." : status === "not_configured" ? "OKO MCP is installed, but OKO_API_KEY is not configured." : status === "invalid_key" ? "OKO MCP is installed, but the configured API key is invalid or revoked." : status === "update_required" ? "This OKO MCP client version is no longer supported." : status === "rate_limited" ? "OKO temporarily rate-limited the connection attempt." : "OKO MCP could not verify the organizer connection.";
|
|
70569
|
+
const message = connected ? "OKO MCP is connected to an active organizer account." : status === "not_configured" ? "OKO MCP is installed, but OKO_API_KEY is not configured." : status === "invalid_key" ? "OKO MCP is installed, but the configured API key is invalid or revoked." : status === "update_required" ? options2.serverMessage ?? "This OKO MCP client version is no longer supported." : status === "rate_limited" ? "OKO temporarily rate-limited the connection attempt." : "OKO MCP could not verify the organizer connection.";
|
|
70570
70570
|
return {
|
|
70571
70571
|
connected,
|
|
70572
70572
|
status,
|
|
@@ -70576,8 +70576,9 @@ function buildConnectionStatus(status, options2 = {}) {
|
|
|
70576
70576
|
organizerAccountRequired: true,
|
|
70577
70577
|
credentialConfigured: status !== "not_configured",
|
|
70578
70578
|
links: OKO_ONBOARDING_URLS,
|
|
70579
|
-
nextSteps: nextSteps(status, options2.minimumVersion),
|
|
70579
|
+
nextSteps: nextSteps(status, options2.minimumVersion, options2.latestVersion),
|
|
70580
70580
|
...options2.minimumVersion ? { minimumVersion: options2.minimumVersion } : {},
|
|
70581
|
+
...options2.latestVersion ? { latestVersion: options2.latestVersion } : {},
|
|
70581
70582
|
...options2.uid ? { uid: options2.uid } : {},
|
|
70582
70583
|
...options2.scopes ? { scopes: options2.scopes } : {},
|
|
70583
70584
|
...options2.update ? { update: options2.update } : {}
|
|
@@ -116223,11 +116224,14 @@ function sanitizeScopes(scopes) {
|
|
|
116223
116224
|
}
|
|
116224
116225
|
|
|
116225
116226
|
// build/version.js
|
|
116226
|
-
var OKO_MCP_VERSION = "0.12.
|
|
116227
|
+
var OKO_MCP_VERSION = "0.12.8";
|
|
116227
116228
|
|
|
116228
116229
|
// build/auth.js
|
|
116229
116230
|
var state = null;
|
|
116230
116231
|
var latestPublishedVersion = null;
|
|
116232
|
+
function recordLatestPublishedVersion(version9) {
|
|
116233
|
+
latestPublishedVersion = version9 && parseVersion(version9) ? version9.trim() : null;
|
|
116234
|
+
}
|
|
116231
116235
|
var OkoScopeError = class extends Error {
|
|
116232
116236
|
scope;
|
|
116233
116237
|
constructor(scope) {
|
|
@@ -116258,7 +116262,7 @@ function isNewerVersion(candidate, current) {
|
|
|
116258
116262
|
var MCP_UPDATE_HOW_TO = [
|
|
116259
116263
|
"Restart the MCP client. With the default `npx -y @okostream/mcp@latest` command the restart installs the newest version automatically.",
|
|
116260
116264
|
"Global install: run `npm install -g @okostream/mcp@latest`, then restart the client.",
|
|
116261
|
-
|
|
116265
|
+
'Claude Desktop with the extension bundle: the bundle is discontinued and never updated itself; switch to Claude Code, Codex, or the "Other MCP" configuration from https://okostream.app/app/mcp.'
|
|
116262
116266
|
];
|
|
116263
116267
|
function updateStatus() {
|
|
116264
116268
|
const latest = latestPublishedVersion;
|
|
@@ -116279,13 +116283,28 @@ function currentUid() {
|
|
|
116279
116283
|
var OkoAuthError = class extends Error {
|
|
116280
116284
|
status;
|
|
116281
116285
|
minimumVersion;
|
|
116282
|
-
|
|
116286
|
+
latestVersion;
|
|
116287
|
+
serverMessage;
|
|
116288
|
+
constructor(status, message, details = {}) {
|
|
116283
116289
|
super(message);
|
|
116284
116290
|
this.status = status;
|
|
116285
|
-
this.minimumVersion = minimumVersion;
|
|
116286
116291
|
this.name = "OkoAuthError";
|
|
116292
|
+
this.minimumVersion = details.minimumVersion;
|
|
116293
|
+
this.latestVersion = details.latestVersion;
|
|
116294
|
+
this.serverMessage = details.serverMessage;
|
|
116287
116295
|
}
|
|
116288
116296
|
};
|
|
116297
|
+
function versionField(value) {
|
|
116298
|
+
return typeof value === "string" && parseVersion(value) ? value.trim() : void 0;
|
|
116299
|
+
}
|
|
116300
|
+
function upgradeRequiredError(body) {
|
|
116301
|
+
const parsed = body && typeof body === "object" ? body : {};
|
|
116302
|
+
const minimumVersion = versionField(parsed.minimumVersion);
|
|
116303
|
+
const latestVersion = versionField(parsed.latestVersion);
|
|
116304
|
+
const serverMessage = typeof parsed.message === "string" && parsed.message.trim() ? parsed.message.trim() : void 0;
|
|
116305
|
+
const fallback = `OKO MCP ${OKO_MCP_VERSION} is no longer supported. Update to @okostream/mcp@latest (minimum ${minimumVersion ?? "the current release"}${latestVersion ? `, latest ${latestVersion}` : ""}) and restart the MCP client.`;
|
|
116306
|
+
return new OkoAuthError("update_required", serverMessage ?? fallback, { minimumVersion, latestVersion, serverMessage });
|
|
116307
|
+
}
|
|
116289
116308
|
async function exchangeApiKey() {
|
|
116290
116309
|
if (!env.apiKey) {
|
|
116291
116310
|
throw new OkoAuthError("not_configured", "OKO MCP is installed but not connected. Configure OKO_API_KEY, then restart or reconnect the MCP server.");
|
|
@@ -116304,8 +116323,10 @@ async function exchangeApiKey() {
|
|
|
116304
116323
|
const status = response.status;
|
|
116305
116324
|
const errorBody = await response.json().catch(() => ({}));
|
|
116306
116325
|
if (status === 426) {
|
|
116307
|
-
const
|
|
116308
|
-
|
|
116326
|
+
const error3 = upgradeRequiredError(errorBody);
|
|
116327
|
+
if (error3.latestVersion)
|
|
116328
|
+
recordLatestPublishedVersion(error3.latestVersion);
|
|
116329
|
+
throw error3;
|
|
116309
116330
|
}
|
|
116310
116331
|
if (status === 429) {
|
|
116311
116332
|
throw new OkoAuthError("rate_limited", "OKO API key exchange is temporarily rate-limited. Wait one minute, then restart the MCP client.");
|
|
@@ -116327,29 +116348,40 @@ async function exchangeApiKey() {
|
|
|
116327
116348
|
latestVersion: typeof data.latestVersion === "string" && parseVersion(data.latestVersion) ? data.latestVersion.trim() : null
|
|
116328
116349
|
};
|
|
116329
116350
|
}
|
|
116330
|
-
|
|
116351
|
+
function singleFlight(run) {
|
|
116352
|
+
let inFlight = null;
|
|
116353
|
+
return () => {
|
|
116354
|
+
inFlight ??= run().finally(() => {
|
|
116355
|
+
inFlight = null;
|
|
116356
|
+
});
|
|
116357
|
+
return inFlight;
|
|
116358
|
+
};
|
|
116359
|
+
}
|
|
116360
|
+
var refreshSession = singleFlight(async () => {
|
|
116331
116361
|
const now = Date.now();
|
|
116332
|
-
|
|
116333
|
-
|
|
116334
|
-
|
|
116335
|
-
|
|
116336
|
-
|
|
116337
|
-
throw new OkoAuthError("unavailable", "OKO MCP received an organizer session but could not initialize it. Reconnect and try again.");
|
|
116338
|
-
}
|
|
116339
|
-
state = {
|
|
116340
|
-
uid: exchanged.organizerUid,
|
|
116341
|
-
scopes: exchanged.scopes,
|
|
116342
|
-
expiresAt: now + exchanged.expiresIn * 1e3
|
|
116343
|
-
};
|
|
116344
|
-
if (exchanged.latestVersion)
|
|
116345
|
-
latestPublishedVersion = exchanged.latestVersion;
|
|
116362
|
+
const exchanged = await exchangeApiKey();
|
|
116363
|
+
try {
|
|
116364
|
+
await signInWithCustomToken(auth, exchanged.customToken);
|
|
116365
|
+
} catch {
|
|
116366
|
+
throw new OkoAuthError("unavailable", "OKO MCP received an organizer session but could not initialize it. Reconnect and try again.");
|
|
116346
116367
|
}
|
|
116368
|
+
state = {
|
|
116369
|
+
uid: exchanged.organizerUid,
|
|
116370
|
+
scopes: exchanged.scopes,
|
|
116371
|
+
expiresAt: now + exchanged.expiresIn * 1e3
|
|
116372
|
+
};
|
|
116373
|
+
if (exchanged.latestVersion)
|
|
116374
|
+
recordLatestPublishedVersion(exchanged.latestVersion);
|
|
116375
|
+
return state;
|
|
116376
|
+
});
|
|
116377
|
+
async function ensureAuth(requiredScopes = []) {
|
|
116378
|
+
const current = state && Date.now() <= state.expiresAt - 5 * 60 * 1e3 && auth.currentUser ? state : await refreshSession();
|
|
116347
116379
|
for (const scope of requiredScopes) {
|
|
116348
|
-
if (!
|
|
116380
|
+
if (!current.scopes.includes(scope)) {
|
|
116349
116381
|
throw new OkoScopeError(scope);
|
|
116350
116382
|
}
|
|
116351
116383
|
}
|
|
116352
|
-
return
|
|
116384
|
+
return current;
|
|
116353
116385
|
}
|
|
116354
116386
|
function currentScopes() {
|
|
116355
116387
|
return state?.scopes ?? [];
|
|
@@ -116366,16 +116398,21 @@ async function getConnectionStatus() {
|
|
|
116366
116398
|
});
|
|
116367
116399
|
} catch (error3) {
|
|
116368
116400
|
if (error3 instanceof OkoAuthError) {
|
|
116369
|
-
return buildConnectionStatus(error3.status,
|
|
116401
|
+
return buildConnectionStatus(error3.status, authErrorOptions(error3));
|
|
116370
116402
|
}
|
|
116371
116403
|
return buildConnectionStatus("unavailable");
|
|
116372
116404
|
}
|
|
116373
116405
|
}
|
|
116406
|
+
function authErrorOptions(error3) {
|
|
116407
|
+
return {
|
|
116408
|
+
minimumVersion: error3.minimumVersion,
|
|
116409
|
+
latestVersion: error3.latestVersion,
|
|
116410
|
+
serverMessage: error3.serverMessage
|
|
116411
|
+
};
|
|
116412
|
+
}
|
|
116374
116413
|
function authErrorResult(error3) {
|
|
116375
116414
|
return {
|
|
116376
|
-
...textResult(buildConnectionStatus(error3.status,
|
|
116377
|
-
minimumVersion: error3.minimumVersion
|
|
116378
|
-
})),
|
|
116415
|
+
...textResult(buildConnectionStatus(error3.status, authErrorOptions(error3))),
|
|
116379
116416
|
isError: true
|
|
116380
116417
|
};
|
|
116381
116418
|
}
|
|
@@ -116639,6 +116676,27 @@ async function responseBufferWithLimit(response, maxBytes) {
|
|
|
116639
116676
|
}
|
|
116640
116677
|
return Buffer.concat(chunks, total);
|
|
116641
116678
|
}
|
|
116679
|
+
function shareLinkHint(url) {
|
|
116680
|
+
let host = "";
|
|
116681
|
+
try {
|
|
116682
|
+
host = new URL(url).hostname.toLowerCase();
|
|
116683
|
+
} catch {
|
|
116684
|
+
return "";
|
|
116685
|
+
}
|
|
116686
|
+
if (host === "drive.google.com" || host === "docs.google.com") {
|
|
116687
|
+
return " This is a Google Drive page, not the file: share it as 'anyone with the link' and use https://drive.google.com/uc?export=download&id=<fileId>, or download the file and pass source.path.";
|
|
116688
|
+
}
|
|
116689
|
+
if (host.endsWith("disk.yandex.ru") || host.endsWith("disk.yandex.com") || host === "yadi.sk" || host.endsWith("disk.360.yandex.ru")) {
|
|
116690
|
+
return " This is a Yandex Disk page, not the file: use the direct download link from the page (\u0421\u043A\u0430\u0447\u0430\u0442\u044C \u2192 copy link), or download the file and pass source.path.";
|
|
116691
|
+
}
|
|
116692
|
+
if (host === "dropbox.com" || host.endsWith(".dropbox.com")) {
|
|
116693
|
+
return " This is a Dropbox page: replace ?dl=0 with ?dl=1 in the link, or download the file and pass source.path.";
|
|
116694
|
+
}
|
|
116695
|
+
if (host.endsWith("onedrive.live.com") || host === "1drv.ms" || host.endsWith("sharepoint.com")) {
|
|
116696
|
+
return " This is a OneDrive page: use its direct download link, or download the file and pass source.path.";
|
|
116697
|
+
}
|
|
116698
|
+
return " Use a direct link to the image file (right-click the image \u2192 copy image address), or download the file and pass source.path.";
|
|
116699
|
+
}
|
|
116642
116700
|
async function readRemoteImage(rawUrl, redirects = 0) {
|
|
116643
116701
|
if (redirects > REMOTE_IMAGE_MAX_REDIRECTS) {
|
|
116644
116702
|
throw new Error("Image URL has too many redirects.");
|
|
@@ -116671,7 +116729,7 @@ async function readRemoteImage(rawUrl, redirects = 0) {
|
|
|
116671
116729
|
}
|
|
116672
116730
|
const contentType = (response.headers.get("content-type") ?? "").split(";")[0]?.trim().toLowerCase();
|
|
116673
116731
|
if (!contentType || !contentType.startsWith("image/")) {
|
|
116674
|
-
throw new Error(
|
|
116732
|
+
throw new Error(`Image URL must return Content-Type image/* (got ${contentType || "none"}).${shareLinkHint(rawUrl)}`);
|
|
116675
116733
|
}
|
|
116676
116734
|
if (!ALLOWED_REMOTE_IMAGE_TYPES.has(contentType)) {
|
|
116677
116735
|
throw new Error(`Image URL Content-Type ${contentType} is not supported. Use JPEG, PNG, WEBP, GIF, or AVIF.`);
|
|
@@ -116734,6 +116792,12 @@ function serverFetchRefusal(error3) {
|
|
|
116734
116792
|
if (code === "functions/invalid-argument" && message.includes("Image payload is empty or too large")) {
|
|
116735
116793
|
return { reason: "server_without_url_support", message: "The OKO server release in use does not fetch URLs yet." };
|
|
116736
116794
|
}
|
|
116795
|
+
if (code === "functions/deadline-exceeded") {
|
|
116796
|
+
return { reason: "server_timeout", message: "The OKO server did not finish fetching the URL within its time limit." };
|
|
116797
|
+
}
|
|
116798
|
+
if (code === "functions/internal") {
|
|
116799
|
+
return { reason: "server_error", message: message && message !== "internal" ? message : "The OKO server failed while fetching the URL." };
|
|
116800
|
+
}
|
|
116737
116801
|
return null;
|
|
116738
116802
|
}
|
|
116739
116803
|
var SERVER_FETCH_NOTE = "Image fetched by the OKO server directly from the URL; this machine's network was not involved.";
|
|
@@ -118332,7 +118396,10 @@ function registerEventTools(server2) {
|
|
|
118332
118396
|
summary: "Read one OKO event visible to the organizer.",
|
|
118333
118397
|
args: ["eventId: OKO event document id."],
|
|
118334
118398
|
returns: "full event document fields plus id and public event URL.",
|
|
118335
|
-
examples: ["Use after oko_create_webinar to verify setup and return the event URL to the user."]
|
|
118399
|
+
examples: ["Use after oko_create_webinar to verify setup and return the event URL to the user."],
|
|
118400
|
+
notes: [
|
|
118401
|
+
"blocks.chat.mode is a legacy field written by older cabinet versions and read by nothing; chat visibility is blocks.chat.visibility (always | hidden). Ignore mode."
|
|
118402
|
+
]
|
|
118336
118403
|
}),
|
|
118337
118404
|
inputSchema: { eventId: eventIdField },
|
|
118338
118405
|
outputSchema: eventOutputSchema,
|
|
@@ -119052,26 +119119,28 @@ function platformRejectionResult(toolName, args, readOnly) {
|
|
|
119052
119119
|
isError: true
|
|
119053
119120
|
};
|
|
119054
119121
|
}
|
|
119055
|
-
var
|
|
119122
|
+
var UPDATE_NOTICE_FULL_LIMIT = 3;
|
|
119056
119123
|
var updateNoticesShown = 0;
|
|
119124
|
+
function updateNoticeText(status, shownSoFar) {
|
|
119125
|
+
if (shownSoFar < UPDATE_NOTICE_FULL_LIMIT) {
|
|
119126
|
+
return `UPDATE AVAILABLE: ${status.message} How to update: ${(status.howTo ?? []).join(" ")}`;
|
|
119127
|
+
}
|
|
119128
|
+
return `UPDATE AVAILABLE: OKO MCP ${status.current} \u2192 ${status.latest}. Restart the MCP client to install it (npx does it automatically); see oko_connection_status for the steps.`;
|
|
119129
|
+
}
|
|
119057
119130
|
function withUpdateNotice(result) {
|
|
119058
|
-
if (updateNoticesShown >= UPDATE_NOTICE_LIMIT)
|
|
119059
|
-
return result;
|
|
119060
119131
|
const status = updateStatus();
|
|
119061
119132
|
if (!status.updateAvailable || !result || typeof result !== "object")
|
|
119062
119133
|
return result;
|
|
119063
119134
|
const typed = result;
|
|
119064
119135
|
if (!Array.isArray(typed.content))
|
|
119065
119136
|
return result;
|
|
119137
|
+
const text = updateNoticeText(status, updateNoticesShown);
|
|
119066
119138
|
updateNoticesShown += 1;
|
|
119067
119139
|
return {
|
|
119068
119140
|
...typed,
|
|
119069
119141
|
content: [
|
|
119070
119142
|
...typed.content,
|
|
119071
|
-
{
|
|
119072
|
-
type: "text",
|
|
119073
|
-
text: `UPDATE AVAILABLE: ${status.message} How to update: ${(status.howTo ?? []).join(" ")}`
|
|
119074
|
-
}
|
|
119143
|
+
{ type: "text", text }
|
|
119075
119144
|
]
|
|
119076
119145
|
};
|
|
119077
119146
|
}
|
|
@@ -120446,7 +120515,7 @@ function registerWorkspaceTools(server2) {
|
|
|
120446
120515
|
description: toolDescription({
|
|
120447
120516
|
summary: "Accept a workspace invite code for the authenticated organizer.",
|
|
120448
120517
|
args: ["code: invite code from /invite/{code}.", "confirm: must be true."],
|
|
120449
|
-
returns: "success
|
|
120518
|
+
returns: "success, workspace id/name, and keptExistingPermissions: true when the organizer was already a member \u2014 an invite never changes existing permissions; the owner changes them with oko_update_workspace_member.",
|
|
120450
120519
|
examples: ["Use when the organizer wants to join a workspace from an invite code."]
|
|
120451
120520
|
}),
|
|
120452
120521
|
inputSchema: {
|
|
@@ -120455,6 +120524,7 @@ function registerWorkspaceTools(server2) {
|
|
|
120455
120524
|
},
|
|
120456
120525
|
outputSchema: external_exports.object({
|
|
120457
120526
|
success: external_exports.boolean(),
|
|
120527
|
+
keptExistingPermissions: external_exports.boolean().optional(),
|
|
120458
120528
|
workspace: external_exports.object({ id: external_exports.string(), name: external_exports.string() }).passthrough()
|
|
120459
120529
|
}).passthrough(),
|
|
120460
120530
|
annotations: writeTool
|