Skip to main content
Glama
PV-Bhat

GemForge-Gemini-Tools-MCP

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
NameRequiredDescriptionDefault
file_pathYesPath to the file or array of file paths
instructionNoSpecific instruction for processing
model_idNoOptional model ID override (advanced users only)
operationNoSpecific operation type
use_large_context_modelNoSet true if the file is very large to use Gemini 1.5 Pro

Implementation Reference

  • 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'],
      },
    },
  • 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;
    }
  • 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)
Install Server

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/PV-Bhat/GemForge-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server