@mgsoftwarebv/mg-dashboard-mcp 7.4.24 → 7.4.25

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 +116 -22
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -724,10 +724,10 @@ var TRIGGER_TOOL_MODULE_MAP = {
724
724
  "trigger-env": "settings"
725
725
  };
726
726
  async function discoverInstance(projectSlug, conn, proxy, sshExec2) {
727
- const sql35 = `SELECT re.\\"apiKey\\" || '~~' || p.\\"externalRef\\" FROM \\"RuntimeEnvironment\\" re JOIN \\"Project\\" p ON re.\\"projectId\\" = p.id WHERE p.slug='${projectSlug}' AND re.slug='prod' LIMIT 1`;
727
+ const sql36 = `SELECT re.\\"apiKey\\" || '~~' || p.\\"externalRef\\" FROM \\"RuntimeEnvironment\\" re JOIN \\"Project\\" p ON re.\\"projectId\\" = p.id WHERE p.slug='${projectSlug}' AND re.slug='prod' LIMIT 1`;
728
728
  const cmd = [
729
729
  `PORT=$(docker port "${WA_CONTAINER}" 3000/tcp 2>/dev/null | head -1 | sed 's/.*://')`,
730
- `ROW=$(docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql35}" 2>/dev/null | tr -d '[:space:]')`,
730
+ `ROW=$(docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql36}" 2>/dev/null | tr -d '[:space:]')`,
731
731
  'echo "$PORT|$ROW"'
732
732
  ].join(" && ");
733
733
  const result = await sshExec2(conn, cmd, proxy);
@@ -749,8 +749,8 @@ async function discoverInstance(projectSlug, conn, proxy, sshExec2) {
749
749
  return { port, apiKey: apiKey2, projectRef: projectRef || "" };
750
750
  }
751
751
  async function fetchRunLogs(runId, conn, proxy, sshExec2) {
752
- const sql35 = `SELECT level, message, \\"isError\\", \\"createdAt\\" FROM \\"TaskEvent\\" WHERE \\"runId\\" = '${runId}' AND level IN ('INFO','WARN','ERROR','DEBUG','LOG','TRACE') ORDER BY \\"startTime\\" ASC LIMIT 200`;
753
- const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql35}" 2>/dev/null`;
752
+ const sql36 = `SELECT level, message, \\"isError\\", \\"createdAt\\" FROM \\"TaskEvent\\" WHERE \\"runId\\" = '${runId}' AND level IN ('INFO','WARN','ERROR','DEBUG','LOG','TRACE') ORDER BY \\"startTime\\" ASC LIMIT 200`;
753
+ const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql36}" 2>/dev/null`;
754
754
  const result = await sshExec2(conn, cmd, proxy);
755
755
  const output = result.stdout.trim();
756
756
  if (!output) return "";
