Home / Function/ zipRepository() — mcp Function Reference

zipRepository() — mcp Function Reference

Architecture documentation for the zipRepository() function in zip-repository.ts from the mcp codebase.

Entity Profile

Dependency Diagram

graph TD
  1fd46af5_c3e2_8998_1eb2_098430ff3629["zipRepository()"]
  1721f7fd_bb7b_c8c3_9b4b_5677293ae256["resolveOrFetchGraph()"]
  1721f7fd_bb7b_c8c3_9b4b_5677293ae256 -->|calls| 1fd46af5_c3e2_8998_1eb2_098430ff3629
  9003922a_3c12_9d09_182b_1d8c2e1893be["precacheForDirectory()"]
  9003922a_3c12_9d09_182b_1d8c2e1893be -->|calls| 1fd46af5_c3e2_8998_1eb2_098430ff3629
  9a818de6_4969_c29c_0dd7_dab4f6d4fbdb["error()"]
  1fd46af5_c3e2_8998_1eb2_098430ff3629 -->|calls| 9a818de6_4969_c29c_0dd7_dab4f6d4fbdb
  d9b9a00b_7ed2_f6c8_1ca7_1e298c76a167["buildIgnoreFilter()"]
  1fd46af5_c3e2_8998_1eb2_098430ff3629 -->|calls| d9b9a00b_7ed2_f6c8_1ca7_1e298c76a167
  69fc7a46_28f6_6b72_2725_66c381e53322["debug()"]
  1fd46af5_c3e2_8998_1eb2_098430ff3629 -->|calls| 69fc7a46_28f6_6b72_2725_66c381e53322
  ee32b175_3040_f03e_0439_e4935b9ba1ca["estimateDirectorySize()"]
  1fd46af5_c3e2_8998_1eb2_098430ff3629 -->|calls| ee32b175_3040_f03e_0439_e4935b9ba1ca
  acd13495_08c9_df51_1b17_173570f3e5ea["formatBytes()"]
  1fd46af5_c3e2_8998_1eb2_098430ff3629 -->|calls| acd13495_08c9_df51_1b17_173570f3e5ea
  e5352615_c5fb_64a2_cc5c_a248578cbc8a["warn()"]
  1fd46af5_c3e2_8998_1eb2_098430ff3629 -->|calls| e5352615_c5fb_64a2_cc5c_a248578cbc8a
  5c9096db_a74b_41f3_ba88_9738d6d799b5["addFilesRecursively()"]
  1fd46af5_c3e2_8998_1eb2_098430ff3629 -->|calls| 5c9096db_a74b_41f3_ba88_9738d6d799b5
  style 1fd46af5_c3e2_8998_1eb2_098430ff3629 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

src/utils/zip-repository.ts lines 176–340

