From 10e639b79d309e2747c49c392e58d4786821a4b9 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Apr 2026 12:55:48 +0000 Subject: [PATCH 1/2] Move --max-chars from global option to subcommand-level, skip in JSON mode --max-chars now only appears on the commands where it applies (tools-call, prompts-get, resources-read) rather than as a global option. Truncation is skipped in --json mode to avoid producing invalid JSON output. https://claude.ai/code/session_01PiFuVVaizRowPPHUwMPKVa --- CHANGELOG.md | 2 +- README.md | 1 - src/cli/commands/prompts.ts | 2 +- src/cli/commands/resources.ts | 2 +- src/cli/commands/tools.ts | 2 +- src/cli/index.ts | 5 +++-- src/cli/parser.ts | 9 ++------- 7 files changed, 9 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9f2d86..77b64fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `connect` command now auto-generates session name when `@session` is omitted (e.g., `mcpc connect mcp.apify.com` creates `@apify`). If a session for the same server already exists with matching auth settings, it is reused instead of creating a duplicate. -- `--max-chars ` global option to truncate large tool/prompt/resource output +- `--max-chars ` option for `tools-call`, `prompts-get`, and `resources-read` to truncate large output (human mode only) - `tools-call --help` shows tool parameter schema (shortcut for `tools-get`) - "Did you mean?" suggestions for unknown commands, including reversed names (e.g., `list-tools` → `tools-list`) - `--json` output documentation in `--help` for all commands, describing the MCP object shape returned diff --git a/README.md b/README.md index 500a70a..9953c6d 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,6 @@ Options: --schema Validate tool/prompt schema against expected schema --schema-mode Schema validation mode: strict, compatible (default), ignore --timeout Request timeout in seconds (default: 300) - --max-chars Truncate tool/prompt output to this many characters --insecure Skip TLS certificate verification (for self-signed certs) -v, --version Output the version number -h, --help Display help diff --git a/src/cli/commands/prompts.ts b/src/cli/commands/prompts.ts index 1e76aa7..83e0986 100644 --- a/src/cli/commands/prompts.ts +++ b/src/cli/commands/prompts.ts @@ -108,7 +108,7 @@ export async function getPrompt( const result = await client.getPrompt(name, promptArgs); let output = formatOutput(result, options.outputMode); - if (options.maxChars) { + if (options.maxChars && options.outputMode === 'human') { output = truncateOutput(output, options.maxChars); } console.log(output); diff --git a/src/cli/commands/resources.ts b/src/cli/commands/resources.ts index 02d41f5..36be04c 100644 --- a/src/cli/commands/resources.ts +++ b/src/cli/commands/resources.ts @@ -84,7 +84,7 @@ export async function getResource( } let output = formatOutput(result, options.outputMode); - if (options.maxChars) { + if (options.maxChars && options.outputMode === 'human') { output = truncateOutput(output, options.maxChars); } console.log(output); diff --git a/src/cli/commands/tools.ts b/src/cli/commands/tools.ts index 850e6c0..13767e6 100644 --- a/src/cli/commands/tools.ts +++ b/src/cli/commands/tools.ts @@ -371,7 +371,7 @@ export async function callTool( } let output = formatOutput(result, options.outputMode); - if (options.maxChars) { + if (options.maxChars && options.outputMode === 'human') { output = truncateOutput(output, options.maxChars); } console.log(output); diff --git a/src/cli/index.ts b/src/cli/index.ts index d49fe80..57c7b3e 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -400,7 +400,6 @@ function createTopLevelProgram(): Command { .option('--schema ', 'Validate tool/prompt schema against expected schema') .option('--schema-mode ', 'Schema validation mode: strict, compatible (default), ignore') .option('--timeout ', 'Request timeout in seconds (default: 300)') - .option('--max-chars ', 'Truncate tool/prompt output to this many characters') .option('--insecure', 'Skip TLS certificate verification (for self-signed certs)') .version(mcpcVersion, '-v, --version', 'Output the version number') .helpOption('-h, --help', 'Display help'); @@ -915,6 +914,7 @@ ${jsonHelp('`{ tools?: Tool[], resources?: Resource[], prompts?: Prompt[], instr .helpOption(false) // Disable built-in --help so we can intercept it for tool schema .option('--task', 'Use async task execution (experimental)') .option('--detach', 'Start task and return immediately with task ID (implies --task)') + .option('--max-chars ', 'Truncate output to n characters (human mode only)') .addHelpText( 'after', ` @@ -1029,6 +1029,7 @@ ${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, st .description('Read an MCP resource by URI.') .option('-o, --output ', 'Write resource to file') .option('--max-size ', 'Maximum resource size in bytes') + .option('--max-chars ', 'Truncate output to n characters (human mode only)') .addHelpText( 'after', jsonHelp( @@ -1110,6 +1111,7 @@ ${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, st program .command('prompts-get [args...]') .description('Get an MCP prompt with arguments.') + .option('--max-chars ', 'Truncate output to n characters (human mode only)') .addHelpText( 'after', jsonHelp( @@ -1176,7 +1178,6 @@ function createSessionProgram(): Command { .option('--schema ', 'Validate tool/prompt schema against expected schema') .option('--schema-mode ', 'Schema validation mode: strict, compatible (default), ignore') .option('--timeout ', 'Request timeout in seconds (default: 300)') - .option('--max-chars ', 'Truncate tool/prompt output to this many characters') .option('--insecure', 'Skip TLS certificate verification (for self-signed certs)') .addHelpText( 'after', diff --git a/src/cli/parser.ts b/src/cli/parser.ts index f668fe8..9005e34 100644 --- a/src/cli/parser.ts +++ b/src/cli/parser.ts @@ -30,13 +30,7 @@ export function getJsonFromEnv(): boolean { } // Global options that take a value (not boolean flags) -const GLOBAL_OPTIONS_WITH_VALUES = [ - '--timeout', - '--profile', - '--schema', - '--schema-mode', - '--max-chars', -]; +const GLOBAL_OPTIONS_WITH_VALUES = ['--timeout', '--profile', '--schema', '--schema-mode']; // All options that take a value — used by optionTakesValue() to correctly skip // the next arg when scanning for command tokens. Includes subcommand-specific @@ -55,6 +49,7 @@ const OPTIONS_WITH_VALUES = [ '-o', '--output', '--max-size', + '--max-chars', '--amount', '--expiry', ]; From 206b34a6c75588d036f30ecec51e073f067ad172 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Apr 2026 15:04:51 +0000 Subject: [PATCH 2/2] Apply --max-chars consistently to all commands, skip in --json mode Move truncation logic into formatOutput() so every command that uses it gets --max-chars support automatically in human mode. This replaces the previous per-handler truncation which only covered 3 commands. JSON output is never truncated to avoid producing invalid JSON. https://claude.ai/code/session_01PiFuVVaizRowPPHUwMPKVa --- CHANGELOG.md | 2 +- README.md | 1 + src/cli/commands/prompts.ts | 18 +++++++++++------- src/cli/commands/resources.ts | 24 ++++++++++++++++-------- src/cli/commands/tools.ts | 12 ++++++------ src/cli/index.ts | 5 ++--- src/cli/output.ts | 9 +++++++-- src/cli/parser.ts | 9 +++++++-- 8 files changed, 51 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77b64fb..716bdda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `connect` command now auto-generates session name when `@session` is omitted (e.g., `mcpc connect mcp.apify.com` creates `@apify`). If a session for the same server already exists with matching auth settings, it is reused instead of creating a duplicate. -- `--max-chars ` option for `tools-call`, `prompts-get`, and `resources-read` to truncate large output (human mode only) +- `--max-chars ` global option to truncate output to a given number of characters (ignored in `--json` mode) - `tools-call --help` shows tool parameter schema (shortcut for `tools-get`) - "Did you mean?" suggestions for unknown commands, including reversed names (e.g., `list-tools` → `tools-list`) - `--json` output documentation in `--help` for all commands, describing the MCP object shape returned diff --git a/README.md b/README.md index 9953c6d..a9edf2f 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ Options: --schema Validate tool/prompt schema against expected schema --schema-mode Schema validation mode: strict, compatible (default), ignore --timeout Request timeout in seconds (default: 300) + --max-chars Truncate output to n characters (ignored in --json mode) --insecure Skip TLS certificate verification (for self-signed certs) -v, --version Output the version number -h, --help Display help diff --git a/src/cli/commands/prompts.ts b/src/cli/commands/prompts.ts index 83e0986..33fc59d 100644 --- a/src/cli/commands/prompts.ts +++ b/src/cli/commands/prompts.ts @@ -3,7 +3,7 @@ */ import type { CommandOptions } from '../../lib/types.js'; -import { formatOutput, formatWarning, truncateOutput } from '../output.js'; +import { formatOutput, formatWarning } from '../output.js'; import { withMcpClient } from '../helpers.js'; import { parseCommandArgs, hasStdinData, readStdinArgs } from '../parser.js'; import { ClientError } from '../../lib/errors.js'; @@ -31,7 +31,11 @@ export async function listPrompts(target: string, options: CommandOptions): Prom cursor = result.nextCursor; } while (cursor); - console.log(formatOutput(allPrompts, options.outputMode)); + console.log( + formatOutput(allPrompts, options.outputMode, { + ...(options.maxChars && { maxChars: options.maxChars }), + }) + ); }); } @@ -107,10 +111,10 @@ export async function getPrompt( const result = await client.getPrompt(name, promptArgs); - let output = formatOutput(result, options.outputMode); - if (options.maxChars && options.outputMode === 'human') { - output = truncateOutput(output, options.maxChars); - } - console.log(output); + console.log( + formatOutput(result, options.outputMode, { + ...(options.maxChars && { maxChars: options.maxChars }), + }) + ); }); } diff --git a/src/cli/commands/resources.ts b/src/cli/commands/resources.ts index 36be04c..3810cc6 100644 --- a/src/cli/commands/resources.ts +++ b/src/cli/commands/resources.ts @@ -2,7 +2,7 @@ * Resources command handlers */ -import { formatOutput, formatSuccess, truncateOutput } from '../output.js'; +import { formatOutput, formatSuccess } from '../output.js'; import { withMcpClient } from '../helpers.js'; import type { CommandOptions } from '../../lib/types.js'; @@ -22,7 +22,11 @@ export async function listResources(target: string, options: CommandOptions): Pr cursor = result.nextCursor; } while (cursor); - console.log(formatOutput(allResources, options.outputMode)); + console.log( + formatOutput(allResources, options.outputMode, { + ...(options.maxChars && { maxChars: options.maxChars }), + }) + ); }); } @@ -45,7 +49,11 @@ export async function listResourceTemplates( cursor = result.nextCursor; } while (cursor); - console.log(formatOutput(allTemplates, options.outputMode)); + console.log( + formatOutput(allTemplates, options.outputMode, { + ...(options.maxChars && { maxChars: options.maxChars }), + }) + ); }); } @@ -83,11 +91,11 @@ export async function getResource( return; } - let output = formatOutput(result, options.outputMode); - if (options.maxChars && options.outputMode === 'human') { - output = truncateOutput(output, options.maxChars); - } - console.log(output); + console.log( + formatOutput(result, options.outputMode, { + ...(options.maxChars && { maxChars: options.maxChars }), + }) + ); }); } diff --git a/src/cli/commands/tools.ts b/src/cli/commands/tools.ts index 13767e6..ad514b8 100644 --- a/src/cli/commands/tools.ts +++ b/src/cli/commands/tools.ts @@ -12,7 +12,6 @@ import { formatError, formatWarning, formatInfo, - truncateOutput, } from '../output.js'; import { ClientError } from '../../lib/errors.js'; import type { CommandOptions, TaskUpdate } from '../../lib/types.js'; @@ -40,6 +39,7 @@ export async function listTools( console.log( formatOutput(result.tools, options.outputMode, { ...(options.full && { full: true }), + ...(options.maxChars && { maxChars: options.maxChars }), sessionName: target, }) ); @@ -370,11 +370,11 @@ export async function callTool( } } - let output = formatOutput(result, options.outputMode); - if (options.maxChars && options.outputMode === 'human') { - output = truncateOutput(output, options.maxChars); - } - console.log(output); + console.log( + formatOutput(result, options.outputMode, { + ...(options.maxChars && { maxChars: options.maxChars }), + }) + ); // Show hint for getting tool schema when the tool returned an error if (result.isError && options.outputMode === 'human') { diff --git a/src/cli/index.ts b/src/cli/index.ts index 57c7b3e..1b0aa5e 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -400,6 +400,7 @@ function createTopLevelProgram(): Command { .option('--schema ', 'Validate tool/prompt schema against expected schema') .option('--schema-mode ', 'Schema validation mode: strict, compatible (default), ignore') .option('--timeout ', 'Request timeout in seconds (default: 300)') + .option('--max-chars ', 'Truncate output to n characters (ignored in --json mode)') .option('--insecure', 'Skip TLS certificate verification (for self-signed certs)') .version(mcpcVersion, '-v, --version', 'Output the version number') .helpOption('-h, --help', 'Display help'); @@ -914,7 +915,6 @@ ${jsonHelp('`{ tools?: Tool[], resources?: Resource[], prompts?: Prompt[], instr .helpOption(false) // Disable built-in --help so we can intercept it for tool schema .option('--task', 'Use async task execution (experimental)') .option('--detach', 'Start task and return immediately with task ID (implies --task)') - .option('--max-chars ', 'Truncate output to n characters (human mode only)') .addHelpText( 'after', ` @@ -1029,7 +1029,6 @@ ${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, st .description('Read an MCP resource by URI.') .option('-o, --output ', 'Write resource to file') .option('--max-size ', 'Maximum resource size in bytes') - .option('--max-chars ', 'Truncate output to n characters (human mode only)') .addHelpText( 'after', jsonHelp( @@ -1111,7 +1110,6 @@ ${jsonHelp('`CallToolResult`', '`{ content: [{ type, text?, ... }], isError?, st program .command('prompts-get [args...]') .description('Get an MCP prompt with arguments.') - .option('--max-chars ', 'Truncate output to n characters (human mode only)') .addHelpText( 'after', jsonHelp( @@ -1178,6 +1176,7 @@ function createSessionProgram(): Command { .option('--schema ', 'Validate tool/prompt schema against expected schema') .option('--schema-mode ', 'Schema validation mode: strict, compatible (default), ignore') .option('--timeout ', 'Request timeout in seconds (default: 300)') + .option('--max-chars ', 'Truncate output to n characters (ignored in --json mode)') .option('--insecure', 'Skip TLS certificate verification (for self-signed certs)') .addHelpText( 'after', diff --git a/src/cli/output.ts b/src/cli/output.ts index daf4c0a..b6482c9 100644 --- a/src/cli/output.ts +++ b/src/cli/output.ts @@ -77,6 +77,8 @@ export interface FormatOptions { full?: boolean; /** Session name for contextual hints (e.g. @apify) */ sessionName?: string; + /** Truncate human-mode output to this many characters */ + maxChars?: number; } /** @@ -91,10 +93,13 @@ export function formatOutput( if (mode === 'json') { return formatJson(data); } - const output = formatHuman(data, options); + let output = formatHuman(data, options); // Ensure trailing newline for visual separation in shell (unless ends with code block) if (!output.endsWith('````') && !output.endsWith('\n')) { - return output + '\n'; + output += '\n'; + } + if (options?.maxChars) { + output = truncateOutput(output, options.maxChars); } return output; } diff --git a/src/cli/parser.ts b/src/cli/parser.ts index 9005e34..f668fe8 100644 --- a/src/cli/parser.ts +++ b/src/cli/parser.ts @@ -30,7 +30,13 @@ export function getJsonFromEnv(): boolean { } // Global options that take a value (not boolean flags) -const GLOBAL_OPTIONS_WITH_VALUES = ['--timeout', '--profile', '--schema', '--schema-mode']; +const GLOBAL_OPTIONS_WITH_VALUES = [ + '--timeout', + '--profile', + '--schema', + '--schema-mode', + '--max-chars', +]; // All options that take a value — used by optionTakesValue() to correctly skip // the next arg when scanning for command tokens. Includes subcommand-specific @@ -49,7 +55,6 @@ const OPTIONS_WITH_VALUES = [ '-o', '--output', '--max-size', - '--max-chars', '--amount', '--expiry', ];