@mgsoftwarebv/mg-dashboard-mcp 7.4.14 → 7.4.16

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 +290 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1136,6 +1136,128 @@ async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSec
1136
1136
  }]
1137
1137
  };
1138
1138
  }
1139
+
1140
+ // src/github-code-access-tools.ts
1141
+ var GITHUB_CODE_ACCESS_TOOLS = [
1142
+ {
1143
+ name: "get_mg_dashboard_file",
1144
+ description: "Read ONE source file at a git ref for an allowlisted GitHub org/repo (CODE_BATTLE_GITHUB_ORGS, default MGSoftwareBV). Use after get_mg_dashboard_commits when commit subjects are not enough to judge exists / polish / new. Returns metadata + text content (secrets redacted). Credential paths (.env, keys) are denied. Binary files return { binary: true } with no payload. Full diffs are never attached to get_mg_dashboard_commits \u2014 use get_mg_dashboard_diff for a compact patch.",
1145
+ inputSchema: {
1146
+ type: "object",
1147
+ properties: {
1148
+ repository: {
1149
+ type: "string",
1150
+ description: "owner/repo, e.g. MGSoftwareBV/mg-dashboard. Org must be allowlisted."
1151
+ },
1152
+ ref: {
1153
+ type: "string",
1154
+ description: "Commit sha, branch, or tag."
1155
+ },
1156
+ path: {
1157
+ type: "string",
1158
+ description: "File path inside the repo (not a directory)."
1159
+ }
1160
+ },
1161
+ required: ["repository", "ref", "path"]
1162
+ }
1163
+ },
1164
+ {
1165
+ name: "get_mg_dashboard_diff",
1166
+ description: "Fetch a COMPACT unified diff for one commit sha XOR one pull request number, same org allowlist as get_mg_dashboard_file. Returns per-file path/status/+/- and a truncated patch (secrets redacted). Binary files are omitted. Does not change get_mg_dashboard_commits (metadata-only by design).",
1167
+ inputSchema: {
1168
+ type: "object",
1169
+ properties: {
1170
+ repository: {
1171
+ type: "string",
1172
+ description: "owner/repo, e.g. MGSoftwareBV/mg-dashboard. Org must be allowlisted."
1173
+ },
1174
+ sha: {
1175
+ type: "string",
1176
+ description: "Commit sha. Provide exactly one of sha or pullNumber."
1177
+ },
1178
+ pullNumber: {
1179
+ type: "number",
1180
+ description: "Pull request number. Provide exactly one of sha or pullNumber."
1181
+ }
1182
+ },
1183
+ required: ["repository"]
1184
+ }
1185
+ }
1186
+ ];
1187
+ async function handleGithubCodeAccessTool(name, args2, ctx) {
1188
+ if (name === "get_mg_dashboard_file") {
1189
+ const repository = typeof args2.repository === "string" ? args2.repository.trim() : "";
1190
+ const ref = typeof args2.ref === "string" ? args2.ref.trim() : "";
1191
+ const path = typeof args2.path === "string" ? args2.path.trim() : "";
1192
+ if (!repository || !ref || !path) {
1193
+ return {
1194
+ content: [
1195
+ {
1196
+ type: "text",
1197
+ text: "Error: repository, ref and path are required"
1198
+ }
1199
+ ]
1200
+ };
1201
+ }
1202
+ return proxyJson(ctx, "/api/activity/file", { repository, ref, path });
1203
+ }
1204
+ if (name === "get_mg_dashboard_diff") {
1205
+ const repository = typeof args2.repository === "string" ? args2.repository.trim() : "";
1206
+ if (!repository) {
1207
+ return {
1208
+ content: [
1209
+ { type: "text", text: "Error: repository is required" }
1210
+ ]
1211
+ };
1212
+ }
1213
+ const sha = typeof args2.sha === "string" ? args2.sha.trim() : void 0;
1214
+ const pullNumber = typeof args2.pullNumber === "number" ? args2.pullNumber : void 0;
1215
+ const hasSha = Boolean(sha);
1216
+ const hasPr = pullNumber != null;
1217
+ if (hasSha === hasPr) {
1218
+ return {
1219
+ content: [
1220
+ {
1221
+ type: "text",
1222
+ text: "Error: provide exactly one of sha or pullNumber"
1223
+ }
1224
+ ]
1225
+ };
1226
+ }
1227
+ return proxyJson(ctx, "/api/activity/diff", {
1228
+ repository,
1229
+ ...hasSha ? { sha } : { pullNumber }
1230
+ });
1231
+ }
1232
+ return {
1233
+ content: [{ type: "text", text: `Error: unknown tool ${name}` }]
1234
+ };
1235
+ }
1236
+ async function proxyJson(ctx, route, body) {
1237
+ const res = await fetch(`${ctx.dashboardBaseUrl}${route}`, {
1238
+ method: "POST",
1239
+ headers: {
1240
+ "content-type": "application/json",
1241
+ authorization: `Bearer ${ctx.apiKey}`
1242
+ },
1243
+ body: JSON.stringify(body)
1244
+ });
1245
+ if (!res.ok) {
1246
+ const detail = await res.text().catch(() => "");
1247
+ return {
1248
+ content: [
1249
+ {
1250
+ type: "text",
1251
+ text: `Error: mg-dashboard ${route} failed (${res.status}). ${detail.slice(0, 300)}`
1252
+ }
1253
+ ]
1254
+ };
1255
+ }
1256
+ const data = await res.json();
1257
+ return {
1258
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
1259
+ };
1260
+ }
1139
1261
  var ALGORITHM = "aes-256-gcm";