export async function zipRepository(
  directoryPath: string,
  options: ZipOptions = {}
): Promise<ZipResult> {
  const maxSizeBytes = options.maxSizeBytes || MAX_ZIP_SIZE_BYTES;

  // Validate directory exists
  try {
    const stats = await fs.stat(directoryPath);
    if (!stats.isDirectory()) {
      const errorMsg = `Path is not a directory: ${directoryPath}`;
      logger.error(errorMsg);
      throw new Error(errorMsg);
    }
  } catch (error: any) {
    if (error.code === 'ENOENT') {
      const errorMsg = `Directory does not exist: ${directoryPath}`;
      logger.error(errorMsg);
      throw new Error(errorMsg);
    }
    if (error.code === 'EACCES') {
      const errorMsg = `Permission denied accessing directory: ${directoryPath}`;
      logger.error(errorMsg);
      throw new Error(errorMsg);
    }
    // Re-throw unknown errors with logging
    logger.error('Failed to validate directory:', directoryPath);
    logger.error('Error:', error.message);
    throw error;
  }

  // Parse gitignore files
  const ignoreFilter = await buildIgnoreFilter(
    directoryPath,
    options.additionalExclusions,
    options.includeGitignore
  );

  // Estimate directory size before starting ZIP creation
  logger.debug('Estimating directory size...');
  const estimatedSize = await estimateDirectorySize(directoryPath, ignoreFilter);
  logger.debug('Estimated size:', formatBytes(estimatedSize));

  // Check if estimated size exceeds limit
  if (estimatedSize > maxSizeBytes) {
    throw new Error(
      `Directory size (${formatBytes(estimatedSize)}) exceeds maximum allowed size (${formatBytes(maxSizeBytes)}). ` +
      `Consider excluding more directories or analyzing a subdirectory.`
    );
  }

  // Create temp file path
  const tempDir = tmpdir();
  const zipFileName = `supermodel-${randomBytes(8).toString('hex')}.zip`;
  const zipPath = join(tempDir, zipFileName);

  logger.debug('Creating ZIP:', zipPath);
  logger.debug('Source directory:', directoryPath);

  // Create ZIP archive
  let fileCount = 0;
  let totalSize = 0;

  const output = createWriteStream(zipPath);
  const archive = archiver('zip', {
    zlib: { level: 6 } // Balanced compression
  });

  // Track errors
  let archiveError: Error | null = null;

  archive.on('error', (err) => {
    logger.error('Archive error:', err.message);
    archiveError = err;
  });

  archive.on('warning', (err) => {
    if (err.code === 'ENOENT') {
      logger.warn('File not found (skipping):', err.message);
    } else {
      logger.warn('Archive warning:', err.message);
    }
  });

  // Track progress
  archive.on('entry', (entry) => {
    fileCount++;
    totalSize += entry.stats?.size || 0;

    // Check size limit
    if (totalSize > maxSizeBytes) {
      const errorMsg =
        `ZIP size exceeds limit (${formatBytes(maxSizeBytes)}). ` +
        `Current size: ${formatBytes(totalSize)}. ` +
        `Consider excluding more directories or analyzing a subdirectory.`;
      logger.error(errorMsg);
      archive.abort();
      archiveError = new Error(errorMsg);
    }
  });

  // Pipe to file
  archive.pipe(output);

  // Add files recursively with filtering
  // Initialize progress state if progress callback is provided
  const progressState: ProgressState | undefined = options.onProgress
    ? { filesProcessed: 0, bytesProcessed: 0, lastReportedCount: 0, lastFile: '' }
    : undefined;

  await addFilesRecursively(archive, directoryPath, directoryPath, ignoreFilter, options, progressState);

  // Finalize archive
  await archive.finalize();

  // Wait for output stream to finish
  await new Promise<void>((resolve, reject) => {
    output.on('close', () => {
      if (archiveError) {
        reject(archiveError);
      } else {
        resolve();
      }
    });
    output.on('error', (err) => {
      logger.error('Output stream error:', err.message);
      reject(err);
    });
  });

  // Check for errors during archiving
  if (archiveError) {
    logger.error('Archiving failed, cleaning up partial ZIP');
    // Clean up partial ZIP
    await fs.unlink(zipPath).catch(() => {});
    throw archiveError;
  }

  // Get final file size
  const zipStats = await fs.stat(zipPath);
  const zipSizeBytes = zipStats.size;

  logger.debug('ZIP created successfully');
  logger.debug('Files included:', fileCount);
  logger.debug('ZIP size:', formatBytes(zipSizeBytes));

  // Create cleanup function
  const cleanup = async () => {
    try {
      await fs.unlink(zipPath);
      logger.debug('Cleaned up ZIP:', zipPath);
    } catch (error: any) {
      if (error.code !== 'ENOENT') {
        logger.warn('Failed to cleanup ZIP:', error.message);
      }
    }
  };

  return {
    path: zipPath,
    cleanup,
    fileCount,
    sizeBytes: zipSizeBytes,
  };
}

Domain

Subdomains

Frequently Asked Questions

What does zipRepository() do?
zipRepository() is a function in the mcp codebase.
What does zipRepository() call?
zipRepository() calls 7 function(s): addFilesRecursively, buildIgnoreFilter, debug, error, estimateDirectorySize, formatBytes, warn.
What calls zipRepository()?
zipRepository() is called by 2 function(s): precacheForDirectory, resolveOrFetchGraph.

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free