get_footnotes
Extract footnotes from DOCX files with IDs, numbers, text, and paragraph references for document analysis.
Instructions
Get all footnotes from the document with IDs, display numbers, text, and anchored paragraph IDs. Read-only.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| file_path | Yes | Path to the DOCX file. |
Implementation Reference
- The MCP tool handler for "get_footnotes" that resolves the document session and calls `session.doc.getFootnotes()`.
export async function getFootnotes( manager: SessionManager, params: { file_path?: string; }, ): Promise<ToolResponse> { const resolved = await resolveSessionForTool(manager, params, { toolName: 'get_footnotes' }); if (!resolved.ok) return resolved.response; const { session, metadata } = resolved; try { const footnotes = await session.doc.getFootnotes(); return ok(mergeSessionResolutionMetadata({ footnotes: footnotes.map((f) => ({ id: f.id, display_number: f.displayNumber, text: f.text, anchored_paragraph_id: f.anchoredParagraphId, })), file_path: manager.normalizePath(session.originalPath), }, metadata)); } catch (e: unknown) { return err('FOOTNOTE_ERROR', errorMessage(e)); } } - The core implementation logic that parses OOXML footnotes and returns the data structure used by the MCP tool.
export async function getFootnotes(zip: DocxZip, documentXml: Document): Promise<Footnote[]> { const footnotesText = await zip.readTextOrNull('word/footnotes.xml'); if (!footnotesText) return []; const footnotesDoc = parseXml(footnotesText); const fnEls = footnotesDoc.getElementsByTagNameNS(OOXML.W_NS, W.footnote); if (fnEls.length === 0) return []; const displayMap = buildDisplayNumberMap(documentXml, footnotesDoc); // Build map of footnoteReference id → anchored paragraph bookmark id const anchorMap = new Map<number, string | null>(); const refs = documentXml.getElementsByTagNameNS(OOXML.W_NS, W.footnoteReference); for (let i = 0; i < refs.length; i++) { const ref = refs.item(i) as Element; const idStr = getWAttr(ref, 'id'); if (!idStr) continue; const id = parseInt(idStr, 10); if (anchorMap.has(id)) continue; // Walk up to enclosing <w:p> let parent = ref.parentNode; while (parent && parent.nodeType === 1) { const pel = parent as Element; if (pel.localName === W.p && pel.namespaceURI === OOXML.W_NS) { anchorMap.set(id, getParagraphBookmarkId(pel)); break; } parent = parent.parentNode; } } const footnotes: Footnote[] = []; for (let i = 0; i < fnEls.length; i++) { const el = fnEls.item(i) as Element; if (isReservedFootnote(el)) continue; const idStr = getWAttr(el, 'id'); if (!idStr) continue; const id = parseInt(idStr, 10); const text = extractFootnoteText(el); const displayNumber = displayMap.get(id) ?? 0; const anchoredParagraphId = anchorMap.get(id) ?? null; footnotes.push({ id, displayNumber, text, anchoredParagraphId }); } // Sort by display number (document order) footnotes.sort((a, b) => a.displayNumber - b.displayNumber); return footnotes; }