@mgsoftwarebv/mg-dashboard-mcp 7.4.7 → 7.4.9

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.
Files changed (2) hide show
  1. package/dist/index.js +257 -43
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import { sql } from 'drizzle-orm';
20
20
  import { once } from 'events';
21
21
  import { lookup } from 'dns/promises';
22
22
  import { connect } from 'tls';
23
- import { pgTable, timestamp, jsonb, uuid, boolean, text, pgEnum, integer, uniqueIndex, index, check, bigint, doublePrecision, foreignKey } from 'drizzle-orm/pg-core';
23
+ import { pgTable, timestamp, jsonb, uuid, boolean, text, pgEnum, integer, uniqueIndex, index, check, bigint, doublePrecision, foreignKey, date, real } from 'drizzle-orm/pg-core';
24
24
  import { HeadObjectCommand, ListObjectsV2Command, DeleteObjectsCommand, S3Client, DeleteObjectCommand, CreateMultipartUploadCommand, UploadPartCommand, CompleteMultipartUploadCommand, AbortMultipartUploadCommand, PutObjectCommand, GetObjectCommand, CopyObjectCommand } from '@aws-sdk/client-s3';
25
25
 
26
26
  var __defProp = Object.defineProperty;
@@ -990,9 +990,9 @@ ${raw2.substring(0, 500)}` }] };
990
990
  return { content: [{ type: "text", text: `No environment variables for ${project}/${env}.` }] };
991
991
  }
992
992
  const lines = vars.map((v) => `${v.isSecret ? "[secret]" : " "} ${v.name}`).sort();
993
- const text13 = `Env vars for ${project}/${env} (${vars.length}) \u2014 values hidden, use action="get" with a key:
993
+ const text14 = `Env vars for ${project}/${env} (${vars.length}) \u2014 values hidden, use action="get" with a key:
994
994
  ` + "-".repeat(50) + "\n" + lines.join("\n");
995
- return { content: [{ type: "text", text: text13 }] };
995
+ return { content: [{ type: "text", text: text14 }] };
996
996
  }
997
997
  if (action === "get") {
998
998
  const key2 = String(args2.key ?? "");
@@ -1079,9 +1079,9 @@ async function fetchAndFormatRun(conn, proxy, sshExec2, instance, runId) {
1079
1079
  return { content: [{ type: "text", text: `Invalid API response:
1080
1080
  ${rawJson.substring(0, 500)}` }] };
1081
1081
  }
1082
- let text13 = formatRunDetail(run);
1083
- if (logs) text13 += "\n\n--- Logs ---\n" + logs;
1084
- return { content: [{ type: "text", text: text13 }] };
1082
+ let text14 = formatRunDetail(run);
1083
+ if (logs) text14 += "\n\n--- Logs ---\n" + logs;
1084
+ return { content: [{ type: "text", text: text14 }] };
1085
1085
  }
1086
1086
  async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSeconds) {
1087
1087
  const pollInterval = 3e3;
@@ -1103,10 +1103,10 @@ async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSec
1103
1103
  continue;
1104
1104
  }
1105
1105
  if (TERMINAL_STATUSES.has(run.status)) {
1106
- let text13 = formatRunDetail(run);
1106
+ let text14 = formatRunDetail(run);
1107
1107
  const logs = await fetchRunLogs(runId, conn, proxy, sshExec2);
1108
- if (logs) text13 += "\n\n--- Logs ---\n" + logs;
1109
- return { content: [{ type: "text", text: text13 }] };
1108
+ if (logs) text14 += "\n\n--- Logs ---\n" + logs;
1109
+ return { content: [{ type: "text", text: text14 }] };
1110
1110
  }
1111
1111
  }
