From d24fb7e02a246ed97ae4d3654f4510b5c40ed635 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 6 Apr 2026 09:04:26 +0000 Subject: [PATCH 01/19] Add --json output docs to help text and tools-call example to tools-get Document the JSON output shape (MCP object types) in --help for all commands that support --json, so AI agents know what to expect when scripting. tools-get now shows an example tools-call command with placeholder arguments derived from the tool's schema (required params first, filling optional up to 3). https://claude.ai/code/session_01JCmVVCvBbxPUrxy3BcgjCc --- CHANGELOG.md | 2 + src/cli/commands/tools.ts | 12 ++++- src/cli/index.ts | 93 ++++++++++++++++++++++++++++++++++++ src/cli/output.ts | 73 ++++++++++++++++++++++++++++ test/unit/cli/output.test.ts | 83 ++++++++++++++++++++++++++++++++ 5 files changed, 262 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 607b2035..b26dbb7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `--json` output documentation in `--help` for all commands, describing the MCP object shape returned +- `tools-get` now shows an example `tools-call` command with placeholder arguments based on the tool's schema - Unit test to verify README help section stays in sync with `mcpc --help` output - Experimental feature warning for `x402 init` and `x402 import` commands - Windows E2E tests via manual-dispatch GitHub Actions workflow (`e2e-windows.yml`) — cross-platform test framework with MSYS path conversion, cached `tasklist` process detection, graceful bridge shutdown via IPC, and Windows named pipe support diff --git a/src/cli/commands/tools.ts b/src/cli/commands/tools.ts index fe7f9547..9b7173ba 100644 --- a/src/cli/commands/tools.ts +++ b/src/cli/commands/tools.ts @@ -4,7 +4,13 @@ import ora from 'ora'; import chalk from 'chalk'; -import { formatOutput, formatToolDetail, formatSuccess, formatWarning } from '../output.js'; +import { + formatOutput, + formatToolDetail, + formatToolCallExample, + formatSuccess, + formatWarning, +} from '../output.js'; import { ClientError } from '../../lib/errors.js'; import type { CommandOptions, TaskUpdate } from '../../lib/types.js'; import { withMcpClient } from '../helpers.js'; @@ -85,6 +91,10 @@ export async function getTool( if (options.outputMode === 'human') { console.log(formatToolDetail(tool)); + const example = formatToolCallExample(tool, target); + if (example) { + console.log('\n' + example + '\n'); + } } else { console.log(formatOutput(tool, 'json')); } diff --git a/src/cli/index.ts b/src/cli/index.ts index 6b1c9be4..bb37cc8c 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -402,6 +402,8 @@ Full docs: ${docsUrl}` ${chalk.bold('Server formats:')} mcp.apify.com Remote HTTP server (https:// added automatically) ~/.vscode/mcp.json:puppeteer Config file entry (file:entry) + +${chalk.bold('JSON output (--json):')}\n MCP InitializeResult: { protocolVersion, capabilities, serverInfo, instructions?, tools? } ` ) .action(async (server, sessionName, opts, command) => { @@ -460,6 +462,10 @@ ${chalk.bold('Server formats:')} .command('close [@session]') .usage('<@session>') .description('Close a session') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n { sessionName, closed: true }\n` + ) .action(async (sessionName, _opts, command) => { if (!sessionName) { throw new ClientError('Missing required argument: @session\n\nExample: mcpc close @myapp'); @@ -508,6 +514,10 @@ ${chalk.bold('Server formats:')} '--client-secret ', 'OAuth client secret (for servers without dynamic client registration)' ) + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n { profile, serverUrl, scopes }\n` + ) .action(async (server, opts, command) => { if (!server) { throw new ClientError( @@ -529,6 +539,10 @@ ${chalk.bold('Server formats:')} .usage('') .description('Delete an OAuth profile for a server') .option('--profile ', 'Profile name (default: "default")') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n { profile, serverUrl, deleted: true, affectedSessions }\n` + ) .action(async (server, opts, command) => { if (!server) { throw new ClientError( @@ -555,6 +569,9 @@ ${chalk.bold('Resources:')} all Remove all of the above Without arguments, performs safe cleanup of stale data only. + +${chalk.bold('JSON output (--json):')} + { crashedBridges, expiredSessions, orphanedBridgeLogs, sessions, profiles, logs } ` ) .action(async (resources: string[], _opts, command) => { @@ -606,6 +623,9 @@ ${chalk.bold('Examples:')} mcpc @apify grep "actor" Search within a single session mcpc grep "file" --json JSON output for scripting mcpc grep "actor" -m 5 Show at most 5 results + +${chalk.bold('JSON output (--json):')} + [{ sessionName, tools?: Tool[], resources?: Resource[], prompts?: Prompt[], instructions?: string[] }] ` ) .action(async (pattern, opts, command) => { @@ -703,6 +723,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('help') .description('Show server instructions and available capabilities') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n MCP InitializeResult: { protocolVersion, capabilities, serverInfo, instructions?, tools? }\n` + ) .action(async (_options, command) => { await sessions.showHelp(session, getOptionsFromCommand(command)); }); @@ -736,6 +760,10 @@ function registerSessionCommands(program: Command, session: string): void { .command('tools') .description('List available tools (shorthand for tools-list)') .option('--full', 'Show full tool details including complete input schema') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Tool objects: [{ name, description?, inputSchema, annotations? }, ...]\n` + ) .action(async (_options, command) => { await tools.listTools(session, getOptionsFromCommand(command)); }); @@ -744,6 +772,10 @@ function registerSessionCommands(program: Command, session: string): void { .command('tools-list') .description('List available tools') .option('--full', 'Show full tool details including complete input schema') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Tool objects: [{ name, description?, inputSchema, annotations? }, ...]\n` + ) .action(async (_options, command) => { await tools.listTools(session, getOptionsFromCommand(command)); }); @@ -751,6 +783,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('tools-get ') .description('Get information about a specific tool') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n MCP Tool object: { name, description?, inputSchema, annotations? }\n` + ) .action(async (name, _options, command) => { await tools.getTool(session, name, getOptionsFromCommand(command)); }); @@ -760,6 +796,10 @@ function registerSessionCommands(program: Command, session: string): void { .description('Call a tool with arguments (key:=value pairs or JSON)') .option('--task', 'Use task execution (experimental)') .option('--detach', 'Start task and return immediately with task ID (implies --task)') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n MCP CallToolResult: { content: [{ type, text?, ... }], isError?, structuredContent? }\n` + ) .action(async (name, args, options, command) => { await tools.callTool(session, name, { args, @@ -773,6 +813,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('tasks-list') .description('List active tasks') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n { tasks: [{ taskId, status, statusMessage?, createdAt?, lastUpdatedAt? }, ...] }\n` + ) .action(async (_options, command) => { await tasks.listTasks(session, getOptionsFromCommand(command)); }); @@ -780,6 +824,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('tasks-get ') .description('Get status of a specific task') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n MCP Task object: { taskId, status, statusMessage?, createdAt?, lastUpdatedAt? }\n` + ) .action(async (taskId, _options, command) => { await tasks.getTask(session, taskId, getOptionsFromCommand(command)); }); @@ -787,6 +835,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('tasks-cancel ') .description('Cancel a running task') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n MCP Task object: { taskId, status, statusMessage? }\n` + ) .action(async (taskId, _options, command) => { await tasks.cancelTask(session, taskId, getOptionsFromCommand(command)); }); @@ -795,6 +847,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources') .description('List available resources (shorthand for resources-list)') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Resource objects: [{ uri, name?, description?, mimeType? }, ...]\n` + ) .action(async (_options, command) => { await resources.listResources(session, getOptionsFromCommand(command)); }); @@ -802,6 +858,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources-list') .description('List available resources') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Resource objects: [{ uri, name?, description?, mimeType? }, ...]\n` + ) .action(async (_options, command) => { await resources.listResources(session, getOptionsFromCommand(command)); }); @@ -811,6 +871,10 @@ function registerSessionCommands(program: Command, session: string): void { .description('Get a resource by URI') .option('-o, --output ', 'Write resource to file') .option('--max-size ', 'Maximum resource size in bytes') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n MCP ReadResourceResult: { contents: [{ uri, mimeType?, text? | blob? }] }\n` + ) .action(async (uri, options, command) => { await resources.getResource(session, uri, { output: options.output, @@ -822,6 +886,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources-subscribe ') .description('Subscribe to resource updates') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n { subscribed: true, uri: string }\n` + ) .action(async (uri, _options, command) => { await resources.subscribeResource(session, uri, getOptionsFromCommand(command)); }); @@ -829,6 +897,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources-unsubscribe ') .description('Unsubscribe from resource updates') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n { unsubscribed: true, uri: string }\n` + ) .action(async (uri, _options, command) => { await resources.unsubscribeResource(session, uri, getOptionsFromCommand(command)); }); @@ -836,6 +908,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources-templates-list') .description('List available resource templates') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n Array of MCP ResourceTemplate objects: [{ uriTemplate, name?, description?, mimeType? }, ...]\n` + ) .action(async (_options, command) => { await resources.listResourceTemplates(session, getOptionsFromCommand(command)); }); @@ -844,6 +920,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('prompts') .description('List available prompts (shorthand for prompts-list)') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Prompt objects: [{ name, description?, arguments?: [{ name, description?, required? }] }, ...]\n` + ) .action(async (_options, command) => { await prompts.listPrompts(session, getOptionsFromCommand(command)); }); @@ -851,6 +931,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('prompts-list') .description('List available prompts') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Prompt objects: [{ name, description?, arguments?: [{ name, description?, required? }] }, ...]\n` + ) .action(async (_options, command) => { await prompts.listPrompts(session, getOptionsFromCommand(command)); }); @@ -858,6 +942,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('prompts-get [args...]') .description('Get a prompt by name with arguments (key:=value pairs or JSON)') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n MCP GetPromptResult: { description?, messages: [{ role, content: { type, text?, ... } }] }\n` + ) .action(async (name, args, _options, command) => { await prompts.getPrompt(session, name, { args, @@ -871,6 +959,7 @@ function registerSessionCommands(program: Command, session: string): void { .description( 'Set server logging level (debug, info, notice, warning, error, critical, alert, emergency)' ) + .addHelpText('after', `\n${chalk.bold('JSON output (--json):')}\n { level: string }\n`) .action(async (level, _options, command) => { await logging.setLogLevel(session, level, getOptionsFromCommand(command)); }); @@ -879,6 +968,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('ping') .description('Ping the MCP server to check if it is alive') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n { success: true, durationMs: number }\n` + ) .action(async (_options, command) => { await utilities.ping(session, getOptionsFromCommand(command)); }); diff --git a/src/cli/output.ts b/src/cli/output.ts index ae6d8ac1..8d96755e 100644 --- a/src/cli/output.ts +++ b/src/cli/output.ts @@ -568,6 +568,79 @@ export function formatToolDetail(tool: Tool): string { return lines.join('\n'); } +/** + * Generate an example placeholder value for a JSON Schema property. + * Uses the default value if available, otherwise a reasonable placeholder. + */ +function exampleValue(propSchema: Record): string { + // Use default value if available + if (propSchema.default !== undefined) { + return JSON.stringify(propSchema.default); + } + + // Use first enum value if available + if (propSchema.enum && Array.isArray(propSchema.enum) && propSchema.enum.length > 0) { + return JSON.stringify(propSchema.enum[0]); + } + + const schemaType = propSchema.type; + + if (schemaType === 'string') return '"something"'; + if (schemaType === 'number') return '1'; + if (schemaType === 'integer') { + // Respect minimum if set + const min = propSchema.minimum as number | undefined; + return String(min ?? 1); + } + if (schemaType === 'boolean') return 'true'; + + // Union types like ['string', 'null'] + if (Array.isArray(schemaType)) { + const nonNull = schemaType.filter((t) => t !== 'null'); + if (nonNull.includes('string')) return '"something"'; + if (nonNull.includes('number') || nonNull.includes('integer')) return '1'; + if (nonNull.includes('boolean')) return 'true'; + } + + return '"something"'; +} + +/** + * Format a tools-call usage example for a tool, showing how to invoke it. + * Shows required params first, then fills with optional params up to 3 total. + */ +export function formatToolCallExample(tool: Tool, sessionName?: string): string | null { + const schema = tool.inputSchema as Record | undefined; + const properties = schema?.properties as Record> | undefined; + + if (!properties || Object.keys(properties).length === 0) { + // Tool takes no arguments — still show the simple call + const session = sessionName || '<@session>'; + return `${chalk.bold('Example:')}\n mcpc ${session} tools-call ${tool.name}`; + } + + const requiredNames = (schema?.required as string[]) || []; + const allNames = Object.keys(properties); + const requiredInOrder = allNames.filter((n) => requiredNames.includes(n)); + const optionalInOrder = allNames.filter((n) => !requiredNames.includes(n)); + + // Pick params: all required, then fill optional up to 3 total + const MAX_EXAMPLE_PARAMS = 3; + const params: string[] = [...requiredInOrder]; + if (params.length < MAX_EXAMPLE_PARAMS) { + const remaining = MAX_EXAMPLE_PARAMS - params.length; + params.push(...optionalInOrder.slice(0, remaining)); + } + + const session = sessionName || '<@session>'; + const argParts = params.map((name) => { + const val = exampleValue(properties[name] ?? {}); + return `${name}:=${val}`; + }); + + return `${chalk.bold('Example:')}\n mcpc ${session} tools-call ${tool.name} ${argParts.join(' ')}`; +} + /** * Format a list of resources with Markdown-like display */ diff --git a/test/unit/cli/output.test.ts b/test/unit/cli/output.test.ts index d36a8044..3ebfa4c7 100644 --- a/test/unit/cli/output.test.ts +++ b/test/unit/cli/output.test.ts @@ -55,6 +55,7 @@ import { formatSessionLine, formatHuman, logTarget, + formatToolCallExample, } from '../../../src/cli/output.js'; import type { Tool, @@ -828,6 +829,88 @@ describe('formatToolDetail', () => { }); }); +describe('formatToolCallExample', () => { + it('should show required params and fill optional up to 3', () => { + const tool: Tool = { + name: 'read_file', + inputSchema: { + type: 'object', + properties: { + path: { type: 'string' }, + encoding: { type: 'string', default: 'utf-8' }, + tail: { type: 'integer', minimum: 0 }, + }, + required: ['path'], + }, + }; + + const output = formatToolCallExample(tool, '@fs'); + expect(output).not.toBeNull(); + expect(output).toContain('tools-call read_file'); + expect(output).toContain('@fs'); + expect(output).toContain('path:="something"'); + expect(output).toContain('encoding:="utf-8"'); + expect(output).toContain('tail:=0'); + }); + + it('should use default values when available', () => { + const tool: Tool = { + name: 'search', + inputSchema: { + type: 'object', + properties: { + query: { type: 'string' }, + limit: { type: 'number', default: 10 }, + }, + required: ['query'], + }, + }; + + const output = formatToolCallExample(tool, '@test'); + expect(output).toContain('query:="something"'); + expect(output).toContain('limit:=10'); + }); + + it('should show example for tool with no parameters', () => { + const tool: Tool = { + name: 'ping', + inputSchema: { type: 'object', properties: {} }, + }; + + const output = formatToolCallExample(tool, '@srv'); + expect(output).not.toBeNull(); + expect(output).toContain('tools-call ping'); + // Should NOT contain any key:= pairs + expect(output).not.toContain(':='); + }); + + it('should use enum first value as example', () => { + const tool: Tool = { + name: 'set-mode', + inputSchema: { + type: 'object', + properties: { + mode: { type: 'string', enum: ['fast', 'slow', 'normal'] }, + }, + required: ['mode'], + }, + }; + + const output = formatToolCallExample(tool, '@s'); + expect(output).toContain('mode:="fast"'); + }); + + it('should use placeholder <@session> when no session name provided', () => { + const tool: Tool = { + name: 'test', + inputSchema: { type: 'object', properties: { a: { type: 'string' } } }, + }; + + const output = formatToolCallExample(tool); + expect(output).toContain('<@session>'); + }); +}); + describe('formatServerDetails', () => { it('should format server info with all features', () => { const details: ServerDetails = { From 1eaf4ee529b52b8827309f7660996864ee848e61 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 6 Apr 2026 10:11:20 +0000 Subject: [PATCH 02/19] Fix build:readme sed -i incompatibility on macOS macOS sed requires a backup extension argument with -i, unlike GNU sed. Use a temp file + mv instead for cross-platform compatibility. https://claude.ai/code/session_01JCmVVCvBbxPUrxy3BcgjCc --- CHANGELOG.md | 1 + scripts/update-readme.sh | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b26dbb7f..28a4511b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- `build:readme` script failing on macOS due to `sed -i` platform difference - Session incorrectly marked as `unauthorized` when access token expires but refresh token is still valid; bridge now attempts token refresh before giving up - "ESC to detach" hint now shows immediately in the spinner when using `--task`, instead of waiting for the server to return a task ID diff --git a/scripts/update-readme.sh b/scripts/update-readme.sh index 1e687f52..5187a0eb 100755 --- a/scripts/update-readme.sh +++ b/scripts/update-readme.sh @@ -66,7 +66,8 @@ echo " Done" echo "Updating table of contents..." doctoc "$README" --github --notitle --maxlevel 2 # Remove mcpc: entries from TOC (internal anchors that shouldn't be in TOC) -sed -i '/^- \[mcpc:/d' "$README" +# Use temp file for cross-platform compatibility (macOS sed -i requires different syntax) +sed '/^- \[mcpc:/d' "$README" > "$README.tmp" && mv "$README.tmp" "$README" echo " Done" # Step 3: Check for broken internal links From 1b6dcbec9cb12a4a24b945e8413e431c0658b5c4 Mon Sep 17 00:00:00 2001 From: Jan Curn Date: Mon, 6 Apr 2026 12:20:06 +0200 Subject: [PATCH 03/19] Writing --- src/cli/output.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/output.ts b/src/cli/output.ts index 8d96755e..00a7214f 100644 --- a/src/cli/output.ts +++ b/src/cli/output.ts @@ -616,7 +616,7 @@ export function formatToolCallExample(tool: Tool, sessionName?: string): string if (!properties || Object.keys(properties).length === 0) { // Tool takes no arguments — still show the simple call const session = sessionName || '<@session>'; - return `${chalk.bold('Example:')}\n mcpc ${session} tools-call ${tool.name}`; + return `${chalk.bold('Call example:')}\n mcpc ${session} tools-call ${tool.name}`; } const requiredNames = (schema?.required as string[]) || []; @@ -638,7 +638,7 @@ export function formatToolCallExample(tool: Tool, sessionName?: string): string return `${name}:=${val}`; }); - return `${chalk.bold('Example:')}\n mcpc ${session} tools-call ${tool.name} ${argParts.join(' ')}`; + return `${chalk.bold('Call example:')}\n mcpc ${session} tools-call ${tool.name} ${argParts.join(' ')}`; } /** From ef1a4d5d24ec05dad699a64c4ce2a0f6f577dbc4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 6 Apr 2026 09:04:26 +0000 Subject: [PATCH 04/19] Add --json output docs to help text and tools-call example to tools-get Document the JSON output shape (MCP object types) in --help for all commands that support --json, so AI agents know what to expect when scripting. tools-get now shows an example tools-call command with placeholder arguments derived from the tool's schema (required params first, filling optional up to 3). https://claude.ai/code/session_01JCmVVCvBbxPUrxy3BcgjCc --- CHANGELOG.md | 5 ++ src/cli/commands/tools.ts | 12 ++++- src/cli/index.ts | 93 ++++++++++++++++++++++++++++++++++++ src/cli/output.ts | 73 ++++++++++++++++++++++++++++ test/unit/cli/output.test.ts | 83 ++++++++++++++++++++++++++++++++ 5 files changed, 265 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af2e8b00..d3b03fe3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `--json` output documentation in `--help` for all commands, describing the MCP object shape returned +- `tools-get` now shows an example `tools-call` command with placeholder arguments based on the tool's schema + ## [0.2.4] - 2026-04-07 ### Security diff --git a/src/cli/commands/tools.ts b/src/cli/commands/tools.ts index fe7f9547..9b7173ba 100644 --- a/src/cli/commands/tools.ts +++ b/src/cli/commands/tools.ts @@ -4,7 +4,13 @@ import ora from 'ora'; import chalk from 'chalk'; -import { formatOutput, formatToolDetail, formatSuccess, formatWarning } from '../output.js'; +import { + formatOutput, + formatToolDetail, + formatToolCallExample, + formatSuccess, + formatWarning, +} from '../output.js'; import { ClientError } from '../../lib/errors.js'; import type { CommandOptions, TaskUpdate } from '../../lib/types.js'; import { withMcpClient } from '../helpers.js'; @@ -85,6 +91,10 @@ export async function getTool( if (options.outputMode === 'human') { console.log(formatToolDetail(tool)); + const example = formatToolCallExample(tool, target); + if (example) { + console.log('\n' + example + '\n'); + } } else { console.log(formatOutput(tool, 'json')); } diff --git a/src/cli/index.ts b/src/cli/index.ts index 6b1c9be4..bb37cc8c 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -402,6 +402,8 @@ Full docs: ${docsUrl}` ${chalk.bold('Server formats:')} mcp.apify.com Remote HTTP server (https:// added automatically) ~/.vscode/mcp.json:puppeteer Config file entry (file:entry) + +${chalk.bold('JSON output (--json):')}\n MCP InitializeResult: { protocolVersion, capabilities, serverInfo, instructions?, tools? } ` ) .action(async (server, sessionName, opts, command) => { @@ -460,6 +462,10 @@ ${chalk.bold('Server formats:')} .command('close [@session]') .usage('<@session>') .description('Close a session') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n { sessionName, closed: true }\n` + ) .action(async (sessionName, _opts, command) => { if (!sessionName) { throw new ClientError('Missing required argument: @session\n\nExample: mcpc close @myapp'); @@ -508,6 +514,10 @@ ${chalk.bold('Server formats:')} '--client-secret ', 'OAuth client secret (for servers without dynamic client registration)' ) + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n { profile, serverUrl, scopes }\n` + ) .action(async (server, opts, command) => { if (!server) { throw new ClientError( @@ -529,6 +539,10 @@ ${chalk.bold('Server formats:')} .usage('') .description('Delete an OAuth profile for a server') .option('--profile ', 'Profile name (default: "default")') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n { profile, serverUrl, deleted: true, affectedSessions }\n` + ) .action(async (server, opts, command) => { if (!server) { throw new ClientError( @@ -555,6 +569,9 @@ ${chalk.bold('Resources:')} all Remove all of the above Without arguments, performs safe cleanup of stale data only. + +${chalk.bold('JSON output (--json):')} + { crashedBridges, expiredSessions, orphanedBridgeLogs, sessions, profiles, logs } ` ) .action(async (resources: string[], _opts, command) => { @@ -606,6 +623,9 @@ ${chalk.bold('Examples:')} mcpc @apify grep "actor" Search within a single session mcpc grep "file" --json JSON output for scripting mcpc grep "actor" -m 5 Show at most 5 results + +${chalk.bold('JSON output (--json):')} + [{ sessionName, tools?: Tool[], resources?: Resource[], prompts?: Prompt[], instructions?: string[] }] ` ) .action(async (pattern, opts, command) => { @@ -703,6 +723,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('help') .description('Show server instructions and available capabilities') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n MCP InitializeResult: { protocolVersion, capabilities, serverInfo, instructions?, tools? }\n` + ) .action(async (_options, command) => { await sessions.showHelp(session, getOptionsFromCommand(command)); }); @@ -736,6 +760,10 @@ function registerSessionCommands(program: Command, session: string): void { .command('tools') .description('List available tools (shorthand for tools-list)') .option('--full', 'Show full tool details including complete input schema') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Tool objects: [{ name, description?, inputSchema, annotations? }, ...]\n` + ) .action(async (_options, command) => { await tools.listTools(session, getOptionsFromCommand(command)); }); @@ -744,6 +772,10 @@ function registerSessionCommands(program: Command, session: string): void { .command('tools-list') .description('List available tools') .option('--full', 'Show full tool details including complete input schema') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Tool objects: [{ name, description?, inputSchema, annotations? }, ...]\n` + ) .action(async (_options, command) => { await tools.listTools(session, getOptionsFromCommand(command)); }); @@ -751,6 +783,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('tools-get ') .description('Get information about a specific tool') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n MCP Tool object: { name, description?, inputSchema, annotations? }\n` + ) .action(async (name, _options, command) => { await tools.getTool(session, name, getOptionsFromCommand(command)); }); @@ -760,6 +796,10 @@ function registerSessionCommands(program: Command, session: string): void { .description('Call a tool with arguments (key:=value pairs or JSON)') .option('--task', 'Use task execution (experimental)') .option('--detach', 'Start task and return immediately with task ID (implies --task)') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n MCP CallToolResult: { content: [{ type, text?, ... }], isError?, structuredContent? }\n` + ) .action(async (name, args, options, command) => { await tools.callTool(session, name, { args, @@ -773,6 +813,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('tasks-list') .description('List active tasks') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n { tasks: [{ taskId, status, statusMessage?, createdAt?, lastUpdatedAt? }, ...] }\n` + ) .action(async (_options, command) => { await tasks.listTasks(session, getOptionsFromCommand(command)); }); @@ -780,6 +824,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('tasks-get ') .description('Get status of a specific task') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n MCP Task object: { taskId, status, statusMessage?, createdAt?, lastUpdatedAt? }\n` + ) .action(async (taskId, _options, command) => { await tasks.getTask(session, taskId, getOptionsFromCommand(command)); }); @@ -787,6 +835,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('tasks-cancel ') .description('Cancel a running task') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n MCP Task object: { taskId, status, statusMessage? }\n` + ) .action(async (taskId, _options, command) => { await tasks.cancelTask(session, taskId, getOptionsFromCommand(command)); }); @@ -795,6 +847,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources') .description('List available resources (shorthand for resources-list)') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Resource objects: [{ uri, name?, description?, mimeType? }, ...]\n` + ) .action(async (_options, command) => { await resources.listResources(session, getOptionsFromCommand(command)); }); @@ -802,6 +858,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources-list') .description('List available resources') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Resource objects: [{ uri, name?, description?, mimeType? }, ...]\n` + ) .action(async (_options, command) => { await resources.listResources(session, getOptionsFromCommand(command)); }); @@ -811,6 +871,10 @@ function registerSessionCommands(program: Command, session: string): void { .description('Get a resource by URI') .option('-o, --output ', 'Write resource to file') .option('--max-size ', 'Maximum resource size in bytes') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n MCP ReadResourceResult: { contents: [{ uri, mimeType?, text? | blob? }] }\n` + ) .action(async (uri, options, command) => { await resources.getResource(session, uri, { output: options.output, @@ -822,6 +886,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources-subscribe ') .description('Subscribe to resource updates') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n { subscribed: true, uri: string }\n` + ) .action(async (uri, _options, command) => { await resources.subscribeResource(session, uri, getOptionsFromCommand(command)); }); @@ -829,6 +897,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources-unsubscribe ') .description('Unsubscribe from resource updates') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n { unsubscribed: true, uri: string }\n` + ) .action(async (uri, _options, command) => { await resources.unsubscribeResource(session, uri, getOptionsFromCommand(command)); }); @@ -836,6 +908,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources-templates-list') .description('List available resource templates') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n Array of MCP ResourceTemplate objects: [{ uriTemplate, name?, description?, mimeType? }, ...]\n` + ) .action(async (_options, command) => { await resources.listResourceTemplates(session, getOptionsFromCommand(command)); }); @@ -844,6 +920,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('prompts') .description('List available prompts (shorthand for prompts-list)') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Prompt objects: [{ name, description?, arguments?: [{ name, description?, required? }] }, ...]\n` + ) .action(async (_options, command) => { await prompts.listPrompts(session, getOptionsFromCommand(command)); }); @@ -851,6 +931,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('prompts-list') .description('List available prompts') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Prompt objects: [{ name, description?, arguments?: [{ name, description?, required? }] }, ...]\n` + ) .action(async (_options, command) => { await prompts.listPrompts(session, getOptionsFromCommand(command)); }); @@ -858,6 +942,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('prompts-get [args...]') .description('Get a prompt by name with arguments (key:=value pairs or JSON)') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n MCP GetPromptResult: { description?, messages: [{ role, content: { type, text?, ... } }] }\n` + ) .action(async (name, args, _options, command) => { await prompts.getPrompt(session, name, { args, @@ -871,6 +959,7 @@ function registerSessionCommands(program: Command, session: string): void { .description( 'Set server logging level (debug, info, notice, warning, error, critical, alert, emergency)' ) + .addHelpText('after', `\n${chalk.bold('JSON output (--json):')}\n { level: string }\n`) .action(async (level, _options, command) => { await logging.setLogLevel(session, level, getOptionsFromCommand(command)); }); @@ -879,6 +968,10 @@ function registerSessionCommands(program: Command, session: string): void { program .command('ping') .description('Ping the MCP server to check if it is alive') + .addHelpText( + 'after', + `\n${chalk.bold('JSON output (--json):')}\n { success: true, durationMs: number }\n` + ) .action(async (_options, command) => { await utilities.ping(session, getOptionsFromCommand(command)); }); diff --git a/src/cli/output.ts b/src/cli/output.ts index ae6d8ac1..8d96755e 100644 --- a/src/cli/output.ts +++ b/src/cli/output.ts @@ -568,6 +568,79 @@ export function formatToolDetail(tool: Tool): string { return lines.join('\n'); } +/** + * Generate an example placeholder value for a JSON Schema property. + * Uses the default value if available, otherwise a reasonable placeholder. + */ +function exampleValue(propSchema: Record): string { + // Use default value if available + if (propSchema.default !== undefined) { + return JSON.stringify(propSchema.default); + } + + // Use first enum value if available + if (propSchema.enum && Array.isArray(propSchema.enum) && propSchema.enum.length > 0) { + return JSON.stringify(propSchema.enum[0]); + } + + const schemaType = propSchema.type; + + if (schemaType === 'string') return '"something"'; + if (schemaType === 'number') return '1'; + if (schemaType === 'integer') { + // Respect minimum if set + const min = propSchema.minimum as number | undefined; + return String(min ?? 1); + } + if (schemaType === 'boolean') return 'true'; + + // Union types like ['string', 'null'] + if (Array.isArray(schemaType)) { + const nonNull = schemaType.filter((t) => t !== 'null'); + if (nonNull.includes('string')) return '"something"'; + if (nonNull.includes('number') || nonNull.includes('integer')) return '1'; + if (nonNull.includes('boolean')) return 'true'; + } + + return '"something"'; +} + +/** + * Format a tools-call usage example for a tool, showing how to invoke it. + * Shows required params first, then fills with optional params up to 3 total. + */ +export function formatToolCallExample(tool: Tool, sessionName?: string): string | null { + const schema = tool.inputSchema as Record | undefined; + const properties = schema?.properties as Record> | undefined; + + if (!properties || Object.keys(properties).length === 0) { + // Tool takes no arguments — still show the simple call + const session = sessionName || '<@session>'; + return `${chalk.bold('Example:')}\n mcpc ${session} tools-call ${tool.name}`; + } + + const requiredNames = (schema?.required as string[]) || []; + const allNames = Object.keys(properties); + const requiredInOrder = allNames.filter((n) => requiredNames.includes(n)); + const optionalInOrder = allNames.filter((n) => !requiredNames.includes(n)); + + // Pick params: all required, then fill optional up to 3 total + const MAX_EXAMPLE_PARAMS = 3; + const params: string[] = [...requiredInOrder]; + if (params.length < MAX_EXAMPLE_PARAMS) { + const remaining = MAX_EXAMPLE_PARAMS - params.length; + params.push(...optionalInOrder.slice(0, remaining)); + } + + const session = sessionName || '<@session>'; + const argParts = params.map((name) => { + const val = exampleValue(properties[name] ?? {}); + return `${name}:=${val}`; + }); + + return `${chalk.bold('Example:')}\n mcpc ${session} tools-call ${tool.name} ${argParts.join(' ')}`; +} + /** * Format a list of resources with Markdown-like display */ diff --git a/test/unit/cli/output.test.ts b/test/unit/cli/output.test.ts index d36a8044..3ebfa4c7 100644 --- a/test/unit/cli/output.test.ts +++ b/test/unit/cli/output.test.ts @@ -55,6 +55,7 @@ import { formatSessionLine, formatHuman, logTarget, + formatToolCallExample, } from '../../../src/cli/output.js'; import type { Tool, @@ -828,6 +829,88 @@ describe('formatToolDetail', () => { }); }); +describe('formatToolCallExample', () => { + it('should show required params and fill optional up to 3', () => { + const tool: Tool = { + name: 'read_file', + inputSchema: { + type: 'object', + properties: { + path: { type: 'string' }, + encoding: { type: 'string', default: 'utf-8' }, + tail: { type: 'integer', minimum: 0 }, + }, + required: ['path'], + }, + }; + + const output = formatToolCallExample(tool, '@fs'); + expect(output).not.toBeNull(); + expect(output).toContain('tools-call read_file'); + expect(output).toContain('@fs'); + expect(output).toContain('path:="something"'); + expect(output).toContain('encoding:="utf-8"'); + expect(output).toContain('tail:=0'); + }); + + it('should use default values when available', () => { + const tool: Tool = { + name: 'search', + inputSchema: { + type: 'object', + properties: { + query: { type: 'string' }, + limit: { type: 'number', default: 10 }, + }, + required: ['query'], + }, + }; + + const output = formatToolCallExample(tool, '@test'); + expect(output).toContain('query:="something"'); + expect(output).toContain('limit:=10'); + }); + + it('should show example for tool with no parameters', () => { + const tool: Tool = { + name: 'ping', + inputSchema: { type: 'object', properties: {} }, + }; + + const output = formatToolCallExample(tool, '@srv'); + expect(output).not.toBeNull(); + expect(output).toContain('tools-call ping'); + // Should NOT contain any key:= pairs + expect(output).not.toContain(':='); + }); + + it('should use enum first value as example', () => { + const tool: Tool = { + name: 'set-mode', + inputSchema: { + type: 'object', + properties: { + mode: { type: 'string', enum: ['fast', 'slow', 'normal'] }, + }, + required: ['mode'], + }, + }; + + const output = formatToolCallExample(tool, '@s'); + expect(output).toContain('mode:="fast"'); + }); + + it('should use placeholder <@session> when no session name provided', () => { + const tool: Tool = { + name: 'test', + inputSchema: { type: 'object', properties: { a: { type: 'string' } } }, + }; + + const output = formatToolCallExample(tool); + expect(output).toContain('<@session>'); + }); +}); + describe('formatServerDetails', () => { it('should format server info with all features', () => { const details: ServerDetails = { From e3b97dfa13b363bb702adcce2db76f0ff3255378 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 6 Apr 2026 10:11:20 +0000 Subject: [PATCH 05/19] Fix build:readme sed -i incompatibility on macOS macOS sed requires a backup extension argument with -i, unlike GNU sed. Use a temp file + mv instead for cross-platform compatibility. https://claude.ai/code/session_01JCmVVCvBbxPUrxy3BcgjCc --- CHANGELOG.md | 1 + scripts/update-readme.sh | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3b03fe3..610b67aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Bridge ignores stored OAuth access token when no refresh token is provided; servers that don't issue refresh tokens now work correctly by using the access token as a static Bearer header +- `build:readme` script failing on macOS due to `sed -i` platform difference - Session incorrectly marked as `unauthorized` when access token expires but refresh token is still valid; bridge now attempts token refresh before giving up - "ESC to detach" hint now shows immediately in the spinner when using `--task`, instead of waiting for the server to return a task ID diff --git a/scripts/update-readme.sh b/scripts/update-readme.sh index 1e687f52..5187a0eb 100755 --- a/scripts/update-readme.sh +++ b/scripts/update-readme.sh @@ -66,7 +66,8 @@ echo " Done" echo "Updating table of contents..." doctoc "$README" --github --notitle --maxlevel 2 # Remove mcpc: entries from TOC (internal anchors that shouldn't be in TOC) -sed -i '/^- \[mcpc:/d' "$README" +# Use temp file for cross-platform compatibility (macOS sed -i requires different syntax) +sed '/^- \[mcpc:/d' "$README" > "$README.tmp" && mv "$README.tmp" "$README" echo " Done" # Step 3: Check for broken internal links From e21ba2922f964fc3354b29663c78427bc1b2ae32 Mon Sep 17 00:00:00 2001 From: Jan Curn Date: Mon, 6 Apr 2026 12:20:06 +0200 Subject: [PATCH 06/19] Writing --- src/cli/output.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/output.ts b/src/cli/output.ts index 8d96755e..00a7214f 100644 --- a/src/cli/output.ts +++ b/src/cli/output.ts @@ -616,7 +616,7 @@ export function formatToolCallExample(tool: Tool, sessionName?: string): string if (!properties || Object.keys(properties).length === 0) { // Tool takes no arguments — still show the simple call const session = sessionName || '<@session>'; - return `${chalk.bold('Example:')}\n mcpc ${session} tools-call ${tool.name}`; + return `${chalk.bold('Call example:')}\n mcpc ${session} tools-call ${tool.name}`; } const requiredNames = (schema?.required as string[]) || []; @@ -638,7 +638,7 @@ export function formatToolCallExample(tool: Tool, sessionName?: string): string return `${name}:=${val}`; }); - return `${chalk.bold('Example:')}\n mcpc ${session} tools-call ${tool.name} ${argParts.join(' ')}`; + return `${chalk.bold('Call example:')}\n mcpc ${session} tools-call ${tool.name} ${argParts.join(' ')}`; } /** From 7760c673abd6e08d8a6d2b56380d43143516eef4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 6 Apr 2026 10:23:48 +0000 Subject: [PATCH 07/19] Show task support hints in tools-get and tools-call example Extract formatToolHints() combining annotations + task support, used by both formatToolLine and formatToolDetail. tools-get now shows task:required or task:optional in the hints bracket. The tools-call example includes --task or [--task] accordingly. https://claude.ai/code/session_01JCmVVCvBbxPUrxy3BcgjCc --- src/cli/output.ts | 60 ++++++++++++++++++++++++++---------- test/unit/cli/output.test.ts | 60 ++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 17 deletions(-) diff --git a/src/cli/output.ts b/src/cli/output.ts index 00a7214f..2afdc4f2 100644 --- a/src/cli/output.ts +++ b/src/cli/output.ts @@ -237,6 +237,31 @@ export function formatToolAnnotations(annotations: Tool['annotations']): string return parts.length > 0 ? parts.join(', ') : null; } +/** + * Get the task support mode for a tool ('required', 'optional', or undefined) + */ +export function getToolTaskSupport(tool: Tool): string | undefined { + const toolAny = tool as Record; + const execution = toolAny.execution as Record | undefined; + return execution?.taskSupport as string | undefined; +} + +/** + * Format tool hints: annotations + task support mode. + * Returns a string like "destructive, open-world, task:required" or null if empty. + */ +export function formatToolHints(tool: Tool): string | null { + const parts: string[] = []; + + const annotationsStr = formatToolAnnotations(tool.annotations); + if (annotationsStr) parts.push(annotationsStr); + + const taskSupport = getToolTaskSupport(tool); + if (taskSupport) parts.push(`task:${taskSupport}`); + + return parts.length > 0 ? parts.join(', ') : null; +} + /** * Convert a JSON Schema type definition to a simplified type string * e.g., { type: 'string' } -> 'string' @@ -466,15 +491,8 @@ export function formatToolParamsInline(schema: Record): string export function formatToolLine(tool: Tool): string { const bullet = chalk.dim('*'); const params = formatToolParamsInline(tool.inputSchema as Record); - const parts: string[] = []; - const annotationsStr = formatToolAnnotations(tool.annotations); - if (annotationsStr) parts.push(annotationsStr); - // Show task execution mode - const toolAny = tool as Record; - const execution = toolAny.execution as Record | undefined; - const taskSupport = execution?.taskSupport as string | undefined; - if (taskSupport) parts.push(`task:${taskSupport}`); - const suffix = parts.length > 0 ? ` ${chalk.gray(`[${parts.join(', ')}]`)}` : ''; + const hintsStr = formatToolHints(tool); + const suffix = hintsStr ? ` ${chalk.gray(`[${hintsStr}]`)}` : ''; return `${bullet} ${grayBacktick()}${chalk.cyan(tool.name)} ${params}${grayBacktick()}${suffix}`; } @@ -536,10 +554,10 @@ export function formatToolDetail(tool: Tool): string { lines.push(chalk.bold(`# ${title}`)); } - // Tool header: Tool: `name` [annotations] - const annotationsStr = formatToolAnnotations(tool.annotations); - const annotationsSuffix = annotationsStr ? ` ${chalk.gray(`[${annotationsStr}]`)}` : ''; - lines.push(`${chalk.bold('Tool:')} ${inBackticks(tool.name)}${annotationsSuffix}`); + // Tool header: Tool: `name` [hints] + const hintsStr = formatToolHints(tool); + const hintsSuffix = hintsStr ? ` ${chalk.gray(`[${hintsStr}]`)}` : ''; + lines.push(`${chalk.bold('Tool:')} ${inBackticks(tool.name)}${hintsSuffix}`); // Input args lines.push(''); @@ -612,11 +630,19 @@ function exampleValue(propSchema: Record): string { export function formatToolCallExample(tool: Tool, sessionName?: string): string | null { const schema = tool.inputSchema as Record | undefined; const properties = schema?.properties as Record> | undefined; + const session = sessionName || '<@session>'; + + // Build --task flag based on task support + const taskSupport = getToolTaskSupport(tool); + const taskFlag = + taskSupport === 'required' ? ' --task' : taskSupport === 'optional' ? ' [--task]' : ''; + + const bullet = chalk.dim('*'); if (!properties || Object.keys(properties).length === 0) { // Tool takes no arguments — still show the simple call - const session = sessionName || '<@session>'; - return `${chalk.bold('Call example:')}\n mcpc ${session} tools-call ${tool.name}`; + const cmd = `mcpc ${session} tools-call ${tool.name}${taskFlag}`; + return `${chalk.bold('Call example:')}\n${bullet} ${grayBacktick()}${chalk.cyan(cmd)}${grayBacktick()}`; } const requiredNames = (schema?.required as string[]) || []; @@ -632,13 +658,13 @@ export function formatToolCallExample(tool: Tool, sessionName?: string): string params.push(...optionalInOrder.slice(0, remaining)); } - const session = sessionName || '<@session>'; const argParts = params.map((name) => { const val = exampleValue(properties[name] ?? {}); return `${name}:=${val}`; }); - return `${chalk.bold('Call example:')}\n mcpc ${session} tools-call ${tool.name} ${argParts.join(' ')}`; + const cmd = `mcpc ${session} tools-call ${tool.name} ${argParts.join(' ')}${taskFlag}`; + return `${chalk.bold('Call example:')}\n${bullet} ${grayBacktick()}${chalk.cyan(cmd)}${grayBacktick()}`; } /** diff --git a/test/unit/cli/output.test.ts b/test/unit/cli/output.test.ts index 3ebfa4c7..ae853ece 100644 --- a/test/unit/cli/output.test.ts +++ b/test/unit/cli/output.test.ts @@ -56,6 +56,7 @@ import { formatHuman, logTarget, formatToolCallExample, + formatToolHints, } from '../../../src/cli/output.js'; import type { Tool, @@ -909,6 +910,65 @@ describe('formatToolCallExample', () => { const output = formatToolCallExample(tool); expect(output).toContain('<@session>'); }); + + it('should include --task for task:required tools', () => { + const tool = { + name: 'long-run', + inputSchema: { type: 'object', properties: { q: { type: 'string' } }, required: ['q'] }, + execution: { taskSupport: 'required' }, + } as unknown as Tool; + + const output = formatToolCallExample(tool, '@s'); + expect(output).toContain('--task'); + expect(output).not.toContain('[--task]'); + }); + + it('should include [--task] for task:optional tools', () => { + const tool = { + name: 'maybe-async', + inputSchema: { type: 'object', properties: {} }, + execution: { taskSupport: 'optional' }, + } as unknown as Tool; + + const output = formatToolCallExample(tool, '@s'); + expect(output).toContain('[--task]'); + }); +}); + +describe('formatToolHints', () => { + it('should combine annotations and task support', () => { + const tool = { + name: 'test', + inputSchema: { type: 'object', properties: {} }, + annotations: { destructiveHint: true, openWorldHint: true }, + execution: { taskSupport: 'required' }, + } as unknown as Tool; + + const hints = formatToolHints(tool); + expect(hints).toContain('destructive'); + expect(hints).toContain('open-world'); + expect(hints).toContain('task:required'); + }); + + it('should return null when no annotations and no task support', () => { + const tool: Tool = { + name: 'plain', + inputSchema: { type: 'object', properties: {} }, + }; + + expect(formatToolHints(tool)).toBeNull(); + }); + + it('should show only task support when no annotations', () => { + const tool = { + name: 'async-only', + inputSchema: { type: 'object', properties: {} }, + execution: { taskSupport: 'optional' }, + } as unknown as Tool; + + const hints = formatToolHints(tool); + expect(hints).toBe('task:optional'); + }); }); describe('formatServerDetails', () => { From 47a3c5d7fa5be6526bdc6a7d680af52c38e56dd4 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Apr 2026 11:47:00 +0000 Subject: [PATCH 08/19] Fix README help block and move changelog entries after v0.2.4 release The v0.2.4 release emptied the README help code block and moved all changelog entries to the release section. Regenerate the help block and move unreleased entries to the new [Unreleased] section. https://claude.ai/code/session_01JCmVVCvBbxPUrxy3BcgjCc --- CHANGELOG.md | 5 ++++- README.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 610b67aa..c866c5fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `--json` output documentation in `--help` for all commands, describing the MCP object shape returned - `tools-get` now shows an example `tools-call` command with placeholder arguments based on the tool's schema +### Fixed + +- `build:readme` script failing on macOS due to `sed -i` platform difference + ## [0.2.4] - 2026-04-07 ### Security @@ -24,7 +28,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Bridge ignores stored OAuth access token when no refresh token is provided; servers that don't issue refresh tokens now work correctly by using the access token as a static Bearer header -- `build:readme` script failing on macOS due to `sed -i` platform difference - Session incorrectly marked as `unauthorized` when access token expires but refresh token is still valid; bridge now attempts token refresh before giving up - "ESC to detach" hint now shows immediately in the spinner when using `--task`, instead of waiting for the server to return a task ID diff --git a/README.md b/README.md index 95d90980..a3d943bd 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,53 @@ mcpc @fs tools-list ``` +Usage: mcpc [<@session>] [] [options] + +Universal command-line client for the Model Context Protocol (MCP). + +Commands: + connect <@session> Connect to an MCP server and start a new named @session + close <@session> Close a session + restart <@session> Restart a session (losing all state) + shell <@session> Open interactive shell for a session + login Interactively login to a server using OAuth and save profile + logout Delete an OAuth profile for a server + clean [resources...] Clean up mcpc data (sessions, profiles, logs, all) + grep Search tools and instructions across all active sessions + x402 [subcommand] [args...] Configure an x402 payment wallet (EXPERIMENTAL) + help [command] [subcommand] Show help for a specific command + +Options: + -j, --json Output in JSON format for scripting + --verbose Enable debug logging + --profile OAuth profile for the server ("default" if not provided) + --schema Validate tool/prompt schema against expected schema + --schema-mode Schema validation mode: strict, compatible (default), ignore + --timeout Request timeout in seconds (default: 300) + --insecure Skip TLS certificate verification (for self-signed certs) + -v, --version Output the version number + -h, --help Display help + +MCP session commands (after connecting): + <@session> Show MCP server info, capabilities, and tools + <@session> grep Search tools and instructions + <@session> tools-list List all server tools + <@session> tools-get Get tool details and schema + <@session> tools-call [arg:=val ... | | prompts-list + <@session> prompts-get [arg:=val ... | | resources-list + <@session> resources-read + <@session> resources-subscribe + <@session> resources-unsubscribe + <@session> resources-templates-list + <@session> tasks-list + <@session> tasks-get + <@session> tasks-cancel + <@session> logging-set-level + <@session> ping + +Run "mcpc" without arguments to show active sessions and OAuth profiles. ``` ### General actions From 963825aa79fb8d169dc20cee12c3f8fcc4da26b7 Mon Sep 17 00:00:00 2001 From: Jan Curn Date: Tue, 7 Apr 2026 14:38:17 +0200 Subject: [PATCH 09/19] Writing --- docs/TODOs.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/TODOs.md b/docs/TODOs.md index 492582bc..41d5ca44 100644 --- a/docs/TODOs.md +++ b/docs/TODOs.md @@ -30,6 +30,8 @@ - Make "mcpc connect mcp.apify.com" work without @session, and generate session name on best effort basis (e.g. use the main hostname without TLD + suffix) +- and finally, "mcpc connect" should connect to all server configs found - see https://www.withone.ai/docs/cli#mcp-server-installation + - mcpc @apify tools-get fetch-actor-details => should print also "object" properties in human mode - mcpc @apify tools-call xxx --help / "mcpc @apify/xxx --help" should print tools-get + command info From facdbb0415a877ccfddc9dd3a800f6f1cabfa9f4 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Apr 2026 13:05:04 +0000 Subject: [PATCH 10/19] Fix --help routing for session sub-commands - mcpc @session cmd --help now shows command-specific help instead of the top-level help (was intercepted too early in main()) - mcpc @session --help now shows session command list - Capitalized "Display help" on all sub-commands (was lowercase default) - Added bold styling to Usage: and other titles in session command help - mcpc help now uses the same styled session program https://claude.ai/code/session_01JCmVVCvBbxPUrxy3BcgjCc --- src/cli/index.ts | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/cli/index.ts b/src/cli/index.ts index bb37cc8c..e71e2e94 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -153,17 +153,23 @@ async function main(): Promise { // Check for help flag // x402 has its own Commander program with full subcommand help, so pass --help through + // Session commands (@name ...) also handle --help via their own Commander program if (args.includes('--help') || args.includes('-h')) { - if (args.includes('x402')) { + // Check if this is a session command — let it fall through to session handling + const hasSessionArg = args.some((a) => a.startsWith('@') && !a.startsWith('--')); + if (hasSessionArg) { + // Fall through — handleSessionCommands will parse --help via Commander + } else if (args.includes('x402')) { const x402Index = args.indexOf('x402'); const x402Args = args.slice(x402Index + 1); await handleX402Command(x402Args); await closeFileLogger(); return; + } else { + const program = createTopLevelProgram(); + await program.parseAsync(process.argv); + return; } - const program = createTopLevelProgram(); - await program.parseAsync(process.argv); - return; } // Validate all options are known (before any processing) @@ -684,9 +690,11 @@ ${chalk.bold('JSON output (--json):')} } // Check session subcommands - const dummyProgram = new Command(); - dummyProgram.name('mcpc <@session>'); - registerSessionCommands(dummyProgram, '@dummy'); + const dummyProgram = createSessionProgram(); + registerSessionCommands(dummyProgram, '<@session>'); + for (const cmd of dummyProgram.commands) { + cmd.helpOption('-h, --help', 'Display help'); + } const sessionCmd = dummyProgram.commands.find( (c) => c.name() === cmdName || c.aliases().includes(cmdName) ); @@ -1017,6 +1025,12 @@ function createSessionProgram(): Command { getErrHelpWidth: () => 100, }); + // Match the top-level help styling: bold titles, cyan subcommand text + program.configureHelp({ + styleTitle: (str) => chalk.bold(str), + styleSubcommandText: (str) => chalk.cyan(str), + }); + program .name('mcpc <@session>') .helpOption('-h, --help', 'Display help') @@ -1035,8 +1049,9 @@ function createSessionProgram(): Command { * Handle commands for a session target (@name) */ async function handleSessionCommands(session: string, args: string[]): Promise { - // Check if no subcommand provided - show server info - if (!hasSubcommand(args)) { + // Check if no subcommand provided - show server info (unless --help is requested) + const argsSlice = args.slice(2); + if (!hasSubcommand(args) && !argsSlice.includes('--help') && !argsSlice.includes('-h')) { const options = extractOptions(args); if (options.verbose) setVerbose(true); if (options.json) setJsonMode(true); @@ -1054,6 +1069,11 @@ async function handleSessionCommands(session: string, args: string[]): Promise Date: Tue, 7 Apr 2026 17:57:45 +0200 Subject: [PATCH 11/19] Writing --- docs/TODOs.md | 3 +++ src/cli/index.ts | 46 +++++++++++++++++++++++----------------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/docs/TODOs.md b/docs/TODOs.md index 41d5ca44..08544c43 100644 --- a/docs/TODOs.md +++ b/docs/TODOs.md @@ -1,6 +1,9 @@ # TODOs +- "mcpc help tools-call" - show more info how to pass args, including stdio pipe and JSON. Maybe add short examples. +Make "mcpc @apify grep --help" and "mcpc grep --help" more consistent with info what they print. +The former should provide the --json example. ## NEW diff --git a/src/cli/index.ts b/src/cli/index.ts index e71e2e94..deac9b74 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -730,7 +730,7 @@ function registerSessionCommands(program: Command, session: string): void { // Help command program .command('help') - .description('Show server instructions and available capabilities') + .description('Show MCP server instructions, capabilities, and tools.') .addHelpText( 'after', `\n${chalk.bold('JSON output (--json):')}\n MCP InitializeResult: { protocolVersion, capabilities, serverInfo, instructions?, tools? }\n` @@ -742,7 +742,7 @@ function registerSessionCommands(program: Command, session: string): void { // Shell command program .command('shell') - .description('Interactive shell for the session') + .description('Launch interactive MCP shell for the session.') .action(async () => { await sessions.openShell(session); }); @@ -750,7 +750,7 @@ function registerSessionCommands(program: Command, session: string): void { // Close command program .command('close', { hidden: true }) - .description('Close the session') + .description('Close the MCP server session.') .action(async (_options, command) => { await sessions.closeSession(session, getOptionsFromCommand(command)); }); @@ -758,7 +758,7 @@ function registerSessionCommands(program: Command, session: string): void { // Restart command program .command('restart') - .description('Restart the session (stop and start the bridge)') + .description('Restart the MCP server session (losing all state).') .action(async (_options, command) => { await sessions.restartSession(session, getOptionsFromCommand(command)); }); @@ -766,7 +766,7 @@ function registerSessionCommands(program: Command, session: string): void { // Tools commands program .command('tools') - .description('List available tools (shorthand for tools-list)') + .description('List all available MCP server tools (shorthand for tools-list).') .option('--full', 'Show full tool details including complete input schema') .addHelpText( 'after', @@ -778,7 +778,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('tools-list') - .description('List available tools') + .description('List all available MCP server tools.') .option('--full', 'Show full tool details including complete input schema') .addHelpText( 'after', @@ -790,7 +790,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('tools-get ') - .description('Get information about a specific tool') + .description('Get full details and schema for a specific MCP server tool.') .addHelpText( 'after', `\n${chalk.bold('JSON output (--json):')}\n MCP Tool object: { name, description?, inputSchema, annotations? }\n` @@ -801,7 +801,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('tools-call [args...]') - .description('Call a tool with arguments (key:=value pairs or JSON)') + .description('Call an MCP server tool with arguments (key:=value pairs or JSON)') .option('--task', 'Use task execution (experimental)') .option('--detach', 'Start task and return immediately with task ID (implies --task)') .addHelpText( @@ -820,7 +820,7 @@ function registerSessionCommands(program: Command, session: string): void { // Tasks commands program .command('tasks-list') - .description('List active tasks') + .description('List all MCP server tasks.') .addHelpText( 'after', `\n${chalk.bold('JSON output (--json):')}\n { tasks: [{ taskId, status, statusMessage?, createdAt?, lastUpdatedAt? }, ...] }\n` @@ -831,7 +831,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('tasks-get ') - .description('Get status of a specific task') + .description('Get status of a specific MCP task.') .addHelpText( 'after', `\n${chalk.bold('JSON output (--json):')}\n MCP Task object: { taskId, status, statusMessage?, createdAt?, lastUpdatedAt? }\n` @@ -842,7 +842,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('tasks-cancel ') - .description('Cancel a running task') + .description('Cancel a running MCP task.') .addHelpText( 'after', `\n${chalk.bold('JSON output (--json):')}\n MCP Task object: { taskId, status, statusMessage? }\n` @@ -854,7 +854,7 @@ function registerSessionCommands(program: Command, session: string): void { // Resources commands program .command('resources') - .description('List available resources (shorthand for resources-list)') + .description('List available MCP server resources (shorthand for resources-list).') .addHelpText( 'after', `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Resource objects: [{ uri, name?, description?, mimeType? }, ...]\n` @@ -865,7 +865,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources-list') - .description('List available resources') + .description('List available MCP server resources.') .addHelpText( 'after', `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Resource objects: [{ uri, name?, description?, mimeType? }, ...]\n` @@ -876,7 +876,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources-read ') - .description('Get a resource by URI') + .description('Get an MCP server resource by URI.') .option('-o, --output ', 'Write resource to file') .option('--max-size ', 'Maximum resource size in bytes') .addHelpText( @@ -893,7 +893,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources-subscribe ') - .description('Subscribe to resource updates') + .description('Subscribe to MCP server resource updates.') .addHelpText( 'after', `\n${chalk.bold('JSON output (--json):')}\n { subscribed: true, uri: string }\n` @@ -904,7 +904,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources-unsubscribe ') - .description('Unsubscribe from resource updates') + .description('Unsubscribe from MCP server resource updates.') .addHelpText( 'after', `\n${chalk.bold('JSON output (--json):')}\n { unsubscribed: true, uri: string }\n` @@ -915,7 +915,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources-templates-list') - .description('List available resource templates') + .description('List available MCP server resource templates.') .addHelpText( 'after', `\n${chalk.bold('JSON output (--json):')}\n Array of MCP ResourceTemplate objects: [{ uriTemplate, name?, description?, mimeType? }, ...]\n` @@ -927,7 +927,7 @@ function registerSessionCommands(program: Command, session: string): void { // Prompts commands program .command('prompts') - .description('List available prompts (shorthand for prompts-list)') + .description('List all available MCP server prompts (shorthand for prompts-list).') .addHelpText( 'after', `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Prompt objects: [{ name, description?, arguments?: [{ name, description?, required? }] }, ...]\n` @@ -938,7 +938,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('prompts-list') - .description('List available prompts') + .description('List all available MCP server prompts.') .addHelpText( 'after', `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Prompt objects: [{ name, description?, arguments?: [{ name, description?, required? }] }, ...]\n` @@ -949,7 +949,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('prompts-get [args...]') - .description('Get a prompt by name with arguments (key:=value pairs or JSON)') + .description('Get a prompt by name with arguments (key:=value pairs or JSON).') .addHelpText( 'after', `\n${chalk.bold('JSON output (--json):')}\n MCP GetPromptResult: { description?, messages: [{ role, content: { type, text?, ... } }] }\n` @@ -965,7 +965,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('logging-set-level ') .description( - 'Set server logging level (debug, info, notice, warning, error, critical, alert, emergency)' + 'Set MCP server server logging level (debug, info, notice, warning, error, critical, alert, emergency)' ) .addHelpText('after', `\n${chalk.bold('JSON output (--json):')}\n { level: string }\n`) .action(async (level, _options, command) => { @@ -975,7 +975,7 @@ function registerSessionCommands(program: Command, session: string): void { // Server commands program .command('ping') - .description('Ping the MCP server to check if it is alive') + .description('Ping the MCP server to check it is alive.') .addHelpText( 'after', `\n${chalk.bold('JSON output (--json):')}\n { success: true, durationMs: number }\n` @@ -987,7 +987,7 @@ function registerSessionCommands(program: Command, session: string): void { // Grep command: @session grep program .command('grep ') - .description('Search tools and instructions') + .description('Search MCP server objects (by default --tools and --instructions).') .option('--tools', 'Search tools') .option('--resources', 'Search resources') .option('--prompts', 'Search prompts') From 9bb703f19e6ba01b09010d5e1870bd16de23c6a4 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Apr 2026 16:09:57 +0000 Subject: [PATCH 12/19] Improve help text: tools-call args docs, grep consistency, backtick formatting - tools-call help now shows how to pass arguments (key:=value, inline JSON, stdin pipe) with concrete examples and auto-parsing notes - Session grep help now includes type filters, examples, and --json output, matching the top-level grep help - All JSON output help text uses Markdown backtick formatting for type names and shapes (e.g. Array of `Tool` objects: `[{ name, ... }]`) - Dropped "MCP" prefix from type names (obvious from context) - Extracted jsonHelp() helper to keep formatting consistent https://claude.ai/code/session_01JCmVVCvBbxPUrxy3BcgjCc --- src/cli/index.ts | 131 +++++++++++++++++++++++++++++------------------ 1 file changed, 80 insertions(+), 51 deletions(-) diff --git a/src/cli/index.ts b/src/cli/index.ts index deac9b74..7190d246 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -122,6 +122,14 @@ function getOptionsFromCommand(command: Command): HandlerOptions { return options; } +/** + * Format a JSON output help line with backtick-style Markdown formatting. + */ +function jsonHelp(description: string, shape?: string): string { + const line = shape ? ` ${description}: ${shape}` : ` ${description}`; + return `\n${chalk.bold('JSON output (--json):')}\n${line}\n`; +} + async function main(): Promise { const args = process.argv.slice(2); @@ -409,8 +417,7 @@ ${chalk.bold('Server formats:')} mcp.apify.com Remote HTTP server (https:// added automatically) ~/.vscode/mcp.json:puppeteer Config file entry (file:entry) -${chalk.bold('JSON output (--json):')}\n MCP InitializeResult: { protocolVersion, capabilities, serverInfo, instructions?, tools? } -` +${jsonHelp('`InitializeResult`', '`{ protocolVersion, capabilities, serverInfo, instructions?, tools? }`')}` ) .action(async (server, sessionName, opts, command) => { if (!server) { @@ -468,10 +475,7 @@ ${chalk.bold('JSON output (--json):')}\n MCP InitializeResult: { protocolVersio .command('close [@session]') .usage('<@session>') .description('Close a session') - .addHelpText( - 'after', - `\n${chalk.bold('JSON output (--json):')}\n { sessionName, closed: true }\n` - ) + .addHelpText('after', jsonHelp('`{ sessionName, closed: true }`')) .action(async (sessionName, _opts, command) => { if (!sessionName) { throw new ClientError('Missing required argument: @session\n\nExample: mcpc close @myapp'); @@ -520,10 +524,7 @@ ${chalk.bold('JSON output (--json):')}\n MCP InitializeResult: { protocolVersio '--client-secret ', 'OAuth client secret (for servers without dynamic client registration)' ) - .addHelpText( - 'after', - `\n${chalk.bold('JSON output (--json):')}\n { profile, serverUrl, scopes }\n` - ) + .addHelpText('after', jsonHelp('`{ profile, serverUrl, scopes }`')) .action(async (server, opts, command) => { if (!server) { throw new ClientError( @@ -545,10 +546,7 @@ ${chalk.bold('JSON output (--json):')}\n MCP InitializeResult: { protocolVersio .usage('') .description('Delete an OAuth profile for a server') .option('--profile ', 'Profile name (default: "default")') - .addHelpText( - 'after', - `\n${chalk.bold('JSON output (--json):')}\n { profile, serverUrl, deleted: true, affectedSessions }\n` - ) + .addHelpText('after', jsonHelp('`{ profile, serverUrl, deleted: true, affectedSessions }`')) .action(async (server, opts, command) => { if (!server) { throw new ClientError( @@ -576,9 +574,7 @@ ${chalk.bold('Resources:')} Without arguments, performs safe cleanup of stale data only. -${chalk.bold('JSON output (--json):')} - { crashedBridges, expiredSessions, orphanedBridgeLogs, sessions, profiles, logs } -` +${jsonHelp('`{ crashedBridges, expiredSessions, orphanedBridgeLogs, sessions, profiles, logs }`')}` ) .action(async (resources: string[], _opts, command) => { const globalOpts = getOptionsFromCommand(command); @@ -630,9 +626,7 @@ ${chalk.bold('Examples:')} mcpc grep "file" --json JSON output for scripting mcpc grep "actor" -m 5 Show at most 5 results -${chalk.bold('JSON output (--json):')} - [{ sessionName, tools?: Tool[], resources?: Resource[], prompts?: Prompt[], instructions?: string[] }] -` +${jsonHelp('`[{ sessionName, tools?: Tool[], resources?: Resource[], prompts?: Prompt[], instructions?: string[] }]`')}` ) .action(async (pattern, opts, command) => { if (!pattern) { @@ -733,7 +727,10 @@ function registerSessionCommands(program: Command, session: string): void { .description('Show MCP server instructions, capabilities, and tools.') .addHelpText( 'after', - `\n${chalk.bold('JSON output (--json):')}\n MCP InitializeResult: { protocolVersion, capabilities, serverInfo, instructions?, tools? }\n` + jsonHelp( + '`InitializeResult`', + '`{ protocolVersion, capabilities, serverInfo, instructions?, tools? }`' + ) ) .action(async (_options, command) => { await sessions.showHelp(session, getOptionsFromCommand(command)); @@ -770,7 +767,10 @@ function registerSessionCommands(program: Command, session: string): void { .option('--full', 'Show full tool details including complete input schema') .addHelpText( 'after', - `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Tool objects: [{ name, description?, inputSchema, annotations? }, ...]\n` + jsonHelp( + 'Array of `Tool` objects', + '`[{ name, description?, inputSchema, annotations? }, ...]`' + ) ) .action(async (_options, command) => { await tools.listTools(session, getOptionsFromCommand(command)); @@ -782,7 +782,10 @@ function registerSessionCommands(program: Command, session: string): void { .option('--full', 'Show full tool details including complete input schema') .addHelpText( 'after', - `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Tool objects: [{ name, description?, inputSchema, annotations? }, ...]\n` + jsonHelp( + 'Array of `Tool` objects', + '`[{ name, description?, inputSchema, annotations? }, ...]`' + ) ) .action(async (_options, command) => { await tools.listTools(session, getOptionsFromCommand(command)); @@ -793,7 +796,7 @@ function registerSessionCommands(program: Command, session: string): void { .description('Get full details and schema for a specific MCP server tool.') .addHelpText( 'after', - `\n${chalk.bold('JSON output (--json):')}\n MCP Tool object: { name, description?, inputSchema, annotations? }\n` + jsonHelp('`Tool` object', '`{ name, description?, inputSchema, annotations? }`') ) .action(async (name, _options, command) => { await tools.getTool(session, name, getOptionsFromCommand(command)); @@ -806,7 +809,16 @@ function registerSessionCommands(program: Command, session: string): void { .option('--detach', 'Start task and return immediately with task ID (implies --task)') .addHelpText( 'after', - `\n${chalk.bold('JSON output (--json):')}\n MCP CallToolResult: { content: [{ type, text?, ... }], isError?, structuredContent? }\n` + ` +${chalk.bold('Arguments:')} + key:=value pairs mcpc ${session} tools-call search query:=hello limit:=10 + Inline JSON mcpc ${session} tools-call search '{"query":"hello"}' + Stdin pipe echo '{"query":"hello"}' | mcpc ${session} tools-call search + + Values are auto-parsed: strings, numbers, booleans, JSON objects/arrays. + To force a string, wrap in quotes: id:='"123"' + +${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, structuredContent? }`')}` ) .action(async (name, args, options, command) => { await tools.callTool(session, name, { @@ -823,7 +835,10 @@ function registerSessionCommands(program: Command, session: string): void { .description('List all MCP server tasks.') .addHelpText( 'after', - `\n${chalk.bold('JSON output (--json):')}\n { tasks: [{ taskId, status, statusMessage?, createdAt?, lastUpdatedAt? }, ...] }\n` + jsonHelp( + '`{ tasks: Task[] }`', + '`{ tasks: [{ taskId, status, statusMessage?, createdAt?, lastUpdatedAt? }] }`' + ) ) .action(async (_options, command) => { await tasks.listTasks(session, getOptionsFromCommand(command)); @@ -834,7 +849,7 @@ function registerSessionCommands(program: Command, session: string): void { .description('Get status of a specific MCP task.') .addHelpText( 'after', - `\n${chalk.bold('JSON output (--json):')}\n MCP Task object: { taskId, status, statusMessage?, createdAt?, lastUpdatedAt? }\n` + jsonHelp('`Task` object', '`{ taskId, status, statusMessage?, createdAt?, lastUpdatedAt? }`') ) .action(async (taskId, _options, command) => { await tasks.getTask(session, taskId, getOptionsFromCommand(command)); @@ -843,10 +858,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('tasks-cancel ') .description('Cancel a running MCP task.') - .addHelpText( - 'after', - `\n${chalk.bold('JSON output (--json):')}\n MCP Task object: { taskId, status, statusMessage? }\n` - ) + .addHelpText('after', jsonHelp('`Task` object', '`{ taskId, status, statusMessage? }`')) .action(async (taskId, _options, command) => { await tasks.cancelTask(session, taskId, getOptionsFromCommand(command)); }); @@ -857,7 +869,7 @@ function registerSessionCommands(program: Command, session: string): void { .description('List available MCP server resources (shorthand for resources-list).') .addHelpText( 'after', - `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Resource objects: [{ uri, name?, description?, mimeType? }, ...]\n` + jsonHelp('Array of `Resource` objects', '`[{ uri, name?, description?, mimeType? }, ...]`') ) .action(async (_options, command) => { await resources.listResources(session, getOptionsFromCommand(command)); @@ -868,7 +880,7 @@ function registerSessionCommands(program: Command, session: string): void { .description('List available MCP server resources.') .addHelpText( 'after', - `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Resource objects: [{ uri, name?, description?, mimeType? }, ...]\n` + jsonHelp('Array of `Resource` objects', '`[{ uri, name?, description?, mimeType? }, ...]`') ) .action(async (_options, command) => { await resources.listResources(session, getOptionsFromCommand(command)); @@ -881,7 +893,7 @@ function registerSessionCommands(program: Command, session: string): void { .option('--max-size ', 'Maximum resource size in bytes') .addHelpText( 'after', - `\n${chalk.bold('JSON output (--json):')}\n MCP ReadResourceResult: { contents: [{ uri, mimeType?, text? | blob? }] }\n` + jsonHelp('`ReadResourceResult`', '`{ contents: [{ uri, mimeType?, text? | blob? }] }`') ) .action(async (uri, options, command) => { await resources.getResource(session, uri, { @@ -894,10 +906,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources-subscribe ') .description('Subscribe to MCP server resource updates.') - .addHelpText( - 'after', - `\n${chalk.bold('JSON output (--json):')}\n { subscribed: true, uri: string }\n` - ) + .addHelpText('after', jsonHelp('`{ subscribed: true, uri: string }`')) .action(async (uri, _options, command) => { await resources.subscribeResource(session, uri, getOptionsFromCommand(command)); }); @@ -905,10 +914,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('resources-unsubscribe ') .description('Unsubscribe from MCP server resource updates.') - .addHelpText( - 'after', - `\n${chalk.bold('JSON output (--json):')}\n { unsubscribed: true, uri: string }\n` - ) + .addHelpText('after', jsonHelp('`{ unsubscribed: true, uri: string }`')) .action(async (uri, _options, command) => { await resources.unsubscribeResource(session, uri, getOptionsFromCommand(command)); }); @@ -918,7 +924,10 @@ function registerSessionCommands(program: Command, session: string): void { .description('List available MCP server resource templates.') .addHelpText( 'after', - `\n${chalk.bold('JSON output (--json):')}\n Array of MCP ResourceTemplate objects: [{ uriTemplate, name?, description?, mimeType? }, ...]\n` + jsonHelp( + 'Array of `ResourceTemplate` objects', + '`[{ uriTemplate, name?, description?, mimeType? }, ...]`' + ) ) .action(async (_options, command) => { await resources.listResourceTemplates(session, getOptionsFromCommand(command)); @@ -930,7 +939,10 @@ function registerSessionCommands(program: Command, session: string): void { .description('List all available MCP server prompts (shorthand for prompts-list).') .addHelpText( 'after', - `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Prompt objects: [{ name, description?, arguments?: [{ name, description?, required? }] }, ...]\n` + jsonHelp( + 'Array of `Prompt` objects', + '`[{ name, description?, arguments?: [{ name, required? }] }, ...]`' + ) ) .action(async (_options, command) => { await prompts.listPrompts(session, getOptionsFromCommand(command)); @@ -941,7 +953,10 @@ function registerSessionCommands(program: Command, session: string): void { .description('List all available MCP server prompts.') .addHelpText( 'after', - `\n${chalk.bold('JSON output (--json):')}\n Array of MCP Prompt objects: [{ name, description?, arguments?: [{ name, description?, required? }] }, ...]\n` + jsonHelp( + 'Array of `Prompt` objects', + '`[{ name, description?, arguments?: [{ name, required? }] }, ...]`' + ) ) .action(async (_options, command) => { await prompts.listPrompts(session, getOptionsFromCommand(command)); @@ -952,7 +967,10 @@ function registerSessionCommands(program: Command, session: string): void { .description('Get a prompt by name with arguments (key:=value pairs or JSON).') .addHelpText( 'after', - `\n${chalk.bold('JSON output (--json):')}\n MCP GetPromptResult: { description?, messages: [{ role, content: { type, text?, ... } }] }\n` + jsonHelp( + '`GetPromptResult`', + '`{ description?, messages: [{ role, content: { type, text?, ... } }] }`' + ) ) .action(async (name, args, _options, command) => { await prompts.getPrompt(session, name, { @@ -967,7 +985,7 @@ function registerSessionCommands(program: Command, session: string): void { .description( 'Set MCP server server logging level (debug, info, notice, warning, error, critical, alert, emergency)' ) - .addHelpText('after', `\n${chalk.bold('JSON output (--json):')}\n { level: string }\n`) + .addHelpText('after', jsonHelp('`{ level: string }`')) .action(async (level, _options, command) => { await logging.setLogLevel(session, level, getOptionsFromCommand(command)); }); @@ -976,10 +994,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('ping') .description('Ping the MCP server to check it is alive.') - .addHelpText( - 'after', - `\n${chalk.bold('JSON output (--json):')}\n { success: true, durationMs: number }\n` - ) + .addHelpText('after', jsonHelp('`{ success: true, durationMs: number }`')) .action(async (_options, command) => { await utilities.ping(session, getOptionsFromCommand(command)); }); @@ -995,6 +1010,20 @@ function registerSessionCommands(program: Command, session: string): void { .option('-E, --regex', 'Treat pattern as a regular expression') .option('-s, --case-sensitive', 'Case-sensitive matching') .option('-m, --max-results ', 'Limit the number of results') + .addHelpText( + 'after', + ` +${chalk.bold('Type filters:')} + By default, tools and instructions are searched. Use --resources or --prompts + to search those instead. Combine flags to search multiple types. + +${chalk.bold('Examples:')} + mcpc ${session} grep "search" Search tools and instructions + mcpc ${session} grep "search" --resources Search resources only + mcpc ${session} grep "search|find" -E Regex search + +${jsonHelp('`{ tools?: Tool[], resources?: Resource[], prompts?: Prompt[], instructions?: string[] }`')}` + ) .action(async (pattern, opts, command) => { const globalOpts = getOptionsFromCommand(command); const maxResults = opts.maxResults ? parseInt(opts.maxResults as string, 10) : undefined; From 69e0c184025c8f9dc48e75d8bc25edf6d7acafa4 Mon Sep 17 00:00:00 2001 From: Jan Curn Date: Tue, 7 Apr 2026 18:44:33 +0200 Subject: [PATCH 13/19] Improvements --- src/cli/commands/grep.ts | 2 +- src/cli/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/commands/grep.ts b/src/cli/commands/grep.ts index 80bf126c..98687682 100644 --- a/src/cli/commands/grep.ts +++ b/src/cli/commands/grep.ts @@ -421,7 +421,7 @@ function formatGrepResultHuman( pattern && options ? highlightMatch(result.instructions, pattern, options) : result.instructions; - lines.push(`${indent}${chalk.bold('Instructions:')} ${chalk.dim('````' + snippet + '````')}`); + lines.push(`${indent}${chalk.bold('Instructions:')} ${chalk.dim('````' + snippet + '````')}`); } lines.push( ...formatResultSection('Tools', result.tools, formatToolLine as (item: never) => string, indent) diff --git a/src/cli/index.ts b/src/cli/index.ts index 7190d246..546ef404 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -1002,7 +1002,7 @@ ${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, st // Grep command: @session grep program .command('grep ') - .description('Search MCP server objects (by default --tools and --instructions).') + .description('Search objects in an MCP server session.') .option('--tools', 'Search tools') .option('--resources', 'Search resources') .option('--prompts', 'Search prompts') From fe83827a016b7b0bb09f57e67cb58323c0673bbb Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Apr 2026 16:50:18 +0000 Subject: [PATCH 14/19] Fix extra blank line before JSON output section in multi-section help text The jsonHelp() helper starts with \n, so when preceded by a blank line in the template literal, it produced a double blank line. Removed the redundant blank lines in tools-call, grep (both), connect, and clean. https://claude.ai/code/session_01JCmVVCvBbxPUrxy3BcgjCc --- src/cli/index.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/cli/index.ts b/src/cli/index.ts index 546ef404..f7cd3fc3 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -416,7 +416,6 @@ Full docs: ${docsUrl}` ${chalk.bold('Server formats:')} mcp.apify.com Remote HTTP server (https:// added automatically) ~/.vscode/mcp.json:puppeteer Config file entry (file:entry) - ${jsonHelp('`InitializeResult`', '`{ protocolVersion, capabilities, serverInfo, instructions?, tools? }`')}` ) .action(async (server, sessionName, opts, command) => { @@ -573,7 +572,6 @@ ${chalk.bold('Resources:')} all Remove all of the above Without arguments, performs safe cleanup of stale data only. - ${jsonHelp('`{ crashedBridges, expiredSessions, orphanedBridgeLogs, sessions, profiles, logs }`')}` ) .action(async (resources: string[], _opts, command) => { @@ -625,7 +623,6 @@ ${chalk.bold('Examples:')} mcpc @apify grep "actor" Search within a single session mcpc grep "file" --json JSON output for scripting mcpc grep "actor" -m 5 Show at most 5 results - ${jsonHelp('`[{ sessionName, tools?: Tool[], resources?: Resource[], prompts?: Prompt[], instructions?: string[] }]`')}` ) .action(async (pattern, opts, command) => { @@ -817,7 +814,6 @@ ${chalk.bold('Arguments:')} Values are auto-parsed: strings, numbers, booleans, JSON objects/arrays. To force a string, wrap in quotes: id:='"123"' - ${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, structuredContent? }`')}` ) .action(async (name, args, options, command) => { @@ -1021,7 +1017,6 @@ ${chalk.bold('Examples:')} mcpc ${session} grep "search" Search tools and instructions mcpc ${session} grep "search" --resources Search resources only mcpc ${session} grep "search|find" -E Regex search - ${jsonHelp('`{ tools?: Tool[], resources?: Resource[], prompts?: Prompt[], instructions?: string[] }`')}` ) .action(async (pattern, opts, command) => { From 9c597189014f1d558817222cdaef85270cd835d1 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Apr 2026 16:50:57 +0000 Subject: [PATCH 15/19] Fix session grep usage to show before [options] Match the top-level grep usage ordering by adding explicit .usage() so both show "grep [options]" consistently. https://claude.ai/code/session_01JCmVVCvBbxPUrxy3BcgjCc --- src/cli/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cli/index.ts b/src/cli/index.ts index f7cd3fc3..a79b3985 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -998,6 +998,7 @@ ${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, st // Grep command: @session grep program .command('grep ') + .usage(' [options]') .description('Search objects in an MCP server session.') .option('--tools', 'Search tools') .option('--resources', 'Search resources') From e03e374b337673233ae250f57bfe143c4ba25dd4 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Apr 2026 20:53:37 +0000 Subject: [PATCH 16/19] Add MCP schema links to JSON output help text For commands that return MCP protocol types, add a "Schema:" link to the respective type definition on the MCP specification page. This helps AI agents look up the full type definition when needed. Links added for: InitializeResult, Tool, CallToolResult, Resource, ReadResourceResult, ResourceTemplate, Prompt, GetPromptResult. No links for mcpc-specific objects (ping, close, login, etc.) or experimental types (Task). https://claude.ai/code/session_01JCmVVCvBbxPUrxy3BcgjCc --- src/cli/index.ts | 54 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/src/cli/index.ts b/src/cli/index.ts index a79b3985..eea417b6 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -124,12 +124,16 @@ function getOptionsFromCommand(command: Command): HandlerOptions { /** * Format a JSON output help line with backtick-style Markdown formatting. + * Optional schemaUrl adds a "Schema:" link for AI agents. */ -function jsonHelp(description: string, shape?: string): string { +function jsonHelp(description: string, shape?: string, schemaUrl?: string): string { const line = shape ? ` ${description}: ${shape}` : ` ${description}`; - return `\n${chalk.bold('JSON output (--json):')}\n${line}\n`; + const link = schemaUrl ? `\n Schema: ${schemaUrl}` : ''; + return `\n${chalk.bold('JSON output (--json):')}\n${line}${link}\n`; } +const SCHEMA_BASE = 'https://modelcontextprotocol.io/specification/2025-11-25/schema'; + async function main(): Promise { const args = process.argv.slice(2); @@ -416,7 +420,7 @@ Full docs: ${docsUrl}` ${chalk.bold('Server formats:')} mcp.apify.com Remote HTTP server (https:// added automatically) ~/.vscode/mcp.json:puppeteer Config file entry (file:entry) -${jsonHelp('`InitializeResult`', '`{ protocolVersion, capabilities, serverInfo, instructions?, tools? }`')}` +${jsonHelp('`InitializeResult`', '`{ protocolVersion, capabilities, serverInfo, instructions?, tools? }`', `${SCHEMA_BASE}#initializeresult`)}` ) .action(async (server, sessionName, opts, command) => { if (!server) { @@ -766,7 +770,8 @@ function registerSessionCommands(program: Command, session: string): void { 'after', jsonHelp( 'Array of `Tool` objects', - '`[{ name, description?, inputSchema, annotations? }, ...]`' + '`[{ name, description?, inputSchema, annotations? }, ...]`', + `${SCHEMA_BASE}#tool` ) ) .action(async (_options, command) => { @@ -781,7 +786,8 @@ function registerSessionCommands(program: Command, session: string): void { 'after', jsonHelp( 'Array of `Tool` objects', - '`[{ name, description?, inputSchema, annotations? }, ...]`' + '`[{ name, description?, inputSchema, annotations? }, ...]`', + `${SCHEMA_BASE}#tool` ) ) .action(async (_options, command) => { @@ -793,7 +799,11 @@ function registerSessionCommands(program: Command, session: string): void { .description('Get full details and schema for a specific MCP server tool.') .addHelpText( 'after', - jsonHelp('`Tool` object', '`{ name, description?, inputSchema, annotations? }`') + jsonHelp( + '`Tool` object', + '`{ name, description?, inputSchema, annotations? }`', + `${SCHEMA_BASE}#tool` + ) ) .action(async (name, _options, command) => { await tools.getTool(session, name, getOptionsFromCommand(command)); @@ -814,7 +824,7 @@ ${chalk.bold('Arguments:')} Values are auto-parsed: strings, numbers, booleans, JSON objects/arrays. To force a string, wrap in quotes: id:='"123"' -${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, structuredContent? }`')}` +${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, structuredContent? }`', `${SCHEMA_BASE}#calltoolresult`)}` ) .action(async (name, args, options, command) => { await tools.callTool(session, name, { @@ -865,7 +875,11 @@ ${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, st .description('List available MCP server resources (shorthand for resources-list).') .addHelpText( 'after', - jsonHelp('Array of `Resource` objects', '`[{ uri, name?, description?, mimeType? }, ...]`') + jsonHelp( + 'Array of `Resource` objects', + '`[{ uri, name?, description?, mimeType? }, ...]`', + `${SCHEMA_BASE}#resource` + ) ) .action(async (_options, command) => { await resources.listResources(session, getOptionsFromCommand(command)); @@ -876,7 +890,11 @@ ${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, st .description('List available MCP server resources.') .addHelpText( 'after', - jsonHelp('Array of `Resource` objects', '`[{ uri, name?, description?, mimeType? }, ...]`') + jsonHelp( + 'Array of `Resource` objects', + '`[{ uri, name?, description?, mimeType? }, ...]`', + `${SCHEMA_BASE}#resource` + ) ) .action(async (_options, command) => { await resources.listResources(session, getOptionsFromCommand(command)); @@ -889,7 +907,11 @@ ${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, st .option('--max-size ', 'Maximum resource size in bytes') .addHelpText( 'after', - jsonHelp('`ReadResourceResult`', '`{ contents: [{ uri, mimeType?, text? | blob? }] }`') + jsonHelp( + '`ReadResourceResult`', + '`{ contents: [{ uri, mimeType?, text? | blob? }] }`', + `${SCHEMA_BASE}#readresourceresult` + ) ) .action(async (uri, options, command) => { await resources.getResource(session, uri, { @@ -922,7 +944,8 @@ ${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, st 'after', jsonHelp( 'Array of `ResourceTemplate` objects', - '`[{ uriTemplate, name?, description?, mimeType? }, ...]`' + '`[{ uriTemplate, name?, description?, mimeType? }, ...]`', + `${SCHEMA_BASE}#resourcetemplate` ) ) .action(async (_options, command) => { @@ -937,7 +960,8 @@ ${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, st 'after', jsonHelp( 'Array of `Prompt` objects', - '`[{ name, description?, arguments?: [{ name, required? }] }, ...]`' + '`[{ name, description?, arguments?: [{ name, required? }] }, ...]`', + `${SCHEMA_BASE}#prompt` ) ) .action(async (_options, command) => { @@ -951,7 +975,8 @@ ${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, st 'after', jsonHelp( 'Array of `Prompt` objects', - '`[{ name, description?, arguments?: [{ name, required? }] }, ...]`' + '`[{ name, description?, arguments?: [{ name, required? }] }, ...]`', + `${SCHEMA_BASE}#prompt` ) ) .action(async (_options, command) => { @@ -965,7 +990,8 @@ ${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, st 'after', jsonHelp( '`GetPromptResult`', - '`{ description?, messages: [{ role, content: { type, text?, ... } }] }`' + '`{ description?, messages: [{ role, content: { type, text?, ... } }] }`', + `${SCHEMA_BASE}#getpromptresult` ) ) .action(async (name, args, _options, command) => { From c096712d05c357a73ef7fc88f641730557a2617d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Apr 2026 21:18:54 +0000 Subject: [PATCH 17/19] Hide redundant -h/--help from session sub-command help output You already need to run --help to see the help screen, so showing "-h, --help Display help" in the options list is circular. Hide it from the output while keeping --help functional for routing. Applies to session sub-commands only (mcpc @session --help and mcpc help ). Top-level commands keep it since they have many options and the help line is less prominent. https://claude.ai/code/session_01JCmVVCvBbxPUrxy3BcgjCc --- src/cli/index.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cli/index.ts b/src/cli/index.ts index eea417b6..044ef041 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -689,6 +689,9 @@ ${jsonHelp('`[{ sessionName, tools?: Tool[], resources?: Resource[], prompts?: P registerSessionCommands(dummyProgram, '<@session>'); for (const cmd of dummyProgram.commands) { cmd.helpOption('-h, --help', 'Display help'); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const helpOpt = (cmd as any)._getHelpOption?.(); + if (helpOpt) helpOpt.hidden = true; } const sessionCmd = dummyProgram.commands.find( (c) => c.name() === cmdName || c.aliases().includes(cmdName) @@ -1120,9 +1123,13 @@ async function handleSessionCommands(session: string, args: string[]): Promise Date: Tue, 7 Apr 2026 21:25:56 +0000 Subject: [PATCH 18/19] Show --json option in session sub-command help When -h/--help was hidden, commands with no other options (like tools-get) lost the Options section entirely, which looked odd. Add --json to each sub-command so it always appears in the options list, making it discoverable for AI agents and scripts. https://claude.ai/code/session_01JCmVVCvBbxPUrxy3BcgjCc --- src/cli/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cli/index.ts b/src/cli/index.ts index 044ef041..314800b8 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -688,6 +688,7 @@ ${jsonHelp('`[{ sessionName, tools?: Tool[], resources?: Resource[], prompts?: P const dummyProgram = createSessionProgram(); registerSessionCommands(dummyProgram, '<@session>'); for (const cmd of dummyProgram.commands) { + cmd.option('-j, --json', 'Output in JSON format'); cmd.helpOption('-h, --help', 'Display help'); // eslint-disable-next-line @typescript-eslint/no-explicit-any const helpOpt = (cmd as any)._getHelpOption?.(); @@ -1123,9 +1124,11 @@ async function handleSessionCommands(session: string, args: string[]): Promise Date: Tue, 7 Apr 2026 23:28:18 +0200 Subject: [PATCH 19/19] Writing --- src/cli/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/index.ts b/src/cli/index.ts index 314800b8..a57953b5 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -816,7 +816,7 @@ function registerSessionCommands(program: Command, session: string): void { program .command('tools-call [args...]') .description('Call an MCP server tool with arguments (key:=value pairs or JSON)') - .option('--task', 'Use task execution (experimental)') + .option('--task', 'Use async task execution (experimental)') .option('--detach', 'Start task and return immediately with task ID (implies --task)') .addHelpText( 'after',