@genart-dev/mcp-server 0.4.2 → 0.4.4

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/lib.d.cts CHANGED
@@ -116,7 +116,11 @@ declare class EditorState extends EventEmitter {
116
116
  * Creates a McpServer instance with all tools, resources, and prompts.
117
117
  */
118
118
 
119
+ interface CreateServerOptions {
120
+ /** Only register capture tools (for local-only capture companion server). */
121
+ captureOnly?: boolean;
122
+ }
119
123
  /** Create and configure the MCP server with all tools. */
120
- declare function createServer(state: EditorState): McpServer;
124
+ declare function createServer(state: EditorState, options?: CreateServerOptions): McpServer;
121
125
 
122
- export { type EditorMutationEvent, type EditorMutationType, EditorState, type EditorStateSnapshot, type LoadedSketch, createServer };
126
+ export { type CreateServerOptions, type EditorMutationEvent, type EditorMutationType, EditorState, type EditorStateSnapshot, type LoadedSketch, createServer };
package/dist/lib.d.ts CHANGED
@@ -116,7 +116,11 @@ declare class EditorState extends EventEmitter {
116
116
  * Creates a McpServer instance with all tools, resources, and prompts.
117
117
  */
118
118
 
119
+ interface CreateServerOptions {
120
+ /** Only register capture tools (for local-only capture companion server). */
121
+ captureOnly?: boolean;
122
+ }
119
123
  /** Create and configure the MCP server with all tools. */
120
- declare function createServer(state: EditorState): McpServer;
124
+ declare function createServer(state: EditorState, options?: CreateServerOptions): McpServer;
121
125
 
122
- export { type EditorMutationEvent, type EditorMutationType, EditorState, type EditorStateSnapshot, type LoadedSketch, createServer };
126
+ export { type CreateServerOptions, type EditorMutationEvent, type EditorMutationType, EditorState, type EditorStateSnapshot, type LoadedSketch, createServer };
package/dist/lib.js CHANGED
@@ -2424,7 +2424,8 @@ function isDirectComponent(name, components) {
2424
2424
  }
2425
2425
 
2426
2426
  // src/tools/capture.ts
2427
- import { writeFile as writeFile5 } from "fs/promises";
2427
+ import { mkdir, writeFile as writeFile5 } from "fs/promises";
2428
+ import { dirname as dirname5, join as join3 } from "path";
2428
2429
  import {
2429
2430
  createDefaultRegistry as createDefaultRegistry2
2430
2431
  } from "@genart-dev/core";
@@ -2529,8 +2530,9 @@ function generateSketchHtml(sketch, opts) {
2529
2530
  }
2530
2531
  return adapter.generateStandaloneHTML(effective);
2531
2532
  }
2532
- function derivePreviewPath(sketchPath) {
2533
- return sketchPath.replace(/\.genart$/, ".png");
2533
+ function deriveSnapshotPath(sketchPath, sketchId, seed) {
2534
+ const wsDir = dirname5(sketchPath);
2535
+ return join3(wsDir, "snapshots", `${sketchId}-${seed}-preview.png`);
2534
2536
  }
2535
2537
  async function captureScreenshot(state, input) {
2536
2538
  state.requireWorkspace();
@@ -2563,11 +2565,12 @@ async function captureScreenshot(state, input) {
2563
2565
  height,
2564
2566
  inlineSize
2565
2567
  });
2566
- const previewPath = derivePreviewPath(loaded.path);
2568
+ const effectiveSeed = input.seed ?? sketch.state.seed;
2569
+ const previewPath = deriveSnapshotPath(loaded.path, sketchId, effectiveSeed);
2567
2570
  const metadata = await buildScreenshotMetadata(state, multi, {
2568
2571
  target,
2569
2572
  sketchId,
2570
- seed: input.seed ?? sketch.state.seed,
2573
+ seed: effectiveSeed,
2571
2574
  previewPath
2572
2575
  });
2573
2576
  const previewJpegBase64 = Buffer.from(multi.inlineJpeg).toString("base64");
@@ -2588,8 +2591,10 @@ async function buildScreenshotMetadata(state, multi, info) {
2588
2591
  previewPath: info.previewPath
2589
2592
  };
2590
2593
  if (!state.remoteMode) {
2594
+ await mkdir(dirname5(info.previewPath), { recursive: true });
2591
2595
  await writeFile5(info.previewPath, multi.previewPng);
2592
2596
  metadata.savedPreviewTo = info.previewPath;
2597
+ metadata.previewWritten = true;
2593
2598
  }
2594
2599
  return metadata;
2595
2600
  }
@@ -2618,11 +2623,12 @@ async function captureBatch(state, input) {
2618
2623
  height,
2619
2624
  inlineSize
2620
2625
  });
