Implement default-server support

- Auto-select server when --config is used without --server
- Use 'default-server' if it exists in config
- Use single server if only one is defined
- Show helpful error if multiple servers exist without default
- Add tests for all default-server scenarios
This commit is contained in:
Felix Weinberger 2025-08-05 13:35:28 +01:00
commit 0e2906c4c4
No known key found for this signature in database
2 changed files with 132 additions and 12 deletions

View file

@ -780,6 +780,102 @@ async function runTests() {
"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 auto-selection
await runBasicTest(
"default_server_auto_select",
"--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}`,
);
@ -799,7 +895,7 @@ async function runTests() {
await new Promise((resolve) => setTimeout(resolve, 3000));
// Test 29: 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",
@ -808,7 +904,7 @@ async function runTests() {
"tools/list",
);
// Test 30: 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",
@ -819,7 +915,7 @@ async function runTests() {
"tools/list",
);
// Test 31: 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",
@ -830,7 +926,7 @@ async function runTests() {
"tools/list",
);
// Test 32: 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",
@ -841,7 +937,7 @@ async function runTests() {
"tools/list",
);
// Test 33: HTTP transport without URL (should fail)
// Test 36: HTTP transport without URL (should fail)
await runErrorTest(
"http_transport_without_url",
"--transport",
@ -851,7 +947,7 @@ async function runTests() {
"tools/list",
);
// Test 34: SSE transport without URL (should fail)
// Test 37: SSE transport without URL (should fail)
await runErrorTest(
"sse_transport_without_url",
"--transport",

View file

@ -220,16 +220,40 @@ 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)
) {
// Validate config and server options
if (!options.config && options.server) {
throw new Error(
"Both --config and --server must be provided together. If you specify one, you must specify the other.",
"--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.includes("default-server")) {
// Use default-server if it exists
options.server = "default-server";
} else 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, no default-server
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
// from the command line and the file together, or we will miss the method options (--method,
// etc.)