Skip to content

Commit 9ca8dab

Browse files
authored
Merge branch 'main' into claude/issue-654-20250828-0243
2 parents ad2fd2a + ec84e21 commit 9ca8dab

33 files changed

+1513
-132
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ client/tsconfig.app.tsbuildinfo
99
client/tsconfig.node.tsbuildinfo
1010
cli/build
1111
test-output
12+
tool-test-output
1213
# symlinked by `npm run link:sdk`:
1314
sdk
1415
client/playwright-report/
1516
client/results.json
1617
client/test-results/
18+
client/e2e/test-results/
1719
mcp.json
20+
.claude/settings.local.json

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ server/build
33
CODE_OF_CONDUCT.md
44
SECURITY.md
55
mcp.json
6+
.claude/settings.local.json

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,9 @@ npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/lis
395395
# Call a specific tool
396396
npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/call --tool-name mytool --tool-arg key=value --tool-arg another=value2
397397

398+
# Call a tool with JSON arguments
399+
npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/call --tool-name mytool --tool-arg 'options={"format": "json", "max_tokens": 100}'
400+
398401
# List available resources
399402
npx @modelcontextprotocol/inspector --cli node build/index.js --method resources/list
400403

@@ -407,6 +410,9 @@ npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com
407410
# Connect to a remote MCP server (with Streamable HTTP transport)
408411
npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --transport http --method tools/list
409412

413+
# Connect to a remote MCP server (with custom headers)
414+
npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --transport http --method tools/list --header "X-API-Key: your-api-key"
415+
410416
# Call a tool on a remote server
411417
npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --method tools/call --tool-name remotetool --tool-arg param=value
412418

cli/package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@modelcontextprotocol/inspector-cli",
3-
"version": "0.16.5",
3+
"version": "0.16.6",
44
"description": "CLI for the Model Context Protocol inspector",
55
"license": "MIT",
66
"author": "Anthropic, PBC (https://anthropic.com)",
@@ -17,11 +17,14 @@
1717
"scripts": {
1818
"build": "tsc",
1919
"postbuild": "node scripts/make-executable.js",
20-
"test": "node scripts/cli-tests.js"
20+
"test": "node scripts/cli-tests.js && node scripts/cli-tool-tests.js && node scripts/cli-header-tests.js",
21+
"test:cli": "node scripts/cli-tests.js",
22+
"test:cli-tools": "node scripts/cli-tool-tests.js",
23+
"test:cli-headers": "node scripts/cli-header-tests.js"
2124
},
2225
"devDependencies": {},
2326
"dependencies": {
24-
"@modelcontextprotocol/sdk": "^1.17.3",
27+
"@modelcontextprotocol/sdk": "^1.17.5",
2528
"commander": "^13.1.0",
2629
"spawn-rx": "^5.1.2"
2730
}