2621
- const previewPath = derivePreviewPath(loaded.path);
2626
+ const effectiveSeed = input.seed ?? sketch.state.seed;
2627
+ const previewPath = deriveSnapshotPath(loaded.path, id, effectiveSeed);
2622
2628
  const itemMetadata = await buildScreenshotMetadata(state, multi, {
2623
2629
  target: "sketch",
2624
2630
  sketchId: id,
2625
- seed: input.seed ?? sketch.state.seed,
2631
+ seed: effectiveSeed,
2626
2632
  previewPath
2627
2633
  });
2628
2634
  items.push({
@@ -2982,7 +2988,7 @@ function gatherRelevantSkills(aspects) {
2982
2988
 
2983
2989
  // src/tools/series.ts
2984
2990
  import { writeFile as writeFile6 } from "fs/promises";
2985
- import { basename as basename8, dirname as dirname5, resolve as resolve2 } from "path";
2991
+ import { basename as basename8, dirname as dirname6, resolve as resolve2 } from "path";
2986
2992
  import {
2987
2993
  serializeGenart as serializeGenart4,
2988
2994
  serializeWorkspace as serializeWorkspace3
@@ -3262,7 +3268,7 @@ async function promoteSketch(state, input) {
3262
3268
  ...input.agent ? { agent: input.agent } : {},
3263
3269
  ...input.model ? { model: input.model } : {}
3264
3270
  };
3265
- const sourceDir = dirname5(source.path);
3271
+ const sourceDir = dirname6(source.path);
3266
3272
  const newPath = resolve2(sourceDir, `${newId}.genart`);
3267
3273
  const json = serializeGenart4(promotedDef);
3268
3274
  if (!state.remoteMode) {
@@ -3332,8 +3338,8 @@ function countBy(items) {
3332
3338
  }
3333
3339
 
3334
3340
  // src/tools/reference.ts
3335
- import { copyFile, mkdir, readFile as readFile4 } from "fs/promises";
3336
- import { basename as basename9, dirname as dirname6, extname, resolve as resolve3 } from "path";
3341
+ import { copyFile, mkdir as mkdir2, readFile as readFile4 } from "fs/promises";
3342
+ import { basename as basename9, dirname as dirname7, extname, resolve as resolve3 } from "path";
3337
3343
  import {
3338
3344
  serializeGenart as serializeGenart5,
3339
3345
  serializeWorkspace as serializeWorkspace4
@@ -3388,9 +3394,9 @@ async function addReference(state, input) {
3388
3394
  `Invalid reference type: '${refType}'. Valid types: ${VALID_REFERENCE_TYPES.join(", ")}`
3389
3395
  );
3390
3396
  }
3391
- const workspaceDir = dirname6(state.workspacePath);
3397
+ const workspaceDir = dirname7(state.workspacePath);
3392
3398
  const refsDir = resolve3(workspaceDir, "references");
3393
- await mkdir(refsDir, { recursive: true });
3399
+ await mkdir2(refsDir, { recursive: true });
3394
3400
  const ext = extname(input.image);
3395
3401
  const destFilename = `${id}${ext}`;
3396
3402
  const destPath = resolve3(refsDir, destFilename);
@@ -3478,7 +3484,7 @@ async function addReference(state, input) {
3478
3484
  async function analyzeReference(state, input) {
3479
3485
  state.requireWorkspace();
3480
3486
  const { ref, location } = findReference(state, input.referenceId, input.seriesId, input.sketchId);
3481
- const workspaceDir = dirname6(state.workspacePath);
3487
+ const workspaceDir = dirname7(state.workspacePath);
3482
3488
  const imagePath = resolve3(workspaceDir, ref.path);
3483
3489
  let previewJpegBase64;
3484
3490
  try {
@@ -3638,7 +3644,7 @@ async function extractPalette(state, input) {
3638
3644
  input.sketchId
3639
3645
  );
3640
3646
  const count = input.count ?? 6;
3641
- const workspaceDir = dirname6(state.workspacePath);
3647
+ const workspaceDir = dirname7(state.workspacePath);
3642
3648
  const imagePath = resolve3(workspaceDir, ref.path);
3643
3649
  let previewJpegBase64;
3644
3650
  try {
@@ -3715,7 +3721,7 @@ function findReference(state, referenceId, seriesId, sketchId) {
3715
3721
  // src/tools/export.ts
3716
3722
  import { createWriteStream } from "fs";
3717
3723
  import { stat as stat4, writeFile as writeFile8 } from "fs/promises";
3718
- import { dirname as dirname7 } from "path";
3724
+ import { dirname as dirname8 } from "path";
3719
3725
  import archiver from "archiver";
3720
3726
  import {
3721
3727
  createDefaultRegistry as createDefaultRegistry3,
@@ -3723,7 +3729,7 @@ import {
3723
3729
  } from "@genart-dev/core";
3724
3730
  var registry4 = createDefaultRegistry3();
3725
3731
  async function validateOutputPath(outputPath) {
3726
- const parentDir = dirname7(outputPath);
3732
+ const parentDir = dirname8(outputPath);
3727
3733
  try {
3728
3734
  const s = await stat4(parentDir);
3729
3735
  if (!s.isDirectory()) {
@@ -4993,20 +4999,24 @@ async function initializePluginRegistry() {
4993
4999
  await registry5.register(tracePlugin);
4994
5000
  return registry5;
4995
5001
  }
4996
- function createServer(state) {
5002
+ function createServer(state, options) {
5003
+ const captureOnly = options?.captureOnly ?? false;
4997
5004
  const server = new McpServer(
4998
5005
  {
4999
- name: "@genart/mcp-server",
5006
+ name: captureOnly ? "@genart/mcp-capture" : "@genart/mcp-server",
5000
5007
  version: "0.4.0"
5001
5008
  },
5002
5009
  {
5003
5010
  capabilities: {
5004
5011
  tools: {},
5005
- resources: {},
5006
- prompts: {}
5012
+ ...!captureOnly && { resources: {}, prompts: {} }
5007
5013
  }
5008
5014
  }
5009
5015
  );
5016
+ if (captureOnly) {
5017
+ registerCaptureTools(server, state);
5018
+ return server;
5019
+ }
5010
5020
  const registryReady = initializePluginRegistry().then((registry5) => {
5011
5021
  state.pluginRegistry = registry5;
5012
5022
  registerPluginMcpTools(server, registry5, state);
@@ -5023,7 +5033,9 @@ function createServer(state) {
5023
5033
  registerSnapshotTools(server, state);
5024
5034
  registerKnowledgeTools(server, state);
5025
5035
  registerDesignTools(server, state);
5026
- registerCaptureTools(server, state);
5036
+ if (!state.remoteMode) {
5037
+ registerCaptureTools(server, state);
5038
+ }
5027
5039
  registerCritiqueTools(server, state);
5028
5040
  registerSeriesTools(server, state);
5029
5041
  registerReferenceTools(server, state);
@@ -5172,11 +5184,31 @@ function registerSketchTools(server, state) {
5172
5184
  ).optional().describe('Component dependencies. Use list_components to see available. Keys are component names, values are semver ranges (e.g. "^1.0.0") or objects with version/code/exports.'),
5173
5185
  addToWorkspace: z2.string().optional().describe("Path to workspace to add sketch to after creation"),
5174
5186
  agent: z2.string().optional().describe("Your CLI agent name (e.g. 'claude-code', 'codex-cli', 'gemini-cli', 'opencode', 'kiro')"),
5175
- model: z2.string().optional().describe("Your AI model identifier (e.g. 'claude-opus-4-6', 'gpt-4o', 'gemini-2.5-pro')")
5187
+ model: z2.string().optional().describe("Your AI model identifier (e.g. 'claude-opus-4-6', 'gpt-4o', 'gemini-2.5-pro')"),
5188
+ capture: z2.boolean().optional().describe("When true, automatically capture a screenshot after creation and return it inline (avoids a separate capture_screenshot call)")
5176
5189
  },
5177
5190
  async (args) => {
5178
5191
  try {
5179
5192
  const result = await createSketch(state, args);
5193
+ if (args.capture && !state.remoteMode) {
5194
+ try {
5195
+ const captureResult = await captureScreenshot(state, {
5196
+ target: "sketch",
5197
+ sketchId: args.id
5198
+ });
5199
+ return {
5200
+ content: [
5201
+ { type: "text", text: JSON.stringify({ ...result, capture: captureResult.metadata }, null, 2) },
5202
+ { type: "image", data: captureResult.previewJpegBase64, mimeType: "image/jpeg" }
5203
+ ]
5204
+ };
5205
+ } catch (captureErr) {
5206
+ return jsonResult({
5207
+ ...result,
5208
+ captureError: captureErr instanceof Error ? captureErr.message : String(captureErr)
5209
+ });
5210
+ }
5211
+ }
5180
5212
  return jsonResult(result);
5181
5213
  } catch (e) {
5182
5214
  return toolError(e instanceof Error ? e.message : String(e));
@@ -5733,7 +5765,7 @@ function registerSnapshotTools(server, state) {
5733
5765
  function registerCaptureTools(server, state) {
5734
5766
  server.tool(
5735
5767
  "capture_screenshot",
5736
- "Capture a screenshot of a sketch. Returns metadata as text + a small inline JPEG image for visual review. In remote mode, metadata includes previewFileContent (base64 PNG) to Write locally.",
5768
+ "Capture a screenshot of a sketch. Returns metadata as text + a small inline JPEG image for visual review. In local mode, writes a full-res PNG to snapshots/<sketchId>-<seed>-preview.png next to the workspace. The savedPreviewTo path in metadata points to the file on disk.",
5737
5769
  {
5738
5770
  target: z2.enum(["selected", "sketch"]).optional().describe("What to capture (default: selected)"),
5739
5771
  sketchId: z2.string().optional().describe("Required when target is 'sketch'"),
@@ -6396,7 +6428,7 @@ function registerKnowledgeTools(server, state) {
6396
6428
  // src/state.ts
6397
6429
  import { EventEmitter } from "events";
6398
6430
  import { readFile as readFile5 } from "fs/promises";
6399
- import { dirname as dirname8, isAbsolute, resolve as resolve4 } from "path";
6431
+ import { dirname as dirname9, isAbsolute, resolve as resolve4 } from "path";
6400
6432
 
6401
6433
  // src/sidecar.ts
6402
6434
  function isSidecarMode() {
@@ -6490,7 +6522,7 @@ var EditorState = class extends EventEmitter {
6490
6522
  }
6491
6523
  throw new Error("No workspace is currently open");
6492
6524
  }
6493
- const resolved = resolve4(dirname8(this.workspacePath), file);
6525
+ const resolved = resolve4(dirname9(this.workspacePath), file);
6494
6526
  if (this.basePath && !resolved.startsWith(this.basePath)) {
6495
6527
  throw new Error(`Path escapes sandbox: ${resolved}`);
6496
6528
  }