Skip to content

Add support for --header flags in MCP Inspector CLI #716

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ sdk
client/playwright-report/
client/results.json
client/test-results/
client/e2e/test-results/
mcp.json
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ server/build
CODE_OF_CONDUCT.md
SECURITY.md
mcp.json
.claude/settings.local.json
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,9 @@ npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com
# Connect to a remote MCP server (with Streamable HTTP transport)
npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --transport http --method tools/list

# Connect to a remote MCP server (with custom headers)
npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --transport http --method tools/list --header "X-API-Key: your-api-key"

# Call a tool on a remote server
npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --method tools/call --tool-name remotetool --tool-arg param=value

Expand Down
49 changes: 46 additions & 3 deletions cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Args = {
cli: boolean;
transport?: "stdio" | "sse" | "streamable-http";
serverUrl?: string;
headers?: Record<string, string>;
};

type CliOptions = {
Expand All @@ -25,6 +26,7 @@ type CliOptions = {
cli?: boolean;
transport?: string;
serverUrl?: string;
header?: Record<string, string>;
};

type ServerConfig =
Expand Down Expand Up @@ -127,6 +129,9 @@ async function runCli(args: Args): Promise<void> {
// Build CLI arguments
const cliArgs = [cliPath];

// Add target URL/command first
cliArgs.push(args.command, ...args.args);

// Add transport flag if specified
if (args.transport && args.transport !== "stdio") {
// Convert streamable-http back to http for CLI mode
Expand All @@ -135,8 +140,12 @@ async function runCli(args: Args): Promise<void> {
cliArgs.push("--transport", cliTransport);
}

// Add command and remaining args
cliArgs.push(args.command, ...args.args);
// Add headers if specified
if (args.headers) {
for (const [key, value] of Object.entries(args.headers)) {
cliArgs.push("--header", `${key}: ${value}`);
}
}

await spawnPromise("node", cliArgs, {
env: { ...process.env, ...args.envArgs },
Expand Down Expand Up @@ -201,6 +210,30 @@ function parseKeyValuePair(
return { ...previous, [key as string]: val };
}

function parseHeaderPair(
value: string,
previous: Record<string, string> = {},
): Record<string, string> {
const colonIndex = value.indexOf(":");

if (colonIndex === -1) {
throw new Error(
`Invalid header format: ${value}. Use "HeaderName: Value" format.`,
);
}

const key = value.slice(0, colonIndex).trim();
const val = value.slice(colonIndex + 1).trim();

if (key === "" || val === "") {
throw new Error(
`Invalid header format: ${value}. Use "HeaderName: Value" format.`,
);
}

return { ...previous, [key]: val };
}

function parseArgs(): Args {
const program = new Command();

Expand All @@ -227,7 +260,13 @@ function parseArgs(): Args {
.option("--server <n>", "server name from config file")
.option("--cli", "enable CLI mode")
.option("--transport <type>", "transport type (stdio, sse, http)")
.option("--server-url <url>", "server URL for SSE/HTTP transport");
.option("--server-url <url>", "server URL for SSE/HTTP transport")
.option(
"--header <headers...>",
'HTTP headers as "HeaderName: Value" pairs (for HTTP/SSE transports)',
parseHeaderPair,
{},
);

// Parse only the arguments before --
program.parse(preArgs);
Expand Down Expand Up @@ -280,6 +319,7 @@ function parseArgs(): Args {
envArgs: { ...(config.env || {}), ...(options.e || {}) },
cli: options.cli || false,
transport: "stdio",
headers: options.header,
};
} else if (config.type === "sse" || config.type === "streamable-http") {
return {
Expand All @@ -289,6 +329,7 @@ function parseArgs(): Args {
cli: options.cli || false,
transport: config.type,
serverUrl: config.url,
headers: options.header,
};
} else {
// Backwards compatibility: if no type field, assume stdio
Expand All @@ -298,6 +339,7 @@ function parseArgs(): Args {
envArgs: { ...((config as any).env || {}), ...(options.e || {}) },
cli: options.cli || false,
transport: "stdio",
headers: options.header,
};
}
}
Expand All @@ -319,6 +361,7 @@ function parseArgs(): Args {
cli: options.cli || false,
transport: transport as "stdio" | "sse" | "streamable-http" | undefined,
serverUrl: options.serverUrl,
headers: options.header,
};
}

Expand Down
53 changes: 49 additions & 4 deletions cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ type Args = {
toolName?: string;
toolArg?: Record<string, string>;
transport?: "sse" | "stdio" | "http";
headers?: Record<string, string>;
};

function createTransportOptions(
target: string[],
transport?: "sse" | "stdio" | "http",
headers?: Record<string, string>,
): TransportOptions {
if (target.length === 0) {
throw new Error(
Expand Down Expand Up @@ -81,11 +83,16 @@ function createTransportOptions(
command: isUrl ? undefined : command,
args: isUrl ? undefined : commandArgs,
url: isUrl ? command : undefined,
headers,
};
}

async function callMethod(args: Args): Promise<void> {
const transportOptions = createTransportOptions(args.target, args.transport);
const transportOptions = createTransportOptions(
args.target,
args.transport,
args.headers,
);
const transport = createTransport(transportOptions);
const client = new Client({
name: "inspector-cli",
Expand Down Expand Up @@ -177,6 +184,30 @@ function parseKeyValuePair(
return { ...previous, [key as string]: val };
}

function parseHeaderPair(
value: string,
previous: Record<string, string> = {},
): Record<string, string> {
const colonIndex = value.indexOf(":");

if (colonIndex === -1) {
throw new Error(
`Invalid header format: ${value}. Use "HeaderName: Value" format.`,
);
}

const key = value.slice(0, colonIndex).trim();
const val = value.slice(colonIndex + 1).trim();

if (key === "" || val === "") {
throw new Error(
`Invalid header format: ${value}. Use "HeaderName: Value" format.`,
);
}

return { ...previous, [key]: val };
}

function parseArgs(): Args {
const program = new Command();

Expand Down Expand Up @@ -256,12 +287,24 @@ function parseArgs(): Args {
}
return value as "sse" | "http" | "stdio";
},
)
//
// HTTP headers
//
.option(
"--header <headers...>",
'HTTP headers as "HeaderName: Value" pairs (for HTTP/SSE transports)',
parseHeaderPair,
{},
);

// Parse only the arguments before --
program.parse(preArgs);

const options = program.opts() as Omit<Args, "target">;
const options = program.opts() as Omit<Args, "target"> & {
header?: Record<string, string>;
};

let remainingArgs = program.args;

// Add back any arguments that came after --
Expand All @@ -276,6 +319,7 @@ function parseArgs(): Args {
return {
target: finalArgs,
...options,
headers: options.header, // commander.js uses 'header' field, map to 'headers'
};
}

Expand All @@ -287,8 +331,9 @@ async function main(): Promise<void> {
try {
const args = parseArgs();
await callMethod(args);
// Explicitly exit to ensure process terminates in CI
process.exit(0);

// Let Node.js naturally exit instead of force-exiting
// process.exit(0) was causing stdout truncation
} catch (error) {
handleError(error);
}
Expand Down
19 changes: 17 additions & 2 deletions cli/src/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type TransportOptions = {
command?: string;
args?: string[];
url?: string;
headers?: Record<string, string>;
};

function createStdioTransport(options: TransportOptions): Transport {
Expand Down Expand Up @@ -64,11 +65,25 @@ export function createTransport(options: TransportOptions): Transport {
const url = new URL(options.url);

if (transportType === "sse") {
return new SSEClientTransport(url);
const transportOptions = options.headers
? {
requestInit: {
headers: options.headers,
},
}
: undefined;
return new SSEClientTransport(url, transportOptions);
}

if (transportType === "http") {
return new StreamableHTTPClientTransport(url);
const transportOptions = options.headers
? {
requestInit: {
headers: options.headers,
},
}
: undefined;
return new StreamableHTTPClientTransport(url, transportOptions);
}

throw new Error(`Unsupported transport type: ${transportType}`);
Expand Down