@apify/mcpc 0.2.4 → 0.3.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -1
- package/CONTRIBUTING.md +12 -0
- package/NOTICE +27 -0
- package/README.md +219 -226
- package/_config.yml +30 -0
- package/client-logo.svg +79 -0
- package/client-metadata.json +16 -0
- package/dist/bridge/index.js +51 -4
- package/dist/bridge/index.js.map +1 -1
- package/dist/cli/commands/auth.d.ts +2 -0
- package/dist/cli/commands/auth.d.ts.map +1 -1
- package/dist/cli/commands/auth.js +32 -10
- package/dist/cli/commands/auth.js.map +1 -1
- package/dist/cli/commands/clean.d.ts.map +1 -1
- package/dist/cli/commands/clean.js +13 -2
- package/dist/cli/commands/clean.js.map +1 -1
- package/dist/cli/commands/grep.d.ts.map +1 -1
- package/dist/cli/commands/grep.js +39 -8
- package/dist/cli/commands/grep.js.map +1 -1
- package/dist/cli/commands/prompts.d.ts.map +1 -1
- package/dist/cli/commands/prompts.js +7 -26
- package/dist/cli/commands/prompts.js.map +1 -1
- package/dist/cli/commands/resources.d.ts.map +1 -1
- package/dist/cli/commands/resources.js +9 -3
- package/dist/cli/commands/resources.js.map +1 -1
- package/dist/cli/commands/sessions.d.ts +45 -2
- package/dist/cli/commands/sessions.d.ts.map +1 -1
- package/dist/cli/commands/sessions.js +493 -27
- package/dist/cli/commands/sessions.js.map +1 -1
- package/dist/cli/commands/tasks.d.ts +1 -0
- package/dist/cli/commands/tasks.d.ts.map +1 -1
- package/dist/cli/commands/tasks.js +15 -1
- package/dist/cli/commands/tasks.js.map +1 -1
- package/dist/cli/commands/tools.d.ts +6 -1
- package/dist/cli/commands/tools.d.ts.map +1 -1
- package/dist/cli/commands/tools.js +66 -14
- package/dist/cli/commands/tools.js.map +1 -1
- package/dist/cli/commands/x402.d.ts.map +1 -1
- package/dist/cli/commands/x402.js +7 -7
- package/dist/cli/commands/x402.js.map +1 -1
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/helpers.js +3 -6
- package/dist/cli/helpers.js.map +1 -1
- package/dist/cli/index.js +370 -131
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/output.d.ts +18 -5
- package/dist/cli/output.d.ts.map +1 -1
- package/dist/cli/output.js +275 -89
- package/dist/cli/output.js.map +1 -1
- package/dist/cli/parser.d.ts +4 -0
- package/dist/cli/parser.d.ts.map +1 -1
- package/dist/cli/parser.js +68 -24
- package/dist/cli/parser.js.map +1 -1
- package/dist/cli/shell.d.ts.map +1 -1
- package/dist/cli/shell.js +44 -21
- package/dist/cli/shell.js.map +1 -1
- package/dist/cli/tool-result.d.ts +1 -1
- package/dist/cli/tool-result.d.ts.map +1 -1
- package/dist/cli/tool-result.js +20 -15
- package/dist/cli/tool-result.js.map +1 -1
- package/dist/core/factory.d.ts +1 -0
- package/dist/core/factory.d.ts.map +1 -1
- package/dist/core/factory.js +3 -0
- package/dist/core/factory.js.map +1 -1
- package/dist/core/mcp-client.d.ts +1 -0
- package/dist/core/mcp-client.d.ts.map +1 -1
- package/dist/core/mcp-client.js +14 -0
- package/dist/core/mcp-client.js.map +1 -1
- package/dist/core/transports.d.ts +5 -1
- package/dist/core/transports.d.ts.map +1 -1
- package/dist/core/transports.js +26 -4
- package/dist/core/transports.js.map +1 -1
- package/dist/lib/auth/auth-page.d.ts +13 -0
- package/dist/lib/auth/auth-page.d.ts.map +1 -0
- package/dist/lib/auth/auth-page.js +129 -0
- package/dist/lib/auth/auth-page.js.map +1 -0
- package/dist/lib/auth/oauth-flow.d.ts +2 -1
- package/dist/lib/auth/oauth-flow.d.ts.map +1 -1
- package/dist/lib/auth/oauth-flow.js +65 -58
- package/dist/lib/auth/oauth-flow.js.map +1 -1
- package/dist/lib/auth/oauth-provider.d.ts +2 -0
- package/dist/lib/auth/oauth-provider.d.ts.map +1 -1
- package/dist/lib/auth/oauth-provider.js +6 -0
- package/dist/lib/auth/oauth-provider.js.map +1 -1
- package/dist/lib/auth/oauth-utils.d.ts +3 -0
- package/dist/lib/auth/oauth-utils.d.ts.map +1 -1
- package/dist/lib/auth/oauth-utils.js +32 -1
- package/dist/lib/auth/oauth-utils.js.map +1 -1
- package/dist/lib/auth/profiles.d.ts.map +1 -1
- package/dist/lib/auth/profiles.js +3 -3
- package/dist/lib/auth/profiles.js.map +1 -1
- package/dist/lib/bridge-manager.d.ts.map +1 -1
- package/dist/lib/bridge-manager.js +43 -28
- package/dist/lib/bridge-manager.js.map +1 -1
- package/dist/lib/cleanup.d.ts +5 -0
- package/dist/lib/cleanup.d.ts.map +1 -1
- package/dist/lib/cleanup.js +38 -1
- package/dist/lib/cleanup.js.map +1 -1
- package/dist/lib/config.d.ts +21 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +99 -5
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/errors.d.ts +1 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +4 -1
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/session-client.d.ts +1 -0
- package/dist/lib/session-client.d.ts.map +1 -1
- package/dist/lib/session-client.js +7 -4
- package/dist/lib/session-client.js.map +1 -1
- package/dist/lib/sessions.d.ts.map +1 -1
- package/dist/lib/sessions.js +18 -9
- package/dist/lib/sessions.js.map +1 -1
- package/dist/lib/types.d.ts +2 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/utils.d.ts +16 -2
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +112 -8
- package/dist/lib/utils.js.map +1 -1
- package/dist/lib/wallets.js +3 -3
- package/dist/lib/wallets.js.map +1 -1
- package/docs/TODOs.md +5 -0
- package/package.json +7 -6
package/dist/cli/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { initProxy } from '../lib/proxy.js';
|
|
3
|
-
import { Command, Help } from 'commander';
|
|
3
|
+
import { Command, CommanderError, Help } from 'commander';
|
|
4
4
|
import { setVerbose, setJsonMode, closeFileLogger } from '../lib/index.js';
|
|
5
5
|
import { isMcpError, formatHumanError, ClientError } from '../lib/index.js';
|
|
6
6
|
import chalk from 'chalk';
|
|
7
|
-
import { formatJson, formatJsonError, rainbow } from './output.js';
|
|
7
|
+
import { formatJson, formatJsonError, rainbow, theme } from './output.js';
|
|
8
8
|
import * as tools from './commands/tools.js';
|
|
9
9
|
import * as resources from './commands/resources.js';
|
|
10
10
|
import * as prompts from './commands/prompts.js';
|
|
@@ -16,7 +16,7 @@ import * as tasks from './commands/tasks.js';
|
|
|
16
16
|
import * as grepCmd from './commands/grep.js';
|
|
17
17
|
import { handleX402Command } from './commands/x402.js';
|
|
18
18
|
import { clean } from './commands/clean.js';
|
|
19
|
-
import { extractOptions, getVerboseFromEnv, getJsonFromEnv, validateOptions, validateArgValues, parseServerArg, hasSubcommand, optionTakesValue, KNOWN_COMMANDS, KNOWN_SESSION_COMMANDS, } from './parser.js';
|
|
19
|
+
import { extractOptions, getVerboseFromEnv, getJsonFromEnv, validateOptions, validateArgValues, parseServerArg, hasSubcommand, optionTakesValue, suggestCommand, KNOWN_COMMANDS, KNOWN_SESSION_COMMANDS, } from './parser.js';
|
|
20
20
|
import { createRequire } from 'module';
|
|
21
21
|
const { version: mcpcVersion } = createRequire(import.meta.url)('../../package.json');
|
|
22
22
|
{
|
|
@@ -64,8 +64,21 @@ function getOptionsFromCommand(command) {
|
|
|
64
64
|
}
|
|
65
65
|
if (opts.full)
|
|
66
66
|
options.full = opts.full;
|
|
67
|
+
if (opts.maxChars) {
|
|
68
|
+
const maxChars = parseInt(opts.maxChars, 10);
|
|
69
|
+
if (isNaN(maxChars) || maxChars <= 0) {
|
|
70
|
+
throw new Error(`Invalid --max-chars value: "${opts.maxChars}". Must be a positive number (characters).`);
|
|
71
|
+
}
|
|
72
|
+
options.maxChars = maxChars;
|
|
73
|
+
}
|
|
67
74
|
return options;
|
|
68
75
|
}
|
|
76
|
+
function jsonHelp(description, shape, schemaUrl) {
|
|
77
|
+
const line = shape ? ` ${description}:\n ${shape}` : ` ${description}`;
|
|
78
|
+
const link = schemaUrl ? `\n Schema: ${schemaUrl}` : '';
|
|
79
|
+
return `\n${chalk.bold('JSON output (--json):')}\n${line}${link}\n`;
|
|
80
|
+
}
|
|
81
|
+
const SCHEMA_BASE = 'https://modelcontextprotocol.io/specification/2025-11-25/schema';
|
|
69
82
|
async function main() {
|
|
70
83
|
const args = process.argv.slice(2);
|
|
71
84
|
const handleExit = () => {
|
|
@@ -90,23 +103,33 @@ async function main() {
|
|
|
90
103
|
return;
|
|
91
104
|
}
|
|
92
105
|
if (args.includes('--help') || args.includes('-h')) {
|
|
93
|
-
|
|
106
|
+
const hasSessionArg = args.some((a) => a.startsWith('@') && !a.startsWith('--'));
|
|
107
|
+
if (hasSessionArg) {
|
|
108
|
+
}
|
|
109
|
+
else if (args.includes('x402')) {
|
|
94
110
|
const x402Index = args.indexOf('x402');
|
|
95
111
|
const x402Args = args.slice(x402Index + 1);
|
|
96
112
|
await handleX402Command(x402Args);
|
|
97
113
|
await closeFileLogger();
|
|
98
114
|
return;
|
|
99
115
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
116
|
+
else {
|
|
117
|
+
const helpTarget = args.find((a) => a !== '--help' && a !== '-h' && !a.startsWith('-') && !a.startsWith('@'));
|
|
118
|
+
if (helpTarget && KNOWN_SESSION_COMMANDS.includes(helpTarget)) {
|
|
119
|
+
showSessionCommandHelp(helpTarget);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const program = createTopLevelProgram();
|
|
123
|
+
await program.parseAsync(process.argv);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
103
126
|
}
|
|
104
127
|
try {
|
|
105
128
|
validateOptions(args);
|
|
106
129
|
validateArgValues(args);
|
|
107
130
|
}
|
|
108
131
|
catch (error) {
|
|
109
|
-
console.error(
|
|
132
|
+
console.error(theme.red(formatHumanError(error, false)));
|
|
110
133
|
process.exit(1);
|
|
111
134
|
}
|
|
112
135
|
let firstNonOption;
|
|
@@ -154,7 +177,7 @@ async function main() {
|
|
|
154
177
|
console.error(formatJsonError(error, error.code));
|
|
155
178
|
}
|
|
156
179
|
else {
|
|
157
|
-
console.error(
|
|
180
|
+
console.error(theme.red(formatHumanError(error, opts.verbose)));
|
|
158
181
|
}
|
|
159
182
|
process.exit(error.code);
|
|
160
183
|
}
|
|
@@ -185,7 +208,7 @@ async function main() {
|
|
|
185
208
|
console.error(formatJsonError(error, error.code));
|
|
186
209
|
}
|
|
187
210
|
else {
|
|
188
|
-
console.error(
|
|
211
|
+
console.error(theme.red(formatHumanError(error, opts.verbose)));
|
|
189
212
|
}
|
|
190
213
|
process.exit(error.code);
|
|
191
214
|
}
|
|
@@ -210,11 +233,20 @@ async function main() {
|
|
|
210
233
|
}
|
|
211
234
|
}
|
|
212
235
|
else {
|
|
236
|
+
const suggestion = suggestCommand(firstNonOption, allCommands);
|
|
213
237
|
if (outputMode === 'json') {
|
|
214
238
|
console.error(formatJsonError(new Error(`Unknown command: ${firstNonOption}`), 1));
|
|
215
239
|
}
|
|
216
240
|
else {
|
|
217
241
|
console.error(`Error: Unknown command: ${firstNonOption}`);
|
|
242
|
+
if (suggestion) {
|
|
243
|
+
if (KNOWN_SESSION_COMMANDS.includes(suggestion)) {
|
|
244
|
+
console.error(`\nDid you mean: mcpc <@session> ${suggestion}`);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
console.error(`\nDid you mean: mcpc ${suggestion}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
218
250
|
console.error(`Run "mcpc --help" for usage information.\n`);
|
|
219
251
|
}
|
|
220
252
|
}
|
|
@@ -231,7 +263,7 @@ function createTopLevelProgram() {
|
|
|
231
263
|
program.configureHelp({
|
|
232
264
|
subcommandTerm: (cmd) => `${cmd.name()} ${cmd.usage()}`.replace(/^\[options\]\s*|\s*\[options\]/g, '').trim(),
|
|
233
265
|
styleTitle: (str) => chalk.bold(str),
|
|
234
|
-
styleSubcommandText: (str) =>
|
|
266
|
+
styleSubcommandText: (str) => theme.cyan(str),
|
|
235
267
|
formatHelp: (cmd, helper) => {
|
|
236
268
|
const output = Help.prototype.formatHelp.call(helper, cmd, helper);
|
|
237
269
|
const sections = output.split('\n\n');
|
|
@@ -248,78 +280,131 @@ function createTopLevelProgram() {
|
|
|
248
280
|
.join('\n\n') + '\n');
|
|
249
281
|
},
|
|
250
282
|
});
|
|
251
|
-
const docsUrl =
|
|
252
|
-
? `https://github.com/apify/mcpc/tree/v${mcpcVersion}`
|
|
253
|
-
: `https://raw.githubusercontent.com/apify/mcpc/v${mcpcVersion}/README.md`;
|
|
283
|
+
const docsUrl = `https://github.com/apify/mcpc/raw/refs/tags/v${mcpcVersion}/README.md`;
|
|
254
284
|
program
|
|
255
285
|
.name('mcpc')
|
|
256
286
|
.description(`${rainbow('Universal')} command-line client for the Model Context Protocol (MCP).`)
|
|
257
287
|
.usage('[<@session>] [<command>] [options]')
|
|
258
|
-
.option('
|
|
288
|
+
.option('--json', 'Output in JSON format for scripting')
|
|
259
289
|
.option('--verbose', 'Enable debug logging')
|
|
260
290
|
.option('--profile <name>', 'OAuth profile for the server ("default" if not provided)')
|
|
261
|
-
.option('--schema <file>', 'Validate tool/prompt schema against expected schema')
|
|
262
|
-
.option('--schema-mode <mode>', 'Schema validation mode: strict, compatible (default), ignore')
|
|
263
291
|
.option('--timeout <seconds>', 'Request timeout in seconds (default: 300)')
|
|
292
|
+
.option('--max-chars <n>', 'Truncate output to n characters (ignored in --json mode)')
|
|
264
293
|
.option('--insecure', 'Skip TLS certificate verification (for self-signed certs)')
|
|
265
294
|
.version(mcpcVersion, '-v, --version', 'Output the version number')
|
|
266
295
|
.helpOption('-h, --help', 'Display help');
|
|
267
296
|
program.addHelpText('after', `
|
|
268
297
|
${chalk.bold('MCP session commands (after connecting):')}
|
|
269
|
-
<@session> Show MCP server info, capabilities, and tools
|
|
270
|
-
<@session> ${
|
|
271
|
-
<@session> ${
|
|
272
|
-
<@session> ${
|
|
273
|
-
<@session> ${
|
|
274
|
-
<@session> ${
|
|
275
|
-
<@session> ${
|
|
276
|
-
<@session> ${
|
|
277
|
-
<@session> ${
|
|
278
|
-
<@session> ${
|
|
279
|
-
<@session> ${
|
|
280
|
-
<@session> ${
|
|
281
|
-
<@session> ${
|
|
282
|
-
<@session> ${
|
|
283
|
-
<@session> ${
|
|
284
|
-
<@session> ${
|
|
285
|
-
<@session> ${
|
|
298
|
+
<@session> Show MCP server info, capabilities, and tools overview
|
|
299
|
+
<@session> ${theme.cyan('grep')} <pattern> Search tools and instructions
|
|
300
|
+
<@session> ${theme.cyan('tools-list')} List all server tools
|
|
301
|
+
<@session> ${theme.cyan('tools-get')} <name> Get tool details and schema
|
|
302
|
+
<@session> ${theme.cyan('tools-call')} <name> [arg:=val ... | <json> | <stdin]
|
|
303
|
+
<@session> ${theme.cyan('prompts-list')}
|
|
304
|
+
<@session> ${theme.cyan('prompts-get')} <name> [arg:=val ... | <json> | <stdin]
|
|
305
|
+
<@session> ${theme.cyan('resources-list')}
|
|
306
|
+
<@session> ${theme.cyan('resources-read')} <uri>
|
|
307
|
+
<@session> ${theme.cyan('resources-subscribe')} <uri>
|
|
308
|
+
<@session> ${theme.cyan('resources-unsubscribe')} <uri>
|
|
309
|
+
<@session> ${theme.cyan('resources-templates-list')}
|
|
310
|
+
<@session> ${theme.cyan('tasks-list')}
|
|
311
|
+
<@session> ${theme.cyan('tasks-get')} <taskId>
|
|
312
|
+
<@session> ${theme.cyan('tasks-result')} <taskId>
|
|
313
|
+
<@session> ${theme.cyan('tasks-cancel')} <taskId>
|
|
314
|
+
<@session> ${theme.cyan('logging-set-level')} <level>
|
|
315
|
+
<@session> ${theme.cyan('ping')}
|
|
286
316
|
|
|
287
317
|
Run "mcpc" without arguments to show active sessions and OAuth profiles.
|
|
318
|
+
Run "mcpc --json" to get the same data as \`{ sessions: [...], profiles: [...] }\`.
|
|
288
319
|
|
|
289
320
|
Full docs: ${docsUrl}`);
|
|
290
321
|
program
|
|
291
322
|
.command('connect [server] [@session]')
|
|
292
|
-
.usage('<server>
|
|
323
|
+
.usage('<server> [@session]')
|
|
293
324
|
.description('Connect to an MCP server and start a new named @session')
|
|
294
325
|
.option('-H, --header <header>', 'HTTP header (can be repeated)')
|
|
295
326
|
.option('--profile <name>', 'OAuth profile to use ("default" if skipped)')
|
|
296
327
|
.option('--no-profile', 'Skip OAuth profile (connect anonymously)')
|
|
297
328
|
.option('--proxy <[host:]port>', 'Start proxy MCP server for session')
|
|
298
329
|
.option('--proxy-bearer-token <token>', 'Require authentication for access to proxy server')
|
|
330
|
+
.option('--stdio', 'Launch all local stdio servers from selected config files')
|
|
299
331
|
.option('--x402', 'Enable x402 auto-payment using the configured wallet')
|
|
300
332
|
.addHelpText('after', `
|
|
301
333
|
${chalk.bold('Server formats:')}
|
|
302
|
-
mcp.apify.com Remote HTTP server (https:// added
|
|
334
|
+
mcp.apify.com Remote HTTP server (https:// auto-added)
|
|
303
335
|
~/.vscode/mcp.json:puppeteer Config file entry (file:entry)
|
|
304
|
-
|
|
336
|
+
~/.vscode/mcp.json Config file — connect every entry
|
|
337
|
+
${chalk.dim('(no server)')} Auto-discover configs and connect everything
|
|
338
|
+
|
|
339
|
+
${chalk.bold('Auto-discovery (no server arg):')}
|
|
340
|
+
Scans ./ and ~ for .mcp.json, mcp.json, mcp_config.json, .cursor/mcp.json,
|
|
341
|
+
.vscode/mcp.json, .kiro/settings/mcp.json, ~/.claude.json,
|
|
342
|
+
~/.codeium/windsurf/mcp_config.json, plus VS Code & Claude Desktop configs.
|
|
343
|
+
Set APIFY_API_TOKEN to auto-connect mcp.apify.com as @apify.
|
|
344
|
+
|
|
345
|
+
${chalk.bold('Session name:')}
|
|
346
|
+
Omit @session to auto-generate from the server (mcp.apify.com → @apify)
|
|
347
|
+
or config entry. Matching sessions (same server, profile, header keys)
|
|
348
|
+
are reused. Bulk connects don't accept @session.
|
|
349
|
+
|
|
350
|
+
${chalk.bold('Stdio servers (command-based, run locally):')}
|
|
351
|
+
Config entries spawn the command on connect, even if the handshake
|
|
352
|
+
later fails — only connect to configs you trust. Stderr is logged to
|
|
353
|
+
~/.mcpc/logs/bridge-<session>.log. Bulk connects skip stdio by default;
|
|
354
|
+
pass --stdio to include them.
|
|
355
|
+
${jsonHelp('Array of `InitializeResult` objects (one per session), extended with `toolNames` and `_mcpc` metadata', '`[{ protocolVersion?, capabilities?, serverInfo?, instructions?, toolNames?, _mcpc: { sessionName, server?, ... }]`', `${SCHEMA_BASE}#initializeresult`)}`)
|
|
305
356
|
.action(async (server, sessionName, opts, command) => {
|
|
306
|
-
if (!server) {
|
|
307
|
-
throw new ClientError('Missing required argument: server\n\nExample: mcpc connect mcp.apify.com @myapp');
|
|
308
|
-
}
|
|
309
|
-
if (!sessionName) {
|
|
310
|
-
throw new ClientError('Missing required argument: @session\n\nExample: mcpc connect mcp.apify.com @myapp');
|
|
311
|
-
}
|
|
312
357
|
const globalOpts = getOptionsFromCommand(command);
|
|
313
|
-
const parsed = parseServerArg(server);
|
|
314
358
|
const headers = opts.header
|
|
315
359
|
? Array.isArray(opts.header)
|
|
316
360
|
? opts.header
|
|
317
361
|
: [opts.header]
|
|
318
362
|
: undefined;
|
|
363
|
+
if (!server) {
|
|
364
|
+
if (sessionName) {
|
|
365
|
+
throw new ClientError(`Cannot specify @session name when discovering and connecting all servers.\n` +
|
|
366
|
+
`To connect a specific server, pass a URL or config entry: mcpc connect <server> ${sessionName}`);
|
|
367
|
+
}
|
|
368
|
+
await sessions.connectAllFromStandardConfigs({
|
|
369
|
+
...globalOpts,
|
|
370
|
+
...(headers && { headers }),
|
|
371
|
+
...(opts.proxy && { proxy: opts.proxy }),
|
|
372
|
+
...(opts.proxyBearerToken && { proxyBearerToken: opts.proxyBearerToken }),
|
|
373
|
+
...(opts.stdio && { stdio: true }),
|
|
374
|
+
...(opts.x402 && { x402: opts.x402 }),
|
|
375
|
+
...(globalOpts.insecure && { insecure: true }),
|
|
376
|
+
});
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const parsed = parseServerArg(server);
|
|
319
380
|
if (!parsed) {
|
|
320
381
|
throw new ClientError(`Invalid server: "${server}"\n\n` +
|
|
321
382
|
`Expected a URL (e.g. mcp.apify.com) or a config file entry (e.g. ~/.vscode/mcp.json:filesystem)`);
|
|
322
383
|
}
|
|
384
|
+
if (parsed.type === 'config-file') {
|
|
385
|
+
if (sessionName) {
|
|
386
|
+
throw new ClientError(`Cannot specify @session name when connecting all servers from a config file.\n` +
|
|
387
|
+
`To connect a specific entry, use: mcpc connect ${server}:<entry> ${sessionName}`);
|
|
388
|
+
}
|
|
389
|
+
await sessions.connectAllFromConfig(parsed.file, {
|
|
390
|
+
...globalOpts,
|
|
391
|
+
...(headers && { headers }),
|
|
392
|
+
...(opts.proxy && { proxy: opts.proxy }),
|
|
393
|
+
...(opts.proxyBearerToken && { proxyBearerToken: opts.proxyBearerToken }),
|
|
394
|
+
...(opts.stdio && { stdio: true }),
|
|
395
|
+
...(opts.x402 && { x402: opts.x402 }),
|
|
396
|
+
...(globalOpts.insecure && { insecure: true }),
|
|
397
|
+
});
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (!sessionName) {
|
|
401
|
+
sessionName = await sessions.resolveSessionName(parsed, {
|
|
402
|
+
outputMode: globalOpts.outputMode,
|
|
403
|
+
...(globalOpts.profile && { profile: globalOpts.profile }),
|
|
404
|
+
...(headers && { headers }),
|
|
405
|
+
...(globalOpts.noProfile && { noProfile: globalOpts.noProfile }),
|
|
406
|
+
});
|
|
407
|
+
}
|
|
323
408
|
if (parsed.type === 'config') {
|
|
324
409
|
await sessions.connectSession(parsed.entry, sessionName, {
|
|
325
410
|
...globalOpts,
|
|
@@ -346,6 +431,7 @@ ${chalk.bold('Server formats:')}
|
|
|
346
431
|
.command('close [@session]')
|
|
347
432
|
.usage('<@session>')
|
|
348
433
|
.description('Close a session')
|
|
434
|
+
.addHelpText('after', jsonHelp('`{ sessionName, closed: true }`'))
|
|
349
435
|
.action(async (sessionName, _opts, command) => {
|
|
350
436
|
if (!sessionName) {
|
|
351
437
|
throw new ClientError('Missing required argument: @session\n\nExample: mcpc close @myapp');
|
|
@@ -377,18 +463,48 @@ ${chalk.bold('Server formats:')}
|
|
|
377
463
|
.usage('<server>')
|
|
378
464
|
.description('Interactively login to a server using OAuth and save profile')
|
|
379
465
|
.option('--profile <name>', 'Profile name (default: "default")')
|
|
380
|
-
.option('--scope <scopes>', 'OAuth scopes to request
|
|
381
|
-
.option('--client-id <id>', 'OAuth client ID (
|
|
382
|
-
.option('--client-secret <secret>', 'OAuth client secret (
|
|
466
|
+
.option('--scope <scopes>', 'OAuth scopes to request (e.g. --scope "read write")')
|
|
467
|
+
.option('--client-id <id>', 'Pre-registered OAuth client ID (skips CIMD and DCR)')
|
|
468
|
+
.option('--client-secret <secret>', 'Pre-registered OAuth client secret (requires --client-id)')
|
|
469
|
+
.option('--client-metadata-url <url>', 'HTTPS URL of an OAuth CIMD (default: https://apify.github.io/mcpc/client-metadata.json)')
|
|
470
|
+
.option('--no-client-metadata-url', 'Disable CIMD; force DCR on CIMD-capable servers')
|
|
471
|
+
.option('--callback-port <port>', 'Loopback port for OAuth callback (default: 13316–13325)')
|
|
472
|
+
.addHelpText('after', `
|
|
473
|
+
${chalk.bold('OAuth client registration approaches:')}
|
|
474
|
+
|
|
475
|
+
1. Pre-registration: --client-id (and optionally --client-secret).
|
|
476
|
+
2. Client ID Metadata Documents (CIMD): used by default. mcpc ships with a
|
|
477
|
+
hosted CIMD at https://apify.github.io/mcpc/client-metadata.json
|
|
478
|
+
which identifies all mcpc installs as the same client. Override with
|
|
479
|
+
--client-metadata-url <url> or disable with --no-client-metadata-url.
|
|
480
|
+
Active only when the authorization server advertises
|
|
481
|
+
"client_id_metadata_document_supported: true".
|
|
482
|
+
3. Dynamic Client Registration (DCR): fallback when the server exposes a
|
|
483
|
+
"registration_endpoint" and CIMD is not supported or disabled.
|
|
484
|
+
|
|
485
|
+
See https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization
|
|
486
|
+
|
|
487
|
+
${jsonHelp('Interactive prompts are written to stderr, stdout contains a clean JSON object', '`{ profile, serverUrl, scopes }`')}
|
|
488
|
+
`)
|
|
383
489
|
.action(async (server, opts, command) => {
|
|
384
490
|
if (!server) {
|
|
385
491
|
throw new ClientError('Missing required argument: server\n\nExample: mcpc login mcp.apify.com');
|
|
386
492
|
}
|
|
493
|
+
let callbackPort;
|
|
494
|
+
if (opts.callbackPort) {
|
|
495
|
+
const parsed = parseInt(opts.callbackPort, 10);
|
|
496
|
+
if (isNaN(parsed) || parsed < 1 || parsed > 65535) {
|
|
497
|
+
throw new ClientError(`Invalid --callback-port value: "${opts.callbackPort}". Must be an integer between 1 and 65535.`);
|
|
498
|
+
}
|
|
499
|
+
callbackPort = parsed;
|
|
500
|
+
}
|
|
387
501
|
await auth.login(server, {
|
|
388
502
|
profile: opts.profile,
|
|
389
503
|
scope: opts.scope,
|
|
390
504
|
clientId: opts.clientId,
|
|
391
505
|
clientSecret: opts.clientSecret,
|
|
506
|
+
clientMetadataUrl: opts.clientMetadataUrl,
|
|
507
|
+
...(callbackPort !== undefined ? { callbackPort } : {}),
|
|
392
508
|
...getOptionsFromCommand(command),
|
|
393
509
|
});
|
|
394
510
|
});
|
|
@@ -397,6 +513,7 @@ ${chalk.bold('Server formats:')}
|
|
|
397
513
|
.usage('<server>')
|
|
398
514
|
.description('Delete an OAuth profile for a server')
|
|
399
515
|
.option('--profile <name>', 'Profile name (default: "default")')
|
|
516
|
+
.addHelpText('after', jsonHelp('`{ profile, serverUrl, deleted: true, affectedSessions }`'))
|
|
400
517
|
.action(async (server, opts, command) => {
|
|
401
518
|
if (!server) {
|
|
402
519
|
throw new ClientError('Missing required argument: server\n\nExample: mcpc logout mcp.apify.com');
|
|
@@ -417,7 +534,7 @@ ${chalk.bold('Resources:')}
|
|
|
417
534
|
all Remove all of the above
|
|
418
535
|
|
|
419
536
|
Without arguments, performs safe cleanup of stale data only.
|
|
420
|
-
`)
|
|
537
|
+
${jsonHelp('`{ crashedBridges, expiredSessions, orphanedBridgeLogs, sessions, profiles, logs }`')}`)
|
|
421
538
|
.action(async (resources, _opts, command) => {
|
|
422
539
|
const globalOpts = getOptionsFromCommand(command);
|
|
423
540
|
const VALID_CLEAN_TYPES = ['sessions', 'profiles', 'logs', 'all'];
|
|
@@ -458,7 +575,7 @@ ${chalk.bold('Examples:')}
|
|
|
458
575
|
mcpc @apify grep "actor" Search within a single session
|
|
459
576
|
mcpc grep "file" --json JSON output for scripting
|
|
460
577
|
mcpc grep "actor" -m 5 Show at most 5 results
|
|
461
|
-
`)
|
|
578
|
+
${jsonHelp('`[{ sessionName, tools?: Tool[], resources?: Resource[], prompts?: Prompt[], instructions?: string[] }]`')}`)
|
|
462
579
|
.action(async (pattern, opts, command) => {
|
|
463
580
|
if (!pattern) {
|
|
464
581
|
throw new ClientError('Missing required argument: pattern\n\nUsage: mcpc grep <pattern>\n\nExample: mcpc grep "search"');
|
|
@@ -496,18 +613,17 @@ ${chalk.bold('Examples:')}
|
|
|
496
613
|
}
|
|
497
614
|
const topLevelCmd = program.commands.find((c) => c.name() === cmdName || c.aliases().includes(cmdName));
|
|
498
615
|
if (topLevelCmd) {
|
|
616
|
+
tuneCommandHelp(topLevelCmd);
|
|
499
617
|
topLevelCmd.outputHelp();
|
|
500
618
|
return;
|
|
501
619
|
}
|
|
502
|
-
|
|
503
|
-
dummyProgram.name('mcpc <@session>');
|
|
504
|
-
registerSessionCommands(dummyProgram, '@dummy');
|
|
505
|
-
const sessionCmd = dummyProgram.commands.find((c) => c.name() === cmdName || c.aliases().includes(cmdName));
|
|
506
|
-
if (sessionCmd) {
|
|
507
|
-
sessionCmd.outputHelp();
|
|
620
|
+
if (showSessionCommandHelp(cmdName))
|
|
508
621
|
return;
|
|
509
|
-
}
|
|
510
622
|
console.error(`Unknown command: ${cmdName}`);
|
|
623
|
+
const suggestion = suggestCommand(cmdName, [...KNOWN_COMMANDS, ...KNOWN_SESSION_COMMANDS]);
|
|
624
|
+
if (suggestion) {
|
|
625
|
+
console.error(`\nDid you mean: mcpc help ${suggestion}`);
|
|
626
|
+
}
|
|
511
627
|
console.error(`Run "mcpc --help" for usage information.`);
|
|
512
628
|
process.exit(1);
|
|
513
629
|
});
|
|
@@ -523,57 +639,158 @@ ${chalk.bold('Examples:')}
|
|
|
523
639
|
});
|
|
524
640
|
return program;
|
|
525
641
|
}
|
|
642
|
+
const NO_JSON_COMMANDS = new Set(['shell']);
|
|
643
|
+
function tuneCommandHelp(cmd) {
|
|
644
|
+
if (!NO_JSON_COMMANDS.has(cmd.name()) && !cmd.options.some((o) => o.long === '--json')) {
|
|
645
|
+
cmd.option('--json', 'Output in JSON format');
|
|
646
|
+
}
|
|
647
|
+
cmd.helpOption('-h, --help', 'Display help');
|
|
648
|
+
const helpOpt = cmd._getHelpOption?.();
|
|
649
|
+
if (helpOpt)
|
|
650
|
+
helpOpt.hidden = true;
|
|
651
|
+
}
|
|
652
|
+
function showSessionCommandHelp(cmdName) {
|
|
653
|
+
const dummyProgram = createSessionProgram();
|
|
654
|
+
registerSessionCommands(dummyProgram, '<@session>');
|
|
655
|
+
for (const cmd of dummyProgram.commands) {
|
|
656
|
+
tuneCommandHelp(cmd);
|
|
657
|
+
}
|
|
658
|
+
const sessionCmd = dummyProgram.commands.find((c) => c.name() === cmdName || c.aliases().includes(cmdName));
|
|
659
|
+
if (sessionCmd) {
|
|
660
|
+
sessionCmd.outputHelp();
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
return false;
|
|
664
|
+
}
|
|
526
665
|
function registerSessionCommands(program, session) {
|
|
527
666
|
program
|
|
528
|
-
.command('help')
|
|
529
|
-
.description('Show
|
|
530
|
-
.action(
|
|
531
|
-
|
|
667
|
+
.command('help', { hidden: true })
|
|
668
|
+
.description('Show available commands and options.')
|
|
669
|
+
.action((_options, command) => {
|
|
670
|
+
command.parent.outputHelp();
|
|
532
671
|
});
|
|
533
672
|
program
|
|
534
673
|
.command('shell')
|
|
535
|
-
.description('
|
|
674
|
+
.description('Launch interactive MCP shell.')
|
|
536
675
|
.action(async () => {
|
|
537
676
|
await sessions.openShell(session);
|
|
538
677
|
});
|
|
539
678
|
program
|
|
540
|
-
.command('close'
|
|
541
|
-
.description('Close
|
|
679
|
+
.command('close')
|
|
680
|
+
.description('Close MCP session.')
|
|
542
681
|
.action(async (_options, command) => {
|
|
543
682
|
await sessions.closeSession(session, getOptionsFromCommand(command));
|
|
544
683
|
});
|
|
545
684
|
program
|
|
546
685
|
.command('restart')
|
|
547
|
-
.description('Restart
|
|
686
|
+
.description('Restart MCP session (losing all state).')
|
|
548
687
|
.action(async (_options, command) => {
|
|
549
688
|
await sessions.restartSession(session, getOptionsFromCommand(command));
|
|
550
689
|
});
|
|
551
690
|
program
|
|
552
|
-
.command('
|
|
553
|
-
.
|
|
554
|
-
.
|
|
555
|
-
.
|
|
556
|
-
|
|
691
|
+
.command('grep <pattern>')
|
|
692
|
+
.usage('<pattern> [options]')
|
|
693
|
+
.description('Search MCP session objects.')
|
|
694
|
+
.option('--tools', 'Search tools')
|
|
695
|
+
.option('--resources', 'Search resources')
|
|
696
|
+
.option('--prompts', 'Search prompts')
|
|
697
|
+
.option('--instructions', 'Search server instructions')
|
|
698
|
+
.option('-E, --regex', 'Treat pattern as a regular expression')
|
|
699
|
+
.option('-s, --case-sensitive', 'Case-sensitive matching')
|
|
700
|
+
.option('-m, --max-results <n>', 'Limit the number of results')
|
|
701
|
+
.addHelpText('after', `
|
|
702
|
+
${chalk.bold('Type filters:')}
|
|
703
|
+
By default, tools and instructions are searched. Use --resources or --prompts
|
|
704
|
+
to search those instead. Combine flags to search multiple types.
|
|
705
|
+
|
|
706
|
+
${chalk.bold('Examples:')}
|
|
707
|
+
mcpc ${session} grep "search" Search tools and instructions
|
|
708
|
+
mcpc ${session} grep "search" --resources Search resources only
|
|
709
|
+
mcpc ${session} grep "search|find" -E Regex search
|
|
710
|
+
${jsonHelp('`{ tools?: Tool[], resources?: Resource[], prompts?: Prompt[], instructions?: string[] }`')}`)
|
|
711
|
+
.action(async (pattern, opts, command) => {
|
|
712
|
+
const globalOpts = getOptionsFromCommand(command);
|
|
713
|
+
const maxResults = opts.maxResults ? parseInt(opts.maxResults, 10) : undefined;
|
|
714
|
+
const exitCode = await grepCmd.grepSession(session, pattern, {
|
|
715
|
+
tools: opts.tools,
|
|
716
|
+
resources: opts.resources,
|
|
717
|
+
prompts: opts.prompts,
|
|
718
|
+
instructions: opts.instructions,
|
|
719
|
+
regex: opts.regex,
|
|
720
|
+
caseSensitive: opts.caseSensitive,
|
|
721
|
+
maxResults,
|
|
722
|
+
...globalOpts,
|
|
723
|
+
});
|
|
724
|
+
process.exit(exitCode);
|
|
557
725
|
});
|
|
558
726
|
program
|
|
559
727
|
.command('tools-list')
|
|
560
|
-
.description('List
|
|
561
|
-
.option('--full', 'Show full tool details including
|
|
728
|
+
.description('List all MCP tools.')
|
|
729
|
+
.option('--full', 'Show full tool details including schema')
|
|
730
|
+
.addHelpText('after', jsonHelp('Array of `Tool` objects', '`[{ name, description?, inputSchema, outputSchema?, annotations? }, ...]`', `${SCHEMA_BASE}#tool`))
|
|
562
731
|
.action(async (_options, command) => {
|
|
563
732
|
await tools.listTools(session, getOptionsFromCommand(command));
|
|
564
733
|
});
|
|
565
734
|
program
|
|
566
735
|
.command('tools-get <name>')
|
|
567
|
-
.description('Get
|
|
736
|
+
.description('Get details and schema for an MCP tool.')
|
|
737
|
+
.option('--schema <file>', 'Validate tool schema against expected schema')
|
|
738
|
+
.option('--schema-mode <mode>', 'Schema validation mode: strict, compatible (default), ignore')
|
|
739
|
+
.addHelpText('after', `
|
|
740
|
+
${chalk.bold('Schema validation:')}
|
|
741
|
+
--schema <file> Validate against expected schema (save with tools-get --json)
|
|
742
|
+
--schema-mode <mode> strict | compatible (default) | ignore
|
|
743
|
+
${jsonHelp('`Tool` object', '`{ name, description?, inputSchema, outputSchema?, annotations? }`', `${SCHEMA_BASE}#tool`)}`)
|
|
568
744
|
.action(async (name, _options, command) => {
|
|
569
745
|
await tools.getTool(session, name, getOptionsFromCommand(command));
|
|
570
746
|
});
|
|
747
|
+
const toolsCallJsonHelp = jsonHelp('`CallToolResult` object', '`{ content: [{ type, text?, ... }], isError?, structuredContent?: { ... } }`', `${SCHEMA_BASE}#calltoolresult`);
|
|
748
|
+
const toolsCallCombinedJsonHelp = `
|
|
749
|
+
${chalk.bold('JSON output (--json):')}
|
|
750
|
+
\`CallToolResult\` object:
|
|
751
|
+
\`{ content: [{ type, text?, ... }], isError?, structuredContent?: { ... } }\`
|
|
752
|
+
Schema: ${SCHEMA_BASE}#calltoolresult
|
|
753
|
+
|
|
754
|
+
With \`--detach\`: \`CreateTaskResult\` object:
|
|
755
|
+
\`{ taskId: string, status: string }\`
|
|
756
|
+
Schema: ${SCHEMA_BASE}#createtaskresult
|
|
757
|
+
`;
|
|
571
758
|
program
|
|
572
759
|
.command('tools-call <name> [args...]')
|
|
573
|
-
.description('Call
|
|
574
|
-
.
|
|
760
|
+
.description('Call an MCP tool with arguments.')
|
|
761
|
+
.helpOption(false)
|
|
762
|
+
.option('--task', 'Use async task execution; Ctrl+C prints the task ID and exits (experimental)')
|
|
575
763
|
.option('--detach', 'Start task and return immediately with task ID (implies --task)')
|
|
764
|
+
.option('--schema <file>', 'Validate tool schema against expected schema before calling')
|
|
765
|
+
.option('--schema-mode <mode>', 'Schema validation mode: strict, compatible (default), ignore')
|
|
766
|
+
.addHelpText('after', `
|
|
767
|
+
${chalk.bold('Arguments:')}
|
|
768
|
+
key:=value pairs mcpc ${session} tools-call search query:=hello limit:=10
|
|
769
|
+
Inline JSON mcpc ${session} tools-call search '{"query":"hello"}'
|
|
770
|
+
Stdin pipe echo '{"query":"hello"}' | mcpc ${session} tools-call search
|
|
771
|
+
|
|
772
|
+
Values are auto-parsed: strings, numbers, booleans, JSON objects/arrays.
|
|
773
|
+
To force a string, wrap in quotes: id:='"123"'
|
|
774
|
+
|
|
775
|
+
${chalk.bold('Async tasks (--task, --detach):')}
|
|
776
|
+
--task shows a progress spinner while the task runs on the server.
|
|
777
|
+
If you press Ctrl+C, the task keeps running and a hint with the task ID
|
|
778
|
+
is printed so you can fetch or cancel it later.
|
|
779
|
+
--detach returns the task ID immediately without waiting.
|
|
780
|
+
|
|
781
|
+
${chalk.bold('Schema validation:')}
|
|
782
|
+
--schema <file> Validate tool schema before calling (save with tools-get --json)
|
|
783
|
+
--schema-mode <mode> strict | compatible (default) | ignore
|
|
784
|
+
${toolsCallCombinedJsonHelp}`)
|
|
576
785
|
.action(async (name, args, options, command) => {
|
|
786
|
+
if (name === '--help' || name === '-h') {
|
|
787
|
+
command.help();
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
791
|
+
await tools.getTool(session, name, getOptionsFromCommand(command));
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
577
794
|
await tools.callTool(session, name, {
|
|
578
795
|
args,
|
|
579
796
|
task: options.task,
|
|
@@ -583,39 +800,45 @@ function registerSessionCommands(program, session) {
|
|
|
583
800
|
});
|
|
584
801
|
program
|
|
585
802
|
.command('tasks-list')
|
|
586
|
-
.description('List
|
|
803
|
+
.description('List all MCP tasks.')
|
|
804
|
+
.addHelpText('after', jsonHelp('`{ tasks: Task[] }`', '`{ tasks: [{ taskId, status, ttl, createdAt, lastUpdatedAt, statusMessage?, pollInterval? }] }`', `${SCHEMA_BASE}#task`))
|
|
587
805
|
.action(async (_options, command) => {
|
|
588
806
|
await tasks.listTasks(session, getOptionsFromCommand(command));
|
|
589
807
|
});
|
|
590
808
|
program
|
|
591
809
|
.command('tasks-get <taskId>')
|
|
592
|
-
.description('Get
|
|
810
|
+
.description('Get MCP task status.')
|
|
811
|
+
.addHelpText('after', jsonHelp('`Task` object', '`{ taskId, status, ttl, createdAt, lastUpdatedAt, statusMessage?, pollInterval? }`', `${SCHEMA_BASE}#task`))
|
|
593
812
|
.action(async (taskId, _options, command) => {
|
|
594
813
|
await tasks.getTask(session, taskId, getOptionsFromCommand(command));
|
|
595
814
|
});
|
|
596
815
|
program
|
|
597
|
-
.command('tasks-
|
|
598
|
-
.description('
|
|
816
|
+
.command('tasks-result <taskId>')
|
|
817
|
+
.description('Get MCP task final result (blocks until task reaches a terminal state).')
|
|
818
|
+
.addHelpText('after', toolsCallJsonHelp)
|
|
599
819
|
.action(async (taskId, _options, command) => {
|
|
600
|
-
await tasks.
|
|
820
|
+
await tasks.getTaskResult(session, taskId, getOptionsFromCommand(command));
|
|
601
821
|
});
|
|
602
822
|
program
|
|
603
|
-
.command('
|
|
604
|
-
.description('
|
|
605
|
-
.
|
|
606
|
-
|
|
823
|
+
.command('tasks-cancel <taskId>')
|
|
824
|
+
.description('Cancel an MCP task.')
|
|
825
|
+
.addHelpText('after', jsonHelp('`Task` object', '`{ taskId, status, ttl, createdAt, lastUpdatedAt, statusMessage?, pollInterval? }`', `${SCHEMA_BASE}#task`))
|
|
826
|
+
.action(async (taskId, _options, command) => {
|
|
827
|
+
await tasks.cancelTask(session, taskId, getOptionsFromCommand(command));
|
|
607
828
|
});
|
|
608
829
|
program
|
|
609
830
|
.command('resources-list')
|
|
610
|
-
.description('List
|
|
831
|
+
.description('List all MCP resources.')
|
|
832
|
+
.addHelpText('after', jsonHelp('Array of `Resource` objects', '`[{ uri, name?, description?, mimeType? }, ...]`', `${SCHEMA_BASE}#resource`))
|
|
611
833
|
.action(async (_options, command) => {
|
|
612
834
|
await resources.listResources(session, getOptionsFromCommand(command));
|
|
613
835
|
});
|
|
614
836
|
program
|
|
615
837
|
.command('resources-read <uri>')
|
|
616
|
-
.description('
|
|
838
|
+
.description('Read an MCP resource by URI.')
|
|
617
839
|
.option('-o, --output <file>', 'Write resource to file')
|
|
618
840
|
.option('--max-size <bytes>', 'Maximum resource size in bytes')
|
|
841
|
+
.addHelpText('after', jsonHelp('`ReadResourceResult` object', '`{ contents: [{ uri, mimeType?, text? | blob? }] }`', `${SCHEMA_BASE}#readresourceresult`))
|
|
619
842
|
.action(async (uri, options, command) => {
|
|
620
843
|
await resources.getResource(session, uri, {
|
|
621
844
|
output: options.output,
|
|
@@ -625,37 +848,44 @@ function registerSessionCommands(program, session) {
|
|
|
625
848
|
});
|
|
626
849
|
program
|
|
627
850
|
.command('resources-subscribe <uri>')
|
|
628
|
-
.description('Subscribe to resource updates')
|
|
851
|
+
.description('Subscribe to MCP resource updates.')
|
|
852
|
+
.addHelpText('after', jsonHelp('`{ subscribed: true, uri: string }`'))
|
|
629
853
|
.action(async (uri, _options, command) => {
|
|
630
854
|
await resources.subscribeResource(session, uri, getOptionsFromCommand(command));
|
|
631
855
|
});
|
|
632
856
|
program
|
|
633
857
|
.command('resources-unsubscribe <uri>')
|
|
634
|
-
.description('Unsubscribe from resource updates')
|
|
858
|
+
.description('Unsubscribe from MCP resource updates.')
|
|
859
|
+
.addHelpText('after', jsonHelp('`{ unsubscribed: true, uri: string }`'))
|
|
635
860
|
.action(async (uri, _options, command) => {
|
|
636
861
|
await resources.unsubscribeResource(session, uri, getOptionsFromCommand(command));
|
|
637
862
|
});
|
|
638
863
|
program
|
|
639
864
|
.command('resources-templates-list')
|
|
640
|
-
.description('List
|
|
865
|
+
.description('List MCP resource templates.')
|
|
866
|
+
.addHelpText('after', jsonHelp('Array of `ResourceTemplate` objects', '`[{ uriTemplate, name?, description?, mimeType? }, ...]`', `${SCHEMA_BASE}#resourcetemplate`))
|
|
641
867
|
.action(async (_options, command) => {
|
|
642
868
|
await resources.listResourceTemplates(session, getOptionsFromCommand(command));
|
|
643
869
|
});
|
|
644
|
-
program
|
|
645
|
-
.command('prompts')
|
|
646
|
-
.description('List available prompts (shorthand for prompts-list)')
|
|
647
|
-
.action(async (_options, command) => {
|
|
648
|
-
await prompts.listPrompts(session, getOptionsFromCommand(command));
|
|
649
|
-
});
|
|
650
870
|
program
|
|
651
871
|
.command('prompts-list')
|
|
652
|
-
.description('List
|
|
872
|
+
.description('List all MCP prompts.')
|
|
873
|
+
.addHelpText('after', jsonHelp('Array of `Prompt` objects', '`[{ name, description?, arguments?: [{ name, required? }] }, ...]`', `${SCHEMA_BASE}#prompt`))
|
|
653
874
|
.action(async (_options, command) => {
|
|
654
875
|
await prompts.listPrompts(session, getOptionsFromCommand(command));
|
|
655
876
|
});
|
|
656
877
|
program
|
|
657
878
|
.command('prompts-get <name> [args...]')
|
|
658
|
-
.description('Get
|
|
879
|
+
.description('Get an MCP prompt with arguments.')
|
|
880
|
+
.addHelpText('after', `
|
|
881
|
+
${chalk.bold('Arguments:')}
|
|
882
|
+
key:=value pairs mcpc ${session} prompts-get summarize style:=brief lang:=en
|
|
883
|
+
Inline JSON mcpc ${session} prompts-get summarize '{"style":"brief"}'
|
|
884
|
+
Stdin pipe echo '{"style":"brief"}' | mcpc ${session} prompts-get summarize
|
|
885
|
+
|
|
886
|
+
Values are auto-parsed: strings, numbers, booleans, JSON objects/arrays.
|
|
887
|
+
To force a string, wrap in quotes: id:='"123"'
|
|
888
|
+
${jsonHelp('`GetPromptResult` object', '`{ description?, messages: [{ role, content: { type, text?, ... } }] }`', `${SCHEMA_BASE}#getpromptresult`)}`)
|
|
659
889
|
.action(async (name, args, _options, command) => {
|
|
660
890
|
await prompts.getPrompt(session, name, {
|
|
661
891
|
args,
|
|
@@ -664,63 +894,47 @@ function registerSessionCommands(program, session) {
|
|
|
664
894
|
});
|
|
665
895
|
program
|
|
666
896
|
.command('logging-set-level <level>')
|
|
667
|
-
.description('Set server logging level
|
|
897
|
+
.description('Set MCP server logging level.')
|
|
898
|
+
.addHelpText('after', jsonHelp('`{ level: string }`'))
|
|
668
899
|
.action(async (level, _options, command) => {
|
|
669
900
|
await logging.setLogLevel(session, level, getOptionsFromCommand(command));
|
|
670
901
|
});
|
|
671
902
|
program
|
|
672
903
|
.command('ping')
|
|
673
|
-
.description('Ping the MCP server
|
|
904
|
+
.description('Ping the MCP server.')
|
|
905
|
+
.addHelpText('after', jsonHelp('`{ success: true, durationMs: number }`'))
|
|
674
906
|
.action(async (_options, command) => {
|
|
675
907
|
await utilities.ping(session, getOptionsFromCommand(command));
|
|
676
908
|
});
|
|
677
|
-
program
|
|
678
|
-
.command('grep <pattern>')
|
|
679
|
-
.description('Search tools and instructions')
|
|
680
|
-
.option('--tools', 'Search tools')
|
|
681
|
-
.option('--resources', 'Search resources')
|
|
682
|
-
.option('--prompts', 'Search prompts')
|
|
683
|
-
.option('--instructions', 'Search server instructions')
|
|
684
|
-
.option('-E, --regex', 'Treat pattern as a regular expression')
|
|
685
|
-
.option('-s, --case-sensitive', 'Case-sensitive matching')
|
|
686
|
-
.option('-m, --max-results <n>', 'Limit the number of results')
|
|
687
|
-
.action(async (pattern, opts, command) => {
|
|
688
|
-
const globalOpts = getOptionsFromCommand(command);
|
|
689
|
-
const maxResults = opts.maxResults ? parseInt(opts.maxResults, 10) : undefined;
|
|
690
|
-
const exitCode = await grepCmd.grepSession(session, pattern, {
|
|
691
|
-
tools: opts.tools,
|
|
692
|
-
resources: opts.resources,
|
|
693
|
-
prompts: opts.prompts,
|
|
694
|
-
instructions: opts.instructions,
|
|
695
|
-
regex: opts.regex,
|
|
696
|
-
caseSensitive: opts.caseSensitive,
|
|
697
|
-
maxResults,
|
|
698
|
-
...globalOpts,
|
|
699
|
-
});
|
|
700
|
-
process.exit(exitCode);
|
|
701
|
-
});
|
|
702
909
|
}
|
|
703
910
|
function createSessionProgram() {
|
|
704
911
|
const program = new Command();
|
|
705
912
|
program.configureOutput({
|
|
706
|
-
outputError: (
|
|
913
|
+
outputError: () => { },
|
|
707
914
|
getOutHelpWidth: () => 100,
|
|
708
915
|
getErrHelpWidth: () => 100,
|
|
709
916
|
});
|
|
917
|
+
program.configureHelp({
|
|
918
|
+
subcommandTerm: (cmd) => `${cmd.name()} ${cmd.usage()}`.replace(/^\[options\]\s*|\s*\[options\]/g, '').trim(),
|
|
919
|
+
styleTitle: (str) => chalk.bold(str),
|
|
920
|
+
styleSubcommandText: (str) => theme.cyan(str),
|
|
921
|
+
});
|
|
710
922
|
program
|
|
711
923
|
.name('mcpc <@session>')
|
|
924
|
+
.description('Execute MCP commands on a connected session.')
|
|
712
925
|
.helpOption('-h, --help', 'Display help')
|
|
713
|
-
.option('
|
|
926
|
+
.option('--json', 'Output in JSON format for scripting and code mode')
|
|
714
927
|
.option('--verbose', 'Enable debug logging')
|
|
715
928
|
.option('--profile <name>', 'OAuth profile override')
|
|
716
|
-
.option('--schema <file>', 'Validate tool/prompt schema against expected schema')
|
|
717
|
-
.option('--schema-mode <mode>', 'Schema validation mode: strict, compatible (default), ignore')
|
|
718
929
|
.option('--timeout <seconds>', 'Request timeout in seconds (default: 300)')
|
|
719
|
-
.option('--
|
|
930
|
+
.option('--max-chars <n>', 'Truncate output to n characters (ignored in --json mode)')
|
|
931
|
+
.option('--insecure', 'Skip TLS certificate verification (for self-signed certs)')
|
|
932
|
+
.addHelpText('after', `\nWhen no command is given, shows server info, capabilities, and tools.\n`);
|
|
720
933
|
return program;
|
|
721
934
|
}
|
|
722
935
|
async function handleSessionCommands(session, args) {
|
|
723
|
-
|
|
936
|
+
const argsSlice = args.slice(2);
|
|
937
|
+
if (!hasSubcommand(args) && !argsSlice.includes('--help') && !argsSlice.includes('-h')) {
|
|
724
938
|
const options = extractOptions(args);
|
|
725
939
|
if (options.verbose)
|
|
726
940
|
setVerbose(true);
|
|
@@ -734,25 +948,50 @@ async function handleSessionCommands(session, args) {
|
|
|
734
948
|
return;
|
|
735
949
|
}
|
|
736
950
|
const program = createSessionProgram();
|
|
951
|
+
program.name(`mcpc ${session}`);
|
|
952
|
+
program.exitOverride();
|
|
737
953
|
registerSessionCommands(program, session);
|
|
954
|
+
for (const cmd of program.commands) {
|
|
955
|
+
tuneCommandHelp(cmd);
|
|
956
|
+
}
|
|
738
957
|
try {
|
|
739
958
|
await program.parseAsync(args);
|
|
740
959
|
}
|
|
741
960
|
catch (error) {
|
|
742
961
|
const opts = program.opts();
|
|
743
962
|
const outputMode = opts.json ? 'json' : 'human';
|
|
963
|
+
if (error instanceof CommanderError && error.code === 'commander.unknownCommand') {
|
|
964
|
+
const unknownCmd = args.find((a, i) => i >= 2 && !a.startsWith('-') && !KNOWN_SESSION_COMMANDS.includes(a));
|
|
965
|
+
if (unknownCmd) {
|
|
966
|
+
const suggestion = suggestCommand(unknownCmd, KNOWN_SESSION_COMMANDS);
|
|
967
|
+
if (outputMode === 'json') {
|
|
968
|
+
console.error(formatJsonError(new Error(`Unknown command: ${unknownCmd}`), 1));
|
|
969
|
+
}
|
|
970
|
+
else {
|
|
971
|
+
console.error(`Error: Unknown command: ${unknownCmd}`);
|
|
972
|
+
if (suggestion) {
|
|
973
|
+
console.error(`\nDid you mean: mcpc ${session} ${suggestion}`);
|
|
974
|
+
}
|
|
975
|
+
console.error(`Run "mcpc ${session} --help" for available commands.\n`);
|
|
976
|
+
}
|
|
977
|
+
process.exit(1);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
if (error instanceof CommanderError && error.code === 'commander.helpDisplayed') {
|
|
981
|
+
process.exit(0);
|
|
982
|
+
}
|
|
744
983
|
if (isMcpError(error)) {
|
|
745
984
|
if (outputMode === 'json') {
|
|
746
985
|
console.error(formatJsonError(error, error.code));
|
|
747
986
|
}
|
|
748
987
|
else {
|
|
749
|
-
console.error(
|
|
988
|
+
console.error(theme.red(formatHumanError(error, opts.verbose)));
|
|
750
989
|
}
|
|
751
990
|
process.exit(error.code);
|
|
752
991
|
}
|
|
753
992
|
console.error(outputMode === 'json'
|
|
754
993
|
? formatJsonError(error, 1)
|
|
755
|
-
:
|
|
994
|
+
: theme.red(formatHumanError(error, opts.verbose)));
|
|
756
995
|
process.exit(1);
|
|
757
996
|
}
|
|
758
997
|
}
|