@mgsoftwarebv/mcp-server-bridge 3.5.2 → 3.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/dist/index.js +403 -96
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -97301,6 +97301,21 @@ var invoiceProducts = pgTable(
|
|
|
97301
97301
|
// Array of ProductOptionGroup (see @refront/invoice/types). Null/empty for
|
|
97302
97302
|
// plain catalog products.
|
|
97303
97303
|
options: jsonb().$type(),
|
|
97304
|
+
// Structured catalog metadata for richer package/quote composition. All
|
|
97305
|
+
// nullable/defaulted so existing products keep working. Validated at the
|
|
97306
|
+
// app/MCP layer (see @refront/invoice/types constants).
|
|
97307
|
+
// "one_time" | "monthly" | "yearly"
|
|
97308
|
+
billingType: text("billing_type"),
|
|
97309
|
+
// "website" | "addon" | "hosting" | "support" | "other"
|
|
97310
|
+
category: text(),
|
|
97311
|
+
// List of included parts shown to the customer (e.g. "Basis SEO").
|
|
97312
|
+
includedItems: jsonb("included_items").$type(),
|
|
97313
|
+
// Whether this product is an optional add-on rather than a base package.
|
|
97314
|
+
optional: boolean3().default(false).notNull(),
|
|
97315
|
+
// Optional preset/tier label, e.g. start/growth/pro or bronze/silver/gold.
|
|
97316
|
+
tier: text(),
|
|
97317
|
+
// Manual ordering hint for catalog presentation (lower shows first).
|
|
97318
|
+
sortOrder: integer2("sort_order").default(0).notNull(),
|
|
97304
97319
|
isActive: boolean3().default(true).notNull(),
|
|
97305
97320
|
usageCount: integer2("usage_count").default(0).notNull(),
|
|
97306
97321
|
lastUsedAt: timestamp("last_used_at", {
|
|
@@ -105939,7 +105954,7 @@ var TOOLS = [
|
|
|
105939
105954
|
},
|
|
105940
105955
|
{
|
|
105941
105956
|
name: "upload-ticket-attachment",
|
|
105942
|
-
description: "Attach a file (image or document) to a ticket. Provide exactly ONE source: `filePath` (absolute local path), `imageUrl` (
|
|
105957
|
+
description: "Attach a file (image or document) to a ticket. Provide exactly ONE source: `filePath` (absolute local path on the MCP runtime), `imageUrl` (URL to download), `uploadId` (from POST /mcp/attachment-upload \u2014 use for Hermes/Telegram gateway cache files over HTTP MCP), or `base64Data` (raw or data: URI, small files only). For Cursor pasted images: locate the newest workspace `assets/image-*.png` and pass its absolute path as `filePath` (stdio MCP). For Hermes cache paths like `/root/.hermes/image_cache/...` over HTTP MCP: POST bytes to `/mcp/attachment-upload` with the same API key, then pass the returned `uploadId`. Optionally set `HERMES_MEDIA_BASE_URL` so Hermes cache `filePath` values are fetched remotely. Allowed types: JPEG, PNG, GIF, WebP, PDF, DOC(X), XLS(X), PPT(X), TXT, CSV. Max 25 MB. Returns the attachment id and a 1-hour download URL.",
|
|
105943
105958
|
inputSchema: {
|
|
105944
105959
|
type: "object",
|
|
105945
105960
|
properties: {
|
|
@@ -105950,11 +105965,15 @@ var TOOLS = [
|
|
|
105950
105965
|
},
|
|
105951
105966
|
filePath: {
|
|
105952
105967
|
type: "string",
|
|
105953
|
-
description: "Absolute local path
|
|
105968
|
+
description: "Absolute local path on the MCP server. Works for Cursor workspace files with stdio MCP; Hermes gateway paths require uploadId or HERMES_MEDIA_BASE_URL."
|
|
105954
105969
|
},
|
|
105955
105970
|
imageUrl: {
|
|
105956
105971
|
type: "string",
|
|
105957
|
-
description: "Public URL to download and attach."
|
|
105972
|
+
description: "Public or gateway URL to download and attach."
|
|
105973
|
+
},
|
|
105974
|
+
uploadId: {
|
|
105975
|
+
type: "string",
|
|
105976
|
+
description: "Staged upload id from POST /mcp/attachment-upload. Preferred for original Telegram/Hermes media over HTTP MCP."
|
|
105958
105977
|
},
|
|
105959
105978
|
base64Data: {
|
|
105960
105979
|
type: "string",
|
|
@@ -106480,7 +106499,7 @@ var TOOLS = [
|
|
|
106480
106499
|
},
|
|
106481
106500
|
{
|
|
106482
106501
|
name: "get-products",
|
|
106483
|
-
description: "List catalog products used on invoices AND quotes (the shared `invoice_products` catalog). Each entry includes its ID (UUID), name, unit price, currency, unit, active/archived flag, configurable flag, and
|
|
106502
|
+
description: "List catalog products used on invoices AND quotes (the shared `invoice_products` catalog). Each entry includes its ID (UUID), name, unit price, currency, unit, active/archived flag, configurable flag, usage stats, and structured package metadata (category, billingType, tier, optional add-on flag and includedItems). Editing or archiving a catalog product never changes existing invoices/quotes \u2014 those keep an immutable line-item snapshot; catalog changes only affect documents created afterwards.",
|
|
106484
106503
|
inputSchema: {
|
|
106485
106504
|
type: "object",
|
|
106486
106505
|
properties: {
|
|
@@ -106506,7 +106525,7 @@ var TOOLS = [
|
|
|
106506
106525
|
},
|
|
106507
106526
|
{
|
|
106508
106527
|
name: "get-product-by-id",
|
|
106509
|
-
description: "Get a single catalog product by its ID (UUID), including name, unit price, currency, unit, active/archived flag, configurable flag, and
|
|
106528
|
+
description: "Get a single catalog product by its ID (UUID), including name, unit price, currency, unit, active/archived flag, configurable flag, usage stats, and structured package metadata (category, billingType, tier, optional add-on flag and includedItems).",
|
|
106510
106529
|
inputSchema: {
|
|
106511
106530
|
type: "object",
|
|
106512
106531
|
properties: {
|
|
@@ -106518,7 +106537,7 @@ var TOOLS = [
|
|
|
106518
106537
|
},
|
|
106519
106538
|
{
|
|
106520
106539
|
name: "create-product",
|
|
106521
|
-
description: "Create a catalog product for use on invoices and quotes. Stored in the shared `invoice_products` catalog. This only adds a reusable catalog entry; it does not place the product on any document. Returns the created product with its ID and normalized fields.",
|
|
106540
|
+
description: "Create a catalog product for use on invoices and quotes. Stored in the shared `invoice_products` catalog. This only adds a reusable catalog entry; it does not place the product on any document. Supports structured package metadata (billingType, category, includedItems, optional add-on flag, tier, sortOrder) so website packages, add-ons, hosting and support subscriptions can be modelled richly. Returns the created product with its ID and normalized fields.",
|
|
106522
106541
|
inputSchema: {
|
|
106523
106542
|
type: "object",
|
|
106524
106543
|
properties: {
|
|
@@ -106533,6 +106552,34 @@ var TOOLS = [
|
|
|
106533
106552
|
unit: {
|
|
106534
106553
|
type: "string",
|
|
106535
106554
|
description: "Unit label (e.g. hour, piece, month)"
|
|
106555
|
+
},
|
|
106556
|
+
billingType: {
|
|
106557
|
+
type: "string",
|
|
106558
|
+
enum: ["one_time", "monthly", "yearly"],
|
|
106559
|
+
description: "Billing cadence for this product"
|
|
106560
|
+
},
|
|
106561
|
+
category: {
|
|
106562
|
+
type: "string",
|
|
106563
|
+
enum: ["website", "addon", "hosting", "support", "other"],
|
|
106564
|
+
description: "Product category for catalog filtering"
|
|
106565
|
+
},
|
|
106566
|
+
includedItems: {
|
|
106567
|
+
type: "array",
|
|
106568
|
+
items: { type: "string" },
|
|
106569
|
+
description: "Parts included in the package (e.g. ['Basis SEO', 'Contactformulier'])"
|
|
106570
|
+
},
|
|
106571
|
+
optional: {
|
|
106572
|
+
type: "boolean",
|
|
106573
|
+
description: "Optional add-on (true) vs base package (false)"
|
|
106574
|
+
},
|
|
106575
|
+
tier: {
|
|
106576
|
+
type: "string",
|
|
106577
|
+
enum: ["start", "growth", "pro", "bronze", "silver", "gold"],
|
|
106578
|
+
description: "Optional preset/tier label"
|
|
106579
|
+
},
|
|
106580
|
+
sortOrder: {
|
|
106581
|
+
type: "number",
|
|
106582
|
+
description: "Manual ordering hint (lower shows first)"
|
|
106536
106583
|
}
|
|
106537
106584
|
},
|
|
106538
106585
|
required: ["name"]
|
|
@@ -106540,7 +106587,7 @@ var TOOLS = [
|
|
|
106540
106587
|
},
|
|
106541
106588
|
{
|
|
106542
106589
|
name: "update-product",
|
|
106543
|
-
description: "Update a catalog product's editable fields (name, description, price, currency, unit) or reactivate it (isActive: true). Only provided fields change. IMPORTANT: updates apply only to FUTURE invoices/quotes. Existing/sent/accepted/paid documents keep their immutable line-item snapshot and are never mutated. Find the product id via get-products.",
|
|
106590
|
+
description: "Update a catalog product's editable fields (name, description, price, currency, unit), its package metadata (billingType, category, includedItems, optional, tier, sortOrder), or reactivate it (isActive: true). Only provided fields change. IMPORTANT: updates apply only to FUTURE invoices/quotes. Existing/sent/accepted/paid documents keep their immutable line-item snapshot and are never mutated. Find the product id via get-products.",
|
|
106544
106591
|
inputSchema: {
|
|
106545
106592
|
type: "object",
|
|
106546
106593
|
properties: {
|
|
@@ -106557,6 +106604,34 @@ var TOOLS = [
|
|
|
106557
106604
|
isActive: {
|
|
106558
106605
|
type: "boolean",
|
|
106559
106606
|
description: "Set true to reactivate an archived product"
|
|
106607
|
+
},
|
|
106608
|
+
billingType: {
|
|
106609
|
+
type: ["string", "null"],
|
|
106610
|
+
enum: ["one_time", "monthly", "yearly", null],
|
|
106611
|
+
description: "Billing cadence; null clears it"
|
|
106612
|
+
},
|
|
106613
|
+
category: {
|
|
106614
|
+
type: ["string", "null"],
|
|
106615
|
+
enum: ["website", "addon", "hosting", "support", "other", null],
|
|
106616
|
+
description: "Product category; null clears it"
|
|
106617
|
+
},
|
|
106618
|
+
includedItems: {
|
|
106619
|
+
type: ["array", "null"],
|
|
106620
|
+
items: { type: "string" },
|
|
106621
|
+
description: "Parts included in the package; null/[] clears it"
|
|
106622
|
+
},
|
|
106623
|
+
optional: {
|
|
106624
|
+
type: "boolean",
|
|
106625
|
+
description: "Optional add-on (true) vs base package (false)"
|
|
106626
|
+
},
|
|
106627
|
+
tier: {
|
|
106628
|
+
type: ["string", "null"],
|
|
106629
|
+
enum: ["start", "growth", "pro", "bronze", "silver", "gold", null],
|
|
106630
|
+
description: "Preset/tier label; null clears it"
|
|
106631
|
+
},
|
|
106632
|
+
sortOrder: {
|
|
106633
|
+
type: "number",
|
|
106634
|
+
description: "Manual ordering hint (lower shows first)"
|
|
106560
106635
|
}
|
|
106561
106636
|
},
|
|
106562
106637
|
required: ["productId"]
|
|
@@ -114088,6 +114163,22 @@ The project had no tickets, hours, trips, or templates. Any project-scoped confi
|
|
|
114088
114163
|
|
|
114089
114164
|
// src/tools/products.ts
|
|
114090
114165
|
var PRODUCT_STATUSES = ["active", "archived", "all"];
|
|
114166
|
+
var BILLING_TYPES = ["one_time", "monthly", "yearly"];
|
|
114167
|
+
var CATEGORIES = [
|
|
114168
|
+
"website",
|
|
114169
|
+
"addon",
|
|
114170
|
+
"hosting",
|
|
114171
|
+
"support",
|
|
114172
|
+
"other"
|
|
114173
|
+
];
|
|
114174
|
+
var TIERS = [
|
|
114175
|
+
"start",
|
|
114176
|
+
"growth",
|
|
114177
|
+
"pro",
|
|
114178
|
+
"bronze",
|
|
114179
|
+
"silver",
|
|
114180
|
+
"gold"
|
|
114181
|
+
];
|
|
114091
114182
|
var PRODUCT_COLUMNS = {
|
|
114092
114183
|
id: schema_exports.invoiceProducts.id,
|
|
114093
114184
|
teamId: schema_exports.invoiceProducts.teamId,
|
|
@@ -114097,6 +114188,12 @@ var PRODUCT_COLUMNS = {
|
|
|
114097
114188
|
currency: schema_exports.invoiceProducts.currency,
|
|
114098
114189
|
unit: schema_exports.invoiceProducts.unit,
|
|
114099
114190
|
isConfigurable: schema_exports.invoiceProducts.isConfigurable,
|
|
114191
|
+
billingType: schema_exports.invoiceProducts.billingType,
|
|
114192
|
+
category: schema_exports.invoiceProducts.category,
|
|
114193
|
+
includedItems: schema_exports.invoiceProducts.includedItems,
|
|
114194
|
+
optional: schema_exports.invoiceProducts.optional,
|
|
114195
|
+
tier: schema_exports.invoiceProducts.tier,
|
|
114196
|
+
sortOrder: schema_exports.invoiceProducts.sortOrder,
|
|
114100
114197
|
isActive: schema_exports.invoiceProducts.isActive,
|
|
114101
114198
|
usageCount: schema_exports.invoiceProducts.usageCount,
|
|
114102
114199
|
lastUsedAt: schema_exports.invoiceProducts.lastUsedAt,
|
|
@@ -114113,13 +114210,34 @@ function formatPrice(p3) {
|
|
|
114113
114210
|
function formatProduct(p3) {
|
|
114114
114211
|
const flags = [p3.isActive ? "active" : "archived"];
|
|
114115
114212
|
if (p3.isConfigurable) flags.push("configurable");
|
|
114213
|
+
if (p3.optional) flags.push("add-on");
|
|
114214
|
+
const meta5 = [];
|
|
114215
|
+
if (p3.category) meta5.push(`category=${p3.category}`);
|
|
114216
|
+
if (p3.billingType) meta5.push(`billing=${p3.billingType}`);
|
|
114217
|
+
if (p3.tier) meta5.push(`tier=${p3.tier}`);
|
|
114218
|
+
const included = p3.includedItems && p3.includedItems.length > 0 ? `Included: ${p3.includedItems.join(", ")}
|
|
114219
|
+
` : "";
|
|
114116
114220
|
return `**${p3.name}** (${flags.join(", ")})
|
|
114117
114221
|
ID: ${p3.id}
|
|
114118
114222
|
Price: ${formatPrice(p3)}
|
|
114119
|
-
${
|
|
114223
|
+
${meta5.length > 0 ? `${meta5.join(", ")}
|
|
114224
|
+
` : ""}` + included + `${p3.description ? `Description: ${p3.description}
|
|
114120
114225
|
` : ""}Used: ${p3.usageCount}x${p3.lastUsedAt ? ` (last ${new Date(p3.lastUsedAt).toLocaleDateString()})` : ""}
|
|
114121
114226
|
`;
|
|
114122
114227
|
}
|
|
114228
|
+
function validateEnum(label, value, allowed) {
|
|
114229
|
+
if (value === void 0 || value === null) return null;
|
|
114230
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
114231
|
+
return `Error: invalid ${label} "${String(value)}". Allowed: ${allowed.join(", ")}.`;
|
|
114232
|
+
}
|
|
114233
|
+
return null;
|
|
114234
|
+
}
|
|
114235
|
+
function normalizeIncludedItems(value) {
|
|
114236
|
+
if (value === void 0) return void 0;
|
|
114237
|
+
if (value === null) return null;
|
|
114238
|
+
const cleaned = value.map((item) => typeof item === "string" ? item.trim() : "").filter((item) => item.length > 0);
|
|
114239
|
+
return cleaned.length > 0 ? cleaned : null;
|
|
114240
|
+
}
|
|
114123
114241
|
async function handleGetProducts(input) {
|
|
114124
114242
|
const { q: q3, currency, pageSize = 20 } = input;
|
|
114125
114243
|
const status = input.status ?? "active";
|
|
@@ -114200,6 +114318,8 @@ async function handleCreateProduct(input) {
|
|
|
114200
114318
|
if (!name21 || name21.trim().length === 0) {
|
|
114201
114319
|
return textResponse3("Error: `name` is required.");
|
|
114202
114320
|
}
|
|
114321
|
+
const enumError = validateEnum("billingType", input.billingType, BILLING_TYPES) ?? validateEnum("category", input.category, CATEGORIES) ?? validateEnum("tier", input.tier, TIERS);
|
|
114322
|
+
if (enumError) return textResponse3(enumError);
|
|
114203
114323
|
const resolved = await resolveTeamId(input.teamId);
|
|
114204
114324
|
if (!resolved.ok) return resolved.response;
|
|
114205
114325
|
const [created] = await db.insert(schema_exports.invoiceProducts).values({
|
|
@@ -114209,6 +114329,12 @@ async function handleCreateProduct(input) {
|
|
|
114209
114329
|
price: price ?? null,
|
|
114210
114330
|
currency: currency ?? null,
|
|
114211
114331
|
unit: unit ?? null,
|
|
114332
|
+
billingType: input.billingType ?? null,
|
|
114333
|
+
category: input.category ?? null,
|
|
114334
|
+
includedItems: normalizeIncludedItems(input.includedItems) ?? null,
|
|
114335
|
+
optional: input.optional ?? false,
|
|
114336
|
+
tier: input.tier ?? null,
|
|
114337
|
+
sortOrder: input.sortOrder ?? 0,
|
|
114212
114338
|
isActive: true,
|
|
114213
114339
|
lastUsedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
114214
114340
|
}).returning(PRODUCT_COLUMNS);
|
|
@@ -114230,6 +114356,8 @@ async function handleUpdateProduct(input) {
|
|
|
114230
114356
|
`Product ${productId} not found, or it is not owned by this team.`
|
|
114231
114357
|
);
|
|
114232
114358
|
}
|
|
114359
|
+
const enumError = validateEnum("billingType", input.billingType, BILLING_TYPES) ?? validateEnum("category", input.category, CATEGORIES) ?? validateEnum("tier", input.tier, TIERS);
|
|
114360
|
+
if (enumError) return textResponse3(enumError);
|
|
114233
114361
|
const updates = {};
|
|
114234
114362
|
if (input.name !== void 0) {
|
|
114235
114363
|
if (!input.name || input.name.trim().length === 0) {
|
|
@@ -114242,9 +114370,17 @@ async function handleUpdateProduct(input) {
|
|
|
114242
114370
|
if (input.currency !== void 0) updates.currency = input.currency;
|
|
114243
114371
|
if (input.unit !== void 0) updates.unit = input.unit;
|
|
114244
114372
|
if (input.isActive !== void 0) updates.isActive = input.isActive;
|
|
114373
|
+
if (input.billingType !== void 0) updates.billingType = input.billingType;
|
|
114374
|
+
if (input.category !== void 0) updates.category = input.category;
|
|
114375
|
+
if (input.includedItems !== void 0) {
|
|
114376
|
+
updates.includedItems = normalizeIncludedItems(input.includedItems);
|
|
114377
|
+
}
|
|
114378
|
+
if (input.optional !== void 0) updates.optional = input.optional;
|
|
114379
|
+
if (input.tier !== void 0) updates.tier = input.tier;
|
|
114380
|
+
if (input.sortOrder !== void 0) updates.sortOrder = input.sortOrder;
|
|
114245
114381
|
if (Object.keys(updates).length === 0) {
|
|
114246
114382
|
return textResponse3(
|
|
114247
|
-
"No fields to update. Provide at least one of: name, description, price, currency, unit, isActive."
|
|
114383
|
+
"No fields to update. Provide at least one of: name, description, price, currency, unit, isActive, billingType, category, includedItems, optional, tier, sortOrder."
|
|
114248
114384
|
);
|
|
114249
114385
|
}
|
|
114250
114386
|
updates.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -114320,7 +114456,12 @@ function snapshotFromProduct(product, defaults, now2 = () => (/* @__PURE__ */ ne
|
|
|
114320
114456
|
capturedAt: now2(),
|
|
114321
114457
|
metadata: {
|
|
114322
114458
|
isConfigurable: product.isConfigurable,
|
|
114323
|
-
options: product.options ?? null
|
|
114459
|
+
options: product.options ?? null,
|
|
114460
|
+
billingType: product.billingType ?? null,
|
|
114461
|
+
category: product.category ?? null,
|
|
114462
|
+
includedItems: product.includedItems ?? null,
|
|
114463
|
+
optional: product.optional ?? false,
|
|
114464
|
+
tier: product.tier ?? null
|
|
114324
114465
|
}
|
|
114325
114466
|
};
|
|
114326
114467
|
}
|
|
@@ -114444,7 +114585,12 @@ async function loadProductsInTeam(productIds, teamId) {
|
|
|
114444
114585
|
currency: schema_exports.invoiceProducts.currency,
|
|
114445
114586
|
unit: schema_exports.invoiceProducts.unit,
|
|
114446
114587
|
isConfigurable: schema_exports.invoiceProducts.isConfigurable,
|
|
114447
|
-
options: schema_exports.invoiceProducts.options
|
|
114588
|
+
options: schema_exports.invoiceProducts.options,
|
|
114589
|
+
billingType: schema_exports.invoiceProducts.billingType,
|
|
114590
|
+
category: schema_exports.invoiceProducts.category,
|
|
114591
|
+
includedItems: schema_exports.invoiceProducts.includedItems,
|
|
114592
|
+
optional: schema_exports.invoiceProducts.optional,
|
|
114593
|
+
tier: schema_exports.invoiceProducts.tier
|
|
114448
114594
|
}).from(schema_exports.invoiceProducts).where(
|
|
114449
114595
|
and(
|
|
114450
114596
|
inArray(schema_exports.invoiceProducts.id, productIds),
|
|
@@ -114780,12 +114926,22 @@ async function handleAddProductToQuote(input) {
|
|
|
114780
114926
|
lastUsedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
114781
114927
|
}).where(eq(schema_exports.invoiceProducts.id, product.id));
|
|
114782
114928
|
const snap = newItem.productSnapshot;
|
|
114929
|
+
const meta5 = snap.metadata;
|
|
114930
|
+
const metaParts = [];
|
|
114931
|
+
if (meta5.category) metaParts.push(`category=${meta5.category}`);
|
|
114932
|
+
if (meta5.billingType) metaParts.push(`billing=${meta5.billingType}`);
|
|
114933
|
+
if (meta5.tier) metaParts.push(`tier=${meta5.tier}`);
|
|
114934
|
+
if (meta5.optional) metaParts.push("optional add-on");
|
|
114935
|
+
if (meta5.includedItems && meta5.includedItems.length > 0) {
|
|
114936
|
+
metaParts.push(`included=[${meta5.includedItems.join(", ")}]`);
|
|
114937
|
+
}
|
|
114783
114938
|
return textResponse4(
|
|
114784
114939
|
`\u2705 **Product added to draft quote ${updated.quotationNumber ?? updated.id}**
|
|
114785
114940
|
|
|
114786
114941
|
Line item: ${newItem.name} \xD7 ${newItem.quantity}${newItem.unit ? ` ${newItem.unit}` : ""} @ ${newItem.price} ${snap.currency}
|
|
114787
114942
|
Snapshot: name="${snap.name}", unitPrice=${snap.unitPrice}, currency=${snap.currency}, vatRate=${snap.vatRate}%, unit=${snap.unit ?? "-"}
|
|
114788
|
-
|
|
114943
|
+
${metaParts.length > 0 ? `Metadata: ${metaParts.join(", ")}
|
|
114944
|
+
` : ""}
|
|
114789
114945
|
New quote total: ${updated.amount} ${updated.currency} (subtotal ${updated.subtotal}, VAT ${updated.vat})
|
|
114790
114946
|
The snapshot is immutable: later catalog edits won't change this quote.`
|
|
114791
114947
|
);
|
|
@@ -120242,8 +120398,6 @@ var storage = new Proxy({}, {
|
|
|
120242
120398
|
return Reflect.get(_storage, prop, _storage);
|
|
120243
120399
|
}
|
|
120244
120400
|
});
|
|
120245
|
-
|
|
120246
|
-
// src/tools/ticket-attachments.ts
|
|
120247
120401
|
var ALLOWED_IMAGE_TYPES = [
|
|
120248
120402
|
"image/jpeg",
|
|
120249
120403
|
"image/png",
|
|
@@ -120282,14 +120436,213 @@ var EXT_MIME = {
|
|
|
120282
120436
|
ppt: "application/vnd.ms-powerpoint",
|
|
120283
120437
|
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
120284
120438
|
};
|
|
120285
|
-
|
|
120286
|
-
return { content: [{ type: "text", text: text3 }] };
|
|
120287
|
-
}
|
|
120439
|
+
var HERMES_CACHE_PATH = /(?:^|[\\/])\.hermes[\\/](.+)$/i;
|
|
120288
120440
|
function mimeFromName(name21) {
|
|
120289
120441
|
if (!name21) return null;
|
|
120290
120442
|
const ext = name21.split(/[?#]/)[0]?.split(".").pop()?.toLowerCase();
|
|
120291
120443
|
return ext && EXT_MIME[ext] ? EXT_MIME[ext] : null;
|
|
120292
120444
|
}
|
|
120445
|
+
function isHermesCachePath(filePath) {
|
|
120446
|
+
return HERMES_CACHE_PATH.test(filePath.replace(/\\/g, "/"));
|
|
120447
|
+
}
|
|
120448
|
+
function hermesCacheRelativePath(filePath) {
|
|
120449
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
120450
|
+
const match = HERMES_CACHE_PATH.exec(normalized);
|
|
120451
|
+
return match?.[1] ?? null;
|
|
120452
|
+
}
|
|
120453
|
+
function getHermesMediaBaseUrl() {
|
|
120454
|
+
const base = process.env.HERMES_MEDIA_BASE_URL?.trim() || process.env.HERMES_GATEWAY_MEDIA_URL?.trim();
|
|
120455
|
+
return base ? base.replace(/\/+$/, "") : null;
|
|
120456
|
+
}
|
|
120457
|
+
function resolveHermesCacheMediaUrl(filePath) {
|
|
120458
|
+
const base = getHermesMediaBaseUrl();
|
|
120459
|
+
const relative = hermesCacheRelativePath(filePath);
|
|
120460
|
+
if (!base || !relative) return null;
|
|
120461
|
+
return `${base}/${relative.replace(/^\/+/, "")}`;
|
|
120462
|
+
}
|
|
120463
|
+
function parseMcpStagingStorageKey(uploadId, teamId, userId) {
|
|
120464
|
+
const normalized = uploadId.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
120465
|
+
const prefix = `${teamId}/mcp-staging/${userId}/`;
|
|
120466
|
+
if (!normalized.startsWith(prefix)) return null;
|
|
120467
|
+
const remainder = normalized.slice(prefix.length);
|
|
120468
|
+
if (!remainder || remainder.includes("..")) return null;
|
|
120469
|
+
return normalized;
|
|
120470
|
+
}
|
|
120471
|
+
function formatFilePathEnoentError(filePath) {
|
|
120472
|
+
const hermesHint = isHermesCachePath(filePath) ? getHermesMediaBaseUrl() ? " Hermes cache paths are fetched via HERMES_MEDIA_BASE_URL when local read fails." : " For Hermes/Telegram cache files over HTTP MCP, POST the bytes to /mcp/attachment-upload and pass the returned uploadId, or set HERMES_MEDIA_BASE_URL so cache paths can be fetched remotely." : " filePath must exist on the MCP server filesystem (works for Cursor workspace paths with stdio MCP, not for gateway-local paths over HTTP MCP).";
|
|
120473
|
+
return `Failed to read the file at "${filePath}": path not found in the MCP runtime (ENOENT).${hermesHint} Alternatives: imageUrl (download URL), uploadId (from POST /mcp/attachment-upload), or base64Data for small files.`;
|
|
120474
|
+
}
|
|
120475
|
+
function hermesMediaFetchHeaders() {
|
|
120476
|
+
const token = process.env.HERMES_MEDIA_AUTH_TOKEN?.trim() || process.env.HERMES_MEDIA_BEARER_TOKEN?.trim();
|
|
120477
|
+
if (!token) return void 0;
|
|
120478
|
+
return { Authorization: `Bearer ${token}` };
|
|
120479
|
+
}
|
|
120480
|
+
async function fetchAttachmentUrl(url3, fallbackName, mimeOverride) {
|
|
120481
|
+
const res = await fetch(url3, { headers: hermesMediaFetchHeaders() });
|
|
120482
|
+
if (!res.ok) {
|
|
120483
|
+
return {
|
|
120484
|
+
ok: false,
|
|
120485
|
+
message: `Could not download from URL: HTTP ${res.status}.`
|
|
120486
|
+
};
|
|
120487
|
+
}
|
|
120488
|
+
const headerType = res.headers.get("content-type")?.split(";")[0]?.trim();
|
|
120489
|
+
const buffer2 = Buffer.from(await res.arrayBuffer());
|
|
120490
|
+
const fileName = fallbackName || url3.split("/").pop()?.split(/[?#]/)[0] || `attachment_${Date.now()}`;
|
|
120491
|
+
const mimeType = mimeOverride?.trim() || (headerType && headerType !== "application/octet-stream" ? headerType : null) || mimeFromName(url3) || mimeFromName(fileName) || "application/octet-stream";
|
|
120492
|
+
return { ok: true, buffer: buffer2, fileName, mimeType };
|
|
120493
|
+
}
|
|
120494
|
+
async function resolveFromFilePath(filePath, fileNameOverride, mimeOverride) {
|
|
120495
|
+
try {
|
|
120496
|
+
const buffer2 = await readFile(filePath);
|
|
120497
|
+
const fileName = fileNameOverride?.trim() || basename(filePath) || "attachment";
|
|
120498
|
+
const mimeType = mimeOverride?.trim() || mimeFromName(fileName) || "application/octet-stream";
|
|
120499
|
+
return { ok: true, buffer: buffer2, fileName, mimeType };
|
|
120500
|
+
} catch (error49) {
|
|
120501
|
+
const code = error49 && typeof error49 === "object" && "code" in error49 ? String(error49.code) : "";
|
|
120502
|
+
if (code !== "ENOENT") {
|
|
120503
|
+
return {
|
|
120504
|
+
ok: false,
|
|
120505
|
+
message: `Failed to read the file: ${error49 instanceof Error ? error49.message : String(error49)}`
|
|
120506
|
+
};
|
|
120507
|
+
}
|
|
120508
|
+
const hermesUrl = resolveHermesCacheMediaUrl(filePath);
|
|
120509
|
+
if (hermesUrl) {
|
|
120510
|
+
const fetched = await fetchAttachmentUrl(
|
|
120511
|
+
hermesUrl,
|
|
120512
|
+
fileNameOverride?.trim() || basename(filePath) || void 0,
|
|
120513
|
+
mimeOverride
|
|
120514
|
+
);
|
|
120515
|
+
if (fetched.ok) {
|
|
120516
|
+
return {
|
|
120517
|
+
ok: true,
|
|
120518
|
+
buffer: fetched.buffer,
|
|
120519
|
+
fileName: fetched.fileName,
|
|
120520
|
+
mimeType: fetched.mimeType
|
|
120521
|
+
};
|
|
120522
|
+
}
|
|
120523
|
+
return {
|
|
120524
|
+
ok: false,
|
|
120525
|
+
message: `${formatFilePathEnoentError(filePath)} Hermes media fetch also failed: ${fetched.message}`
|
|
120526
|
+
};
|
|
120527
|
+
}
|
|
120528
|
+
return { ok: false, message: formatFilePathEnoentError(filePath) };
|
|
120529
|
+
}
|
|
120530
|
+
}
|
|
120531
|
+
async function resolveFromUploadId(uploadId, teamId, userId, fileNameOverride, mimeOverride) {
|
|
120532
|
+
const storageKey = parseMcpStagingStorageKey(uploadId, teamId, userId);
|
|
120533
|
+
if (!storageKey) {
|
|
120534
|
+
return {
|
|
120535
|
+
ok: false,
|
|
120536
|
+
message: "Invalid uploadId. Use the uploadId returned by POST /mcp/attachment-upload for your API key user."
|
|
120537
|
+
};
|
|
120538
|
+
}
|
|
120539
|
+
let downloaded;
|
|
120540
|
+
try {
|
|
120541
|
+
downloaded = await storage.download({ bucket: "vault", path: storageKey });
|
|
120542
|
+
} catch (error49) {
|
|
120543
|
+
return {
|
|
120544
|
+
ok: false,
|
|
120545
|
+
message: `Staged upload not found or expired (${uploadId}): ${error49 instanceof Error ? error49.message : String(error49)}`
|
|
120546
|
+
};
|
|
120547
|
+
}
|
|
120548
|
+
const buffer2 = Buffer.from(await downloaded.blob.arrayBuffer());
|
|
120549
|
+
const defaultName = storageKey.split("/").pop() || "attachment";
|
|
120550
|
+
const fileName = fileNameOverride?.trim() || defaultName;
|
|
120551
|
+
const mimeType = mimeOverride?.trim() || downloaded.contentType || mimeFromName(fileName) || "application/octet-stream";
|
|
120552
|
+
return {
|
|
120553
|
+
ok: true,
|
|
120554
|
+
buffer: buffer2,
|
|
120555
|
+
fileName,
|
|
120556
|
+
mimeType,
|
|
120557
|
+
stagingStorageKey: storageKey
|
|
120558
|
+
};
|
|
120559
|
+
}
|
|
120560
|
+
async function resolveAttachmentSource(input) {
|
|
120561
|
+
const sources = [
|
|
120562
|
+
input.filePath,
|
|
120563
|
+
input.imageUrl,
|
|
120564
|
+
input.base64Data,
|
|
120565
|
+
input.uploadId
|
|
120566
|
+
].filter((v2) => typeof v2 === "string" && v2.trim().length > 0);
|
|
120567
|
+
if (sources.length === 0) {
|
|
120568
|
+
return {
|
|
120569
|
+
ok: false,
|
|
120570
|
+
message: "Provide exactly one source: filePath, imageUrl, uploadId (from POST /mcp/attachment-upload), or base64Data."
|
|
120571
|
+
};
|
|
120572
|
+
}
|
|
120573
|
+
if (sources.length > 1) {
|
|
120574
|
+
return {
|
|
120575
|
+
ok: false,
|
|
120576
|
+
message: "Provide only one source (filePath, imageUrl, uploadId, or base64Data), not several."
|
|
120577
|
+
};
|
|
120578
|
+
}
|
|
120579
|
+
if (input.uploadId) {
|
|
120580
|
+
return resolveFromUploadId(
|
|
120581
|
+
input.uploadId.trim(),
|
|
120582
|
+
input.teamId,
|
|
120583
|
+
input.userId,
|
|
120584
|
+
input.fileName,
|
|
120585
|
+
input.mimeType
|
|
120586
|
+
);
|
|
120587
|
+
}
|
|
120588
|
+
if (input.filePath) {
|
|
120589
|
+
return resolveFromFilePath(input.filePath, input.fileName, input.mimeType);
|
|
120590
|
+
}
|
|
120591
|
+
if (input.imageUrl) {
|
|
120592
|
+
const fetched = await fetchAttachmentUrl(
|
|
120593
|
+
input.imageUrl,
|
|
120594
|
+
input.fileName,
|
|
120595
|
+
input.mimeType
|
|
120596
|
+
);
|
|
120597
|
+
if (!fetched.ok) return fetched;
|
|
120598
|
+
return {
|
|
120599
|
+
ok: true,
|
|
120600
|
+
buffer: fetched.buffer,
|
|
120601
|
+
fileName: fetched.fileName,
|
|
120602
|
+
mimeType: fetched.mimeType
|
|
120603
|
+
};
|
|
120604
|
+
}
|
|
120605
|
+
let b64 = input.base64Data;
|
|
120606
|
+
let mimeType = input.mimeType?.trim() ?? "";
|
|
120607
|
+
const dataUri = b64.match(/^data:([^;]+);base64,(.*)$/s);
|
|
120608
|
+
if (dataUri) {
|
|
120609
|
+
if (!mimeType) mimeType = dataUri[1] ?? "";
|
|
120610
|
+
b64 = dataUri[2] ?? "";
|
|
120611
|
+
} else if (b64.includes(",")) {
|
|
120612
|
+
b64 = b64.split(",")[1] || b64;
|
|
120613
|
+
}
|
|
120614
|
+
const buffer2 = Buffer.from(b64, "base64");
|
|
120615
|
+
const fileName = input.fileName?.trim() || `attachment_${Date.now()}`;
|
|
120616
|
+
if (!mimeType) {
|
|
120617
|
+
mimeType = mimeFromName(fileName) ?? "application/octet-stream";
|
|
120618
|
+
}
|
|
120619
|
+
return { ok: true, buffer: buffer2, fileName, mimeType };
|
|
120620
|
+
}
|
|
120621
|
+
function validateAttachmentBuffer(buffer2, mimeType) {
|
|
120622
|
+
if (buffer2.byteLength === 0) {
|
|
120623
|
+
return { ok: false, message: "The file is empty (0 bytes); nothing to upload." };
|
|
120624
|
+
}
|
|
120625
|
+
if (buffer2.byteLength > MAX_FILE_SIZE) {
|
|
120626
|
+
return {
|
|
120627
|
+
ok: false,
|
|
120628
|
+
message: `File too large (${(buffer2.byteLength / 1024 / 1024).toFixed(
|
|
120629
|
+
1
|
|
120630
|
+
)} MB). Max: 25 MB.`
|
|
120631
|
+
};
|
|
120632
|
+
}
|
|
120633
|
+
if (!ALLOWED_MIME_TYPES.has(mimeType)) {
|
|
120634
|
+
return {
|
|
120635
|
+
ok: false,
|
|
120636
|
+
message: `Unsupported file type: ${mimeType}. Allowed: JPEG, PNG, GIF, WebP, PDF, DOC(X), XLS(X), PPT(X), TXT, CSV.`
|
|
120637
|
+
};
|
|
120638
|
+
}
|
|
120639
|
+
return null;
|
|
120640
|
+
}
|
|
120641
|
+
|
|
120642
|
+
// src/tools/ticket-attachments.ts
|
|
120643
|
+
function textResponse5(text3) {
|
|
120644
|
+
return { content: [{ type: "text", text: text3 }] };
|
|
120645
|
+
}
|
|
120293
120646
|
async function findAttachment(attachmentId) {
|
|
120294
120647
|
const [ticketAtt] = await db.select({
|
|
120295
120648
|
ticketId: schema_exports.ticketAttachments.ticketId,
|
|
@@ -120358,82 +120711,30 @@ ${url3}`
|
|
|
120358
120711
|
};
|
|
120359
120712
|
}
|
|
120360
120713
|
async function handleUploadTicketAttachment(input) {
|
|
120361
|
-
const ctx = getAuthContext();
|
|
120362
|
-
|
|
120363
|
-
(
|
|
120364
|
-
);
|
|
120365
|
-
if (sources.length === 0) {
|
|
120366
|
-
return textResponse5(
|
|
120367
|
-
"Provide exactly one source: filePath (absolute local path), imageUrl, or base64Data."
|
|
120368
|
-
);
|
|
120369
|
-
}
|
|
120370
|
-
if (sources.length > 1) {
|
|
120371
|
-
return textResponse5(
|
|
120372
|
-
"Provide only one source (filePath, imageUrl, or base64Data), not several."
|
|
120373
|
-
);
|
|
120714
|
+
const ctx = getAuthContext() ?? authContext;
|
|
120715
|
+
if (!ctx) {
|
|
120716
|
+
return textResponse5("Error: Not authenticated.");
|
|
120374
120717
|
}
|
|
120375
120718
|
const access = await loadAccessibleTicket(input.teamId, input.ticketId);
|
|
120376
120719
|
if (!access.ok) return access.response;
|
|
120377
120720
|
const ticket = access.ticket;
|
|
120378
|
-
|
|
120379
|
-
|
|
120380
|
-
|
|
120381
|
-
|
|
120382
|
-
|
|
120383
|
-
|
|
120384
|
-
|
|
120385
|
-
|
|
120386
|
-
|
|
120387
|
-
|
|
120388
|
-
|
|
120389
|
-
|
|
120390
|
-
if (!res.ok) {
|
|
120391
|
-
return textResponse5(
|
|
120392
|
-
`Could not download from URL: HTTP ${res.status}.`
|
|
120393
|
-
);
|
|
120394
|
-
}
|
|
120395
|
-
const headerType = res.headers.get("content-type")?.split(";")[0]?.trim();
|
|
120396
|
-
buffer2 = Buffer.from(await res.arrayBuffer());
|
|
120397
|
-
if (!fileName) {
|
|
120398
|
-
fileName = input.imageUrl.split("/").pop()?.split(/[?#]/)[0] || `attachment_${Date.now()}`;
|
|
120399
|
-
}
|
|
120400
|
-
if (!mimeType) {
|
|
120401
|
-
mimeType = (headerType && headerType !== "application/octet-stream" ? headerType : null) ?? mimeFromName(input.imageUrl) ?? mimeFromName(fileName) ?? "application/octet-stream";
|
|
120402
|
-
}
|
|
120403
|
-
} else {
|
|
120404
|
-
let b64 = input.base64Data;
|
|
120405
|
-
const dataUri = b64.match(/^data:([^;]+);base64,(.*)$/s);
|
|
120406
|
-
if (dataUri) {
|
|
120407
|
-
if (!mimeType) mimeType = dataUri[1] ?? "";
|
|
120408
|
-
b64 = dataUri[2] ?? "";
|
|
120409
|
-
} else if (b64.includes(",")) {
|
|
120410
|
-
b64 = b64.split(",")[1] || b64;
|
|
120411
|
-
}
|
|
120412
|
-
buffer2 = Buffer.from(b64, "base64");
|
|
120413
|
-
if (!fileName) fileName = `attachment_${Date.now()}`;
|
|
120414
|
-
if (!mimeType) {
|
|
120415
|
-
mimeType = mimeFromName(fileName) ?? "application/octet-stream";
|
|
120416
|
-
}
|
|
120417
|
-
}
|
|
120418
|
-
} catch (error49) {
|
|
120419
|
-
return textResponse5(
|
|
120420
|
-
`Failed to read the file: ${error49 instanceof Error ? error49.message : String(error49)}`
|
|
120421
|
-
);
|
|
120422
|
-
}
|
|
120423
|
-
if (buffer2.byteLength === 0) {
|
|
120424
|
-
return textResponse5("The file is empty (0 bytes); nothing to upload.");
|
|
120425
|
-
}
|
|
120426
|
-
if (buffer2.byteLength > MAX_FILE_SIZE) {
|
|
120427
|
-
return textResponse5(
|
|
120428
|
-
`File too large (${(buffer2.byteLength / 1024 / 1024).toFixed(
|
|
120429
|
-
1
|
|
120430
|
-
)} MB). Max: 25 MB.`
|
|
120431
|
-
);
|
|
120721
|
+
const resolved = await resolveAttachmentSource({
|
|
120722
|
+
filePath: input.filePath,
|
|
120723
|
+
imageUrl: input.imageUrl,
|
|
120724
|
+
base64Data: input.base64Data,
|
|
120725
|
+
uploadId: input.uploadId,
|
|
120726
|
+
fileName: input.fileName,
|
|
120727
|
+
mimeType: input.mimeType,
|
|
120728
|
+
teamId: ctx.teamId,
|
|
120729
|
+
userId: ctx.userId
|
|
120730
|
+
});
|
|
120731
|
+
if (!resolved.ok) {
|
|
120732
|
+
return textResponse5(resolved.message);
|
|
120432
120733
|
}
|
|
120433
|
-
|
|
120434
|
-
|
|
120435
|
-
|
|
120436
|
-
);
|
|
120734
|
+
const { buffer: buffer2, fileName, mimeType, stagingStorageKey } = resolved;
|
|
120735
|
+
const validationError = validateAttachmentBuffer(buffer2, mimeType);
|
|
120736
|
+
if (validationError) {
|
|
120737
|
+
return textResponse5(validationError.message);
|
|
120437
120738
|
}
|
|
120438
120739
|
const storageKey = `${ticket.teamId}/tickets/${ticket.id}/${Date.now()}_${fileName}`;
|
|
120439
120740
|
try {
|
|
@@ -120448,6 +120749,12 @@ async function handleUploadTicketAttachment(input) {
|
|
|
120448
120749
|
`Upload failed: ${error49 instanceof Error ? error49.message : String(error49)}`
|
|
120449
120750
|
);
|
|
120450
120751
|
}
|
|
120752
|
+
if (stagingStorageKey) {
|
|
120753
|
+
try {
|
|
120754
|
+
await storage.remove({ bucket: "vault", paths: [stagingStorageKey] });
|
|
120755
|
+
} catch {
|
|
120756
|
+
}
|
|
120757
|
+
}
|
|
120451
120758
|
const [row] = await db.insert(schema_exports.ticketAttachments).values({
|
|
120452
120759
|
ticketId: ticket.id,
|
|
120453
120760
|
teamId: ticket.teamId,
|
|
@@ -121555,7 +121862,7 @@ function attemptedLockedFields(update) {
|
|
|
121555
121862
|
|
|
121556
121863
|
// src/tools/trips.ts
|
|
121557
121864
|
var TRIP_TYPES = ["private", "business"];
|
|
121558
|
-
var
|
|
121865
|
+
var BILLING_TYPES2 = TRIP_BILLING_TYPES;
|
|
121559
121866
|
function textResponse7(text3) {
|
|
121560
121867
|
return { content: [{ type: "text", text: text3 }] };
|
|
121561
121868
|
}
|
|
@@ -121616,9 +121923,9 @@ async function handleGetTrips(input) {
|
|
|
121616
121923
|
`Error: invalid tripType "${input.tripType}". Allowed: ${TRIP_TYPES.join(", ")}.`
|
|
121617
121924
|
);
|
|
121618
121925
|
}
|
|
121619
|
-
if (input.billingType && !
|
|
121926
|
+
if (input.billingType && !BILLING_TYPES2.includes(input.billingType)) {
|
|
121620
121927
|
return textResponse7(
|
|
121621
|
-
`Error: invalid billingType "${input.billingType}". Allowed: ${
|
|
121928
|
+
`Error: invalid billingType "${input.billingType}". Allowed: ${BILLING_TYPES2.join(", ")}.`
|
|
121622
121929
|
);
|
|
121623
121930
|
}
|
|
121624
121931
|
const scope = await resolveTeamScope(input.teamId);
|
|
@@ -121738,9 +122045,9 @@ async function handleCreateTrip(input) {
|
|
|
121738
122045
|
);
|
|
121739
122046
|
}
|
|
121740
122047
|
const billingType = input.billingType ?? "not_billable";
|
|
121741
|
-
if (!
|
|
122048
|
+
if (!BILLING_TYPES2.includes(billingType)) {
|
|
121742
122049
|
return textResponse7(
|
|
121743
|
-
`Error: invalid billingType "${input.billingType}". Allowed: ${
|
|
122050
|
+
`Error: invalid billingType "${input.billingType}". Allowed: ${BILLING_TYPES2.join(", ")}.`
|
|
121744
122051
|
);
|
|
121745
122052
|
}
|
|
121746
122053
|
const resolved = await resolveTeamId(input.teamId);
|
|
@@ -121819,9 +122126,9 @@ async function handleUpdateTrip(input) {
|
|
|
121819
122126
|
`Error: invalid tripType "${input.tripType}". Allowed: ${TRIP_TYPES.join(", ")}.`
|
|
121820
122127
|
);
|
|
121821
122128
|
}
|
|
121822
|
-
if (input.billingType && !
|
|
122129
|
+
if (input.billingType && !BILLING_TYPES2.includes(input.billingType)) {
|
|
121823
122130
|
return textResponse7(
|
|
121824
|
-
`Error: invalid billingType "${input.billingType}". Allowed: ${
|
|
122131
|
+
`Error: invalid billingType "${input.billingType}". Allowed: ${BILLING_TYPES2.join(", ")}.`
|
|
121825
122132
|
);
|
|
121826
122133
|
}
|
|
121827
122134
|
const resolved = await resolveTeamId(input.teamId);
|
|
@@ -122472,7 +122779,7 @@ ${tagErrors.map((e6) => ` \u2022 ${e6}`).join("\n")}
|
|
|
122472
122779
|
}
|
|
122473
122780
|
|
|
122474
122781
|
// src/server.ts
|
|
122475
|
-
var SERVER_VERSION = "3.5.
|
|
122782
|
+
var SERVER_VERSION = "3.5.4";
|
|
122476
122783
|
function createMcpServer() {
|
|
122477
122784
|
const server = new Server(
|
|
122478
122785
|
{
|