Skip to content

Commit 0157b13

Browse files
feat: Add non-interactive MCP server listing commands
Add comprehensive CLI commands for listing MCP server capabilities without interactive mode: - list-tools: List all available tools with descriptions - list-resources: List static resources and resource templates - list-prompts: List available prompts with arguments - list-all: Comprehensive overview of all server capabilities Features: - Multiple server types: config stdio, remote HTTP/SSE, URL servers in config - Dual output formats: human-readable with colors + JSON (--json flag) - OAuth authentication support for remote servers - Consistent data structure across all commands - Performance optimized with concurrent Promise.all API calls Architecture: - Type-specific formatters (formatListTools, formatListPrompts, formatListResources) - Compositional design with formatListAll reusing existing formatters - Single unified data format: {capabilities, tools, prompts, resources, resourceTemplates} - Zero code duplication with helper functions like shouldInclude - Clean separation of data fetching, filtering, and formatting Maintains full backward compatibility - no breaking changes to existing CLI interface. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 3562966 commit 0157b13

File tree

2 files changed

+354
-48
lines changed

2 files changed

+354
-48
lines changed

src/cli.js

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import meow from 'meow'
44
import './eventsource-polyfill.js'
5-
import { runWithCommand, runWithConfig, runWithConfigNonInteractive, runWithSSE, runWithURL } from './mcp.js'
5+
import { runWithCommand, runWithConfig, runWithConfigNonInteractive, runWithSSE, runWithURL, runListCommand, LIST_COMMANDS } from './mcp.js'
66
import { purge } from './config.js'
77

88
const cli = meow(
@@ -18,6 +18,10 @@ const cli = meow(
1818
$ mcp-cli [--config config.json] call-tool <server_name>:<tool_name> [--args '{"key":"value"}']
1919
$ mcp-cli [--config config.json] read-resource <server_name>:<resource_uri>
2020
$ mcp-cli [--config config.json] get-prompt <server_name>:<prompt_name> [--args '{"key":"value"}']
21+
$ mcp-cli [--config config.json] list-tools <server_name>
22+
$ mcp-cli [--config config.json] list-resources <server_name>
23+
$ mcp-cli [--config config.json] list-prompts <server_name>
24+
$ mcp-cli [--config config.json] list-all <server_name>
2125
2226
Options
2327
--config, -c Path to the config file
@@ -26,6 +30,7 @@ const cli = meow(
2630
--url Streamable HTTP endpoint
2731
--sse SSE endpoint
2832
--args JSON arguments for tools and prompts (non-interactive mode)
33+
--json Output results in JSON format (for list commands)
2934
`,
3035
{
3136
importMeta: import.meta,
@@ -45,14 +50,25 @@ const cli = meow(
4550
args: {
4651
type: 'string',
4752
},
53+
json: {
54+
type: 'boolean',
55+
},
4856
},
4957
},
5058
)
5159

52-
const options = { compact: cli.flags.compact }
60+
const options = { compact: cli.flags.compact, json: cli.flags.json }
61+
62+
function isListCommand(command) {
63+
return command in LIST_COMMANDS
64+
}
5365

5466
if (cli.input[0] === 'purge') {
5567
purge()
68+
} else if (cli.input.length >= 2 && isListCommand(cli.input[0])) {
69+
// Non-interactive list mode: mcp-cli [--config config.json] <list-command> <server-name>
70+
const [command, serverName] = cli.input
71+
await runListCommand(cli.flags.config, serverName, command, options)
5672
} else if (
5773
cli.input.length >= 2 &&
5874
(cli.input[0] === 'call-tool' || cli.input[0] === 'read-resource' || cli.input[0] === 'get-prompt')
@@ -61,13 +77,19 @@ if (cli.input[0] === 'purge') {
6177
const [command, serverTarget] = cli.input
6278
const [serverName, target] = serverTarget.split(':')
6379
await runWithConfigNonInteractive(cli.flags.config, serverName, command, target, cli.flags.args)
80+
} else if (cli.flags.url || cli.flags.sse) {
81+
const endpoint = cli.flags.url || cli.flags.sse
82+
const transportType = cli.flags.url ? 'url' : 'sse'
83+
84+
if (cli.input.length >= 1 && isListCommand(cli.input[0])) {
85+
await runListCommand(null, null, cli.input[0], { ...options, [transportType]: endpoint })
86+
} else {
87+
const runner = cli.flags.url ? runWithURL : runWithSSE
88+
await runner(endpoint, options)
89+
}
6490
} else if (cli.input.length > 0) {
6591
const [command, ...args] = cli.input
6692
await runWithCommand(command, args, cli.flags.passEnv ? process.env : undefined, options)
67-
} else if (cli.flags.url) {
68-
await runWithURL(cli.flags.url, options)
69-
} else if (cli.flags.sse) {
70-
await runWithSSE(cli.flags.sse, options)
7193
} else {
7294
await runWithConfig(cli.flags.config, options)
7395
}

0 commit comments

Comments
 (0)