Skip to content

Commit 1778ab7

Browse files
authored
Merge branch 'main' into fix766
2 parents 6b26dd3 + b7ef160 commit 1778ab7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+4072
-1024
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
name: Bug report
3+
about: Create a report to help us improve
4+
title: ""
5+
labels: ""
6+
assignees: ""
7+
---
8+
9+
**Inspector Version**
10+
11+
- [e.g. 0.16.5)
12+
13+
**Describe the bug**
14+
A clear and concise description of what the bug is.
15+
16+
**To Reproduce**
17+
Steps to reproduce the behavior:
18+
19+
1. Go to '...'
20+
2. Click on '....'
21+
3. Scroll down to '....'
22+
4. See error
23+
24+
**Expected behavior**
25+
A clear and concise description of what you expected to happen.
26+
27+
**Screenshots**
28+
If applicable, add screenshots to help explain your problem.
29+
30+
**Environment (please complete the following information):**
31+
32+
- OS: [e.g. iOS]
33+
- Browser [e.g. chrome, safari]
34+
35+
**Additional context**
36+
Add any other context about the problem here.

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ sdk
1515
client/playwright-report/
1616
client/results.json
1717
client/test-results/
18+
client/e2e/test-results/
1819
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: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ The MCP Inspector provides convenient buttons to export server launch configurat
9898
}
9999
```
100100

101+
**Streamable HTTP transport example:**
102+
103+
```json
104+
{
105+
"type": "streamable-http",
106+
"url": "http://localhost:3000/mcp",
107+
"note": "For Streamable HTTP connections, add this URL directly in your MCP Client"
108+
}
109+
```
110+
101111
- **Servers File** - Copies a complete MCP configuration file structure to your clipboard, with your current server configuration added as `default-server`. This can be saved directly as `mcp.json`.
102112

103113
**STDIO transport example:**
@@ -131,9 +141,23 @@ The MCP Inspector provides convenient buttons to export server launch configurat
131141
}
132142
```
133143

144+
**Streamable HTTP transport example:**
145+
146+
```json
147+
{
148+
"mcpServers": {
149+
"default-server": {
150+
"type": "streamable-http",
151+
"url": "http://localhost:3000/mcp",
152+
"note": "For Streamable HTTP connections, add this URL directly in your MCP Client"
153+
}
154+
}
155+
}
156+
```
157+
134158
These buttons appear in the Inspector UI after you've configured your server settings, making it easy to save and reuse your configurations.
135159

136-
For SSE transport connections, the Inspector provides similar functionality for both buttons. The "Server Entry" button copies the SSE URL configuration that can be added to your existing configuration file, while the "Servers File" button creates a complete configuration file containing the SSE URL for direct use in clients.
160+
For SSE and Streamable HTTP transport connections, the Inspector provides similar functionality for both buttons. The "Server Entry" button copies the configuration that can be added to your existing configuration file, while the "Servers File" button creates a complete configuration file containing the URL for direct use in clients.
137161

138162
You can paste the Server Entry into your existing `mcp.json` file under your chosen server name, or use the complete Servers File payload to create a new configuration file.
139163

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

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

cli/package.json

Lines changed: 5 additions & 4 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.17.0",
44
"description": "CLI for the Model Context Protocol inspector",
55
"license": "MIT",
66
"author": "Anthropic, PBC (https://anthropic.com)",
@@ -17,13 +17,14 @@
1717
"scripts": {
1818
"build": "tsc",
1919
"postbuild": "node scripts/make-executable.js",
20-
"test": "node scripts/cli-tests.js && node scripts/cli-tool-tests.js",
20+
"test": "node scripts/cli-tests.js && node scripts/cli-tool-tests.js && node scripts/cli-header-tests.js",
2121
"test:cli": "node scripts/cli-tests.js",
22-
"test:cli-tools": "node scripts/cli-tool-tests.js"
22+
"test:cli-tools": "node scripts/cli-tool-tests.js",
23+
"test:cli-headers": "node scripts/cli-header-tests.js"
2324
},
2425
"devDependencies": {},
2526
"dependencies": {
26-
"@modelcontextprotocol/sdk": "^1.17.3",
27+
"@modelcontextprotocol/sdk": "^1.18.0",
2728
"commander": "^13.1.0",
2829
"spawn-rx": "^5.1.2"
2930
}

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+
});

0 commit comments

Comments
 (0)