@okostream/mcp 0.5.1 → 0.5.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 +8 -0
- package/dist/server.js +1 -1
- package/dist/tools.js +39 -21
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.2 - 2026-07-10
|
|
4
|
+
|
|
5
|
+
- Fixed webinar create/duplicate output schemas so successful writes cannot be reported as protocol failures.
|
|
6
|
+
- Made Firestore event creation compensate automatically when the required RTDB mirror fails.
|
|
7
|
+
- Removed a stale RTDB mirror rewrite from metadata updates.
|
|
8
|
+
- Routed event deletion through an authorized server callable that cleans Firestore subcollections, both RTDB instances, Storage, participants, active sessions, and scheduled Telegram posts.
|
|
9
|
+
- Added a repeatable production smoke workflow against an exact published npm version.
|
|
10
|
+
|
|
3
11
|
## 0.5.1 - 2026-07-10
|
|
4
12
|
|
|
5
13
|
- Replaced hand-maintained remote-image IP ranges with a tested public-unicast policy covering IPv4, IPv6, mapped, reserved, documentation, benchmark, multicast, and private address classes.
|
package/dist/server.js
CHANGED
package/dist/tools.js
CHANGED
|
@@ -77,11 +77,21 @@ const updateOutputSchema = z.object({
|
|
|
77
77
|
eventId: z.string().optional(),
|
|
78
78
|
updated: z.array(z.string()).optional(),
|
|
79
79
|
}).passthrough();
|
|
80
|
-
const
|
|
81
|
-
id: z.string(),
|
|
80
|
+
const eventLinksOutputShape = {
|
|
82
81
|
url: z.string().url(),
|
|
83
|
-
|
|
82
|
+
setupUrl: z.string().url(),
|
|
83
|
+
analyticsUrl: z.string().url(),
|
|
84
|
+
registrationUrl: z.string().url(),
|
|
85
|
+
loginUrl: z.string().url(),
|
|
86
|
+
nmoRegistrationUrl: z.string().url().optional(),
|
|
87
|
+
nmoLoginUrl: z.string().url().optional(),
|
|
84
88
|
};
|
|
89
|
+
const createWebinarOutputSchema = z.object({
|
|
90
|
+
id: z.string(),
|
|
91
|
+
workspaceId: z.string(),
|
|
92
|
+
sourceEventId: z.string().optional(),
|
|
93
|
+
...eventLinksOutputShape,
|
|
94
|
+
}).passthrough();
|
|
85
95
|
const cursorMetaSchema = {
|
|
86
96
|
count: z.number().int().nonnegative(),
|
|
87
97
|
limit: z.number().int().positive(),
|
|
@@ -100,7 +110,7 @@ const eventsOutputSchema = z.object({
|
|
|
100
110
|
}).passthrough();
|
|
101
111
|
const eventOutputSchema = z.object({
|
|
102
112
|
id: z.string(),
|
|
103
|
-
|
|
113
|
+
...eventLinksOutputShape,
|
|
104
114
|
}).passthrough();
|
|
105
115
|
const liveStateOutputSchema = z.object({
|
|
106
116
|
eventId: z.string(),
|
|
@@ -1099,6 +1109,22 @@ function initialEventAccess(uid) {
|
|
|
1099
1109
|
notificationSenders: [uid],
|
|
1100
1110
|
};
|
|
1101
1111
|
}
|
|
1112
|
+
async function addEventWithRtdbMirror(uid, workspaceId, eventData) {
|
|
1113
|
+
const eventRef = await addDoc(collection(firestore, "events"), eventData);
|
|
1114
|
+
try {
|
|
1115
|
+
await update(ref(rtdb, `events/${eventRef.id}`), { ownerId: uid, workspaceId });
|
|
1116
|
+
}
|
|
1117
|
+
catch (error) {
|
|
1118
|
+
try {
|
|
1119
|
+
await deleteDoc(eventRef);
|
|
1120
|
+
}
|
|
1121
|
+
catch (rollbackError) {
|
|
1122
|
+
throw new AggregateError([error, rollbackError], `RTDB mirror failed for event ${eventRef.id}, and Firestore rollback also failed.`);
|
|
1123
|
+
}
|
|
1124
|
+
throw error;
|
|
1125
|
+
}
|
|
1126
|
+
return eventRef;
|
|
1127
|
+
}
|
|
1102
1128
|
async function assertCanCreateInWorkspace(uid, workspaceId) {
|
|
1103
1129
|
if (workspaceId === uid)
|
|
1104
1130
|
return;
|
|
@@ -2040,7 +2066,7 @@ export function registerTools(server) {
|
|
|
2040
2066
|
const workspaceId = args.workspaceId ?? uid;
|
|
2041
2067
|
await assertCanCreateInWorkspace(uid, workspaceId);
|
|
2042
2068
|
const video = normalizeVideo(args.videoUrl);
|
|
2043
|
-
const docRef = await
|
|
2069
|
+
const docRef = await addEventWithRtdbMirror(uid, workspaceId, {
|
|
2044
2070
|
ownerId: uid,
|
|
2045
2071
|
workspaceId,
|
|
2046
2072
|
...initialEventAccess(uid),
|
|
@@ -2058,10 +2084,6 @@ export function registerTools(server) {
|
|
|
2058
2084
|
promoBanner: { enabled: false, text: "", link: "", visibility: "always" },
|
|
2059
2085
|
},
|
|
2060
2086
|
});
|
|
2061
|
-
await update(ref(rtdb, `events/${docRef.id}`), {
|
|
2062
|
-
ownerId: uid,
|
|
2063
|
-
workspaceId,
|
|
2064
|
-
});
|
|
2065
2087
|
return textResult({ id: docRef.id, ...eventLinks(docRef.id), workspaceId });
|
|
2066
2088
|
});
|
|
2067
2089
|
server.registerTool("oko_update_event", {
|
|
@@ -2096,7 +2118,7 @@ export function registerTools(server) {
|
|
|
2096
2118
|
if (requiredScopes.size === 0)
|
|
2097
2119
|
throw new Error("Provide at least one field to update.");
|
|
2098
2120
|
const { uid } = await authed([...requiredScopes]);
|
|
2099
|
-
|
|
2121
|
+
await readEvent(args.eventId, uid, "edit");
|
|
2100
2122
|
const patch = {};
|
|
2101
2123
|
if (args.title !== undefined)
|
|
2102
2124
|
patch.title = args.title.trim();
|
|
@@ -2114,10 +2136,6 @@ export function registerTools(server) {
|
|
|
2114
2136
|
if (args.pinned !== undefined)
|
|
2115
2137
|
patch.pinned = args.pinned;
|
|
2116
2138
|
await updateDoc(doc(firestore, "events", args.eventId), patch);
|
|
2117
|
-
await update(ref(rtdb, `events/${args.eventId}`), {
|
|
2118
|
-
ownerId: safeString(data.ownerId, uid),
|
|
2119
|
-
workspaceId: safeString(data.workspaceId, uid),
|
|
2120
|
-
});
|
|
2121
2139
|
return textResult({ id: args.eventId, updated: Object.keys(patch) });
|
|
2122
2140
|
});
|
|
2123
2141
|
server.registerTool("oko_configure_blocks", {
|
|
@@ -2499,11 +2517,11 @@ export function registerTools(server) {
|
|
|
2499
2517
|
server.registerTool("oko_delete_event", {
|
|
2500
2518
|
title: "Delete event",
|
|
2501
2519
|
description: toolDescription({
|
|
2502
|
-
summary: "Delete an OKO event
|
|
2520
|
+
summary: "Delete an OKO event and its associated Firestore, RTDB, chat, Storage, participant, and scheduled-post data.",
|
|
2503
2521
|
args: ["eventId: event to delete.", "confirm: must be true."],
|
|
2504
|
-
returns: "deleted event id.",
|
|
2522
|
+
returns: "deleted event id and cross-store cleanup counts.",
|
|
2505
2523
|
examples: ["Use only after the user explicitly confirms the exact event title/id."],
|
|
2506
|
-
notes: ["Destructive.
|
|
2524
|
+
notes: ["Destructive. The server validates the current per-event delete capability and performs idempotent cross-store cleanup."],
|
|
2507
2525
|
}),
|
|
2508
2526
|
inputSchema: { eventId: eventIdField, confirm: z.boolean().optional().describe("Required true for deletion.") },
|
|
2509
2527
|
outputSchema: z.object({ deleted: z.string() }).passthrough(),
|
|
@@ -2512,8 +2530,9 @@ export function registerTools(server) {
|
|
|
2512
2530
|
const { uid } = await authed(["events:delete"]);
|
|
2513
2531
|
const data = await readEvent(eventId, uid, "delete");
|
|
2514
2532
|
requireConfirm(confirm, "oko_delete_event", { eventId, title: data.title });
|
|
2515
|
-
|
|
2516
|
-
|
|
2533
|
+
const deleteEvent = httpsCallable(functions, "deleteEventCompletely");
|
|
2534
|
+
const result = await deleteEvent({ eventId });
|
|
2535
|
+
return textResult(result.data);
|
|
2517
2536
|
});
|
|
2518
2537
|
server.registerTool("oko_duplicate_event", {
|
|
2519
2538
|
title: "Duplicate event",
|
|
@@ -2535,7 +2554,7 @@ export function registerTools(server) {
|
|
|
2535
2554
|
const source = await readEvent(eventId, uid, "read");
|
|
2536
2555
|
const workspaceId = targetWorkspaceId ?? safeString(source.workspaceId, uid);
|
|
2537
2556
|
await assertCanCreateInWorkspace(uid, workspaceId);
|
|
2538
|
-
const newRef = await
|
|
2557
|
+
const newRef = await addEventWithRtdbMirror(uid, workspaceId, {
|
|
2539
2558
|
ownerId: uid,
|
|
2540
2559
|
workspaceId,
|
|
2541
2560
|
...initialEventAccess(uid),
|
|
@@ -2551,7 +2570,6 @@ export function registerTools(server) {
|
|
|
2551
2570
|
...(source.nmoCriteria && typeof source.nmoCriteria === "object" ? { nmoCriteria: source.nmoCriteria } : {}),
|
|
2552
2571
|
...(source.nmoExtraFields && typeof source.nmoExtraFields === "object" ? { nmoExtraFields: source.nmoExtraFields } : {}),
|
|
2553
2572
|
});
|
|
2554
|
-
await update(ref(rtdb, `events/${newRef.id}`), { ownerId: uid, workspaceId });
|
|
2555
2573
|
return textResult({ id: newRef.id, sourceEventId: eventId, workspaceId, ...eventLinks(newRef.id) });
|
|
2556
2574
|
});
|
|
2557
2575
|
server.registerTool("oko_move_event", {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@okostream/mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "MCP server for managing OKO Stream webinars as an organizer.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"test": "npm run build && node --test test/*.test.mjs",
|
|
27
27
|
"eval:contract": "npm run build && node eval/contract-eval.mjs",
|
|
28
28
|
"eval:model": "npm run build && node eval/model-eval.mjs",
|
|
29
|
+
"smoke:prod": "node eval/prod-smoke.mjs",
|
|
29
30
|
"prepublishOnly": "npm run build",
|
|
30
31
|
"start": "node dist/server.js"
|
|
31
32
|
},
|