@mgsoftwarebv/mg-dashboard-mcp 7.4.24 → 7.4.26

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 +198 -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 sql37 = `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 "${sql37}" 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 sql37 = `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 "${sql37}" 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 sql37 = 'SELECT slug, name FROM \\"Project\\" ORDER BY name';
836
+ const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql37}" 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 = [
@@ -6291,6 +6341,52 @@ pgTable("two_factor", {
6291
6341
  secret: text("secret").notNull(),
6292
6342
  backupCodes: text("backup_codes")
6293
6343
  });
6344
+ var oauthApplications = pgTable("oauth_application", {
6345
+ id: uuid("id").primaryKey().defaultRandom(),
6346
+ clientId: text("client_id").notNull().unique(),
6347
+ clientSecret: text("client_secret"),
6348
+ name: text("name").notNull(),
6349
+ icon: text("icon"),
6350
+ metadata: text("metadata"),
6351
+ redirectUrls: text("redirect_urls").notNull(),
6352
+ type: text("type").notNull(),
6353
+ disabled: boolean("disabled").notNull().default(false),
6354
+ userId: uuid("user_id").references(() => users.id, { onDelete: "cascade" }),
6355
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6356
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6357
+ });
6358
+ pgTable("oauth_access_token", {
6359
+ id: uuid("id").primaryKey().defaultRandom(),
6360
+ accessToken: text("access_token").notNull().unique(),
6361
+ refreshToken: text("refresh_token").unique(),
6362
+ accessTokenExpiresAt: timestamp("access_token_expires_at", {
6363
+ withTimezone: true
6364
+ }).notNull(),
6365
+ refreshTokenExpiresAt: timestamp("refresh_token_expires_at", {
6366
+ withTimezone: true
6367
+ }),
6368
+ clientId: text("client_id").notNull().references(() => oauthApplications.clientId, { onDelete: "cascade" }),
6369
+ userId: uuid("user_id").references(() => users.id, { onDelete: "cascade" }),
6370
+ scopes: text("scopes").notNull().default(""),
6371
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6372
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6373
+ });
6374
+ pgTable("oauth_consent", {
6375
+ id: uuid("id").primaryKey().defaultRandom(),
6376
+ clientId: text("client_id").notNull().references(() => oauthApplications.clientId, { onDelete: "cascade" }),
6377
+ userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
6378
+ scopes: text("scopes").notNull().default(""),
6379
+ consentGiven: boolean("consent_given").notNull().default(false),
6380
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6381
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6382
+ });
6383
+ pgTable("jwks", {
6384
+ id: uuid("id").primaryKey().defaultRandom(),
6385
+ publicKey: text("public_key").notNull(),
6386
+ privateKey: text("private_key").notNull(),
6387
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6388
+ expiresAt: timestamp("expires_at", { withTimezone: true })
6389
+ });
6294
6390
  var WIKI_EMBEDDING_DIMENSIONS = 1536;
