Skip to main content
Glama

docx-openFile

Open a .docx file from disk into memory for editing and management within the DOCX MCP Server, returning a file ID for subsequent operations.

Instructions

Open a .docx file from disk into memory and return id.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
idNo
pathYes

Implementation Reference

  • Main handler for 'docx-openFile' tool: validates args, parses DOCX file to JSON using helper, generates ID if needed, registers in DocRegistry, returns ID.
    case "docx-openFile": { const { id, path: filePath } = parseArgs<{ id?: string; path: string }>(args, tools["docx-openFile"].inputSchema); const json = await parseDocxFileToJson(filePath); const docId = id ?? nanoid(); registry.open(docId, json); return ok({ id: docId }); }
  • Input schema definition for 'docx-openFile' tool, requiring 'path' and optional 'id'.
    "docx-openFile": { description: "Open a .docx file from disk into memory and return id.", inputSchema: { type: "object", required: ["path"], properties: { id: { type: "string" }, path: { type: "string" } } } },
  • src/index.ts:101-103 (registration)
    Registers the listTools endpoint which exposes 'docx-openFile' via the tools object.
    server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: Object.entries(tools).map(([name, t]) => ({ name, description: t.description, inputSchema: t.inputSchema as any })) }));
  • Core helper: Parses DOCX ZIP buffer to structured JSON (metadata + content blocks like paragraphs, tables). Called indirectly by handler.
    export async function parseDocxBufferToJson(buf: Uint8Array): Promise<DocxJSON> { const zip = await JSZip.loadAsync(buf as any); // Parse core properties const coreXml = await zip.file("docProps/core.xml")?.async("string"); const appXml = await zip.file("docProps/app.xml")?.async("string"); const meta: DocxJSON["meta"] = {}; if (coreXml) { const core = await parseStringPromise(coreXml); const c = core["cp:coreProperties"] || {}; meta.title = textOf(c["dc:title"]?.[0]); meta.subject = textOf(c["dc:subject"]?.[0]); meta.creator = textOf(c["dc:creator"]?.[0]); meta.description = textOf(c["dc:description"]?.[0]); meta.keywords = textOf(c["cp:keywords"]?.[0]); meta.lastModifiedBy = textOf(c["cp:lastModifiedBy"]?.[0]); meta.category = textOf(c["cp:category"]?.[0]); const created = textOf(c["dcterms:created"]?.[0]); const modified = textOf(c["dcterms:modified"]?.[0]); if (created) meta.createdAt = created; if (modified) meta.modifiedAt = modified; } if (appXml) { // company/manager sometimes in app.xml (not always) const app = await parseStringPromise(appXml); const a = app.Properties || {}; meta.company = textOf(a.Company?.[0]); meta.manager = textOf(a.Manager?.[0]); } // Parse document.xml to extract paragraphs/tables at a basic level const docXml = await zip.file("word/document.xml")?.async("string"); const content: any[] = []; if (docXml) { const doc = await parseStringPromise(docXml); const body = doc["w:document"]?.["w:body"]?.[0]; const children: any[] = body ? Object.values(body).flat() as any[] : []; // xml2js gives arrays keyed by tags; iterate in original order via a custom approach // Fallback: manually scan body._children is not available, so we reconstruct by looking at known sequences const seq = [] as any[]; for (const key of Object.keys(body || {})) { const arr = (body as any)[key]; if (Array.isArray(arr)) { for (const item of arr) seq.push({ tag: key, node: item }); } } for (const item of seq) { if (item.tag === "w:p") { const p = item.node; const pPr = p["w:pPr"]?.[0]; let headingLevel: number | undefined; const styleVal = pPr?.["w:pStyle"]?.[0]?.["$"]?.["w:val"]; if (typeof styleVal === "string") { const m = /Heading([1-6])/.exec(styleVal); if (m) headingLevel = parseInt(m[1], 10); } const runs = [] as any[]; for (const r of p["w:r"] || []) { const t = textOf(r["w:t"]?.[0]); if (t) { const rPr = r["w:rPr"]?.[0] || {}; runs.push({ type: "text", text: t, bold: rPr["w:b"] ? true : undefined, italics: rPr["w:i"] ? true : undefined, underline: rPr["w:u"] ? true : undefined, }); } } content.push(headingLevel ? { type: "heading", level: headingLevel, children: runs } : { type: "paragraph", children: runs }); } else if (item.tag === "w:tbl") { const tbl = item.node; const rows = [] as any[]; for (const tr of tbl["w:tr"] || []) { const cells = [] as any[]; for (const tc of tr["w:tc"] || []) { const paras = [] as any[]; for (const p of tc["w:p"] || []) { const runs = [] as any[]; for (const r of p["w:r"] || []) { const t = textOf(r["w:t"]?.[0]); if (t) runs.push({ type: "text", text: t }); } paras.push({ type: "paragraph", children: runs }); } cells.push({ children: paras }); } rows.push({ cells }); } content.push({ type: "table", rows }); } } } const json: DocxJSON = { meta, content }; return json; }
  • DocRegistry.open: Registers the parsed JSON as a managed document by calling create.
    open(id: DocId, json: DocxJSON): ManagedDoc { // open means register from existing JSON (e.g., load from disk by caller) return this.create(id, json); }

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/lihongjie0209/docx-mcp'

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