construct-shader-graph-mcp 0.4.0 → 0.5.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/package.json +1 -1
- package/src/server.mjs +62 -1
package/package.json
CHANGED
package/src/server.mjs
CHANGED
|
@@ -22,6 +22,7 @@ let localServer = null;
|
|
|
22
22
|
let bridge = null;
|
|
23
23
|
let controlServer = null;
|
|
24
24
|
let isPrimaryInstance = false;
|
|
25
|
+
let promotionInFlight = null;
|
|
25
26
|
|
|
26
27
|
function log(message, ...args) {
|
|
27
28
|
console.error(`[construct-shader-graph-mcp] ${message}`, ...args);
|
|
@@ -706,7 +707,7 @@ function createProxyServer() {
|
|
|
706
707
|
return server;
|
|
707
708
|
}
|
|
708
709
|
|
|
709
|
-
function
|
|
710
|
+
function callPrimaryToolDirect(tool, input) {
|
|
710
711
|
return new Promise((resolve, reject) => {
|
|
711
712
|
const socket = net.createConnection({
|
|
712
713
|
host: "127.0.0.1",
|
|
@@ -751,6 +752,66 @@ function callPrimaryTool(tool, input) {
|
|
|
751
752
|
});
|
|
752
753
|
}
|
|
753
754
|
|
|
755
|
+
async function tryPromoteToPrimary() {
|
|
756
|
+
if (isPrimaryInstance) return true;
|
|
757
|
+
|
|
758
|
+
// Deduplicate concurrent promotion attempts
|
|
759
|
+
if (promotionInFlight) return promotionInFlight;
|
|
760
|
+
|
|
761
|
+
promotionInFlight = (async () => {
|
|
762
|
+
try {
|
|
763
|
+
await startPrimaryBackend();
|
|
764
|
+
log("promoted to primary instance");
|
|
765
|
+
return true;
|
|
766
|
+
} catch (err) {
|
|
767
|
+
if (err?.code === "EADDRINUSE") {
|
|
768
|
+
log("promotion failed: another primary appeared");
|
|
769
|
+
} else {
|
|
770
|
+
log("promotion failed:", err.message);
|
|
771
|
+
}
|
|
772
|
+
// Reset partial state
|
|
773
|
+
isPrimaryInstance = false;
|
|
774
|
+
localServer = null;
|
|
775
|
+
bridge = null;
|
|
776
|
+
controlServer = null;
|
|
777
|
+
return false;
|
|
778
|
+
} finally {
|
|
779
|
+
promotionInFlight = null;
|
|
780
|
+
}
|
|
781
|
+
})();
|
|
782
|
+
|
|
783
|
+
return promotionInFlight;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function executeToolLocally(toolName, input) {
|
|
787
|
+
const tool = createToolDefinitions().find((t) => t.name === toolName);
|
|
788
|
+
if (!tool) throw new Error(`Unknown tool '${toolName}'`);
|
|
789
|
+
return tool.handler(input || {});
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
async function callPrimaryTool(toolName, input) {
|
|
793
|
+
// If we're already promoted, go local
|
|
794
|
+
if (isPrimaryInstance) {
|
|
795
|
+
return executeToolLocally(toolName, input);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
try {
|
|
799
|
+
return await callPrimaryToolDirect(toolName, input);
|
|
800
|
+
} catch (error) {
|
|
801
|
+
// If connection refused, the primary is gone — try to take over
|
|
802
|
+
if (error?.code === "ECONNREFUSED") {
|
|
803
|
+
log("primary unreachable, attempting promotion...");
|
|
804
|
+
const promoted = await tryPromoteToPrimary();
|
|
805
|
+
if (promoted) {
|
|
806
|
+
return executeToolLocally(toolName, input);
|
|
807
|
+
}
|
|
808
|
+
// Another primary appeared while we promoted — retry via proxy
|
|
809
|
+
return callPrimaryToolDirect(toolName, input);
|
|
810
|
+
}
|
|
811
|
+
throw error;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
754
815
|
async function ensureBackend() {
|
|
755
816
|
try {
|
|
756
817
|
await startPrimaryBackend();
|