vibo-mcp 2.3.0 → 2.3.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.
@@ -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.3.0"
10
+ "version": "2.3.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.3.0",
18
+ "version": "2.3.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.3.0",
4
+ "version": "2.3.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
@@ -35165,7 +35165,7 @@ function toolAnnotations(opts = {}) {
35165
35165
  }
35166
35166
 
35167
35167
  // src/version.ts
35168
- var VERSION = "2.3.0";
35168
+ var VERSION = "2.3.1";
35169
35169
 
35170
35170
  // src/client.ts
35171
35171
  import { createHash as createHash4 } from "crypto";
@@ -36000,7 +36000,9 @@ var UPLOAD_USER_PHOTO = `
36000
36000
  // src/view.ts
36001
36001
  var VIBO_VIEWS = ["compact", "full"];
36002
36002
  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.`;
36003
+ var EVENT_USERS_NOTE = `compact (default) returns each member as {_id, firstName, lastName, role} only \u2014 no email addresses or avatar URLs; "full" returns Vibo's payload untouched, emails included. This document's field list is fixed by its query, so the projection cannot drop an unknown field.`;
36003
36004
  var viewArg = () => viewParam(VIBO_VIEWS, { note: NOTE });
36005
+ var eventUsersViewArg = () => viewParam(VIBO_VIEWS, { note: EVENT_USERS_NOTE });
36004
36006
  function viewResponse(view, data) {
36005
36007
  const rung = resolveView(view, VIBO_VIEWS);
36006
36008
  return minifiedResult(rung === "compact" ? stripMediaUrls(data) : data);
@@ -37193,16 +37195,23 @@ function registerImportTools(server, client2) {
37193
37195
  }
37194
37196
 
37195
37197
  // src/tools/collaboration.ts
37198
+ function membersForView(view, users) {
37199
+ if (resolveView(view, VIBO_VIEWS) !== "compact") return users;
37200
+ return users.map((u) => {
37201
+ const { _id, firstName, lastName, role } = u ?? {};
37202
+ return { _id, firstName, lastName, role };
37203
+ });
37204
+ }
37196
37205
  function registerCollaborationTools(server, client2) {
37197
37206
  server.registerTool(
37198
37207
  "vibo_list_event_users",
37199
37208
  {
37200
- 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.",
37209
+ 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. Default (compact) view returns each member's id, name and role only; pass view:'full' for email addresses and avatars.",
37201
37210
  annotations: toolAnnotations({ title: "List Vibo event users", readOnly: true }),
37202
37211
  inputSchema: external_exports.object({
37203
37212
  eventId: external_exports.string().describe("Event id."),
37204
37213
  usersType: external_exports.enum(["host", "guest"]).optional().describe("Filter to only hosts or only guests."),
37205
- view: viewArg(),
37214
+ view: eventUsersViewArg(),
37206
37215
  limit: limitSchema.describe("Max items to return (default 20). Applies per group when usersType is omitted."),
37207
37216
  skip: skipSchema
37208
37217
  })
@@ -37215,15 +37224,19 @@ function registerCollaborationTools(server, client2) {
37215
37224
  usersType,
37216
37225
  pagination: page
37217
37226
  });
37218
- return viewResponse(view, { ...data.eventUsers, usersType });
37227
+ return viewResponse(view, {
37228
+ ...data.eventUsers,
37229
+ users: membersForView(view, data.eventUsers.users),
37230
+ usersType
37231
+ });
37219
37232
  }
37220
37233
  const [hosts, guests] = await Promise.all([
37221
37234
  client2.gql(LIST_EVENT_USERS, { eventId, usersType: "host", pagination: page }),
37222
37235
  client2.gql(LIST_EVENT_USERS, { eventId, usersType: "guest", pagination: page })
37223
37236
  ]);
37224
37237
  return viewResponse(view, {
37225
- hosts: hosts.eventUsers.users,
37226
- guests: guests.eventUsers.users,
37238
+ hosts: membersForView(view, hosts.eventUsers.users),
37239
+ guests: membersForView(view, guests.eventUsers.users),
37227
37240
  hostsCount: hosts.eventUsers.totalCount,
37228
37241
  guestsCount: guests.eventUsers.totalCount
37229
37242
  });
@@ -1,16 +1,24 @@
1
1
  import { z } from 'zod';
2
- import { minifiedResult, confirmTokenParam, toolAnnotations } from '@chrischall/mcp-utils';
2
+ import { minifiedResult, confirmTokenParam, resolveView, 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, confirmWrite, CONFIRM_NOTE } from './shared.js';
5
- import { viewArg, viewResponse } from '../view.js';
5
+ import { eventUsersViewArg, viewResponse, VIBO_VIEWS } from '../view.js';
6
+ function membersForView(view, users) {
7
+ if (resolveView(view, VIBO_VIEWS) !== 'compact')
8
+ return users;
9
+ return users.map((u) => {
10
+ const { _id, firstName, lastName, role } = (u ?? {});
11
+ return { _id, firstName, lastName, role };
12
+ });
13
+ }
6
14
  export function registerCollaborationTools(server, client) {
7
15
  server.registerTool('vibo_list_event_users', {
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.",
16
+ 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. Default (compact) view returns each member's id, name and role only; pass view:'full' for email addresses and avatars.",
9
17
  annotations: toolAnnotations({ title: 'List Vibo event users', readOnly: true }),
10
18
  inputSchema: z.object({
11
19
  eventId: z.string().describe('Event id.'),
12
20
  usersType: z.enum(['host', 'guest']).optional().describe('Filter to only hosts or only guests.'),
13
- view: viewArg(),
21
+ view: eventUsersViewArg(),
14
22
  limit: limitSchema.describe('Max items to return (default 20). Applies per group when usersType is omitted.'),
15
23
  skip: skipSchema,
16
24
  }),
@@ -22,7 +30,11 @@ export function registerCollaborationTools(server, client) {
22
30
  usersType,
23
31
  pagination: page,
24
32
  });
25
- return viewResponse(view, { ...data.eventUsers, usersType });
33
+ return viewResponse(view, {
34
+ ...data.eventUsers,
35
+ users: membersForView(view, data.eventUsers.users),
36
+ usersType,
37
+ });
26
38
  }
27
39
  // The API returns nothing unless usersType is set, so fetch both groups
28
40
  // and merge for the intuitive "everyone on the event" listing.
@@ -34,8 +46,8 @@ export function registerCollaborationTools(server, client) {
34
46
  // when `usersType` is omitted — the default call — so honouring `view` on
35
47
  // only the filtered branch would leave the common path paying full price.
36
48
  return viewResponse(view, {
37
- hosts: hosts.eventUsers.users,
38
- guests: guests.eventUsers.users,
49
+ hosts: membersForView(view, hosts.eventUsers.users),
50
+ guests: membersForView(view, guests.eventUsers.users),
39
51
  hostsCount: hosts.eventUsers.totalCount,
40
52
  guestsCount: guests.eventUsers.totalCount,
41
53
  });
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.3.0'; // x-release-please-version
5
+ export const VERSION = '2.3.1'; // x-release-please-version
package/dist/view.js CHANGED
@@ -18,13 +18,26 @@ import { minifiedResult, resolveView, stripMediaUrls, viewParam } from '@chrisch
18
18
  * When a real payload can be captured, a field projection belongs here beside
19
19
  * this one and will save considerably more. Until then this is the honest
20
20
  * ceiling, and this docblock says so rather than implying a shape was checked.
21
+ *
22
+ * One exception: `vibo_list_event_users` projects its members to
23
+ * `{_id, firstName, lastName, role}` on compact, because its document's field
24
+ * list is fixed by the query and the dropped `email` is third-party PII
25
+ * (fleet-audit #1136). See `membersForView` in `tools/collaboration.ts`.
21
26
  */
22
27
  export const VIBO_VIEWS = ['compact', 'full'];
23
28
  const NOTE = 'compact strips image/avatar URLs from the response; "full" returns Vibo\'s payload untouched. ' +
24
29
  'No field projection: this server has no verified record of which Vibo fields matter, and inventing ' +
25
30
  'one would risk dropping a field a caller needs.';
31
+ const EVENT_USERS_NOTE = 'compact (default) returns each member as {_id, firstName, lastName, role} only — no email addresses or ' +
32
+ 'avatar URLs; "full" returns Vibo\'s payload untouched, emails included. This document\'s field list is ' +
33
+ 'fixed by its query, so the projection cannot drop an unknown field.';
26
34
  /** The `view` parameter every read tool in this server takes. */
27
35
  export const viewArg = () => viewParam(VIBO_VIEWS, { note: NOTE });
36
+ /**
37
+ * `vibo_list_event_users`' `view` parameter — the one exception to NOTE's
38
+ * "no field projection" (see the docblock above), so its schema says so.
39
+ */
40
+ export const eventUsersViewArg = () => viewParam(VIBO_VIEWS, { note: EVENT_USERS_NOTE });
28
41
  /**
29
42
  * Answer in the requested rung.
30
43
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibo-mcp",
3
- "version": "2.3.0",
3
+ "version": "2.3.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>",
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.3.0",
9
+ "version": "2.3.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "vibo-mcp",
14
- "version": "2.3.0",
14
+ "version": "2.3.1",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },
@@ -104,7 +104,7 @@ They are exactly the six reads whose GraphQL document asks Vibo for media:
104
104
  | `vibo_search_songs` | `thumbnails { s180x180 original }` per track |
105
105
  | `vibo_get_section_songs` | `thumbnails { s180x180 original }` per song |
106
106
  | `vibo_list_song_ideas_songs` | `thumbnails { s180x180 original }` per song |
107
- | `vibo_list_event_users` | `imageUrl` per person (both exits — merged and filtered) |
107
+ | `vibo_list_event_users` | `email` + `imageUrl` per person — projected to `_id`/`firstName`/`lastName`/`role` on both exits (merged and filtered); `view: "full"` for emails |
108
108
  | `vibo_list_notifications` | `imageUrl` per notification |
109
109
  | `vibo_get_me` | `imageUrl` |
110
110
 
@@ -117,6 +117,11 @@ writes no field list, because this repo holds no captured Vibo payload to
117
117
  derive one from honestly; instead it removes keys whose value is a picture,
118
118
  which is subtractive and so cannot drop a field nobody knew about.
119
119
 
120
+ The one exception is `vibo_list_event_users`: its query fixes the member field
121
+ list, so compact projects each member to `_id`/`firstName`/`lastName`/`role`
122
+ and keeps other people's email addresses out of the default answer (fleet-audit
123
+ #1136). Ask for `view: "full"` only when an email is actually needed.
124
+
120
125
  What compact does **not** touch is the point of these tools: `songUrl`, the
121
126
  `links` block (`spotify` / `youtube` / `appleMusic`) and the `quality` verdict
122
127
  all survive. A streaming link is this server's product, not decoration —