#!/usr/bin/env node
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { Command } from "commander";
import { callTool, connect, disconnect, getPrompt, listPrompts, listResources, listResourceTemplates, listTools, readResource, setLoggingLevel, validLogLevels, } from "./client/index.js";
import { handleError } from "./error-handler.js";
import { createTransport } from "./transport.js";
function createTransportOptions(target) {
if (target.length === 0) {
throw new Error("Target is required. Specify a URL or a command to execute.");
}
const [command, ...commandArgs] = target;
if (!command) {
throw new Error("Command is required.");
}
const isUrl = command.startsWith("http://") || command.startsWith("https://");
if (isUrl && commandArgs.length > 0) {
throw new Error("Arguments cannot be passed to a URL-based MCP server.");
}
return {
transportType: isUrl ? "sse" : "stdio",
command: isUrl ? undefined : command,
args: isUrl ? undefined : commandArgs,
url: isUrl ? command : undefined,
};
}
async function callMethod(args) {
const transportOptions = createTransportOptions(args.target);
const transport = createTransport(transportOptions);
const client = new Client({
name: "inspector-cli",
version: "0.5.1",
});
try {
await connect(client, transport);
let result;
// Tools methods
if (args.method === "tools/list") {
result = await listTools(client);
}
else if (args.method === "tools/call") {
if (!args.toolName) {
throw new Error("Tool name is required for tools/call method. Use --tool-name to specify the tool name.");
}
result = await callTool(client, args.toolName, args.toolArg || {});
}
// Resources methods
else if (args.method === "resources/list") {
result = await listResources(client);
}
else if (args.method === "resources/read") {
if (!args.uri) {
throw new Error("URI is required for resources/read method. Use --uri to specify the resource URI.");
}
result = await readResource(client, args.uri);
}
else if (args.method === "resources/templates/list") {
result = await listResourceTemplates(client);
}
// Prompts methods
else if (args.method === "prompts/list") {
result = await listPrompts(client);
}
else if (args.method === "prompts/get") {
if (!args.promptName) {
throw new Error("Prompt name is required for prompts/get method. Use --prompt-name to specify the prompt name.");
}
result = await getPrompt(client, args.promptName, args.promptArgs || {});
}
// Logging methods
else if (args.method === "logging/setLevel") {
if (!args.logLevel) {
throw new Error("Log level is required for logging/setLevel method. Use --log-level to specify the log level.");
}
result = await setLoggingLevel(client, args.logLevel);
}
else {
throw new Error(`Unsupported method: ${args.method}. Supported methods include: tools/list, tools/call, resources/list, resources/read, resources/templates/list, prompts/list, prompts/get, logging/setLevel`);
}
console.log(JSON.stringify(result, null, 2));
}
finally {
try {
await disconnect(transport);
}
catch (disconnectError) {
throw disconnectError;
}
}
}
function parseKeyValuePair(value, previous = {}) {
const parts = value.split("=");
const key = parts[0];
const val = parts.slice(1).join("=");
if (val === undefined || val === "") {
throw new Error(`Invalid parameter format: ${value}. Use key=value format.`);
}
return { ...previous, [key]: val };
}
function parseArgs() {
const program = new Command();
// Find if there's a -- in the arguments and split them
const argSeparatorIndex = process.argv.indexOf("--");
let preArgs = process.argv;
let postArgs = [];
if (argSeparatorIndex !== -1) {
preArgs = process.argv.slice(0, argSeparatorIndex);
postArgs = process.argv.slice(argSeparatorIndex + 1);
}
program
.name("inspector-cli")
.allowUnknownOption()
.argument("<target...>", "Command and arguments or URL of the MCP server")
//
// Method selection
//
.option("--method <method>", "Method to invoke")
//
// Tool-related options
//
.option("--tool-name <toolName>", "Tool name (for tools/call method)")
.option("--tool-arg <pairs...>", "Tool argument as key=value pair", parseKeyValuePair, {})
//
// Resource-related options
//
.option("--uri <uri>", "URI of the resource (for resources/read method)")
//
// Prompt-related options
//
.option("--prompt-name <promptName>", "Name of the prompt (for prompts/get method)")
.option("--prompt-args <pairs...>", "Prompt arguments as key=value pairs", parseKeyValuePair, {})
//
// Logging options
//
.option("--log-level <level>", "Logging level (for logging/setLevel method)", (value) => {
if (!validLogLevels.includes(value)) {
throw new Error(`Invalid log level: ${value}. Valid levels are: ${validLogLevels.join(", ")}`);
}
return value;
});
// Parse only the arguments before --
program.parse(preArgs);
const options = program.opts();
let remainingArgs = program.args;
// Add back any arguments that came after --
const finalArgs = [...remainingArgs, ...postArgs];
if (!options.method) {
throw new Error("Method is required. Use --method to specify the method to invoke.");
}
return {
target: finalArgs,
...options,
};
}
async function main() {
process.on("uncaughtException", (error) => {
handleError(error);
});
try {
const args = parseArgs();
await callMethod(args);
}
catch (error) {
handleError(error);
}
}
main();