Forgot package files

This commit is contained in:
Nicolas Barraud
2025-03-10 20:34:07 -04:00
parent 4c4c8a0884
commit 9f42629b34
3 changed files with 238 additions and 204 deletions

View File

@@ -7,213 +7,186 @@ import { spawnPromise } from "spawn-rx";
import { fileURLToPath } from "url"; import { fileURLToPath } from "url";
const __dirname = dirname(fileURLToPath(import.meta.url)); const __dirname = dirname(fileURLToPath(import.meta.url));
function handleError(error) { function handleError(error) {
let message; let message;
if (error instanceof Error) { if (error instanceof Error) {
message = error.message; message = error.message;
} else if (typeof error === "string") { }
message = error; else if (typeof error === "string") {
} else { message = error;
message = "Unknown error"; }
} else {
console.error(message); message = "Unknown error";
process.exit(1); }
console.error(message);
process.exit(1);
} }
function delay(ms) { function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => setTimeout(resolve, ms));
} }
async function runWebClient(args) { async function runWebClient(args) {
const inspectorServerPath = resolve( const inspectorServerPath = resolve(__dirname, "..", "server", "build", "index.js");
__dirname, // Path to the client entry point
"..", const inspectorClientPath = resolve(__dirname, "..", "client", "bin", "cli.js");
"server", const CLIENT_PORT = process.env.CLIENT_PORT ?? "5173";
"build", const SERVER_PORT = process.env.SERVER_PORT ?? "3000";
"index.js", console.log("Starting MCP inspector...");
); const abort = new AbortController();
// Path to the client entry point let cancelled = false;
const inspectorClientPath = resolve( process.on("SIGINT", () => {
__dirname, cancelled = true;
"..", abort.abort();
"client", });
"bin", const server = spawnPromise("node", [
"cli.js", inspectorServerPath,
); ...(args.command ? [`--env`, args.command] : []),
const CLIENT_PORT = process.env.CLIENT_PORT ?? "5173"; ...(args.args ? [`--args=${args.args.join(" ")}`] : []),
const SERVER_PORT = process.env.SERVER_PORT ?? "3000"; ], {
console.log("Starting MCP inspector..."); env: {
const abort = new AbortController(); ...process.env,
let cancelled = false; PORT: SERVER_PORT,
process.on("SIGINT", () => { MCP_ENV_VARS: JSON.stringify(args.envArgs),
cancelled = true; },
abort.abort(); signal: abort.signal,
}); echoOutput: true,
const server = spawnPromise( });
"node", const client = spawnPromise("node", [inspectorClientPath], {
[ env: { ...process.env, PORT: CLIENT_PORT },
inspectorServerPath, signal: abort.signal,
...(args.command ? [`--env`, args.command] : []), echoOutput: true,
...(args.args ? [`--args=${args.args.join(" ")}`] : []), });
], // Make sure our server/client didn't immediately fail
{ await Promise.any([server, client, delay(2 * 1000)]);
env: { const portParam = SERVER_PORT === "3000" ? "" : `?proxyPort=${SERVER_PORT}`;
...process.env, console.log(`\n🔍 MCP Inspector is up and running at http://localhost:${CLIENT_PORT}${portParam} 🚀`);
PORT: SERVER_PORT, try {
MCP_ENV_VARS: JSON.stringify(args.envArgs), await Promise.any([server, client]);
}, }
signal: abort.signal, catch (e) {
echoOutput: true, if (!cancelled || process.env.DEBUG) {
}, throw e;
); }
const client = spawnPromise("node", [inspectorClientPath], {
env: { ...process.env, PORT: CLIENT_PORT },
signal: abort.signal,
echoOutput: true,
});
// Make sure our server/client didn't immediately fail
await Promise.any([server, client, delay(2 * 1000)]);
const portParam = SERVER_PORT === "3000" ? "" : `?proxyPort=${SERVER_PORT}`;
console.log(
`\n🔍 MCP Inspector is up and running at http://localhost:${CLIENT_PORT}${portParam} 🚀`,
);
try {
await Promise.any([server, client]);
} catch (e) {
if (!cancelled || process.env.DEBUG) {
throw e;
} }
}
} }
async function runCli(args) { async function runCli(args) {
const projectRoot = resolve(__dirname, ".."); const projectRoot = resolve(__dirname, "..");
const cliPath = resolve(projectRoot, "cli", "build", "index.js"); const cliPath = resolve(projectRoot, "cli", "build", "index.js");
const abort = new AbortController(); const abort = new AbortController();
let cancelled = false; let cancelled = false;
process.on("SIGINT", () => { process.on("SIGINT", () => {
cancelled = true; cancelled = true;
abort.abort(); abort.abort();
});
try {
await spawnPromise("node", [cliPath, args.command, ...args.args], {
env: { ...process.env, ...args.envArgs },
signal: abort.signal,
echoOutput: true,
}); });
} catch (e) { try {
if (!cancelled || process.env.DEBUG) { await spawnPromise("node", [cliPath, args.command, ...args.args], {
throw e; env: { ...process.env, ...args.envArgs },
signal: abort.signal,
echoOutput: true,
});
}
catch (e) {
if (!cancelled || process.env.DEBUG) {
throw e;
}
} }
}
} }
function loadConfigFile(configPath, serverName) { function loadConfigFile(configPath, serverName) {
try { try {
const resolvedConfigPath = path.isAbsolute(configPath) const resolvedConfigPath = path.isAbsolute(configPath)
? configPath ? configPath
: path.resolve(process.cwd(), configPath); : path.resolve(process.cwd(), configPath);
if (!fs.existsSync(resolvedConfigPath)) { if (!fs.existsSync(resolvedConfigPath)) {
throw new Error(`Config file not found: ${resolvedConfigPath}`); throw new Error(`Config file not found: ${resolvedConfigPath}`);
}
const configContent = fs.readFileSync(resolvedConfigPath, "utf8");
const parsedConfig = JSON.parse(configContent);
if (!parsedConfig.mcpServers || !parsedConfig.mcpServers[serverName]) {
const availableServers = Object.keys(parsedConfig.mcpServers || {}).join(", ");
throw new Error(`Server '${serverName}' not found in config file. Available servers: ${availableServers}`);
}
const serverConfig = parsedConfig.mcpServers[serverName];
return serverConfig;
} }
const configContent = fs.readFileSync(resolvedConfigPath, "utf8"); catch (err) {
const parsedConfig = JSON.parse(configContent); if (err instanceof SyntaxError) {
if (!parsedConfig.mcpServers || !parsedConfig.mcpServers[serverName]) { throw new Error(`Invalid JSON in config file: ${err.message}`);
const availableServers = Object.keys(parsedConfig.mcpServers || {}).join( }
", ", throw err;
);
throw new Error(
`Server '${serverName}' not found in config file. Available servers: ${availableServers}`,
);
} }
const serverConfig = parsedConfig.mcpServers[serverName];
return serverConfig;
} catch (err) {
if (err instanceof SyntaxError) {
throw new Error(`Invalid JSON in config file: ${err.message}`);
}
throw err;
}
} }
function parseKeyValuePair(value, previous = {}) { function parseKeyValuePair(value, previous = {}) {
const parts = value.split("="); const parts = value.split("=");
const key = parts[0]; const key = parts[0];
const val = parts.slice(1).join("="); const val = parts.slice(1).join("=");
if (val === undefined || val === "") { if (val === undefined || val === "") {
throw new Error( throw new Error(`Invalid parameter format: ${value}. Use key=value format.`);
`Invalid parameter format: ${value}. Use key=value format.`, }
); return { ...previous, [key]: val };
}
return { ...previous, [key]: val };
} }
function parseArgs() { function parseArgs() {
const program = new Command(); const program = new Command();
const argSeparatorIndex = process.argv.indexOf("--"); const argSeparatorIndex = process.argv.indexOf("--");
let preArgs = process.argv; let preArgs = process.argv;
let postArgs = []; let postArgs = [];
if (argSeparatorIndex !== -1) { if (argSeparatorIndex !== -1) {
preArgs = process.argv.slice(0, argSeparatorIndex); preArgs = process.argv.slice(0, argSeparatorIndex);
postArgs = process.argv.slice(argSeparatorIndex + 1); postArgs = process.argv.slice(argSeparatorIndex + 1);
} }
program program
.name("inspector-bin") .name("inspector-bin")
.allowExcessArguments() .allowExcessArguments()
.allowUnknownOption() .allowUnknownOption()
.option( .option("-e <env>", "environment variables in KEY=VALUE format", parseKeyValuePair, {})
"-e <env>", .option("--config <path>", "config file path")
"environment variables in KEY=VALUE format", .option("--server <n>", "server name from config file")
parseKeyValuePair, .option("--cli", "enable CLI mode");
{}, // Parse only the arguments before --
) program.parse(preArgs);
.option("--config <path>", "config file path") const options = program.opts();
.option("--server <n>", "server name from config file") const remainingArgs = program.args;
.option("--cli", "enable CLI mode"); // Add back any arguments that came after --
// Parse only the arguments before -- const finalArgs = [...remainingArgs, ...postArgs];
program.parse(preArgs); // Validate that config and server are provided together
const options = program.opts(); if ((options.config && !options.server) ||
const remainingArgs = program.args; (!options.config && options.server)) {
// Add back any arguments that came after -- throw new Error("Both --config and --server must be provided together. If you specify one, you must specify the other.");
const finalArgs = [...remainingArgs, ...postArgs]; }
// Validate that config and server are provided together // If config file is specified, load and use the options from the file. We must merge the args
if ( // from the command line and the file together, or we will miss the method options (--method,
(options.config && !options.server) || // etc.)
(!options.config && options.server) if (options.config && options.server) {
) { const config = loadConfigFile(options.config, options.server);
throw new Error( return {
"Both --config and --server must be provided together. If you specify one, you must specify the other.", command: config.command,
); args: [...(config.args || []), ...finalArgs],
} envArgs: { ...(config.env || {}), ...(options.e || {}) },
// If config file is specified, load and use the options from the file. We must merge the args cli: options.cli || false,
// from the command line and the file together, or we will miss the method options (--method, };
// etc.) }
if (options.config && options.server) { // Otherwise use command line arguments
const config = loadConfigFile(options.config, options.server); const command = finalArgs[0] || "";
const args = finalArgs.slice(1);
return { return {
command: config.command, command,
args: [...(config.args || []), ...finalArgs], args,
envArgs: { ...(config.env || {}), ...(options.e || {}) }, envArgs: options.e || {},
cli: options.cli || false, cli: options.cli || false,
}; };
}
// Otherwise use command line arguments
const command = finalArgs[0] || "";
const args = finalArgs.slice(1);
return {
command,
args,
envArgs: options.e || {},
cli: options.cli || false,
};
} }
async function main() { async function main() {
process.on("uncaughtException", (error) => { process.on("uncaughtException", (error) => {
handleError(error); handleError(error);
}); });
try { try {
const args = parseArgs(); const args = parseArgs();
console.log(args); if (args.cli) {
if (args.cli) { runCli(args);
runCli(args); }
} else { else {
await runWebClient(args); await runWebClient(args);
}
}
catch (error) {
handleError(error);
} }
} catch (error) {
handleError(error);
}
} }
main(); main();

68
package-lock.json generated
View File

@@ -10,25 +10,56 @@
"license": "MIT", "license": "MIT",
"workspaces": [ "workspaces": [
"client", "client",
"server" "server",
"cli",
"bin"
], ],
"dependencies": { "dependencies": {
"@modelcontextprotocol/inspector-client": "0.4.1", "@modelcontextprotocol/inspector-bin": "0.5.1",
"@modelcontextprotocol/inspector-server": "0.4.1", "@modelcontextprotocol/inspector-cli": "0.5.1",
"@modelcontextprotocol/inspector-client": "0.5.1",
"@modelcontextprotocol/inspector-server": "0.5.1",
"@modelcontextprotocol/sdk": "^1.6.1",
"commander": "^13.1.0",
"concurrently": "^9.0.1", "concurrently": "^9.0.1",
"shell-quote": "^1.8.2", "shell-quote": "^1.8.2",
"spawn-rx": "^5.1.2", "spawn-rx": "^5.1.2",
"ts-node": "^10.9.2" "ts-node": "^10.9.2",
"zod": "^3.23.8"
}, },
"bin": { "bin": {
"mcp-inspector": "bin/cli.js" "mcp-inspector": "bin/cli.js",
"mcp-inspector-cli": "cli/bin/cli.js"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.7.5", "@types/node": "^22.7.5",
"@types/shell-quote": "^1.7.5", "@types/shell-quote": "^1.7.5",
"prettier": "3.3.3" "prettier": "3.3.3",
"typescript": "^5.4.2"
} }
}, },
"bin": {
"name": "@modelcontextprotocol/inspector-bin",
"version": "0.5.1",
"license": "MIT",
"bin": {
"mcp-inspector": "cli.js"
},
"devDependencies": {}
},
"cli": {
"name": "@modelcontextprotocol/inspector-cli",
"version": "0.5.1",
"license": "MIT",
"dependencies": {
"commander": "^13.1.0",
"spawn-rx": "^5.1.2"
},
"bin": {
"mcp-inspector-cli": "build/index.js"
},
"devDependencies": {}
},
"client": { "client": {
"name": "@modelcontextprotocol/inspector-client", "name": "@modelcontextprotocol/inspector-client",
"version": "0.5.1", "version": "0.5.1",
@@ -1204,6 +1235,14 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@modelcontextprotocol/inspector-bin": {
"resolved": "bin",
"link": true
},
"node_modules/@modelcontextprotocol/inspector-cli": {
"resolved": "cli",
"link": true
},
"node_modules/@modelcontextprotocol/inspector-client": { "node_modules/@modelcontextprotocol/inspector-client": {
"resolved": "client", "resolved": "client",
"link": true "link": true
@@ -4108,12 +4147,12 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/commander": { "node_modules/commander": {
"version": "4.1.1", "version": "13.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 6" "node": ">=18"
} }
}, },
"node_modules/concat-map": { "node_modules/concat-map": {
@@ -6936,6 +6975,15 @@
"node": ">=16 || 14 >=14.17" "node": ">=16 || 14 >=14.17"
} }
}, },
"node_modules/sucrase/node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/supports-color": { "node_modules/supports-color": {
"version": "8.1.1", "version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",

View File

@@ -8,24 +8,31 @@
"bugs": "https://github.com/modelcontextprotocol/inspector/issues", "bugs": "https://github.com/modelcontextprotocol/inspector/issues",
"type": "module", "type": "module",
"bin": { "bin": {
"mcp-inspector": "./bin/cli.js" "mcp-inspector": "./bin/cli.js",
"mcp-inspector-cli": "./cli/bin/cli.js"
}, },
"files": [ "files": [
"bin", "bin",
"client/bin", "client/bin",
"client/dist", "client/dist",
"server/build" "server/build",
"cli/bin",
"cli/build"
], ],
"workspaces": [ "workspaces": [
"client", "client",
"server" "server",
"cli",
"bin"
], ],
"scripts": { "scripts": {
"dev": "concurrently \"cd client && npm run dev\" \"cd server && npm run dev\"", "dev": "concurrently \"cd client && npm run dev\" \"cd server && npm run dev\"",
"dev:windows": "concurrently \"cd client && npm run dev\" \"cd server && npm run dev:windows", "dev:windows": "concurrently \"cd client && npm run dev\" \"cd server && npm run dev:windows",
"build-bin": "cd bin && npm run build",
"build-server": "cd server && npm run build", "build-server": "cd server && npm run build",
"build-client": "cd client && npm run build", "build-client": "cd client && npm run build",
"build": "npm run build-server && npm run build-client", "build-cli": "cd cli && npm run build",
"build": "npm run build-bin && npm run build-server && npm run build-client && npm run build-cli",
"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": "node ./bin/cli.js", "start": "node ./bin/cli.js",
@@ -34,16 +41,22 @@
"publish-all": "npm publish --workspaces --access public && npm publish --access public" "publish-all": "npm publish --workspaces --access public && npm publish --access public"
}, },
"dependencies": { "dependencies": {
"@modelcontextprotocol/inspector-client": "0.4.1", "@modelcontextprotocol/inspector-bin": "0.5.1",
"@modelcontextprotocol/inspector-server": "0.4.1", "@modelcontextprotocol/inspector-cli": "0.5.1",
"@modelcontextprotocol/inspector-client": "0.5.1",
"@modelcontextprotocol/inspector-server": "0.5.1",
"@modelcontextprotocol/sdk": "^1.6.1",
"commander": "^13.1.0",
"concurrently": "^9.0.1", "concurrently": "^9.0.1",
"shell-quote": "^1.8.2", "shell-quote": "^1.8.2",
"spawn-rx": "^5.1.2", "spawn-rx": "^5.1.2",
"ts-node": "^10.9.2" "ts-node": "^10.9.2",
"zod": "^3.23.8"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.7.5", "@types/node": "^22.7.5",
"@types/shell-quote": "^1.7.5", "@types/shell-quote": "^1.7.5",
"prettier": "3.3.3" "prettier": "3.3.3",
"typescript": "^5.4.2"
} }
} }