Merge pull request #661 from modelcontextprotocol/fweinberger/better-config-support
feat: Auto-detect transport type from config files
This commit is contained in:
commit
7b3e2c19e7
9 changed files with 524 additions and 28 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -14,3 +14,4 @@ sdk
|
|||
client/playwright-report/
|
||||
client/results.json
|
||||
client/test-results/
|
||||
mcp.json
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@ packages
|
|||
server/build
|
||||
CODE_OF_CONDUCT.md
|
||||
SECURITY.md
|
||||
mcp.json
|
||||
|
|
|
|||
72
README.md
72
README.md
|
|
@ -234,6 +234,78 @@ Example server configuration file:
|
|||
}
|
||||
```
|
||||
|
||||
#### Transport Types in Config Files
|
||||
|
||||
The inspector automatically detects the transport type from your config file. You can specify different transport types:
|
||||
|
||||
**STDIO (default):**
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"my-stdio-server": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["@modelcontextprotocol/server-everything"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**SSE (Server-Sent Events):**
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"my-sse-server": {
|
||||
"type": "sse",
|
||||
"url": "http://localhost:3000/sse"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Streamable HTTP:**
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"my-http-server": {
|
||||
"type": "streamable-http",
|
||||
"url": "http://localhost:3000/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Default Server Selection
|
||||
|
||||
You can launch the inspector without specifying a server name if your config has:
|
||||
|
||||
1. **A single server** - automatically selected:
|
||||
|
||||
```bash
|
||||
# Automatically uses "my-server" if it's the only one
|
||||
npx @modelcontextprotocol/inspector --config mcp.json
|
||||
```
|
||||
|
||||
2. **A server named "default-server"** - automatically selected:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"default-server": {
|
||||
"command": "npx",
|
||||
"args": ["@modelcontextprotocol/server-everything"]
|
||||
},
|
||||
"other-server": {
|
||||
"command": "node",
|
||||
"args": ["other.js"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Tip:** You can easily generate this configuration format using the **Server Entry** and **Servers File** buttons in the Inspector UI, as described in the Servers File Export section above.
|
||||
|
||||
You can also set the initial `transport` type, `serverUrl`, `serverCommand`, and `serverArgs` via query params, for example:
|
||||
|
|
|
|||
|
|
@ -120,6 +120,85 @@ try {
|
|||
const invalidConfigPath = path.join(TEMP_DIR, "invalid-config.json");
|
||||
fs.writeFileSync(invalidConfigPath, '{\n "mcpServers": {\n "invalid": {');
|
||||
|
||||
// Create config files with different transport types for testing
|
||||
const sseConfigPath = path.join(TEMP_DIR, "sse-config.json");
|
||||
fs.writeFileSync(
|
||||
sseConfigPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
mcpServers: {
|
||||
"test-sse": {
|
||||
type: "sse",
|
||||
url: "http://localhost:3000/sse",
|
||||
note: "Test SSE server",
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
|
||||
const httpConfigPath = path.join(TEMP_DIR, "http-config.json");
|
||||
fs.writeFileSync(
|
||||
httpConfigPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
mcpServers: {
|
||||
"test-http": {
|
||||
type: "streamable-http",
|
||||
url: "http://localhost:3000/mcp",
|
||||
note: "Test HTTP server",
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
|
||||
const stdioConfigPath = path.join(TEMP_DIR, "stdio-config.json");
|
||||
fs.writeFileSync(
|
||||
stdioConfigPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
mcpServers: {
|
||||
"test-stdio": {
|
||||
type: "stdio",
|
||||
command: "npx",
|
||||
args: ["@modelcontextprotocol/server-everything"],
|
||||
env: {
|
||||
TEST_ENV: "test-value",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
|
||||
// Config without type field (backward compatibility)
|
||||
const legacyConfigPath = path.join(TEMP_DIR, "legacy-config.json");
|
||||
fs.writeFileSync(
|
||||
legacyConfigPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
mcpServers: {
|
||||
"test-legacy": {
|
||||
command: "npx",
|
||||
args: ["@modelcontextprotocol/server-everything"],
|
||||
env: {
|
||||
LEGACY_ENV: "legacy-value",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
|
||||
// Function to run a basic test
|
||||
async function runBasicTest(testName, ...args) {
|
||||
const outputFile = path.join(
|
||||
|
|
@ -649,6 +728,160 @@ async function runTests() {
|
|||
"debug",
|
||||
);
|
||||
|
||||
console.log(
|
||||
`\n${colors.YELLOW}=== Running Config Transport Type Tests ===${colors.NC}`,
|
||||
);
|
||||
|
||||
// Test 25: Config with stdio transport type
|
||||
await runBasicTest(
|
||||
"config_stdio_type",
|
||||
"--config",
|
||||
stdioConfigPath,
|
||||
"--server",
|
||||
"test-stdio",
|
||||
"--cli",
|
||||
"--method",
|
||||
"tools/list",
|
||||
);
|
||||
|
||||
// Test 26: Config with SSE transport type (CLI mode) - expects connection error
|
||||
await runErrorTest(
|
||||
"config_sse_type_cli",
|
||||
"--config",
|
||||
sseConfigPath,
|
||||
"--server",
|
||||
"test-sse",
|
||||
"--cli",
|
||||
"--method",
|
||||
"tools/list",
|
||||
);
|
||||
|
||||
// Test 27: Config with streamable-http transport type (CLI mode) - expects connection error
|
||||
await runErrorTest(
|
||||
"config_http_type_cli",
|
||||
"--config",
|
||||
httpConfigPath,
|
||||
"--server",
|
||||
"test-http",
|
||||
"--cli",
|
||||
"--method",
|
||||
"tools/list",
|
||||
);
|
||||
|
||||
// Test 28: Legacy config without type field (backward compatibility)
|
||||
await runBasicTest(
|
||||
"config_legacy_no_type",
|
||||
"--config",
|
||||
legacyConfigPath,
|
||||
"--server",
|
||||
"test-legacy",
|
||||
"--cli",
|
||||
"--method",
|
||||
"tools/list",
|
||||
);
|
||||
|
||||
console.log(
|
||||
`\n${colors.YELLOW}=== Running Default Server Tests ===${colors.NC}`,
|
||||
);
|
||||
|
||||
// Create config with single server for auto-selection
|
||||
const singleServerConfigPath = path.join(
|
||||
TEMP_DIR,
|
||||
"single-server-config.json",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
singleServerConfigPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
mcpServers: {
|
||||
"only-server": {
|
||||
command: "npx",
|
||||
args: ["@modelcontextprotocol/server-everything"],
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
|
||||
// Create config with default-server
|
||||
const defaultServerConfigPath = path.join(
|
||||
TEMP_DIR,
|
||||
"default-server-config.json",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
defaultServerConfigPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
mcpServers: {
|
||||
"default-server": {
|
||||
command: "npx",
|
||||
args: ["@modelcontextprotocol/server-everything"],
|
||||
},
|
||||
"other-server": {
|
||||
command: "node",
|
||||
args: ["other.js"],
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
|
||||
// Create config with multiple servers (no default)
|
||||
const multiServerConfigPath = path.join(TEMP_DIR, "multi-server-config.json");
|
||||
fs.writeFileSync(
|
||||
multiServerConfigPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
mcpServers: {
|
||||
server1: {
|
||||
command: "npx",
|
||||
args: ["@modelcontextprotocol/server-everything"],
|
||||
},
|
||||
server2: {
|
||||
command: "node",
|
||||
args: ["other.js"],
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
|
||||
// Test 29: Config with single server auto-selection
|
||||
await runBasicTest(
|
||||
"single_server_auto_select",
|
||||
"--config",
|
||||
singleServerConfigPath,
|
||||
"--cli",
|
||||
"--method",
|
||||
"tools/list",
|
||||
);
|
||||
|
||||
// Test 30: Config with default-server should now require explicit selection (multiple servers)
|
||||
await runErrorTest(
|
||||
"default_server_requires_explicit_selection",
|
||||
"--config",
|
||||
defaultServerConfigPath,
|
||||
"--cli",
|
||||
"--method",
|
||||
"tools/list",
|
||||
);
|
||||
|
||||
// Test 31: Config with multiple servers and no default (should fail)
|
||||
await runErrorTest(
|
||||
"multi_server_no_default",
|
||||
"--config",
|
||||
multiServerConfigPath,
|
||||
"--cli",
|
||||
"--method",
|
||||
"tools/list",
|
||||
);
|
||||
|
||||
console.log(
|
||||
`\n${colors.YELLOW}=== Running HTTP Transport Tests ===${colors.NC}`,
|
||||
);
|
||||
|
|
@ -668,7 +901,7 @@ async function runTests() {
|
|||
|
||||
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||
|
||||
// Test 25: HTTP transport inferred from URL ending with /mcp
|
||||
// Test 32: HTTP transport inferred from URL ending with /mcp
|
||||
await runBasicTest(
|
||||
"http_transport_inferred",
|
||||
"http://127.0.0.1:3001/mcp",
|
||||
|
|
@ -677,7 +910,7 @@ async function runTests() {
|
|||
"tools/list",
|
||||
);
|
||||
|
||||
// Test 26: HTTP transport with explicit --transport http flag
|
||||
// Test 33: HTTP transport with explicit --transport http flag
|
||||
await runBasicTest(
|
||||
"http_transport_with_explicit_flag",
|
||||
"http://127.0.0.1:3001/mcp",
|
||||
|
|
@ -688,7 +921,7 @@ async function runTests() {
|
|||
"tools/list",
|
||||
);
|
||||
|
||||
// Test 27: HTTP transport with suffix and --transport http flag
|
||||
// Test 34: HTTP transport with suffix and --transport http flag
|
||||
await runBasicTest(
|
||||
"http_transport_with_explicit_flag_and_suffix",
|
||||
"http://127.0.0.1:3001/mcp",
|
||||
|
|
@ -699,7 +932,7 @@ async function runTests() {
|
|||
"tools/list",
|
||||
);
|
||||
|
||||
// Test 28: SSE transport given to HTTP server (should fail)
|
||||
// Test 35: SSE transport given to HTTP server (should fail)
|
||||
await runErrorTest(
|
||||
"sse_transport_given_to_http_server",
|
||||
"http://127.0.0.1:3001",
|
||||
|
|
@ -710,7 +943,7 @@ async function runTests() {
|
|||
"tools/list",
|
||||
);
|
||||
|
||||
// Test 29: HTTP transport without URL (should fail)
|
||||
// Test 36: HTTP transport without URL (should fail)
|
||||
await runErrorTest(
|
||||
"http_transport_without_url",
|
||||
"--transport",
|
||||
|
|
@ -720,7 +953,7 @@ async function runTests() {
|
|||
"tools/list",
|
||||
);
|
||||
|
||||
// Test 30: SSE transport without URL (should fail)
|
||||
// Test 37: SSE transport without URL (should fail)
|
||||
await runErrorTest(
|
||||
"sse_transport_without_url",
|
||||
"--transport",
|
||||
|
|
|
|||
125
cli/src/cli.ts
125
cli/src/cli.ts
|
|
@ -14,6 +14,8 @@ type Args = {
|
|||
args: string[];
|
||||
envArgs: Record<string, string>;
|
||||
cli: boolean;
|
||||
transport?: "stdio" | "sse" | "streamable-http";
|
||||
serverUrl?: string;
|
||||
};
|
||||
|
||||
type CliOptions = {
|
||||
|
|
@ -21,13 +23,22 @@ type CliOptions = {
|
|||
config?: string;
|
||||
server?: string;
|
||||
cli?: boolean;
|
||||
transport?: string;
|
||||
serverUrl?: string;
|
||||
};
|
||||
|
||||
type ServerConfig = {
|
||||
command: string;
|
||||
args?: string[];
|
||||
env?: Record<string, string>;
|
||||
};
|
||||
type ServerConfig =
|
||||
| {
|
||||
type: "stdio";
|
||||
command: string;
|
||||
args?: string[];
|
||||
env?: Record<string, string>;
|
||||
}
|
||||
| {
|
||||
type: "sse" | "streamable-http";
|
||||
url: string;
|
||||
note?: string;
|
||||
};
|
||||
|
||||
function handleError(error: unknown): never {
|
||||
let message: string;
|
||||
|
|
@ -74,6 +85,16 @@ async function runWebClient(args: Args): Promise<void> {
|
|||
startArgs.push("-e", `${key}=${value}`);
|
||||
}
|
||||
|
||||
// Pass transport type if specified
|
||||
if (args.transport) {
|
||||
startArgs.push("--transport", args.transport);
|
||||
}
|
||||
|
||||
// Pass server URL if specified
|
||||
if (args.serverUrl) {
|
||||
startArgs.push("--server-url", args.serverUrl);
|
||||
}
|
||||
|
||||
// Pass command and args (using -- to separate them)
|
||||
if (args.command) {
|
||||
startArgs.push("--", args.command, ...args.args);
|
||||
|
|
@ -103,7 +124,21 @@ async function runCli(args: Args): Promise<void> {
|
|||
});
|
||||
|
||||
try {
|
||||
await spawnPromise("node", [cliPath, args.command, ...args.args], {
|
||||
// Build CLI arguments
|
||||
const cliArgs = [cliPath];
|
||||
|
||||
// Add transport flag if specified
|
||||
if (args.transport && args.transport !== "stdio") {
|
||||
// Convert streamable-http back to http for CLI mode
|
||||
const cliTransport =
|
||||
args.transport === "streamable-http" ? "http" : args.transport;
|
||||
cliArgs.push("--transport", cliTransport);
|
||||
}
|
||||
|
||||
// Add command and remaining args
|
||||
cliArgs.push(args.command, ...args.args);
|
||||
|
||||
await spawnPromise("node", cliArgs, {
|
||||
env: { ...process.env, ...args.envArgs },
|
||||
signal: abort.signal,
|
||||
echoOutput: true,
|
||||
|
|
@ -190,7 +225,9 @@ function parseArgs(): Args {
|
|||
)
|
||||
.option("--config <path>", "config file path")
|
||||
.option("--server <n>", "server name from config file")
|
||||
.option("--cli", "enable CLI mode");
|
||||
.option("--cli", "enable CLI mode")
|
||||
.option("--transport <type>", "transport type (stdio, sse, http)")
|
||||
.option("--server-url <url>", "server URL for SSE/HTTP transport");
|
||||
|
||||
// Parse only the arguments before --
|
||||
program.parse(preArgs);
|
||||
|
|
@ -201,14 +238,33 @@ function parseArgs(): Args {
|
|||
// Add back any arguments that came after --
|
||||
const finalArgs = [...remainingArgs, ...postArgs];
|
||||
|
||||
// Validate that config and server are provided together
|
||||
if (
|
||||
(options.config && !options.server) ||
|
||||
(!options.config && options.server)
|
||||
) {
|
||||
throw new Error(
|
||||
"Both --config and --server must be provided together. If you specify one, you must specify the other.",
|
||||
// Validate config and server options
|
||||
if (!options.config && options.server) {
|
||||
throw new Error("--server requires --config to be specified");
|
||||
}
|
||||
|
||||
// If config is provided without server, try to auto-select
|
||||
if (options.config && !options.server) {
|
||||
const configContent = fs.readFileSync(
|
||||
path.isAbsolute(options.config)
|
||||
? options.config
|
||||
: path.resolve(process.cwd(), options.config),
|
||||
"utf8",
|
||||
);
|
||||
const parsedConfig = JSON.parse(configContent);
|
||||
const servers = Object.keys(parsedConfig.mcpServers || {});
|
||||
|
||||
if (servers.length === 1) {
|
||||
// Use the only server if there's just one
|
||||
options.server = servers[0];
|
||||
} else if (servers.length === 0) {
|
||||
throw new Error("No servers found in config file");
|
||||
} else {
|
||||
// Multiple servers, require explicit selection
|
||||
throw new Error(
|
||||
`Multiple servers found in config file. Please specify one with --server.\nAvailable servers: ${servers.join(", ")}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// If config file is specified, load and use the options from the file. We must merge the args
|
||||
|
|
@ -217,23 +273,52 @@ function parseArgs(): Args {
|
|||
if (options.config && options.server) {
|
||||
const config = loadConfigFile(options.config, options.server);
|
||||
|
||||
return {
|
||||
command: config.command,
|
||||
args: [...(config.args || []), ...finalArgs],
|
||||
envArgs: { ...(config.env || {}), ...(options.e || {}) },
|
||||
cli: options.cli || false,
|
||||
};
|
||||
if (config.type === "stdio") {
|
||||
return {
|
||||
command: config.command,
|
||||
args: [...(config.args || []), ...finalArgs],
|
||||
envArgs: { ...(config.env || {}), ...(options.e || {}) },
|
||||
cli: options.cli || false,
|
||||
transport: "stdio",
|
||||
};
|
||||
} else if (config.type === "sse" || config.type === "streamable-http") {
|
||||
return {
|
||||
command: config.url,
|
||||
args: finalArgs,
|
||||
envArgs: options.e || {},
|
||||
cli: options.cli || false,
|
||||
transport: config.type,
|
||||
serverUrl: config.url,
|
||||
};
|
||||
} else {
|
||||
// Backwards compatibility: if no type field, assume stdio
|
||||
return {
|
||||
command: (config as any).command || "",
|
||||
args: [...((config as any).args || []), ...finalArgs],
|
||||
envArgs: { ...((config as any).env || {}), ...(options.e || {}) },
|
||||
cli: options.cli || false,
|
||||
transport: "stdio",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise use command line arguments
|
||||
const command = finalArgs[0] || "";
|
||||
const args = finalArgs.slice(1);
|
||||
|
||||
// Map "http" shorthand to "streamable-http"
|
||||
let transport = options.transport;
|
||||
if (transport === "http") {
|
||||
transport = "streamable-http";
|
||||
}
|
||||
|
||||
return {
|
||||
command,
|
||||
args,
|
||||
envArgs: options.e || {},
|
||||
cli: options.cli || false,
|
||||
transport: transport as "stdio" | "sse" | "streamable-http" | undefined,
|
||||
serverUrl: options.serverUrl,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,15 @@ function getClientUrl(port, authDisabled, sessionToken, serverPort) {
|
|||
}
|
||||
|
||||
async function startDevServer(serverOptions) {
|
||||
const { SERVER_PORT, CLIENT_PORT, sessionToken, envVars, abort } =
|
||||
serverOptions;
|
||||
const {
|
||||
SERVER_PORT,
|
||||
CLIENT_PORT,
|
||||
sessionToken,
|
||||
envVars,
|
||||
abort,
|
||||
transport,
|
||||
serverUrl,
|
||||
} = serverOptions;
|
||||
const serverCommand = "npx";
|
||||
const serverArgs = ["tsx", "watch", "--clear-screen=false", "src/index.ts"];
|
||||
const isWindows = process.platform === "win32";
|
||||
|
|
@ -42,6 +49,8 @@ async function startDevServer(serverOptions) {
|
|||
CLIENT_PORT,
|
||||
MCP_PROXY_AUTH_TOKEN: sessionToken,
|
||||
MCP_ENV_VARS: JSON.stringify(envVars),
|
||||
...(transport ? { MCP_TRANSPORT: transport } : {}),
|
||||
...(serverUrl ? { MCP_SERVER_URL: serverUrl } : {}),
|
||||
},
|
||||
signal: abort.signal,
|
||||
echoOutput: true,
|
||||
|
|
@ -78,6 +87,8 @@ async function startProdServer(serverOptions) {
|
|||
abort,
|
||||
command,
|
||||
mcpServerArgs,
|
||||
transport,
|
||||
serverUrl,
|
||||
} = serverOptions;
|
||||
const inspectorServerPath = resolve(
|
||||
__dirname,
|
||||
|
|
@ -95,6 +106,8 @@ async function startProdServer(serverOptions) {
|
|||
...(mcpServerArgs && mcpServerArgs.length > 0
|
||||
? [`--args=${mcpServerArgs.join(" ")}`]
|
||||
: []),
|
||||
...(transport ? [`--transport=${transport}`] : []),
|
||||
...(serverUrl ? [`--server-url=${serverUrl}`] : []),
|
||||
],
|
||||
{
|
||||
env: {
|
||||
|
|
@ -208,6 +221,8 @@ async function main() {
|
|||
let command = null;
|
||||
let parsingFlags = true;
|
||||
let isDev = false;
|
||||
let transport = null;
|
||||
let serverUrl = null;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
|
|
@ -222,6 +237,16 @@ async function main() {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (parsingFlags && arg === "--transport" && i + 1 < args.length) {
|
||||
transport = args[++i];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parsingFlags && arg === "--server-url" && i + 1 < args.length) {
|
||||
serverUrl = args[++i];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parsingFlags && arg === "-e" && i + 1 < args.length) {
|
||||
const envVar = args[++i];
|
||||
const equalsIndex = envVar.indexOf("=");
|
||||
|
|
@ -273,6 +298,8 @@ async function main() {
|
|||
abort,
|
||||
command,
|
||||
mcpServerArgs,
|
||||
transport,
|
||||
serverUrl,
|
||||
};
|
||||
|
||||
const result = isDev
|
||||
|
|
|
|||
65
client/e2e/cli-arguments.spec.ts
Normal file
65
client/e2e/cli-arguments.spec.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import { test, expect } from "@playwright/test";
|
||||
|
||||
// These tests verify that CLI arguments correctly set URL parameters
|
||||
// The CLI should parse config files and pass transport/serverUrl as URL params
|
||||
test.describe("CLI Arguments @cli", () => {
|
||||
test("should pass transport parameter from command line", async ({
|
||||
page,
|
||||
}) => {
|
||||
// Simulate: npx . --transport sse --server-url http://localhost:3000/sse
|
||||
await page.goto(
|
||||
"http://localhost:6274/?transport=sse&serverUrl=http://localhost:3000/sse",
|
||||
);
|
||||
|
||||
// Wait for the Transport Type dropdown to be visible
|
||||
const selectTrigger = page.getByLabel("Transport Type");
|
||||
await expect(selectTrigger).toBeVisible();
|
||||
|
||||
// Verify transport dropdown shows SSE
|
||||
await expect(selectTrigger).toContainText("SSE");
|
||||
|
||||
// Verify URL field is visible and populated
|
||||
const urlInput = page.locator("#sse-url-input");
|
||||
await expect(urlInput).toBeVisible();
|
||||
await expect(urlInput).toHaveValue("http://localhost:3000/sse");
|
||||
});
|
||||
|
||||
test("should pass transport parameter for streamable-http", async ({
|
||||
page,
|
||||
}) => {
|
||||
// Simulate config with streamable-http transport
|
||||
await page.goto(
|
||||
"http://localhost:6274/?transport=streamable-http&serverUrl=http://localhost:3000/mcp",
|
||||
);
|
||||
|
||||
// Wait for the Transport Type dropdown to be visible
|
||||
const selectTrigger = page.getByLabel("Transport Type");
|
||||
await expect(selectTrigger).toBeVisible();
|
||||
|
||||
// Verify transport dropdown shows Streamable HTTP
|
||||
await expect(selectTrigger).toContainText("Streamable HTTP");
|
||||
|
||||
// Verify URL field is visible and populated
|
||||
const urlInput = page.locator("#sse-url-input");
|
||||
await expect(urlInput).toBeVisible();
|
||||
await expect(urlInput).toHaveValue("http://localhost:3000/mcp");
|
||||
});
|
||||
|
||||
test("should not pass transport parameter for stdio config", async ({
|
||||
page,
|
||||
}) => {
|
||||
// Simulate stdio config (no transport param needed)
|
||||
await page.goto("http://localhost:6274/");
|
||||
|
||||
// Wait for the Transport Type dropdown to be visible
|
||||
const selectTrigger = page.getByLabel("Transport Type");
|
||||
await expect(selectTrigger).toBeVisible();
|
||||
|
||||
// Verify transport dropdown defaults to STDIO
|
||||
await expect(selectTrigger).toContainText("STDIO");
|
||||
|
||||
// Verify command/args fields are visible
|
||||
await expect(page.locator("#command-input")).toBeVisible();
|
||||
await expect(page.locator("#arguments-input")).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
|
@ -455,6 +455,14 @@ const App = () => {
|
|||
if (data.defaultArgs) {
|
||||
setArgs(data.defaultArgs);
|
||||
}
|
||||
if (data.defaultTransport) {
|
||||
setTransportType(
|
||||
data.defaultTransport as "stdio" | "sse" | "streamable-http",
|
||||
);
|
||||
}
|
||||
if (data.defaultServerUrl) {
|
||||
setSseUrl(data.defaultServerUrl);
|
||||
}
|
||||
})
|
||||
.catch((error) =>
|
||||
console.error("Error fetching default environment:", error),
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ const { values } = parseArgs({
|
|||
env: { type: "string", default: "" },
|
||||
args: { type: "string", default: "" },
|
||||
command: { type: "string", default: "" },
|
||||
transport: { type: "string", default: "" },
|
||||
"server-url": { type: "string", default: "" },
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -523,6 +525,8 @@ app.get("/config", originValidationMiddleware, authMiddleware, (req, res) => {
|
|||
defaultEnvironment,
|
||||
defaultCommand: values.command,
|
||||
defaultArgs: values.args,
|
||||
defaultTransport: values.transport,
|
||||
defaultServerUrl: values["server-url"],
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error in /config route:", error);
|
||||
|
|
|
|||
Loading…
Reference in a new issue