localwp-mcp 0.1.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/LICENSE +15 -0
- package/README.md +229 -0
- package/dist/backup.d.ts +72 -0
- package/dist/backup.js +351 -0
- package/dist/backup.js.map +1 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.js +58 -0
- package/dist/config.js.map +1 -0
- package/dist/environment-check.d.ts +187 -0
- package/dist/environment-check.js +174 -0
- package/dist/environment-check.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/local-doctor.d.ts +42 -0
- package/dist/local-doctor.js +224 -0
- package/dist/local-doctor.js.map +1 -0
- package/dist/local-sites.d.ts +103 -0
- package/dist/local-sites.js +234 -0
- package/dist/local-sites.js.map +1 -0
- package/dist/local-tooling.d.ts +2 -0
- package/dist/local-tooling.js +23 -0
- package/dist/local-tooling.js.map +1 -0
- package/dist/logs.d.ts +144 -0
- package/dist/logs.js +208 -0
- package/dist/logs.js.map +1 -0
- package/dist/mysql.d.ts +17 -0
- package/dist/mysql.js +229 -0
- package/dist/mysql.js.map +1 -0
- package/dist/permissions.d.ts +4 -0
- package/dist/permissions.js +129 -0
- package/dist/permissions.js.map +1 -0
- package/dist/platform-paths.d.ts +10 -0
- package/dist/platform-paths.js +95 -0
- package/dist/platform-paths.js.map +1 -0
- package/dist/process-utils.d.ts +13 -0
- package/dist/process-utils.js +128 -0
- package/dist/process-utils.js.map +1 -0
- package/dist/prompts.d.ts +2 -0
- package/dist/prompts.js +64 -0
- package/dist/prompts.js.map +1 -0
- package/dist/resources.d.ts +2 -0
- package/dist/resources.js +110 -0
- package/dist/resources.js.map +1 -0
- package/dist/results.d.ts +15 -0
- package/dist/results.js +26 -0
- package/dist/results.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +23 -0
- package/dist/server.js.map +1 -0
- package/dist/tool-schemas.d.ts +14 -0
- package/dist/tool-schemas.js +20 -0
- package/dist/tool-schemas.js.map +1 -0
- package/dist/tools/backup-site.d.ts +2 -0
- package/dist/tools/backup-site.js +42 -0
- package/dist/tools/backup-site.js.map +1 -0
- package/dist/tools/db-export.d.ts +2 -0
- package/dist/tools/db-export.js +40 -0
- package/dist/tools/db-export.js.map +1 -0
- package/dist/tools/db-import.d.ts +2 -0
- package/dist/tools/db-import.js +42 -0
- package/dist/tools/db-import.js.map +1 -0
- package/dist/tools/execute-wp-cli.d.ts +2 -0
- package/dist/tools/execute-wp-cli.js +34 -0
- package/dist/tools/execute-wp-cli.js.map +1 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +29 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-local-sites.d.ts +2 -0
- package/dist/tools/list-local-sites.js +32 -0
- package/dist/tools/list-local-sites.js.map +1 -0
- package/dist/tools/local-doctor.d.ts +2 -0
- package/dist/tools/local-doctor.js +23 -0
- package/dist/tools/local-doctor.js.map +1 -0
- package/dist/tools/local-environment-check.d.ts +2 -0
- package/dist/tools/local-environment-check.js +32 -0
- package/dist/tools/local-environment-check.js.map +1 -0
- package/dist/tools/local-logs.d.ts +2 -0
- package/dist/tools/local-logs.js +31 -0
- package/dist/tools/local-logs.js.map +1 -0
- package/dist/tools/local-site-info.d.ts +2 -0
- package/dist/tools/local-site-info.js +46 -0
- package/dist/tools/local-site-info.js.map +1 -0
- package/dist/tools/mysql-execute.d.ts +2 -0
- package/dist/tools/mysql-execute.js +46 -0
- package/dist/tools/mysql-execute.js.map +1 -0
- package/dist/tools/mysql-query.d.ts +2 -0
- package/dist/tools/mysql-query.js +43 -0
- package/dist/tools/mysql-query.js.map +1 -0
- package/dist/tools/mysql-schema.d.ts +2 -0
- package/dist/tools/mysql-schema.js +50 -0
- package/dist/tools/mysql-schema.js.map +1 -0
- package/dist/tools/restore-backup.d.ts +2 -0
- package/dist/tools/restore-backup.js +52 -0
- package/dist/tools/restore-backup.js.map +1 -0
- package/dist/types.d.ts +84 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/wp-cli.d.ts +14 -0
- package/dist/wp-cli.js +140 -0
- package/dist/wp-cli.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { buildSiteContext, summarizeSite, } from "../local-sites.js";
|
|
3
|
+
import { clampMaxRows, executeMysqlStatement, ensureMysqlReady, validateSafeSqlQuery, } from "../mysql.js";
|
|
4
|
+
import { createErrorToolResult, createJsonToolResult } from "../results.js";
|
|
5
|
+
import { siteSelectorSchema } from "../tool-schemas.js";
|
|
6
|
+
export function registerMysqlQueryTool(server) {
|
|
7
|
+
server.registerTool("mysql_query", {
|
|
8
|
+
description: "Runs a SQL query against the selected LocalWP site's MySQL database in the 'safe' profile. Only SELECT, SHOW, DESCRIBE, DESC, and EXPLAIN are allowed.",
|
|
9
|
+
inputSchema: {
|
|
10
|
+
...siteSelectorSchema,
|
|
11
|
+
sql: z
|
|
12
|
+
.string()
|
|
13
|
+
.min(1)
|
|
14
|
+
.describe("A single SQL statement allowed by the 'safe' profile."),
|
|
15
|
+
maxRows: z
|
|
16
|
+
.number()
|
|
17
|
+
.int()
|
|
18
|
+
.positive()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("Maximum rows to return to the client."),
|
|
21
|
+
},
|
|
22
|
+
annotations: {
|
|
23
|
+
readOnlyHint: true,
|
|
24
|
+
},
|
|
25
|
+
}, async ({ siteId, siteName, sql, maxRows }) => {
|
|
26
|
+
try {
|
|
27
|
+
const context = await buildSiteContext({ siteId, siteName });
|
|
28
|
+
await ensureMysqlReady(context);
|
|
29
|
+
const safeSql = validateSafeSqlQuery(sql);
|
|
30
|
+
const result = await executeMysqlStatement(context, safeSql, clampMaxRows(maxRows));
|
|
31
|
+
return createJsonToolResult({
|
|
32
|
+
site: summarizeSite(context.site),
|
|
33
|
+
selectionMethod: context.selectionMethod,
|
|
34
|
+
sql: safeSql,
|
|
35
|
+
...result,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
return createErrorToolResult(error);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=mysql-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql-query.js","sourceRoot":"","sources":["../../src/tools/mysql-query.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EACL,gBAAgB,EAChB,aAAa,GACd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,UAAU,sBAAsB,CAAC,MAAiB;IACtD,MAAM,CAAC,YAAY,CACjB,aAAa,EACb;QACE,WAAW,EACT,wJAAwJ;QAC1J,WAAW,EAAE;YACX,GAAG,kBAAkB;YACrB,GAAG,EAAE,CAAC;iBACH,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,CAAC,uDAAuD,CAAC;YACpE,OAAO,EAAE,CAAC;iBACP,MAAM,EAAE;iBACR,GAAG,EAAE;iBACL,QAAQ,EAAE;iBACV,QAAQ,EAAE;iBACV,QAAQ,CAAC,uCAAuC,CAAC;SACrD;QACD,WAAW,EAAE;YACX,YAAY,EAAE,IAAI;SACnB;KACF,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE;QAC3C,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC7D,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAC;YAEhC,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;YAC1C,MAAM,MAAM,GAAG,MAAM,qBAAqB,CACxC,OAAO,EACP,OAAO,EACP,YAAY,CAAC,OAAO,CAAC,CACtB,CAAC;YAEF,OAAO,oBAAoB,CAAC;gBAC1B,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC;gBACjC,eAAe,EAAE,OAAO,CAAC,eAAe;gBACxC,GAAG,EAAE,OAAO;gBACZ,GAAG,MAAM;aACV,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,qBAAqB,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { buildSiteContext, summarizeSite } from "../local-sites.js";
|
|
3
|
+
import { buildDescribeTableSql, buildListTablesSql, clampMaxRows, executeMysqlStatement, ensureMysqlReady, } from "../mysql.js";
|
|
4
|
+
import { createErrorToolResult, createJsonToolResult } from "../results.js";
|
|
5
|
+
import { siteSelectorSchema } from "../tool-schemas.js";
|
|
6
|
+
export function registerMysqlSchemaTool(server) {
|
|
7
|
+
server.registerTool("mysql_schema", {
|
|
8
|
+
description: "Returns table or column metadata for the selected LocalWP site's database without needing free-form SQL.",
|
|
9
|
+
inputSchema: {
|
|
10
|
+
...siteSelectorSchema,
|
|
11
|
+
table: z
|
|
12
|
+
.string()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe("Optional table name. If omitted, the tool lists tables in the site's database."),
|
|
15
|
+
tableName: z
|
|
16
|
+
.string()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Backward-compatible alias for 'table'. If provided, it is treated the same way."),
|
|
19
|
+
maxRows: z
|
|
20
|
+
.number()
|
|
21
|
+
.int()
|
|
22
|
+
.positive()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe("Maximum rows to return to the client."),
|
|
25
|
+
},
|
|
26
|
+
annotations: {
|
|
27
|
+
readOnlyHint: true,
|
|
28
|
+
},
|
|
29
|
+
}, async ({ siteId, siteName, table, tableName, maxRows }) => {
|
|
30
|
+
try {
|
|
31
|
+
const context = await buildSiteContext({ siteId, siteName });
|
|
32
|
+
await ensureMysqlReady(context);
|
|
33
|
+
const selectedTable = table || tableName;
|
|
34
|
+
const sql = selectedTable
|
|
35
|
+
? buildDescribeTableSql(context.database, selectedTable)
|
|
36
|
+
: buildListTablesSql(context.database);
|
|
37
|
+
const result = await executeMysqlStatement(context, sql, clampMaxRows(maxRows));
|
|
38
|
+
return createJsonToolResult({
|
|
39
|
+
site: summarizeSite(context.site),
|
|
40
|
+
selectionMethod: context.selectionMethod,
|
|
41
|
+
table: selectedTable || null,
|
|
42
|
+
...result,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
return createErrorToolResult(error);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=mysql-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql-schema.js","sourceRoot":"","sources":["../../src/tools/mysql-schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,YAAY,EACZ,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,UAAU,uBAAuB,CAAC,MAAiB;IACvD,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,WAAW,EACT,0GAA0G;QAC5G,WAAW,EAAE;YACX,GAAG,kBAAkB;YACrB,KAAK,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CACP,gFAAgF,CACjF;YACH,SAAS,EAAE,CAAC;iBACT,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CACP,iFAAiF,CAClF;YACH,OAAO,EAAE,CAAC;iBACP,MAAM,EAAE;iBACR,GAAG,EAAE;iBACL,QAAQ,EAAE;iBACV,QAAQ,EAAE;iBACV,QAAQ,CAAC,uCAAuC,CAAC;SACrD;QACD,WAAW,EAAE;YACX,YAAY,EAAE,IAAI;SACnB;KACF,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE;QACxD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC7D,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAC;YAChC,MAAM,aAAa,GAAG,KAAK,IAAI,SAAS,CAAC;YAEzC,MAAM,GAAG,GAAG,aAAa;gBACvB,CAAC,CAAC,qBAAqB,CAAC,OAAO,CAAC,QAAQ,EAAE,aAAa,CAAC;gBACxD,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACzC,MAAM,MAAM,GAAG,MAAM,qBAAqB,CACxC,OAAO,EACP,GAAG,EACH,YAAY,CAAC,OAAO,CAAC,CACtB,CAAC;YAEF,OAAO,oBAAoB,CAAC;gBAC1B,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC;gBACjC,eAAe,EAAE,OAAO,CAAC,eAAe;gBACxC,KAAK,EAAE,aAAa,IAAI,IAAI;gBAC5B,GAAG,MAAM;aACV,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,qBAAqB,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { restoreSiteBackup } from "../backup.js";
|
|
3
|
+
import { config } from "../config.js";
|
|
4
|
+
import { buildSiteContext, summarizeSite } from "../local-sites.js";
|
|
5
|
+
import { createErrorToolResult, createJsonToolResult } from "../results.js";
|
|
6
|
+
import { siteSelectorSchema } from "../tool-schemas.js";
|
|
7
|
+
export function registerRestoreBackupTool(server) {
|
|
8
|
+
server.registerTool("restore_backup", {
|
|
9
|
+
description: "Restores a LocalWP site from a .sql file or a backup_site directory. In full-access mode it can restore the database and, when available, the backup's app/conf/logs directories.",
|
|
10
|
+
inputSchema: {
|
|
11
|
+
...siteSelectorSchema,
|
|
12
|
+
sourcePath: z
|
|
13
|
+
.string()
|
|
14
|
+
.min(1)
|
|
15
|
+
.describe("Path to a .sql file or a backup_site directory containing manifest.json."),
|
|
16
|
+
restoreFiles: z
|
|
17
|
+
.boolean()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("When true or omitted, also restore app/conf/logs if the backup directory contains them."),
|
|
20
|
+
replaceDirectories: z
|
|
21
|
+
.boolean()
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("When true or omitted, replace the target app/conf/logs directories before copying the backup versions."),
|
|
24
|
+
backupBeforeRestore: z
|
|
25
|
+
.boolean()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("When true or omitted, create a pre-restore backup automatically first."),
|
|
28
|
+
},
|
|
29
|
+
}, async ({ siteId, siteName, sourcePath, restoreFiles, replaceDirectories, backupBeforeRestore, }) => {
|
|
30
|
+
try {
|
|
31
|
+
if (config.profile !== "full-access") {
|
|
32
|
+
throw new Error("restore_backup requires LOCALWP_MCP_PROFILE=full-access.");
|
|
33
|
+
}
|
|
34
|
+
const context = await buildSiteContext({ siteId, siteName });
|
|
35
|
+
const result = await restoreSiteBackup(context, sourcePath, {
|
|
36
|
+
restoreFiles,
|
|
37
|
+
replaceDirectories,
|
|
38
|
+
backupBeforeRestore,
|
|
39
|
+
});
|
|
40
|
+
return createJsonToolResult({
|
|
41
|
+
site: summarizeSite(context.site),
|
|
42
|
+
selectionMethod: context.selectionMethod,
|
|
43
|
+
accessProfile: config.profile,
|
|
44
|
+
...result,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
return createErrorToolResult(error);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=restore-backup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restore-backup.js","sourceRoot":"","sources":["../../src/tools/restore-backup.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,UAAU,yBAAyB,CAAC,MAAiB;IACzD,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;QACE,WAAW,EACT,mLAAmL;QACrL,WAAW,EAAE;YACX,GAAG,kBAAkB;YACrB,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,CACP,0EAA0E,CAC3E;YACH,YAAY,EAAE,CAAC;iBACZ,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CACP,yFAAyF,CAC1F;YACH,kBAAkB,EAAE,CAAC;iBAClB,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CACP,wGAAwG,CACzG;YACH,mBAAmB,EAAE,CAAC;iBACnB,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CACP,wEAAwE,CACzE;SACJ;KACF,EACD,KAAK,EAAE,EACL,MAAM,EACN,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,GACpB,EAAE,EAAE;QACH,IAAI,CAAC;YACH,IAAI,MAAM,CAAC,OAAO,KAAK,aAAa,EAAE,CAAC;gBACrC,MAAM,IAAI,KAAK,CACb,0DAA0D,CAC3D,CAAC;YACJ,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC7D,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,OAAO,EAAE,UAAU,EAAE;gBAC1D,YAAY;gBACZ,kBAAkB;gBAClB,mBAAmB;aACpB,CAAC,CAAC;YAEH,OAAO,oBAAoB,CAAC;gBAC1B,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC;gBACjC,eAAe,EAAE,OAAO,CAAC,eAAe;gBACxC,aAAa,EAAE,MAAM,CAAC,OAAO;gBAC7B,GAAG,MAAM;aACV,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,qBAAqB,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export type AccessProfile = "safe" | "full-access";
|
|
2
|
+
export type LogScope = "site" | "global" | "all";
|
|
3
|
+
export type BackupScope = "database" | "full";
|
|
4
|
+
export type LocalSiteStatus = "running" | "halted" | "unknown" | string;
|
|
5
|
+
export interface LocalSiteServices {
|
|
6
|
+
nginx?: {
|
|
7
|
+
ports?: {
|
|
8
|
+
HTTP?: number[];
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
mysql?: {
|
|
12
|
+
version?: string;
|
|
13
|
+
ports?: {
|
|
14
|
+
MYSQL?: number[];
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
php?: {
|
|
18
|
+
version?: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface LocalSiteRecord {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
path: string;
|
|
25
|
+
domain?: string;
|
|
26
|
+
localVersion?: string;
|
|
27
|
+
mysql?: {
|
|
28
|
+
database?: string;
|
|
29
|
+
user?: string;
|
|
30
|
+
password?: string;
|
|
31
|
+
};
|
|
32
|
+
services?: LocalSiteServices;
|
|
33
|
+
}
|
|
34
|
+
export interface LocalSite extends LocalSiteRecord {
|
|
35
|
+
absolutePath: string;
|
|
36
|
+
wpRoot: string;
|
|
37
|
+
runtimeDir: string;
|
|
38
|
+
status: LocalSiteStatus;
|
|
39
|
+
selectionMethod?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface SiteSelection {
|
|
42
|
+
siteId?: string;
|
|
43
|
+
siteName?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface ServiceBinary {
|
|
46
|
+
packageDir: string;
|
|
47
|
+
platformDirName: string | null;
|
|
48
|
+
binaryPath: string;
|
|
49
|
+
layout: "lightning-services" | "site-binaries";
|
|
50
|
+
}
|
|
51
|
+
export interface SiteContext {
|
|
52
|
+
site: LocalSite;
|
|
53
|
+
selectionMethod?: string;
|
|
54
|
+
runtimeDir: string;
|
|
55
|
+
wpRoot: string;
|
|
56
|
+
database: string;
|
|
57
|
+
phpConfigDir: string;
|
|
58
|
+
mysqlDefaultsFile: string;
|
|
59
|
+
mysqlSocket: string | null;
|
|
60
|
+
mysqlPort: number | null;
|
|
61
|
+
mysqlHost: string | null;
|
|
62
|
+
php: ServiceBinary;
|
|
63
|
+
mysql: ServiceBinary;
|
|
64
|
+
magickCoderModulePath: string;
|
|
65
|
+
}
|
|
66
|
+
export interface ResolvedLocalTooling {
|
|
67
|
+
wpCliPhar: string;
|
|
68
|
+
wpCliConfig: string | null;
|
|
69
|
+
helperBinDirs: string[];
|
|
70
|
+
}
|
|
71
|
+
export interface SpawnResult {
|
|
72
|
+
stdout: string;
|
|
73
|
+
stderr: string;
|
|
74
|
+
exitCode: number;
|
|
75
|
+
timedOut: boolean;
|
|
76
|
+
}
|
|
77
|
+
export interface MysqlQueryResult {
|
|
78
|
+
columns: string[];
|
|
79
|
+
rows: Record<string, unknown>[];
|
|
80
|
+
totalRows: number;
|
|
81
|
+
returnedRows: number;
|
|
82
|
+
truncated: boolean;
|
|
83
|
+
stderr: string | null;
|
|
84
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/dist/wp-cli.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SiteContext } from "./types.js";
|
|
2
|
+
export declare function runWpCli(context: SiteContext, command: string, options?: {
|
|
3
|
+
skipPermissionCheck?: boolean;
|
|
4
|
+
}): Promise<{
|
|
5
|
+
stdout: string;
|
|
6
|
+
stderr: string;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function runWpCliArgs(context: SiteContext, rawArgs: string[], options?: {
|
|
9
|
+
skipPermissionCheck?: boolean;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
stdout: string;
|
|
12
|
+
stderr: string;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function tokenizeCommand(command: string): string[];
|
package/dist/wp-cli.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { config } from "./config.js";
|
|
3
|
+
import { resolveLocalTooling } from "./local-tooling.js";
|
|
4
|
+
import { assertAllowedWpFlags, assertWpCliPermissions } from "./permissions.js";
|
|
5
|
+
import { spawnCommand } from "./process-utils.js";
|
|
6
|
+
export async function runWpCli(context, command, options = {}) {
|
|
7
|
+
return runWpCliArgs(context, tokenizeCommand(command), options);
|
|
8
|
+
}
|
|
9
|
+
export async function runWpCliArgs(context, rawArgs, options = {}) {
|
|
10
|
+
const args = [...rawArgs];
|
|
11
|
+
if (args[0] === "wp") {
|
|
12
|
+
args.shift();
|
|
13
|
+
}
|
|
14
|
+
if (args.length === 0) {
|
|
15
|
+
throw new Error("Please pass WP-CLI arguments only, for example 'plugin list'.");
|
|
16
|
+
}
|
|
17
|
+
assertAllowedWpFlags(args);
|
|
18
|
+
if (!options.skipPermissionCheck) {
|
|
19
|
+
assertWpCliPermissions(args);
|
|
20
|
+
}
|
|
21
|
+
const tooling = await resolveLocalTooling();
|
|
22
|
+
const env = buildWpCliEnv(context, tooling);
|
|
23
|
+
const processArgs = [tooling.wpCliPhar, `--path=${context.wpRoot}`, ...args];
|
|
24
|
+
const result = await spawnCommand(context.php.binaryPath, processArgs, {
|
|
25
|
+
cwd: context.wpRoot,
|
|
26
|
+
env,
|
|
27
|
+
});
|
|
28
|
+
if (result.timedOut) {
|
|
29
|
+
throw new Error(`WP-CLI timed out after ${config.defaultTimeoutMs / 1000}s for site '${context.site.name}'.`);
|
|
30
|
+
}
|
|
31
|
+
if (result.exitCode !== 0) {
|
|
32
|
+
throw new Error([
|
|
33
|
+
`WP-CLI exited with code ${result.exitCode} for site '${context.site.name}'.`,
|
|
34
|
+
result.stdout,
|
|
35
|
+
result.stderr,
|
|
36
|
+
]
|
|
37
|
+
.filter(Boolean)
|
|
38
|
+
.join("\n\n"));
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
stdout: result.stdout || "Command executed successfully with no output.",
|
|
42
|
+
stderr: result.stderr,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function buildWpCliEnv(context, tooling) {
|
|
46
|
+
const env = {
|
|
47
|
+
...process.env,
|
|
48
|
+
PHPRC: context.phpConfigDir,
|
|
49
|
+
WP_CLI_DISABLE_AUTO_CHECK_UPDATE: "1",
|
|
50
|
+
PATH: [
|
|
51
|
+
path.dirname(context.mysql.binaryPath),
|
|
52
|
+
path.dirname(context.php.binaryPath),
|
|
53
|
+
path.dirname(tooling.wpCliPhar),
|
|
54
|
+
...tooling.helperBinDirs,
|
|
55
|
+
process.env.PATH,
|
|
56
|
+
]
|
|
57
|
+
.filter(Boolean)
|
|
58
|
+
.join(path.delimiter),
|
|
59
|
+
};
|
|
60
|
+
if (tooling.wpCliConfig) {
|
|
61
|
+
env.WP_CLI_CONFIG_PATH = tooling.wpCliConfig;
|
|
62
|
+
}
|
|
63
|
+
if (context.magickCoderModulePath) {
|
|
64
|
+
env.MAGICK_CODER_MODULE_PATH = context.magickCoderModulePath;
|
|
65
|
+
}
|
|
66
|
+
return env;
|
|
67
|
+
}
|
|
68
|
+
export function tokenizeCommand(command) {
|
|
69
|
+
const input = command.trim();
|
|
70
|
+
if (!input) {
|
|
71
|
+
throw new Error("Command cannot be empty.");
|
|
72
|
+
}
|
|
73
|
+
const tokens = [];
|
|
74
|
+
let current = "";
|
|
75
|
+
let quote = null;
|
|
76
|
+
let escaping = false;
|
|
77
|
+
let buildingToken = false;
|
|
78
|
+
for (const character of input) {
|
|
79
|
+
if (escaping) {
|
|
80
|
+
current += character;
|
|
81
|
+
escaping = false;
|
|
82
|
+
buildingToken = true;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (quote === "'") {
|
|
86
|
+
if (character === "'") {
|
|
87
|
+
quote = null;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
current += character;
|
|
91
|
+
}
|
|
92
|
+
buildingToken = true;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (quote === '"') {
|
|
96
|
+
if (character === '"') {
|
|
97
|
+
quote = null;
|
|
98
|
+
}
|
|
99
|
+
else if (character === "\\") {
|
|
100
|
+
escaping = true;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
current += character;
|
|
104
|
+
}
|
|
105
|
+
buildingToken = true;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (character === "'" || character === '"') {
|
|
109
|
+
quote = character;
|
|
110
|
+
buildingToken = true;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (character === "\\") {
|
|
114
|
+
escaping = true;
|
|
115
|
+
buildingToken = true;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (/\s/.test(character)) {
|
|
119
|
+
if (buildingToken) {
|
|
120
|
+
tokens.push(current);
|
|
121
|
+
current = "";
|
|
122
|
+
buildingToken = false;
|
|
123
|
+
}
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
current += character;
|
|
127
|
+
buildingToken = true;
|
|
128
|
+
}
|
|
129
|
+
if (quote) {
|
|
130
|
+
throw new Error("Command contains an unterminated quote.");
|
|
131
|
+
}
|
|
132
|
+
if (escaping) {
|
|
133
|
+
throw new Error("Command ends with an unfinished escape sequence.");
|
|
134
|
+
}
|
|
135
|
+
if (buildingToken) {
|
|
136
|
+
tokens.push(current);
|
|
137
|
+
}
|
|
138
|
+
return tokens;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=wp-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wp-cli.js","sourceRoot":"","sources":["../src/wp-cli.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "localwp-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A LocalWP-aware MCP server for WordPress developers with simple safe and full-access profiles, logs, doctor diagnostics, SQL access, backups, restore flows, MCP resources/prompts, and site-aware WP-CLI.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"packageManager": "pnpm@10.32.1",
|
|
7
|
+
"main": "./dist/server.js",
|
|
8
|
+
"types": "./dist/server.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/server.d.ts",
|
|
12
|
+
"import": "./dist/server.js"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"localwp-mcp": "dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"LICENSE",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc -p tsconfig.json",
|
|
29
|
+
"check": "tsc --noEmit -p tsconfig.json",
|
|
30
|
+
"clean": "rm -rf dist",
|
|
31
|
+
"dev": "tsx src/index.ts",
|
|
32
|
+
"prepublishOnly": "pnpm clean && pnpm check && pnpm test && pnpm build",
|
|
33
|
+
"start": "node ./dist/index.js",
|
|
34
|
+
"test": "tsx --test tests/**/*.test.ts"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"mcp",
|
|
38
|
+
"model-context-protocol",
|
|
39
|
+
"wordpress",
|
|
40
|
+
"localwp",
|
|
41
|
+
"wp-cli",
|
|
42
|
+
"mysql",
|
|
43
|
+
"backup",
|
|
44
|
+
"restore",
|
|
45
|
+
"logs",
|
|
46
|
+
"developer-tools"
|
|
47
|
+
],
|
|
48
|
+
"license": "ISC",
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=20"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
54
|
+
"zod": "^4.3.6"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^24.6.0",
|
|
58
|
+
"tsx": "^4.20.5",
|
|
59
|
+
"typescript": "^5.9.3"
|
|
60
|
+
}
|
|
61
|
+
}
|