Merge pull request #79 from modelcontextprotocol/ani/fix-windows

Fix launch issues on Windows
This commit is contained in:
Ani Betts
2024-11-26 18:12:48 +01:00
committed by GitHub
7 changed files with 1831 additions and 745 deletions

View File

@@ -1,65 +1,82 @@
#!/usr/bin/env node #!/usr/bin/env node
import { join, dirname } from "path"; import { resolve, dirname } from "path";
import { spawnPromise } from "spawn-rx";
import { fileURLToPath } from "url"; import { fileURLToPath } from "url";
import concurrently from "concurrently";
const __dirname = dirname(fileURLToPath(import.meta.url)); const __dirname = dirname(fileURLToPath(import.meta.url));
// Get command line arguments function delay(ms) {
const [, , command, ...mcpServerArgs] = process.argv; return new Promise((resolve) => setTimeout(resolve, ms));
const inspectorServerPath = join(__dirname, "../server/build/index.js");
// Path to the client entry point
const inspectorClientPath = join(__dirname, "../client/bin/cli.js");
console.log("Starting MCP inspector...");
function escapeArg(arg) {
if (arg.includes(" ") || arg.includes("'") || arg.includes('"')) {
return `\\"${arg.replace(/"/g, '\\\\\\"')}\\"`;
}
return arg;
} }
const serverCommand = [ async function main() {
`node`, // Get command line arguments
inspectorServerPath, const [, , command, ...mcpServerArgs] = process.argv;
command ? `--env ${escapeArg(command)}` : "",
mcpServerArgs.length
? `--args="${mcpServerArgs.map(escapeArg).join(" ")}"`
: "",
]
.filter(Boolean)
.join(" ");
const CLIENT_PORT = process.env.CLIENT_PORT ?? ""; const inspectorServerPath = resolve(
const SERVER_PORT = process.env.SERVER_PORT ?? ""; __dirname,
"..",
"server",
"build",
"index.js",
);
const { result } = concurrently( // Path to the client entry point
const inspectorClientPath = resolve(
__dirname,
"..",
"client",
"bin",
"cli.js",
);
const CLIENT_PORT = process.env.CLIENT_PORT ?? "5173";
const SERVER_PORT = process.env.SERVER_PORT ?? "3000";
console.log("Starting MCP inspector...");
const abort = new AbortController();
let cancelled = false;
process.on("SIGINT", () => {
cancelled = true;
abort.abort();
});
const server = spawnPromise(
"node",
[ [
{ inspectorServerPath,
command: `PORT=${SERVER_PORT} ${serverCommand}`, ...(command ? [`--env`, command] : []),
name: "server", ...(mcpServerArgs ? ["--args", mcpServerArgs.join(" ")] : []),
},
{
command: `PORT=${CLIENT_PORT} node ${inspectorClientPath}`,
name: "client",
},
], ],
{ { env: { ...process.env, PORT: SERVER_PORT }, signal: abort.signal },
prefix: "name", );
killOthers: ["failure", "success"],
restartTries: 3,
},
);
console.log( const client = spawnPromise("node", [inspectorClientPath], {
`\n🔍 MCP Inspector is up and running at http://localhost:${CLIENT_PORT || 5173} 🚀`, env: { ...process.env, PORT: CLIENT_PORT },
); signal: abort.signal,
});
result.catch((err) => { // Make sure our server/client didn't immediately fail
console.error("An error occurred:", err); await Promise.any([server, client, delay(2 * 1000)]);
console.log(
`\n🔍 MCP Inspector is up and running at http://localhost:${CLIENT_PORT} 🚀`,
);
try {
await Promise.any([server, client]);
} catch (e) {
if (!cancelled || process.env.DEBUG) throw e;
}
return 0;
}
main()
.then((_) => process.exit(0))
.catch((e) => {
console.error(e);
process.exit(1); process.exit(1);
}); });

View File

@@ -21,7 +21,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "0.7.0", "@modelcontextprotocol/sdk": "^1.0.1",
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0", "@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-select": "^2.1.2", "@radix-ui/react-select": "^2.1.2",

View File

