Skip to main content
Glama

accept_changes

Accept all tracked changes in a DOCX document to remove revision markup and produce a clean final version with acceptance statistics.

Instructions

Accept all tracked changes in the document body, producing a clean document with no revision markup. Returns acceptance stats.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
file_pathYesPath to the DOCX file.

Implementation Reference

  • MCP tool handler that calls doc.acceptChanges() and marks the session as edited.
    export async function acceptChanges(
      manager: SessionManager,
      params: { file_path?: string },
    ): Promise<ToolResponse> {
      const resolved = await resolveSessionForTool(manager, params, { toolName: 'accept_changes' });
      if (!resolved.ok) return resolved.response;
      const { session, metadata } = resolved;
    
      try {
        const stats = session.doc.acceptChanges();
        manager.markEdited(session);
        return ok(mergeSessionResolutionMetadata({
          ...stats,
          file_path: manager.normalizePath(session.originalPath),
        }, metadata));
      } catch (e: unknown) {
        return err('ACCEPT_CHANGES_ERROR', errorMessage(e));
      }
    }
  • Core implementation of acceptChanges, which traverses the XML DOM to remove/unwrap revision-related elements.
    export function acceptChanges(doc: Document): AcceptChangesResult {
      const body = doc.getElementsByTagNameNS(W_NS, 'body').item(0);
      if (!body) {
        return { insertionsAccepted: 0, deletionsAccepted: 0, movesResolved: 0, propertyChangesResolved: 0 };
      }
    
      // Phase A — Identify paragraphs to remove
      const paragraphsToRemove = new Set<Element>();
      const allParagraphs = collectByLocalName(body, 'p');
    
      for (const p of allParagraphs) {
        // Paragraph-level deletion marker: w:p > w:pPr > w:rPr > w:del
        if (paragraphHasParaMarker(p, 'del')) {
          paragraphsToRemove.add(p);
          continue;
        }
        // Paragraphs whose only content is inside w:del or w:moveFrom
        if (paragraphHasOnlyRemovedContent(p)) {
          paragraphsToRemove.add(p);
        }
      }
    
      // Phase B — Remove deletions and move sources
      const deletionsAccepted = removeAllByLocalName(body, 'del');
      const moveFromRemoved = removeAllByLocalName(body, 'moveFrom');
      removeAllByLocalName(body, 'moveFromRangeStart');
      removeAllByLocalName(body, 'moveFromRangeEnd');
      removeAllByLocalName(body, 'moveToRangeStart');
      removeAllByLocalName(body, 'moveToRangeEnd');
    
      // Phase C — Unwrap insertions and move destinations (depth-sorted)
      const insertionsAccepted = unwrapAllByLocalName(body, 'ins');
      const moveToUnwrapped = unwrapAllByLocalName(body, 'moveTo');
    
      // Phase D — Remove property change records
      let propertyChangesResolved = 0;
      for (const localName of PR_CHANGE_LOCALS) {
        propertyChangesResolved += removeAllByLocalName(body, localName);
      }
    
      // Phase E — Cleanup
      // Strip paragraph-level revision markers from w:pPr/w:rPr
      for (const p of collectByLocalName(body, 'p')) {
        for (let i = 0; i < p.childNodes.length; i++) {
          const child = p.childNodes[i]!;
          if (!isW(child, 'pPr')) continue;
          for (let j = 0; j < child.childNodes.length; j++) {
            const pPrChild = child.childNodes[j]!;
            if (!isW(pPrChild, 'rPr')) continue;
            // Remove w:ins and w:del marker elements inside pPr > rPr
            const toRemove: Element[] = [];
            for (let k = 0; k < pPrChild.childNodes.length; k++) {
              const rPrChild = pPrChild.childNodes[k]!;
              if (isW(rPrChild, 'ins') || isW(rPrChild, 'del')) {
                toRemove.push(rPrChild as Element);
              }
            }
            for (const el of toRemove) {
              pPrChild.removeChild(el);
            }
          }
        }
      }
    
      // Remove paragraphs collected in Phase A (check parentNode still exists)
      for (const p of paragraphsToRemove) {
        if (p.parentNode) {
          p.parentNode.removeChild(p);
        }
      }
    
      // Strip w:rsidDel attributes on remaining elements
      const allElements = body.getElementsByTagNameNS(W_NS, '*');
      for (let i = 0; i < allElements.length; i++) {
        const el = allElements[i]!;
        if (el.hasAttributeNS(W_NS, 'rsidDel')) {
          el.removeAttributeNS(W_NS, 'rsidDel');
        }
        // Also check prefixed form
        if (el.hasAttribute('w:rsidDel')) {
          el.removeAttribute('w:rsidDel');
        }
      }
    
      return {
        insertionsAccepted,
        deletionsAccepted,
        movesResolved: moveFromRemoved + moveToUnwrapped,
        propertyChangesResolved,
      };
    }
  • Registration of the 'accept_changes' tool in the tool catalog.
    name: 'accept_changes',

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