vibo-mcp 2.3.1 → 2.4.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/README.md +50 -1
- package/dist/auth.js +7 -3
- package/dist/bundle.js +675 -81
- package/dist/client.js +13 -1
- package/dist/gql.js +50 -0
- package/dist/index.js +2 -0
- package/dist/reorder.js +27 -0
- package/dist/tools/lookups.js +59 -0
- package/dist/tools/section-manage.js +288 -0
- package/dist/tools/session.js +1 -1
- package/dist/tools/shared.js +8 -3
- package/dist/tools/song-management.js +70 -10
- package/dist/tools/songs.js +44 -3
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/server.json +2 -2
- package/skills/vibo-mcp/SKILL.md +11 -4
package/dist/bundle.js
CHANGED
|
@@ -19782,7 +19782,7 @@ function date4(params) {
|
|
|
19782
19782
|
return _coercedDate(ZodDate, params);
|
|
19783
19783
|
}
|
|
19784
19784
|
|
|
19785
|
-
// node_modules/@modelcontextprotocol/core/dist/auth-
|
|
19785
|
+
// node_modules/@modelcontextprotocol/server/node_modules/@modelcontextprotocol/core/dist/auth-CGP0BDVq.mjs
|
|
19786
19786
|
var LATEST_PROTOCOL_VERSION = "2025-11-25";
|
|
19787
19787
|
var SUPPORTED_PROTOCOL_VERSIONS = [
|
|
19788
19788
|
LATEST_PROTOCOL_VERSION,
|
|
@@ -20619,7 +20619,8 @@ var OAuthMetadataSchema = looseObject({
|
|
|
20619
20619
|
introspection_endpoint_auth_signing_alg_values_supported: array(string2()).optional(),
|
|
20620
20620
|
code_challenge_methods_supported: array(string2()).optional(),
|
|
20621
20621
|
client_id_metadata_document_supported: boolean2().optional(),
|
|
20622
|
-
authorization_response_iss_parameter_supported: boolean2().optional().catch(void 0)
|
|
20622
|
+
authorization_response_iss_parameter_supported: boolean2().optional().catch(void 0),
|
|
20623
|
+
dpop_signing_alg_values_supported: array(string2()).optional()
|
|
20623
20624
|
});
|
|
20624
20625
|
var OpenIdProviderMetadataSchema = looseObject({
|
|
20625
20626
|
issuer: string2(),
|
|
@@ -20720,7 +20721,7 @@ var OAuthTokenRevocationRequestSchema = object({
|
|
|
20720
20721
|
token_type_hint: string2().optional()
|
|
20721
20722
|
}).strip();
|
|
20722
20723
|
|
|
20723
|
-
// node_modules/@modelcontextprotocol/server/dist/src-
|
|
20724
|
+
// node_modules/@modelcontextprotocol/server/dist/src-D-y6h4N7.mjs
|
|
20724
20725
|
var BRANDS = /* @__PURE__ */ Symbol.for("mcp.sdk.errorBrands");
|
|
20725
20726
|
function stampErrorBrands(instance, ctor) {
|
|
20726
20727
|
const brands = /* @__PURE__ */ new Set();
|
|
@@ -20834,8 +20835,18 @@ var SdkError = class extends Error {
|
|
|
20834
20835
|
if (typeof this !== "function") throw new TypeError("isInstance must be called on the class (e.g. `SdkError.isInstance(value)`); for callbacks use `v => SdkError.isInstance(v)`");
|
|
20835
20836
|
return brandedHasInstance(this, value);
|
|
20836
20837
|
}
|
|
20837
|
-
|
|
20838
|
-
|
|
20838
|
+
/**
|
|
20839
|
+
* @param code - Stable string code identifying the failure ({@linkcode SdkErrorCode}).
|
|
20840
|
+
* @param message - Human-readable description.
|
|
20841
|
+
* @param data - Optional structured payload (for example the HTTP status carried by
|
|
20842
|
+
* {@linkcode SdkHttpError}). Opaque to the SDK: a `cause` key inside `data` is not
|
|
20843
|
+
* promoted to `Error.cause`.
|
|
20844
|
+
* @param options - Standard `ErrorOptions`, forwarded to `Error`. Pass the underlying
|
|
20845
|
+
* failure as `{ cause }` so it is reachable through the `Error.cause` chain that
|
|
20846
|
+
* loggers and error trackers walk.
|
|
20847
|
+
*/
|
|
20848
|
+
constructor(code, message, data, options) {
|
|
20849
|
+
super(message, options);
|
|
20839
20850
|
this.code = code;
|
|
20840
20851
|
this.data = data;
|
|
20841
20852
|
this.name = "SdkError";
|
|
@@ -20846,8 +20857,11 @@ var SdkHttpError = class extends SdkError {
|
|
|
20846
20857
|
static {
|
|
20847
20858
|
Object.defineProperty(this, "mcpBrand", { value: "mcp.SdkHttpError" });
|
|
20848
20859
|
}
|
|
20849
|
-
|
|
20850
|
-
|
|
20860
|
+
/**
|
|
20861
|
+
* @param options - Standard `ErrorOptions`, forwarded to `Error` (see {@linkcode SdkError}).
|
|
20862
|
+
*/
|
|
20863
|
+
constructor(code, message, data, options) {
|
|
20864
|
+
super(code, message, data, options);
|
|
20851
20865
|
this.name = "SdkHttpError";
|
|
20852
20866
|
}
|
|
20853
20867
|
get status() {
|
|
@@ -23798,7 +23812,7 @@ var INBOUND_VALIDATION_LADDER = [
|
|
|
23798
23812
|
evaluatedAt: "pre-dispatch",
|
|
23799
23813
|
codes: [HEADER_MISMATCH_ERROR_CODE],
|
|
23800
23814
|
conformance: ["http-header-validation"],
|
|
23801
|
-
rationale: "SEP-2243 standard `Mcp-Method` / `Mcp-Name` headers \u2014 presence, sentinel decoding, and `Mcp-Name` \u2194 body cross-check \u2014 are validated by the HTTP entry on a modern-classified request after the supported-revision gate and before dispatch. The classifier\u2019s own header-mismatch cells (protocol-version, `Mcp-Method` mismatch) stay on the edge `era-classification` rung; this rung carries the entry-layer presence/`Mcp-Name` half. Evaluated before the capability gate, the factory call, and the `Mcp-Param-*` rung so a request that fails several rungs is answered by the standard-header rung first. The documented order (after method-registry 5 and request-params 6) is NOT the observed precedence: serveModern evaluates this rung immediately after the supported-revision gate, so a request that also fails a dispatch rung is answered here before the dispatch rungs (5\u20136) are consulted."
|
|
23815
|
+
rationale: "SEP-2243 standard `MCP-Protocol-Version` / `Mcp-Method` / `Mcp-Name` headers \u2014 presence, sentinel decoding, and `Mcp-Name` \u2194 body cross-check \u2014 are validated by the HTTP entry on a modern-classified request after the supported-revision gate and before dispatch. The spec requires `MCP-Protocol-Version` and `Mcp-Method` on every modern *request* POST (`Mcp-Name` only for the methods that mirror `params.name` / `params.uri` / `params.taskId`, see `MCP_NAME_HEADER_SOURCE`) and names them in that order, so a request missing several is answered by the earliest. Notification POSTs are exempt: the presence half runs on requests only, so a modern-enveloped notification is dispatched even with no standard headers at all. The classifier\u2019s own header-mismatch cells (protocol-version, `Mcp-Method` mismatch) stay on the edge `era-classification` rung; this rung carries the entry-layer presence/`Mcp-Name` half \u2014 including the missing `MCP-Protocol-Version` cell, which cannot live on the edge rung without breaking body-primary classification. Evaluated before the capability gate, the factory call, and the `Mcp-Param-*` rung so a request that fails several rungs is answered by the standard-header rung first. The documented order (after method-registry 5 and request-params 6) is NOT the observed precedence: serveModern evaluates this rung immediately after the supported-revision gate, so a request that also fails a dispatch rung is answered here before the dispatch rungs (5\u20136) are consulted."
|
|
23802
23816
|
},
|
|
23803
23817
|
{
|
|
23804
23818
|
rung: "client-capabilities",
|
|
@@ -24599,7 +24613,7 @@ var Protocol = class {
|
|
|
24599
24613
|
return this._requestHandlers.get(method);
|
|
24600
24614
|
}
|
|
24601
24615
|
async _oncancel(notification) {
|
|
24602
|
-
if (
|
|
24616
|
+
if (notification.params.requestId === void 0) return;
|
|
24603
24617
|
this._requestHandlerAbortControllers.get(notification.params.requestId)?.abort(notification.params.reason);
|
|
24604
24618
|
}
|
|
24605
24619
|
_setupTimeout(messageId, timeout, maxTotalTimeout, onTimeout, resetTimeoutOnProgress = false) {
|
|
@@ -24998,19 +25012,20 @@ var Protocol = class {
|
|
|
24998
25012
|
const cancel = (reason) => {
|
|
24999
25013
|
if (responseReceived) return;
|
|
25000
25014
|
this._progressHandlers.delete(messageId);
|
|
25001
|
-
if (requestAbort === void 0)
|
|
25002
|
-
|
|
25003
|
-
|
|
25004
|
-
|
|
25005
|
-
|
|
25006
|
-
|
|
25007
|
-
|
|
25008
|
-
|
|
25009
|
-
|
|
25010
|
-
|
|
25011
|
-
|
|
25012
|
-
|
|
25013
|
-
|
|
25015
|
+
if (requestAbort === void 0) {
|
|
25016
|
+
if (request.method !== "initialize") this._transport?.send(this._envelopeOutbound({
|
|
25017
|
+
jsonrpc: "2.0",
|
|
25018
|
+
method: "notifications/cancelled",
|
|
25019
|
+
params: {
|
|
25020
|
+
requestId: messageId,
|
|
25021
|
+
reason: String(reason)
|
|
25022
|
+
}
|
|
25023
|
+
}), {
|
|
25024
|
+
relatedRequestId,
|
|
25025
|
+
resumptionToken,
|
|
25026
|
+
onresumptiontoken
|
|
25027
|
+
}).catch((error62) => this._onerror(/* @__PURE__ */ new Error(`Failed to send cancellation: ${error62}`)));
|
|
25028
|
+
} else requestAbort.abort();
|
|
25014
25029
|
reject(reason instanceof SdkError ? reason : new SdkError(SdkErrorCode.RequestTimeout, String(reason)));
|
|
25015
25030
|
};
|
|
25016
25031
|
this._responseHandlers.set(messageId, (response) => {
|
|
@@ -25090,7 +25105,7 @@ var Protocol = class {
|
|
|
25090
25105
|
jsonrpc: "2.0",
|
|
25091
25106
|
...notification
|
|
25092
25107
|
});
|
|
25093
|
-
if ((this._options?.debouncedNotificationMethods ?? []).includes(notification.method) && !notification.params &&
|
|
25108
|
+
if ((this._options?.debouncedNotificationMethods ?? []).includes(notification.method) && !notification.params && options?.relatedRequestId === void 0) {
|
|
25094
25109
|
if (this._pendingDebouncedNotifications.has(notification.method)) return;
|
|
25095
25110
|
this._pendingDebouncedNotifications.add(notification.method);
|
|
25096
25111
|
Promise.resolve().then(() => {
|
|
@@ -32393,7 +32408,7 @@ var Ajv = import_ajv.Ajv;
|
|
|
32393
32408
|
// node_modules/@modelcontextprotocol/server/dist/shimsNode.mjs
|
|
32394
32409
|
import process2 from "node:process";
|
|
32395
32410
|
|
|
32396
|
-
// node_modules/@modelcontextprotocol/server/dist/mcp-
|
|
32411
|
+
// node_modules/@modelcontextprotocol/server/dist/mcp-Dw2OlZ1f.mjs
|
|
32397
32412
|
var COMPLETABLE_SYMBOL = /* @__PURE__ */ Symbol.for("mcp.completable");
|
|
32398
32413
|
function isCompletable(schema) {
|
|
32399
32414
|
return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
|
|
@@ -32586,6 +32601,9 @@ function notificationToServerEvent(method, uri) {
|
|
|
32586
32601
|
};
|
|
32587
32602
|
}
|
|
32588
32603
|
}
|
|
32604
|
+
function supportsScopeChallengeResolver(transport) {
|
|
32605
|
+
return typeof transport === "object" && transport !== null && "setScopeChallengeResolver" in transport && typeof transport.setScopeChallengeResolver === "function";
|
|
32606
|
+
}
|
|
32589
32607
|
var DEFAULT_LEGACY_SHIM_MAX_ROUNDS = 8;
|
|
32590
32608
|
var DEFAULT_LEGACY_SHIM_ROUND_TIMEOUT_MS = 6e5;
|
|
32591
32609
|
function resolveLegacyShimOptions(options) {
|
|
@@ -33396,6 +33414,7 @@ var McpServer = class {
|
|
|
33396
33414
|
* ```
|
|
33397
33415
|
*/
|
|
33398
33416
|
async connect(transport) {
|
|
33417
|
+
if (supportsScopeChallengeResolver(transport)) transport.setScopeChallengeResolver((context) => this.resolveScopeChallenge(context));
|
|
33399
33418
|
return await this.server.connect(transport);
|
|
33400
33419
|
}
|
|
33401
33420
|
/**
|
|
@@ -33404,6 +33423,41 @@ var McpServer = class {
|
|
|
33404
33423
|
async close() {
|
|
33405
33424
|
await this.server.close();
|
|
33406
33425
|
}
|
|
33426
|
+
/** @internal */
|
|
33427
|
+
resolveScopeChallenge = (context) => {
|
|
33428
|
+
switch (context.request.method) {
|
|
33429
|
+
case "tools/call": {
|
|
33430
|
+
const toolName = context.request.params?.name;
|
|
33431
|
+
if (typeof toolName !== "string") return;
|
|
33432
|
+
const tool = this._registeredTools[toolName];
|
|
33433
|
+
if (tool === void 0 || !tool.enabled) return;
|
|
33434
|
+
return tool.scopeChallenge?.(context);
|
|
33435
|
+
}
|
|
33436
|
+
case "resources/read": {
|
|
33437
|
+
const resourceUri = context.request.params?.uri;
|
|
33438
|
+
if (typeof resourceUri !== "string") return;
|
|
33439
|
+
let uri;
|
|
33440
|
+
try {
|
|
33441
|
+
uri = new URL(resourceUri);
|
|
33442
|
+
} catch {
|
|
33443
|
+
return;
|
|
33444
|
+
}
|
|
33445
|
+
const resource = this._registeredResources[uri.toString()];
|
|
33446
|
+
if (resource !== void 0) return resource.enabled ? resource.scopeChallenge?.(context) : void 0;
|
|
33447
|
+
for (const template of Object.values(this._registeredResourceTemplates)) if (template.resourceTemplate.uriTemplate.match(uri.toString())) return template.enabled ? template.scopeChallenge?.(context) : void 0;
|
|
33448
|
+
return;
|
|
33449
|
+
}
|
|
33450
|
+
case "prompts/get": {
|
|
33451
|
+
const promptName = context.request.params?.name;
|
|
33452
|
+
if (typeof promptName !== "string") return;
|
|
33453
|
+
const prompt = this._registeredPrompts[promptName];
|
|
33454
|
+
if (prompt === void 0 || !prompt.enabled) return;
|
|
33455
|
+
return prompt.scopeChallenge?.(context);
|
|
33456
|
+
}
|
|
33457
|
+
default:
|
|
33458
|
+
return;
|
|
33459
|
+
}
|
|
33460
|
+
};
|
|
33407
33461
|
_toolHandlersInitialized = false;
|
|
33408
33462
|
setToolRequestHandlers() {
|
|
33409
33463
|
if (this._toolHandlersInitialized) return;
|
|
@@ -33570,7 +33624,10 @@ var McpServer = class {
|
|
|
33570
33624
|
}
|
|
33571
33625
|
for (const template of Object.values(this._registeredResourceTemplates)) {
|
|
33572
33626
|
const variables = template.resourceTemplate.uriTemplate.match(uri.toString());
|
|
33573
|
-
if (variables)
|
|
33627
|
+
if (variables) {
|
|
33628
|
+
if (!template.enabled) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Resource template ${template.resourceTemplate.uriTemplate} disabled`);
|
|
33629
|
+
return attachCacheHintFallback(await template.readCallback(uri, variables, ctx), template.cacheHint);
|
|
33630
|
+
}
|
|
33574
33631
|
}
|
|
33575
33632
|
throw new ResourceNotFoundError(request.params.uri);
|
|
33576
33633
|
});
|
|
@@ -33601,36 +33658,32 @@ var McpServer = class {
|
|
|
33601
33658
|
this._promptHandlersInitialized = true;
|
|
33602
33659
|
}
|
|
33603
33660
|
registerResource(name, uriOrTemplate, config2, readCallback) {
|
|
33604
|
-
const cacheHint = config2
|
|
33605
|
-
|
|
33606
|
-
if (cacheHint !== void 0) {
|
|
33607
|
-
assertValidCacheHint(cacheHint, `resource ${name}`);
|
|
33608
|
-
const rest = { ...config2 };
|
|
33609
|
-
delete rest.cacheHint;
|
|
33610
|
-
metadata = rest;
|
|
33611
|
-
}
|
|
33661
|
+
const { cacheHint, scopeChallenge, ...resourceMetadata } = config2;
|
|
33662
|
+
const metadata = resourceMetadata;
|
|
33663
|
+
if (cacheHint !== void 0) assertValidCacheHint(cacheHint, `resource ${name}`);
|
|
33612
33664
|
if (typeof uriOrTemplate === "string") {
|
|
33613
33665
|
if (this._registeredResources[uriOrTemplate]) throw new Error(`Resource ${uriOrTemplate} is already registered`);
|
|
33614
|
-
const registeredResource = this._createRegisteredResource(name, config2.title, uriOrTemplate, metadata, readCallback);
|
|
33666
|
+
const registeredResource = this._createRegisteredResource(name, config2.title, uriOrTemplate, metadata, scopeChallenge, readCallback);
|
|
33615
33667
|
if (cacheHint !== void 0) registeredResource.cacheHint = cacheHint;
|
|
33616
33668
|
this.setResourceRequestHandlers();
|
|
33617
33669
|
this.sendResourceListChanged();
|
|
33618
33670
|
return registeredResource;
|
|
33619
33671
|
} else {
|
|
33620
33672
|
if (this._registeredResourceTemplates[name]) throw new Error(`Resource template ${name} is already registered`);
|
|
33621
|
-
const registeredResourceTemplate = this._createRegisteredResourceTemplate(name, config2.title, uriOrTemplate, metadata, readCallback);
|
|
33673
|
+
const registeredResourceTemplate = this._createRegisteredResourceTemplate(name, config2.title, uriOrTemplate, metadata, scopeChallenge, readCallback);
|
|
33622
33674
|
if (cacheHint !== void 0) registeredResourceTemplate.cacheHint = cacheHint;
|
|
33623
33675
|
this.setResourceRequestHandlers();
|
|
33624
33676
|
this.sendResourceListChanged();
|
|
33625
33677
|
return registeredResourceTemplate;
|
|
33626
33678
|
}
|
|
33627
33679
|
}
|
|
33628
|
-
_createRegisteredResource(name, title, uri, metadata, readCallback) {
|
|
33680
|
+
_createRegisteredResource(name, title, uri, metadata, scopeChallenge, readCallback) {
|
|
33629
33681
|
const registeredResource = {
|
|
33630
33682
|
name,
|
|
33631
33683
|
title,
|
|
33632
33684
|
metadata,
|
|
33633
33685
|
readCallback,
|
|
33686
|
+
scopeChallenge,
|
|
33634
33687
|
enabled: true,
|
|
33635
33688
|
disable: () => registeredResource.update({ enabled: false }),
|
|
33636
33689
|
enable: () => registeredResource.update({ enabled: true }),
|
|
@@ -33644,6 +33697,7 @@ var McpServer = class {
|
|
|
33644
33697
|
if (updates.title !== void 0) registeredResource.title = updates.title;
|
|
33645
33698
|
if (updates.metadata !== void 0) registeredResource.metadata = updates.metadata;
|
|
33646
33699
|
if (updates.callback !== void 0) registeredResource.readCallback = updates.callback;
|
|
33700
|
+
if (updates.scopeChallenge !== void 0) registeredResource.scopeChallenge = updates.scopeChallenge === null ? void 0 : updates.scopeChallenge;
|
|
33647
33701
|
if (updates.enabled !== void 0) registeredResource.enabled = updates.enabled;
|
|
33648
33702
|
this.sendResourceListChanged();
|
|
33649
33703
|
}
|
|
@@ -33651,12 +33705,13 @@ var McpServer = class {
|
|
|
33651
33705
|
this._registeredResources[uri] = registeredResource;
|
|
33652
33706
|
return registeredResource;
|
|
33653
33707
|
}
|
|
33654
|
-
_createRegisteredResourceTemplate(name, title, template, metadata, readCallback) {
|
|
33708
|
+
_createRegisteredResourceTemplate(name, title, template, metadata, scopeChallenge, readCallback) {
|
|
33655
33709
|
const registeredResourceTemplate = {
|
|
33656
33710
|
resourceTemplate: template,
|
|
33657
33711
|
title,
|
|
33658
33712
|
metadata,
|
|
33659
33713
|
readCallback,
|
|
33714
|
+
scopeChallenge,
|
|
33660
33715
|
enabled: true,
|
|
33661
33716
|
disable: () => registeredResourceTemplate.update({ enabled: false }),
|
|
33662
33717
|
enable: () => registeredResourceTemplate.update({ enabled: true }),
|
|
@@ -33670,6 +33725,7 @@ var McpServer = class {
|
|
|
33670
33725
|
if (updates.template !== void 0) registeredResourceTemplate.resourceTemplate = updates.template;
|
|
33671
33726
|
if (updates.metadata !== void 0) registeredResourceTemplate.metadata = updates.metadata;
|
|
33672
33727
|
if (updates.callback !== void 0) registeredResourceTemplate.readCallback = updates.callback;
|
|
33728
|
+
if (updates.scopeChallenge !== void 0) registeredResourceTemplate.scopeChallenge = updates.scopeChallenge === null ? void 0 : updates.scopeChallenge;
|
|
33673
33729
|
if (updates.enabled !== void 0) registeredResourceTemplate.enabled = updates.enabled;
|
|
33674
33730
|
this.sendResourceListChanged();
|
|
33675
33731
|
}
|
|
@@ -33679,7 +33735,7 @@ var McpServer = class {
|
|
|
33679
33735
|
if (Array.isArray(variableNames) && variableNames.some((v) => !!template.completeCallback(v))) this.setCompletionRequestHandler();
|
|
33680
33736
|
return registeredResourceTemplate;
|
|
33681
33737
|
}
|
|
33682
|
-
_createRegisteredPrompt(name, title, description, argsSchema, callback, icons, _meta) {
|
|
33738
|
+
_createRegisteredPrompt(name, title, description, argsSchema, callback, icons, scopeChallenge, _meta) {
|
|
33683
33739
|
let currentArgsSchema = argsSchema;
|
|
33684
33740
|
let currentCallback = callback;
|
|
33685
33741
|
const registeredPrompt = {
|
|
@@ -33687,6 +33743,7 @@ var McpServer = class {
|
|
|
33687
33743
|
description,
|
|
33688
33744
|
argsSchema,
|
|
33689
33745
|
icons,
|
|
33746
|
+
scopeChallenge,
|
|
33690
33747
|
_meta,
|
|
33691
33748
|
handler: createPromptHandler(name, argsSchema, callback),
|
|
33692
33749
|
enabled: true,
|
|
@@ -33701,6 +33758,7 @@ var McpServer = class {
|
|
|
33701
33758
|
if (updates.title !== void 0) registeredPrompt.title = updates.title;
|
|
33702
33759
|
if (updates.description !== void 0) registeredPrompt.description = updates.description;
|
|
33703
33760
|
if (updates.icons !== void 0) registeredPrompt.icons = updates.icons;
|
|
33761
|
+
if (updates.scopeChallenge !== void 0) registeredPrompt.scopeChallenge = updates.scopeChallenge === null ? void 0 : updates.scopeChallenge;
|
|
33704
33762
|
if (updates._meta !== void 0) registeredPrompt._meta = updates._meta;
|
|
33705
33763
|
let needsHandlerRegen = false;
|
|
33706
33764
|
if (updates.argsSchema !== void 0) {
|
|
@@ -33728,7 +33786,7 @@ var McpServer = class {
|
|
|
33728
33786
|
}
|
|
33729
33787
|
return registeredPrompt;
|
|
33730
33788
|
}
|
|
33731
|
-
_createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, icons, execution, _meta, handler) {
|
|
33789
|
+
_createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, icons, execution, scopeChallenge, _meta, handler) {
|
|
33732
33790
|
validateAndWarnToolName(name);
|
|
33733
33791
|
if (inputSchema !== void 0) try {
|
|
33734
33792
|
const json2 = standardSchemaToJsonSchema(inputSchema, "input");
|
|
@@ -33747,6 +33805,7 @@ var McpServer = class {
|
|
|
33747
33805
|
annotations,
|
|
33748
33806
|
icons,
|
|
33749
33807
|
execution,
|
|
33808
|
+
scopeChallenge,
|
|
33750
33809
|
_meta,
|
|
33751
33810
|
handler,
|
|
33752
33811
|
executor: createToolExecutor(inputSchema, handler),
|
|
@@ -33785,6 +33844,7 @@ var McpServer = class {
|
|
|
33785
33844
|
}
|
|
33786
33845
|
if (updates.annotations !== void 0) registeredTool.annotations = updates.annotations;
|
|
33787
33846
|
if (updates.icons !== void 0) registeredTool.icons = updates.icons;
|
|
33847
|
+
if (updates.scopeChallenge !== void 0) registeredTool.scopeChallenge = updates.scopeChallenge === null ? void 0 : updates.scopeChallenge;
|
|
33788
33848
|
if (updates._meta !== void 0) registeredTool._meta = updates._meta;
|
|
33789
33849
|
if (updates.enabled !== void 0) registeredTool.enabled = updates.enabled;
|
|
33790
33850
|
this.sendToolListChanged();
|
|
@@ -33797,13 +33857,13 @@ var McpServer = class {
|
|
|
33797
33857
|
}
|
|
33798
33858
|
registerTool(name, config2, cb) {
|
|
33799
33859
|
if (this._registeredTools[name]) throw new Error(`Tool ${name} is already registered`);
|
|
33800
|
-
const { title, description, inputSchema, outputSchema, annotations, icons, _meta } = config2;
|
|
33801
|
-
return this._createRegisteredTool(name, title, description, normalizeRawShapeSchema(inputSchema), normalizeRawShapeSchema(outputSchema), annotations, icons, void 0, _meta, cb);
|
|
33860
|
+
const { title, description, inputSchema, outputSchema, annotations, icons, scopeChallenge, _meta } = config2;
|
|
33861
|
+
return this._createRegisteredTool(name, title, description, normalizeRawShapeSchema(inputSchema), normalizeRawShapeSchema(outputSchema), annotations, icons, void 0, scopeChallenge, _meta, cb);
|
|
33802
33862
|
}
|
|
33803
33863
|
registerPrompt(name, config2, cb) {
|
|
33804
33864
|
if (this._registeredPrompts[name]) throw new Error(`Prompt ${name} is already registered`);
|
|
33805
|
-
const { title, description, argsSchema, icons, _meta } = config2;
|
|
33806
|
-
const registeredPrompt = this._createRegisteredPrompt(name, title, description, normalizeRawShapeSchema(argsSchema), cb, icons, _meta);
|
|
33865
|
+
const { title, description, argsSchema, icons, scopeChallenge, _meta } = config2;
|
|
33866
|
+
const registeredPrompt = this._createRegisteredPrompt(name, title, description, normalizeRawShapeSchema(argsSchema), cb, icons, scopeChallenge, _meta);
|
|
33807
33867
|
this.setPromptRequestHandlers();
|
|
33808
33868
|
this.sendPromptListChanged();
|
|
33809
33869
|
return registeredPrompt;
|
|
@@ -33915,6 +33975,7 @@ function unwrapOptionalSchema(schema) {
|
|
|
33915
33975
|
}
|
|
33916
33976
|
|
|
33917
33977
|
// node_modules/@modelcontextprotocol/server/dist/stdio.mjs
|
|
33978
|
+
var swallowsErrorsAfterClose = /* @__PURE__ */ Symbol("swallowsErrorsAfterClose");
|
|
33918
33979
|
var StdioServerTransport = class {
|
|
33919
33980
|
_readBuffer;
|
|
33920
33981
|
_started = false;
|
|
@@ -33941,18 +34002,27 @@ var StdioServerTransport = class {
|
|
|
33941
34002
|
this.onerror?.(error62);
|
|
33942
34003
|
};
|
|
33943
34004
|
_onstdouterror = (error62) => {
|
|
34005
|
+
if (this._closed) return;
|
|
33944
34006
|
this.onerror?.(error62);
|
|
33945
34007
|
this.close().catch(() => {
|
|
33946
34008
|
});
|
|
33947
34009
|
};
|
|
34010
|
+
_onstdinclose = () => {
|
|
34011
|
+
this.close().catch(() => {
|
|
34012
|
+
});
|
|
34013
|
+
};
|
|
33948
34014
|
/**
|
|
33949
34015
|
* Starts listening for messages on `stdin`.
|
|
33950
34016
|
*/
|
|
33951
34017
|
async start() {
|
|
33952
34018
|
if (this._started) throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
|
|
33953
34019
|
this._started = true;
|
|
34020
|
+
for (const listener of this._stdout.listeners("error")) if (listener[swallowsErrorsAfterClose]) this._stdout.off("error", listener);
|
|
34021
|
+
if (this._stdin.readableEnded || this._stdin.destroyed) setImmediate(this._onstdinclose);
|
|
33954
34022
|
this._stdin.on("data", this._ondata);
|
|
33955
34023
|
this._stdin.on("error", this._onerror);
|
|
34024
|
+
this._stdin.on("end", this._onstdinclose);
|
|
34025
|
+
this._stdin.on("close", this._onstdinclose);
|
|
33956
34026
|
this._stdout.on("error", this._onstdouterror);
|
|
33957
34027
|
}
|
|
33958
34028
|
processReadBuffer() {
|
|
@@ -33969,7 +34039,9 @@ var StdioServerTransport = class {
|
|
|
33969
34039
|
this._closed = true;
|
|
33970
34040
|
this._stdin.off("data", this._ondata);
|
|
33971
34041
|
this._stdin.off("error", this._onerror);
|
|
33972
|
-
this.
|
|
34042
|
+
this._stdin.off("end", this._onstdinclose);
|
|
34043
|
+
this._stdin.off("close", this._onstdinclose);
|
|
34044
|
+
this._onstdouterror[swallowsErrorsAfterClose] = true;
|
|
33973
34045
|
if (this._stdin.listenerCount("data") === 0) this._stdin.pause();
|
|
33974
34046
|
this._readBuffer.clear();
|
|
33975
34047
|
this.onclose?.();
|
|
@@ -34246,6 +34318,7 @@ function serveStdio(factory, options = {}) {
|
|
|
34246
34318
|
return;
|
|
34247
34319
|
}
|
|
34248
34320
|
if (state.era === "modern" && await tryServeListen(message)) return;
|
|
34321
|
+
if (isTornDown()) return;
|
|
34249
34322
|
state.instance.channel.deliver(message);
|
|
34250
34323
|
return;
|
|
34251
34324
|
}
|
|
@@ -34312,6 +34385,7 @@ function serveStdio(factory, options = {}) {
|
|
|
34312
34385
|
};
|
|
34313
34386
|
}
|
|
34314
34387
|
if (await tryServeListen(message)) return;
|
|
34388
|
+
if (isTornDown()) return;
|
|
34315
34389
|
state.instance.channel.deliver(message, { classification: opening.classification });
|
|
34316
34390
|
return;
|
|
34317
34391
|
case "legacy": {
|
|
@@ -34398,6 +34472,9 @@ function toError(value) {
|
|
|
34398
34472
|
return value instanceof Error ? value : new Error(String(value));
|
|
34399
34473
|
}
|
|
34400
34474
|
|
|
34475
|
+
// node_modules/@modelcontextprotocol/server/dist/index.mjs
|
|
34476
|
+
var DEFAULT_MAX_REQUEST_BODY_SIZE = 4 * 1024 * 1024;
|
|
34477
|
+
|
|
34401
34478
|
// node_modules/@chrischall/mcp-utils/dist/errors/index.js
|
|
34402
34479
|
var DEFAULT_ERROR_MESSAGE_MAX = 500;
|
|
34403
34480
|
var McpToolError = class extends Error {
|
|
@@ -34419,11 +34496,12 @@ var SessionNotAuthenticatedError = class extends McpToolError {
|
|
|
34419
34496
|
this.name = "SessionNotAuthenticatedError";
|
|
34420
34497
|
}
|
|
34421
34498
|
};
|
|
34499
|
+
var HEADER_NAME_RUN = "[a-z0-9_-]{0,40}";
|
|
34422
34500
|
var BEARER_RE = /(bearer\s+)[A-Za-z0-9._~+/=-]{8,}/gi;
|
|
34423
|
-
var JWT_RE =
|
|
34501
|
+
var JWT_RE = /(?<![A-Za-z0-9_-])[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{8,}(?![A-Za-z0-9_-])/g;
|
|
34424
34502
|
var BASIC_AUTH_RE = /(authorization\s*:\s*basic\s+)[A-Za-z0-9+/=_-]{6,}/gi;
|
|
34425
|
-
var SET_COOKIE_RE = /(\bset-cookie\s*:\s*)([
|
|
34426
|
-
var COOKIE_HEADER_RE = /((?<!set-)\bcookie\s*:\s*)((?:[
|
|
34503
|
+
var SET_COOKIE_RE = /(\bset-cookie\s*:\s*)([^=;,:\s]+)=[^;,\s]*/gi;
|
|
34504
|
+
var COOKIE_HEADER_RE = /((?<!set-)\bcookie\s*:\s*)((?:[^=;,:\s]+=[^;,\s]*)(?:;\s*[^=;,:\s]+=[^;,\s]*)*)/gi;
|
|
34427
34505
|
var API_KEY_RE = new RegExp([
|
|
34428
34506
|
"sk-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])",
|
|
34429
34507
|
// OpenAI / Anthropic (incl. sk-ant-…)
|
|
@@ -34438,10 +34516,10 @@ var API_KEY_RE = new RegExp([
|
|
|
34438
34516
|
"whsec_[A-Za-z0-9]{16,}\\b"
|
|
34439
34517
|
// webhook signing secret (Stripe-style)
|
|
34440
34518
|
].map((p) => `\\b${p}`).join("|"), "g");
|
|
34441
|
-
var QUERY_SECRET_RE =
|
|
34519
|
+
var QUERY_SECRET_RE = new RegExp(`([?&](?:(?:access|refresh|id|auth|session|csrf|xsrf)[_-]?token|client[_-]?secret|api[_-]?secret|api[_-]?key|password|passwd|signature|token|key|sig|(?:php|j)?sess(?:ion)?[_-]?id|sid|x[-_]${HEADER_NAME_RUN}?(?:api[-_]?key|token|secret|auth)${HEADER_NAME_RUN})=)[^&#\\s"'<>\`]+`, "gi");
|
|
34442
34520
|
var OAUTH_CODE_RE = /([?&]code=)(?=[^&#\s"'<>`]{16,})[^&#\s"'<>`]+/gi;
|
|
34443
34521
|
var FORM_PASSWORD_RE = /((?:^|[\s,;:"'(\[{])(?:password|passwd)=)[^&#\s"'<>`]+/gim;
|
|
34444
|
-
var HEADER_SECRET_RE =
|
|
34522
|
+
var HEADER_SECRET_RE = new RegExp(`(\\b(?:x[-_]${HEADER_NAME_RUN}?(?:api[-_]?key|token|secret|auth)${HEADER_NAME_RUN}|api[-_]?key)\\s*:\\s*)[^\\s,;"'<>\`]+`, "gi");
|
|
34445
34523
|
var AWS_SIGV4_RE = /([?&]X-Amz-(?:Signature|Security-Token|Credential)=)[^&#\s"'<>`]+/gi;
|
|
34446
34524
|
var JSON_SECRET_KEYS = [
|
|
34447
34525
|
"(?:access|refresh|id|auth|session|bearer|csrf|xsrf)[_-]?token",
|
|
@@ -34449,7 +34527,8 @@ var JSON_SECRET_KEYS = [
|
|
|
34449
34527
|
"api[_-]?secret",
|
|
34450
34528
|
"(?:x[_-])?api[_-]?key",
|
|
34451
34529
|
"private[_-]?key",
|
|
34452
|
-
|
|
34530
|
+
`x[_-]${HEADER_NAME_RUN}?(?:api[_-]?key|token|secret|auth)${HEADER_NAME_RUN}`,
|
|
34531
|
+
// bounded like HEADER_SECRET_RE
|
|
34453
34532
|
"(?:proxy-)?authorization",
|
|
34454
34533
|
"password",
|
|
34455
34534
|
"passwd",
|
|
@@ -34458,12 +34537,30 @@ var JSON_SECRET_KEYS = [
|
|
|
34458
34537
|
].join("|");
|
|
34459
34538
|
var JSON_SECRET_DQ_RE = new RegExp(`("(?:${JSON_SECRET_KEYS})"\\s*:\\s*")(?:[^"\\\\]|\\\\.)*(")`, "gi");
|
|
34460
34539
|
var JSON_SECRET_SQ_RE = new RegExp(`('(?:${JSON_SECRET_KEYS})'\\s*:\\s*')(?:[^'\\\\]|\\\\.)*(')`, "gi");
|
|
34540
|
+
var JSON_SECRET_NUM_RE = new RegExp(`(["'](?:${JSON_SECRET_KEYS})["']\\s*:\\s*)-?\\d+(?:\\.\\d+)?(?![\\d.])`, "gi");
|
|
34541
|
+
var JSON_COOKIE_DQ_RE = /("((?:set-)?cookie)"\s*:\s*)("(?:[^"\\]|\\.)*"|\[\s*"(?:[^"\\]|\\.)*"(?:\s*,\s*"(?:[^"\\]|\\.)*")*)/gi;
|
|
34542
|
+
var JSON_COOKIE_SQ_RE = /('((?:set-)?cookie)'\s*:\s*)('(?:[^'\\]|\\.)*'|\[\s*'(?:[^'\\]|\\.)*'(?:\s*,\s*'(?:[^'\\]|\\.)*')*)/gi;
|
|
34543
|
+
var DQ_STRING_RE = /"((?:[^"\\]|\\.)*)"/g;
|
|
34544
|
+
var SQ_STRING_RE = /'((?:[^'\\]|\\.)*)'/g;
|
|
34545
|
+
function redactJsonCookie(quote) {
|
|
34546
|
+
const stringRe = quote === '"' ? DQ_STRING_RE : SQ_STRING_RE;
|
|
34547
|
+
return (_m, prefix, key, value) => {
|
|
34548
|
+
const setCookie = key.toLowerCase() === "set-cookie";
|
|
34549
|
+
const redacted = value.replace(stringRe, (_s, body) => {
|
|
34550
|
+
const out = setCookie ? body.replace(/^(\s*[^=;]+)=[^;]*/, "$1=[REDACTED]") : body.replace(/=[^;]*/g, "=[REDACTED]");
|
|
34551
|
+
return `${quote}${out}${quote}`;
|
|
34552
|
+
});
|
|
34553
|
+
return `${prefix}${redacted}`;
|
|
34554
|
+
};
|
|
34555
|
+
}
|
|
34461
34556
|
function redactSecrets(text) {
|
|
34462
|
-
return text.replace(BEARER_RE, "$1[REDACTED]").replace(BASIC_AUTH_RE, "$1[REDACTED]").replace(SET_COOKIE_RE, "$1$2=[REDACTED]").replace(COOKIE_HEADER_RE, (_m, prefix, pairs) => `${prefix}${pairs.replace(/=[^;,\s]*/g, "=[REDACTED]")}`).replace(API_KEY_RE, "[REDACTED]").replace(HEADER_SECRET_RE, "$1[REDACTED]").replace(QUERY_SECRET_RE, "$1[REDACTED]").replace(OAUTH_CODE_RE, "$1[REDACTED]").replace(FORM_PASSWORD_RE, "$1[REDACTED]").replace(AWS_SIGV4_RE, "$1[REDACTED]").replace(JSON_SECRET_DQ_RE, "$1[REDACTED]$2").replace(JSON_SECRET_SQ_RE, "$1[REDACTED]$2").replace(JWT_RE, "[REDACTED]");
|
|
34557
|
+
return text.replace(BEARER_RE, "$1[REDACTED]").replace(BASIC_AUTH_RE, "$1[REDACTED]").replace(SET_COOKIE_RE, "$1$2=[REDACTED]").replace(COOKIE_HEADER_RE, (_m, prefix, pairs) => `${prefix}${pairs.replace(/=[^;,\s]*/g, "=[REDACTED]")}`).replace(API_KEY_RE, "[REDACTED]").replace(HEADER_SECRET_RE, "$1[REDACTED]").replace(QUERY_SECRET_RE, "$1[REDACTED]").replace(OAUTH_CODE_RE, "$1[REDACTED]").replace(FORM_PASSWORD_RE, "$1[REDACTED]").replace(AWS_SIGV4_RE, "$1[REDACTED]").replace(JSON_COOKIE_DQ_RE, redactJsonCookie('"')).replace(JSON_COOKIE_SQ_RE, redactJsonCookie("'")).replace(JSON_SECRET_DQ_RE, "$1[REDACTED]$2").replace(JSON_SECRET_SQ_RE, "$1[REDACTED]$2").replace(JSON_SECRET_NUM_RE, "$1[REDACTED]").replace(JWT_RE, "[REDACTED]");
|
|
34463
34558
|
}
|
|
34559
|
+
var ERROR_REDACTION_INPUT_MAX = 64 * 1024;
|
|
34464
34560
|
function truncateErrorMessage(text, max = DEFAULT_ERROR_MESSAGE_MAX) {
|
|
34465
34561
|
const str = text === null || text === void 0 ? "" : String(text);
|
|
34466
|
-
const
|
|
34562
|
+
const cap = Math.max(ERROR_REDACTION_INPUT_MAX, max);
|
|
34563
|
+
const redacted = redactSecrets(str.length > cap ? str.slice(0, cap) : str);
|
|
34467
34564
|
if (redacted.length <= max)
|
|
34468
34565
|
return redacted;
|
|
34469
34566
|
return `${redacted.slice(0, max)}\u2026 [truncated]`;
|
|
@@ -34828,6 +34925,9 @@ async function tokenConfirmation(action, fb) {
|
|
|
34828
34925
|
const subject = await fb.subject();
|
|
34829
34926
|
if (isToolResult(subject))
|
|
34830
34927
|
return subject;
|
|
34928
|
+
if (subject.payload === void 0) {
|
|
34929
|
+
throw new TypeError(`confirm token: ${fb.tool}'s subject() returned no payload. Bind what the call will do (the arguments or the request body), or pass \`args\` to confirmationFromEnv.`);
|
|
34930
|
+
}
|
|
34831
34931
|
const binding = {
|
|
34832
34932
|
tool: fb.tool,
|
|
34833
34933
|
...fb.account === void 0 ? {} : { account: fb.account },
|
|
@@ -34920,9 +35020,19 @@ function readConfirmMode(env = process.env) {
|
|
|
34920
35020
|
}
|
|
34921
35021
|
return "refuse";
|
|
34922
35022
|
}
|
|
34923
|
-
function
|
|
35023
|
+
function readConfirmTtl(env) {
|
|
34924
35024
|
const raw = readEnvVar("MCP_CONFIRM_TTL_SECONDS", { env })?.trim();
|
|
34925
|
-
|
|
35025
|
+
if (!raw)
|
|
35026
|
+
return DEFAULT_TTL_SECONDS2;
|
|
35027
|
+
if (/^[1-9]\d*$/.test(raw))
|
|
35028
|
+
return Number(raw);
|
|
35029
|
+
const key = `ttl:${raw}`;
|
|
35030
|
+
if (!warned.has(key)) {
|
|
35031
|
+
warned.add(key);
|
|
35032
|
+
process.stderr.write(`MCP_CONFIRM_TTL_SECONDS="${raw}" is not a positive whole number of seconds; gated writes will refuse on clients that cannot show a confirmation prompt until it is fixed.
|
|
35033
|
+
`);
|
|
35034
|
+
}
|
|
35035
|
+
return void 0;
|
|
34926
35036
|
}
|
|
34927
35037
|
var processKey;
|
|
34928
35038
|
function confirmKeyFromEnv(env = process.env) {
|
|
@@ -34934,15 +35044,33 @@ function confirmKeyFromEnv(env = process.env) {
|
|
|
34934
35044
|
}
|
|
34935
35045
|
var CONFIRM_TOKEN_AUTO_INSTRUCTION = "Nothing has been done yet. Review this preview; if it is what was intended, call again with the same arguments plus confirmToken. (This server runs with MCP_CONFIRM_MODE=auto, so the user's approval in chat is not required.)";
|
|
34936
35046
|
var REFUSE_HINT = "Set MCP_CONFIRM_MODE=ask-user on the server to allow two-step confirmation instead.";
|
|
35047
|
+
var BAD_TTL_HINT = "MCP_CONFIRM_TTL_SECONDS on the server is not a positive whole number of seconds; fix it to allow two-step confirmation.";
|
|
35048
|
+
function boundArgs(args) {
|
|
35049
|
+
if (args === null || typeof args !== "object" || Array.isArray(args) || !("confirmToken" in args))
|
|
35050
|
+
return args;
|
|
35051
|
+
const { confirmToken, ...rest } = args;
|
|
35052
|
+
void confirmToken;
|
|
35053
|
+
return rest;
|
|
35054
|
+
}
|
|
34937
35055
|
function confirmationFromEnv(options) {
|
|
34938
|
-
const { tool, account, confirmToken, subject, instruction, spent, env = process.env, ...
|
|
35056
|
+
const { tool, account, confirmToken, subject, instruction, spent, args, env = process.env, ...rest } = options;
|
|
34939
35057
|
const mode = readConfirmMode(env);
|
|
34940
|
-
|
|
35058
|
+
const ttlSeconds = readConfirmTtl(env);
|
|
35059
|
+
const bound = args === void 0 ? void 0 : boundArgs(args);
|
|
35060
|
+
const confirmation = bound === void 0 || rest.binding ? rest : { ...rest, binding: { key: confirmKeyFromEnv(env), args: bound, ttlSeconds: ttlSeconds ?? DEFAULT_TTL_SECONDS2 } };
|
|
35061
|
+
if (mode === "refuse" || ttlSeconds === void 0) {
|
|
35062
|
+
const hint = mode === "refuse" ? REFUSE_HINT : BAD_TTL_HINT;
|
|
34941
35063
|
return {
|
|
34942
35064
|
...confirmation,
|
|
34943
|
-
unsupportedNote: confirmation.unsupportedNote ? `${confirmation.unsupportedNote} ${
|
|
35065
|
+
unsupportedNote: confirmation.unsupportedNote ? `${confirmation.unsupportedNote} ${hint}` : hint
|
|
34944
35066
|
};
|
|
34945
35067
|
}
|
|
35068
|
+
const boundSubject = bound === void 0 ? subject : async () => {
|
|
35069
|
+
const read = await subject();
|
|
35070
|
+
if ("content" in read)
|
|
35071
|
+
return read;
|
|
35072
|
+
return { ...read, payload: { payload: read.payload, args: bound } };
|
|
35073
|
+
};
|
|
34946
35074
|
return {
|
|
34947
35075
|
...confirmation,
|
|
34948
35076
|
tokenFallback: {
|
|
@@ -34950,8 +35078,8 @@ function confirmationFromEnv(options) {
|
|
|
34950
35078
|
tool,
|
|
34951
35079
|
...account === void 0 ? {} : { account },
|
|
34952
35080
|
...confirmToken === void 0 ? {} : { confirmToken },
|
|
34953
|
-
subject,
|
|
34954
|
-
ttlSeconds
|
|
35081
|
+
subject: boundSubject,
|
|
35082
|
+
ttlSeconds,
|
|
34955
35083
|
instruction: mode === "auto" ? CONFIRM_TOKEN_AUTO_INSTRUCTION : instruction ?? CONFIRM_TOKEN_INSTRUCTION,
|
|
34956
35084
|
...spent === void 0 ? {} : { spent }
|
|
34957
35085
|
}
|
|
@@ -35075,6 +35203,8 @@ function withGracefulShutdown(target, opts = {}) {
|
|
|
35075
35203
|
};
|
|
35076
35204
|
const handler = (signal) => {
|
|
35077
35205
|
if (shuttingDown) {
|
|
35206
|
+
if (signal === "stdin-eof")
|
|
35207
|
+
return;
|
|
35078
35208
|
if (Date.now() - firstSignalAt < repeatGraceMs)
|
|
35079
35209
|
return;
|
|
35080
35210
|
if (shouldExit) {
|
|
@@ -35110,6 +35240,10 @@ function withGracefulShutdown(target, opts = {}) {
|
|
|
35110
35240
|
};
|
|
35111
35241
|
process.on("SIGINT", () => handler("SIGINT"));
|
|
35112
35242
|
process.on("SIGTERM", () => handler("SIGTERM"));
|
|
35243
|
+
if (opts.stdin) {
|
|
35244
|
+
opts.stdin.once("end", () => handler("stdin-eof"));
|
|
35245
|
+
opts.stdin.once("close", () => handler("stdin-eof"));
|
|
35246
|
+
}
|
|
35113
35247
|
}
|
|
35114
35248
|
function runMcp(opts) {
|
|
35115
35249
|
if (opts.banner !== void 0) {
|
|
@@ -35128,7 +35262,8 @@ function runMcp(opts) {
|
|
|
35128
35262
|
});
|
|
35129
35263
|
const shutdown = opts.shutdown ?? true;
|
|
35130
35264
|
if (shutdown !== false) {
|
|
35131
|
-
|
|
35265
|
+
const stdinDefault = spec === "stdio" ? { stdin: process.stdin } : {};
|
|
35266
|
+
withGracefulShutdown(handle, { ...stdinDefault, ...shutdown === true ? {} : shutdown });
|
|
35132
35267
|
}
|
|
35133
35268
|
return handle;
|
|
35134
35269
|
}
|
|
@@ -35165,7 +35300,7 @@ function toolAnnotations(opts = {}) {
|
|
|
35165
35300
|
}
|
|
35166
35301
|
|
|
35167
35302
|
// src/version.ts
|
|
35168
|
-
var VERSION = "2.
|
|
35303
|
+
var VERSION = "2.4.0";
|
|
35169
35304
|
|
|
35170
35305
|
// src/client.ts
|
|
35171
35306
|
import { createHash as createHash4 } from "crypto";
|
|
@@ -35374,6 +35509,7 @@ var REFRESH = `
|
|
|
35374
35509
|
`;
|
|
35375
35510
|
var AUTH_ERROR_CODES = /* @__PURE__ */ new Set(["UNAUTHORIZED", "UNAUTHENTICATED"]);
|
|
35376
35511
|
var PERMISSION_ERROR_CODES = /* @__PURE__ */ new Set(["FORBIDDEN"]);
|
|
35512
|
+
var PERMISSION_MESSAGE_PATTERN = /\bnot allowed for user\b/i;
|
|
35377
35513
|
var AUTH_MESSAGE_PATTERN = /not authoriz|unauthoriz|unauthenticated|invalid token|token (has )?expired|expired token|jwt (expired|malformed)|try to log ?in/i;
|
|
35378
35514
|
function requestSignal() {
|
|
35379
35515
|
return withAmbientCancellation(AbortSignal.timeout(REQUEST_TIMEOUT_MS));
|
|
@@ -35654,7 +35790,11 @@ var ViboClient = class {
|
|
|
35654
35790
|
/** Signed in, but not allowed to do this (HTTP 403 / FORBIDDEN). */
|
|
35655
35791
|
isPermissionError(status, errors) {
|
|
35656
35792
|
if (status === 403) return true;
|
|
35657
|
-
return (errors ?? []).some((e) =>
|
|
35793
|
+
return (errors ?? []).some((e) => {
|
|
35794
|
+
const code = e.code ?? e.extensions?.code ?? "";
|
|
35795
|
+
if (PERMISSION_ERROR_CODES.has(code)) return true;
|
|
35796
|
+
return !AUTH_ERROR_CODES.has(code) && PERMISSION_MESSAGE_PATTERN.test(e.message ?? "");
|
|
35797
|
+
});
|
|
35658
35798
|
}
|
|
35659
35799
|
unwrap(status, body) {
|
|
35660
35800
|
if (this.isPermissionError(status, body.errors)) {
|
|
@@ -35766,6 +35906,39 @@ var LIST_SECTIONS = `
|
|
|
35766
35906
|
}
|
|
35767
35907
|
}
|
|
35768
35908
|
`;
|
|
35909
|
+
var SECTIONS_FOR_WRITE = `
|
|
35910
|
+
query sectionsForWrite($eventId: ID!) {
|
|
35911
|
+
sections(eventId: $eventId) {
|
|
35912
|
+
_id name type songsCount questionsCount answeredCount canRemove visibility
|
|
35913
|
+
settings { canHostsOrderSongs canHostDeleteSection }
|
|
35914
|
+
}
|
|
35915
|
+
}
|
|
35916
|
+
`;
|
|
35917
|
+
var EVENT_PERMISSIONS = `
|
|
35918
|
+
query eventPermissions($eventId: ID!) {
|
|
35919
|
+
event(eventId: $eventId) {
|
|
35920
|
+
_id role isLocked
|
|
35921
|
+
settings { canHostCreateSections canHostReorderSections sectionSongsLimit sectionMustPlayLimit }
|
|
35922
|
+
}
|
|
35923
|
+
}
|
|
35924
|
+
`;
|
|
35925
|
+
var CREATE_SECTION = `
|
|
35926
|
+
mutation createSection($eventId: ID!, $payload: CreateSectionInput!) {
|
|
35927
|
+
createSection(eventId: $eventId, payload: $payload) {
|
|
35928
|
+
_id name type time description visibility
|
|
35929
|
+
}
|
|
35930
|
+
}
|
|
35931
|
+
`;
|
|
35932
|
+
var REMOVE_SECTION = `
|
|
35933
|
+
mutation removeSection($eventId: ID!, $sectionId: ID!) {
|
|
35934
|
+
removeSection(eventId: $eventId, sectionId: $sectionId)
|
|
35935
|
+
}
|
|
35936
|
+
`;
|
|
35937
|
+
var REORDER_SECTIONS = `
|
|
35938
|
+
mutation reorderSections($eventId: ID!, $sourceSectionId: ID!, $targetSectionId: ID) {
|
|
35939
|
+
reorderSections(eventId: $eventId, sourceSectionId: $sourceSectionId, targetSectionId: $targetSectionId)
|
|
35940
|
+
}
|
|
35941
|
+
`;
|
|
35769
35942
|
var GET_SECTION_SONGS = `
|
|
35770
35943
|
query getSectionSongs($eventId: ID!, $sectionId: ID!, $filter: SectionSongsFilter, $pagination: PaginationInput, $sort: SongsSortInput) {
|
|
35771
35944
|
getSectionSongs(eventId: $eventId, sectionId: $sectionId, filter: $filter, pagination: $pagination, sort: $sort) {
|
|
@@ -35781,6 +35954,15 @@ var GET_SECTION_SONGS = `
|
|
|
35781
35954
|
}
|
|
35782
35955
|
}
|
|
35783
35956
|
`;
|
|
35957
|
+
var SECTION_SONG_IDS = `
|
|
35958
|
+
query sectionSongIds($eventId: ID!, $sectionId: ID!, $pagination: PaginationInput) {
|
|
35959
|
+
getSectionSongs(eventId: $eventId, sectionId: $sectionId, pagination: $pagination) {
|
|
35960
|
+
songs { _id viboSongId artist title }
|
|
35961
|
+
next { skip limit }
|
|
35962
|
+
totalCount
|
|
35963
|
+
}
|
|
35964
|
+
}
|
|
35965
|
+
`;
|
|
35784
35966
|
var SEARCH_SONGS = `
|
|
35785
35967
|
query getSongs($eventId: ID!, $sectionId: ID!, $filter: SongsFilter!, $limit: Int!) {
|
|
35786
35968
|
getSongs(eventId: $eventId, sectionId: $sectionId, filter: $filter, limit: $limit) {
|
|
@@ -36047,7 +36229,11 @@ var inlineFileSchema = external_exports.object({
|
|
|
36047
36229
|
});
|
|
36048
36230
|
var CONFIRM_NOTE = "Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call returns a preview and a confirmToken, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE).";
|
|
36049
36231
|
function confirmWrite(ctx, options) {
|
|
36050
|
-
const preview = {
|
|
36232
|
+
const preview = {
|
|
36233
|
+
action: options.mutation,
|
|
36234
|
+
willSend: options.willSend,
|
|
36235
|
+
...options.context ? { context: options.context } : {}
|
|
36236
|
+
};
|
|
36051
36237
|
return requireConfirmationWithFallback(
|
|
36052
36238
|
ctx,
|
|
36053
36239
|
confirmationFromEnv({
|
|
@@ -36058,7 +36244,7 @@ function confirmWrite(ctx, options) {
|
|
|
36058
36244
|
confirmToken: options.confirmToken,
|
|
36059
36245
|
subject: () => ({
|
|
36060
36246
|
target: options.target,
|
|
36061
|
-
payload: options.payload ?? options.willSend,
|
|
36247
|
+
payload: options.payload ?? (options.context ? { willSend: options.willSend, context: options.context } : options.willSend),
|
|
36062
36248
|
preview
|
|
36063
36249
|
})
|
|
36064
36250
|
})
|
|
@@ -36345,7 +36531,61 @@ function annotateSearchResults(results, query, source = "searchField") {
|
|
|
36345
36531
|
return { query: parsed, ...hint ? { hint } : {}, summary, results: annotated };
|
|
36346
36532
|
}
|
|
36347
36533
|
|
|
36534
|
+
// src/tools/lookups.ts
|
|
36535
|
+
async function fetchSections(client2, eventId) {
|
|
36536
|
+
const data = await client2.gql(SECTIONS_FOR_WRITE, { eventId });
|
|
36537
|
+
return data.sections ?? [];
|
|
36538
|
+
}
|
|
36539
|
+
async function fetchEventPermissions(client2, eventId) {
|
|
36540
|
+
const data = await client2.gql(EVENT_PERMISSIONS, { eventId });
|
|
36541
|
+
return data.event;
|
|
36542
|
+
}
|
|
36543
|
+
function findSection(sections, sectionId) {
|
|
36544
|
+
const section = sections.find((s) => s._id === sectionId);
|
|
36545
|
+
if (!section) {
|
|
36546
|
+
throw new McpToolError(`Section ${sectionId} is not in this event's timeline.`, {
|
|
36547
|
+
hint: "Get section ids from vibo_list_sections for the same eventId."
|
|
36548
|
+
});
|
|
36549
|
+
}
|
|
36550
|
+
return section;
|
|
36551
|
+
}
|
|
36552
|
+
async function fetchSectionSongs(client2, eventId, sectionId) {
|
|
36553
|
+
const songs = [];
|
|
36554
|
+
const limit = 100;
|
|
36555
|
+
for (let page = 0; page < 50; page++) {
|
|
36556
|
+
const data = await client2.gql(
|
|
36557
|
+
SECTION_SONG_IDS,
|
|
36558
|
+
{ eventId, sectionId, pagination: { skip: songs.length, limit } }
|
|
36559
|
+
);
|
|
36560
|
+
const batch = data.getSectionSongs?.songs ?? [];
|
|
36561
|
+
songs.push(...batch);
|
|
36562
|
+
const total = data.getSectionSongs?.totalCount;
|
|
36563
|
+
if (batch.length < limit || typeof total === "number" && songs.length >= total) break;
|
|
36564
|
+
}
|
|
36565
|
+
return songs;
|
|
36566
|
+
}
|
|
36567
|
+
function assertSongsInSection(songIds, songs) {
|
|
36568
|
+
const have = new Set(songs.map((s) => s._id));
|
|
36569
|
+
const missing = songIds.filter((id) => !have.has(id));
|
|
36570
|
+
if (missing.length === 0) return;
|
|
36571
|
+
const hints = missing.map((id) => {
|
|
36572
|
+
const byVibo = songs.find((s) => s.viboSongId === id);
|
|
36573
|
+
return byVibo ? `${id} is a viboSongId \u2014 its section-song _id is ${byVibo._id}` : null;
|
|
36574
|
+
}).filter(Boolean);
|
|
36575
|
+
throw new McpToolError(
|
|
36576
|
+
`${missing.length} of ${songIds.length} song id(s) are not in this section: ${missing.join(", ")}. Nothing was sent.`,
|
|
36577
|
+
{
|
|
36578
|
+
hint: (hints.length ? hints.join("; ") + ". " : "") + "Use the song _id values from vibo_get_section_songs for this same section."
|
|
36579
|
+
}
|
|
36580
|
+
);
|
|
36581
|
+
}
|
|
36582
|
+
function isHost(perms) {
|
|
36583
|
+
return perms.role === "host";
|
|
36584
|
+
}
|
|
36585
|
+
|
|
36348
36586
|
// src/tools/songs.ts
|
|
36587
|
+
var addVerifyDelaysMs = [0, 700, 1500];
|
|
36588
|
+
var sleep2 = (ms) => ms > 0 ? new Promise((r) => setTimeout(r, ms)) : Promise.resolve();
|
|
36349
36589
|
function registerSongTools(server, client2) {
|
|
36350
36590
|
server.registerTool(
|
|
36351
36591
|
"vibo_get_section_songs",
|
|
@@ -36413,7 +36653,7 @@ function registerSongTools(server, client2) {
|
|
|
36413
36653
|
server.registerTool(
|
|
36414
36654
|
"vibo_add_song_to_section",
|
|
36415
36655
|
{
|
|
36416
|
-
description: "Add a song to a section. Pass a song from vibo_search_songs (songUrl is required; include viboSongId/title/artist when known). Before adding, check that result's `quality.confidence`: adding a `likely-not-original` result puts a cover, karaoke track or junk-metadata re-upload in front of a live DJ. If nothing looks original, report the closest matches back rather than adding a best guess. " + CONFIRM_NOTE,
|
|
36656
|
+
description: "Add a song to a section. Pass a song from vibo_search_songs (songUrl is required; include viboSongId/title/artist when known). Before adding, check that result's `quality.confidence`: adding a `likely-not-original` result puts a cover, karaoke track or junk-metadata re-upload in front of a live DJ. If nothing looks original, report the closest matches back rather than adding a best guess. After adding, re-reads the section and reports an error if the song is not actually there (Vibo has answered added:true without adding). " + CONFIRM_NOTE,
|
|
36417
36657
|
annotations: toolAnnotations({ title: "Add song to Vibo section", readOnly: false, destructive: false }),
|
|
36418
36658
|
inputSchema: external_exports.object({
|
|
36419
36659
|
eventId: external_exports.string().describe("Event id."),
|
|
@@ -36445,7 +36685,35 @@ function registerSongTools(server, client2) {
|
|
|
36445
36685
|
sectionId,
|
|
36446
36686
|
payload
|
|
36447
36687
|
});
|
|
36448
|
-
|
|
36688
|
+
const res = data.addSongToSection ?? {};
|
|
36689
|
+
if (res.added === false) {
|
|
36690
|
+
throw new McpToolError("Vibo did not add the song (added: false).", {
|
|
36691
|
+
hint: "It may already be in this section, be on the do-not-play list, or the section may be full. Check with vibo_get_section_songs."
|
|
36692
|
+
});
|
|
36693
|
+
}
|
|
36694
|
+
if (!res.songId && !viboSongId) {
|
|
36695
|
+
return minifiedResult({
|
|
36696
|
+
...res,
|
|
36697
|
+
verified: false,
|
|
36698
|
+
note: "Vibo returned no songId and no viboSongId was passed, so the add could not be checked by re-reading. Check with vibo_get_section_songs before retrying."
|
|
36699
|
+
});
|
|
36700
|
+
}
|
|
36701
|
+
const isOurs = (s) => !!res.songId && s._id === res.songId || !!viboSongId && s.viboSongId === viboSongId;
|
|
36702
|
+
let found;
|
|
36703
|
+
for (const delay of addVerifyDelaysMs) {
|
|
36704
|
+
await sleep2(delay);
|
|
36705
|
+
found = (await fetchSectionSongs(client2, eventId, sectionId)).find(isOurs);
|
|
36706
|
+
if (found) break;
|
|
36707
|
+
}
|
|
36708
|
+
if (!found) {
|
|
36709
|
+
throw new McpToolError(
|
|
36710
|
+
"Vibo answered added:true, but the song is not in the section when re-read, so it was NOT added.",
|
|
36711
|
+
{
|
|
36712
|
+
hint: "Retry once; if it still fails, add it in the Vibo app. Details: " + JSON.stringify({ songId: res.songId ?? null, viboSongId: viboSongId ?? null, totalCount: res.totalCount })
|
|
36713
|
+
}
|
|
36714
|
+
);
|
|
36715
|
+
}
|
|
36716
|
+
return minifiedResult({ ...res, verified: true, song: found });
|
|
36449
36717
|
}
|
|
36450
36718
|
);
|
|
36451
36719
|
server.registerTool(
|
|
@@ -36861,12 +37129,35 @@ function registerQuestionTools(server, client2, resolveUpload = nodeUploadResolv
|
|
|
36861
37129
|
);
|
|
36862
37130
|
}
|
|
36863
37131
|
|
|
37132
|
+
// src/reorder.ts
|
|
37133
|
+
function planMoves(order, sources, after) {
|
|
37134
|
+
let current = [...order];
|
|
37135
|
+
let anchor2 = after;
|
|
37136
|
+
const moves = [];
|
|
37137
|
+
for (const source of sources) {
|
|
37138
|
+
const from = current.indexOf(source);
|
|
37139
|
+
const rest = current.filter((id) => id !== source);
|
|
37140
|
+
const to = anchor2 === null ? 0 : rest.indexOf(anchor2) + 1;
|
|
37141
|
+
if (to !== from) {
|
|
37142
|
+
moves.push({ source, target: anchor2 });
|
|
37143
|
+
current = [...rest.slice(0, to), source, ...rest.slice(to)];
|
|
37144
|
+
}
|
|
37145
|
+
anchor2 = source;
|
|
37146
|
+
}
|
|
37147
|
+
return { moves, finalOrder: current };
|
|
37148
|
+
}
|
|
37149
|
+
function missingIds(wanted, have) {
|
|
37150
|
+
const set2 = new Set(have);
|
|
37151
|
+
return wanted.filter((id) => !set2.has(id));
|
|
37152
|
+
}
|
|
37153
|
+
|
|
36864
37154
|
// src/tools/song-management.ts
|
|
37155
|
+
var SONG_COMMENT_MAX = 90;
|
|
36865
37156
|
function registerSongManagementTools(server, client2) {
|
|
36866
37157
|
server.registerTool(
|
|
36867
37158
|
"vibo_remove_song_from_section",
|
|
36868
37159
|
{
|
|
36869
|
-
description: "Remove one or more songs from a section. " + CONFIRM_NOTE,
|
|
37160
|
+
description: "Remove one or more songs from a section. Every id is checked against the section first; if any is not there, nothing is sent and the missing ids are listed. " + CONFIRM_NOTE,
|
|
36870
37161
|
annotations: toolAnnotations({ title: "Remove songs from Vibo section", readOnly: false, destructive: true }),
|
|
36871
37162
|
inputSchema: external_exports.object({
|
|
36872
37163
|
eventId: external_exports.string().describe("Event id."),
|
|
@@ -36876,6 +37167,8 @@ function registerSongManagementTools(server, client2) {
|
|
|
36876
37167
|
})
|
|
36877
37168
|
},
|
|
36878
37169
|
async ({ eventId, sectionId, songIds, confirmToken }, ctx) => {
|
|
37170
|
+
const songs = await fetchSectionSongs(client2, eventId, sectionId);
|
|
37171
|
+
assertSongsInSection(songIds, songs);
|
|
36879
37172
|
const vars = { eventId, sectionId, songIds };
|
|
36880
37173
|
const gate = await confirmWrite(ctx, {
|
|
36881
37174
|
tool: "vibo_remove_song_from_section",
|
|
@@ -36883,7 +37176,13 @@ function registerSongManagementTools(server, client2) {
|
|
|
36883
37176
|
message: "Review and confirm removing these songs:",
|
|
36884
37177
|
confirmToken,
|
|
36885
37178
|
target: sectionId,
|
|
36886
|
-
willSend: vars
|
|
37179
|
+
willSend: vars,
|
|
37180
|
+
context: {
|
|
37181
|
+
songs: songIds.map((id) => {
|
|
37182
|
+
const s = songs.find((x) => x._id === id);
|
|
37183
|
+
return `${s.title ?? "?"} \u2014 ${s.artist ?? "?"}`;
|
|
37184
|
+
})
|
|
37185
|
+
}
|
|
36887
37186
|
});
|
|
36888
37187
|
if (gate) return gate;
|
|
36889
37188
|
const data = await client2.gql(REMOVE_SECTION_SONGS, vars);
|
|
@@ -36893,7 +37192,7 @@ function registerSongManagementTools(server, client2) {
|
|
|
36893
37192
|
server.registerTool(
|
|
36894
37193
|
"vibo_update_song",
|
|
36895
37194
|
{
|
|
36896
|
-
description:
|
|
37195
|
+
description: `Update songs in a section: mark must-play, flag as do-not-play, and/or set a comment. Provide at least one field. A comment can be at most ${SONG_COMMENT_MAX} characters (Vibo's limit; emoji count double; checked before anything is sent). ` + CONFIRM_NOTE,
|
|
36897
37196
|
annotations: toolAnnotations({ title: "Update Vibo section songs", readOnly: false, destructive: false }),
|
|
36898
37197
|
inputSchema: external_exports.object({
|
|
36899
37198
|
eventId: external_exports.string().describe("Event id."),
|
|
@@ -36901,7 +37200,7 @@ function registerSongManagementTools(server, client2) {
|
|
|
36901
37200
|
songIds: external_exports.array(external_exports.string()).min(1).describe("Song _ids from vibo_get_section_songs."),
|
|
36902
37201
|
isMustPlay: external_exports.boolean().optional(),
|
|
36903
37202
|
isFlagged: external_exports.boolean().optional().describe("mark do-not-play / flagged"),
|
|
36904
|
-
comment: external_exports.string().optional(),
|
|
37203
|
+
comment: external_exports.string().optional().describe(`Comment for the DJ, at most ${SONG_COMMENT_MAX} characters.`),
|
|
36905
37204
|
confirmToken: confirmTokenParam
|
|
36906
37205
|
})
|
|
36907
37206
|
},
|
|
@@ -36915,6 +37214,12 @@ function registerSongManagementTools(server, client2) {
|
|
|
36915
37214
|
hint: "Pass isMustPlay, isFlagged, and/or comment to update the songs."
|
|
36916
37215
|
});
|
|
36917
37216
|
}
|
|
37217
|
+
if (comment !== void 0 && comment.length > SONG_COMMENT_MAX) {
|
|
37218
|
+
throw new McpToolError(
|
|
37219
|
+
`Comment is ${comment.length} characters; Vibo allows at most ${SONG_COMMENT_MAX}. Nothing was sent.`,
|
|
37220
|
+
{ hint: `Shorten it to ${SONG_COMMENT_MAX} characters or fewer, or put the longer text in a section note.` }
|
|
37221
|
+
);
|
|
37222
|
+
}
|
|
36918
37223
|
const vars = { eventId, sectionId, songIds, payload };
|
|
36919
37224
|
const gate = await confirmWrite(ctx, {
|
|
36920
37225
|
tool: "vibo_update_song",
|
|
@@ -36960,29 +37265,58 @@ function registerSongManagementTools(server, client2) {
|
|
|
36960
37265
|
server.registerTool(
|
|
36961
37266
|
"vibo_reorder_songs",
|
|
36962
37267
|
{
|
|
36963
|
-
description: "Reorder songs within a section. " + CONFIRM_NOTE,
|
|
37268
|
+
description: "Reorder songs within a section: move sourceSongIds (in the given order) to directly after targetSongId, or to the top when it is omitted. Checks the ids against the section and, for a host, that the DJ allows hosts to order this section's songs. That setting is OFF for every section a host creates and only the DJ can turn it on, so a host usually cannot reorder songs in sections they added. Sends one reorderSongsBatch call per song that actually moves. " + CONFIRM_NOTE,
|
|
36964
37269
|
annotations: toolAnnotations({ title: "Reorder Vibo section songs", readOnly: false, destructive: false }),
|
|
36965
37270
|
inputSchema: external_exports.object({
|
|
36966
37271
|
eventId: external_exports.string().describe("Event id."),
|
|
36967
37272
|
sectionId: external_exports.string().describe("Section id."),
|
|
36968
|
-
sourceSongIds: external_exports.array(external_exports.string()).min(1).describe("Song _ids from vibo_get_section_songs."),
|
|
36969
|
-
targetSongId: external_exports.string().optional().describe("place the moved songs after this song _id; omit for
|
|
37273
|
+
sourceSongIds: external_exports.array(external_exports.string()).min(1).describe("Song _ids from vibo_get_section_songs, in the order they should end up."),
|
|
37274
|
+
targetSongId: external_exports.string().optional().describe("place the moved songs directly after this song _id; omit for the top"),
|
|
36970
37275
|
confirmToken: confirmTokenParam
|
|
36971
37276
|
})
|
|
36972
37277
|
},
|
|
36973
37278
|
async ({ eventId, sectionId, sourceSongIds, targetSongId, confirmToken }, ctx) => {
|
|
36974
|
-
|
|
37279
|
+
if (new Set(sourceSongIds).size !== sourceSongIds.length) {
|
|
37280
|
+
throw new McpToolError("sourceSongIds contains a duplicate.", { hint: "List each song once." });
|
|
37281
|
+
}
|
|
37282
|
+
if (targetSongId !== void 0 && sourceSongIds.includes(targetSongId)) {
|
|
37283
|
+
throw new McpToolError("targetSongId is also one of the songs being moved.", {
|
|
37284
|
+
hint: "Pick a target outside sourceSongIds."
|
|
37285
|
+
});
|
|
37286
|
+
}
|
|
37287
|
+
const [perms, sections, songs] = await Promise.all([
|
|
37288
|
+
fetchEventPermissions(client2, eventId),
|
|
37289
|
+
fetchSections(client2, eventId),
|
|
37290
|
+
fetchSectionSongs(client2, eventId, sectionId)
|
|
37291
|
+
]);
|
|
37292
|
+
const section = findSection(sections, sectionId);
|
|
37293
|
+
if (isHost(perms) && section.settings?.canHostsOrderSongs === false) {
|
|
37294
|
+
throw new McpToolError(
|
|
37295
|
+
`The DJ has turned off host song ordering for "${section.name}", so Vibo refuses reorders from hosts there. Nothing was sent.`,
|
|
37296
|
+
{
|
|
37297
|
+
hint: "It's the section's canHostsOrderSongs setting. Vibo turns it off for sections a host creates, and only the DJ can turn it on (a host update is ignored). Ask the DJ, or leave a comment on the song instead."
|
|
37298
|
+
}
|
|
37299
|
+
);
|
|
37300
|
+
}
|
|
37301
|
+
assertSongsInSection([...sourceSongIds, ...targetSongId ? [targetSongId] : []], songs);
|
|
37302
|
+
const { moves } = planMoves(songs.map((s) => s._id), sourceSongIds, targetSongId ?? null);
|
|
37303
|
+
if (moves.length === 0) {
|
|
37304
|
+
return minifiedResult({ changed: false, message: "Those songs are already in that order." });
|
|
37305
|
+
}
|
|
37306
|
+
const calls = moves.map((m) => ({ sourceSongIds: [m.source], targetSongId: m.target }));
|
|
36975
37307
|
const gate = await confirmWrite(ctx, {
|
|
36976
37308
|
tool: "vibo_reorder_songs",
|
|
36977
37309
|
mutation: "reorderSongsBatch",
|
|
36978
37310
|
message: "Review and confirm this reorder:",
|
|
36979
37311
|
confirmToken,
|
|
36980
37312
|
target: sectionId,
|
|
36981
|
-
willSend:
|
|
37313
|
+
willSend: { eventId, sectionId, calls }
|
|
36982
37314
|
});
|
|
36983
37315
|
if (gate) return gate;
|
|
36984
|
-
const
|
|
36985
|
-
|
|
37316
|
+
for (const c of calls) {
|
|
37317
|
+
await client2.gql(REORDER_SONGS, { eventId, sectionId, ...c });
|
|
37318
|
+
}
|
|
37319
|
+
return minifiedResult({ changed: true, callsSent: calls.length });
|
|
36986
37320
|
}
|
|
36987
37321
|
);
|
|
36988
37322
|
}
|
|
@@ -37369,6 +37703,264 @@ function registerSectionEditTools(server, client2) {
|
|
|
37369
37703
|
);
|
|
37370
37704
|
}
|
|
37371
37705
|
|
|
37706
|
+
// src/tools/section-manage.ts
|
|
37707
|
+
var SECTION_NAME_MAX = 45;
|
|
37708
|
+
var DEFAULT_SECTION_SETTINGS = {
|
|
37709
|
+
timeEnabled: true,
|
|
37710
|
+
songsEnabled: true,
|
|
37711
|
+
notesEnabled: true,
|
|
37712
|
+
canHostsOrderSongs: true,
|
|
37713
|
+
canHostDeleteSection: true,
|
|
37714
|
+
notesVisibleForHosts: true,
|
|
37715
|
+
canHostChangeSectionName: true,
|
|
37716
|
+
canHostChangeSectionTime: true
|
|
37717
|
+
};
|
|
37718
|
+
var VISIBILITY_SETTINGS = {
|
|
37719
|
+
host: { visibleForGuests: false, visibleForHosts: true },
|
|
37720
|
+
// "Me and DJ"
|
|
37721
|
+
public: { visibleForGuests: true, visibleForHosts: true }
|
|
37722
|
+
// "Guests"
|
|
37723
|
+
};
|
|
37724
|
+
var PROTECTED_TYPES = /* @__PURE__ */ new Set(["dontPlay", "headline"]);
|
|
37725
|
+
var TIME_PATTERN = /^(0?[1-9]|1[0-2]):([0-5]\d)\s*([ap])\.?m\.?$/i;
|
|
37726
|
+
function normalizeTime(time3) {
|
|
37727
|
+
const m = TIME_PATTERN.exec(time3.trim());
|
|
37728
|
+
if (!m) return void 0;
|
|
37729
|
+
return `${m[1].padStart(2, "0")}:${m[2]} ${m[3].toLowerCase()}m`;
|
|
37730
|
+
}
|
|
37731
|
+
function assertCanWriteTimeline(perms, flag, what) {
|
|
37732
|
+
if (!isHost(perms)) return;
|
|
37733
|
+
if (perms.isLocked) {
|
|
37734
|
+
throw new McpToolError(`This event is locked, so hosts can't ${what}. Nothing was sent.`, {
|
|
37735
|
+
hint: "The DJ locks an event close to the date; ask them to unlock it or make the change."
|
|
37736
|
+
});
|
|
37737
|
+
}
|
|
37738
|
+
if (perms.settings?.[flag] === false) {
|
|
37739
|
+
throw new McpToolError(`The DJ has turned off "${flag}" for this event, so hosts can't ${what}. Nothing was sent.`, {
|
|
37740
|
+
hint: "It's an event setting only the DJ can change \u2014 ask them to enable it or make the change."
|
|
37741
|
+
});
|
|
37742
|
+
}
|
|
37743
|
+
}
|
|
37744
|
+
var sectionLabel = (s) => ({ _id: s._id, name: s.name });
|
|
37745
|
+
function registerSectionManageTools(server, client2) {
|
|
37746
|
+
server.registerTool(
|
|
37747
|
+
"vibo_create_section",
|
|
37748
|
+
{
|
|
37749
|
+
description: `Create a new timeline section. Name is required, max ${SECTION_NAME_MAX} characters (Vibo's limit, checked before anything is sent). Place it with afterSectionId (directly after that section) or position (0-based index; 0 = first); with neither it goes at the end of the timeline, like the web app. visibility "host" (default) = "Me and DJ", "public" = visible to guests too. Vibo gives a section a host creates "hosts can order songs" = off, which only the DJ can turn on. Returns the new section _id. ` + CONFIRM_NOTE,
|
|
37750
|
+
annotations: toolAnnotations({ title: "Create Vibo section", readOnly: false, destructive: false }),
|
|
37751
|
+
inputSchema: external_exports.object({
|
|
37752
|
+
eventId: external_exports.string().describe("Event id."),
|
|
37753
|
+
name: external_exports.string().describe(`Section name, 1\u2013${SECTION_NAME_MAX} characters.`),
|
|
37754
|
+
visibility: external_exports.enum(["host", "public"]).optional().describe('"host" (default) = Me and DJ only; "public" = guests can see it too.'),
|
|
37755
|
+
time: external_exports.string().optional().describe('Scheduled time as "hh:mm am/pm", e.g. "05:30 pm".'),
|
|
37756
|
+
note: external_exports.string().optional().describe("Note to the DJ (set right after creating, via updateSection)."),
|
|
37757
|
+
afterSectionId: external_exports.string().optional().describe("Put the new section directly after this section _id."),
|
|
37758
|
+
position: external_exports.number().int().min(0).optional().describe("0-based timeline index for the new section; past the end appends. Use instead of afterSectionId."),
|
|
37759
|
+
confirmToken: confirmTokenParam
|
|
37760
|
+
})
|
|
37761
|
+
},
|
|
37762
|
+
async ({ eventId, name, visibility, time: time3, note, afterSectionId, position, confirmToken }, ctx) => {
|
|
37763
|
+
const trimmed = name.trim();
|
|
37764
|
+
if (!trimmed) {
|
|
37765
|
+
throw new McpToolError("Section name is empty.", { hint: "Pass a name of 1\u201345 characters." });
|
|
37766
|
+
}
|
|
37767
|
+
if (trimmed.length > SECTION_NAME_MAX) {
|
|
37768
|
+
throw new McpToolError(
|
|
37769
|
+
`Section name is ${trimmed.length} characters; Vibo allows at most ${SECTION_NAME_MAX}. Nothing was sent.`,
|
|
37770
|
+
{ hint: `Shorten the name to ${SECTION_NAME_MAX} characters or fewer.` }
|
|
37771
|
+
);
|
|
37772
|
+
}
|
|
37773
|
+
let normalizedTime;
|
|
37774
|
+
if (time3 !== void 0 && time3.trim() !== "") {
|
|
37775
|
+
normalizedTime = normalizeTime(time3);
|
|
37776
|
+
if (!normalizedTime) {
|
|
37777
|
+
throw new McpToolError(`"${time3}" is not a 12-hour time.`, { hint: 'Use "hh:mm am/pm", e.g. "05:30 pm".' });
|
|
37778
|
+
}
|
|
37779
|
+
}
|
|
37780
|
+
if (afterSectionId !== void 0 && position !== void 0) {
|
|
37781
|
+
throw new McpToolError("Pass afterSectionId or position, not both.", {
|
|
37782
|
+
hint: "afterSectionId places it after a named section; position places it at a 0-based index."
|
|
37783
|
+
});
|
|
37784
|
+
}
|
|
37785
|
+
const [perms, sections] = await Promise.all([fetchEventPermissions(client2, eventId), fetchSections(client2, eventId)]);
|
|
37786
|
+
assertCanWriteTimeline(perms, "canHostCreateSections", "add sections");
|
|
37787
|
+
let insertBefore;
|
|
37788
|
+
let placedAfter;
|
|
37789
|
+
if (afterSectionId !== void 0) {
|
|
37790
|
+
const anchor2 = findSection(sections, afterSectionId);
|
|
37791
|
+
insertBefore = sections[sections.indexOf(anchor2) + 1];
|
|
37792
|
+
placedAfter = anchor2;
|
|
37793
|
+
} else if (position !== void 0) {
|
|
37794
|
+
insertBefore = sections[position];
|
|
37795
|
+
placedAfter = position === 0 ? null : sections[Math.min(position, sections.length) - 1] ?? null;
|
|
37796
|
+
} else {
|
|
37797
|
+
placedAfter = sections[sections.length - 1] ?? null;
|
|
37798
|
+
}
|
|
37799
|
+
const payload = {
|
|
37800
|
+
type: "simple",
|
|
37801
|
+
name: trimmed,
|
|
37802
|
+
settings: {
|
|
37803
|
+
...DEFAULT_SECTION_SETTINGS,
|
|
37804
|
+
...perms.settings?.sectionSongsLimit != null ? { songsLimit: perms.settings.sectionSongsLimit } : {},
|
|
37805
|
+
...perms.settings?.sectionMustPlayLimit != null ? { mustPlayLimit: perms.settings.sectionMustPlayLimit } : {},
|
|
37806
|
+
...VISIBILITY_SETTINGS[visibility ?? "host"]
|
|
37807
|
+
}
|
|
37808
|
+
};
|
|
37809
|
+
if (normalizedTime) payload.time = normalizedTime;
|
|
37810
|
+
if (insertBefore) payload.insertBeforeSectionId = insertBefore._id;
|
|
37811
|
+
const willSend = { eventId, payload };
|
|
37812
|
+
if (note !== void 0) willSend.thenUpdateSection = { payload: { note } };
|
|
37813
|
+
const gate = await confirmWrite(ctx, {
|
|
37814
|
+
tool: "vibo_create_section",
|
|
37815
|
+
mutation: note !== void 0 ? "createSection, then updateSection (note)" : "createSection",
|
|
37816
|
+
message: `Review and confirm creating section "${trimmed}":`,
|
|
37817
|
+
confirmToken,
|
|
37818
|
+
target: eventId,
|
|
37819
|
+
willSend,
|
|
37820
|
+
context: { placedAfter: placedAfter ? sectionLabel(placedAfter) : "start of timeline" }
|
|
37821
|
+
});
|
|
37822
|
+
if (gate) return gate;
|
|
37823
|
+
const created = (await client2.gql(CREATE_SECTION, {
|
|
37824
|
+
eventId,
|
|
37825
|
+
payload
|
|
37826
|
+
})).createSection;
|
|
37827
|
+
let noteResult;
|
|
37828
|
+
if (note !== void 0) {
|
|
37829
|
+
noteResult = (await client2.gql(UPDATE_SECTION, {
|
|
37830
|
+
eventId,
|
|
37831
|
+
sectionId: created._id,
|
|
37832
|
+
payload: { note }
|
|
37833
|
+
})).updateSection?.note;
|
|
37834
|
+
}
|
|
37835
|
+
let placement = "unverified";
|
|
37836
|
+
const after = await fetchSections(client2, eventId);
|
|
37837
|
+
const order = after.map((s) => s._id);
|
|
37838
|
+
if (order.includes(created._id) && (!placedAfter || order.includes(placedAfter._id))) {
|
|
37839
|
+
const { moves } = planMoves(order, [created._id], placedAfter?._id ?? null);
|
|
37840
|
+
for (const m of moves) {
|
|
37841
|
+
await client2.gql(REORDER_SECTIONS, { eventId, sourceSectionId: m.source, targetSectionId: m.target });
|
|
37842
|
+
}
|
|
37843
|
+
placement = moves.length ? "fixed-by-reorder" : "as-requested";
|
|
37844
|
+
}
|
|
37845
|
+
return minifiedResult({
|
|
37846
|
+
...created,
|
|
37847
|
+
...note !== void 0 ? { note: noteResult ?? note } : {},
|
|
37848
|
+
placedAfter: placedAfter ? sectionLabel(placedAfter) : null,
|
|
37849
|
+
placement
|
|
37850
|
+
});
|
|
37851
|
+
}
|
|
37852
|
+
);
|
|
37853
|
+
server.registerTool(
|
|
37854
|
+
"vibo_delete_section",
|
|
37855
|
+
{
|
|
37856
|
+
description: `Delete a timeline section, with every song, answer and comment in it \u2014 this cannot be undone. The preview names the section and how many songs and answered questions it holds. Sections of type "dontPlay" (the DJ's do-not-play list) and "headline" (timeline dividers) are refused unless force:true. ` + CONFIRM_NOTE,
|
|
37857
|
+
annotations: toolAnnotations({ title: "Delete Vibo section", readOnly: false, destructive: true }),
|
|
37858
|
+
inputSchema: external_exports.object({
|
|
37859
|
+
eventId: external_exports.string().describe("Event id."),
|
|
37860
|
+
sectionId: external_exports.string().describe("Section _id from vibo_list_sections."),
|
|
37861
|
+
force: external_exports.boolean().optional().describe('Required to delete a "dontPlay" or "headline" section.'),
|
|
37862
|
+
confirmToken: confirmTokenParam
|
|
37863
|
+
})
|
|
37864
|
+
},
|
|
37865
|
+
async ({ eventId, sectionId, force, confirmToken }, ctx) => {
|
|
37866
|
+
const section = findSection(await fetchSections(client2, eventId), sectionId);
|
|
37867
|
+
if (PROTECTED_TYPES.has(section.type) && !force) {
|
|
37868
|
+
throw new McpToolError(
|
|
37869
|
+
`"${section.name}" is a ${section.type} section${section.type === "dontPlay" ? " (the DJ's do-not-play list)" : " (a timeline divider)"}; refusing to delete it. Nothing was sent.`,
|
|
37870
|
+
{ hint: "Pass force:true only if the user explicitly wants this section gone." }
|
|
37871
|
+
);
|
|
37872
|
+
}
|
|
37873
|
+
if (section.canRemove === false) {
|
|
37874
|
+
throw new McpToolError(`Vibo reports you can't delete "${section.name}". Nothing was sent.`, {
|
|
37875
|
+
hint: 'The DJ controls who may delete each section ("Who can delete section"); ask them to allow it or delete it.'
|
|
37876
|
+
});
|
|
37877
|
+
}
|
|
37878
|
+
const summary = {
|
|
37879
|
+
name: section.name,
|
|
37880
|
+
type: section.type,
|
|
37881
|
+
songsCount: section.songsCount,
|
|
37882
|
+
questionsAnswered: `${section.answeredCount}/${section.questionsCount}`
|
|
37883
|
+
};
|
|
37884
|
+
const vars = { eventId, sectionId };
|
|
37885
|
+
const gate = await confirmWrite(ctx, {
|
|
37886
|
+
tool: "vibo_delete_section",
|
|
37887
|
+
mutation: "removeSection",
|
|
37888
|
+
message: `Delete section "${section.name}"? It holds ${section.songsCount} song(s) and ${section.answeredCount} of ${section.questionsCount} answered question(s). This cannot be undone.`,
|
|
37889
|
+
confirmToken,
|
|
37890
|
+
target: sectionId,
|
|
37891
|
+
willSend: vars,
|
|
37892
|
+
context: summary
|
|
37893
|
+
});
|
|
37894
|
+
if (gate) return gate;
|
|
37895
|
+
const data = await client2.gql(REMOVE_SECTION, vars);
|
|
37896
|
+
if (data.removeSection !== true) {
|
|
37897
|
+
throw new McpToolError(`Vibo did not confirm deleting "${section.name}" (removeSection returned ${String(data.removeSection)}).`, {
|
|
37898
|
+
hint: "Re-check with vibo_list_sections before retrying."
|
|
37899
|
+
});
|
|
37900
|
+
}
|
|
37901
|
+
return minifiedResult({ deleted: true, section: { _id: sectionId, ...summary } });
|
|
37902
|
+
}
|
|
37903
|
+
);
|
|
37904
|
+
server.registerTool(
|
|
37905
|
+
"vibo_reorder_sections",
|
|
37906
|
+
{
|
|
37907
|
+
description: "Move one or more timeline sections to directly after targetSectionId (omit it to move them to the start), keeping the given order. Mirrors vibo_reorder_songs. Sends one reorderSections call per section that actually moves. " + CONFIRM_NOTE,
|
|
37908
|
+
annotations: toolAnnotations({ title: "Reorder Vibo sections", readOnly: false, destructive: false }),
|
|
37909
|
+
inputSchema: external_exports.object({
|
|
37910
|
+
eventId: external_exports.string().describe("Event id."),
|
|
37911
|
+
sourceSectionIds: external_exports.array(external_exports.string()).min(1).describe("Section _ids to move, in the order they should end up."),
|
|
37912
|
+
targetSectionId: external_exports.string().optional().describe("Place the moved sections right after this section _id; omit for the start."),
|
|
37913
|
+
confirmToken: confirmTokenParam
|
|
37914
|
+
})
|
|
37915
|
+
},
|
|
37916
|
+
async ({ eventId, sourceSectionIds, targetSectionId, confirmToken }, ctx) => {
|
|
37917
|
+
if (new Set(sourceSectionIds).size !== sourceSectionIds.length) {
|
|
37918
|
+
throw new McpToolError("sourceSectionIds contains a duplicate.", { hint: "List each section once." });
|
|
37919
|
+
}
|
|
37920
|
+
if (targetSectionId !== void 0 && sourceSectionIds.includes(targetSectionId)) {
|
|
37921
|
+
throw new McpToolError("targetSectionId is also one of the sections being moved.", {
|
|
37922
|
+
hint: "Pick a target outside sourceSectionIds (the moved sections keep their given order)."
|
|
37923
|
+
});
|
|
37924
|
+
}
|
|
37925
|
+
const [perms, sections] = await Promise.all([fetchEventPermissions(client2, eventId), fetchSections(client2, eventId)]);
|
|
37926
|
+
const order = sections.map((s) => s._id);
|
|
37927
|
+
const missing = missingIds([...sourceSectionIds, ...targetSectionId ? [targetSectionId] : []], order);
|
|
37928
|
+
if (missing.length) {
|
|
37929
|
+
throw new McpToolError(`Not in this event's timeline: ${missing.join(", ")}. Nothing was sent.`, {
|
|
37930
|
+
hint: "Get section ids from vibo_list_sections for the same eventId."
|
|
37931
|
+
});
|
|
37932
|
+
}
|
|
37933
|
+
assertCanWriteTimeline(perms, "canHostReorderSections", "reorder sections");
|
|
37934
|
+
const byId = new Map(sections.map((s) => [s._id, s]));
|
|
37935
|
+
const { moves, finalOrder } = planMoves(order, sourceSectionIds, targetSectionId ?? null);
|
|
37936
|
+
const after = targetSectionId ? sectionLabel(byId.get(targetSectionId)) : "start of timeline";
|
|
37937
|
+
if (moves.length === 0) {
|
|
37938
|
+
return minifiedResult({ changed: false, message: "Those sections are already in that order.", after });
|
|
37939
|
+
}
|
|
37940
|
+
const calls = moves.map((m) => ({ sourceSectionId: m.source, targetSectionId: m.target }));
|
|
37941
|
+
const gate = await confirmWrite(ctx, {
|
|
37942
|
+
tool: "vibo_reorder_sections",
|
|
37943
|
+
mutation: "reorderSections",
|
|
37944
|
+
message: `Review and confirm moving ${sourceSectionIds.length} section(s):`,
|
|
37945
|
+
confirmToken,
|
|
37946
|
+
target: eventId,
|
|
37947
|
+
willSend: { eventId, calls },
|
|
37948
|
+
context: { moving: sourceSectionIds.map((id) => byId.get(id).name), after }
|
|
37949
|
+
});
|
|
37950
|
+
if (gate) return gate;
|
|
37951
|
+
for (const c of calls) {
|
|
37952
|
+
await client2.gql(REORDER_SECTIONS, { eventId, ...c });
|
|
37953
|
+
}
|
|
37954
|
+
return minifiedResult({
|
|
37955
|
+
changed: true,
|
|
37956
|
+
callsSent: calls.length,
|
|
37957
|
+
after,
|
|
37958
|
+
newPositions: sourceSectionIds.map((id) => ({ ...sectionLabel(byId.get(id)), index: finalOrder.indexOf(id) }))
|
|
37959
|
+
});
|
|
37960
|
+
}
|
|
37961
|
+
);
|
|
37962
|
+
}
|
|
37963
|
+
|
|
37372
37964
|
// src/tools/uploads.ts
|
|
37373
37965
|
function registerUploadTools(server, client2, resolveUpload = nodeUploadResolver) {
|
|
37374
37966
|
server.registerTool(
|
|
@@ -37441,8 +38033,9 @@ async function captureViboSession(deps = {}) {
|
|
|
37441
38033
|
});
|
|
37442
38034
|
} catch (err) {
|
|
37443
38035
|
const msg = err instanceof Error ? err.message : String(err);
|
|
38036
|
+
const bridgeHint = err?.hint;
|
|
37444
38037
|
throw new McpToolError(`Vibo browser token capture failed: ${msg}`, {
|
|
37445
|
-
hint: "Install the
|
|
38038
|
+
hint: typeof bridgeHint === "string" && bridgeHint ? bridgeHint : "Install the ContextMint Bridge browser extension (https://github.com/nullnet-app/contextmint-bridge/releases), sign into https://web.vibodj.com, approve the pair code, then retry.",
|
|
37446
38039
|
cause: err
|
|
37447
38040
|
});
|
|
37448
38041
|
}
|
|
@@ -37450,7 +38043,7 @@ async function captureViboSession(deps = {}) {
|
|
|
37450
38043
|
const refreshToken = session.localStorage?.["x-refresh-token"] ?? null;
|
|
37451
38044
|
if (!accessToken) {
|
|
37452
38045
|
throw new McpToolError("No Vibo token found in the signed-in browser tab.", {
|
|
37453
|
-
hint: "Make sure you are signed into https://web.vibodj.com in the browser with
|
|
38046
|
+
hint: "Make sure you are signed into https://web.vibodj.com in the browser with ContextMint Bridge, then retry."
|
|
37454
38047
|
});
|
|
37455
38048
|
}
|
|
37456
38049
|
return { accessToken, refreshToken };
|
|
@@ -37461,7 +38054,7 @@ function registerSessionTools(server, client2) {
|
|
|
37461
38054
|
server.registerTool(
|
|
37462
38055
|
"vibo_capture_session",
|
|
37463
38056
|
{
|
|
37464
|
-
description: "Capture your Vibo login from a signed-in web.vibodj.com browser tab via
|
|
38057
|
+
description: "Capture your Vibo login from a signed-in web.vibodj.com browser tab via ContextMint Bridge \u2014 for accounts that sign in with Apple/Google/Facebook (no password). Requires the ContextMint Bridge 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.",
|
|
37465
38058
|
annotations: toolAnnotations({ title: "Capture Vibo session (SSO)", readOnly: false, destructive: false })
|
|
37466
38059
|
},
|
|
37467
38060
|
async () => {
|
|
@@ -37502,13 +38095,14 @@ await runMcp({
|
|
|
37502
38095
|
registerImportTools,
|
|
37503
38096
|
registerCollaborationTools,
|
|
37504
38097
|
registerSectionEditTools,
|
|
38098
|
+
registerSectionManageTools,
|
|
37505
38099
|
registerUploadTools,
|
|
37506
38100
|
registerSessionTools
|
|
37507
38101
|
]
|
|
37508
38102
|
});
|
|
37509
38103
|
/*! Bundled license information:
|
|
37510
38104
|
|
|
37511
|
-
@modelcontextprotocol/server/dist/src-
|
|
38105
|
+
@modelcontextprotocol/server/dist/src-D-y6h4N7.mjs:
|
|
37512
38106
|
(*!
|
|
37513
38107
|
* content-type
|
|
37514
38108
|
* Copyright(c) 2015 Douglas Christopher Wilson
|