@@ -11,6 +11,7 @@ import type { QsvSkill, Argument, Option, McpToolDefinition, McpToolProperty, Fi
1111import type { SkillExecutor } from './executor.js' ;
1212import type { SkillLoader } from './loader.js' ;
1313import { config } from './config.js' ;
14+ import { formatBytes } from './utils.js' ;
1415
1516/**
1617 * Auto-indexing threshold in MB
@@ -72,6 +73,13 @@ const METADATA_COMMANDS = new Set([
7273 */
7374const LARGE_FILE_THRESHOLD_BYTES = 10 * 1024 * 1024 ; // 10MB
7475
76+ /**
77+ * Maximum size for MCP response (in bytes)
78+ * Outputs larger than this will be saved to working directory instead of returned directly
79+ * Claude Desktop has a 1MB limit, so we use 850KB to stay safely under
80+ */
81+ const MAX_MCP_RESPONSE_SIZE = 850 * 1024 ; // 850KB - safe for Claude Desktop (< 1MB limit)
82+
7583/**
7684 * Track active child processes for graceful shutdown
7785 */
@@ -646,27 +654,54 @@ export async function handleToolCall(
646654
647655 if ( outputFile ) {
648656 if ( autoCreatedTempFile ) {
649- // Read temp file contents and return them
657+ // Check temp file size before deciding how to handle it
650658 try {
651- const { readFile, unlink } = await import ( 'fs/promises' ) ;
652- const fileContents = await readFile ( outputFile , 'utf-8 ') ;
659+ const { stat , readFile, unlink, rename } = await import ( 'fs/promises' ) ;
660+ const { join } = await import ( 'path ') ;
653661
654- // Clean up temp file
655- try {
656- await unlink ( outputFile ) ;
657- console . error ( `[MCP Tools] Deleted temp file: ${ outputFile } ` ) ;
658- } catch ( unlinkError ) {
659- console . error ( `[MCP Tools] Failed to delete temp file:` , unlinkError ) ;
660- }
662+ const tempFileStats = await stat ( outputFile ) ;
663+
664+ if ( tempFileStats . size > MAX_MCP_RESPONSE_SIZE ) {
665+ // Output too large for MCP response - save to working directory instead
666+ console . error ( `[MCP Tools] Output file (${ formatBytes ( tempFileStats . size ) } ) exceeds MCP response limit (${ formatBytes ( MAX_MCP_RESPONSE_SIZE ) } )` ) ;
667+
668+ const timestamp = new Date ( ) . toISOString ( ) . replace ( / [: .] / g, '-' ) . replace ( 'T' , '_' ) . split ( '.' ) [ 0 ] ;
669+ const savedFileName = `qsv-${ commandName } -${ timestamp } .csv` ;
670+ const savedPath = join ( config . workingDir , savedFileName ) ;
661671
662- // Return the file contents
663- responseText = fileContents ;
672+ // Move temp file to working directory
673+ await rename ( outputFile , savedPath ) ;
674+ console . error ( `[MCP Tools] Saved large output to: ${ savedPath } ` ) ;
675+
676+ responseText = `✅ Large output saved to file (too large to display in chat)\n\n` ;
677+ responseText += `File: ${ savedFileName } \n` ;
678+ responseText += `Location: ${ config . workingDir } \n` ;
679+ responseText += `Size: ${ formatBytes ( tempFileStats . size ) } \n` ;
680+ responseText += `Duration: ${ result . metadata . duration } ms\n\n` ;
681+ responseText += `The file is now available in your working directory and can be processed with additional qsv commands.` ;
682+
683+ } else {
684+ // Small enough - return contents directly
685+ console . error ( `[MCP Tools] Output file (${ formatBytes ( tempFileStats . size ) } ) is small enough to return directly` ) ;
686+ const fileContents = await readFile ( outputFile , 'utf-8' ) ;
687+
688+ // Clean up temp file
689+ try {
690+ await unlink ( outputFile ) ;
691+ console . error ( `[MCP Tools] Deleted temp file: ${ outputFile } ` ) ;
692+ } catch ( unlinkError ) {
693+ console . error ( `[MCP Tools] Failed to delete temp file:` , unlinkError ) ;
694+ }
695+
696+ // Return the file contents
697+ responseText = fileContents ;
698+ }
664699 } catch ( readError ) {
665- console . error ( `[MCP Tools] Failed to read temp file:` , readError ) ;
700+ console . error ( `[MCP Tools] Failed to process temp file:` , readError ) ;
666701 return {
667702 content : [ {
668703 type : 'text' as const ,
669- text : `Error reading output from temp file: ${ readError instanceof Error ? readError . message : String ( readError ) } ` ,
704+ text : `Error processing output from temp file: ${ readError instanceof Error ? readError . message : String ( readError ) } ` ,
670705 } ] ,
671706 isError : true ,
672707 } ;
0 commit comments