1112
1112
  return {
@@ -3793,10 +3793,10 @@ var ZodObject = class _ZodObject extends ZodType {
3793
3793
  // }) as any;
3794
3794
  // return merged;
3795
3795
  // }
3796
- catchall(index10) {
3796
+ catchall(index11) {
3797
3797
  return new _ZodObject({
3798
3798
  ...this._def,
3799
- catchall: index10
3799
+ catchall: index11
3800
3800
  });
3801
3801
  }
3802
3802
  pick(mask) {
@@ -4114,9 +4114,9 @@ function mergeValues(a, b) {
4114
4114
  return { valid: false };
4115
4115
  }
4116
4116
  const newArray = [];
4117
- for (let index10 = 0; index10 < a.length; index10++) {
4118
- const itemA = a[index10];
4119
- const itemB = b[index10];
4117
+ for (let index11 = 0; index11 < a.length; index11++) {
4118
+ const itemA = a[index11];
4119
+ const itemB = b[index11];
4120
4120
  const sharedValue = mergeValues(itemA, itemB);
4121
4121
  if (!sharedValue.valid) {
4122
4122
  return { valid: false };
@@ -4322,10 +4322,10 @@ var ZodMap = class extends ZodType {
4322
4322
  }
4323
4323
  const keyType = this._def.keyType;
4324
4324
  const valueType = this._def.valueType;
4325
- const pairs = [...ctx.data.entries()].map(([key, value], index10) => {
4325
+ const pairs = [...ctx.data.entries()].map(([key, value], index11) => {
4326
4326
  return {
4327
- key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index10, "key"])),
4328
- value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index10, "value"]))
4327
+ key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index11, "key"])),
4328
+ value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index11, "value"]))
4329
4329
  };
4330
4330
  });
4331
4331
  if (ctx.common.async) {
@@ -6153,6 +6153,12 @@ var directorySubmissionStatus = pgEnum(
6153
6153
  "manual"
6154
6154
  ]
6155
6155
  );
6156
+ var directoryAccountStatus = pgEnum("directory_account_status", [
6157
+ "pending_verification",
6158
+ "active",
6159
+ "failed",
6160
+ "captcha_blocked"
6161
+ ]);
6156
6162
  var linkDirectory = pgTable(
6157
6163
  "link_directory",
6158
6164
  {
@@ -6245,6 +6251,32 @@ pgTable(
6245
6251
  )
6246
6252
  ]
6247
6253
  );
6254
+ pgTable(
6255
+ "directory_account",
6256
+ {
6257
+ id: uuid("id").primaryKey().defaultRandom(),
6258
+ clientId: uuid("client_id").notNull().references(() => linkBuildingClient.id, { onDelete: "cascade" }),
6259
+ directoryId: uuid("directory_id").notNull().references(() => linkDirectory.id, { onDelete: "cascade" }),
6260
+ email: text("email").notNull(),
6261
+ passwordEncrypted: text("password_encrypted").notNull(),
6262
+ status: directoryAccountStatus("status").notNull().default("pending_verification"),
6263
+ registerUrl: text("register_url"),
6264
+ loginUrl: text("login_url"),
6265
+ lastError: text("last_error"),
6266
+ registeredAt: timestamp("registered_at", { withTimezone: true }),
6267
+ verifiedAt: timestamp("verified_at", { withTimezone: true }),
6268
+ lastLoginAt: timestamp("last_login_at", { withTimezone: true }),
6269
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6270
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6271
+ },
6272
+ (table) => [
6273
+ uniqueIndex("directory_account_client_directory_uidx").on(
6274
+ table.clientId,
6275
+ table.directoryId
6276
+ ),
6277
+ index("directory_account_status_idx").on(table.status)
6278
+ ]
6279
+ );
6248
6280
  var githubToken = pgTable(
6249
6281
  "github_token",
6250
6282
  {
@@ -6506,6 +6538,97 @@ pgTable(
6506
6538
  }).onDelete("cascade")
6507
6539
  ]
6508
6540
  );
