@mgsoftwarebv/mg-dashboard-mcp 6.2.0 → 6.3.0
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 +86 -31
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2750,7 +2750,7 @@ async function getServerConnection(serverIdOrName) {
|
|
|
2750
2750
|
const needsProxy = data.allowed_ssh_ips !== null && serverId !== SSH_PROXY_SERVER_ID;
|
|
2751
2751
|
const proxy = needsProxy ? await getProxyConnection() : void 0;
|
|
2752
2752
|
const os = data.os_type === "windows" ? "windows" : "linux";
|
|
2753
|
-
return { conn, proxy, os };
|
|
2753
|
+
return { serverId, conn, proxy, os };
|
|
2754
2754
|
}
|
|
2755
2755
|
async function sshExec(opts, command, proxy, options) {
|
|
2756
2756
|
const first = await sshExecOnce(opts, command, proxy, options);
|
|
@@ -4232,6 +4232,34 @@ done
|
|
|
4232
4232
|
}
|
|
4233
4233
|
return out;
|
|
4234
4234
|
}
|
|
4235
|
+
var POSTGRES_CONTAINER_CREDS_CACHE = /* @__PURE__ */ new Map();
|
|
4236
|
+
var PG_CREDS_TTL_MS = 60 * 60 * 1e3;
|
|
4237
|
+
function cachePostgresContainerCreds(serverId, containerName, creds) {
|
|
4238
|
+
POSTGRES_CONTAINER_CREDS_CACHE.set(`${serverId}|${containerName}`, { ...creds, capturedAt: Date.now() });
|
|
4239
|
+
}
|
|
4240
|
+
async function resolvePostgresContainerCreds(conn, proxy, serverId, containerName) {
|
|
4241
|
+
const key = `${serverId}|${containerName}`;
|
|
4242
|
+
const cached = POSTGRES_CONTAINER_CREDS_CACHE.get(key);
|
|
4243
|
+
if (cached && Date.now() - cached.capturedAt < PG_CREDS_TTL_MS) return cached;
|
|
4244
|
+
const safeContainer = containerName.replace(/[^a-zA-Z0-9._-]/g, "");
|
|
4245
|
+
const res = await sshExec(
|
|
4246
|
+
conn,
|
|
4247
|
+
`docker exec ${safeContainer} env 2>/dev/null | grep -E '^POSTGRES_(USER|DB|PASSWORD)='`,
|
|
4248
|
+
proxy
|
|
4249
|
+
);
|
|
4250
|
+
let dbUser = "postgres";
|
|
4251
|
+
let dbName = "postgres";
|
|
4252
|
+
let hasPassword = false;
|
|
4253
|
+
for (const raw of res.stdout.split("\n")) {
|
|
4254
|
+
const line = raw.trim();
|
|
4255
|
+
if (line.startsWith("POSTGRES_USER=")) dbUser = line.slice("POSTGRES_USER=".length) || "postgres";
|
|
4256
|
+
else if (line.startsWith("POSTGRES_DB=")) dbName = line.slice("POSTGRES_DB=".length) || "postgres";
|
|
4257
|
+
else if (line.startsWith("POSTGRES_PASSWORD=")) hasPassword = true;
|
|
4258
|
+
}
|
|
4259
|
+
const creds = { dbName, dbUser, hasPassword, capturedAt: Date.now() };
|
|
4260
|
+
POSTGRES_CONTAINER_CREDS_CACHE.set(key, creds);
|
|
4261
|
+
return creds;
|
|
4262
|
+
}
|
|
4235
4263
|
async function psqlInContainer(conn, proxy, containerName, dbUser, dbName, scriptSql, flags) {
|
|
4236
4264
|
const safeContainer = containerName.replace(/[^a-zA-Z0-9._-]/g, "");
|
|
4237
4265
|
const safeUser = dbUser.replace(/[^a-zA-Z0-9_-]/g, "") || "postgres";
|
|
@@ -4687,22 +4715,22 @@ var TOOLS = [
|
|
|
4687
4715
|
},
|
|
4688
4716
|
{
|
|
4689
4717
|
name: "db-tables",
|
|
4690
|
-
description: "List tables with row counts + sizes. Two routing modes:\n- MySQL via `/var/www` autodiscover: pass `sitePath`. Credentials are read from wp-config.php / parameters.php / .env.\n- Postgres container: pass `containerName
|
|
4718
|
+
description: "List tables with row counts + sizes. Two routing modes:\n- MySQL via `/var/www` autodiscover: pass `sitePath`. Credentials are read from wp-config.php / parameters.php / .env.\n- Postgres container: pass `containerName`. dbName + dbUser are auto-resolved from the container `POSTGRES_DB` / `POSTGRES_USER` env vars (cached 1h). Returns one row per user table sorted by total size, with estimated row count from pg_class.reltuples.",
|
|
4691
4719
|
inputSchema: {
|
|
4692
4720
|
type: "object",
|
|
4693
4721
|
properties: {
|
|
4694
|
-
serverId: { type: "string", description: "UUID of the SSH server" },
|
|
4722
|
+
serverId: { type: "string", description: "UUID or (fuzzy) name of the SSH server" },
|
|
4695
4723
|
sitePath: { type: "string", description: "Site root path (e.g. /var/www/example.com). MySQL autodiscover mode." },
|
|
4696
4724
|
containerName: { type: "string", description: "Postgres container name. Activates direct Postgres mode." },
|
|
4697
|
-
dbName: { type: "string", description:
|
|
4698
|
-
dbUser: { type: "string", description:
|
|
4725
|
+
dbName: { type: "string", description: "Database name (containerName mode). Auto-resolved from container env when omitted." },
|
|
4726
|
+
dbUser: { type: "string", description: "Database user (containerName mode). Auto-resolved from container env when omitted." }
|
|
4699
4727
|
},
|
|
4700
4728
|
required: ["serverId"]
|
|
4701
4729
|
}
|
|
4702
4730
|
},
|
|
4703
4731
|
{
|
|
4704
4732
|
name: "db-query",
|
|
4705
|
-
description: 'Execute SQL against any database. **Use this for ALL queries (SELECT, INSERT, UPDATE, DELETE, schema introspection) against vanilla Postgres / MySQL / MSSQL containers** \u2014 do NOT fall back to `ssh-execute` + `docker cp` + `psql -f` with .tmp.sql files; the container path here already pipes via stdin so quotes / `$` / `;` are safe. Multi-statement scripts work fine: separate with `;` and they will all be executed by psql/mysql.\n\nTwo routing modes:\n- MySQL via `/var/www` autodiscover (default): pass `sitePath` (and `engine: "mysql"` implicitly). Credentials are read from wp-config.php, parameters.php, .env.\n- Direct via Docker container: pass `containerName` (the container running the DB server). Engine defaults to `postgres`; pass `engine: "mysql"` or `"mssql"` to override. Use `db-discover include=["postgres-containers"]` to find available containers + creds.\n\nPostgres example: `{ serverId, containerName: "refront-postgres-vanilla",
|
|
4733
|
+
description: 'Execute SQL against any database. **Use this for ALL queries (SELECT, INSERT, UPDATE, DELETE, schema introspection) against vanilla Postgres / MySQL / MSSQL containers** \u2014 do NOT fall back to `ssh-execute` + `docker cp` + `psql -f` with .tmp.sql files; the container path here already pipes via stdin so quotes / `$` / `;` are safe. Multi-statement scripts work fine: separate with `;` and they will all be executed by psql/mysql.\n\nTwo routing modes:\n- MySQL via `/var/www` autodiscover (default): pass `sitePath` (and `engine: "mysql"` implicitly). Credentials are read from wp-config.php, parameters.php, .env.\n- Direct via Docker container: pass `containerName` (the container running the DB server). Engine defaults to `postgres`; pass `engine: "mysql"` or `"mssql"` to override. Use `db-discover include=["postgres-containers"]` to find available containers + creds.\n\nPostgres example: `{ serverId, containerName: "refront-postgres-vanilla", query: "SELECT 1" }` \u2014 dbName and dbUser are AUTO-RESOLVED from the container\'s `POSTGRES_USER` / `POSTGRES_DB` env vars (cached 1h per server). Only pass `dbName`/`dbUser` explicitly when the instance hosts multiple databases or you want to query as a non-default role.\nMSSQL example: `{ serverId, containerName: "mssql-1", engine: "mssql", dbUser: "sa", dbPass: "...", query: "SELECT 1" }`.\n\nResult safety: `maxRows` (default 1000, max 10000) auto-injects a `LIMIT` for SELECT queries that don\'t have one. The footer reports whether the cap was applied. Pass `maxRows: 0` to disable.\n\nDiagnostics: `explain: true` prepends EXPLAIN (postgres / mysql) or runs `SET SHOWPLAN_TEXT ON` (mssql).\n\nAtomic multi-statement: pass `transaction: true` to wrap the query in BEGIN/COMMIT (postgres / mysql) so a failure in any statement rolls back the lot. Use for ad-hoc fixes that are not schema migrations (e.g. `UPDATE tax_rate SET pct = pct * 100; UPDATE order SET ...`).\n\nSchema introspection: pass `describe: "tablename"` to get columns + indexes (works for mysql, postgres, and mssql). Pass `describe: "*"` to list all tables. When `describe` is set, `query` is ignored.\n\nDestructive operations (DROP, TRUNCATE, ALTER \u2026 DROP, naked DELETE) are blocked by default. For schema migrations, **prefer `db-apply-migration`** \u2014 it has an audit ledger and idempotency. As an escape hatch for ad-hoc hot-fixes, pass `allowDestructive: "yes-i-understand-this-is-not-logged"` (literal string) to bypass the gate. The bypass is logged in the response footer.',
|
|
4706
4734
|
inputSchema: {
|
|
4707
4735
|
type: "object",
|
|
4708
4736
|
properties: {
|
|
@@ -4712,8 +4740,8 @@ var TOOLS = [
|
|
|
4712
4740
|
sitePath: { type: "string", description: "Site root path (e.g. /var/www/example.com). Used only with engine=mysql autodiscover." },
|
|
4713
4741
|
containerName: { type: "string", description: 'Docker container running the DB server (e.g. "refront-postgres-vanilla", "supabase-db"). Activates direct-query mode.' },
|
|
4714
4742
|
engine: { type: "string", enum: ["mysql", "postgres", "mssql"], description: 'DB engine. Defaults to "mysql" with sitePath, "postgres" with containerName.' },
|
|
4715
|
-
dbName: { type: "string", description:
|
|
4716
|
-
dbUser: { type: "string", description: 'Database user (containerName mode).
|
|
4743
|
+
dbName: { type: "string", description: "Database name (containerName mode). For postgres: auto-resolved from container `POSTGRES_DB` env var when omitted. For mysql: server default. For mssql: server default." },
|
|
4744
|
+
dbUser: { type: "string", description: 'Database user (containerName mode). For postgres: auto-resolved from container `POSTGRES_USER` env var when omitted. For mysql: defaults to "root". For mssql: defaults to "sa".' },
|
|
4717
4745
|
dbPass: { type: "string", description: "Database password (containerName mode). Required for mssql; optional for mysql; postgres uses trust auth by default." },
|
|
4718
4746
|
maxRows: { type: "number", description: "Auto-inject LIMIT for unbounded SELECTs. Default 1000, max 10000, set 0 to disable." },
|
|
4719
4747
|
explain: { type: "boolean", description: "Prepend EXPLAIN (postgres/mysql) or SHOWPLAN (mssql) instead of executing. Useful for slow-query diagnosis." },
|
|
@@ -4725,14 +4753,14 @@ var TOOLS = [
|
|
|
4725
4753
|
},
|
|
4726
4754
|
{
|
|
4727
4755
|
name: "db-apply-migration",
|
|
4728
|
-
description: 'Apply a SQL migration to a Postgres container and record it in an MCP-managed ledger table (`_mcp_migrations`) so re-runs are idempotent. Use this for ALL schema changes (CREATE/ALTER/DROP/TRUNCATE) \u2014 it allows DDL that `db-query` blocks, and gives you audit + drift detection in exchange.\n\nLedger schema (auto-created on first call): `_mcp_migrations(name TEXT PRIMARY KEY, sha256 TEXT, applied_at TIMESTAMPTZ, applied_by TEXT)`.\n\nBehaviour:\n- If `name` is already in the ledger AND the sha256 of the supplied SQL matches \u2192 no-op, returns "already applied".\n- If `name` is already in the ledger with a DIFFERENT sha256 \u2192 fails loudly ("drift detected"). Pass `force: true` to overwrite (logged).\n- Otherwise the SQL is wrapped in BEGIN/COMMIT and applied, then a row is inserted into `_mcp_migrations`.\n\nPass `noTransaction: true` for statements that cannot run inside a transaction (`CREATE INDEX CONCURRENTLY`, `ALTER TYPE \u2026 ADD VALUE`, `VACUUM`, etc.). In that mode the user SQL runs first and the ledger is recorded in a separate follow-up call.\n\nSQL source: provide ONE of `sql` (inline string), `localFile` (path on this machine, read and piped via stdin), or `remoteFile` (absolute path on the SSH server, read with `cat` first).\n\nExample: `{ serverId, containerName: "refront-postgres-vanilla",
|
|
4756
|
+
description: 'Apply a SQL migration to a Postgres container and record it in an MCP-managed ledger table (`_mcp_migrations`) so re-runs are idempotent. Use this for ALL schema changes (CREATE/ALTER/DROP/TRUNCATE) \u2014 it allows DDL that `db-query` blocks, and gives you audit + drift detection in exchange.\n\nLedger schema (auto-created on first call): `_mcp_migrations(name TEXT PRIMARY KEY, sha256 TEXT, applied_at TIMESTAMPTZ, applied_by TEXT)`.\n\nBehaviour:\n- If `name` is already in the ledger AND the sha256 of the supplied SQL matches \u2192 no-op, returns "already applied".\n- If `name` is already in the ledger with a DIFFERENT sha256 \u2192 fails loudly ("drift detected"). Pass `force: true` to overwrite (logged).\n- Otherwise the SQL is wrapped in BEGIN/COMMIT and applied, then a row is inserted into `_mcp_migrations`.\n\nPass `noTransaction: true` for statements that cannot run inside a transaction (`CREATE INDEX CONCURRENTLY`, `ALTER TYPE \u2026 ADD VALUE`, `VACUUM`, etc.). In that mode the user SQL runs first and the ledger is recorded in a separate follow-up call.\n\nSQL source: provide ONE of `sql` (inline string), `localFile` (path on this machine, read and piped via stdin), or `remoteFile` (absolute path on the SSH server, read with `cat` first).\n\nExample: `{ serverId, containerName: "refront-postgres-vanilla", name: "20260517120000_add_unified_classification", localFile: "./migrations/20260517120000_add_unified_classification.sql" }` \u2014 dbName + dbUser are auto-resolved from the container env (cached 1h).',
|
|
4729
4757
|
inputSchema: {
|
|
4730
4758
|
type: "object",
|
|
4731
4759
|
properties: {
|
|
4732
|
-
serverId: { type: "string", description: "UUID of the SSH server" },
|
|
4760
|
+
serverId: { type: "string", description: "UUID or (fuzzy) name of the SSH server" },
|
|
4733
4761
|
containerName: { type: "string", description: 'Postgres container name (e.g. "refront-postgres-vanilla")' },
|
|
4734
|
-
dbName: { type: "string", description:
|
|
4735
|
-
dbUser: { type: "string", description:
|
|
4762
|
+
dbName: { type: "string", description: "Database. Auto-resolved from container `POSTGRES_DB` env when omitted. Override only when the instance hosts multiple databases." },
|
|
4763
|
+
dbUser: { type: "string", description: "User. Auto-resolved from container `POSTGRES_USER` env when omitted." },
|
|
4736
4764
|
name: { type: "string", description: "Unique migration name (recommended format: `YYYYMMDDhhmmss_description`)" },
|
|
4737
4765
|
sql: { type: "string", description: "Inline SQL string (mutually exclusive with localFile / remoteFile)" },
|
|
4738
4766
|
localFile: { type: "string", description: "Path on this machine to a .sql file (read here, streamed via stdin)" },
|
|
@@ -4746,14 +4774,14 @@ var TOOLS = [
|
|
|
4746
4774
|
},
|
|
4747
4775
|
{
|
|
4748
4776
|
name: "db-list-migrations",
|
|
4749
|
-
description: 'List entries from the `_mcp_migrations` ledger on a Postgres container. Answers "what migrations have already been applied here?" without having to write SQL. Returns name, sha256
|
|
4777
|
+
description: 'List entries from the `_mcp_migrations` ledger on a Postgres container. Answers "what migrations have already been applied here?" without having to write SQL. Returns full name, sha256, applied_at, applied_by, ordered by most recent first. If the ledger table does not exist yet (no migrations applied via db-apply-migration), returns a friendly empty response. dbName + dbUser are auto-resolved from the container env when omitted.',
|
|
4750
4778
|
inputSchema: {
|
|
4751
4779
|
type: "object",
|
|
4752
4780
|
properties: {
|
|
4753
|
-
serverId: { type: "string", description: "UUID of the SSH server" },
|
|
4781
|
+
serverId: { type: "string", description: "UUID or (fuzzy) name of the SSH server" },
|
|
4754
4782
|
containerName: { type: "string", description: "Postgres container name" },
|
|
4755
|
-
dbName: { type: "string", description:
|
|
4756
|
-
dbUser: { type: "string", description:
|
|
4783
|
+
dbName: { type: "string", description: "Database. Auto-resolved from container `POSTGRES_DB` env when omitted." },
|
|
4784
|
+
dbUser: { type: "string", description: "User. Auto-resolved from container `POSTGRES_USER` env when omitted." },
|
|
4757
4785
|
limit: { type: "number", description: "Max number of entries (default 50, max 500)" }
|
|
4758
4786
|
},
|
|
4759
4787
|
required: ["serverId", "containerName"]
|
|
@@ -4858,7 +4886,7 @@ var TOOLS = [
|
|
|
4858
4886
|
// ----- Repo reference -----
|
|
4859
4887
|
...REPO_TOOLS
|
|
4860
4888
|
];
|
|
4861
|
-
var MCP_VERSION = "6.
|
|
4889
|
+
var MCP_VERSION = "6.3.0";
|
|
4862
4890
|
async function handleListTools() {
|
|
4863
4891
|
if (!authContext) return { tools: TOOLS };
|
|
4864
4892
|
const accessible = TOOLS.filter((tool) => {
|
|
@@ -5814,7 +5842,7 @@ ${trail.join("\n")}` }] };
|
|
|
5814
5842
|
}
|
|
5815
5843
|
// ----- Database -----
|
|
5816
5844
|
case "db-discover": {
|
|
5817
|
-
const { conn, proxy } = await getServerConnection(String(a.serverId));
|
|
5845
|
+
const { serverId, conn, proxy } = await getServerConnection(String(a.serverId));
|
|
5818
5846
|
const rawInclude = Array.isArray(a.include) ? a.include.map(String) : ["www"];
|
|
5819
5847
|
const include = new Set(rawInclude.filter((m) => m === "www" || m === "postgres-containers"));
|
|
5820
5848
|
if (include.size === 0) include.add("www");
|
|
@@ -5833,6 +5861,13 @@ ${lines.join("\n")}`);
|
|
|
5833
5861
|
}
|
|
5834
5862
|
if (include.has("postgres-containers")) {
|
|
5835
5863
|
const containers = await discoverPostgresContainers(conn, proxy);
|
|
5864
|
+
for (const c of containers) {
|
|
5865
|
+
cachePostgresContainerCreds(serverId, c.container, {
|
|
5866
|
+
dbName: c.db,
|
|
5867
|
+
dbUser: c.user,
|
|
5868
|
+
hasPassword: c.hasPassword
|
|
5869
|
+
});
|
|
5870
|
+
}
|
|
5836
5871
|
if (!containers.length) {
|
|
5837
5872
|
sections.push("## Postgres containers\n(none \u2014 no running container exposes POSTGRES_USER/POSTGRES_DB)");
|
|
5838
5873
|
} else {
|
|
@@ -5842,17 +5877,22 @@ ${lines.join("\n")}`);
|
|
|
5842
5877
|
sections.push(`## Postgres containers (${containers.length})
|
|
5843
5878
|
${lines.join("\n")}
|
|
5844
5879
|
|
|
5845
|
-
Tip:
|
|
5880
|
+
Tip: pass just \`containerName\` to \`db-query\` / \`db-apply-migration\` / \`db-list-migrations\` \u2014 dbName + dbUser are auto-resolved from the container env (cached for 1h per server).`);
|
|
5846
5881
|
}
|
|
5847
5882
|
}
|
|
5848
5883
|
return { content: [{ type: "text", text: sections.join("\n\n") }] };
|
|
5849
5884
|
}
|
|
5850
5885
|
case "db-tables": {
|
|
5851
|
-
const { conn, proxy } = await getServerConnection(String(a.serverId));
|
|
5886
|
+
const { serverId, conn, proxy } = await getServerConnection(String(a.serverId));
|
|
5852
5887
|
const containerName = typeof a.containerName === "string" && a.containerName ? a.containerName : "";
|
|
5853
5888
|
if (containerName) {
|
|
5854
|
-
|
|
5855
|
-
|
|
5889
|
+
let dbUser = typeof a.dbUser === "string" && a.dbUser ? a.dbUser : "";
|
|
5890
|
+
let dbName = typeof a.dbName === "string" && a.dbName ? a.dbName : "";
|
|
5891
|
+
if (!dbUser || !dbName) {
|
|
5892
|
+
const creds = await resolvePostgresContainerCreds(conn, proxy, serverId, containerName);
|
|
5893
|
+
if (!dbUser) dbUser = creds.dbUser;
|
|
5894
|
+
if (!dbName) dbName = creds.dbName;
|
|
5895
|
+
}
|
|
5856
5896
|
const sqlText2 = `SELECT n.nspname AS schema,
|
|
5857
5897
|
c.relname AS "table",
|
|
5858
5898
|
pg_size_pretty(pg_total_relation_size(c.oid)) AS size,
|
|
@@ -5948,9 +5988,14 @@ COMMIT;`;
|
|
|
5948
5988
|
if (!containerName) {
|
|
5949
5989
|
return { content: [{ type: "text", text: `Error: engine=${engine} requires containerName (the docker container running the DB server, e.g. "supabase-db" or "trigger-postgres")` }] };
|
|
5950
5990
|
}
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
const { conn, proxy } = await getServerConnection(String(a.serverId));
|
|
5991
|
+
let dbName = typeof a.dbName === "string" && a.dbName ? a.dbName.replace(/[^a-zA-Z0-9_-]/g, "") : "";
|
|
5992
|
+
let dbUser = typeof a.dbUser === "string" && a.dbUser ? a.dbUser.replace(/[^a-zA-Z0-9_-]/g, "") : "";
|
|
5993
|
+
const { serverId, conn, proxy } = await getServerConnection(String(a.serverId));
|
|
5994
|
+
if (engine === "postgres" && (!dbName || !dbUser)) {
|
|
5995
|
+
const creds = await resolvePostgresContainerCreds(conn, proxy, serverId, containerName);
|
|
5996
|
+
if (!dbName) dbName = creds.dbName.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
5997
|
+
if (!dbUser) dbUser = creds.dbUser.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
5998
|
+
}
|
|
5954
5999
|
let cmd;
|
|
5955
6000
|
let stdinPayload;
|
|
5956
6001
|
if (engine === "postgres") {
|
|
@@ -5988,8 +6033,8 @@ GO
|
|
|
5988
6033
|
case "db-apply-migration": {
|
|
5989
6034
|
const containerName = String(a.containerName || "").replace(/[^a-zA-Z0-9._-]/g, "");
|
|
5990
6035
|
if (!containerName) return { content: [{ type: "text", text: "Error: containerName is required" }] };
|
|
5991
|
-
|
|
5992
|
-
|
|
6036
|
+
let dbUser = (typeof a.dbUser === "string" && a.dbUser ? a.dbUser : "").replace(/[^a-zA-Z0-9_-]/g, "");
|
|
6037
|
+
let dbName = (typeof a.dbName === "string" && a.dbName ? a.dbName : "").replace(/[^a-zA-Z0-9_-]/g, "");
|
|
5993
6038
|
const name2 = String(a.name || "").trim();
|
|
5994
6039
|
if (!name2) return { content: [{ type: "text", text: 'Error: name is required (e.g. "20260517120000_add_foo")' }] };
|
|
5995
6040
|
if (!/^[\w.@:+\-]+$/.test(name2) || name2.length > 200) {
|
|
@@ -6008,7 +6053,12 @@ GO
|
|
|
6008
6053
|
if (sources.length !== 1) {
|
|
6009
6054
|
return { content: [{ type: "text", text: `Error: pass exactly one of \`sql\`, \`localFile\`, or \`remoteFile\` (got ${sources.length || "none"}: ${sources.join(", ") || "\u2014"})` }] };
|
|
6010
6055
|
}
|
|
6011
|
-
const { conn, proxy } = await getServerConnection(String(a.serverId));
|
|
6056
|
+
const { serverId, conn, proxy } = await getServerConnection(String(a.serverId));
|
|
6057
|
+
if (!dbUser || !dbName) {
|
|
6058
|
+
const creds = await resolvePostgresContainerCreds(conn, proxy, serverId, containerName);
|
|
6059
|
+
if (!dbUser) dbUser = creds.dbUser.replace(/[^a-zA-Z0-9_-]/g, "") || "postgres";
|
|
6060
|
+
if (!dbName) dbName = creds.dbName.replace(/[^a-zA-Z0-9_-]/g, "") || "postgres";
|
|
6061
|
+
}
|
|
6012
6062
|
let migrationSql;
|
|
6013
6063
|
if (typeof a.sql === "string" && a.sql) {
|
|
6014
6064
|
migrationSql = a.sql;
|
|
@@ -6168,11 +6218,16 @@ ${res.stdout.trim()}` : "(no output)")
|
|
|
6168
6218
|
case "db-list-migrations": {
|
|
6169
6219
|
const containerName = String(a.containerName || "").replace(/[^a-zA-Z0-9._-]/g, "");
|
|
6170
6220
|
if (!containerName) return { content: [{ type: "text", text: "Error: containerName is required" }] };
|
|
6171
|
-
|
|
6172
|
-
|
|
6221
|
+
let dbUser = (typeof a.dbUser === "string" && a.dbUser ? a.dbUser : "").replace(/[^a-zA-Z0-9_-]/g, "");
|
|
6222
|
+
let dbName = (typeof a.dbName === "string" && a.dbName ? a.dbName : "").replace(/[^a-zA-Z0-9_-]/g, "");
|
|
6173
6223
|
const limitRaw = Number(a.limit);
|
|
6174
6224
|
const limit = Math.min(Math.max(Number.isFinite(limitRaw) && limitRaw > 0 ? Math.floor(limitRaw) : 50, 1), 500);
|
|
6175
|
-
const { conn, proxy } = await getServerConnection(String(a.serverId));
|
|
6225
|
+
const { serverId, conn, proxy } = await getServerConnection(String(a.serverId));
|
|
6226
|
+
if (!dbUser || !dbName) {
|
|
6227
|
+
const creds = await resolvePostgresContainerCreds(conn, proxy, serverId, containerName);
|
|
6228
|
+
if (!dbUser) dbUser = creds.dbUser.replace(/[^a-zA-Z0-9_-]/g, "") || "postgres";
|
|
6229
|
+
if (!dbName) dbName = creds.dbName.replace(/[^a-zA-Z0-9_-]/g, "") || "postgres";
|
|
6230
|
+
}
|
|
6176
6231
|
const sqlText = `SELECT name, sha256, applied_at, applied_by
|
|
6177
6232
|
FROM _mcp_migrations
|
|
6178
6233
|
ORDER BY applied_at DESC
|