1140
1262
  var IV_LENGTH = 16;
1141
1263
  var AUTH_TAG_LENGTH = 16;
@@ -5663,8 +5785,7 @@ pgTable(
5663
5785
  ),
5664
5786
  index("idx_agent_memory_repo").on(table.gitRepository),
5665
5787
  index("idx_agent_memory_user").on(table.userId),
5666
- index("idx_agent_memory_kind").on(table.kind),
5667
- index("idx_agent_memory_category").on(table.category)
5788
+ index("idx_agent_memory_kind").on(table.kind)
5668
5789
  ]
5669
5790
  );
5670
5791
  var agentWorldAgent = pgTable(
@@ -5712,7 +5833,12 @@ pgTable(
5712
5833
  (table) => [
5713
5834
  index("idx_agent_world_event_created").on(table.createdAt),
5714
5835
  index("idx_agent_world_event_type").on(table.type),
5715
- index("idx_agent_world_event_agent").on(table.agentId)
5836
+ index("idx_agent_world_event_agent").on(table.agentId),
5837
+ // Diary / per-agent feed: WHERE agent_id ORDER BY created_at DESC LIMIT N
5838
+ index("idx_agent_world_event_agent_created").on(
5839
+ table.agentId,
5840
+ table.createdAt
5841
+ )
5716
5842
  ]
5717
5843
  );
5718
5844
  pgTable(
@@ -10547,6 +10673,8 @@ var RESPONSE_MAX_BYTES = 8192;
10547
10673
  var NO_FOOTER_TOOLS = /* @__PURE__ */ new Set();
10548
10674
  var RAW_JSON_TOOLS = /* @__PURE__ */ new Set([
10549
10675
  "get_mg_dashboard_commits",
10676
+ "get_mg_dashboard_file",
10677
+ "get_mg_dashboard_diff",
10550
10678
  "extract_article",
10551
10679
  // Content family (2026-DASMG-040/041/042) — compact JSON consumed verbatim.
10552
10680
  // Consolidated action-based tools (the Cursor surface); each proxies to the
@@ -12717,6 +12845,7 @@ var TOOLS = [
12717
12845
  required: ["actor", "dateFrom", "dateTo"]
12718
12846
  }
12719
12847
  },
12848
+ ...GITHUB_CODE_ACCESS_TOOLS,
12720
12849
  {
12721
12850
  name: "extract_article",
12722
12851
  description: "Extract readable article/content data from ONE PUBLIC URL, with a browser-render fallback for pages that block a normal fetch (403) or only render with JavaScript (ticket 2026-DASMG-039). Returns title, author, publishedAt, source, summary, facts[], optional rawText (capped), canonicalUrl, the `extractionMethod` used (http | browser | jsonld | rss | amp | metadata_only) and an explicit `limitations` array (e.g. blocked_without_browser, paywall_detected, partial_content, rawtext_truncated). PUBLIC URLs only \u2014 SSRF-guarded (private/loopback/metadata addresses rejected), no paywall bypass, no credentials; the source/canonical URL is always preserved. Use it to turn a news/article link into safe factual source material for agents; it never auto-publishes or rewrites. For several sources + cross-checking, use `research` with action=topic.",
@@ -13328,6 +13457,42 @@ var TOOLS = [
13328
13457
  required: ["slug", "detail"]
13329
13458
  }
13330
13459
  },
13460
+ {
13461
+ name: "wiki-review-queue",
13462
+ description: "List the knowledge-wiki review queue without the Dashboard UI: open flags first (they jump the queue), then needs-work pages, oldest drafts, and proposed insights. Each item includes slug, status, folder, and updated date. Read a candidate with get-wiki-page, then finish it with wiki-review (action=review for durable pages, action=accept for wiki/insights/*). Folder-filtered to the API key owner's wiki areas.",
13463
+ inputSchema: {
13464
+ type: "object",
13465
+ properties: {
13466
+ limit: {
13467
+ type: "number",
13468
+ description: "Max items per bucket (flags, needs-work, drafts, proposed insights). 1-100, default 50."
13469
+ }
13470
+ }
13471
+ }
13472
+ },
13473
+ {
13474
+ name: "wiki-review",
13475
+ description: "Finish a knowledge-wiki queue item without the Dashboard UI. Same write as wiki-graph: updates page status, resolves open flags on that slug, and closes a linked review-ticket when promoting. action=review \u2192 status reviewed on a durable page (not wiki/insights/*). action=accept \u2192 status accepted on a wiki/insights/* proposal. Read the page with get-wiki-page first. Does not edit page body \u2014 only status/frontmatter. Does not resolve Refront pipeline tickets on behalf of operators.",
13476
+ inputSchema: {
13477
+ type: "object",
13478
+ properties: {
13479
+ slug: {
13480
+ type: "string",
13481
+ description: "Page slug from wiki-review-queue or get-wiki-page, e.g. 'wiki/engineering/team-memory' or 'wiki/insights/2026-08-14-example'."
13482
+ },
13483
+ action: {
13484
+ type: "string",
13485
+ enum: ["accept", "review"],
13486
+ description: "review = mark a durable page reviewed; accept = accept an insight proposal."
13487
+ },
13488
+ note: {
13489
+ type: "string",
13490
+ description: "Optional note appended under ## Review notes (max 2000 chars)."
13491
+ }
13492
+ },
13493
+ required: ["slug", "action"]
13494
+ }
13495
+ },
13331
13496
  // ----- Cursor Remote (Agent Control) -----
13332
13497
  {
13333
13498
  name: "cursor-remote-list",
@@ -13629,6 +13794,12 @@ async function executeToolCall(name, a, _serverId) {
13629
13794
  content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
13630
13795
  };
13631
13796
  }
13797
+ case "get_mg_dashboard_file":
13798
+ case "get_mg_dashboard_diff":
13799
+ return handleGithubCodeAccessTool(name, a, {
13800
+ dashboardBaseUrl,
13801
+ apiKey: apiKey ?? ""
13802
+ });
13632
13803
  // ----- Public-web extraction (article tools) -----
13633
13804
  case "extract_article": {
13634
13805
  const url = typeof a.url === "string" ? a.url.trim() : "";
@@ -14175,6 +14346,122 @@ Archived sources (${archiveOnly.length}):
14175
14346
  ]
14176
14347
  };
14177
14348
  }
14349
+ case "wiki-review-queue": {
14350
+ let formatItems2 = function(items) {
14351
+ if (items.length === 0) return "(none)";
14352
+ return items.map(
14353
+ (item, index19) => `${index19 + 1}. [${item.status} \xB7 ${item.folder} \xB7 updated ${item.updated ?? "unknown"}] ${item.title}
14354
+ slug: ${item.slug}`
14355
+ ).join("\n");
14356
+ };
14357
+ var formatItems = formatItems2;
14358
+ const limit = typeof a.limit === "number" ? a.limit : void 0;
14359
+ const res = await fetch(`${dashboardBaseUrl}/api/wiki/review-queue`, {
14360
+ method: "POST",
14361
+ headers: {
14362
+ "content-type": "application/json",
14363
+ authorization: `Bearer ${apiKey}`
14364
+ },
14365
+ body: JSON.stringify({ limit })
14366
+ });
14367
+ if (!res.ok) {
14368
+ const detail = await res.text().catch(() => "");
14369
+ return {
14370
+ content: [
14371
+ {
14372
+ type: "text",
14373
+ text: `Error: wiki review queue failed (${res.status}). ${detail.slice(0, 300)}`
14374
+ }
14375
+ ]
14376
+ };
14377
+ }
14378
+ const data = await res.json();
14379
+ const countLine = Object.entries(data.counts).sort(([left], [right]) => left.localeCompare(right)).map(([status, count]) => `${status} ${count}`).join(" \xB7 ");
14380
+ const flagLines = data.flags.length === 0 ? "(none)" : data.flags.map((flag, index19) => {
14381
+ const detail = flag.detail.replace(/\s+/g, " ").slice(0, 240);
14382
+ return `${index19 + 1}. [${flag.kind} \xB7 ${flag.folder}${flag.status ? ` \xB7 page ${flag.status}` : ""}] ${flag.title}
14383
+ slug: ${flag.slug}
14384
+ ${detail}`;
14385
+ }).join("\n");
14386
+ return {
14387
+ content: [
14388
+ {
14389
+ type: "text",
14390
+ text: `Wiki review queue${countLine ? ` (${countLine})` : ""}.
14391
+ Open flags jump the queue. Read a slug with get-wiki-page, then wiki-review action=review (durable page) or action=accept (insight).
14392
+
14393
+ Flags (${data.flags.length})
14394
+ ${flagLines}
14395
+
14396
+ Needs-work (${data.needsWork.length})
14397
+ ${formatItems2(data.needsWork)}
14398
+
14399
+ Drafts (${data.drafts.length})
14400
+ ${formatItems2(data.drafts)}
14401
+
14402
+ Proposed insights (${data.proposedInsights.length})
14403
+ ${formatItems2(data.proposedInsights)}`
14404
+ }
14405
+ ]
14406
+ };
14407
+ }
14408
+ case "wiki-review": {
14409
+ const slug = typeof a.slug === "string" ? a.slug.trim() : "";
14410
+ const action = typeof a.action === "string" ? a.action.trim() : "";
14411
+ if (!slug || action !== "accept" && action !== "review") {
14412
+ return {
14413
+ content: [
14414
+ {
14415
+ type: "text",
14416
+ text: 'Error: slug and action ("accept" or "review") are required'
14417
+ }
14418
+ ]
14419
+ };
14420
+ }
14421
+ const note = typeof a.note === "string" ? a.note.trim() : "";
14422
+ const res = await fetch(`${dashboardBaseUrl}/api/wiki/review`, {
14423
+ method: "POST",
14424
+ headers: {
14425
+ "content-type": "application/json",
14426
+ authorization: `Bearer ${apiKey}`
14427
+ },
14428
+ body: JSON.stringify({
14429
+ slug,
14430
+ action,
14431
+ ...note ? { note } : {}
14432
+ })
14433
+ });
14434
+ if (!res.ok) {
14435
+ const errDetail = await res.text().catch(() => "");
14436
+ return {
14437
+ content: [
14438
+ {
14439
+ type: "text",
14440
+ text: `Error: wiki review failed (${res.status}). ${errDetail.slice(0, 300)}`
14441
+ }
14442
+ ]
14443
+ };
14444
+ }
14445
+ const data = await res.json();
14446
+ if (!data.found || !data.result) {
14447
+ return {
14448
+ content: [
14449
+ {
14450
+ type: "text",
14451
+ text: `No wiki page found for slug "${slug}" (or it is outside this API key's wiki areas). Check the slug via wiki-review-queue or get-wiki-page.`
14452
+ }
14453
+ ]
14454
+ };
14455
+ }
14456
+ return {
14457
+ content: [
14458
+ {
14459
+ type: "text",
14460
+ text: `Set ${data.result.slug} to ${data.result.status}. Open flags on this slug are resolved.` + (action === "accept" ? " Insight is accepted \u2014 executor/distiller can act on it; do not resolve Refront pipeline tickets for Sidney/Jordan." : " Durable page is reviewed.")
14461
+ }
14462
+ ]
14463
+ };
14464
+ }
14178
14465
  // ----- Cursor Remote (Agent Control) -----
14179
14466
  case "cursor-remote-list": {
14180
14467
  const onlineOnly = a.onlineOnly === true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mgsoftwarebv/mg-dashboard-mcp",
3
- "version": "7.4.14",
3
+ "version": "7.4.16",
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",