start-mcp.mjs
start-mcp.mjs
#!/usr/bin/env node
// Entry point for Bionic: keeps process.argv[1] at the project root so
// drawThingsGrpcService.ts's project-root walk-up finds the real package.json
// first, not dist-mcp/mcp/package.json (see /memories/repo/generate-image-mcp-esm-cjs.md).
//
// Also pins process.cwd() here, regardless of whatever cwd Bionic/MCP Inspector
// spawns this process with: fpzip_loader.js (spawned as a subprocess by the
// GRPCBin2PNG decoder, inheriting our cwd) resolves its own directory by walking
// UP FROM process.cwd() looking for dist/fpzip — a cwd outside this repo (e.g. a
// scratchpad folder) makes that walk fail, the decoder subprocess exits 1, and
// a render call silently falls back to writing raw tensor .bin files instead of
// PNGs (see /memories/repo/generate-image-mcp-esm-cjs.md for the full story).
import { fileURLToPath } from "node:url";
import path from "node:path";
process.chdir(path.dirname(fileURLToPath(import.meta.url)));
// Redirect all non-error console output to the log file so stdout stays
// clean for JSON-RPC. The Python MCP client (Unsloth Studio) parses every
// stdout line as JSON-RPC and throws "validation error for JSONRPCMessage"
// on any non-JSON line. console.error is left untouched → stderr.
import fs from "node:fs";
const LOG_DIR = path.join(process.cwd(), "logs");
const LOG_FILE = path.join(LOG_DIR, "process-image-mcp.log");
fs.mkdirSync(LOG_DIR, { recursive: true });
function logToFile(level, args) {
const ts = new Date().toISOString();
const msg = args.map((a) => (typeof a === "string" ? a : JSON.stringify(a))).join(" ");
fs.appendFileSync(LOG_FILE, `${ts} [${level}] ${msg}\n`);
}
console.log = (...args) => logToFile("LOG", args);
console.info = (...args) => logToFile("INFO", args);
console.warn = (...args) => logToFile("WARN", args);
console.debug = (...args) => logToFile("DEBUG", args);
// Dynamic import, not static: static imports are hoisted and evaluate before
// this file's other top-level code in ESM, which would run dist-mcp/mcp/index.js
// (and everything it pulls in) before process.chdir() above ever executes.
await import("./dist-mcp/mcp/index.js");
#!/usr/bin/env node
// Entry point for Bionic: keeps process.argv[1] at the project root so
// drawThingsGrpcService.ts's project-root walk-up finds the real package.json
// first, not dist-mcp/mcp/package.json (see /memories/repo/generate-image-mcp-esm-cjs.md).
//
// Also pins process.cwd() here, regardless of whatever cwd Bionic/MCP Inspector
// spawns this process with: fpzip_loader.js (spawned as a subprocess by the
// GRPCBin2PNG decoder, inheriting our cwd) resolves its own directory by walking
// UP FROM process.cwd() looking for dist/fpzip — a cwd outside this repo (e.g. a
// scratchpad folder) makes that walk fail, the decoder subprocess exits 1, and
// a render call silently falls back to writing raw tensor .bin files instead of
// PNGs (see /memories/repo/generate-image-mcp-esm-cjs.md for the full story).
import { fileURLToPath } from "node:url";
import path from "node:path";
process.chdir(path.dirname(fileURLToPath(import.meta.url)));
// Redirect all non-error console output to the log file so stdout stays
// clean for JSON-RPC. The Python MCP client (Unsloth Studio) parses every
// stdout line as JSON-RPC and throws "validation error for JSONRPCMessage"
// on any non-JSON line. console.error is left untouched → stderr.
import fs from "node:fs";
const LOG_DIR = path.join(process.cwd(), "logs");
const LOG_FILE = path.join(LOG_DIR, "process-image-mcp.log");
fs.mkdirSync(LOG_DIR, { recursive: true });
function logToFile(level, args) {
const ts = new Date().toISOString();
const msg = args.map((a) => (typeof a === "string" ? a : JSON.stringify(a))).join(" ");
fs.appendFileSync(LOG_FILE, `${ts} [${level}] ${msg}\n`);
}
console.log = (...args) => logToFile("LOG", args);
console.info = (...args) => logToFile("INFO", args);
console.warn = (...args) => logToFile("WARN", args);
console.debug = (...args) => logToFile("DEBUG", args);
// Dynamic import, not static: static imports are hoisted and evaluate before
// this file's other top-level code in ESM, which would run dist-mcp/mcp/index.js
// (and everything it pulls in) before process.chdir() above ever executes.
await import("./dist-mcp/mcp/index.js");