construct-shader-graph-mcp 0.3.0 → 0.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/server.mjs +53 -16
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "construct-shader-graph-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Standalone MCP server for Construct Shader Graph",
5
5
  "type": "module",
6
6
  "files": [
package/src/server.mjs CHANGED
@@ -227,7 +227,9 @@ function createToolDefinitions() {
227
227
  description:
228
228
  "Choose which connected shader graph tab future MCP calls should target.",
229
229
  inputSchema: {
230
- sessionId: z.string().describe("Session id returned by list_projects."),
230
+ sessionId: z
231
+ .string()
232
+ .describe("Session id returned by list_projects."),
231
233
  },
232
234
  outputSchema: {
233
235
  sessionId: z.string(),
@@ -291,7 +293,9 @@ function createToolDefinitions() {
291
293
  .describe("Optional session id; defaults to the selected project."),
292
294
  method: z
293
295
  .string()
294
- .describe("Manifest method path, for example nodes.create or shader.getInfo."),
296
+ .describe(
297
+ "Manifest method path, for example nodes.create or shader.getInfo.",
298
+ ),
295
299
  args: z
296
300
  .array(z.any())
297
301
  .optional()
@@ -397,12 +401,15 @@ function registerPrompts(server) {
397
401
  "inspect-graph",
398
402
  {
399
403
  title: "Inspect Graph",
400
- description: "Prompt for safely inspecting the current graph before any edits.",
404
+ description:
405
+ "Prompt for safely inspecting the current graph before any edits.",
401
406
  argsSchema: z.object({
402
407
  focus: z
403
408
  .string()
404
409
  .optional()
405
- .describe("Optional area to inspect, like uniforms, preview, or node types."),
410
+ .describe(
411
+ "Optional area to inspect, like uniforms, preview, or node types.",
412
+ ),
406
413
  }),
407
414
  },
408
415
  ({ focus }) => ({
@@ -447,7 +454,10 @@ function registerPrompts(server) {
447
454
  description:
448
455
  "Prompt for debugging generated code or preview issues in a shader graph project.",
449
456
  argsSchema: z.object({
450
- issue: z.string().optional().describe("Optional description of the observed preview issue."),
457
+ issue: z
458
+ .string()
459
+ .optional()
460
+ .describe("Optional description of the observed preview issue."),
451
461
  }),
452
462
  },
453
463
  ({ issue }) => ({
@@ -480,7 +490,6 @@ function createLocalServer() {
480
490
  }
481
491
 
482
492
  async function startPrimaryBackend() {
483
- isPrimaryInstance = true;
484
493
  localServer = createLocalServer();
485
494
 
486
495
  bridge = new WebSocketServer({ noServer: true });
@@ -541,7 +550,9 @@ async function startPrimaryBackend() {
541
550
  selectedSessionId = sessionId;
542
551
  }
543
552
 
544
- log(`registered ${sessionId} (${session.project?.name || "Untitled Shader"})`);
553
+ log(
554
+ `registered ${sessionId} (${session.project?.name || "Untitled Shader"})`,
555
+ );
545
556
  sendWsJson(socket, {
546
557
  type: "registered",
547
558
  sessionId,
@@ -631,9 +642,15 @@ async function startPrimaryBackend() {
631
642
  return;
632
643
  }
633
644
 
634
- const tool = createToolDefinitions().find((entry) => entry.name === request.tool);
645
+ const tool = createToolDefinitions().find(
646
+ (entry) => entry.name === request.tool,
647
+ );
635
648
  if (!tool) {
636
- sendJson(socket, { id: request.id, ok: false, error: `Unknown tool '${request.tool}'` });
649
+ sendJson(socket, {
650
+ id: request.id,
651
+ ok: false,
652
+ error: `Unknown tool '${request.tool}'`,
653
+ });
637
654
  return;
638
655
  }
639
656
 
@@ -650,14 +667,25 @@ async function startPrimaryBackend() {
650
667
  });
651
668
  });
652
669
 
653
- await new Promise((resolve, reject) => {
654
- controlServer.once("error", reject);
655
- controlServer.listen(CONTROL_PORT, "127.0.0.1", () => {
656
- controlServer.off("error", reject);
657
- resolve();
670
+ try {
671
+ await new Promise((resolve, reject) => {
672
+ controlServer.once("error", reject);
673
+ controlServer.listen(CONTROL_PORT, "127.0.0.1", () => {
674
+ controlServer.off("error", reject);
675
+ resolve();
676
+ });
658
677
  });
659
- });
678
+ } catch (err) {
679
+ httpServer.close();
680
+ bridge.close();
681
+ throw err;
682
+ }
683
+
684
+ // Permanent error handlers to prevent unhandled error crashes
685
+ httpServer.on("error", (err) => log("bridge http error:", err.message));
686
+ controlServer.on("error", (err) => log("control server error:", err.message));
660
687
 
688
+ isPrimaryInstance = true;
661
689
  log(`control listening on tcp://127.0.0.1:${CONTROL_PORT}`);
662
690
  }
663
691
 
@@ -680,7 +708,10 @@ function createProxyServer() {
680
708
 
681
709
  function callPrimaryTool(tool, input) {
682
710
  return new Promise((resolve, reject) => {
683
- const socket = net.createConnection({ host: "127.0.0.1", port: CONTROL_PORT });
711
+ const socket = net.createConnection({
712
+ host: "127.0.0.1",
713
+ port: CONTROL_PORT,
714
+ });
684
715
  const requestId = `rpc_${Date.now()}_${Math.random().toString(16).slice(2)}`;
685
716
  const rl = readline.createInterface({ input: socket });
686
717
 
@@ -728,6 +759,12 @@ async function ensureBackend() {
728
759
  throw error;
729
760
  }
730
761
 
762
+ // Clean up any partially created resources
763
+ isPrimaryInstance = false;
764
+ localServer = null;
765
+ bridge = null;
766
+ controlServer = null;
767
+
731
768
  log(`bridge already running on ${BRIDGE_PORT}; starting follower proxy`);
732
769
  }
733
770
  }