cli/scripts/cli-header-tests.js

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Integration tests for header functionality
5+
* Tests the CLI header parsing end-to-end
6+
*/
7+
8+
import { spawn } from "node:child_process";
9+
import { resolve, dirname } from "node:path";
10+
import { fileURLToPath } from "node:url";
11+
12+
const __dirname = dirname(fileURLToPath(import.meta.url));
13+
const CLI_PATH = resolve(__dirname, "..", "build", "index.js");
14+
15+
// ANSI colors for output
16+
const colors = {
17+
GREEN: "\x1b[32m",
18+
RED: "\x1b[31m",
19+
YELLOW: "\x1b[33m",
20+
BLUE: "\x1b[34m",
21+
NC: "\x1b[0m", // No Color
22+
};
23+
24+
let testsPassed = 0;
25+
let testsFailed = 0;
26+
27+
/**
28+
* Run a CLI test with given arguments and check for expected behavior
29+
*/
30+
function runHeaderTest(
31+
testName,
32+
args,
33+
expectSuccess = false,
34+
expectedInOutput = null,
35+
) {
36+
return new Promise((resolve) => {
37+
console.log(`\n${colors.BLUE}Testing: ${testName}${colors.NC}`);
38+
console.log(
39+
`${colors.BLUE}Command: node ${CLI_PATH} ${args.join(" ")}${colors.NC}`,
40+
);
41+
42+
const child = spawn("node", [CLI_PATH, ...args], {
43+
stdio: ["pipe", "pipe", "pipe"],
44+
timeout: 10000,
45+
});
46+
47+
let stdout = "";
48+
let stderr = "";
49+
50+
child.stdout.on("data", (data) => {
51+
stdout += data.toString();
52+
});
53+
54+
child.stderr.on("data", (data) => {
55+
stderr += data.toString();
56+
});
57+
58+
child.on("close", (code) => {
59+
const output = stdout + stderr;
60+
let passed = true;
61+
let reason = "";
62+
63+
// Check exit code expectation
64+
if (expectSuccess && code !== 0) {
65+
passed = false;
66+
reason = `Expected success (exit code 0) but got ${code}`;
67+
} else if (!expectSuccess && code === 0) {
68+
passed = false;
69+
reason = `Expected failure (non-zero exit code) but got success`;
70+
}
71+
72+
// Check expected output
73+
if (passed && expectedInOutput && !output.includes(expectedInOutput)) {
74+
passed = false;
75+
reason = `Expected output to contain "${expectedInOutput}"`;
76+
}
77+
78+
if (passed) {
79+
console.log(`${colors.GREEN}PASS: ${testName}${colors.NC}`);
80+
testsPassed++;
81+
} else {
82+
console.log(`${colors.RED}FAIL: ${testName}${colors.NC}`);
83+
console.log(`${colors.RED}Reason: ${reason}${colors.NC}`);
84+
console.log(`${colors.RED}Exit code: ${code}${colors.NC}`);
85+
console.log(`${colors.RED}Output: ${output}${colors.NC}`);
86+
testsFailed++;
87+
}
88+
89+
resolve();
90+
});
91+
92+
child.on("error", (error) => {
93+
console.log(
94+
`${colors.RED}ERROR: ${testName} - ${error.message}${colors.NC}`,
95+
);
96+
testsFailed++;
97+
resolve();
98+
});
99+
});
100+
}
101+
102+
async function runHeaderIntegrationTests() {
103+
console.log(
104+
`${colors.YELLOW}=== MCP Inspector CLI Header Integration Tests ===${colors.NC}`,
105+
);
106+
console.log(
107+
`${colors.BLUE}Testing header parsing and validation${colors.NC}`,
108+
);
109+
110+
// Test 1: Valid header format should parse successfully (connection will fail)
111+
await runHeaderTest(
112+
"Valid single header",
113+
[
114+
"https://example.com",
115+
"--method",
116+
"tools/list",
117+
"--transport",
118+
"http",
119+
"--header",
120+
"Authorization: Bearer token123",
121+
],
122+
false,
123+
);
124+
125+
// Test 2: Multiple headers should parse successfully
126+
await runHeaderTest(
127+
"Multiple headers",
128+
[
129+
"https://example.com",
130+
"--method",
131+
"tools/list",
132+
"--transport",
133+
"http",
134+
"--header",
135+
"Authorization: Bearer token123",
136+
"--header",
137+
"X-API-Key: secret123",
138+
],
139+
false,
140+
);
141+
142+
// Test 3: Invalid header format - no colon
143+
await runHeaderTest(
144+
"Invalid header format - no colon",
145+
[
146+
"https://example.com",
147+
"--method",
148+
"tools/list",
149+
"--transport",
150+
"http",
151+
"--header",
152+
"InvalidHeader",
153+
],
154+
false,
155+
"Invalid header format",
156+
);
157+
158+
// Test 4: Invalid header format - empty name
159+
await runHeaderTest(
160+
"Invalid header format - empty name",
161+
[
162+
"https://example.com",
163+
"--method",
164+
"tools/list",
165+
"--transport",
166+
"http",
167+
"--header",
168+
": value",
169+
],
170+
false,
171+
"Invalid header format",
172+
);
173+
174+
// Test 5: Invalid header format - empty value
175+
await runHeaderTest(
176+
"Invalid header format - empty value",
177+
[
178+
"https://example.com",
179+
"--method",
180+
"tools/list",
181+
"--transport",
182+
"http",
183+
"--header",
184+
"Header:",
185+
],
186+
false,
187+
"Invalid header format",
188+
);
189+
190+
// Test 6: Header with colons in value
191+
await runHeaderTest(
192+
"Header with colons in value",
193+
[
194+
"https://example.com",
195+
"--method",
196+
"tools/list",
197+
"--transport",
198+
"http",
199+
"--header",
200+
"X-Time: 2023:12:25:10:30:45",
201+
],
202+
false,
203+
);
204+
205+
// Test 7: Whitespace handling
206+
await runHeaderTest(
207+
"Whitespace handling in headers",
208+
[
209+
"https://example.com",
210+
"--method",
211+
"tools/list",
212+
"--transport",
213+
"http",
214+
"--header",
215+
" X-Header : value with spaces ",
216+
],
217+
false,
218+
);
219+
220+
console.log(`\n${colors.YELLOW}=== Test Results ===${colors.NC}`);
221+
console.log(`${colors.GREEN}Tests passed: ${testsPassed}${colors.NC}`);
222+
console.log(`${colors.RED}Tests failed: ${testsFailed}${colors.NC}`);
223+
224+
if (testsFailed === 0) {
225+
console.log(
226+
`${colors.GREEN}All header integration tests passed!${colors.NC}`,
227+
);
228+
process.exit(0);
229+
} else {
230+
console.log(
231+
`${colors.RED}Some header integration tests failed.${colors.NC}`,
232+
);
233+
process.exit(1);
234+
}
235+
}
236+
237+
// Handle graceful shutdown
238+
process.on("SIGINT", () => {
239+
console.log(`\n${colors.YELLOW}Test interrupted by user${colors.NC}`);
240+
process.exit(1);
241+
});
242+
243+
process.on("SIGTERM", () => {
244+
console.log(`\n${colors.YELLOW}Test terminated${colors.NC}`);
245+
process.exit(1);
246+
});
247+
248+
// Run the tests
249+
runHeaderIntegrationTests().catch((error) => {
250+
console.error(`${colors.RED}Test runner error: ${error.message}${colors.NC}`);
251+
process.exit(1);
252+
});