@@ -832,8 +832,8 @@ async function handleTriggerTool(name, args2, deps) {
832
832
  switch (name) {
833
833
  // -----------------------------------------------------------------
834
834
  case "trigger-list": {
835
- const sql35 = 'SELECT slug, name FROM \\"Project\\" ORDER BY name';
836
- const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql35}" 2>/dev/null`;
835
+ const sql36 = 'SELECT slug, name FROM \\"Project\\" ORDER BY name';
836
+ const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql36}" 2>/dev/null`;
837
837
  const result = await sshExec2(conn, cmd, proxy);
838
838
  const output = result.stdout.trim();
839
839
  if (!output) {
@@ -1317,6 +1317,56 @@ async function proxyJson(ctx, route, body) {
1317
1317
  };
1318
1318
  }
1319
1319
 
1320
+ // src/git-credential-tools.ts
1321
+ var GIT_CREDENTIAL_TOOL_NAME = "git-credential";
1322
+ var GIT_CREDENTIAL_TOOLS = [
1323
+ {
1324
+ name: GIT_CREDENTIAL_TOOL_NAME,
1325
+ description: "Issue a 15-minute mggit access token for git.mgsoftware.nl (username=token, Basic password). Bound to owner/name + write bit. Intended for git credential helpers and agents that need to clone/fetch/push. Not a git runner \u2014 do not use this to execute git commands. Requires module git_hosting.",
1326
+ inputSchema: {
1327
+ type: "object",
1328
+ properties: {
1329
+ repo: {
1330
+ type: "string",
1331
+ description: "Repository as owner/name (e.g. MGSoftwareBV/mg-dashboard)."
1332
+ },
1333
+ write: {
1334
+ type: "boolean",
1335
+ description: "Request push rights (default true). Fetch-only: false."
1336
+ }
1337
+ },
1338
+ required: ["repo"]
1339
+ }
1340
+ }
1341
+ ];
1342
+ async function handleGitCredentialTool(args2, ctx) {
1343
+ const repo = typeof args2.repo === "string" ? args2.repo.trim() : "";
1344
+ if (!repo) {
1345
+ return { content: [{ type: "text", text: "Error: repo is required (owner/name)" }] };
1346
+ }
1347
+ const write = args2.write !== false;
1348
+ const res = await fetch(`${ctx.dashboardBaseUrl.replace(/\/$/, "")}/api/git/credential`, {
1349
+ method: "POST",
1350
+ headers: {
1351
+ "content-type": "application/json",
1352
+ authorization: `Bearer ${ctx.apiKey}`
1353
+ },
1354
+ body: JSON.stringify({ repo, write })
1355
+ });
1356
+ const text23 = await res.text().catch(() => "");
1357
+ if (!res.ok) {
1358
+ return {
1359
+ content: [
1360
+ {
1361
+ type: "text",
1362
+ text: `Error: git-credential failed (${res.status}). ${text23.slice(0, 300)}`
1363
+ }
1364
+ ]
1365
+ };
1366
+ }
1367
+ return { content: [{ type: "text", text: text23 || "{}" }] };
1368
+ }
1369
+
1320
1370
  // src/mailserver-tools.ts
1321
1371
  var MAILSERVER_TOOL_NAME = "mailserver";
1322
1372
  var MAILSERVER_ACTIONS = [
@@ -7331,6 +7381,26 @@ pgTable(
7331
7381
  index("git_access_log_op_status_idx").on(table.op, table.status)
7332
7382
  ]
7333
7383
  );
7384
+ pgTable(
7385
+ "git_credential_grant",
7386
+ {
7387
+ id: uuid("id").primaryKey().defaultRandom(),
7388
+ tokenHash: text("token_hash").notNull(),
7389
+ userId: uuid("user_id").notNull(),
7390
+ apiKeyId: uuid("api_key_id").notNull(),
7391
+ repo: text("repo").notNull(),
7392
+ write: boolean("write").notNull().default(true),
7393
+ principal: text("principal").notNull(),
7394
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
7395
+ issuedIp: text("issued_ip").notNull().default(""),
7396
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
7397
+ },
7398
+ (table) => [
7399
+ uniqueIndex("git_credential_grant_token_hash_uidx").on(table.tokenHash),
7400
+ index("git_credential_grant_expires_idx").on(table.expiresAt),
7401
+ index("git_credential_grant_user_idx").on(table.userId, table.expiresAt)
7402
+ ]
7403
+ );
7334
7404
  var directoryLinkType = pgEnum("directory_link_type", [
7335
7405
  "dofollow",
7336
7406
  "nofollow",
@@ -8135,6 +8205,22 @@ pgTable(
8135
8205
  )
8136
8206
  ]
8137
8207
  );
8208
+
8209
+ // ../platform/dist/utils/permissions.js
8210
+ var MODULE_KEYS = [
8211
+ "users",
8212
+ "ssh_servers",
8213
+ "wiki",
8214
+ "ci_cd",
8215
+ "settings",
8216
+ "cursor_remote",
8217
+ "code_battle",
8218
+ "git_hosting"
8219
+ ];
8220
+ ({
8221
+ modules: Object.fromEntries(MODULE_KEYS.map((k) => [k, false]))});
8222
+ ({
8223
+ modules: Object.fromEntries(MODULE_KEYS.map((k) => [k, true]))});
8138
8224
  var SSH_POOL_IDLE_MS = 6e4;
8139
8225
  var sshPool = /* @__PURE__ */ new Map();
8140
8226
  function poolKey(options) {
@@ -10688,7 +10774,7 @@ async function writeAuditLog(entry) {
10688
10774
  );
10689
10775
  }
10690
10776
  }
