Skip to main content
Glama

compare_documents

Compare two DOCX documents to generate a tracked-changes output showing differences between original and revised versions.

Instructions

Compare two DOCX documents and produce a tracked-changes output document. Provide original_file_path + revised_file_path for standalone comparison, or file_path to compare session edits against the original.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
original_file_pathNoPath to the original DOCX file.
revised_file_pathNoPath to the revised DOCX file.
file_pathNoPath to the DOCX file.
save_to_local_pathYesPath to save the tracked-changes DOCX output.
authorNoAuthor name for track changes. Default: 'Comparison'.
engineNoComparison engine. Default: 'auto'.

Implementation Reference

  • The `compare_documents_tool` function is the handler that manages the comparison logic. It supports two modes: comparing two specific files (two-file mode) or comparing a current document session state (session mode). It validates input, prepares document buffers, runs the comparison using `@usejunior/docx-core`'s `compareDocuments` function, and saves the resulting document.
    export async function compareDocuments_tool(
      manager: SessionManager,
      params: {
        original_file_path?: string;
        revised_file_path?: string;
        file_path?: string;
        save_to_local_path: string;
        author?: string;
        engine?: string;
      },
    ): Promise<ToolResponse> {
      try {
        const hasOriginal = typeof params.original_file_path === 'string' && params.original_file_path.trim().length > 0;
        const hasRevised = typeof params.revised_file_path === 'string' && params.revised_file_path.trim().length > 0;
        const hasSession = typeof params.file_path === 'string' && params.file_path.trim().length > 0;
    
        // Determine mode
        const twoFileMode = hasOriginal && hasRevised;
        const sessionMode = !twoFileMode && hasSession;
    
        if (!twoFileMode && !sessionMode) {
          return err(
            'MISSING_PARAMS',
            'Provide original_file_path + revised_file_path for two-file comparison, or file_path for session comparison.',
            'Two-file mode compares two DOCX files. Session mode compares the current session state against the original.',
          );
        }
    
        // Validate engine
        const engine = params.engine ?? 'auto';
        if (engine !== 'auto' && engine !== 'atomizer') {
          if (engine === 'wmlcomparer') {
            return err('INVALID_ENGINE', "Engine 'wmlcomparer' is not supported.", "Use 'auto' or 'atomizer'.");
          }
          return err('INVALID_ENGINE', `Invalid engine: ${String(engine)}`, "Use 'auto' or 'atomizer'.");
        }
        const compareEngine: CompareOptions['engine'] = engine;
    
        const author = params.author ?? 'Comparison';
    
        let originalBuffer: Buffer;
        let revisedBuffer: Buffer;
        let sessionMetadata: Record<string, unknown> = {};
        let originalFilePath: string | undefined;
        let revisedFilePath: string | undefined;
    
        if (twoFileMode) {
          // Mode 1: two file paths
          const originalLoaded = await validateAndLoadDocxFromPath(manager, params.original_file_path!);
          if (!originalLoaded.ok) return originalLoaded.response;
    
          const revisedLoaded = await validateAndLoadDocxFromPath(manager, params.revised_file_path!);
          if (!revisedLoaded.ok) return revisedLoaded.response;
    
          originalBuffer = originalLoaded.content;
          revisedBuffer = revisedLoaded.content;
          originalFilePath = originalLoaded.normalizedPath;
          revisedFilePath = revisedLoaded.normalizedPath;
        } else {
          // Mode 2: session edits
          const resolved = await resolveSessionForTool(manager, params, { toolName: 'compare_documents' });
          if (!resolved.ok) return resolved.response;
          const { session, metadata } = resolved;
          sessionMetadata = metadata;
    
          // Lazily generate comparison baselines if not yet available.
          await manager.ensureBaselines(session);
          originalBuffer = session.comparisonBaselineWithBookmarks ?? session.originalBuffer;
          const revised = await session.doc.toBuffer({ cleanBookmarks: false });
          revisedBuffer = revised.buffer;
          originalFilePath = manager.normalizePath(session.originalPath);
        }
    
        // Run comparison
        const result = await runWithoutConsoleLog(() =>
          compareDocuments(originalBuffer, revisedBuffer, {
            author,
            engine: compareEngine,
            reconstructionMode: DEFAULT_RECONSTRUCTION_MODE,
          }),
        );
    
        // Validate and write output
        const savePath = expandPath(params.save_to_local_path);
        const writePolicy = await enforceWritePathPolicy(savePath);
        if (!writePolicy.ok) return writePolicy.response;
    
        await fs.mkdir(path.dirname(savePath), { recursive: true });
        await fs.writeFile(savePath, new Uint8Array(result.document));
    
        const response: Record<string, unknown> = {
          mode: twoFileMode ? 'two_file' : 'session',
          original_file_path: originalFilePath,
          revised_file_path: revisedFilePath,
          saved_to: savePath,
          size_bytes: result.document.length,
          engine_requested: compareEngine,
          engine_used: result.engine,
          author,
          stats: result.stats,
          reconstruction_mode_requested: result.reconstructionModeRequested,
          reconstruction_mode_used: result.reconstructionModeUsed,
          fallback_reason: result.fallbackReason,
          message: twoFileMode
            ? `Redline comparing '${path.basename(originalFilePath!)}' vs '${path.basename(revisedFilePath!)}' saved to ${savePath}`
            : `Redline of session edits saved to ${savePath}`,
        };
    
        if (sessionMode) {
          return ok(mergeSessionResolutionMetadata(response, sessionMetadata));
        }
        return ok(response);
      } catch (e: unknown) {
        const msg = errorMessage(e);
        if (String(errorCode(e) ?? '').toUpperCase() === 'EACCES') {
          return err('PERMISSION_DENIED', `Cannot write to: ${params.save_to_local_path}`, 'Try saving to ~/Downloads/ or ~/Documents/ instead.');
        }
        return err('COMPARE_ERROR', `Comparison failed: ${msg}`);
      }
    }

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/UseJunior/safe-docx'

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