gemini_fileops
Process files (text, PDF, images, etc.) for summarization, extraction, or analysis using Gemini models. Supports large files with intelligent model selection and specific operation instructions.
Instructions
Performs efficient operations on files (text, PDF, images, etc.) using appropriate Gemini models (Flash-Lite or 1.5 Pro for large files). Use for summarization, extraction, or basic analysis.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| file_path | Yes | Path to the file or array of file paths | |
| instruction | No | Specific instruction for processing | |
| model_id | No | Optional model ID override (advanced users only) | |
| operation | No | Specific operation type | |
| use_large_context_model | No | Set true if the file is very large to use Gemini 1.5 Pro |
Implementation Reference
- src/handlers/fileops.ts:22-211 (handler)The core handler function for the 'gemini_fileops' tool. It validates input files (single or array), handles multiple files by concatenating them, selects appropriate Gemini model, builds and executes the API request, formats the response, and includes fallback logic for large files using gemini-1.5-pro.export async function handleFileops(request: any) { try { // Validate required parameters if (!request.params.arguments || !request.params.arguments.file_path) { throw new McpError( ErrorCode.InvalidParams, 'file_path parameter is required' ); } // Extract arguments as FileopsArgs const args = request.params.arguments as FileopsArgs; const { file_path, instruction, operation, use_large_context_model = false, model_id } = args; // Validate file_path if (typeof file_path !== 'string' && !Array.isArray(file_path)) { throw new McpError( ErrorCode.InvalidParams, 'file_path must be a string or array of strings' ); } // Handle file path validation for both single files and arrays if (Array.isArray(file_path)) { console.error(`[handleFileops] Checking if files exist: ${JSON.stringify(file_path)}`); // Validate each file in the array const validatedPaths = []; for (const path of file_path) { console.error(`[handleFileops] Checking file: ${path}`); const exists = await fileExists(path); if (!exists) { throw new McpError( ErrorCode.InvalidParams, `File not found: ${path}` ); } validatedPaths.push(path); } console.error(`[handleFileops] All files exist and are accessible`); // If we have multiple files, create a temporary file with their contents if (validatedPaths.length > 1) { console.error(`[handleFileops] Processing multiple files: ${validatedPaths.length}`); try { // Concatenate all files into a single temporary file const tempFilePath = await concatenateFiles(validatedPaths); console.error(`[handleFileops] Created temporary file with concatenated content: ${tempFilePath}`); // Update the file_path to use the temporary file args.file_path = tempFilePath; // Add a note about the original files if (!args.instruction) { args.instruction = ""; } args.instruction = `This is a combined file containing the contents of ${validatedPaths.length} files: ${validatedPaths.map(p => path.basename(p)).join(", ")}.\n\n${args.instruction}`; } catch (error) { console.error(`[handleFileops] Error concatenating files:`, error); throw new McpError( ErrorCode.InternalError, `Error processing multiple files: ${error instanceof Error ? error.message : String(error)}` ); } } } else { // Single file path console.error(`[handleFileops] Checking if file exists: ${file_path}`); const exists = await fileExists(file_path); if (!exists) { throw new McpError( ErrorCode.InvalidParams, `File not found: ${file_path}` ); } } // Determine file type for logging (use first file if array) const firstFilePath = Array.isArray(file_path) ? file_path[0] : file_path; const fileType = getFileTypeCategory(firstFilePath); console.error(`[handleFileops] File type: ${fileType}`); // Select the model using the tool-specific model selector const targetModelId = selectToolModel(TOOL_NAMES.GEM_FILEOPS, args); console.error(`[handleFileops] Selected model: ${targetModelId}`); // Build request using the new builder function const internalRequest = await buildFileopsRequest(args); console.error(`[handleFileops] Request built for model: ${targetModelId}`); // Explicitly load file content using preparePartsWithFiles try { const { preparePartsWithFiles } = await import('../utils/file-handler.js'); // Get the instruction text from the first user message let instructionText = ''; const userMsgIndex = internalRequest.contents.findIndex((c: any) => c.role === 'user'); if (userMsgIndex >= 0 && internalRequest.contents[userMsgIndex].parts.length > 0) { instructionText = internalRequest.contents[userMsgIndex].parts[0].text || ''; } console.error(`[handleFileops] Explicitly loading file content for: ${typeof file_path === 'string' ? file_path : JSON.stringify(file_path)}`); const parts = await preparePartsWithFiles(file_path, instructionText); console.error(`[handleFileops] Successfully loaded ${parts.length} parts`); // Replace the parts in the user message with our explicitly loaded parts if (userMsgIndex >= 0) { // Need to cast parts to any to avoid TypeScript errors internalRequest.contents[userMsgIndex].parts = parts as any; console.error(`[handleFileops] Replaced parts in user message at index ${userMsgIndex}`); } else if (internalRequest.contents.length > 0) { // If no user message found but we have contents, replace the first message internalRequest.contents[0].parts = parts as any; console.error(`[handleFileops] Replaced parts in first message`); } } catch (error) { console.error(`[handleFileops] Error explicitly loading file content:`, error); // Continue with the original request if there's an error } try { // Execute request const { response, rawSdkResponse } = await executeRequest(targetModelId, internalRequest); console.error(`[handleFileops] Got response from ${targetModelId}`); // Format response return formatResponse(response, targetModelId, { operation: TOOL_NAMES.GEM_FILEOPS, processingType: operation || 'analyze', withFile: true, fileType: fileType, isImageFile: fileType === 'image', customFormat: { usedLargeContext: use_large_context_model === true } }, rawSdkResponse); } catch (apiError) { console.error('[handleFileops] API error:', apiError); if (apiError instanceof McpError) { throw apiError; } // For large files, try falling back to 1.5 model if not already using it if (isLargeFile(file_path) && targetModelId !== 'gemini-1.5-pro') { console.error('[handleFileops] Large file detected, retrying with gemini-1.5-pro...'); // Create a new args object with large context model flag set to true const fallbackArgs: FileopsArgs = { ...args, use_large_context_model: true }; // Build a new request with the fallback model const fallbackRequest = buildFileopsRequest(fallbackArgs); fallbackRequest.modelId = 'gemini-1.5-pro'; const { response, rawSdkResponse } = await executeRequest('gemini-1.5-pro', fallbackRequest); return formatResponse(response, 'gemini-1.5-pro', { operation: TOOL_NAMES.GEM_FILEOPS, processingType: operation || 'analyze', withFile: true, fileType: fileType, isImageFile: fileType === 'image', customFormat: { usedLargeContext: true, fallbackModel: true } }, rawSdkResponse); } throw apiError; } } catch (error) { console.error('Error in fileops handler:', error); if (error instanceof McpError) { throw error; } return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}` } ], isError: true }; } }
- src/index.ts:206-228 (registration)The switch statement in the CallToolRequest handler that routes 'gemini_fileops' (TOOL_NAMES.GEM_FILEOPS) calls to the handleFileops function.switch (request.params.name) { case TOOL_NAMES.GEM_SEARCH: // Use the alternative direct search handler that works with Flash models return await handleDirectSearchAlt(request); case TOOL_NAMES.GEM_REASON: return await handleReason(request); case TOOL_NAMES.GEM_CODE: return await handleCode(request); case TOOL_NAMES.GEM_FILEOPS: return await handleFileops(request); // Support legacy tool names for backward compatibility case 'gemini_search': // Use the alternative direct search handler that works with Flash models return await handleDirectSearchAlt(request); case 'gemini_reason': return await handleReason(request); default: console.error(`Unknown tool requested: ${request.params.name}`); throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}. Available tools: gemini_search, gemini_reason, gemini_code, gemini_fileops` ); }
- src/index.ts:144-178 (registration)Registration of the 'gemini_fileops' tool in the ListTools response, including name, description, and input schema definition.{ name: TOOL_NAMES.GEM_FILEOPS, description: 'Performs efficient operations on files (text, PDF, images, etc.) using appropriate Gemini models (Flash-Lite or 1.5 Pro for large files). Use for summarization, extraction, or basic analysis.', inputSchema: { type: 'object', properties: { file_path: { oneOf: [ { type: 'string', description: 'Path to a single file' }, { type: 'array', items: { type: 'string' }, description: 'Array of file paths' } ], description: 'Path to the file or array of file paths' }, instruction: { type: 'string', description: 'Specific instruction for processing' }, operation: { type: 'string', description: 'Specific operation type', enum: ['summarize', 'extract', 'analyze'] }, use_large_context_model: { type: 'boolean', description: 'Set true if the file is very large to use Gemini 1.5 Pro', default: false }, model_id: { type: 'string', description: 'Optional model ID override (advanced users only)' } }, required: ['file_path'], }, },
- src/interfaces/tool-args.ts:42-54 (schema)TypeScript interface defining the arguments for the gemini_fileops tool, used for type safety in the handler./** * Arguments for gemini_fileops tool */ export interface FileopsArgs extends BaseArgs, FileInput { /** Specific instruction for processing */ instruction?: string; /** Specific operation type */ operation?: 'summarize' | 'extract' | 'analyze'; /** Set true if the file is very large to use Gemini 1.5 Pro */ use_large_context_model?: boolean; }
- src/config/constants.ts:110-110 (helper)Constant definition for the tool name TOOL_NAMES.GEM_FILEOPS = 'gemini_fileops', referenced in index.ts for registration and dispatch.GEM_FILEOPS: 'gemini_fileops', // For file operations (Gemini 2.0 Flash-Lite/1.5 Pro)