vibo-mcp 1.5.2 → 1.5.4

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": "1.5.2"
10
+ "version": "1.5.4"
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.5.2",
18
+ "version": "1.5.4",
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.5.2",
4
+ "version": "1.5.4",
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
@@ -3644,7 +3644,12 @@ var require_fast_uri = __commonJS({
3644
3644
  }
3645
3645
  function resolve(baseURI, relativeURI, options) {
3646
3646
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3647
- const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
3647
+ const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
3648
+ const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
3649
+ if (baseMalformed || relativeMalformed) {
3650
+ throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
3651
+ }
3652
+ const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
3648
3653
  schemelessOptions.skipEscape = true;
3649
3654
  return serialize(resolved, schemelessOptions);
3650
3655
  }
@@ -3770,6 +3775,7 @@ var require_fast_uri = __commonJS({
3770
3775
  }
3771
3776
  var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
3772
3777
  var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
3778
+ var AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
3773
3779
  function getParseError(parsed, matches) {
3774
3780
  if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
3775
3781
  return 'URI path must start with "/" when authority is present.';
@@ -3804,6 +3810,20 @@ var require_fast_uri = __commonJS({
3804
3810
  parsed.error = "URI authority must not contain a literal backslash.";
3805
3811
  malformedAuthorityOrPort = true;
3806
3812
  }
3813
+ const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION);
3814
+ if (introducerMatch !== null) {
3815
+ const region = introducerMatch[1];
3816
+ const normalizedRegion = region.replace(/[\t\n\r]/g, "");
3817
+ if (normalizedRegion.length >= 2) {
3818
+ if (normalizedRegion.slice(0, 2) !== "//") {
3819
+ parsed.error = parsed.error || "URI authority must not contain a literal backslash.";
3820
+ malformedAuthorityOrPort = true;
3821
+ } else if (region.length !== normalizedRegion.length) {
3822
+ parsed.error = parsed.error || "URI authority introducer must not contain whitespace.";
3823
+ malformedAuthorityOrPort = true;
3824
+ }
3825
+ }
3826
+ }
3807
3827
  const matches = uri.match(URI_PARSE);
3808
3828
  if (matches) {
3809
3829
  parsed.scheme = matches[1];
@@ -31148,7 +31168,7 @@ function toolAnnotations(opts = {}) {
31148
31168
  }
31149
31169
 
31150
31170
  // src/version.ts
31151
- var VERSION = "1.5.2";
31171
+ var VERSION = "1.5.4";
31152
31172
 
31153
31173
  // src/client.ts
31154
31174
  import { dirname as dirname2, join as join2 } from "path";
@@ -31382,7 +31402,7 @@ var ViboClient = class {
31382
31402
  /**
31383
31403
  * Resolve the saved-session fallback and the deferred config error on first
31384
31404
  * use. Kept out of the constructor so construction is pure (Worker-safe):
31385
- * `loadSession()` reads homedir()/the filesystem, which the Workers runtime
31405
+ * `loadSession()` reads homedir()/the filesystem, which a sandboxed runtime
31386
31406
  * forbids at global scope. Runs its body at most once.
31387
31407
  */
31388
31408
  ensureConfigResolved() {
@@ -31440,10 +31460,9 @@ var ViboClient = class {
31440
31460
  * path (e.g. "variables.photo" or "variables.payload.answer.images.0") to an
31441
31461
  * in-memory {@link UploadFile} (blob + filename); `variables` must carry
31442
31462
  * `null` at each of those positions. The bytes arrive already resolved (from
31443
- * a local file on stdio, or inline base64 on the hosted connector see
31444
- * src/upload-source.ts), so this method never touches the filesystem and runs
31445
- * unchanged in the Workers runtime (`FormData`/`Blob`/`fetch` are all
31446
- * available there). Same auth + single-retry-on-expiry behavior as `gql`.
31463
+ * a local file, or inline base64 when the caller has no filesystem to name
31464
+ * see src/upload-source.ts), so this method never touches the filesystem
31465
+ * itself. Same auth + single-retry-on-expiry behavior as `gql`.
31447
31466
  */
31448
31467
  async gqlUpload(query, variables, files) {
31449
31468
  this.ensureConfigResolved();
@@ -32559,7 +32578,7 @@ function registerQuestionTools(server, client2, resolveUpload = nodeUploadResolv
32559
32578
  server.registerTool(
32560
32579
  "vibo_answer_question",
32561
32580
  {
32562
- 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`) on the stdio server, or inline base64 bytes (`images`/`files`) on the hosted connector. Confirm-gated.",
32581
+ 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.",
32563
32582
  annotations: toolAnnotations({ title: "Answer Vibo question", readOnly: false }),
32564
32583
  inputSchema: {
32565
32584
  eventId: external_exports.string().describe("Event id."),
@@ -32571,8 +32590,8 @@ function registerQuestionTools(server, client2, resolveUpload = nodeUploadResolv
32571
32590
  otherOptionTitle: external_exports.string().optional().describe(`Free-text value when selecting the question's "other" option.`),
32572
32591
  imagePaths: external_exports.array(external_exports.string()).optional().describe("Absolute local image file paths, for a photo question (local/stdio server only)."),
32573
32592
  filePaths: external_exports.array(external_exports.string()).optional().describe("Absolute local file paths, for a file-attachment question (local/stdio server only)."),
32574
- images: external_exports.array(inlineFileSchema).optional().describe("Inline base64 images, for a photo question (used by the hosted connector, which has no filesystem)."),
32575
- files: external_exports.array(inlineFileSchema).optional().describe("Inline base64 files, for a file-attachment question (used by the hosted connector)."),
32593
+ images: external_exports.array(inlineFileSchema).optional().describe("Inline base64 images, for a photo question \u2014 use these when the server cannot read your filesystem."),
32594
+ files: external_exports.array(inlineFileSchema).optional().describe("Inline base64 files, for a file-attachment question \u2014 use these when the server cannot read your filesystem."),
32576
32595
  confirm: schemaConfirm
32577
32596
  }
32578
32597
  },
@@ -32589,7 +32608,7 @@ function registerQuestionTools(server, client2, resolveUpload = nodeUploadResolv
32589
32608
  const hasFiles = fileRefs.length > 0;
32590
32609
  if (text === void 0 && selectedOptions === void 0 && link === void 0 && !hasImages && !hasFiles) {
32591
32610
  throw new McpToolError("Provide an answer: text, selectedOptions, link, imagePaths/images, or filePaths/files.", {
32592
- hint: "Match the question's type \u2014 text \u2192 `text`, radio/checkbox/select \u2192 `selectedOptions`, link \u2192 `link`, photo/file \u2192 `imagePaths`/`filePaths` (stdio) or `images`/`files` (hosted connector)."
32611
+ hint: "Match the question's type \u2014 text \u2192 `text`, radio/checkbox/select \u2192 `selectedOptions`, link \u2192 `link`, photo/file \u2192 `imagePaths`/`filePaths` for local files, or `images`/`files` for inline base64."
32593
32612
  });
32594
32613
  }
32595
32614
  const answer = {};
@@ -33034,11 +33053,11 @@ function registerUploadTools(server, client2, resolveUpload = nodeUploadResolver
33034
33053
  server.registerTool(
33035
33054
  "vibo_set_profile_photo",
33036
33055
  {
33037
- description: "Set your Vibo profile photo from an image. On the local (stdio) server pass a local file `path`; on the hosted connector pass the image bytes as base64 `fileData`. Returns the uploaded image URL. Confirm-gated.",
33056
+ 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.",
33038
33057
  annotations: toolAnnotations({ title: "Set Vibo profile photo", readOnly: false }),
33039
33058
  inputSchema: {
33040
33059
  path: external_exports.string().optional().describe("Absolute path to a local image file (jpg/png). Local/stdio server only."),
33041
- fileData: external_exports.string().optional().describe("Base64-encoded image bytes (a `data:` URL prefix is allowed). Used by the hosted connector, which has no filesystem."),
33060
+ 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."),
33042
33061
  filename: external_exports.string().optional().describe('Filename for the image when using fileData (default "photo.jpg").'),
33043
33062
  confirm: schemaConfirm
33044
33063
  }
@@ -33046,7 +33065,7 @@ function registerUploadTools(server, client2, resolveUpload = nodeUploadResolver
33046
33065
  async ({ path, fileData, filename, confirm }) => {
33047
33066
  if (!path && !fileData) {
33048
33067
  throw new McpToolError("Provide an image: a local file `path` or inline base64 `fileData`.", {
33049
- hint: "On the local server pass `path`; on the hosted connector pass `fileData` (base64)."
33068
+ hint: "Pass `path` for a local file, or `fileData` (base64) if the server cannot read your filesystem."
33050
33069
  });
33051
33070
  }
33052
33071
  if (!confirm) return previewResult("uploadUserPhoto", { photo: path ?? "(inline bytes)" });
package/dist/client.js CHANGED
@@ -5,7 +5,7 @@ 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
7
7
  // host-provided env var always wins over .env. The try/catch additionally
8
- // guards the Cloudflare Worker runtime (src/worker.ts): there `import.meta.url`
8
+ // guards non-Node runtimes, where `import.meta.url`
9
9
  // is undefined and `fileURLToPath(undefined)` would otherwise throw at module
10
10
  // init (Worker startup validation) — there is no filesystem / .env there anyway.
11
11
  try {
@@ -69,7 +69,7 @@ export class ViboClient {
69
69
  /**
70
70
  * Resolve the saved-session fallback and the deferred config error on first
71
71
  * use. Kept out of the constructor so construction is pure (Worker-safe):
72
- * `loadSession()` reads homedir()/the filesystem, which the Workers runtime
72
+ * `loadSession()` reads homedir()/the filesystem, which a sandboxed runtime
73
73
  * forbids at global scope. Runs its body at most once.
74
74
  */
75
75
  ensureConfigResolved() {
@@ -133,10 +133,9 @@ export class ViboClient {
133
133
  * path (e.g. "variables.photo" or "variables.payload.answer.images.0") to an
134
134
  * in-memory {@link UploadFile} (blob + filename); `variables` must carry
135
135
  * `null` at each of those positions. The bytes arrive already resolved (from
136
- * a local file on stdio, or inline base64 on the hosted connector see
137
- * src/upload-source.ts), so this method never touches the filesystem and runs
138
- * unchanged in the Workers runtime (`FormData`/`Blob`/`fetch` are all
139
- * available there). Same auth + single-retry-on-expiry behavior as `gql`.
136
+ * a local file, or inline base64 when the caller has no filesystem to name
137
+ * see src/upload-source.ts), so this method never touches the filesystem
138
+ * itself. Same auth + single-retry-on-expiry behavior as `gql`.
140
139
  */
141
140
  async gqlUpload(query, variables, files) {
142
141
  this.ensureConfigResolved();
@@ -6,7 +6,7 @@ import { previewResult, inlineFileSchema } from './shared.js';
6
6
  /**
7
7
  * `resolveUpload` is the injectable file-source seam for photo/file answers:
8
8
  * stdio uses the default `nodeUploadResolver` (local `*Paths`), the hosted
9
- * connector passes `inlineUploadResolver` (inline base64 `images`/`files`).
9
+ * remote caller sends inline base64 (`images`/`files`) instead.
10
10
  */
11
11
  export function registerQuestionTools(server, client, resolveUpload = nodeUploadResolver) {
12
12
  server.registerTool('vibo_list_section_questions', {
@@ -24,7 +24,7 @@ export function registerQuestionTools(server, client, resolveUpload = nodeUpload
24
24
  return textResult(data.getEventSectionQuestionsV2);
25
25
  });
26
26
  server.registerTool('vibo_answer_question', {
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`) on the stdio server, or inline base64 bytes (`images`/`files`) on the hosted connector. Confirm-gated.",
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
28
  annotations: toolAnnotations({ title: 'Answer Vibo question', readOnly: false }),
29
29
  inputSchema: {
30
30
  eventId: z.string().describe('Event id.'),
@@ -51,16 +51,16 @@ export function registerQuestionTools(server, client, resolveUpload = nodeUpload
51
51
  images: z
52
52
  .array(inlineFileSchema)
53
53
  .optional()
54
- .describe('Inline base64 images, for a photo question (used by the hosted connector, which has no filesystem).'),
54
+ .describe('Inline base64 images, for a photo question use these when the server cannot read your filesystem.'),
55
55
  files: z
56
56
  .array(inlineFileSchema)
57
57
  .optional()
58
- .describe('Inline base64 files, for a file-attachment question (used by the hosted connector).'),
58
+ .describe('Inline base64 files, for a file-attachment question use these when the server cannot read your filesystem.'),
59
59
  confirm: schemaConfirm,
60
60
  },
61
61
  }, async ({ eventId, sectionId, questionId, text, selectedOptions, link, otherOptionTitle, imagePaths, filePaths, images, files, confirm }) => {
62
62
  // Merge local-path and inline-byte file refs (in that order) into one list
63
- // per slot. stdio supplies paths; the hosted connector supplies inline
63
+ // per slot. A local caller supplies paths; a remote one supplies inline
64
64
  // bytes; the injected resolver turns each ref into an in-memory blob.
65
65
  const imageRefs = [
66
66
  ...(imagePaths ?? []).map((path) => ({ path })),
@@ -76,7 +76,7 @@ export function registerQuestionTools(server, client, resolveUpload = nodeUpload
76
76
  // modifier for selectedOptions — on its own it is not a valid answer.
77
77
  if (text === undefined && selectedOptions === undefined && link === undefined && !hasImages && !hasFiles) {
78
78
  throw new McpToolError('Provide an answer: text, selectedOptions, link, imagePaths/images, or filePaths/files.', {
79
- hint: "Match the question's type — text → `text`, radio/checkbox/select → `selectedOptions`, link → `link`, photo/file → `imagePaths`/`filePaths` (stdio) or `images`/`files` (hosted connector).",
79
+ hint: "Match the question's type — text → `text`, radio/checkbox/select → `selectedOptions`, link → `link`, photo/file → `imagePaths`/`filePaths` for local files, or `images`/`files` for inline base64.",
80
80
  });
81
81
  }
82
82
  const answer = {};
@@ -2,10 +2,9 @@ import { textResult, toolAnnotations } from '@chrischall/mcp-utils';
2
2
  import { captureViboSession } from '../auth.js';
3
3
  import { saveSession } from '../session-store.js';
4
4
  import { GET_ME } from '../gql.js';
5
- // NB: this registrar is STDIO-ONLY it is deliberately NOT wired into the
6
- // hosted Cloudflare connector (src/worker.ts). vibo_capture_session needs the
7
- // fetchproxy browser bridge + a signed-in browser tab, neither of which exists
8
- // in the serverless Worker runtime.
5
+ // NB: vibo_capture_session needs the fetchproxy browser bridge and a signed-in
6
+ // browser tab on the same machine, so this registrar is only useful where both
7
+ // exist a deployment without them should not wire it in.
9
8
  export function registerSessionTools(server, client) {
10
9
  server.registerTool('vibo_capture_session', {
11
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.",
@@ -19,9 +19,9 @@ export function pagination(limit, skip) {
19
19
  return { skip: skip ?? 0, limit: limit ?? 20 };
20
20
  }
21
21
  /**
22
- * One inline file (base64 bytes + optional filename) for the hosted connector,
23
- * which has no filesystem. Mirrors the local-path upload inputs so a tool can
24
- * accept either a filesystem path (stdio) or inline bytes (Worker).
22
+ * One inline file (base64 bytes + optional filename) for callers with no
23
+ * filesystem to name a hosted deployment reaches the server's disk, not the
24
+ * user's. Mirrors the local-path upload inputs so a tool can accept either.
25
25
  */
26
26
  export const inlineFileSchema = z.object({
27
27
  data: z.string().describe('Base64-encoded file bytes (a `data:` URL prefix is allowed).'),
@@ -5,27 +5,27 @@ import { nodeUploadResolver } from '../upload-source.js';
5
5
  import { previewResult } from './shared.js';
6
6
  /**
7
7
  * `resolveUpload` is the injectable file-source seam: the stdio server uses the
8
- * default `nodeUploadResolver` (reads a local `path`), while the hosted
9
- * Cloudflare connector (src/worker.ts) passes `inlineUploadResolver` so the same
10
- * tool works from base64 `fileData` with no filesystem.
8
+ * default `nodeUploadResolver`, which resolves either a local `path` or inline
9
+ * base64 `fileData`. A caller that shares no filesystem with the server sends
10
+ * the bytes inline, and the same tool works unchanged.
11
11
  */
12
12
  export function registerUploadTools(server, client, resolveUpload = nodeUploadResolver) {
13
13
  server.registerTool('vibo_set_profile_photo', {
14
- description: 'Set your Vibo profile photo from an image. On the local (stdio) server pass a local file `path`; on the hosted connector pass the image bytes as base64 `fileData`. Returns the uploaded image URL. Confirm-gated.',
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
15
  annotations: toolAnnotations({ title: 'Set Vibo profile photo', readOnly: false }),
16
16
  inputSchema: {
17
17
  path: z.string().optional().describe('Absolute path to a local image file (jpg/png). Local/stdio server only.'),
18
18
  fileData: z
19
19
  .string()
20
20
  .optional()
21
- .describe('Base64-encoded image bytes (a `data:` URL prefix is allowed). Used by the hosted connector, which has no filesystem.'),
21
+ .describe('Base64-encoded image bytes (a `data:` URL prefix is allowed). Use this when the server cannot read your filesystem.'),
22
22
  filename: z.string().optional().describe('Filename for the image when using fileData (default "photo.jpg").'),
23
23
  confirm: schemaConfirm,
24
24
  },
25
25
  }, async ({ path, fileData, filename, confirm }) => {
26
26
  if (!path && !fileData) {
27
27
  throw new McpToolError('Provide an image: a local file `path` or inline base64 `fileData`.', {
28
- hint: 'On the local server pass `path`; on the hosted connector pass `fileData` (base64).',
28
+ hint: 'Pass `path` for a local file, or `fileData` (base64) if the server cannot read your filesystem.',
29
29
  });
30
30
  }
31
31
  if (!confirm)
@@ -2,16 +2,14 @@
2
2
  //
3
3
  // The Vibo upload tools (`vibo_set_profile_photo`, `vibo_answer_question` with
4
4
  // photo/file answers) send local media through the GraphQL `Upload` scalar.
5
- // The stdio server reads those bytes from a local file path; the hosted
6
- // Cloudflare Worker has NO filesystem, so it receives the bytes inline as
7
- // base64 instead. Both paths converge on an in-memory {@link UploadFile} that
8
- // `ViboClient.gqlUpload` streams into a `FormData` — the client itself never
9
- // touches `node:fs`, which keeps it loadable in the Workers runtime.
5
+ // A caller that shares a filesystem with the server names a local path; one
6
+ // that does not anything reaching this server remotely — sends the bytes
7
+ // inline as base64 instead. Both converge on an in-memory {@link UploadFile}
8
+ // that `ViboClient.gqlUpload` streams into a `FormData`.
10
9
  //
11
10
  // A tool hands the resolver a {@link FileRef} (a local `path`, or inline base64
12
- // `data`) and gets back a `Blob` + filename. `nodeUploadResolver` (stdio)
13
- // resolves either; `inlineUploadResolver` (Worker) resolves only inline bytes
14
- // and throws an actionable error for a filesystem path.
11
+ // `data`) and gets back a `Blob` + filename; `nodeUploadResolver` resolves
12
+ // either.
15
13
  import { McpToolError } from '@chrischall/mcp-utils';
16
14
  const DEFAULT_FILENAME = 'upload';
17
15
  /** Decode base64 (optionally a `data:` URL) into an {@link UploadFile}. */
@@ -60,20 +58,3 @@ export const nodeUploadResolver = async (ref) => {
60
58
  hint: 'Pass a local file `path` (or inline base64 `fileData`).',
61
59
  });
62
60
  };
63
- /**
64
- * Hosted-connector resolver: the Worker has no filesystem, so it accepts ONLY
65
- * inline base64 bytes. A filesystem path draws an actionable error rather than
66
- * a runtime crash.
67
- */
68
- export const inlineUploadResolver = async (ref) => {
69
- if (ref.data)
70
- return blobFromBase64(ref.data, ref.filename);
71
- if (ref.path) {
72
- throw new McpToolError('Local file paths are not available on the hosted Vibo connector.', {
73
- hint: 'The hosted connector has no filesystem — pass the file bytes as base64 in `fileData` instead of a `path`.',
74
- });
75
- }
76
- throw new McpToolError('No file provided for upload.', {
77
- hint: 'Pass the file bytes as base64 in `fileData`.',
78
- });
79
- };
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.2'; // x-release-please-version
5
+ export const VERSION = '1.5.4'; // x-release-please-version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibo-mcp",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
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>",
@@ -41,30 +41,20 @@
41
41
  "dev": "node dist/index.js",
42
42
  "test": "vitest run",
43
43
  "test:watch": "vitest",
44
- "test:coverage": "vitest run --coverage",
45
- "worker:dev": "wrangler dev",
46
- "worker:deploy": "wrangler deploy",
47
- "worker:typecheck": "tsc --noEmit -p tsconfig.worker.json",
48
- "worker:test": "vitest run --config vitest.workers.config.ts"
44
+ "test:coverage": "vitest run --coverage"
49
45
  },
50
46
  "dependencies": {
51
47
  "@chrischall/mcp-utils": "^0.14.0",
52
- "@fetchproxy/bootstrap": "^1.7.0",
48
+ "@fetchproxy/bootstrap": "^2.0.0",
53
49
  "@modelcontextprotocol/sdk": "^1.29.0",
54
50
  "dotenv": "^17.4.0",
55
51
  "zod": "^4.4.2"
56
52
  },
57
53
  "devDependencies": {
58
- "@chrischall/mcp-connector": "^1.0.0",
59
- "@cloudflare/vitest-pool-workers": "^0.18.4",
60
- "@cloudflare/workers-oauth-provider": "^0.8.1",
61
- "@cloudflare/workers-types": "^5.20260708.1",
62
54
  "@types/node": "^26.0.0",
63
55
  "@vitest/coverage-v8": "^4.1.2",
64
- "agents": "^0.19.0",
65
56
  "esbuild": "^0.28.0",
66
57
  "typescript": "^7.0.2",
67
- "vitest": "^4.1.2",
68
- "wrangler": "^4.110.0"
58
+ "vitest": "^4.1.2"
69
59
  }
70
60
  }
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.5.2",
9
+ "version": "1.5.4",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "vibo-mcp",
14
- "version": "1.5.2",
14
+ "version": "1.5.4",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },
package/dist/vibo-auth.js DELETED
@@ -1,34 +0,0 @@
1
- import { ViboClient } from './client.js';
2
- import { GET_ME } from './gql.js';
3
- /**
4
- * `ConnectorAuth` for the Vibo remote connector: the login page collects the
5
- * user's Vibo email + password, VERIFIES them by constructing a `ViboClient`
6
- * with the injected creds and forcing a `signIn` + `me` read (a bad
7
- * email/password makes the mutation throw here, surfaced back on the login
8
- * page), and stores `{ email, password }` as the OAuth props that `worker.ts`'s
9
- * `buildClient` turns into a per-user client.
10
- *
11
- * SSO-only accounts (Apple/Google/Facebook, no password) are NOT supported on
12
- * the hosted connector — they have no password to sign in with. Use a Vibo
13
- * account with an email + password, or the local stdio server's
14
- * `vibo_capture_session` browser-capture flow instead.
15
- */
16
- export const viboAuth = {
17
- service: 'Vibo',
18
- accent: '#5B2AE0',
19
- privacyNote: 'Your Vibo email and password are stored encrypted and used only to sign into Vibo (vibodj.com) on your behalf to mint short-lived access tokens. SSO-only Apple/Google/Facebook accounts are not supported — use an account with a password.',
20
- fields: [
21
- { name: 'email', label: 'Vibo email', type: 'text' },
22
- { name: 'password', label: 'Vibo password', type: 'password' },
23
- ],
24
- async login(fields) {
25
- // Verify the creds up front: build a client with the injected email +
26
- // password and force a server-side signIn by hitting a cheap authenticated
27
- // read. Bad creds make `signIn` (and thus this call) throw here — surfaced
28
- // back on the login page. The response is discarded; the per-user client is
29
- // built fresh from the stored props by buildClient.
30
- const client = new ViboClient({ email: fields.email, password: fields.password });
31
- await client.gql(GET_ME);
32
- return { email: fields.email, password: fields.password };
33
- },
34
- };