cli/scripts/cli-tests.js

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,6 @@ console.log(`${colors.BLUE}- Environment variables (-e)${colors.NC}`);
3737
console.log(`${colors.BLUE}- Config file (--config)${colors.NC}`);
3838
console.log(`${colors.BLUE}- Server selection (--server)${colors.NC}`);
3939
console.log(`${colors.BLUE}- Method selection (--method)${colors.NC}`);
40-
console.log(
41-
`${colors.BLUE}- Tool-related options (--tool-name, --tool-arg)${colors.NC}`,
42-
);
4340
console.log(`${colors.BLUE}- Resource-related options (--uri)${colors.NC}`);
4441
console.log(
4542
`${colors.BLUE}- Prompt-related options (--prompt-name, --prompt-args)${colors.NC}`,
@@ -533,65 +530,6 @@ async function runTests() {
533530
"tools/list",
534531
);
535532

536-
console.log(
537-
`\n${colors.YELLOW}=== Running Tool-Related Tests ===${colors.NC}`,
538-
);
539-
540-
// Test 12: CLI mode with tool call
541-
await runBasicTest(
542-
"tool_call",
543-
TEST_CMD,
544-
...TEST_ARGS,
545-
"--cli",
546-
"--method",
547-
"tools/call",
548-
"--tool-name",
549-
"echo",
550-
"--tool-arg",
551-
"message=Hello",
552-
);
553-
554-
// Test 13: CLI mode with tool call but missing tool name (should fail)
555-
await runErrorTest(
556-
"missing_tool_name",
557-
TEST_CMD,
558-
...TEST_ARGS,
559-
"--cli",
560-
"--method",
561-
"tools/call",
562-
"--tool-arg",
563-
"message=Hello",
564-
);
565-
566-
// Test 14: CLI mode with tool call but invalid tool args format (should fail)
567-
await runErrorTest(
568-
"invalid_tool_args",
569-
TEST_CMD,
570-
...TEST_ARGS,
571-
"--cli",
572-
"--method",
573-
"tools/call",
574-
"--tool-name",
575-
"echo",
576-
"--tool-arg",
577-
"invalid_format",
578-
);
579-
580-
// Test 15: CLI mode with multiple tool args
581-
await runBasicTest(
582-
"multiple_tool_args",
583-
TEST_CMD,
584-
...TEST_ARGS,
585-
"--cli",
586-
"--method",
587-
"tools/call",
588-
"--tool-name",
589-
"add",
590-
"--tool-arg",
591-
"a=1",
592-
"b=2",
593-
);
594-
595533
console.log(
596534
`\n${colors.YELLOW}=== Running Resource-Related Tests ===${colors.NC}`,
597535
);

0 commit comments

Comments
 (0)