@pkagentic/mcp 1.4.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 ADDED
@@ -0,0 +1,745 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ import dotenv from "dotenv";
6
+ import { PKAgentApi, UtilityApi, WebCrawlerApi, ImageGenerationApi, TailwindApi } from "./handlers/index.js";
7
+ dotenv.config();
8
+ // Disable TLS certificate verification for local development (DDEV)
9
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
10
+ const API_BASE_URL = process.env.PK_AGENT_API_URL;
11
+ const API_KEY = process.env.PK_AGENT_KEY;
12
+ const API_EMAIL = process.env.PK_AGENT_EMAIL;
13
+ const GEMINI_API_KEY = process.env.GEMINI_API_KEY;
14
+ const GEMINI_MODEL = process.env.GEMINI_MODEL;
15
+ if (!API_BASE_URL || !API_KEY || !API_EMAIL) {
16
+ console.error("Error: PK_AGENT_API_URL, PK_AGENT_KEY, and PK_AGENT_EMAIL environment variables are required.");
17
+ process.exit(1);
18
+ }
19
+ class PKAgentMcpServer {
20
+ server;
21
+ api;
22
+ utility;
23
+ web;
24
+ image;
25
+ tailwind;
26
+ constructor() {
27
+ this.server = new McpServer({
28
+ name: "pk-agent-mcp",
29
+ version: "1.4.0",
30
+ });
31
+ this.api = new PKAgentApi(API_BASE_URL, API_KEY, API_EMAIL);
32
+ this.utility = new UtilityApi();
33
+ this.web = new WebCrawlerApi();
34
+ this.image = new ImageGenerationApi(GEMINI_API_KEY, GEMINI_MODEL);
35
+ this.tailwind = new TailwindApi();
36
+ this.setupTools();
37
+ // Error handling
38
+ this.server.server.onerror = (error) => console.error("[MCP Error]", error);
39
+ process.on("SIGINT", async () => {
40
+ await this.server.close();
41
+ process.exit(0);
42
+ });
43
+ }
44
+ setupTools() {
45
+ // Info
46
+ this.server.registerTool("get_site_info", {
47
+ description: "Get WordPress site metadata including site name/URL, plugin version, WordPress version, PHP version, active theme (with parent theme if child theme), active plugins, permissions, batch limits, and menu locations. Automatically writes the full response to .pk-agentic/site-info.json and returns a summary with key fields.",
48
+ }, async () => {
49
+ const result = await this.api.getSiteInfo();
50
+ return {
51
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
52
+ };
53
+ });
54
+ this.server.registerTool("sync", {
55
+ description: "List all items (templates, scripts, content) managed by the plugin. Automatically writes the full inventory to .pk-agentic/file-list.json and returns a pagination summary. For page > 1, new items are merged into the existing file. Check has_more and call again with the next page number until all pages are fetched.",
56
+ inputSchema: {
57
+ type: z.enum(["all", "template", "script", "content"]).optional().default("all"),
58
+ page: z.number().optional().default(1),
59
+ per_page: z.number().optional().default(25),
60
+ },
61
+ }, async (args) => {
62
+ const result = await this.api.syncItems(args);
63
+ return {
64
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
65
+ };
66
+ });
67
+ this.server.registerTool("get_file", {
68
+ description: "Download content (HTML/CSS/JS) for a single item. Full downloads (no from_line/to_line) write files to workspace/{type}/{slug}/ and return a summary with paths. Partial/line-range downloads return the requested lines inline without writing to disk (safe for inspecting large files). Templates/scripts use .php extension; content uses .html.",
69
+ inputSchema: {
70
+ id: z.number(),
71
+ version: z.enum(["sandbox", "production"]).optional().default("sandbox"),
72
+ download_type: z.enum(["all", "html", "css", "js"]).optional().default("all"),
73
+ from_line: z.number().optional(),
74
+ to_line: z.number().optional(),
75
+ },
76
+ }, async (args) => {
77
+ const result = await this.api.getFile(args);
78
+ return {
79
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
80
+ };
81
+ });
82
+ this.server.registerTool("get_files_batch", {
83
+ description: "Batch download full content for multiple items (max = batch_file_limit from get_site_info). Each item's files are written to workspace/{type}/{slug}/ automatically. Returns a summary of items downloaded and files written. Split large ID lists into chunks of batch_file_limit.",
84
+ inputSchema: {
85
+ ids: z.array(z.number()),
86
+ version: z.enum(["sandbox", "production"]).optional().default("sandbox"),
87
+ },
88
+ }, async (args) => {
89
+ const result = await this.api.getFilesBatch(args);
90
+ return {
91
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
92
+ };
93
+ });
94
+ this.server.registerTool("get_template_info", {
95
+ description: "Retrieve metadata for a template (conditions, sub_type, location_php, location_js, lock status) without code content.",
96
+ inputSchema: { id: z.number() },
97
+ }, async ({ id }) => {
98
+ const result = await this.api.getTemplateInfo(id);
99
+ return {
100
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
101
+ };
102
+ });
103
+ this.server.registerTool("get_script_info", {
104
+ description: "Retrieve metadata for a global script (conditions, location_js, location_php, mode, lock status) without code content.",
105
+ inputSchema: { id: z.number() },
106
+ }, async ({ id }) => {
107
+ const result = await this.api.getScriptInfo(id);
108
+ return {
109
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
110
+ };
111
+ });
112
+ this.server.registerTool("get_content_info", {
113
+ description: "Retrieve metadata for a content item (permalink, preview key, location_js, lock status) without code content.",
114
+ inputSchema: { id: z.number() },
115
+ }, async ({ id }) => {
116
+ const result = await this.api.getContentInfo(id);
117
+ return {
118
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
119
+ };
120
+ });
121
+ // Templates
122
+ this.server.registerTool("save_template", {
123
+ description: "Save template code and settings to sandbox (draft). Pass document_content_path to update the item's Markdown documentation from a local .md file.",
124
+ inputSchema: {
125
+ id: z.number(),
126
+ html_path: z.string().optional(),
127
+ css_path: z.string().optional(),
128
+ js_path: z.string().optional(),
129
+ document_content_path: z.string().optional(),
130
+ description: z.string().optional(),
131
+ priority_css: z.number().optional(),
132
+ priority_js: z.number().optional(),
133
+ location_js: z.enum(["head", "open_body", "foot"]).optional(),
134
+ location_php: z.object({}).passthrough().optional(),
135
+ },
136
+ }, async (args) => {
137
+ const result = await this.api.saveItem({ ...args, type: "templates" });
138
+ return {
139
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
140
+ };
141
+ });
142
+ this.server.registerTool("publish_template", {
143
+ description: "Publish template from sandbox to production.",
144
+ inputSchema: { id: z.number() },
145
+ }, async (args) => {
146
+ const result = await this.api.publishItem({ ...args, type: "templates" });
147
+ return {
148
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
149
+ };
150
+ });
151
+ this.server.registerTool("create_template", {
152
+ description: "Create a new template in sandbox.",
153
+ inputSchema: {
154
+ title: z.string(),
155
+ sub_type: z.enum(["header", "footer", "single", "archive", "404", "search", "front_page", "blog", "custom", "partial"]),
156
+ html_path: z.string().optional(),
157
+ css_path: z.string().optional(),
158
+ js_path: z.string().optional(),
159
+ conditions: z.object({}).passthrough().optional(),
160
+ priority_css: z.number().optional(),
161
+ priority_js: z.number().optional(),
162
+ location_js: z.enum(["head", "open_body", "foot"]).optional(),
163
+ location_php: z.object({}).passthrough().optional(),
164
+ },
165
+ }, async (args) => {
166
+ const result = await this.api.createItem({ ...args, type: "templates" });
167
+ return {
168
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
169
+ };
170
+ });
171
+ this.server.registerTool("update_template", {
172
+ description: "Update template metadata (title, slug).",
173
+ inputSchema: {
174
+ id: z.number(),
175
+ title: z.string().optional(),
176
+ slug: z.string().optional(),
177
+ },
178
+ }, async (args) => {
179
+ const result = await this.api.updateMetadata({ ...args, type: "templates" });
180
+ return {
181
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
182
+ };
183
+ });
184
+ this.server.registerTool("toggle_template", {
185
+ description: "Enable/Disable template.",
186
+ inputSchema: {
187
+ id: z.number(),
188
+ enabled: z.boolean().optional(),
189
+ },
190
+ }, async (args) => {
191
+ const result = await this.api.toggleItem({ ...args, type: "templates" });
192
+ return {
193
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
194
+ };
195
+ });
196
+ this.server.registerTool("update_template_conditions", {
197
+ description: "Update template display conditions. Supports unified groups format (v3) and legacy format.",
198
+ inputSchema: {
199
+ id: z.number(),
200
+ conditions: z.object({}).passthrough(),
201
+ },
202
+ }, async (args) => {
203
+ const result = await this.api.updateConditions({ ...args, type: "templates" });
204
+ return {
205
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
206
+ };
207
+ });
208
+ this.server.registerTool("delete_template", {
209
+ description: "Delete a template.",
210
+ inputSchema: { id: z.number() },
211
+ }, async (args) => {
212
+ const result = await this.api.deleteItem({ ...args, type: "templates" });
213
+ return {
214
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
215
+ };
216
+ });
217
+ this.server.registerTool("get_template_revisions", {
218
+ description: "List revisions for a template.",
219
+ inputSchema: { id: z.number() },
220
+ }, async (args) => {
221
+ const result = await this.api.getRevisions({ ...args, type: "templates" });
222
+ return {
223
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
224
+ };
225
+ });
226
+ this.server.registerTool("restore_template_revision", {
227
+ description: "Restore a template revision to sandbox.",
228
+ inputSchema: {
229
+ id: z.number(),
230
+ revision_id: z.number(),
231
+ },
232
+ }, async (args) => {
233
+ const result = await this.api.restoreRevision({ ...args, type: "templates" });
234
+ return {
235
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
236
+ };
237
+ });
238
+ // Scripts
239
+ this.server.registerTool("save_script", {
240
+ description: "Save script code and settings to sandbox (draft). Pass document_content_path to update the item's Markdown documentation from a local .md file.",
241
+ inputSchema: {
242
+ id: z.number(),
243
+ html_path: z.string().optional(),
244
+ css_path: z.string().optional(),
245
+ js_path: z.string().optional(),
246
+ document_content_path: z.string().optional(),
247
+ description: z.string().optional(),
248
+ priority_css: z.number().optional(),
249
+ priority_js: z.number().optional(),
250
+ location_js: z.enum(["head", "open_body", "foot"]).optional(),
251
+ script_mode: z.enum(["advanced", "php", "css", "js"]).optional(),
252
+ location_php: z.object({}).passthrough().optional(),
253
+ },
254
+ }, async (args) => {
255
+ const result = await this.api.saveItem({ ...args, type: "scripts" });
256
+ return {
257
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
258
+ };
259
+ });
260
+ this.server.registerTool("publish_script", {
261
+ description: "Publish script from sandbox to production.",
262
+ inputSchema: { id: z.number() },
263
+ }, async (args) => {
264
+ const result = await this.api.publishItem({ ...args, type: "scripts" });
265
+ return {
266
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
267
+ };
268
+ });
269
+ this.server.registerTool("create_script", {
270
+ description: "Create a new script in sandbox.",
271
+ inputSchema: {
272
+ title: z.string(),
273
+ html_path: z.string().optional(),
274
+ css_path: z.string().optional(),
275
+ js_path: z.string().optional(),
276
+ conditions: z.object({}).passthrough().optional(),
277
+ priority_css: z.number().optional(),
278
+ priority_js: z.number().optional(),
279
+ location_js: z.enum(["head", "open_body", "foot"]).optional(),
280
+ script_mode: z.enum(["advanced", "php", "css", "js"]).optional(),
281
+ location_php: z.object({}).passthrough().optional(),
282
+ },
283
+ }, async (args) => {
284
+ const result = await this.api.createItem({ ...args, type: "scripts" });
285
+ return {
286
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
287
+ };
288
+ });
289
+ this.server.registerTool("update_script", {
290
+ description: "Update script metadata (title, slug).",
291
+ inputSchema: {
292
+ id: z.number(),
293
+ title: z.string().optional(),
294
+ slug: z.string().optional(),
295
+ },
296
+ }, async (args) => {
297
+ const result = await this.api.updateMetadata({ ...args, type: "scripts" });
298
+ return {
299
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
300
+ };
301
+ });
302
+ this.server.registerTool("toggle_script", {
303
+ description: "Enable/Disable script.",
304
+ inputSchema: {
305
+ id: z.number(),
306
+ enabled: z.boolean().optional(),
307
+ },
308
+ }, async (args) => {
309
+ const result = await this.api.toggleItem({ ...args, type: "scripts" });
310
+ return {
311
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
312
+ };
313
+ });
314
+ this.server.registerTool("update_script_conditions", {
315
+ description: "Update script display conditions. Supports unified groups format (v3) and legacy format.",
316
+ inputSchema: {
317
+ id: z.number(),
318
+ conditions: z.object({}).passthrough(),
319
+ },
320
+ }, async (args) => {
321
+ const result = await this.api.updateConditions({ ...args, type: "scripts" });
322
+ return {
323
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
324
+ };
325
+ });
326
+ this.server.registerTool("delete_script", {
327
+ description: "Delete a script.",
328
+ inputSchema: { id: z.number() },
329
+ }, async (args) => {
330
+ const result = await this.api.deleteItem({ ...args, type: "scripts" });
331
+ return {
332
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
333
+ };
334
+ });
335
+ this.server.registerTool("get_script_revisions", {
336
+ description: "List revisions for a script.",
337
+ inputSchema: { id: z.number() },
338
+ }, async (args) => {
339
+ const result = await this.api.getRevisions({ ...args, type: "scripts" });
340
+ return {
341
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
342
+ };
343
+ });
344
+ this.server.registerTool("restore_script_revision", {
345
+ description: "Restore a script revision to sandbox.",
346
+ inputSchema: {
347
+ id: z.number(),
348
+ revision_id: z.number(),
349
+ },
350
+ }, async (args) => {
351
+ const result = await this.api.restoreRevision({ ...args, type: "scripts" });
352
+ return {
353
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
354
+ };
355
+ });
356
+ // Content
357
+ this.server.registerTool("save_content", {
358
+ description: "Save content code (HTML/CSS/JS) and settings to sandbox (draft). Pass document_content_path to update the item's Markdown documentation from a local .md file.",
359
+ inputSchema: {
360
+ id: z.number(),
361
+ html_path: z.string().optional(),
362
+ css_path: z.string().optional(),
363
+ js_path: z.string().optional(),
364
+ document_content_path: z.string().optional(),
365
+ description: z.string().optional(),
366
+ priority_css: z.number().optional(),
367
+ priority_js: z.number().optional(),
368
+ location_js: z.enum(["head", "open_body", "foot"]).optional(),
369
+ },
370
+ }, async (args) => {
371
+ const result = await this.api.saveItem({ ...args, type: "contents" });
372
+ return {
373
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
374
+ };
375
+ });
376
+ this.server.registerTool("publish_content", {
377
+ description: "Publish content from sandbox to production.",
378
+ inputSchema: { id: z.number() },
379
+ }, async (args) => {
380
+ const result = await this.api.publishItem({ ...args, type: "contents" });
381
+ return {
382
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
383
+ };
384
+ });
385
+ this.server.registerTool("create_content", {
386
+ description: "Create a new content item in sandbox.",
387
+ inputSchema: {
388
+ title: z.string(),
389
+ post_type: z.string(),
390
+ html_path: z.string().optional(),
391
+ css_path: z.string().optional(),
392
+ js_path: z.string().optional(),
393
+ priority_css: z.number().optional(),
394
+ priority_js: z.number().optional(),
395
+ location_js: z.enum(["head", "open_body", "foot"]).optional(),
396
+ },
397
+ }, async (args) => {
398
+ const result = await this.api.createItem({ ...args, type: "contents" });
399
+ return {
400
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
401
+ };
402
+ });
403
+ this.server.registerTool("update_content", {
404
+ description: "Update content metadata (title, slug).",
405
+ inputSchema: {
406
+ id: z.number(),
407
+ title: z.string().optional(),
408
+ slug: z.string().optional(),
409
+ },
410
+ }, async (args) => {
411
+ const result = await this.api.updateMetadata({ ...args, type: "contents" });
412
+ return {
413
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
414
+ };
415
+ });
416
+ // Search & Onboarding
417
+ this.server.registerTool("search_contents", {
418
+ description: "Search for WordPress content (posts, pages, products, etc.).",
419
+ inputSchema: {
420
+ search: z.string(),
421
+ post_type: z.string().optional().default("any"),
422
+ per_page: z.number().optional().default(20),
423
+ },
424
+ }, async (args) => {
425
+ const result = await this.api.searchContents(args);
426
+ return {
427
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
428
+ };
429
+ });
430
+ this.server.registerTool("mark_content_edit", {
431
+ description: "Register that a content item is being edited by the AI.",
432
+ inputSchema: { post_id: z.number() },
433
+ }, async (args) => {
434
+ const result = await this.api.markContentEdit(args);
435
+ return {
436
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
437
+ };
438
+ });
439
+ this.server.registerTool("search_media", {
440
+ description: "Search for media items by title or filename.",
441
+ inputSchema: {
442
+ search: z.string().optional(),
443
+ media_type: z.enum(["image", "svg", "pdf", "font"]).optional(),
444
+ per_page: z.number().optional().default(10),
445
+ },
446
+ }, async (args) => {
447
+ const result = await this.api.searchMedia(args);
448
+ return {
449
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
450
+ };
451
+ });
452
+ // Media & Utilities
453
+ this.server.registerTool("upload_media", {
454
+ description: "Upload a media file (image, SVG, PDF, or font) to the WordPress Media Library. Supports local file paths (multipart) or base64 data.",
455
+ inputSchema: {
456
+ file_path: z.string().optional(),
457
+ base64: z.string().optional(),
458
+ filename: z.string().optional().describe("Required if using base64."),
459
+ media_type: z.enum(["image", "svg", "pdf", "font"]).optional().default("image"),
460
+ title: z.string().optional(),
461
+ alt_text: z.string().optional(),
462
+ },
463
+ }, async (args) => {
464
+ const result = await this.api.uploadMedia(args);
465
+ return {
466
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
467
+ };
468
+ });
469
+ this.server.registerTool("get_image_srcset", {
470
+ description: "Get the WordPress-generated srcset string and basic metadata for an existing image attachment by attachment_id.",
471
+ inputSchema: {
472
+ attachment_id: z.number().describe("The WordPress attachment ID of the image."),
473
+ },
474
+ }, async (args) => {
475
+ const result = await this.api.getImageSrcset(args);
476
+ return {
477
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
478
+ };
479
+ });
480
+ this.server.registerTool("get_roles", {
481
+ description: "Get all available WordPress user roles.",
482
+ }, async () => {
483
+ const result = await this.api.getRoles();
484
+ return {
485
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
486
+ };
487
+ });
488
+ this.server.registerTool("download_image", {
489
+ description: "Download an image from a URL to a local temporary file.",
490
+ inputSchema: { url: z.string(), path: z.string() },
491
+ }, async (args) => {
492
+ const result = await this.utility.downloadImage(args);
493
+ return {
494
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
495
+ };
496
+ });
497
+ this.server.registerTool("optimize_image", {
498
+ description: "Resize and/or convert an image to WebP in one call. PNG, JPG, BMP, TIFF, and HEIC files are always converted to WebP. Resizing only occurs when the image exceeds the specified width or height — it never upscales. Default quality is 80%.",
499
+ inputSchema: {
500
+ file_path: z.string().describe("Path to the source image file."),
501
+ output_path: z.string().optional().describe("Output file path. Defaults to same directory as input with .webp extension."),
502
+ width: z.number().optional().describe("Max width in pixels. Image is resized only if its width exceeds this value. Aspect ratio is preserved."),
503
+ height: z.number().optional().describe("Max height in pixels. Image is resized only if its height exceeds this value. Aspect ratio is preserved."),
504
+ quality: z.number().min(1).max(100).optional().default(80).describe("WebP quality 1–100. Default is 80."),
505
+ },
506
+ }, async (args) => {
507
+ const result = await this.utility.optimizeImage(args);
508
+ return {
509
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
510
+ };
511
+ });
512
+ this.server.registerTool("init_project", {
513
+ description: "Initialize a local project directory with default structure.",
514
+ inputSchema: {
515
+ path: z.string().optional(),
516
+ },
517
+ }, async (args) => {
518
+ const result = await this.utility.initProject(args);
519
+ return {
520
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
521
+ };
522
+ });
523
+ this.server.registerTool("read_web_page", {
524
+ description: "Extract main content and metadata from a web page using Playwright and Readability.",
525
+ inputSchema: {
526
+ url: z.string(),
527
+ render_js: z.boolean().optional().default(true),
528
+ wait_selector: z.string().optional(),
529
+ css_selector: z.string().optional(),
530
+ },
531
+ }, async (args) => {
532
+ const result = await this.web.readWebPage(args);
533
+ return {
534
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
535
+ };
536
+ });
537
+ this.server.registerTool("get_agent_guide", {
538
+ description: "Retrieve a detailed sub-guide for a specific topic and write it to agent-guide/{topic}.md in the project directory. After calling this tool, read the returned file path to access the full guide content. Available guides: workflow, templates, conditions, scripts, preview, errors, media, permissions, navigation, tailwind-config, image-generation, php-location, image-optimize, global-library, tailwind-optimize.",
539
+ inputSchema: {
540
+ guide: z.enum(["workflow", "templates", "conditions", "scripts", "preview", "errors", "media", "permissions", "navigation", "tailwind-config", "image-generation", "php-location", "image-optimize", "global-library", "tailwind-optimize"]),
541
+ },
542
+ }, async (args) => {
543
+ const result = await this.api.getAgentGuide(args.guide);
544
+ return {
545
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
546
+ };
547
+ });
548
+ this.server.registerTool("get_tailwind_config", {
549
+ description: "Get the Tailwind CSS configuration (@theme CSS block) from Settings > Layout. The prefix is always 'pkt-'. Returns the default kit config if none has been saved.",
550
+ }, async () => {
551
+ const result = await this.api.getTailwindConfig();
552
+ return {
553
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
554
+ };
555
+ });
556
+ this.server.registerTool("update_tailwind_config", {
557
+ description: "Update the @theme CSS block of the Tailwind CSS config in Settings > Layout. Pass the complete @theme { ... } CSS block — it is not merged. The prefix 'pkt-' is fixed and cannot be changed. Changes take effect immediately on the next page load.",
558
+ inputSchema: {
559
+ theme_css: z.string().describe("The new @theme { ... } CSS block as a string. Replaces the entire existing theme config."),
560
+ },
561
+ }, async (args) => {
562
+ const result = await this.api.updateTailwindConfig(args);
563
+ return {
564
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
565
+ };
566
+ });
567
+ this.server.registerTool("tailwind_optimize", {
568
+ description: "Optimize Tailwind CSS for production using PostCSS and Tailwind v4. Scans all HTML/PHP files in the workspace and uses the provided Tailwind configuration to generate a minified CSS file at workspace/optimize/optimized-tailwind.css.",
569
+ inputSchema: {
570
+ tailwind_config: z.string().describe("The Tailwind @theme { ... } CSS block."),
571
+ },
572
+ }, async (args) => {
573
+ const result = await this.tailwind.optimize(args);
574
+ return {
575
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
576
+ };
577
+ });
578
+ // Global Library
579
+ this.server.registerTool("list_libraries", {
580
+ description: "List all Global Libraries managed by the plugin. Automatically writes the full inventory to .pk-agentic/library-info.json and returns a pagination summary.",
581
+ inputSchema: {
582
+ page: z.number().optional().default(1),
583
+ per_page: z.number().optional().default(50),
584
+ },
585
+ }, async (args) => {
586
+ const result = await this.api.listLibraries(args);
587
+ return {
588
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
589
+ };
590
+ });
591
+ this.server.registerTool("create_library", {
592
+ description: "Create a new Global Library.",
593
+ inputSchema: {
594
+ title: z.string(),
595
+ library_type: z.enum(["js", "css", "font"]),
596
+ },
597
+ }, async (args) => {
598
+ const result = await this.api.createLibrary(args);
599
+ return {
600
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
601
+ };
602
+ });
603
+ this.server.registerTool("update_library", {
604
+ description: "Update Global Library metadata (title, type, mode).",
605
+ inputSchema: {
606
+ id: z.number(),
607
+ title: z.string().optional(),
608
+ library_type: z.enum(["js", "css", "font"]).optional(),
609
+ mode: z.enum(["local", "cdn"]).optional(),
610
+ },
611
+ }, async (args) => {
612
+ const result = await this.api.updateLibrary(args);
613
+ return {
614
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
615
+ };
616
+ });
617
+ this.server.registerTool("delete_library", {
618
+ description: "Delete a Global Library and all its files from disk.",
619
+ inputSchema: { id: z.number() },
620
+ }, async (args) => {
621
+ const result = await this.api.deleteLibrary(args);
622
+ return {
623
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
624
+ };
625
+ });
626
+ this.server.registerTool("search_libraries", {
627
+ description: "Search Global Libraries by title.",
628
+ inputSchema: { q: z.string() },
629
+ }, async (args) => {
630
+ const result = await this.api.searchLibraries(args);
631
+ return {
632
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
633
+ };
634
+ });
635
+ this.server.registerTool("get_library", {
636
+ description: "Retrieve full metadata and file list for a specific Global Library.",
637
+ inputSchema: { id: z.number() },
638
+ }, async (args) => {
639
+ const result = await this.api.getLibrary(args);
640
+ return {
641
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
642
+ };
643
+ });
644
+ this.server.registerTool("cdn_download_library", {
645
+ description: "Download asset files from CDN URLs to local library storage.",
646
+ inputSchema: {
647
+ id: z.number(),
648
+ urls: z.array(z.string()),
649
+ },
650
+ }, async (args) => {
651
+ const result = await this.api.cdnDownloadLibrary(args);
652
+ return {
653
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
654
+ };
655
+ });
656
+ this.server.registerTool("save_library_files", {
657
+ description: "Update settings for files within a library (priority, location).",
658
+ inputSchema: {
659
+ id: z.number(),
660
+ files: z.array(z.object({
661
+ filename: z.string(),
662
+ priority: z.number().optional(),
663
+ location_js: z.enum(["foot", "head", "open_body"]).optional(),
664
+ })),
665
+ },
666
+ }, async (args) => {
667
+ const result = await this.api.saveLibraryFiles(args);
668
+ return {
669
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
670
+ };
671
+ });
672
+ this.server.registerTool("restore_library_backup", {
673
+ description: "Restore library files from a previous backup.",
674
+ inputSchema: {
675
+ id: z.number(),
676
+ backup_key: z.string(),
677
+ },
678
+ }, async (args) => {
679
+ const result = await this.api.restoreLibraryBackup(args);
680
+ return {
681
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
682
+ };
683
+ });
684
+ this.server.registerTool("update_library_conditions", {
685
+ description: "Update display conditions for a Global Library.",
686
+ inputSchema: {
687
+ id: z.number(),
688
+ conditions: z.object({}).passthrough(),
689
+ },
690
+ }, async (args) => {
691
+ const result = await this.api.updateLibraryConditions(args);
692
+ return {
693
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
694
+ };
695
+ });
696
+ this.server.registerTool("toggle_library", {
697
+ description: "Enable or disable a Global Library.",
698
+ inputSchema: {
699
+ id: z.number(),
700
+ enabled: z.boolean().optional(),
701
+ },
702
+ }, async (args) => {
703
+ const result = await this.api.toggleLibrary(args);
704
+ return {
705
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
706
+ };
707
+ });
708
+ this.server.registerTool("toggle_library_lock", {
709
+ description: "Lock or unlock a Global Library. Locked libraries cannot be modified or deleted.",
710
+ inputSchema: {
711
+ id: z.number(),
712
+ locked: z.boolean().optional(),
713
+ },
714
+ }, async (args) => {
715
+ const result = await this.api.toggleLibraryLock(args);
716
+ return {
717
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
718
+ };
719
+ });
720
+ if (GEMINI_API_KEY) {
721
+ this.server.registerTool("generate_image", {
722
+ description: "Generate an image using Gemini AI based on a prompt stored in a file. Saves the image to workspace/generated-images/.",
723
+ inputSchema: {
724
+ prompt_path: z.string().describe("Path to the Markdown file containing the image prompt (e.g., 'workspace/prompt-images/home/hero.md')."),
725
+ image_name: z.string().describe("Base name for the generated image file (e.g., 'hero-banner')."),
726
+ page_name: z.string().describe("Name of the page this image is for (used for metadata)."),
727
+ aspect_ratio: z.enum(["1:1", "9:16", "16:9", "3:2", "4:3"]).optional().default("1:1"),
728
+ },
729
+ }, async (args) => {
730
+ const result = await this.image.generateImage(args);
731
+ return {
732
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
733
+ };
734
+ });
735
+ }
736
+ }
737
+ async run() {
738
+ const transport = new StdioServerTransport();
739
+ await this.server.connect(transport);
740
+ console.error("PK Agent MCP server running on stdio");
741
+ }
742
+ }
743
+ const server = new PKAgentMcpServer();
744
+ server.run().catch(console.error);
745
+ //# sourceMappingURL=index.js.map