@mgsoftwarebv/mg-dashboard-mcp 6.0.0 → 6.0.2
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 +71 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1121,6 +1121,7 @@ async function handleVercelTool(name, args2, deps) {
|
|
|
1121
1121
|
sinceExplicit ? Promise.resolve(null) : getDeploymentCreatedMs(token, deploymentId)
|
|
1122
1122
|
]);
|
|
1123
1123
|
const maxWindowMin = 7 * 24 * 60;
|
|
1124
|
+
const autoCapMin = 30;
|
|
1124
1125
|
let sinceMs;
|
|
1125
1126
|
let windowNote = "";
|
|
1126
1127
|
if (sinceExplicit) {
|
|
@@ -1129,17 +1130,18 @@ async function handleVercelTool(name, args2, deps) {
|
|
|
1129
1130
|
windowNote = `window: last ${capped} min (caller-specified)`;
|
|
1130
1131
|
} else if (deploymentCreatedMs) {
|
|
1131
1132
|
const bufferMs = 5 * 6e4;
|
|
1132
|
-
|
|
1133
|
-
const ageMin = Math.max(1, Math.round((Date.now() -
|
|
1134
|
-
if (ageMin
|
|
1135
|
-
sinceMs =
|
|
1136
|
-
windowNote = `window: capped to ${maxWindowMin} min (deployment is older than 7 days)`;
|
|
1137
|
-
} else {
|
|
1133
|
+
const sinceDeploymentMs = deploymentCreatedMs - bufferMs;
|
|
1134
|
+
const ageMin = Math.max(1, Math.round((Date.now() - sinceDeploymentMs) / 6e4));
|
|
1135
|
+
if (ageMin <= autoCapMin) {
|
|
1136
|
+
sinceMs = sinceDeploymentMs;
|
|
1138
1137
|
windowNote = `window: auto ${ageMin} min from deployment createdAt - 5 min buffer`;
|
|
1138
|
+
} else {
|
|
1139
|
+
sinceMs = Date.now() - autoCapMin * 6e4;
|
|
1140
|
+
windowNote = `window: capped to last ${autoCapMin} min (deployment is ${ageMin} min old \u2014 pass sinceMinutes to widen up to ${maxWindowMin})`;
|
|
1139
1141
|
}
|
|
1140
1142
|
} else {
|
|
1141
|
-
sinceMs = Date.now() -
|
|
1142
|
-
windowNote =
|
|
1143
|
+
sinceMs = Date.now() - autoCapMin * 6e4;
|
|
1144
|
+
windowNote = `window: last ${autoCapMin} min (deployment metadata unavailable, used fallback)`;
|
|
1143
1145
|
}
|
|
1144
1146
|
const { logs, error } = await getRuntimeLogs(
|
|
1145
1147
|
token,
|
|
@@ -1152,10 +1154,15 @@ async function handleVercelTool(name, args2, deps) {
|
|
|
1152
1154
|
const hint = error.includes("404") || error.includes("400") ? '\n\nThis endpoint requires both project ID and deployment ID and may not be available on every Vercel plan. Use kind="webhooks" or the supabase MCP (vercel_deployment_log table) for archived runtime logs.' : "";
|
|
1153
1155
|
return { content: [{ type: "text", text: `Error: ${error}${hint}` }] };
|
|
1154
1156
|
}
|
|
1155
|
-
const
|
|
1157
|
+
const body = formatRuntimeLogs(logs);
|
|
1158
|
+
const hitDurationLimit = /Exceeded query duration limit/i.test(body);
|
|
1159
|
+
const footer = hitDurationLimit ? `
|
|
1160
|
+
|
|
1161
|
+
[${windowNote}]
|
|
1162
|
+
[hint] Vercel hit its 5-min query budget for this window. Try a smaller sinceMinutes (e.g. 5-10), lower limit, or use kind="webhooks" / the supabase MCP vercel_deployment_log table for archived logs.` : `
|
|
1156
1163
|
|
|
1157
1164
|
[${windowNote}]`;
|
|
1158
|
-
return { content: [{ type: "text", text }] };
|
|
1165
|
+
return { content: [{ type: "text", text: body + footer }] };
|
|
1159
1166
|
}
|
|
1160
1167
|
if (kind === "webhooks") {
|
|
1161
1168
|
const limit = Math.min(Math.max(Number(args2.limit) || 25, 1), 200);
|
|
@@ -3386,7 +3393,10 @@ function formatDbQueryFooter(output, appliedLimit, maxRows, explainMode) {
|
|
|
3386
3393
|
return "\n\n[explain] Plan returned, no rows executed.";
|
|
3387
3394
|
}
|
|
3388
3395
|
if (!appliedLimit) return "";
|
|
3389
|
-
const rows = output
|
|
3396
|
+
const rows = parseRowCountFromOutput(output);
|
|
3397
|
+
if (rows == null) return `
|
|
3398
|
+
|
|
3399
|
+
[ok] auto-LIMIT ${maxRows} applied (row count not detected).`;
|
|
3390
3400
|
if (rows > maxRows) {
|
|
3391
3401
|
return `
|
|
3392
3402
|
|
|
@@ -3396,6 +3406,21 @@ function formatDbQueryFooter(output, appliedLimit, maxRows, explainMode) {
|
|
|
3396
3406
|
|
|
3397
3407
|
[ok] returned ${rows} row(s), under auto-LIMIT ${maxRows}.`;
|
|
3398
3408
|
}
|
|
3409
|
+
function parseRowCountFromOutput(output) {
|
|
3410
|
+
const lines = output.split("\n");
|
|
3411
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3412
|
+
const l = lines[i]?.trim();
|
|
3413
|
+
if (!l) continue;
|
|
3414
|
+
const m1 = /^\(\s*(\d+)\s+rows?\s*\)$/i.exec(l);
|
|
3415
|
+
if (m1?.[1]) return Number(m1[1]);
|
|
3416
|
+
const m2 = /^(\d+)\s+rows?\s+in\s+set\b/i.exec(l);
|
|
3417
|
+
if (m2?.[1]) return Number(m2[1]);
|
|
3418
|
+
const m3 = /^\(\s*(\d+)\s+rows?\s+affected\s*\)$/i.exec(l);
|
|
3419
|
+
if (m3?.[1]) return Number(m3[1]);
|
|
3420
|
+
if (/[a-zA-Z0-9]/.test(l) && i < lines.length - 3) return null;
|
|
3421
|
+
}
|
|
3422
|
+
return null;
|
|
3423
|
+
}
|
|
3399
3424
|
function assertSafeSql(query) {
|
|
3400
3425
|
const trimmed = query.trim();
|
|
3401
3426
|
for (const pattern of BLOCKED_SQL_PATTERNS) {
|
|
@@ -3497,6 +3522,28 @@ function formatDnsDiff(domain, before, after, change) {
|
|
|
3497
3522
|
lines.push("", "Re-run without `dryRun: true` to apply.");
|
|
3498
3523
|
return lines.join("\n");
|
|
3499
3524
|
}
|
|
3525
|
+
function describeDnsCandidates(records, type, name, attemptedValue) {
|
|
3526
|
+
const sameTypeAndName = records.filter((r) => r.type === type && r.name === name);
|
|
3527
|
+
const fmt = (r) => ` - ${r.type.padEnd(6)} ${r.name.padEnd(30)} ttl=${String(r.ttl).padEnd(5)} ${r.value}`;
|
|
3528
|
+
if (sameTypeAndName.length > 0) {
|
|
3529
|
+
const lines = ["Current records with this type + name (use one of these values verbatim):"];
|
|
3530
|
+
for (const r of sameTypeAndName.slice(0, 10)) lines.push(fmt(r));
|
|
3531
|
+
if (sameTypeAndName.length > 10) lines.push(` ...and ${sameTypeAndName.length - 10} more`);
|
|
3532
|
+
lines.push(`(attempted value: ${attemptedValue})`);
|
|
3533
|
+
return lines.join("\n");
|
|
3534
|
+
}
|
|
3535
|
+
const sameName = records.filter((r) => r.name === name);
|
|
3536
|
+
if (sameName.length > 0) {
|
|
3537
|
+
const types = [...new Set(sameName.map((r) => r.type))].sort().join(", ");
|
|
3538
|
+
const lines = [
|
|
3539
|
+
`No ${type} records exist for "${name}", but other records do (types: ${types}). Sample:`
|
|
3540
|
+
];
|
|
3541
|
+
for (const r of sameName.slice(0, 10)) lines.push(fmt(r));
|
|
3542
|
+
if (sameName.length > 10) lines.push(` ...and ${sameName.length - 10} more`);
|
|
3543
|
+
return lines.join("\n");
|
|
3544
|
+
}
|
|
3545
|
+
return `No records found at all for name "${name}". Run dns-list to inspect the zone.`;
|
|
3546
|
+
}
|
|
3500
3547
|
async function mijnhostFetch(path, options = {}) {
|
|
3501
3548
|
const key = requireMijnhostApiKey();
|
|
3502
3549
|
const res = await fetch(`${MIJNHOST_BASE_URL}${path}`, {
|
|
@@ -3810,7 +3857,7 @@ var TOOLS = [
|
|
|
3810
3857
|
// ----- Domains (mijn.host) -----
|
|
3811
3858
|
{
|
|
3812
3859
|
name: "domain-list",
|
|
3813
|
-
description: "List all domains from the mijn.host account. Returns domain name, status, renewal date (= expiration), and tags. Requires MIJNHOST_API_KEY.\n\nPass `details: true` to also fetch DNS zone summary per domain in parallel:
|
|
3860
|
+
description: "List all domains from the mijn.host account. Returns domain name, status, renewal date (= expiration), and tags. Requires MIJNHOST_API_KEY.\n\nPass `details: true` to also fetch DNS zone summary per domain in parallel: MX target(s) and presence of SPF/DMARC TXT records. Useful as a single-call overview instead of N follow-up dns-list calls. Skipped for inactive/expired domains.",
|
|
3814
3861
|
inputSchema: {
|
|
3815
3862
|
type: "object",
|
|
3816
3863
|
properties: {
|
|
@@ -3855,7 +3902,7 @@ var TOOLS = [
|
|
|
3855
3902
|
// ----- Vercel -----
|
|
3856
3903
|
...VERCEL_TOOLS
|
|
3857
3904
|
];
|
|
3858
|
-
var MCP_VERSION = "6.0.
|
|
3905
|
+
var MCP_VERSION = "6.0.2";
|
|
3859
3906
|
async function handleListTools() {
|
|
3860
3907
|
if (!authContext) return { tools: TOOLS };
|
|
3861
3908
|
const accessible = TOOLS.filter((tool) => {
|
|
@@ -5107,15 +5154,14 @@ ${lines2.join("\n")}` }] };
|
|
|
5107
5154
|
`/domains/${encodeURIComponent(domain)}/dns`
|
|
5108
5155
|
);
|
|
5109
5156
|
const recs = r.data.records || [];
|
|
5110
|
-
const ns = recs.filter((x) => x.type === "NS").map((x) => x.value).sort();
|
|
5111
5157
|
const mx = recs.filter((x) => x.type === "MX").map((x) => x.value).sort();
|
|
5112
5158
|
const hasSpf = recs.some((x) => x.type === "TXT" && x.value.toLowerCase().includes("v=spf1"));
|
|
5113
5159
|
const hasDmarc = recs.some(
|
|
5114
5160
|
(x) => x.type === "TXT" && (x.name.toLowerCase().startsWith("_dmarc") || x.value.toLowerCase().includes("v=dmarc1"))
|
|
5115
5161
|
);
|
|
5116
|
-
return {
|
|
5162
|
+
return { mx, hasSpf, hasDmarc };
|
|
5117
5163
|
} catch (err) {
|
|
5118
|
-
return {
|
|
5164
|
+
return { mx: [], hasSpf: false, hasDmarc: false, error: err instanceof Error ? err.message : String(err) };
|
|
5119
5165
|
}
|
|
5120
5166
|
}
|
|
5121
5167
|
const queue = [...activeDomains];
|
|
@@ -5134,12 +5180,9 @@ ${lines2.join("\n")}` }] };
|
|
|
5134
5180
|
if (!s) return head;
|
|
5135
5181
|
if (s.error) return `${head}
|
|
5136
5182
|
dns: error: ${s.error}`;
|
|
5137
|
-
const ns = s.ns.length ? s.ns.join(", ") : "(none)";
|
|
5138
5183
|
const mx = s.mx.length ? s.mx.join(", ") : "(none)";
|
|
5139
|
-
const mail = `mx=${mx} spf=${s.hasSpf ? "yes" : "NO"} dmarc=${s.hasDmarc ? "yes" : "NO"}`;
|
|
5140
5184
|
return `${head}
|
|
5141
|
-
|
|
5142
|
-
${mail}`;
|
|
5185
|
+
mx=${mx} spf=${s.hasSpf ? "yes" : "NO"} dmarc=${s.hasDmarc ? "yes" : "NO"}`;
|
|
5143
5186
|
});
|
|
5144
5187
|
return {
|
|
5145
5188
|
content: [{
|
|
@@ -5206,7 +5249,10 @@ ${lines.join("\n")}` }] };
|
|
|
5206
5249
|
(r) => r.type === type && r.name === dnsName && r.value === oldValue
|
|
5207
5250
|
);
|
|
5208
5251
|
if (idx === -1) {
|
|
5209
|
-
throw new Error(
|
|
5252
|
+
throw new Error(
|
|
5253
|
+
`No matching DNS record found: ${type} ${dnsName} = ${oldValue}
|
|
5254
|
+
` + describeDnsCandidates(current.data.records, type, dnsName, oldValue)
|
|
5255
|
+
);
|
|
5210
5256
|
}
|
|
5211
5257
|
const updated = [...current.data.records];
|
|
5212
5258
|
const before = updated[idx];
|
|
@@ -5230,7 +5276,10 @@ ${lines.join("\n")}` }] };
|
|
|
5230
5276
|
(r) => !(r.type === type && r.name === dnsName && r.value === value)
|
|
5231
5277
|
);
|
|
5232
5278
|
if (removed.length === 0) {
|
|
5233
|
-
throw new Error(
|
|
5279
|
+
throw new Error(
|
|
5280
|
+
`No matching DNS record found: ${type} ${dnsName} = ${value}
|
|
5281
|
+
` + describeDnsCandidates(current.data.records, type, dnsName, value)
|
|
5282
|
+
);
|
|
5234
5283
|
}
|
|
5235
5284
|
nextRecords = remaining;
|
|
5236
5285
|
diffArgs = { verb: "delete", removed };
|