6541
+ pgTable(
6542
+ "wiki_page",
6543
+ {
6544
+ id: uuid("id").primaryKey().defaultRandom(),
6545
+ slug: text("slug").notNull().unique(),
6546
+ title: text("title").notNull().default(""),
6547
+ summary: text("summary"),
6548
+ content: text("content").notNull().default(""),
6549
+ status: text("status").notNull().default("draft"),
6550
+ pageUpdatedOn: date("page_updated_on"),
6551
+ reviewedBy: text("reviewed_by"),
6552
+ reviewedOn: date("reviewed_on"),
6553
+ lastVerifiedOn: date("last_verified_on"),
6554
+ verifyCount: integer("verify_count").notNull().default(0),
6555
+ verifyNote: text("verify_note"),
6556
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6557
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6558
+ },
6559
+ (table) => [
6560
+ index("idx_wiki_page_status").on(table.status),
6561
+ index("idx_wiki_page_last_verified").on(table.lastVerifiedOn)
6562
+ ]
6563
+ );
6564
+ pgTable(
6565
+ "wiki_page_flag",
6566
+ {
6567
+ id: uuid("id").primaryKey().defaultRandom(),
6568
+ pageSlug: text("page_slug").notNull(),
6569
+ kind: text("kind").notNull(),
6570
+ detail: text("detail").notNull().default(""),
6571
+ createdBy: text("created_by"),
6572
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6573
+ resolvedAt: timestamp("resolved_at", { withTimezone: true }),
6574
+ resolvedBy: text("resolved_by")
6575
+ },
6576
+ (table) => [index("idx_wiki_page_flag_created").on(table.createdAt)]
6577
+ );
6578
+ pgTable(
6579
+ "wiki_page_evidence",
6580
+ {
6581
+ id: uuid("id").primaryKey().defaultRandom(),
6582
+ pageSlug: text("page_slug").notNull(),
6583
+ evidenceType: text("evidence_type").notNull(),
6584
+ evidenceRef: text("evidence_ref").notNull(),
6585
+ matchScore: real("match_score"),
6586
+ matchReason: text("match_reason"),
6587
+ snippet: text("snippet"),
6588
+ status: text("status").notNull().default("pending"),
6589
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6590
+ resolvedAt: timestamp("resolved_at", { withTimezone: true })
6591
+ },
6592
+ (table) => [
6593
+ uniqueIndex("wiki_page_evidence_page_slug_evidence_type_evidence_ref_key").on(
6594
+ table.pageSlug,
6595
+ table.evidenceType,
6596
+ table.evidenceRef
6597
+ ),
6598
+ index("idx_wiki_page_evidence_created").on(table.createdAt)
6599
+ ]
6600
+ );
6601
+ pgTable(
6602
+ "wiki_page_read",
6603
+ {
6604
+ id: uuid("id").primaryKey().defaultRandom(),
6605
+ slug: text("slug").notNull(),
6606
+ caller: text("caller"),
6607
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6608
+ },
6609
+ (table) => [
6610
+ index("idx_wiki_page_read_slug").on(table.slug, table.createdAt),
6611
+ index("idx_wiki_page_read_created").on(table.createdAt)
6612
+ ]
6613
+ );
6614
+ pgTable(
6615
+ "wiki_verify_event",
6616
+ {
6617
+ id: uuid("id").primaryKey().defaultRandom(),
6618
+ pageSlug: text("page_slug").notNull(),
6619
+ outcome: text("outcome").notNull(),
6620
+ runMode: text("run_mode"),
6621
+ note: text("note"),
6622
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6623
+ },
6624
+ (table) => [
6625
+ index("idx_wiki_verify_event_created").on(table.createdAt),
6626
+ index("idx_wiki_verify_event_outcome_created").on(
6627
+ table.outcome,
6628
+ table.createdAt
6629
+ )
6630
+ ]
6631
+ );
6509
6632
  var GITHUB_API = "https://api.github.com";
