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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/bundle.js +33 -14
- package/dist/client.js +5 -6
- package/dist/tools/questions.js +6 -6
- package/dist/tools/session.js +3 -4
- package/dist/tools/shared.js +3 -3
- package/dist/tools/uploads.js +6 -6
- package/dist/upload-source.js +6 -25
- package/dist/version.js +1 -1
- package/package.json +4 -14
- package/server.json +2 -2
- package/dist/vibo-auth.js +0 -34
|
@@ -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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
31444
|
-
* src/upload-source.ts), so this method never touches the filesystem
|
|
31445
|
-
*
|
|
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`)
|
|
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
|
|
32575
|
-
files: external_exports.array(inlineFileSchema).optional().describe("Inline base64 files, for a file-attachment question
|
|
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`
|
|
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.
|
|
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).
|
|
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: "
|
|
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
|
|
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
|
|
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
|
|
137
|
-
* src/upload-source.ts), so this method never touches the filesystem
|
|
138
|
-
*
|
|
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();
|
package/dist/tools/questions.js
CHANGED
|
@@ -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
|
-
*
|
|
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`)
|
|
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
|
|
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
|
|
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.
|
|
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`
|
|
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 = {};
|
package/dist/tools/session.js
CHANGED
|
@@ -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:
|
|
6
|
-
//
|
|
7
|
-
//
|
|
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.",
|
package/dist/tools/shared.js
CHANGED
|
@@ -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
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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).'),
|
package/dist/tools/uploads.js
CHANGED
|
@@ -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
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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.
|
|
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).
|
|
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: '
|
|
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)
|
package/dist/upload-source.js
CHANGED
|
@@ -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
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// base64 instead. Both
|
|
8
|
-
// `ViboClient.gqlUpload` streams into a `FormData
|
|
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
|
|
13
|
-
//
|
|
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.
|
|
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.
|
|
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": "^
|
|
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.
|
|
9
|
+
"version": "1.5.4",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "vibo-mcp",
|
|
14
|
-
"version": "1.5.
|
|
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
|
-
};
|