6295
6391
  var vector = customType({
6296
6392
  dataType(config) {
@@ -7331,6 +7427,62 @@ pgTable(
7331
7427
  index("git_access_log_op_status_idx").on(table.op, table.status)
7332
7428
  ]
7333
7429
  );
7430
+ pgTable(
7431
+ "git_credential_grant",
7432
+ {
7433
+ id: uuid("id").primaryKey().defaultRandom(),
7434
+ tokenHash: text("token_hash").notNull(),
7435
+ userId: uuid("user_id").notNull(),
7436
+ apiKeyId: uuid("api_key_id"),
7437
+ helperDeviceId: uuid("helper_device_id"),
7438
+ repo: text("repo").notNull(),
7439
+ write: boolean("write").notNull().default(true),
7440
+ principal: text("principal").notNull(),
7441
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
7442
+ issuedIp: text("issued_ip").notNull().default(""),
7443
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
7444
+ },
7445
+ (table) => [
7446
+ uniqueIndex("git_credential_grant_token_hash_uidx").on(table.tokenHash),
7447
+ index("git_credential_grant_expires_idx").on(table.expiresAt),
7448
+ index("git_credential_grant_user_idx").on(table.userId, table.expiresAt),
7449
+ index("git_credential_grant_helper_device_idx").on(table.helperDeviceId)
7450
+ ]
7451
+ );
7452
+ pgTable(
7453
+ "git_helper_enroll",
7454
+ {
7455
+ id: uuid("id").primaryKey().defaultRandom(),
7456
+ userId: uuid("user_id").notNull(),
7457
+ codeHash: text("code_hash").notNull(),
7458
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
7459
+ usedAt: timestamp("used_at", { withTimezone: true }),
7460
+ issuedIp: text("issued_ip").notNull().default(""),
7461
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
7462
+ },
7463
+ (table) => [
7464
+ uniqueIndex("git_helper_enroll_code_hash_uidx").on(table.codeHash),
7465
+ index("git_helper_enroll_user_idx").on(table.userId, table.expiresAt)
7466
+ ]
7467
+ );
7468
+ pgTable(
7469
+ "git_helper_device",
7470
+ {
7471
+ id: uuid("id").primaryKey().defaultRandom(),
7472
+ userId: uuid("user_id").notNull(),
7473
+ tokenHash: text("token_hash").notNull(),
7474
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
7475
+ revokedAt: timestamp("revoked_at", { withTimezone: true }),
7476
+ lastUsedAt: timestamp("last_used_at", { withTimezone: true }),
7477
+ issuedIp: text("issued_ip").notNull().default(""),
7478
+ userAgent: text("user_agent").notNull().default(""),
7479
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
7480
+ },
7481
+ (table) => [
7482
+ uniqueIndex("git_helper_device_token_hash_uidx").on(table.tokenHash),
7483
+ index("git_helper_device_user_idx").on(table.userId, table.expiresAt)
7484
+ ]
7485
+ );
7334
7486
  var directoryLinkType = pgEnum("directory_link_type", [
7335
7487
  "dofollow",
7336
7488
  "nofollow",
@@ -8135,6 +8287,22 @@ pgTable(
8135
8287
  )
8136
8288
  ]
8137
8289
  );
8290
+
8291
+ // ../platform/dist/utils/permissions.js
8292
+ var MODULE_KEYS = [
8293
+ "users",
8294
+ "ssh_servers",
8295
+ "wiki",
8296
+ "ci_cd",
8297
+ "settings",
8298
+ "cursor_remote",
8299
+ "code_battle",
8300
+ "git_hosting"
8301
+ ];
8302
+ ({
8303
+ modules: Object.fromEntries(MODULE_KEYS.map((k) => [k, false]))});
8304
+ ({
8305
+ modules: Object.fromEntries(MODULE_KEYS.map((k) => [k, true]))});
8138
8306
  var SSH_POOL_IDLE_MS = 6e4;
8139
8307
  var sshPool = /* @__PURE__ */ new Map();
8140
8308
  function poolKey(options) {
@@ -10688,7 +10856,7 @@ async function writeAuditLog(entry) {
10688
10856
  );
10689
10857
  }
10690
10858
  }
10691
- var MODULE_KEYS = [
10859
+ var MODULE_KEYS2 = [
10692
10860
  "users",
10693
10861
  "ssh_servers",
10694
10862
  "wiki",
@@ -10699,9 +10867,9 @@ var MODULE_KEYS = [
10699
10867
  "code_battle",
10700
10868
  "git_hosting"
10701
10869
  ];
10702
- var FULL_PERMISSIONS = {
10870
+ var FULL_PERMISSIONS2 = {
10703
10871
  modules: Object.fromEntries(
10704
- MODULE_KEYS.map((k) => [k, true])
10872
+ MODULE_KEYS2.map((k) => [k, true])
10705
10873
  ),
10706
10874
  resources: { ssh_servers: ["*"] }
10707
10875
  };
@@ -10709,12 +10877,12 @@ function parsePermissions(raw) {
10709
10877
  if (!raw || typeof raw !== "object") return null;
10710
10878
  return raw;
10711
10879
  }
10712
- function resolvePermissions(roleName, roleDefaults, userOverrides) {
10713
- if (roleName === "superadmin") return FULL_PERMISSIONS;
10880
+ function resolvePermissions2(roleName, roleDefaults, userOverrides) {
10881
+ if (roleName === "superadmin") return FULL_PERMISSIONS2;
10714
10882
  const base = parsePermissions(roleDefaults);
10715
10883
  const overrides = parsePermissions(userOverrides);
10716
10884
  const modules = {};
10717
- for (const key of MODULE_KEYS) {
10885
+ for (const key of MODULE_KEYS2) {
10718
10886
  const userVal = overrides?.modules?.[key];
10719
10887
  const roleVal = base?.modules?.[key];
10720
10888
  modules[key] = userVal !== void 0 ? userVal : roleVal !== void 0 ? roleVal : false;
@@ -10763,6 +10931,7 @@ var TOOL_MODULE_MAP = {
10763
10931
  "cursor-remote-list": "cursor_remote",
10764
10932
  "cursor-remote-run": "cursor_remote",
10765
10933
  "ai-company": "ssh_servers",
10934
+ "git-credential": "git_hosting",
10766
10935
  // cursor-skill intentionally unmapped — any valid API key may pull shared skills
10767
10936
  ...TRIGGER_TOOL_MODULE_MAP
10768
10937
  };
@@ -10906,7 +11075,7 @@ async function validateApiKey(key) {
10906
11075
  const roleName = userData.role_name || "user";
10907
11076
  const roleDefaults = userData.role_default_permissions ?? {};
10908
11077
  const userOverrides = userData.permissions ?? null;
10909
- const permissions = resolvePermissions(roleName, roleDefaults, userOverrides);
11078
+ const permissions = resolvePermissions2(roleName, roleDefaults, userOverrides);
10910
11079
  const allowedServerIds = intersectServerAccess(
10911
11080
  data.allowed_server_ids,
10912
11081
  permissions.resources.ssh_servers
@@ -10917,9 +11086,9 @@ async function validateApiKey(key) {
10917
11086
  WHERE id = ${data.id}
10918
11087
  AND (last_used_at IS NULL OR last_used_at < now() - interval '60 seconds')
10919
11088
  `);
10920
- const moduleCount = MODULE_KEYS.filter((k) => permissions.modules[k]).length;
11089
+ const moduleCount = MODULE_KEYS2.filter((k) => permissions.modules[k]).length;
10921
11090
  console.error(
10922
- `Authenticated as user ${data.created_by} (role: ${roleName}, modules: ${moduleCount}/${MODULE_KEYS.length})`
11091
+ `Authenticated as user ${data.created_by} (role: ${roleName}, modules: ${moduleCount}/${MODULE_KEYS2.length})`
10923
11092
  );
10924
11093
  return {
10925
11094
  apiKeyId: data.id,
@@ -11153,7 +11322,7 @@ async function validateSshKey(pubkeyPathInput, expectedApiKeyId) {
11153
11322
  const roleName = userData.role_name || "user";
11154
11323
  const roleDefaults = userData.role_default_permissions ?? {};
11155
11324
  const userOverrides = userData.permissions ?? null;
11156
- const permissions = resolvePermissions(roleName, roleDefaults, userOverrides);
11325
+ const permissions = resolvePermissions2(roleName, roleDefaults, userOverrides);
11157
11326
  const allowedServerIds = intersectServerAccess(
11158
11327
  apiRow.allowed_server_ids,
11159
11328
  permissions.resources.ssh_servers
@@ -11170,9 +11339,9 @@ async function validateSshKey(pubkeyPathInput, expectedApiKeyId) {
11170
11339
  AND (last_used_at IS NULL OR last_used_at < now() - interval '60 seconds')`
11171
11340
  )
11172
11341
  ]);
11173
- const moduleCount = MODULE_KEYS.filter((k) => permissions.modules[k]).length;
11342
+ const moduleCount = MODULE_KEYS2.filter((k) => permissions.modules[k]).length;
11174
11343
  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})`
11344
+ `Authenticated via SSH key "${keyRow.name}" (fp ${fingerprint.slice(0, 24)}...) as user ${apiRow.created_by} (role: ${roleName}, modules: ${moduleCount}/${MODULE_KEYS2.length})`
11176
11345
  );
11177
11346
  return {
11178
11347
  apiKeyId: apiRow.id,
@@ -13453,11 +13622,11 @@ CREATE TABLE IF NOT EXISTS _mcp_migrations (
13453
13622
  applied_by TEXT
13454
13623
  );
13455
13624
  `.trim();
13456
- function normaliseMigrationSql(sql35) {
13457
- return sql35.replace(/\r\n/g, "\n").trim() + "\n";
13625
+ function normaliseMigrationSql(sql37) {
13626
+ return sql37.replace(/\r\n/g, "\n").trim() + "\n";
13458
13627
  }
13459
- function migrationSha256(sql35) {
13460
- return createHash("sha256").update(sql35.replace(/\r\n/g, "\n"), "utf8").digest("hex");
13628
+ function migrationSha256(sql37) {
13629
+ return createHash("sha256").update(sql37.replace(/\r\n/g, "\n"), "utf8").digest("hex");
13461
13630
  }
13462
13631
  function dollarQuoteTag(value) {
13463
13632
  let tag = "_mcp";
@@ -15469,6 +15638,7 @@ var TOOLS = [
15469
15638
  }
15470
15639
  },
15471
15640
  ...MAILSERVER_TOOLS,
15641
+ ...GIT_CREDENTIAL_TOOLS,
15472
15642
  ...HERMES_COMPANY_TOOLS,
15473
15643
  // ----- Trigger.dev -----
15474
15644
  ...TRIGGER_TOOLS
@@ -19237,6 +19407,12 @@ Install: GET https://dashboard.mgsoftware.nl/api/downloads/versions.txt then \u2
19237
19407
  token
19238
19408
  });
19239
19409
  }
19410
+ case GIT_CREDENTIAL_TOOL_NAME: {
19411
+ return handleGitCredentialTool(a, {
19412
+ dashboardBaseUrl,
19413
+ apiKey: apiKey ?? ""
19414
+ });
19415
+ }
19240
19416
  case HERMES_COMPANY_TOOL_NAME: {
19241
19417
  return handleHermesCompanyTool(a, {
19242
19418
  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.26",
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",