Skip to main content
Glama

delete_comment

Remove a comment and all its threaded replies from a DOCX document. Specify the file path and comment ID to delete comments while preserving document formatting.

Instructions

Delete a comment and all its threaded replies from the document. Cascade-deletes all descendants.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
file_pathYesPath to the DOCX file.
comment_idYesComment ID to delete.

Implementation Reference

  • MCP tool handler that validates input, resolves the document session, calls the core deleteComment primitive, and returns the appropriate tool response.
    export async function deleteComment(
      manager: SessionManager,
      params: {
        file_path?: string;
        comment_id?: number;
      },
    ): Promise<ToolResponse> {
      const resolved = await resolveSessionForTool(manager, params, { toolName: 'delete_comment' });
      if (!resolved.ok) return resolved.response;
      const { session, metadata } = resolved;
    
      if (params.comment_id == null) {
        return err('MISSING_PARAMETER', 'comment_id is required.', 'Provide the comment ID to delete.');
      }
    
      try {
        await session.doc.deleteComment({ commentId: params.comment_id });
    
        manager.markEdited(session);
        return ok(mergeSessionResolutionMetadata({
          comment_id: params.comment_id,
          file_path: manager.normalizePath(session.originalPath),
        }, metadata));
      } catch (e: unknown) {
        const msg = errorMessage(e);
        if (msg.includes('not found')) {
          return err('COMMENT_NOT_FOUND', msg, 'Use get_comments to list available comments.');
        }
        return err('COMMENT_ERROR', msg);
      }
    }
  • Core implementation of comment deletion. It manipulates the document XML structure, including removing comment elements, extended metadata, and associated markers/references in document.xml.
    export async function deleteComment(
      documentXml: Document,
      zip: DocxZip,
      params: { commentId: number },
    ): Promise<void> {
      const { commentId } = params;
    
      const commentsText = await zip.readTextOrNull('word/comments.xml');
      if (!commentsText) throw new Error(`Comment ID ${commentId} not found`);
    
      const commentsDoc = parseXml(commentsText);
    
      // Find the target comment element and its paraId
      const targetEl = findCommentElementById(commentsDoc, commentId);
      if (!targetEl) throw new Error(`Comment ID ${commentId} not found`);
    
      const targetParaId = getCommentElParaId(targetEl);
    
      // Collect all IDs to delete: the target + all transitive descendants
      const idsToDelete = new Set<number>([commentId]);
      const paraIdsToDelete = new Set<string>();
      if (targetParaId) paraIdsToDelete.add(targetParaId);
    
      // Build paraId→commentId and paraId→commentEl maps for all comments
      const paraIdToId = new Map<string, number>();
      const allCommentEls = commentsDoc.getElementsByTagNameNS(OOXML.W_NS, W.comment);
      for (let i = 0; i < allCommentEls.length; i++) {
        const el = allCommentEls.item(i) as Element;
        const idStr = el.getAttributeNS(OOXML.W_NS, 'id') ?? el.getAttribute('w:id');
        const id = idStr ? parseInt(idStr, 10) : -1;
        if (id < 0) continue;
        const pid = getCommentElParaId(el);
        if (pid) paraIdToId.set(pid, id);
      }
    
      // Read commentsExtended.xml to find descendants via paraIdParent graph
      const extText = await zip.readTextOrNull('word/commentsExtended.xml');
      if (extText) {
        const extDoc = parseXml(extText);
        const exEls = extDoc.getElementsByTagNameNS(OOXML.W15_NS, 'commentEx');
    
        // Build parent→children map
        const childrenOf = new Map<string, string[]>();
        for (let i = 0; i < exEls.length; i++) {
          const ex = exEls.item(i) as Element;
          const childPid = ex.getAttributeNS(OOXML.W15_NS, 'paraId') ?? ex.getAttribute('w15:paraId');
          const parentPid = ex.getAttributeNS(OOXML.W15_NS, 'paraIdParent') ?? ex.getAttribute('w15:paraIdParent');
          if (childPid && parentPid) {
            const arr = childrenOf.get(parentPid);
            if (arr) arr.push(childPid);
            else childrenOf.set(parentPid, [childPid]);
          }
        }
    
        // BFS from target paraId to collect all descendant paraIds
        const queue = targetParaId ? [targetParaId] : [];
        while (queue.length > 0) {
          const pid = queue.shift()!;
          const children = childrenOf.get(pid);
          if (!children) continue;
          for (const childPid of children) {
            if (!paraIdsToDelete.has(childPid)) {
              paraIdsToDelete.add(childPid);
              const childId = paraIdToId.get(childPid);
              if (childId != null) idsToDelete.add(childId);
              queue.push(childPid);
            }
          }
        }
      }
    
      // 1. Remove comment elements from comments.xml
      const elsToRemove: Element[] = [];
      for (let i = 0; i < allCommentEls.length; i++) {
        const el = allCommentEls.item(i) as Element;
        const idStr = el.getAttributeNS(OOXML.W_NS, 'id') ?? el.getAttribute('w:id');
        const id = idStr ? parseInt(idStr, 10) : -1;
        if (idsToDelete.has(id)) elsToRemove.push(el);
      }
      for (const el of elsToRemove) {
        el.parentNode?.removeChild(el);
      }
      zip.writeText('word/comments.xml', serializeXml(commentsDoc));
    
      // 2. Remove commentEx entries from commentsExtended.xml (if present)
      if (extText) {
        const extDoc = parseXml(extText);
        const exEls = extDoc.getElementsByTagNameNS(OOXML.W15_NS, 'commentEx');
        const exToRemove: Element[] = [];
        for (let i = 0; i < exEls.length; i++) {
          const ex = exEls.item(i) as Element;
          const pid = ex.getAttributeNS(OOXML.W15_NS, 'paraId') ?? ex.getAttribute('w15:paraId');
          if (pid && paraIdsToDelete.has(pid)) exToRemove.push(ex);
        }
        for (const ex of exToRemove) {
          ex.parentNode?.removeChild(ex);
        }
        zip.writeText('word/commentsExtended.xml', serializeXml(extDoc));
      }
    
      // 3. Remove range markers and commentReference from document.xml (for root comments)
      for (const cid of idsToDelete) {
        removeCommentMarkersFromDocument(documentXml, cid);
      }
    }

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