6510
6633
  var ReleaseProfileQuickCreateError = class extends Error {
6511
6634
  constructor(message, code = "INTERNAL_SERVER_ERROR") {
@@ -7909,7 +8032,7 @@ function getEncryptionKey() {
7909
8032
  throw new Error("ENCRYPTION_KEY must be a 64-character hex string");
7910
8033
  return buf;
7911
8034
  }
7912
- function encrypt(text13) {
8035
+ function encrypt(text14) {
7913
8036
  const key = getEncryptionKey();
7914
8037
  const iv = randomBytes(ENC_IV_LENGTH);
7915
8038
  const cipher = createCipheriv(
@@ -7917,7 +8040,7 @@ function encrypt(text13) {
7917
8040
  new Uint8Array(key),
7918
8041
  new Uint8Array(iv)
7919
8042
  );
7920
- let encrypted = cipher.update(text13, "utf8", "hex");
8043
+ let encrypted = cipher.update(text14, "utf8", "hex");
7921
8044
  encrypted += cipher.final("hex");
7922
8045
  const authTag = cipher.getAuthTag();
7923
8046
  return Buffer.concat([
@@ -8637,10 +8760,10 @@ async function r2GetObjectRange(bucket, key, range) {
8637
8760
  const body = result.Body;
8638
8761
  if (!body?.transformToString)
8639
8762
  throw new Error("R2 returned no readable body");
8640
- const text13 = await body.transformToString();
8763
+ const text14 = await body.transformToString();
8641
8764
  const header = `# range: bytes ${range.offset}-${end} of ${size} (${effectiveLen} bytes)`;
8642
8765
  return `${header}
8643
- ${text13}`;
8766
+ ${text14}`;
8644
8767
  } catch (e) {
8645
8768
  throw r2WrapError(bucket, key, e);
8646
8769
  }
@@ -9003,15 +9126,15 @@ async function sftpRead(opts, filePath, proxy, options) {
9003
9126
  clearTimeout(timer);
9004
9127
  cleanup?.();
9005
9128
  cleanup = void 0;
9006
- const text13 = Buffer.concat(
9129
+ const text14 = Buffer.concat(
9007
9130
  chunks.map((ch) => new Uint8Array(ch))
9008
9131
  ).toString("utf-8");
9009
9132
  if (!isWholeFileRequest) {
9010
9133
  const header = `# range: bytes ${offset}-${offset + effectiveLen - 1} of ${total} (${effectiveLen} bytes)`;
9011
9134
  resolve(`${header}
9012
- ${text13}`);
9135
+ ${text14}`);
9013
9136
  } else {
9014
- resolve(text13);
9137
+ resolve(text14);
9015
9138
  }
9016
9139
  });
9017
9140
  rs.on("error", (e) => {
@@ -9158,11 +9281,11 @@ function getKnownContainers(serverId, maxAgeMs = 5 * 6e4) {
9158
9281
  if (Date.now() - e.capturedAt > maxAgeMs) return void 0;
9159
9282
  return e.names;
9160
9283
  }
9161
- function truncateForLLM(text13, maxBytes) {
9162
- const totalBytes = Buffer.byteLength(text13, "utf8");
9284
+ function truncateForLLM(text14, maxBytes) {
9285
+ const totalBytes = Buffer.byteLength(text14, "utf8");
9163
9286
  if (totalBytes <= maxBytes)
9164
- return { text: text13, truncated: false, totalBytes, shownBytes: totalBytes };
9165
- const buf = Buffer.from(text13, "utf8");
9287
+ return { text: text14, truncated: false, totalBytes, shownBytes: totalBytes };
9288
+ const buf = Buffer.from(text14, "utf8");
9166
9289
  let cut = maxBytes;
9167
9290
  while (cut > 0 && (buf[cut] & 192) === 128) cut--;
9168
9291
  const head = buf.subarray(0, cut).toString("utf8");
@@ -9193,10 +9316,10 @@ function postprocessResult(result, meta) {
9193
9316
  if (!result.content?.length) return result;
9194
9317
  if (RAW_JSON_TOOLS.has(meta.toolName)) return result;
9195
9318
  const block = result.content[0];
9196
- let text13 = String(block.text ?? "");
9197
- const trunc = truncateForLLM(text13, RESPONSE_MAX_BYTES);
9319
+ let text14 = String(block.text ?? "");
9320
+ const trunc = truncateForLLM(text14, RESPONSE_MAX_BYTES);
9198
9321
  if (trunc.truncated) {
9199
- text13 = trunc.text + "\n\n... " + buildTruncationHint(
9322
+ text14 = trunc.text + "\n\n... " + buildTruncationHint(
9200
9323
  meta.toolName,
9201
9324
  meta.args,
9202
9325
  trunc.totalBytes,
@@ -9210,11 +9333,11 @@ function postprocessResult(result, meta) {
9210
9333
  const parts = [`took ${tookStr}`, sizeStr];
9211
9334
  if (meta.serverIdLabel) parts.push(`server: ${meta.serverIdLabel}`);
9212
9335
  if (meta.cached) parts.push("cached");
9213
- text13 = `${text13}
9336
+ text14 = `${text14}
9214
9337
 
9215
9338
  [${parts.join(", ")}]`;
9216
9339
  }
9217
- return { ...result, content: [{ ...block, text: text13 }] };
9340
+ return { ...result, content: [{ ...block, text: text14 }] };
9218
9341
  }
9219
9342
  function buildPipelineScript(commands, shell, marker, stopOnError) {
9220
9343
  if (shell === "powershell") {
@@ -11721,6 +11844,32 @@ var TOOLS = [
11721
11844
  }
11722
11845
  }
11723
11846
  },
11847
+ {
11848
+ name: "submit-team-memory",
11849
+ description: "Manually push a curated problem\u2192solution note into the team's searchable memory (agent_memory). Use ONLY when the user explicitly asks to push/share context to team memory (e.g. no agent-host daemon). Do NOT auto-submit. Do NOT dump raw chat. English; no secrets/PII. Pass repo for the current project. Distinct from wiki intake \u2014 this is searchable via search-team-memory.",
11850
+ inputSchema: {
11851
+ type: "object",
11852
+ properties: {
11853
+ title: {
11854
+ type: "string",
11855
+ description: "Short English title for the note (max 300 chars)."
11856
+ },
11857
+ content: {
11858
+ type: "string",
11859
+ description: "Problem / root cause / solution / context markdown (min ~40 chars, max ~24k stored)."
11860
+ },
11861
+ repo: {
11862
+ type: "string",
11863
+ description: "Git repository, e.g. 'github.com/MGSoftwareBV/fenicks' or 'github.com/MGSoftwareBV/mg-boiler'."
11864
+ },
11865
+ externalRef: {
11866
+ type: "string",
11867
+ description: "Optional client dedupe key; re-submitting the same ref upserts the row."
11868
+ }
11869
+ },
11870
+ required: ["title", "content", "repo"]
11871
+ }
11872
+ },
11724
11873
  {
11725
11874
  name: "search-wiki",
11726
11875
  description: "Search the team's curated knowledge wiki (the distilled layer ABOVE team memory): compiled pages about how systems actually work, customer setups/quirks, and known recurring issues with their standard fixes. Check this BEFORE search-team-memory \u2014 it is smaller, curated, and deduplicated, so a hit here is usually the complete answer. Every page lists its `sources` (team-memory ids, ticket numbers, commits): follow those with get-team-memory for full raw detail instead of broad searches. Fall back to search-team-memory when nothing relevant comes back or the page's `updated` date looks stale.",
@@ -12095,7 +12244,7 @@ async function executeToolCall(name, a, _serverId) {
12095
12244
  ]
12096
12245
  };
12097
12246
  }
12098
- const lines = data.hits.map((hit, index10) => {
12247
+ const lines = data.hits.map((hit, index11) => {
12099
12248
  const when = hit.lastMessageAt ? new Date(hit.lastMessageAt).toISOString().slice(0, 10) : "unknown date";
12100
12249
  const repo = hit.repo ?? "unknown repo";
12101
12250
  const sim = hit.similarity !== null ? ` \xB7 ${(hit.similarity * 100).toFixed(0)}% match` : "";
@@ -12105,7 +12254,7 @@ async function executeToolCall(name, a, _serverId) {
12105
12254
  ...Array.isArray(hit.tech) ? hit.tech.slice(0, 4) : []
12106
12255
  ].filter(Boolean);
12107
12256
  const tags = facets.length > 0 ? ` \xB7 ${facets.join(", ")}` : "";
12108
- return `${index10 + 1}. [${repo} \xB7 ${when}${sim}${tags}] ${hit.title}
12257
+ return `${index11 + 1}. [${repo} \xB7 ${when}${sim}${tags}] ${hit.title}
12109
12258
  id: ${hit.id}
12110
12259
  ${(hit.summary ?? hit.snippet).replace(/\s+/g, " ").slice(0, 500)}`;
12111
12260
  });
@@ -12207,6 +12356,71 @@ ${facets.join(", ")}` : ""),
12207
12356
  ]
12208
12357
  };
12209
12358
  }
12359
+ // ----- Team memory: manual curated submit (no daemon) -----
12360
+ case "submit-team-memory": {
12361
+ const title = typeof a.title === "string" ? a.title.trim() : "";
12362
+ const content = typeof a.content === "string" ? a.content.trim() : "";
12363
+ const repo = typeof a.repo === "string" ? a.repo.trim() : "";
12364
+ const externalRef = typeof a.externalRef === "string" ? a.externalRef.trim() : void 0;
12365
+ if (!title || !content || !repo) {
12366
+ return {
12367
+ content: [
12368
+ {
12369
+ type: "text",
12370
+ text: "Error: title, content, and repo are required."
12371
+ }
12372
+ ]
12373
+ };
12374
+ }
12375
+ if (content.length < 40) {
12376
+ return {
12377
+ content: [
12378
+ {
12379
+ type: "text",
12380
+ text: "Error: content must be at least 40 characters (curated problem\u2192solution, not a one-liner)."
12381
+ }
12382
+ ]
12383
+ };
12384
+ }
12385
+ const res = await fetch(`${dashboardBaseUrl}/api/team-memory/submit`, {
12386
+ method: "POST",
12387
+ headers: {
12388
+ "content-type": "application/json",
12389
+ authorization: `Bearer ${apiKey}`
12390
+ },
12391
+ body: JSON.stringify({
12392
+ title,
12393
+ content,
12394
+ gitRepository: repo,
12395
+ ...externalRef ? { externalRef } : {}
12396
+ })
12397
+ });
12398
+ if (!res.ok) {
12399
+ const detail = await res.text().catch(() => "");
12400
+ return {
12401
+ content: [
12402
+ {
12403
+ type: "text",
12404
+ text: `Error: team-memory submit failed (${res.status}). ${detail.slice(0, 400)}`
12405
+ }
12406
+ ]
12407
+ };
12408
+ }
12409
+ const data = await res.json();
12410
+ return {
12411
+ content: [
12412
+ {
12413
+ type: "text",
12414
+ text: `Submitted to team memory${data.upserted ? "" : " (unchanged \u2014 identical content)"}.
12415
+ id: ${data.id}
12416
+ localConversationId: ${data.localConversationId}
12417
+ repo: ${repo}
12418
+ summarized: ${data.summarized}, embedded: ${data.embedded}
12419
+ Searchable via search-team-memory once indexing completes (usually immediate).`
12420
+ }
12421
+ ]
12422
+ };
12423
+ }
12210
12424
  // ----- Knowledge wiki: search -----
12211
12425
  case "search-wiki": {
12212
12426
  const query = typeof a.query === "string" ? a.query.trim() : "";
@@ -12237,22 +12451,22 @@ ${facets.join(", ")}` : ""),
12237
12451
  };
12238
12452
  }
12239
12453
  const data = await res.json();
12240
- if (data.count === 0) {
12454
+ if (data.count === 0 || data.confident === false) {
12241
12455
  return {
12242
12456
  content: [
12243
12457
  {
12244
12458
  type: "text",
12245
- text: "No wiki pages matched. Fall back to search-team-memory for raw history \u2014 repeated misses here become insight-scout signals automatically."
12459
+ text: data.hint ?? "No confident wiki answer. Fall back to search-team-memory for raw history \u2014 repeated misses here become insight-scout / gap-filler signals automatically."
12246
12460
  }
12247
12461
  ]
12248
12462
  };
12249
12463
  }
12250
- const lines = data.hits.map((hit, index10) => {
12464
+ const lines = data.hits.map((hit, index11) => {
12251
12465
  const sim = hit.similarity !== null ? ` \xB7 ${Math.round(hit.similarity * 100)}% match` : "";
12252
12466
  const tags = hit.tags.length ? ` \xB7 ${hit.tags.slice(0, 5).join(", ")}` : "";
12253
12467
  const sources = hit.sources.length ? `
12254
12468
  sources: ${hit.sources.slice(0, 6).map((s) => `${s.type}:${s.ref}`).join(", ")}` : "";
12255
- return `${index10 + 1}. [${hit.status} \xB7 updated ${hit.pageUpdatedOn ?? "unknown"}${sim}${tags}] ${hit.title}
12469
+ return `${index11 + 1}. [${hit.status} \xB7 updated ${hit.pageUpdatedOn ?? "unknown"}${sim}${tags}] ${hit.title}
12256
12470
  slug: ${hit.slug}${sources}
12257
12471
  ${(hit.summary ?? hit.snippet).replace(/\s+/g, " ").slice(0, 400)}`;
12258
12472
  });
@@ -13459,8 +13673,8 @@ ${sample.join("\n")}${files.length > 5 ? `
13459
13673
  };
13460
13674
  const filtered = sortRows(applyFilter(only.rows));
13461
13675
  if (format === "json") {
13462
- const text14 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
13463
- return { content: [{ type: "text", text: text14 }] };
13676
+ const text15 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
13677
+ return { content: [{ type: "text", text: text15 }] };
13464
13678
  }
13465
13679
  if (groupByProject) {
13466
13680
  const groups = /* @__PURE__ */ new Map();
@@ -13487,8 +13701,8 @@ ${sample.join("\n")}${files.length > 5 ? `
13487
13701
  }
13488
13702
  const header = `${"NAMES".padEnd(36)} ${"IMAGE".padEnd(40)} ${"STATUS".padEnd(24)} ${"HEALTH".padEnd(10)} ${"PORTS".padEnd(40)} PROJECT`;
13489
13703
  const body = filtered.map((r) => `${fmtRow(r)} ${r.Project || ""}`);
13490
- const text13 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
13491
- return { content: [{ type: "text", text: text13 }] };
13704
+ const text14 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
13705
+ return { content: [{ type: "text", text: text14 }] };
13492
13706
  }
13493
13707
  if (format === "json") {
13494
13708
  const lines = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mgsoftwarebv/mg-dashboard-mcp",
3
- "version": "7.4.7",
3
+ "version": "7.4.9",
4
4
  "description": "MCP Server for MG Dashboard - SSH, SFTP, Docker, domains, DNS, and environment config tools for Cursor",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",