diff --git a/bin/omniroute.mjs b/bin/omniroute.mjs index 79513bd63f..f0c5bcec44 100644 --- a/bin/omniroute.mjs +++ b/bin/omniroute.mjs @@ -16,7 +16,7 @@ import { spawn } from "node:child_process"; import { existsSync, readFileSync } from "node:fs"; import { join, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { fileURLToPath, pathToFileURL } from "node:url"; import { homedir, platform } from "node:os"; import { isNativeBinaryCompatible } from "../scripts/native-binary-compat.mjs"; import { getNodeRuntimeSupport, getNodeRuntimeWarning } from "./nodeRuntimeSupport.mjs"; @@ -218,7 +218,7 @@ if (args.includes("reset-encrypted-columns")) { if (args.includes("--mcp")) { try { - const { startMcpCli } = await import(join(ROOT, "bin", "mcp-server.mjs")); + const { startMcpCli } = await import(pathToFileURL(join(ROOT, "bin", "mcp-server.mjs")).href); await startMcpCli(ROOT); } catch (err) { console.error("\x1b[31m✖ Failed to start MCP server:\x1b[0m", err.message || err); diff --git a/tests/unit/bin-omniroute-mcp.test.ts b/tests/unit/bin-omniroute-mcp.test.ts new file mode 100644 index 0000000000..c67f035bb7 --- /dev/null +++ b/tests/unit/bin-omniroute-mcp.test.ts @@ -0,0 +1,56 @@ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; +import { pathToFileURL } from "node:url"; +import { join } from "node:path"; +import { platform } from "node:os"; + +describe("bin/omniroute.mjs MCP path handling", () => { + it("pathToFileURL converts Windows paths to valid file:// URLs", () => { + if (platform() !== "win32") { + // Skip on non-Windows platforms + return; + } + + const testPath = "C:\\Users\\test\\projects\\OmniRoute\\bin\\mcp-server.mjs"; + const fileUrl = pathToFileURL(testPath); + + assert.ok(fileUrl.href.startsWith("file:///"), "URL should start with file:///"); + assert.ok(fileUrl.href.includes("C:/"), "Windows drive letter should be converted"); + assert.ok(!fileUrl.href.includes("\\"), "Backslashes should be converted to forward slashes"); + assert.ok(fileUrl.href.endsWith("mcp-server.mjs"), "Filename should be preserved"); + }); + + it("pathToFileURL converts Unix paths to valid file:// URLs", () => { + if (platform() === "win32") { + // Skip on Windows + return; + } + + const testPath = "/home/user/projects/OmniRoute/bin/mcp-server.mjs"; + const fileUrl = pathToFileURL(testPath); + + assert.ok(fileUrl.href.startsWith("file:///"), "URL should start with file:///"); + assert.ok(fileUrl.href.endsWith("mcp-server.mjs"), "Filename should be preserved"); + }); + + it("pathToFileURL handles relative paths correctly", () => { + const relativePath = join("bin", "mcp-server.mjs"); + const absolutePath = join(process.cwd(), relativePath); + const fileUrl = pathToFileURL(absolutePath); + + assert.ok(fileUrl.href.startsWith("file:///"), "URL should start with file:///"); + assert.ok(fileUrl.href.endsWith("mcp-server.mjs"), "Filename should be preserved"); + }); + + it("pathToFileURL result can be used with dynamic import", async () => { + // This test verifies that the URL format is compatible with import() + const testPath = join(process.cwd(), "package.json"); + const fileUrl = pathToFileURL(testPath); + + // Verify the URL is valid for import (we use a JSON file as a safe test) + assert.ok(fileUrl.href.startsWith("file:///"), "URL should be valid for import"); + assert.doesNotThrow(() => { + new URL(fileUrl.href); + }, "URL should be parseable"); + }); +});