Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions cli/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { describe, it, beforeAll, afterAll, expect } from "vitest";
import { cpSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { runCli } from "./helpers/cli-runner.js";
import {
expectCliSuccess,
Expand All @@ -19,6 +22,37 @@ import {
createTestServerInfo,
} from "./helpers/test-fixtures.js";

const __dirname = dirname(fileURLToPath(import.meta.url));

function createPublishedCliFixture(): {
cliPath: string;
cwd: string;
root: string;
} {
const root = mkdtempSync(join(process.cwd(), ".published-cli-"));
mkdirSync(join(root, "cli"), { recursive: true });
cpSync(resolve(__dirname, "../build"), join(root, "cli", "build"), {
recursive: true,
});
writeFileSync(
join(root, "package.json"),
JSON.stringify({
name: "@modelcontextprotocol/inspector",
version: "0.0.0-test",
type: "module",
}),
);

const cwd = join(root, "runner");
mkdirSync(cwd);

return {
cliPath: join(root, "cli", "build", "cli.js"),
cwd,
root,
};
}

describe("CLI Tests", () => {
describe("Basic CLI Mode", () => {
it("should execute tools/list successfully", async () => {
Expand All @@ -43,6 +77,30 @@ describe("CLI Tests", () => {
expect(toolNames).toContain("get-annotated-message");
});

it("should resolve package metadata from a published package layout", async () => {
const { command, args } = getTestMcpServerCommand();
const fixture = createPublishedCliFixture();

try {
const result = await runCli(
[command, ...args, "--cli", "--method", "tools/list"],
{
cliPath: fixture.cliPath,
cwd: fixture.cwd,
},
);

expectCliSuccess(result);
const json = expectValidJson(result);
expect(json).toHaveProperty("tools");
expect(Array.isArray(json.tools)).toBe(true);
const toolNames = json.tools.map((tool: any) => tool.name);
expect(toolNames).toContain("echo");
} finally {
rmSync(fixture.root, { recursive: true, force: true });
}
});

it("should fail with nonexistent method", async () => {
const result = await runCli([
NO_SERVER_SENTINEL,
Expand Down
3 changes: 2 additions & 1 deletion cli/__tests__/helpers/cli-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface CliResult {
export interface CliOptions {
timeout?: number;
cwd?: string;
cliPath?: string;
env?: Record<string, string>;
signal?: AbortSignal;
}
Expand All @@ -28,7 +29,7 @@ export async function runCli(
options: CliOptions = {},
): Promise<CliResult> {
return new Promise((resolve, reject) => {
const child = spawn("node", [CLI_PATH, ...args], {
const child = spawn("node", [options.cliPath ?? CLI_PATH, ...args], {
stdio: ["pipe", "pipe", "pipe"],
cwd: options.cwd,
env: { ...process.env, ...options.env },
Expand Down
14 changes: 11 additions & 3 deletions cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,18 @@ function createTransportOptions(

async function callMethod(args: Args): Promise<void> {
// Read package.json to get name and version for client identity
const pathA = "../package.json"; // We're in package @modelcontextprotocol/inspector-cli
const pathB = "../../package.json"; // We're in package @modelcontextprotocol/inspector
const packageJsonUrls = [
new URL("../package.json", import.meta.url), // We're in package @modelcontextprotocol/inspector-cli
new URL("../../package.json", import.meta.url), // We're in package @modelcontextprotocol/inspector
];
let packageJson: { name: string; version: string };
let packageJsonData = await import(fs.existsSync(pathA) ? pathA : pathB, {
const packageJsonUrl = packageJsonUrls.find((url) => fs.existsSync(url));

if (!packageJsonUrl) {
throw new Error("Unable to locate package.json for client identity");
}

let packageJsonData = await import(packageJsonUrl.href, {
with: { type: "json" },
});
packageJson = packageJsonData.default;
Expand Down