vibo-mcp 1.7.0 → 1.8.0
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 +27 -22
- package/dist/tools/collaboration.js +8 -3
- package/dist/tools/ideas.js +4 -2
- package/dist/tools/notifications.js +4 -2
- package/dist/tools/profile.js +4 -2
- package/dist/tools/songs.js +3 -2
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/vibo-mcp/SKILL.md +55 -0
|
@@ -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": "1.
|
|
10
|
+
"version": "1.8.0"
|
|
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": "1.
|
|
18
|
+
"version": "1.8.0",
|
|
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": "1.
|
|
4
|
+
"version": "1.8.0",
|
|
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
|
@@ -35790,7 +35790,7 @@ function toolAnnotations(opts = {}) {
|
|
|
35790
35790
|
}
|
|
35791
35791
|
|
|
35792
35792
|
// src/version.ts
|
|
35793
|
-
var VERSION = "1.
|
|
35793
|
+
var VERSION = "1.8.0";
|
|
35794
35794
|
|
|
35795
35795
|
// src/client.ts
|
|
35796
35796
|
import { dirname as dirname2, join as join3 } from "path";
|
|
@@ -36569,17 +36569,27 @@ var UPLOAD_USER_PHOTO = `
|
|
|
36569
36569
|
}
|
|
36570
36570
|
`;
|
|
36571
36571
|
|
|
36572
|
+
// src/view.ts
|
|
36573
|
+
var VIBO_VIEWS = ["compact", "full"];
|
|
36574
|
+
var NOTE = `compact strips image/avatar URLs from the response; "full" returns Vibo's payload untouched. No field projection: this server has no verified record of which Vibo fields matter, and inventing one would risk dropping a field a caller needs.`;
|
|
36575
|
+
var viewArg = () => viewParam(VIBO_VIEWS, { note: NOTE });
|
|
36576
|
+
function viewResponse(view, data) {
|
|
36577
|
+
const rung = resolveView(view, VIBO_VIEWS);
|
|
36578
|
+
return minifiedResult(rung === "compact" ? stripMediaUrls(data) : data);
|
|
36579
|
+
}
|
|
36580
|
+
|
|
36572
36581
|
// src/tools/profile.ts
|
|
36573
36582
|
function registerProfileTools(server, client2) {
|
|
36574
36583
|
server.registerTool(
|
|
36575
36584
|
"vibo_get_me",
|
|
36576
36585
|
{
|
|
36577
36586
|
description: "Get the signed-in Vibo user's profile (id, name, email, phone, locale, and whether Spotify/Apple Music are connected). Use the returned _id to recognize your own songs/contacts.",
|
|
36578
|
-
annotations: toolAnnotations({ title: "Get my Vibo profile", readOnly: true })
|
|
36587
|
+
annotations: toolAnnotations({ title: "Get my Vibo profile", readOnly: true }),
|
|
36588
|
+
inputSchema: { view: viewArg() }
|
|
36579
36589
|
},
|
|
36580
|
-
async () => {
|
|
36590
|
+
async ({ view }) => {
|
|
36581
36591
|
const data = await client2.gql(GET_ME);
|
|
36582
|
-
return
|
|
36592
|
+
return viewResponse(view, data.me);
|
|
36583
36593
|
}
|
|
36584
36594
|
);
|
|
36585
36595
|
server.registerTool(
|
|
@@ -36745,15 +36755,6 @@ function registerSectionTools(server, client2) {
|
|
|
36745
36755
|
);
|
|
36746
36756
|
}
|
|
36747
36757
|
|
|
36748
|
-
// src/view.ts
|
|
36749
|
-
var VIBO_VIEWS = ["compact", "full"];
|
|
36750
|
-
var NOTE = `compact strips image/avatar URLs from the response; "full" returns Vibo's payload untouched. No field projection: this server has no verified record of which Vibo fields matter, and inventing one would risk dropping a field a caller needs.`;
|
|
36751
|
-
var viewArg = () => viewParam(VIBO_VIEWS, { note: NOTE });
|
|
36752
|
-
function viewResponse(view, data) {
|
|
36753
|
-
const rung = resolveView(view, VIBO_VIEWS);
|
|
36754
|
-
return minifiedResult(rung === "compact" ? stripMediaUrls(data) : data);
|
|
36755
|
-
}
|
|
36756
|
-
|
|
36757
36758
|
// src/song-search.ts
|
|
36758
36759
|
var NON_ORIGINAL_MARKERS = [
|
|
36759
36760
|
"karaoke",
|
|
@@ -36896,11 +36897,12 @@ function registerSongTools(server, client2) {
|
|
|
36896
36897
|
isFlagged: external_exports.boolean().optional().describe("Filter to do-not-play / flagged songs."),
|
|
36897
36898
|
sortField: external_exports.enum(["likesCount", "createdAt", "title"]).optional(),
|
|
36898
36899
|
sortDirection: external_exports.enum(["asc", "desc"]).optional(),
|
|
36900
|
+
view: viewArg(),
|
|
36899
36901
|
limit: limitSchema,
|
|
36900
36902
|
skip: skipSchema
|
|
36901
36903
|
}
|
|
36902
36904
|
},
|
|
36903
|
-
async ({ eventId, sectionId, q, isMustPlay, isFlagged, sortField, sortDirection, limit, skip }) => {
|
|
36905
|
+
async ({ eventId, sectionId, q, isMustPlay, isFlagged, sortField, sortDirection, limit, skip, view }) => {
|
|
36904
36906
|
const filter = {};
|
|
36905
36907
|
if (q !== void 0) filter.q = q;
|
|
36906
36908
|
if (isMustPlay !== void 0) filter.isMustPlay = isMustPlay;
|
|
@@ -36913,7 +36915,7 @@ function registerSongTools(server, client2) {
|
|
|
36913
36915
|
...sortField ? { sort: { field: sortField, direction: sortDirection ?? "desc" } } : {}
|
|
36914
36916
|
};
|
|
36915
36917
|
const data = await client2.gql(GET_SECTION_SONGS, variables);
|
|
36916
|
-
return
|
|
36918
|
+
return viewResponse(view, data.getSectionSongs);
|
|
36917
36919
|
}
|
|
36918
36920
|
);
|
|
36919
36921
|
server.registerTool(
|
|
@@ -37100,15 +37102,16 @@ function registerNotificationTools(server, client2) {
|
|
|
37100
37102
|
description: "List your Vibo notifications (song additions, comments, DJ updates, etc.) with read state and linked event/section ids.",
|
|
37101
37103
|
annotations: toolAnnotations({ title: "List Vibo notifications", readOnly: true }),
|
|
37102
37104
|
inputSchema: {
|
|
37105
|
+
view: viewArg(),
|
|
37103
37106
|
limit: limitSchema,
|
|
37104
37107
|
skip: skipSchema
|
|
37105
37108
|
}
|
|
37106
37109
|
},
|
|
37107
|
-
async ({ limit, skip }) => {
|
|
37110
|
+
async ({ limit, skip, view }) => {
|
|
37108
37111
|
const data = await client2.gql(GET_NOTIFICATIONS, {
|
|
37109
37112
|
pagination: pagination(limit, skip)
|
|
37110
37113
|
});
|
|
37111
|
-
return
|
|
37114
|
+
return viewResponse(view, data.getNotifications);
|
|
37112
37115
|
}
|
|
37113
37116
|
);
|
|
37114
37117
|
server.registerTool(
|
|
@@ -37497,18 +37500,19 @@ function registerIdeasTools(server, client2) {
|
|
|
37497
37500
|
eventId: external_exports.string().describe("Event id."),
|
|
37498
37501
|
sectionId: external_exports.string().describe("Section id (from vibo_list_sections)."),
|
|
37499
37502
|
songIdeasId: external_exports.string().describe("The _id from vibo_list_section_song_ideas."),
|
|
37503
|
+
view: viewArg(),
|
|
37500
37504
|
limit: limitSchema,
|
|
37501
37505
|
skip: skipSchema
|
|
37502
37506
|
}
|
|
37503
37507
|
},
|
|
37504
|
-
async ({ eventId, sectionId, songIdeasId, limit, skip }) => {
|
|
37508
|
+
async ({ eventId, sectionId, songIdeasId, limit, skip, view }) => {
|
|
37505
37509
|
const data = await client2.gql(LIST_SONG_IDEAS_SONGS, {
|
|
37506
37510
|
eventId,
|
|
37507
37511
|
sectionId,
|
|
37508
37512
|
songIdeasId,
|
|
37509
37513
|
pagination: pagination(limit, skip)
|
|
37510
37514
|
});
|
|
37511
|
-
return
|
|
37515
|
+
return viewResponse(view, data.getEventSectionSongIdeasSongs);
|
|
37512
37516
|
}
|
|
37513
37517
|
);
|
|
37514
37518
|
}
|
|
@@ -37559,11 +37563,12 @@ function registerCollaborationTools(server, client2) {
|
|
|
37559
37563
|
inputSchema: {
|
|
37560
37564
|
eventId: external_exports.string().describe("Event id."),
|
|
37561
37565
|
usersType: external_exports.enum(["host", "guest"]).optional().describe("Filter to only hosts or only guests."),
|
|
37566
|
+
view: viewArg(),
|
|
37562
37567
|
limit: limitSchema.describe("Max items to return (default 20). Applies per group when usersType is omitted."),
|
|
37563
37568
|
skip: skipSchema
|
|
37564
37569
|
}
|
|
37565
37570
|
},
|
|
37566
|
-
async ({ eventId, usersType, limit, skip }) => {
|
|
37571
|
+
async ({ eventId, usersType, limit, skip, view }) => {
|
|
37567
37572
|
const page = pagination(limit, skip);
|
|
37568
37573
|
if (usersType) {
|
|
37569
37574
|
const data = await client2.gql(LIST_EVENT_USERS, {
|
|
@@ -37571,13 +37576,13 @@ function registerCollaborationTools(server, client2) {
|
|
|
37571
37576
|
usersType,
|
|
37572
37577
|
pagination: page
|
|
37573
37578
|
});
|
|
37574
|
-
return
|
|
37579
|
+
return viewResponse(view, { ...data.eventUsers, usersType });
|
|
37575
37580
|
}
|
|
37576
37581
|
const [hosts, guests] = await Promise.all([
|
|
37577
37582
|
client2.gql(LIST_EVENT_USERS, { eventId, usersType: "host", pagination: page }),
|
|
37578
37583
|
client2.gql(LIST_EVENT_USERS, { eventId, usersType: "guest", pagination: page })
|
|
37579
37584
|
]);
|
|
37580
|
-
return
|
|
37585
|
+
return viewResponse(view, {
|
|
37581
37586
|
hosts: hosts.eventUsers.users,
|
|
37582
37587
|
guests: guests.eventUsers.users,
|
|
37583
37588
|
hostsCount: hosts.eventUsers.totalCount,
|
|
@@ -2,6 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { minifiedResult, schemaConfirm, toolAnnotations } from '@chrischall/mcp-utils';
|
|
3
3
|
import { LIST_EVENT_USERS, INVITE_USERS, CHANGE_USER_ROLE, REMOVE_USER } from '../gql.js';
|
|
4
4
|
import { limitSchema, skipSchema, pagination, previewResult } from './shared.js';
|
|
5
|
+
import { viewArg, viewResponse } from '../view.js';
|
|
5
6
|
export function registerCollaborationTools(server, client) {
|
|
6
7
|
server.registerTool('vibo_list_event_users', {
|
|
7
8
|
description: "List the hosts and guests on an event. With no usersType, returns both groups merged ({hosts, guests, hostsCount, guestsCount}) and `limit`/`skip` apply per group; with usersType, returns that one group's page.",
|
|
@@ -9,10 +10,11 @@ export function registerCollaborationTools(server, client) {
|
|
|
9
10
|
inputSchema: {
|
|
10
11
|
eventId: z.string().describe('Event id.'),
|
|
11
12
|
usersType: z.enum(['host', 'guest']).optional().describe('Filter to only hosts or only guests.'),
|
|
13
|
+
view: viewArg(),
|
|
12
14
|
limit: limitSchema.describe('Max items to return (default 20). Applies per group when usersType is omitted.'),
|
|
13
15
|
skip: skipSchema,
|
|
14
16
|
},
|
|
15
|
-
}, async ({ eventId, usersType, limit, skip }) => {
|
|
17
|
+
}, async ({ eventId, usersType, limit, skip, view }) => {
|
|
16
18
|
const page = pagination(limit, skip);
|
|
17
19
|
if (usersType) {
|
|
18
20
|
const data = await client.gql(LIST_EVENT_USERS, {
|
|
@@ -20,7 +22,7 @@ export function registerCollaborationTools(server, client) {
|
|
|
20
22
|
usersType,
|
|
21
23
|
pagination: page,
|
|
22
24
|
});
|
|
23
|
-
return
|
|
25
|
+
return viewResponse(view, { ...data.eventUsers, usersType });
|
|
24
26
|
}
|
|
25
27
|
// The API returns nothing unless usersType is set, so fetch both groups
|
|
26
28
|
// and merge for the intuitive "everyone on the event" listing.
|
|
@@ -28,7 +30,10 @@ export function registerCollaborationTools(server, client) {
|
|
|
28
30
|
client.gql(LIST_EVENT_USERS, { eventId, usersType: 'host', pagination: page }),
|
|
29
31
|
client.gql(LIST_EVENT_USERS, { eventId, usersType: 'guest', pagination: page }),
|
|
30
32
|
]);
|
|
31
|
-
|
|
33
|
+
// BOTH exits go through the rung. The merged branch is the one that runs
|
|
34
|
+
// when `usersType` is omitted — the default call — so honouring `view` on
|
|
35
|
+
// only the filtered branch would leave the common path paying full price.
|
|
36
|
+
return viewResponse(view, {
|
|
32
37
|
hosts: hosts.eventUsers.users,
|
|
33
38
|
guests: guests.eventUsers.users,
|
|
34
39
|
hostsCount: hosts.eventUsers.totalCount,
|
package/dist/tools/ideas.js
CHANGED
|
@@ -2,6 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { minifiedResult, toolAnnotations } from '@chrischall/mcp-utils';
|
|
3
3
|
import { LIST_SECTION_SONG_IDEAS, LIST_SONG_IDEAS_SONGS } from '../gql.js';
|
|
4
4
|
import { limitSchema, skipSchema, pagination } from './shared.js';
|
|
5
|
+
import { viewArg, viewResponse } from '../view.js';
|
|
5
6
|
export function registerIdeasTools(server, client) {
|
|
6
7
|
server.registerTool('vibo_list_section_song_ideas', {
|
|
7
8
|
description: "List the DJ's suggested song-idea collections for a section (each with a title, songsCount and _id). Use a song-ideas _id with vibo_list_song_ideas_songs to see the suggested songs, then add the ones you like with vibo_add_song_to_section.",
|
|
@@ -27,16 +28,17 @@ export function registerIdeasTools(server, client) {
|
|
|
27
28
|
eventId: z.string().describe('Event id.'),
|
|
28
29
|
sectionId: z.string().describe('Section id (from vibo_list_sections).'),
|
|
29
30
|
songIdeasId: z.string().describe('The _id from vibo_list_section_song_ideas.'),
|
|
31
|
+
view: viewArg(),
|
|
30
32
|
limit: limitSchema,
|
|
31
33
|
skip: skipSchema,
|
|
32
34
|
},
|
|
33
|
-
}, async ({ eventId, sectionId, songIdeasId, limit, skip }) => {
|
|
35
|
+
}, async ({ eventId, sectionId, songIdeasId, limit, skip, view }) => {
|
|
34
36
|
const data = await client.gql(LIST_SONG_IDEAS_SONGS, {
|
|
35
37
|
eventId,
|
|
36
38
|
sectionId,
|
|
37
39
|
songIdeasId,
|
|
38
40
|
pagination: pagination(limit, skip),
|
|
39
41
|
});
|
|
40
|
-
return
|
|
42
|
+
return viewResponse(view, data.getEventSectionSongIdeasSongs);
|
|
41
43
|
});
|
|
42
44
|
}
|
|
@@ -2,19 +2,21 @@ import { z } from 'zod';
|
|
|
2
2
|
import { McpToolError, minifiedResult, schemaConfirm, toolAnnotations } from '@chrischall/mcp-utils';
|
|
3
3
|
import { GET_NOTIFICATIONS, GET_NOTIFICATIONS_COUNT, MARK_AS_READ } from '../gql.js';
|
|
4
4
|
import { limitSchema, skipSchema, pagination, previewResult } from './shared.js';
|
|
5
|
+
import { viewArg, viewResponse } from '../view.js';
|
|
5
6
|
export function registerNotificationTools(server, client) {
|
|
6
7
|
server.registerTool('vibo_list_notifications', {
|
|
7
8
|
description: 'List your Vibo notifications (song additions, comments, DJ updates, etc.) with read state and linked event/section ids.',
|
|
8
9
|
annotations: toolAnnotations({ title: 'List Vibo notifications', readOnly: true }),
|
|
9
10
|
inputSchema: {
|
|
11
|
+
view: viewArg(),
|
|
10
12
|
limit: limitSchema,
|
|
11
13
|
skip: skipSchema,
|
|
12
14
|
},
|
|
13
|
-
}, async ({ limit, skip }) => {
|
|
15
|
+
}, async ({ limit, skip, view }) => {
|
|
14
16
|
const data = await client.gql(GET_NOTIFICATIONS, {
|
|
15
17
|
pagination: pagination(limit, skip),
|
|
16
18
|
});
|
|
17
|
-
return
|
|
19
|
+
return viewResponse(view, data.getNotifications);
|
|
18
20
|
});
|
|
19
21
|
server.registerTool('vibo_get_notifications_count', {
|
|
20
22
|
description: 'Get the count of unread Vibo notifications.',
|
package/dist/tools/profile.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { minifiedResult, toolAnnotations } from '@chrischall/mcp-utils';
|
|
2
2
|
import { GET_ME } from '../gql.js';
|
|
3
|
+
import { viewArg, viewResponse } from '../view.js';
|
|
3
4
|
export function registerProfileTools(server, client) {
|
|
4
5
|
server.registerTool('vibo_get_me', {
|
|
5
6
|
description: "Get the signed-in Vibo user's profile (id, name, email, phone, locale, and whether Spotify/Apple Music are connected). Use the returned _id to recognize your own songs/contacts.",
|
|
6
7
|
annotations: toolAnnotations({ title: 'Get my Vibo profile', readOnly: true }),
|
|
7
|
-
|
|
8
|
+
inputSchema: { view: viewArg() },
|
|
9
|
+
}, async ({ view }) => {
|
|
8
10
|
const data = await client.gql(GET_ME);
|
|
9
|
-
return
|
|
11
|
+
return viewResponse(view, data.me);
|
|
10
12
|
});
|
|
11
13
|
server.registerTool('vibo_healthcheck', {
|
|
12
14
|
description: 'Verify connectivity and authentication to the Vibo API by fetching the current user. Returns ok:true with your account id when credentials work.',
|
package/dist/tools/songs.js
CHANGED
|
@@ -16,10 +16,11 @@ export function registerSongTools(server, client) {
|
|
|
16
16
|
isFlagged: z.boolean().optional().describe('Filter to do-not-play / flagged songs.'),
|
|
17
17
|
sortField: z.enum(['likesCount', 'createdAt', 'title']).optional(),
|
|
18
18
|
sortDirection: z.enum(['asc', 'desc']).optional(),
|
|
19
|
+
view: viewArg(),
|
|
19
20
|
limit: limitSchema,
|
|
20
21
|
skip: skipSchema,
|
|
21
22
|
},
|
|
22
|
-
}, async ({ eventId, sectionId, q, isMustPlay, isFlagged, sortField, sortDirection, limit, skip }) => {
|
|
23
|
+
}, async ({ eventId, sectionId, q, isMustPlay, isFlagged, sortField, sortDirection, limit, skip, view }) => {
|
|
23
24
|
const filter = {};
|
|
24
25
|
if (q !== undefined)
|
|
25
26
|
filter.q = q;
|
|
@@ -35,7 +36,7 @@ export function registerSongTools(server, client) {
|
|
|
35
36
|
...(sortField ? { sort: { field: sortField, direction: sortDirection ?? 'desc' } } : {}),
|
|
36
37
|
};
|
|
37
38
|
const data = await client.gql(GET_SECTION_SONGS, variables);
|
|
38
|
-
return
|
|
39
|
+
return viewResponse(view, data.getSectionSongs);
|
|
39
40
|
});
|
|
40
41
|
server.registerTool('vibo_search_songs', {
|
|
41
42
|
description: 'Search for songs to add to a section. ALWAYS query as "<Artist> - <Title>" with a ' +
|
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 = '1.
|
|
5
|
+
export const VERSION = '1.8.0'; // x-release-please-version
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibo-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
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>",
|
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": "1.
|
|
9
|
+
"version": "1.8.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "vibo-mcp",
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "1.8.0",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
package/skills/vibo-mcp/SKILL.md
CHANGED
|
@@ -85,6 +85,61 @@ you get a dry-run preview of exactly what would be sent.
|
|
|
85
85
|
- `vibo_mark_notifications_read`.
|
|
86
86
|
- `vibo_export_event_to_spotify` / `vibo_export_event_to_apple_music`.
|
|
87
87
|
|
|
88
|
+
## Response shape (`view`)
|
|
89
|
+
|
|
90
|
+
**Six of this server's 39 tools take `view: "compact" | "full"`**, and on every
|
|
91
|
+
one of them **`compact` is the DEFAULT**. You get the slim rung without asking.
|
|
92
|
+
|
|
93
|
+
They are exactly the six reads whose GraphQL document asks Vibo for media:
|
|
94
|
+
|
|
95
|
+
| Tool | What compact drops |
|
|
96
|
+
| --- | --- |
|
|
97
|
+
| `vibo_search_songs` | `thumbnails { s180x180 original }` per track |
|
|
98
|
+
| `vibo_get_section_songs` | `thumbnails { s180x180 original }` per song |
|
|
99
|
+
| `vibo_list_song_ideas_songs` | `thumbnails { s180x180 original }` per song |
|
|
100
|
+
| `vibo_list_event_users` | `imageUrl` per person (both exits — merged and filtered) |
|
|
101
|
+
| `vibo_list_notifications` | `imageUrl` per notification |
|
|
102
|
+
| `vibo_get_me` | `imageUrl` |
|
|
103
|
+
|
|
104
|
+
That correspondence is not a coincidence to be maintained by hand: a test in
|
|
105
|
+
`tests/view.test.ts` counts the media selections in `src/gql.ts` and asserts
|
|
106
|
+
the roster of `view`-taking tools matches. Add a seventh selection and it fails.
|
|
107
|
+
|
|
108
|
+
**Compact here is media stripping, not a field projection.** `src/view.ts`
|
|
109
|
+
writes no field list, because this repo holds no captured Vibo payload to
|
|
110
|
+
derive one from honestly; instead it removes keys whose value is a picture,
|
|
111
|
+
which is subtractive and so cannot drop a field nobody knew about.
|
|
112
|
+
|
|
113
|
+
What compact does **not** touch is the point of these tools: `songUrl`, the
|
|
114
|
+
`links` block (`spotify` / `youtube` / `appleMusic`) and the `quality` verdict
|
|
115
|
+
all survive. A streaming link is this server's product, not decoration —
|
|
116
|
+
`vibo_add_song_to_section` cannot add anything without `songUrl`, and the
|
|
117
|
+
quality verdict is computed from how many services carry a track. A `null` link
|
|
118
|
+
survives too: an absent key and a null one are the same to `JSON.parse` but not
|
|
119
|
+
to a reader deciding whether a service was checked.
|
|
120
|
+
|
|
121
|
+
`view: "full"` returns Vibo's payload untouched, cover art included. There is
|
|
122
|
+
deliberately **no `raw` rung**: nothing here re-serialises or normalises the
|
|
123
|
+
GraphQL response, so `full` already IS the upstream payload and a third value
|
|
124
|
+
would silently alias one that exists.
|
|
125
|
+
|
|
126
|
+
### The other 33 tools have no `view`
|
|
127
|
+
|
|
128
|
+
- **The 24 mutating tools** (every confirm-gated write, plus
|
|
129
|
+
`vibo_capture_session`) answer with a dry-run preview or a receipt — an id,
|
|
130
|
+
a count, a status. Nothing in a receipt is decoration, and slimming one is
|
|
131
|
+
how you lose the field that says what actually happened.
|
|
132
|
+
- **`vibo_healthcheck`** answers with a connectivity/auth diagnostic. It runs
|
|
133
|
+
the same `GET_ME` document as `vibo_get_me` and still takes no rung, because
|
|
134
|
+
it never returns Vibo's user object — it builds `{ok, userId, email}` here.
|
|
135
|
+
A `view` on it would be a parameter that changes nothing.
|
|
136
|
+
- **The 8 remaining reads hand back Vibo's GraphQL payload as it arrived**, and
|
|
137
|
+
their documents select no media, so there is nothing for a rung to remove.
|
|
138
|
+
|
|
139
|
+
A `view` passed to a tool that does not declare one is dropped by zod without a
|
|
140
|
+
warning, so a successful call is never evidence the rung was honoured. Check
|
|
141
|
+
the table above rather than assuming.
|
|
142
|
+
|
|
88
143
|
## Typical flow
|
|
89
144
|
|
|
90
145
|
1. `vibo_list_events` → pick an event id.
|