vibo-mcp 2.1.0 → 2.2.1
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/bundle.js +121 -36
- package/dist/client.js +22 -3
- package/dist/tools/collaboration.js +3 -3
- package/dist/tools/comments.js +4 -4
- package/dist/tools/events.js +3 -3
- package/dist/tools/imports.js +1 -1
- package/dist/tools/notifications.js +1 -1
- package/dist/tools/playlists.js +2 -2
- package/dist/tools/questions.js +1 -1
- package/dist/tools/section-edit.js +1 -1
- package/dist/tools/session.js +1 -1
- package/dist/tools/song-management.js +4 -4
- package/dist/tools/songs.js +2 -2
- package/dist/tools/uploads.js +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/server.json +2 -2
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "MCP server for Vibo (vibodj.com) — plan & manage event music, song requests, ideas, guests, and playlists via natural language",
|
|
10
|
-
"version": "2.1
|
|
10
|
+
"version": "2.2.1"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"displayName": "Vibo",
|
|
16
16
|
"source": "./",
|
|
17
17
|
"description": "MCP server for Vibo — browse & manage events, timeline, songs, the DJ song ideas/questions, guests, and exports to Spotify/Apple Music",
|
|
18
|
-
"version": "2.1
|
|
18
|
+
"version": "2.2.1",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Hall"
|
|
21
21
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibo-mcp",
|
|
3
3
|
"displayName": "Vibo",
|
|
4
|
-
"version": "2.1
|
|
4
|
+
"version": "2.2.1",
|
|
5
5
|
"description": "MCP server for Vibo (vibodj.com) — plan & manage event music, song requests, ideas, guests, and playlists via natural language",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Chris Hall",
|
package/dist/bundle.js
CHANGED
|
@@ -5,6 +5,31 @@ var __export = (target, all) => {
|
|
|
5
5
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
+
// node_modules/@chrischall/mcp-utils/dist/cancel/index.js
|
|
9
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
10
|
+
var storage = new AsyncLocalStorage();
|
|
11
|
+
function withCallSignal(signal, fn, request) {
|
|
12
|
+
return signal ? storage.run({ signal, ...request ? { request } : {} }, fn) : fn();
|
|
13
|
+
}
|
|
14
|
+
function currentCallSignal() {
|
|
15
|
+
return storage.getStore()?.signal;
|
|
16
|
+
}
|
|
17
|
+
function withAmbientCancellation(own2) {
|
|
18
|
+
const ambient = currentCallSignal();
|
|
19
|
+
if (!ambient)
|
|
20
|
+
return own2;
|
|
21
|
+
if (!own2)
|
|
22
|
+
return ambient;
|
|
23
|
+
return AbortSignal.any([own2, ambient]);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// node_modules/@chrischall/mcp-utils/dist/caller/index.js
|
|
27
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
28
|
+
var storage2 = new AsyncLocalStorage2();
|
|
29
|
+
function withCallerCapabilities(capabilities, fn) {
|
|
30
|
+
return capabilities ? storage2.run(capabilities, fn) : fn();
|
|
31
|
+
}
|
|
32
|
+
|
|
8
33
|
// node_modules/@modelcontextprotocol/server/dist/chunk-Br0eD_fh.mjs
|
|
9
34
|
var __create = Object.create;
|
|
10
35
|
var __defProp2 = Object.defineProperty;
|
|
@@ -34474,17 +34499,73 @@ Hint: ${err.hint}`);
|
|
|
34474
34499
|
}
|
|
34475
34500
|
throw err;
|
|
34476
34501
|
}
|
|
34502
|
+
function mcpRequestFrom(args) {
|
|
34503
|
+
const ctx = args.at(-1);
|
|
34504
|
+
if (typeof ctx !== "object" || ctx === null)
|
|
34505
|
+
return void 0;
|
|
34506
|
+
const req = ctx.mcpReq;
|
|
34507
|
+
if (typeof req !== "object" || req === null)
|
|
34508
|
+
return void 0;
|
|
34509
|
+
return typeof req.notify === "function" ? req : void 0;
|
|
34510
|
+
}
|
|
34511
|
+
function callSignalFrom(args) {
|
|
34512
|
+
const ctx = args.at(-1);
|
|
34513
|
+
if (typeof ctx !== "object" || ctx === null)
|
|
34514
|
+
return void 0;
|
|
34515
|
+
const req = ctx.mcpReq;
|
|
34516
|
+
if (typeof req !== "object" || req === null)
|
|
34517
|
+
return void 0;
|
|
34518
|
+
const signal = req.signal;
|
|
34519
|
+
return signal instanceof AbortSignal ? signal : void 0;
|
|
34520
|
+
}
|
|
34521
|
+
function declaredCapabilitiesFrom(server) {
|
|
34522
|
+
const inner = server.server;
|
|
34523
|
+
if (typeof inner !== "object" || inner === null)
|
|
34524
|
+
return void 0;
|
|
34525
|
+
const read = inner.getClientCapabilities;
|
|
34526
|
+
if (typeof read !== "function")
|
|
34527
|
+
return void 0;
|
|
34528
|
+
try {
|
|
34529
|
+
const declared = read.call(inner);
|
|
34530
|
+
return typeof declared === "object" && declared !== null ? declared : void 0;
|
|
34531
|
+
} catch {
|
|
34532
|
+
return void 0;
|
|
34533
|
+
}
|
|
34534
|
+
}
|
|
34477
34535
|
function surfaceToolHints(server) {
|
|
34478
34536
|
const register2 = server.registerTool.bind(server);
|
|
34479
|
-
server.registerTool = (name, config2, cb) => register2(name, config2, (...args) =>
|
|
34480
|
-
|
|
34481
|
-
|
|
34482
|
-
|
|
34483
|
-
|
|
34484
|
-
|
|
34485
|
-
|
|
34486
|
-
|
|
34487
|
-
|
|
34537
|
+
server.registerTool = (name, config2, cb) => register2(name, config2, (...args) => (
|
|
34538
|
+
// THE CALLER'S CANCELLATION, made ambient for the whole handler
|
|
34539
|
+
// (`cancel/index.ts`). The SDK delivers it and the fleet ignored it:
|
|
34540
|
+
// measured against @modelcontextprotocol/server 2.0.0, a cancelled
|
|
34541
|
+
// call aborts `ctx.mcpReq.signal` with the caller's reason and the
|
|
34542
|
+
// handler runs to completion regardless — so the HTTP request stays
|
|
34543
|
+
// in flight, the child keeps burning metered CPU, and the upstream
|
|
34544
|
+
// keeps being hit for somebody who has gone. claude.ai sent 101
|
|
34545
|
+
// cancellations in the week to 2026-09-20.
|
|
34546
|
+
//
|
|
34547
|
+
// Here because this is the one wrapper every tool already passes
|
|
34548
|
+
// through: threading the signal by hand would mean editing several
|
|
34549
|
+
// hundred handlers and missing exactly the ones nobody edits.
|
|
34550
|
+
withCallSignal(callSignalFrom(args), () => (
|
|
34551
|
+
// WHAT THE CALLER CAN DO, made ambient for the same reason and in
|
|
34552
|
+
// the same place (`caller/index.ts`). Only the 2025-era half needs
|
|
34553
|
+
// the wrapper — a 2026-07-28 request answers for itself off its own
|
|
34554
|
+
// envelope — but a guarded tool that cannot tell whether the caller
|
|
34555
|
+
// can be shown a prompt returns one the SDK then refuses to deliver,
|
|
34556
|
+
// and that refusal reaches the caller as an unexplained protocol
|
|
34557
|
+
// error it had no chance to catch.
|
|
34558
|
+
withCallerCapabilities(declaredCapabilitiesFrom(server), () => {
|
|
34559
|
+
let result;
|
|
34560
|
+
try {
|
|
34561
|
+
result = cb(...args);
|
|
34562
|
+
} catch (err) {
|
|
34563
|
+
return hintResultOrRethrow(err);
|
|
34564
|
+
}
|
|
34565
|
+
return result instanceof Promise ? result.catch(hintResultOrRethrow) : result;
|
|
34566
|
+
})
|
|
34567
|
+
), mcpRequestFrom(args))
|
|
34568
|
+
));
|
|
34488
34569
|
}
|
|
34489
34570
|
async function createMcpServer(opts) {
|
|
34490
34571
|
const server = new McpServer({ name: opts.name, version: opts.version }, { supportedProtocolVersions: [...SERVER_PROTOCOL_VERSIONS] });
|
|
@@ -34597,12 +34678,13 @@ function toolAnnotations(opts = {}) {
|
|
|
34597
34678
|
...opts.title !== void 0 ? { title: opts.title } : {},
|
|
34598
34679
|
readOnlyHint: opts.readOnly ?? true,
|
|
34599
34680
|
...opts.idempotent !== void 0 ? { idempotentHint: opts.idempotent } : {},
|
|
34681
|
+
...opts.destructive !== void 0 ? { destructiveHint: opts.destructive } : {},
|
|
34600
34682
|
...opts.openWorld !== void 0 ? { openWorldHint: opts.openWorld } : {}
|
|
34601
34683
|
};
|
|
34602
34684
|
}
|
|
34603
34685
|
|
|
34604
34686
|
// src/version.ts
|
|
34605
|
-
var VERSION = "2.1
|
|
34687
|
+
var VERSION = "2.2.1";
|
|
34606
34688
|
|
|
34607
34689
|
// src/client.ts
|
|
34608
34690
|
import { dirname as dirname2, join as join3 } from "path";
|
|
@@ -34807,6 +34889,9 @@ var REFRESH = `
|
|
|
34807
34889
|
}
|
|
34808
34890
|
`;
|
|
34809
34891
|
var AUTH_ERROR_CODES = /* @__PURE__ */ new Set(["UNAUTHORIZED", "UNAUTHENTICATED", "FORBIDDEN"]);
|
|
34892
|
+
function requestSignal() {
|
|
34893
|
+
return withAmbientCancellation(AbortSignal.timeout(REQUEST_TIMEOUT_MS));
|
|
34894
|
+
}
|
|
34810
34895
|
var ViboClient = class {
|
|
34811
34896
|
apiUrl;
|
|
34812
34897
|
email;
|
|
@@ -34931,7 +35016,7 @@ var ViboClient = class {
|
|
|
34931
35016
|
headers,
|
|
34932
35017
|
// NB: no content-type — fetch sets the multipart boundary
|
|
34933
35018
|
body: form,
|
|
34934
|
-
signal:
|
|
35019
|
+
signal: requestSignal()
|
|
34935
35020
|
});
|
|
34936
35021
|
} catch (err) {
|
|
34937
35022
|
const reason = err instanceof Error && err.name === "TimeoutError" ? "timed out" : "failed";
|
|
@@ -35021,7 +35106,7 @@ var ViboClient = class {
|
|
|
35021
35106
|
method: "POST",
|
|
35022
35107
|
headers,
|
|
35023
35108
|
body: JSON.stringify({ query, variables }),
|
|
35024
|
-
signal:
|
|
35109
|
+
signal: requestSignal()
|
|
35025
35110
|
});
|
|
35026
35111
|
} catch (err) {
|
|
35027
35112
|
const reason = err instanceof Error && err.name === "TimeoutError" ? "timed out" : "failed";
|
|
@@ -35478,7 +35563,7 @@ function registerEventTools(server, client2) {
|
|
|
35478
35563
|
"vibo_join_event",
|
|
35479
35564
|
{
|
|
35480
35565
|
description: "Join an event you were invited to, via its share link or hash (e.g. a vibodj.app.link/... URL someone sent you). Returns the joined event's id. Confirm-gated.",
|
|
35481
|
-
annotations: toolAnnotations({ title: "Join Vibo event", readOnly: false }),
|
|
35566
|
+
annotations: toolAnnotations({ title: "Join Vibo event", readOnly: false, destructive: false }),
|
|
35482
35567
|
inputSchema: external_exports.object({
|
|
35483
35568
|
link: external_exports.string().describe("The full event share URL (vibodj.app.link/... or web.vibodj.com/...) or the bare join hash."),
|
|
35484
35569
|
confirm: schemaConfirm
|
|
@@ -35503,7 +35588,7 @@ function registerEventTools(server, client2) {
|
|
|
35503
35588
|
"vibo_leave_event",
|
|
35504
35589
|
{
|
|
35505
35590
|
description: "Leave an event you previously joined. Confirm-gated.",
|
|
35506
|
-
annotations: toolAnnotations({ title: "Leave Vibo event", readOnly: false }),
|
|
35591
|
+
annotations: toolAnnotations({ title: "Leave Vibo event", readOnly: false, destructive: true }),
|
|
35507
35592
|
inputSchema: external_exports.object({
|
|
35508
35593
|
eventId: external_exports.string().describe("Event id to leave."),
|
|
35509
35594
|
confirm: schemaConfirm
|
|
@@ -35519,7 +35604,7 @@ function registerEventTools(server, client2) {
|
|
|
35519
35604
|
"vibo_create_event_contact",
|
|
35520
35605
|
{
|
|
35521
35606
|
description: "Add a contact (host or guest) to an event with their name/email/phone. Confirm-gated.",
|
|
35522
|
-
annotations: toolAnnotations({ title: "Add Vibo event contact", readOnly: false }),
|
|
35607
|
+
annotations: toolAnnotations({ title: "Add Vibo event contact", readOnly: false, destructive: false }),
|
|
35523
35608
|
inputSchema: external_exports.object({
|
|
35524
35609
|
eventId: external_exports.string().describe("Event id."),
|
|
35525
35610
|
role: external_exports.enum(["host", "guest"]).describe("The contact's role in the event."),
|
|
@@ -35763,7 +35848,7 @@ function registerSongTools(server, client2) {
|
|
|
35763
35848
|
"vibo_add_song_to_section",
|
|
35764
35849
|
{
|
|
35765
35850
|
description: "Add a song to a section. Pass a song from vibo_search_songs (songUrl is required; include viboSongId/title/artist when known). Before adding, check that result's `quality.confidence`: adding a `likely-not-original` result puts a cover, karaoke track or junk-metadata re-upload in front of a live DJ. If nothing looks original, report the closest matches back rather than adding a best guess. Confirm-gated.",
|
|
35766
|
-
annotations: toolAnnotations({ title: "Add song to Vibo section", readOnly: false }),
|
|
35851
|
+
annotations: toolAnnotations({ title: "Add song to Vibo section", readOnly: false, destructive: false }),
|
|
35767
35852
|
inputSchema: external_exports.object({
|
|
35768
35853
|
eventId: external_exports.string().describe("Event id."),
|
|
35769
35854
|
sectionId: external_exports.string().describe("Section id to add the song to."),
|
|
@@ -35793,7 +35878,7 @@ function registerSongTools(server, client2) {
|
|
|
35793
35878
|
"vibo_toggle_song_like",
|
|
35794
35879
|
{
|
|
35795
35880
|
description: "Like or unlike a song in a section. Confirm-gated.",
|
|
35796
|
-
annotations: toolAnnotations({ title: "Like/unlike Vibo song", readOnly: false }),
|
|
35881
|
+
annotations: toolAnnotations({ title: "Like/unlike Vibo song", readOnly: false, destructive: false }),
|
|
35797
35882
|
inputSchema: external_exports.object({
|
|
35798
35883
|
eventId: external_exports.string().describe("Event id."),
|
|
35799
35884
|
sectionId: external_exports.string().describe("Section id."),
|
|
@@ -35864,7 +35949,7 @@ function registerPlaylistTools(server, client2) {
|
|
|
35864
35949
|
"vibo_export_event_to_spotify",
|
|
35865
35950
|
{
|
|
35866
35951
|
description: "Export an event's song selections to a new Spotify playlist (Spotify must be connected). Returns the playlist URL plus how many tracks exported / failed. Confirm-gated.",
|
|
35867
|
-
annotations: toolAnnotations({ title: "Export event to Spotify", readOnly: false }),
|
|
35952
|
+
annotations: toolAnnotations({ title: "Export event to Spotify", readOnly: false, destructive: false }),
|
|
35868
35953
|
inputSchema: external_exports.object({
|
|
35869
35954
|
eventId: external_exports.string().describe("Event id."),
|
|
35870
35955
|
sectionIds: external_exports.array(external_exports.string()).min(1).describe("Section ids to include (from vibo_list_sections)."),
|
|
@@ -35886,7 +35971,7 @@ function registerPlaylistTools(server, client2) {
|
|
|
35886
35971
|
"vibo_export_event_to_apple_music",
|
|
35887
35972
|
{
|
|
35888
35973
|
description: "Export an event's song selections to a new Apple Music playlist (Apple Music must be connected). Returns the playlist URL plus how many tracks exported / failed. Confirm-gated.",
|
|
35889
|
-
annotations: toolAnnotations({ title: "Export event to Apple Music", readOnly: false }),
|
|
35974
|
+
annotations: toolAnnotations({ title: "Export event to Apple Music", readOnly: false, destructive: false }),
|
|
35890
35975
|
inputSchema: external_exports.object({
|
|
35891
35976
|
eventId: external_exports.string().describe("Event id."),
|
|
35892
35977
|
sectionIds: external_exports.array(external_exports.string()).min(1).describe("Section ids to include (from vibo_list_sections)."),
|
|
@@ -35941,7 +36026,7 @@ function registerNotificationTools(server, client2) {
|
|
|
35941
36026
|
"vibo_mark_notifications_read",
|
|
35942
36027
|
{
|
|
35943
36028
|
description: "Mark notifications as read \u2014 pass specific notificationIds, or readAll:true to clear everything. Confirm-gated.",
|
|
35944
|
-
annotations: toolAnnotations({ title: "Mark notifications read", readOnly: false }),
|
|
36029
|
+
annotations: toolAnnotations({ title: "Mark notifications read", readOnly: false, destructive: false }),
|
|
35945
36030
|
inputSchema: external_exports.object({
|
|
35946
36031
|
notificationIds: external_exports.array(external_exports.string()).optional().describe("Specific notification ids to mark read."),
|
|
35947
36032
|
readAll: external_exports.boolean().optional().describe("Mark every notification as read."),
|
|
@@ -36026,7 +36111,7 @@ function registerQuestionTools(server, client2, resolveUpload = nodeUploadResolv
|
|
|
36026
36111
|
"vibo_answer_question",
|
|
36027
36112
|
{
|
|
36028
36113
|
description: "Answer a section planning question. Provide the field matching the question's type: `text` for a text question, `selectedOptions` (array of option _ids from vibo_list_section_questions) for radio/checkbox/select, or `link` (array of URLs) for a link question. Use `otherOptionTitle` with the question's \"other\" option. For photo/file questions, pass local paths (`imagePaths`/`filePaths`) when the server can read your disk, or inline base64 bytes (`images`/`files`) otherwise. Confirm-gated.",
|
|
36029
|
-
annotations: toolAnnotations({ title: "Answer Vibo question", readOnly: false }),
|
|
36114
|
+
annotations: toolAnnotations({ title: "Answer Vibo question", readOnly: false, destructive: false }),
|
|
36030
36115
|
inputSchema: external_exports.object({
|
|
36031
36116
|
eventId: external_exports.string().describe("Event id."),
|
|
36032
36117
|
sectionId: external_exports.string().describe("Section id."),
|
|
@@ -36110,7 +36195,7 @@ function registerSongManagementTools(server, client2) {
|
|
|
36110
36195
|
"vibo_remove_song_from_section",
|
|
36111
36196
|
{
|
|
36112
36197
|
description: "Remove one or more songs from a section. Confirm-gated.",
|
|
36113
|
-
annotations: toolAnnotations({ title: "Remove songs from Vibo section", readOnly: false }),
|
|
36198
|
+
annotations: toolAnnotations({ title: "Remove songs from Vibo section", readOnly: false, destructive: true }),
|
|
36114
36199
|
inputSchema: external_exports.object({
|
|
36115
36200
|
eventId: external_exports.string().describe("Event id."),
|
|
36116
36201
|
sectionId: external_exports.string().describe("Section id."),
|
|
@@ -36129,7 +36214,7 @@ function registerSongManagementTools(server, client2) {
|
|
|
36129
36214
|
"vibo_update_song",
|
|
36130
36215
|
{
|
|
36131
36216
|
description: "Update songs in a section: mark must-play, flag as do-not-play, and/or set a comment. Provide at least one field. Confirm-gated.",
|
|
36132
|
-
annotations: toolAnnotations({ title: "Update Vibo section songs", readOnly: false }),
|
|
36217
|
+
annotations: toolAnnotations({ title: "Update Vibo section songs", readOnly: false, destructive: false }),
|
|
36133
36218
|
inputSchema: external_exports.object({
|
|
36134
36219
|
eventId: external_exports.string().describe("Event id."),
|
|
36135
36220
|
sectionId: external_exports.string().describe("Section id."),
|
|
@@ -36160,7 +36245,7 @@ function registerSongManagementTools(server, client2) {
|
|
|
36160
36245
|
"vibo_move_song",
|
|
36161
36246
|
{
|
|
36162
36247
|
description: "Move songs from one section to another. Confirm-gated.",
|
|
36163
|
-
annotations: toolAnnotations({ title: "Move Vibo section songs", readOnly: false }),
|
|
36248
|
+
annotations: toolAnnotations({ title: "Move Vibo section songs", readOnly: false, destructive: false }),
|
|
36164
36249
|
inputSchema: external_exports.object({
|
|
36165
36250
|
eventId: external_exports.string().describe("Event id."),
|
|
36166
36251
|
sourceSectionId: external_exports.string().describe("Section id the songs are currently in."),
|
|
@@ -36180,7 +36265,7 @@ function registerSongManagementTools(server, client2) {
|
|
|
36180
36265
|
"vibo_reorder_songs",
|
|
36181
36266
|
{
|
|
36182
36267
|
description: "Reorder songs within a section. Confirm-gated.",
|
|
36183
|
-
annotations: toolAnnotations({ title: "Reorder Vibo section songs", readOnly: false }),
|
|
36268
|
+
annotations: toolAnnotations({ title: "Reorder Vibo section songs", readOnly: false, destructive: false }),
|
|
36184
36269
|
inputSchema: external_exports.object({
|
|
36185
36270
|
eventId: external_exports.string().describe("Event id."),
|
|
36186
36271
|
sectionId: external_exports.string().describe("Section id."),
|
|
@@ -36204,7 +36289,7 @@ function registerCommentTools(server, client2) {
|
|
|
36204
36289
|
"vibo_comment_on_song",
|
|
36205
36290
|
{
|
|
36206
36291
|
description: "Leave a comment / note for the DJ on a specific song. Confirm-gated.",
|
|
36207
|
-
annotations: toolAnnotations({ title: "Comment on Vibo song", readOnly: false }),
|
|
36292
|
+
annotations: toolAnnotations({ title: "Comment on Vibo song", readOnly: false, destructive: false }),
|
|
36208
36293
|
inputSchema: external_exports.object({
|
|
36209
36294
|
eventId: external_exports.string().describe("Event id."),
|
|
36210
36295
|
sectionId: external_exports.string().describe("Section id."),
|
|
@@ -36224,7 +36309,7 @@ function registerCommentTools(server, client2) {
|
|
|
36224
36309
|
"vibo_delete_song_comment",
|
|
36225
36310
|
{
|
|
36226
36311
|
description: "Delete a comment on a song. Confirm-gated.",
|
|
36227
|
-
annotations: toolAnnotations({ title: "Delete Vibo song comment", readOnly: false }),
|
|
36312
|
+
annotations: toolAnnotations({ title: "Delete Vibo song comment", readOnly: false, destructive: true }),
|
|
36228
36313
|
inputSchema: external_exports.object({
|
|
36229
36314
|
eventId: external_exports.string().describe("Event id."),
|
|
36230
36315
|
sectionId: external_exports.string().describe("Section id."),
|
|
@@ -36244,7 +36329,7 @@ function registerCommentTools(server, client2) {
|
|
|
36244
36329
|
"vibo_comment_on_section",
|
|
36245
36330
|
{
|
|
36246
36331
|
description: "Leave a comment on a timeline section. Confirm-gated.",
|
|
36247
|
-
annotations: toolAnnotations({ title: "Comment on Vibo section", readOnly: false }),
|
|
36332
|
+
annotations: toolAnnotations({ title: "Comment on Vibo section", readOnly: false, destructive: false }),
|
|
36248
36333
|
inputSchema: external_exports.object({
|
|
36249
36334
|
eventId: external_exports.string().describe("Event id."),
|
|
36250
36335
|
sectionId: external_exports.string().describe("Section id (from vibo_list_sections)."),
|
|
@@ -36263,7 +36348,7 @@ function registerCommentTools(server, client2) {
|
|
|
36263
36348
|
"vibo_delete_section_comment",
|
|
36264
36349
|
{
|
|
36265
36350
|
description: "Delete a comment on a timeline section. Confirm-gated.",
|
|
36266
|
-
annotations: toolAnnotations({ title: "Delete Vibo section comment", readOnly: false }),
|
|
36351
|
+
annotations: toolAnnotations({ title: "Delete Vibo section comment", readOnly: false, destructive: true }),
|
|
36267
36352
|
inputSchema: external_exports.object({
|
|
36268
36353
|
eventId: external_exports.string().describe("Event id."),
|
|
36269
36354
|
sectionId: external_exports.string().describe("Section id (from vibo_list_sections)."),
|
|
@@ -36335,7 +36420,7 @@ function registerImportTools(server, client2) {
|
|
|
36335
36420
|
"vibo_import_playlist_to_section",
|
|
36336
36421
|
{
|
|
36337
36422
|
description: "Import selected tracks from a connected Spotify/Apple Music playlist into a section. Returns counts of added/existing/ignored. Confirm-gated.",
|
|
36338
|
-
annotations: toolAnnotations({ title: "Import playlist to section", readOnly: false }),
|
|
36423
|
+
annotations: toolAnnotations({ title: "Import playlist to section", readOnly: false, destructive: false }),
|
|
36339
36424
|
inputSchema: external_exports.object({
|
|
36340
36425
|
eventId: external_exports.string().describe("Event id."),
|
|
36341
36426
|
sectionId: external_exports.string().describe("Section id (from vibo_list_sections)."),
|
|
@@ -36406,7 +36491,7 @@ function registerCollaborationTools(server, client2) {
|
|
|
36406
36491
|
"vibo_invite_users",
|
|
36407
36492
|
{
|
|
36408
36493
|
description: "Invite people to an event by email (as host or guest). Confirm-gated.",
|
|
36409
|
-
annotations: toolAnnotations({ title: "Invite Vibo event users", readOnly: false }),
|
|
36494
|
+
annotations: toolAnnotations({ title: "Invite Vibo event users", readOnly: false, destructive: true }),
|
|
36410
36495
|
inputSchema: external_exports.object({
|
|
36411
36496
|
eventId: external_exports.string().describe("Event id."),
|
|
36412
36497
|
type: external_exports.enum(["host", "guest"]).describe("Invite as host or guest."),
|
|
@@ -36426,7 +36511,7 @@ function registerCollaborationTools(server, client2) {
|
|
|
36426
36511
|
"vibo_change_user_role",
|
|
36427
36512
|
{
|
|
36428
36513
|
description: "Change an event member's role between host and guest. Confirm-gated.",
|
|
36429
|
-
annotations: toolAnnotations({ title: "Change Vibo user role", readOnly: false }),
|
|
36514
|
+
annotations: toolAnnotations({ title: "Change Vibo user role", readOnly: false, destructive: true }),
|
|
36430
36515
|
inputSchema: external_exports.object({
|
|
36431
36516
|
eventId: external_exports.string().describe("Event id."),
|
|
36432
36517
|
userId: external_exports.string().describe("Id of the member to update."),
|
|
@@ -36445,7 +36530,7 @@ function registerCollaborationTools(server, client2) {
|
|
|
36445
36530
|
"vibo_remove_user",
|
|
36446
36531
|
{
|
|
36447
36532
|
description: "Remove a member from an event. Confirm-gated.",
|
|
36448
|
-
annotations: toolAnnotations({ title: "Remove Vibo event user", readOnly: false }),
|
|
36533
|
+
annotations: toolAnnotations({ title: "Remove Vibo event user", readOnly: false, destructive: true }),
|
|
36449
36534
|
inputSchema: external_exports.object({
|
|
36450
36535
|
eventId: external_exports.string().describe("Event id."),
|
|
36451
36536
|
userId: external_exports.string().describe("Id of the member to remove."),
|
|
@@ -36467,7 +36552,7 @@ function registerSectionEditTools(server, client2) {
|
|
|
36467
36552
|
"vibo_update_section",
|
|
36468
36553
|
{
|
|
36469
36554
|
description: "Edit a timeline section's name, time, note, or description. Subject to the section's host-edit permissions. Confirm-gated.",
|
|
36470
|
-
annotations: toolAnnotations({ title: "Update Vibo section", readOnly: false }),
|
|
36555
|
+
annotations: toolAnnotations({ title: "Update Vibo section", readOnly: false, destructive: false }),
|
|
36471
36556
|
inputSchema: external_exports.object({
|
|
36472
36557
|
eventId: external_exports.string(),
|
|
36473
36558
|
sectionId: external_exports.string(),
|
|
@@ -36503,7 +36588,7 @@ function registerUploadTools(server, client2, resolveUpload = nodeUploadResolver
|
|
|
36503
36588
|
"vibo_set_profile_photo",
|
|
36504
36589
|
{
|
|
36505
36590
|
description: "Set your Vibo profile photo from an image. Pass a local file `path` if the server shares your filesystem; otherwise pass the image bytes as base64 `fileData`. Returns the uploaded image URL. Confirm-gated.",
|
|
36506
|
-
annotations: toolAnnotations({ title: "Set Vibo profile photo", readOnly: false }),
|
|
36591
|
+
annotations: toolAnnotations({ title: "Set Vibo profile photo", readOnly: false, destructive: false }),
|
|
36507
36592
|
inputSchema: external_exports.object({
|
|
36508
36593
|
path: external_exports.string().optional().describe("Absolute path to a local image file (jpg/png). Local/stdio server only."),
|
|
36509
36594
|
fileData: external_exports.string().optional().describe("Base64-encoded image bytes (a `data:` URL prefix is allowed). Use this when the server cannot read your filesystem."),
|
|
@@ -36581,7 +36666,7 @@ function registerSessionTools(server, client2) {
|
|
|
36581
36666
|
"vibo_capture_session",
|
|
36582
36667
|
{
|
|
36583
36668
|
description: "Capture your Vibo login from a signed-in web.vibodj.com browser tab via the fetchproxy bridge \u2014 for accounts that sign in with Apple/Google/Facebook (no password). Requires the fetchproxy browser extension installed and you signed into https://web.vibodj.com; approve the pair code shown on first use. The token is saved locally and reused on future calls.",
|
|
36584
|
-
annotations: toolAnnotations({ title: "Capture Vibo session (SSO)", readOnly: false })
|
|
36669
|
+
annotations: toolAnnotations({ title: "Capture Vibo session (SSO)", readOnly: false, destructive: false })
|
|
36585
36670
|
},
|
|
36586
36671
|
async () => {
|
|
36587
36672
|
const { accessToken, refreshToken } = await captureViboSession();
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { dirname, join } from 'path';
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
|
-
import { loadDotenvSafely, readEnvVar, McpToolError, SessionNotAuthenticatedError, truncateErrorMessage, } from '@chrischall/mcp-utils';
|
|
3
|
+
import { loadDotenvSafely, readEnvVar, McpToolError, SessionNotAuthenticatedError, truncateErrorMessage, withAmbientCancellation, } from '@chrischall/mcp-utils';
|
|
4
4
|
import { loadSession, saveSession } from './session-store.js';
|
|
5
5
|
// Load .env for local dev; silently skip if dotenv is unavailable (e.g. the
|
|
6
6
|
// mcpb bundle, which externalizes dotenv). `override: false` means a
|
|
@@ -40,6 +40,25 @@ const REFRESH = `
|
|
|
40
40
|
// Error codes Vibo (and conventional GraphQL servers) use for an expired /
|
|
41
41
|
// missing session — these should trigger a token refresh + replay.
|
|
42
42
|
const AUTH_ERROR_CODES = new Set(['UNAUTHORIZED', 'UNAUTHENTICATED', 'FORBIDDEN']);
|
|
43
|
+
/**
|
|
44
|
+
* The signal every Vibo request is made with: our timeout, and the caller's
|
|
45
|
+
* cancellation if there is one (mcp-utils `cancel`).
|
|
46
|
+
*
|
|
47
|
+
* Until this, only the timeout could stop a request — a cancelled tool call
|
|
48
|
+
* held it open for the full budget while the child burned the CPU mcp-host
|
|
49
|
+
* meters it on. Measured on that fleet: claude.ai sent 101 cancellations in
|
|
50
|
+
* the week to 2026-09-20.
|
|
51
|
+
*
|
|
52
|
+
* ONE definition for both request paths, which is not tidiness: the two are
|
|
53
|
+
* the multipart upload and the plain query, they had the same seven lines
|
|
54
|
+
* copied between them, and the next person to add a third path is the one
|
|
55
|
+
* this saves. The `TimeoutError` checks at both call sites still name a real
|
|
56
|
+
* timeout — an abort from the caller arrives as `AbortError` and falls
|
|
57
|
+
* through to 'failed'.
|
|
58
|
+
*/
|
|
59
|
+
function requestSignal() {
|
|
60
|
+
return withAmbientCancellation(AbortSignal.timeout(REQUEST_TIMEOUT_MS));
|
|
61
|
+
}
|
|
43
62
|
export class ViboClient {
|
|
44
63
|
apiUrl;
|
|
45
64
|
email;
|
|
@@ -172,7 +191,7 @@ export class ViboClient {
|
|
|
172
191
|
method: 'POST',
|
|
173
192
|
headers, // NB: no content-type — fetch sets the multipart boundary
|
|
174
193
|
body: form,
|
|
175
|
-
signal:
|
|
194
|
+
signal: requestSignal(),
|
|
176
195
|
});
|
|
177
196
|
}
|
|
178
197
|
catch (err) {
|
|
@@ -265,7 +284,7 @@ export class ViboClient {
|
|
|
265
284
|
method: 'POST',
|
|
266
285
|
headers,
|
|
267
286
|
body: JSON.stringify({ query, variables }),
|
|
268
|
-
signal:
|
|
287
|
+
signal: requestSignal(),
|
|
269
288
|
});
|
|
270
289
|
}
|
|
271
290
|
catch (err) {
|
|
@@ -42,7 +42,7 @@ export function registerCollaborationTools(server, client) {
|
|
|
42
42
|
});
|
|
43
43
|
server.registerTool('vibo_invite_users', {
|
|
44
44
|
description: 'Invite people to an event by email (as host or guest). Confirm-gated.',
|
|
45
|
-
annotations: toolAnnotations({ title: 'Invite Vibo event users', readOnly: false }),
|
|
45
|
+
annotations: toolAnnotations({ title: 'Invite Vibo event users', readOnly: false, destructive: true }),
|
|
46
46
|
inputSchema: z.object({
|
|
47
47
|
eventId: z.string().describe('Event id.'),
|
|
48
48
|
type: z.enum(['host', 'guest']).describe('Invite as host or guest.'),
|
|
@@ -59,7 +59,7 @@ export function registerCollaborationTools(server, client) {
|
|
|
59
59
|
});
|
|
60
60
|
server.registerTool('vibo_change_user_role', {
|
|
61
61
|
description: "Change an event member's role between host and guest. Confirm-gated.",
|
|
62
|
-
annotations: toolAnnotations({ title: 'Change Vibo user role', readOnly: false }),
|
|
62
|
+
annotations: toolAnnotations({ title: 'Change Vibo user role', readOnly: false, destructive: true }),
|
|
63
63
|
inputSchema: z.object({
|
|
64
64
|
eventId: z.string().describe('Event id.'),
|
|
65
65
|
userId: z.string().describe('Id of the member to update.'),
|
|
@@ -75,7 +75,7 @@ export function registerCollaborationTools(server, client) {
|
|
|
75
75
|
});
|
|
76
76
|
server.registerTool('vibo_remove_user', {
|
|
77
77
|
description: 'Remove a member from an event. Confirm-gated.',
|
|
78
|
-
annotations: toolAnnotations({ title: 'Remove Vibo event user', readOnly: false }),
|
|
78
|
+
annotations: toolAnnotations({ title: 'Remove Vibo event user', readOnly: false, destructive: true }),
|
|
79
79
|
inputSchema: z.object({
|
|
80
80
|
eventId: z.string().describe('Event id.'),
|
|
81
81
|
userId: z.string().describe('Id of the member to remove.'),
|
package/dist/tools/comments.js
CHANGED
|
@@ -5,7 +5,7 @@ import { previewResult } from './shared.js';
|
|
|
5
5
|
export function registerCommentTools(server, client) {
|
|
6
6
|
server.registerTool('vibo_comment_on_song', {
|
|
7
7
|
description: 'Leave a comment / note for the DJ on a specific song. Confirm-gated.',
|
|
8
|
-
annotations: toolAnnotations({ title: 'Comment on Vibo song', readOnly: false }),
|
|
8
|
+
annotations: toolAnnotations({ title: 'Comment on Vibo song', readOnly: false, destructive: false }),
|
|
9
9
|
inputSchema: z.object({
|
|
10
10
|
eventId: z.string().describe('Event id.'),
|
|
11
11
|
sectionId: z.string().describe('Section id.'),
|
|
@@ -22,7 +22,7 @@ export function registerCommentTools(server, client) {
|
|
|
22
22
|
});
|
|
23
23
|
server.registerTool('vibo_delete_song_comment', {
|
|
24
24
|
description: 'Delete a comment on a song. Confirm-gated.',
|
|
25
|
-
annotations: toolAnnotations({ title: 'Delete Vibo song comment', readOnly: false }),
|
|
25
|
+
annotations: toolAnnotations({ title: 'Delete Vibo song comment', readOnly: false, destructive: true }),
|
|
26
26
|
inputSchema: z.object({
|
|
27
27
|
eventId: z.string().describe('Event id.'),
|
|
28
28
|
sectionId: z.string().describe('Section id.'),
|
|
@@ -39,7 +39,7 @@ export function registerCommentTools(server, client) {
|
|
|
39
39
|
});
|
|
40
40
|
server.registerTool('vibo_comment_on_section', {
|
|
41
41
|
description: 'Leave a comment on a timeline section. Confirm-gated.',
|
|
42
|
-
annotations: toolAnnotations({ title: 'Comment on Vibo section', readOnly: false }),
|
|
42
|
+
annotations: toolAnnotations({ title: 'Comment on Vibo section', readOnly: false, destructive: false }),
|
|
43
43
|
inputSchema: z.object({
|
|
44
44
|
eventId: z.string().describe('Event id.'),
|
|
45
45
|
sectionId: z.string().describe('Section id (from vibo_list_sections).'),
|
|
@@ -55,7 +55,7 @@ export function registerCommentTools(server, client) {
|
|
|
55
55
|
});
|
|
56
56
|
server.registerTool('vibo_delete_section_comment', {
|
|
57
57
|
description: 'Delete a comment on a timeline section. Confirm-gated.',
|
|
58
|
-
annotations: toolAnnotations({ title: 'Delete Vibo section comment', readOnly: false }),
|
|
58
|
+
annotations: toolAnnotations({ title: 'Delete Vibo section comment', readOnly: false, destructive: true }),
|
|
59
59
|
inputSchema: z.object({
|
|
60
60
|
eventId: z.string().describe('Event id.'),
|
|
61
61
|
sectionId: z.string().describe('Section id (from vibo_list_sections).'),
|
package/dist/tools/events.js
CHANGED
|
@@ -33,7 +33,7 @@ export function registerEventTools(server, client) {
|
|
|
33
33
|
});
|
|
34
34
|
server.registerTool('vibo_join_event', {
|
|
35
35
|
description: "Join an event you were invited to, via its share link or hash (e.g. a vibodj.app.link/... URL someone sent you). Returns the joined event's id. Confirm-gated.",
|
|
36
|
-
annotations: toolAnnotations({ title: 'Join Vibo event', readOnly: false }),
|
|
36
|
+
annotations: toolAnnotations({ title: 'Join Vibo event', readOnly: false, destructive: false }),
|
|
37
37
|
inputSchema: z.object({
|
|
38
38
|
link: z
|
|
39
39
|
.string()
|
|
@@ -56,7 +56,7 @@ export function registerEventTools(server, client) {
|
|
|
56
56
|
});
|
|
57
57
|
server.registerTool('vibo_leave_event', {
|
|
58
58
|
description: 'Leave an event you previously joined. Confirm-gated.',
|
|
59
|
-
annotations: toolAnnotations({ title: 'Leave Vibo event', readOnly: false }),
|
|
59
|
+
annotations: toolAnnotations({ title: 'Leave Vibo event', readOnly: false, destructive: true }),
|
|
60
60
|
inputSchema: z.object({
|
|
61
61
|
eventId: z.string().describe('Event id to leave.'),
|
|
62
62
|
confirm: schemaConfirm,
|
|
@@ -69,7 +69,7 @@ export function registerEventTools(server, client) {
|
|
|
69
69
|
});
|
|
70
70
|
server.registerTool('vibo_create_event_contact', {
|
|
71
71
|
description: 'Add a contact (host or guest) to an event with their name/email/phone. Confirm-gated.',
|
|
72
|
-
annotations: toolAnnotations({ title: 'Add Vibo event contact', readOnly: false }),
|
|
72
|
+
annotations: toolAnnotations({ title: 'Add Vibo event contact', readOnly: false, destructive: false }),
|
|
73
73
|
inputSchema: z.object({
|
|
74
74
|
eventId: z.string().describe('Event id.'),
|
|
75
75
|
role: z.enum(['host', 'guest']).describe("The contact's role in the event."),
|
package/dist/tools/imports.js
CHANGED
|
@@ -5,7 +5,7 @@ import { previewResult } from './shared.js';
|
|
|
5
5
|
export function registerImportTools(server, client) {
|
|
6
6
|
server.registerTool('vibo_import_playlist_to_section', {
|
|
7
7
|
description: 'Import selected tracks from a connected Spotify/Apple Music playlist into a section. Returns counts of added/existing/ignored. Confirm-gated.',
|
|
8
|
-
annotations: toolAnnotations({ title: 'Import playlist to section', readOnly: false }),
|
|
8
|
+
annotations: toolAnnotations({ title: 'Import playlist to section', readOnly: false, destructive: false }),
|
|
9
9
|
inputSchema: z.object({
|
|
10
10
|
eventId: z.string().describe('Event id.'),
|
|
11
11
|
sectionId: z.string().describe('Section id (from vibo_list_sections).'),
|
|
@@ -27,7 +27,7 @@ export function registerNotificationTools(server, client) {
|
|
|
27
27
|
});
|
|
28
28
|
server.registerTool('vibo_mark_notifications_read', {
|
|
29
29
|
description: 'Mark notifications as read — pass specific notificationIds, or readAll:true to clear everything. Confirm-gated.',
|
|
30
|
-
annotations: toolAnnotations({ title: 'Mark notifications read', readOnly: false }),
|
|
30
|
+
annotations: toolAnnotations({ title: 'Mark notifications read', readOnly: false, destructive: false }),
|
|
31
31
|
inputSchema: z.object({
|
|
32
32
|
notificationIds: z.array(z.string()).optional().describe('Specific notification ids to mark read.'),
|
|
33
33
|
readAll: z.boolean().optional().describe('Mark every notification as read.'),
|
package/dist/tools/playlists.js
CHANGED
|
@@ -42,7 +42,7 @@ export function registerPlaylistTools(server, client) {
|
|
|
42
42
|
});
|
|
43
43
|
server.registerTool('vibo_export_event_to_spotify', {
|
|
44
44
|
description: "Export an event's song selections to a new Spotify playlist (Spotify must be connected). Returns the playlist URL plus how many tracks exported / failed. Confirm-gated.",
|
|
45
|
-
annotations: toolAnnotations({ title: 'Export event to Spotify', readOnly: false }),
|
|
45
|
+
annotations: toolAnnotations({ title: 'Export event to Spotify', readOnly: false, destructive: false }),
|
|
46
46
|
inputSchema: z.object({
|
|
47
47
|
eventId: z.string().describe('Event id.'),
|
|
48
48
|
sectionIds: z.array(z.string()).min(1).describe('Section ids to include (from vibo_list_sections).'),
|
|
@@ -63,7 +63,7 @@ export function registerPlaylistTools(server, client) {
|
|
|
63
63
|
});
|
|
64
64
|
server.registerTool('vibo_export_event_to_apple_music', {
|
|
65
65
|
description: "Export an event's song selections to a new Apple Music playlist (Apple Music must be connected). Returns the playlist URL plus how many tracks exported / failed. Confirm-gated.",
|
|
66
|
-
annotations: toolAnnotations({ title: 'Export event to Apple Music', readOnly: false }),
|
|
66
|
+
annotations: toolAnnotations({ title: 'Export event to Apple Music', readOnly: false, destructive: false }),
|
|
67
67
|
inputSchema: z.object({
|
|
68
68
|
eventId: z.string().describe('Event id.'),
|
|
69
69
|
sectionIds: z.array(z.string()).min(1).describe('Section ids to include (from vibo_list_sections).'),
|
package/dist/tools/questions.js
CHANGED
|
@@ -25,7 +25,7 @@ export function registerQuestionTools(server, client, resolveUpload = nodeUpload
|
|
|
25
25
|
});
|
|
26
26
|
server.registerTool('vibo_answer_question', {
|
|
27
27
|
description: "Answer a section planning question. Provide the field matching the question's type: `text` for a text question, `selectedOptions` (array of option _ids from vibo_list_section_questions) for radio/checkbox/select, or `link` (array of URLs) for a link question. Use `otherOptionTitle` with the question's \"other\" option. For photo/file questions, pass local paths (`imagePaths`/`filePaths`) when the server can read your disk, or inline base64 bytes (`images`/`files`) otherwise. Confirm-gated.",
|
|
28
|
-
annotations: toolAnnotations({ title: 'Answer Vibo question', readOnly: false }),
|
|
28
|
+
annotations: toolAnnotations({ title: 'Answer Vibo question', readOnly: false, destructive: false }),
|
|
29
29
|
inputSchema: z.object({
|
|
30
30
|
eventId: z.string().describe('Event id.'),
|
|
31
31
|
sectionId: z.string().describe('Section id.'),
|
|
@@ -5,7 +5,7 @@ import { previewResult } from './shared.js';
|
|
|
5
5
|
export function registerSectionEditTools(server, client) {
|
|
6
6
|
server.registerTool('vibo_update_section', {
|
|
7
7
|
description: "Edit a timeline section's name, time, note, or description. Subject to the section's host-edit permissions. Confirm-gated.",
|
|
8
|
-
annotations: toolAnnotations({ title: 'Update Vibo section', readOnly: false }),
|
|
8
|
+
annotations: toolAnnotations({ title: 'Update Vibo section', readOnly: false, destructive: false }),
|
|
9
9
|
inputSchema: z.object({
|
|
10
10
|
eventId: z.string(),
|
|
11
11
|
sectionId: z.string(),
|
package/dist/tools/session.js
CHANGED
|
@@ -8,7 +8,7 @@ import { GET_ME } from '../gql.js';
|
|
|
8
8
|
export function registerSessionTools(server, client) {
|
|
9
9
|
server.registerTool('vibo_capture_session', {
|
|
10
10
|
description: "Capture your Vibo login from a signed-in web.vibodj.com browser tab via the fetchproxy bridge — for accounts that sign in with Apple/Google/Facebook (no password). Requires the fetchproxy browser extension installed and you signed into https://web.vibodj.com; approve the pair code shown on first use. The token is saved locally and reused on future calls.",
|
|
11
|
-
annotations: toolAnnotations({ title: 'Capture Vibo session (SSO)', readOnly: false }),
|
|
11
|
+
annotations: toolAnnotations({ title: 'Capture Vibo session (SSO)', readOnly: false, destructive: false }),
|
|
12
12
|
}, async () => {
|
|
13
13
|
const { accessToken, refreshToken } = await captureViboSession();
|
|
14
14
|
client.setTokens(accessToken, refreshToken);
|
|
@@ -5,7 +5,7 @@ import { previewResult } from './shared.js';
|
|
|
5
5
|
export function registerSongManagementTools(server, client) {
|
|
6
6
|
server.registerTool('vibo_remove_song_from_section', {
|
|
7
7
|
description: 'Remove one or more songs from a section. Confirm-gated.',
|
|
8
|
-
annotations: toolAnnotations({ title: 'Remove songs from Vibo section', readOnly: false }),
|
|
8
|
+
annotations: toolAnnotations({ title: 'Remove songs from Vibo section', readOnly: false, destructive: true }),
|
|
9
9
|
inputSchema: z.object({
|
|
10
10
|
eventId: z.string().describe('Event id.'),
|
|
11
11
|
sectionId: z.string().describe('Section id.'),
|
|
@@ -24,7 +24,7 @@ export function registerSongManagementTools(server, client) {
|
|
|
24
24
|
});
|
|
25
25
|
server.registerTool('vibo_update_song', {
|
|
26
26
|
description: 'Update songs in a section: mark must-play, flag as do-not-play, and/or set a comment. Provide at least one field. Confirm-gated.',
|
|
27
|
-
annotations: toolAnnotations({ title: 'Update Vibo section songs', readOnly: false }),
|
|
27
|
+
annotations: toolAnnotations({ title: 'Update Vibo section songs', readOnly: false, destructive: false }),
|
|
28
28
|
inputSchema: z.object({
|
|
29
29
|
eventId: z.string().describe('Event id.'),
|
|
30
30
|
sectionId: z.string().describe('Section id.'),
|
|
@@ -58,7 +58,7 @@ export function registerSongManagementTools(server, client) {
|
|
|
58
58
|
});
|
|
59
59
|
server.registerTool('vibo_move_song', {
|
|
60
60
|
description: 'Move songs from one section to another. Confirm-gated.',
|
|
61
|
-
annotations: toolAnnotations({ title: 'Move Vibo section songs', readOnly: false }),
|
|
61
|
+
annotations: toolAnnotations({ title: 'Move Vibo section songs', readOnly: false, destructive: false }),
|
|
62
62
|
inputSchema: z.object({
|
|
63
63
|
eventId: z.string().describe('Event id.'),
|
|
64
64
|
sourceSectionId: z.string().describe('Section id the songs are currently in.'),
|
|
@@ -78,7 +78,7 @@ export function registerSongManagementTools(server, client) {
|
|
|
78
78
|
});
|
|
79
79
|
server.registerTool('vibo_reorder_songs', {
|
|
80
80
|
description: 'Reorder songs within a section. Confirm-gated.',
|
|
81
|
-
annotations: toolAnnotations({ title: 'Reorder Vibo section songs', readOnly: false }),
|
|
81
|
+
annotations: toolAnnotations({ title: 'Reorder Vibo section songs', readOnly: false, destructive: false }),
|
|
82
82
|
inputSchema: z.object({
|
|
83
83
|
eventId: z.string().describe('Event id.'),
|
|
84
84
|
sectionId: z.string().describe('Section id.'),
|
package/dist/tools/songs.js
CHANGED
|
@@ -96,7 +96,7 @@ export function registerSongTools(server, client) {
|
|
|
96
96
|
'`quality.confidence`: adding a `likely-not-original` result puts a cover, karaoke ' +
|
|
97
97
|
'track or junk-metadata re-upload in front of a live DJ. If nothing looks original, ' +
|
|
98
98
|
'report the closest matches back rather than adding a best guess. Confirm-gated.',
|
|
99
|
-
annotations: toolAnnotations({ title: 'Add song to Vibo section', readOnly: false }),
|
|
99
|
+
annotations: toolAnnotations({ title: 'Add song to Vibo section', readOnly: false, destructive: false }),
|
|
100
100
|
inputSchema: z.object({
|
|
101
101
|
eventId: z.string().describe('Event id.'),
|
|
102
102
|
sectionId: z.string().describe('Section id to add the song to.'),
|
|
@@ -126,7 +126,7 @@ export function registerSongTools(server, client) {
|
|
|
126
126
|
});
|
|
127
127
|
server.registerTool('vibo_toggle_song_like', {
|
|
128
128
|
description: 'Like or unlike a song in a section. Confirm-gated.',
|
|
129
|
-
annotations: toolAnnotations({ title: 'Like/unlike Vibo song', readOnly: false }),
|
|
129
|
+
annotations: toolAnnotations({ title: 'Like/unlike Vibo song', readOnly: false, destructive: false }),
|
|
130
130
|
inputSchema: z.object({
|
|
131
131
|
eventId: z.string().describe('Event id.'),
|
|
132
132
|
sectionId: z.string().describe('Section id.'),
|
package/dist/tools/uploads.js
CHANGED
|
@@ -12,7 +12,7 @@ import { previewResult } from './shared.js';
|
|
|
12
12
|
export function registerUploadTools(server, client, resolveUpload = nodeUploadResolver) {
|
|
13
13
|
server.registerTool('vibo_set_profile_photo', {
|
|
14
14
|
description: 'Set your Vibo profile photo from an image. Pass a local file `path` if the server shares your filesystem; otherwise pass the image bytes as base64 `fileData`. Returns the uploaded image URL. Confirm-gated.',
|
|
15
|
-
annotations: toolAnnotations({ title: 'Set Vibo profile photo', readOnly: false }),
|
|
15
|
+
annotations: toolAnnotations({ title: 'Set Vibo profile photo', readOnly: false, destructive: false }),
|
|
16
16
|
inputSchema: z.object({
|
|
17
17
|
path: z.string().optional().describe('Absolute path to a local image file (jpg/png). Local/stdio server only.'),
|
|
18
18
|
fileData: z
|
package/dist/version.js
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
// literal on the line carrying the release marker; every manifest and the MCP
|
|
3
3
|
// server banner import VERSION from here, so there is exactly one place to keep
|
|
4
4
|
// in sync (and one release-please extra-files entry).
|
|
5
|
-
export const VERSION = '2.1
|
|
5
|
+
export const VERSION = '2.2.1'; // x-release-please-version
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibo-mcp",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"mcpName": "io.github.chrischall/vibo-mcp",
|
|
5
5
|
"description": "Vibo (vibodj.com) MCP server for Claude — host/couple event music planning & management. Developed and maintained by AI (Claude Code).",
|
|
6
6
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@chrischall/mcp-utils": "^
|
|
49
|
+
"@chrischall/mcp-utils": "^2.2.0",
|
|
50
50
|
"@fetchproxy/bootstrap": "^3.0.1",
|
|
51
51
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
52
52
|
"dotenv": "^17.4.0",
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/chrischall/vibo-mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "2.1
|
|
9
|
+
"version": "2.2.1",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "vibo-mcp",
|
|
14
|
-
"version": "2.1
|
|
14
|
+
"version": "2.2.1",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|