10691
- var MODULE_KEYS = [
10777
+ var MODULE_KEYS2 = [
10692
10778
  "users",
10693
10779
  "ssh_servers",
10694
10780
  "wiki",
@@ -10699,9 +10785,9 @@ var MODULE_KEYS = [
10699
10785
  "code_battle",
10700
10786
  "git_hosting"
10701
10787
  ];
10702
- var FULL_PERMISSIONS = {
10788
+ var FULL_PERMISSIONS2 = {
10703
10789
  modules: Object.fromEntries(
10704
- MODULE_KEYS.map((k) => [k, true])
10790
+ MODULE_KEYS2.map((k) => [k, true])
10705
10791
  ),
10706
10792
  resources: { ssh_servers: ["*"] }
10707
10793
  };
@@ -10709,12 +10795,12 @@ function parsePermissions(raw) {
10709
10795
  if (!raw || typeof raw !== "object") return null;
10710
10796
  return raw;
10711
10797
  }
10712
- function resolvePermissions(roleName, roleDefaults, userOverrides) {
10713
- if (roleName === "superadmin") return FULL_PERMISSIONS;
10798
+ function resolvePermissions2(roleName, roleDefaults, userOverrides) {
10799
+ if (roleName === "superadmin") return FULL_PERMISSIONS2;
10714
10800
  const base = parsePermissions(roleDefaults);
10715
10801
  const overrides = parsePermissions(userOverrides);
10716
10802
  const modules = {};
10717
- for (const key of MODULE_KEYS) {
10803
+ for (const key of MODULE_KEYS2) {
10718
10804
  const userVal = overrides?.modules?.[key];
10719
10805
  const roleVal = base?.modules?.[key];
10720
10806
  modules[key] = userVal !== void 0 ? userVal : roleVal !== void 0 ? roleVal : false;
@@ -10763,6 +10849,7 @@ var TOOL_MODULE_MAP = {
10763
10849
  "cursor-remote-list": "cursor_remote",
10764
10850
  "cursor-remote-run": "cursor_remote",
10765
10851
  "ai-company": "ssh_servers",
10852
+ "git-credential": "git_hosting",
10766
10853
  // cursor-skill intentionally unmapped — any valid API key may pull shared skills
10767
10854
  ...TRIGGER_TOOL_MODULE_MAP
10768
10855
  };
@@ -10906,7 +10993,7 @@ async function validateApiKey(key) {
10906
10993
  const roleName = userData.role_name || "user";
10907
10994
  const roleDefaults = userData.role_default_permissions ?? {};
10908
10995
  const userOverrides = userData.permissions ?? null;
10909
- const permissions = resolvePermissions(roleName, roleDefaults, userOverrides);
10996
+ const permissions = resolvePermissions2(roleName, roleDefaults, userOverrides);
10910
10997
  const allowedServerIds = intersectServerAccess(
10911
10998
  data.allowed_server_ids,
10912
10999
  permissions.resources.ssh_servers
@@ -10917,9 +11004,9 @@ async function validateApiKey(key) {
10917
11004
  WHERE id = ${data.id}
10918
11005
  AND (last_used_at IS NULL OR last_used_at < now() - interval '60 seconds')
10919
11006
  `);
10920
- const moduleCount = MODULE_KEYS.filter((k) => permissions.modules[k]).length;
11007
+ const moduleCount = MODULE_KEYS2.filter((k) => permissions.modules[k]).length;
10921
11008
  console.error(
10922
- `Authenticated as user ${data.created_by} (role: ${roleName}, modules: ${moduleCount}/${MODULE_KEYS.length})`
11009
+ `Authenticated as user ${data.created_by} (role: ${roleName}, modules: ${moduleCount}/${MODULE_KEYS2.length})`
10923
11010
  );
10924
11011
  return {
10925
11012
  apiKeyId: data.id,
@@ -11153,7 +11240,7 @@ async function validateSshKey(pubkeyPathInput, expectedApiKeyId) {
11153
11240
  const roleName = userData.role_name || "user";
11154
11241
  const roleDefaults = userData.role_default_permissions ?? {};
11155
11242
  const userOverrides = userData.permissions ?? null;
11156
- const permissions = resolvePermissions(roleName, roleDefaults, userOverrides);
11243
+ const permissions = resolvePermissions2(roleName, roleDefaults, userOverrides);
11157
11244
  const allowedServerIds = intersectServerAccess(
11158
11245
  apiRow.allowed_server_ids,
11159
11246
  permissions.resources.ssh_servers
@@ -11170,9 +11257,9 @@ async function validateSshKey(pubkeyPathInput, expectedApiKeyId) {
11170
11257
  AND (last_used_at IS NULL OR last_used_at < now() - interval '60 seconds')`
11171
11258
  )
11172
11259
  ]);
11173
- const moduleCount = MODULE_KEYS.filter((k) => permissions.modules[k]).length;
11260
+ const moduleCount = MODULE_KEYS2.filter((k) => permissions.modules[k]).length;
11174
11261
  console.error(
11175
- `Authenticated via SSH key "${keyRow.name}" (fp ${fingerprint.slice(0, 24)}...) as user ${apiRow.created_by} (role: ${roleName}, modules: ${moduleCount}/${MODULE_KEYS.length})`
11262
+ `Authenticated via SSH key "${keyRow.name}" (fp ${fingerprint.slice(0, 24)}...) as user ${apiRow.created_by} (role: ${roleName}, modules: ${moduleCount}/${MODULE_KEYS2.length})`
11176
11263
  );
11177
11264
  return {
11178
11265
  apiKeyId: apiRow.id,
@@ -13453,11 +13540,11 @@ CREATE TABLE IF NOT EXISTS _mcp_migrations (
13453
13540
  applied_by TEXT
13454
13541
  );
13455
13542
  `.trim();
13456
- function normaliseMigrationSql(sql35) {
13457
- return sql35.replace(/\r\n/g, "\n").trim() + "\n";
13543
+ function normaliseMigrationSql(sql36) {
13544
+ return sql36.replace(/\r\n/g, "\n").trim() + "\n";
13458
13545
  }
13459
- function migrationSha256(sql35) {
13460
- return createHash("sha256").update(sql35.replace(/\r\n/g, "\n"), "utf8").digest("hex");
13546
+ function migrationSha256(sql36) {
13547
+ return createHash("sha256").update(sql36.replace(/\r\n/g, "\n"), "utf8").digest("hex");
13461
13548
  }
13462
13549
  function dollarQuoteTag(value) {
13463
13550
  let tag = "_mcp";
@@ -15469,6 +15556,7 @@ var TOOLS = [
15469
15556
  }
15470
15557
  },
15471
15558
  ...MAILSERVER_TOOLS,
15559
+ ...GIT_CREDENTIAL_TOOLS,
15472
15560
  ...HERMES_COMPANY_TOOLS,
15473
15561
  // ----- Trigger.dev -----
15474
15562
  ...TRIGGER_TOOLS
@@ -19237,6 +19325,12 @@ Install: GET https://dashboard.mgsoftware.nl/api/downloads/versions.txt then \u2
19237
19325
  token
19238
19326
  });
19239
19327
  }
19328
+ case GIT_CREDENTIAL_TOOL_NAME: {
19329
+ return handleGitCredentialTool(a, {
19330
+ dashboardBaseUrl,
19331
+ apiKey: apiKey ?? ""
19332
+ });
19333
+ }
19240
19334
  case HERMES_COMPANY_TOOL_NAME: {
19241
19335
  return handleHermesCompanyTool(a, {
19242
19336
  userId: ctx.userId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mgsoftwarebv/mg-dashboard-mcp",
3
- "version": "7.4.24",
3
+ "version": "7.4.25",
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",