@@ -1,4 +1,5 @@
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
import animate from "tailwindcss-animate";
export default { export default {
darkMode: ["class"], darkMode: ["class"],
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
@@ -53,5 +54,5 @@ export default {
}, },
}, },
}, },
plugins: [require("tailwindcss-animate")], plugins: [animate],
}; };

2425
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -27,7 +27,7 @@
"build": "npm run build-server && npm run build-client", "build": "npm run build-server && npm run build-client",
"start-server": "cd server && npm run start", "start-server": "cd server && npm run start",
"start-client": "cd client && npm run preview", "start-client": "cd client && npm run preview",
"start": "./bin/cli.js", "start": "node ./bin/cli.js",
"prepare": "npm run build", "prepare": "npm run build",
"prettier-fix": "prettier --write .", "prettier-fix": "prettier --write .",
"publish-all": "npm publish --workspaces --access public && npm publish --access public" "publish-all": "npm publish --workspaces --access public && npm publish --access public"
@@ -35,10 +35,12 @@
"dependencies": { "dependencies": {
"@modelcontextprotocol/inspector-client": "0.2.1", "@modelcontextprotocol/inspector-client": "0.2.1",
"@modelcontextprotocol/inspector-server": "0.2.1", "@modelcontextprotocol/inspector-server": "0.2.1",
"concurrently": "^9.0.1" "concurrently": "^9.0.1",
"spawn-rx": "^5.0.4",
"ts-node": "^10.9.2"
}, },
"devDependencies": { "devDependencies": {
"prettier": "3.3.3", "@types/node": "^22.7.5",
"@types/node": "^22.7.5" "prettier": "3.3.3"
} }
} }

View File

@@ -27,7 +27,7 @@
"typescript": "^5.6.2" "typescript": "^5.6.2"
}, },
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "0.7.0", "@modelcontextprotocol/sdk": "^1.0.1",
"cors": "^2.8.5", "cors": "^2.8.5",
"eventsource": "^2.0.2", "eventsource": "^2.0.2",
"express": "^4.21.0", "express": "^4.21.0",

View File

@@ -39,6 +39,7 @@ const createTransport = async (query: express.Request["query"]) => {
const command = query.command as string; const command = query.command as string;
const args = (query.args as string).split(/\s+/); const args = (query.args as string).split(/\s+/);
const env = query.env ? JSON.parse(query.env as string) : undefined; const env = query.env ? JSON.parse(query.env as string) : undefined;
console.log( console.log(
`Stdio transport: command=${command}, args=${args}, env=${JSON.stringify(env)}`, `Stdio transport: command=${command}, args=${args}, env=${JSON.stringify(env)}`,
); );
@@ -48,14 +49,18 @@ const createTransport = async (query: express.Request["query"]) => {
env, env,
stderr: "pipe", stderr: "pipe",
}); });
await transport.start(); await transport.start();
console.log("Spawned stdio transport"); console.log("Spawned stdio transport");
return transport; return transport;
} else if (transportType === "sse") { } else if (transportType === "sse") {
const url = query.url as string; const url = query.url as string;
console.log(`SSE transport: url=${url}`); console.log(`SSE transport: url=${url}`);
const transport = new SSEClientTransport(new URL(url)); const transport = new SSEClientTransport(new URL(url));
await transport.start(); await transport.start();
console.log("Connected to SSE transport"); console.log("Connected to SSE transport");
return transport; return transport;
} else { } else {
@@ -99,6 +104,7 @@ app.get("/sse", async (req, res) => {
console.error(error); console.error(error);
}, },
}); });
console.log("Set up MCP proxy"); console.log("Set up MCP proxy");
} catch (error) { } catch (error) {
console.error("Error in /sse route:", error); console.error("Error in /sse route:", error);
@@ -126,6 +132,7 @@ app.post("/message", async (req, res) => {
app.get("/config", (req, res) => { app.get("/config", (req, res) => {
try { try {
const defaultEnvironment = getDefaultEnvironment(); const defaultEnvironment = getDefaultEnvironment();
res.json({ res.json({
defaultEnvironment, defaultEnvironment,
defaultCommand: values.env, defaultCommand: values.env,
@@ -138,4 +145,4 @@ app.get("/config", (req, res) => {
}); });
const PORT = process.env.PORT || 3000; const PORT = process.env.PORT || 3000;
app.listen(PORT, () => { }); app.listen(PORT, () => {});