Skip to content

Commit 207274c

Browse files
committed
refactor: make output/temp file processing smarter
- for small output less than 1mb (Claude Desktop limit), return output to stdout - for large output, auto use --output option and return metadata to user of the output file auto-created in the working dir
1 parent 5801506 commit 207274c

File tree

1 file changed

+49
-14
lines changed

1 file changed

+49
-14
lines changed

.claude/skills/src/mcp-tools.ts

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type { QsvSkill, Argument, Option, McpToolDefinition, McpToolProperty, Fi
1111
import type { SkillExecutor } from './executor.js';
1212
import type { SkillLoader } from './loader.js';
1313
import { 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
*/
7374
const 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

Comments
 (0)