document-mcp 2.0.9 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,9 +15,12 @@ graph TD
15
15
  mdcodec("markdown-codec")
16
16
  bytecodec("byte-codec")
17
17
  documents("documents.js")
18
+ outline("document-outline.js")
18
19
  mcp("document-mcp")
19
20
  cli("document-cli")
20
21
 
22
+ schema --> outline
23
+ outline --> mcp
21
24
  schema --> ooxml
22
25
  schema --> odf
23
26
  schema --> pdfcodec
@@ -42,6 +45,7 @@ graph TD
42
45
  click mdcodec "https://github.com/ExaDev/markdown-codec" "markdown-codec"
43
46
  click bytecodec "https://github.com/ExaDev/byte-codec" "byte-codec"
44
47
  click documents "https://github.com/ExaDev/documents.js" "documents.js"
48
+ click outline "https://github.com/ExaDev/document-outline.js" "document-outline.js"
45
49
  click mcp "https://github.com/ExaDev/document-mcp" "document-mcp"
46
50
  click cli "https://github.com/ExaDev/document-cli" "document-cli"
47
51
 
@@ -210,6 +214,7 @@ Every tool that takes or produces document bytes goes through the same two hybri
210
214
  | `pdf_inspect` | Parses a PDF and reports a summary (page count, per-page size and item-kind histogram, metadata, embedded image formats), or the entire parsed `LayoutDocument` with `full: true`. |
211
215
  | `odm_to_pdf` | Converts a `.odm` (ODF master document) to PDF. A `.odm` never carries its chapters' content inline, so each chapter resolves via a caller-supplied `chapters` href-to-document map and/or a `chaptersDir` searched by basename. |
212
216
  | `from_package` | Rebuilds real document bytes in a target format from a `DocumentPackage` previously serialised to JSON (e.g. by a conversion tool's own `onDocument`/package-dump step). |
217
+ | `outline_document` | Projects a document's own table of contents as structured JSON: groups (`{ text, level, children }`) for headings, list items, slides, sheets, and draw pages, leaves (`{ kind, text }`) for the content between them. The outline is over the source's own content — read through `documents.js`'s `DocumentConverter` port and built by `document-outline.js`'s `buildOutline`. |
213
218
  | `odb_tables` | Lists every table an embedded `.odb` database declares — column names, types, and row data — across every storage tier `documents.js` supports (HSQLDB TEXT/CACHED/BINARY, Firebird gbak backups). |
214
219
  | `odb_forms` | Lists every form an `.odb` database declares, with each form's own data source and field-bound controls. |
215
220
  | `odb_reports` | Lists every report an `.odb` database declares, with each report's own data-source command, band/group structure, and `rpt:` formula expressions. |
@@ -221,12 +226,13 @@ Every tool that takes or produces document bytes goes through the same two hybri
221
226
  ## References
222
227
 
223
228
  - [documents.js](https://github.com/ExaDev/documents.js) — the library this server exposes.
229
+ - [document-outline.js](https://github.com/ExaDev/document-outline.js) — the artefact-utilities package over document-schema.js's tree-form `DocumentPackage` whose `buildOutline` powers `outline_document`.
224
230
  - [document-cli](https://github.com/ExaDev/document-cli) — the sibling CLI/TUI over the same library, whose toolchain this repository's scaffold mirrors.
225
231
  - [Model Context Protocol](https://modelcontextprotocol.io) — the protocol this server implements, via [`@modelcontextprotocol/server`](https://www.npmjs.com/package/@modelcontextprotocol/server).
226
232
 
227
233
  ## Gotchas
228
234
 
229
- - **Runtime dependencies are `documents.js` + `@modelcontextprotocol/server` + `zod` only; `pdf-codec` and `odf.js` are devDependencies (test-support only).** Every runtime reach into either — `ProvidedFont`/`FontSubstitution`/`describeFontFace`/the `WinAnsi` substitution shape — goes through `documents.js`'s own re-exports, so a published install pulls in no direct `pdf-codec`/`odf.js` dependency. `odf.js` survives in `devDependencies` solely because `src/test-support/odm-fixture.ts` and `src/test-support/embedded-font-fixture.ts` build real ODF package fixtures from its low-level XML primitives (`zipPackage`/`el`/`rootElement`), and `src/test-support/` is excluded from the `tsdown` build — only `src/index.ts` and `src/bin.ts` are entry points — so neither fixture module ever ships in `dist/`.
235
+ - **Runtime dependencies are `documents.js` + `document-outline.js` + `@modelcontextprotocol/server` + `zod` only; `pdf-codec` and `odf.js` are devDependencies (test-support only).** `document-outline.js` is the one dependency beyond the server stack itself: `outline_document` imports `buildOutline`/`outlineLeafText` from it, and documents.js deliberately does not re-export them (the outline projection lives in the family's artefact-utilities package, which depends only on `document-schema.js` — already a transitive dependency via documents.js — so it adds no second copy of anything). Every runtime reach into `pdf-codec`/`odf.js` — `ProvidedFont`/`FontSubstitution`/`describeFontFace`/the `WinAnsi` substitution shape — goes through `documents.js`'s own re-exports, so a published install pulls in no direct `pdf-codec`/`odf.js` dependency. `odf.js` survives in `devDependencies` solely because `src/test-support/odm-fixture.ts` and `src/test-support/embedded-font-fixture.ts` build real ODF package fixtures from its low-level XML primitives (`zipPackage`/`el`/`rootElement`), and `src/test-support/` is excluded from the `tsdown` build — only `src/index.ts` and `src/bin.ts` are entry points — so neither fixture module ever ships in `dist/`.
230
236
 
231
237
  ## License
232
238
 
package/dist/bin.js CHANGED
@@ -6,8 +6,9 @@ import { z } from "zod";
6
6
  import { readFile, writeFile } from "node:fs/promises";
7
7
  import { existsSync, readFileSync } from "node:fs";
8
8
  import { basename, join } from "node:path";
9
+ import { buildOutline, isOutlineNode, outlineLeafText } from "document-outline.js";
9
10
  //#region package.json
10
- var version = "2.0.9";
11
+ var version = "2.1.0";
11
12
  //#endregion
12
13
  //#region src/io/document-input.ts
13
14
  const EXTENSION_TO_FORMAT = {
@@ -773,6 +774,69 @@ function registerOdmTools(server) {
773
774
  });
774
775
  }
775
776
  //#endregion
777
+ //#region src/tools/outline.ts
778
+ const OUTLINE_PROBE_TARGETS = {
779
+ csv: "ods",
780
+ docx: "odt",
781
+ odf: "pdf",
782
+ odg: "pdf",
783
+ odt: "docx",
784
+ odp: "pptx",
785
+ ods: "xlsx",
786
+ markdown: "docx",
787
+ pdf: "docx",
788
+ pptx: "odp",
789
+ svg: "odg",
790
+ xlsx: "ods"
791
+ };
792
+ function leafKind(leaf) {
793
+ if ("kind" in leaf) return leaf.kind;
794
+ if ("mathml" in leaf) return "formula";
795
+ return "embeddedObject";
796
+ }
797
+ function toOutlineJson(children) {
798
+ return children.map((child) => isOutlineNode(child) ? {
799
+ text: child.text,
800
+ level: child.level,
801
+ children: toOutlineJson(child.children)
802
+ } : {
803
+ kind: leafKind(child),
804
+ text: outlineLeafText(child)
805
+ });
806
+ }
807
+ function registerOutlineTools(server) {
808
+ const converter = createLocalDocumentConverter();
809
+ server.registerTool("outline_document", {
810
+ title: "Outline document",
811
+ description: "Projects a document's table of contents as a structured outline: reads the source through documents.js's DocumentConverter port, takes the tree-form DocumentPackage the conversion reports, and runs document-outline.js's buildOutline over it. Groups carry { text, level, children } (a heading's, list item's, slide's, sheet's, or page's own label plus nested children); leaves carry { kind, text }. The outline is over the source's own content -- the internal conversion target preserves it and is pdf only for odf (the one format that only converts to pdf) and odg (whose alternative, svg, cannot represent more than one page).",
812
+ inputSchema: z.object({ source: DocumentInputSchema.describe("The document to outline.") })
813
+ }, async ({ source }, ctx) => {
814
+ const { signal } = ctx.mcpReq;
815
+ const { bytes, format } = await resolveDocumentInput(source, { signal });
816
+ const targetFormat = OUTLINE_PROBE_TARGETS[format];
817
+ const pkg = (await converter.convert({
818
+ source: {
819
+ format,
820
+ bytes
821
+ },
822
+ targetFormat
823
+ }, { signal })).package;
824
+ if (pkg === void 0) throw new Error(`the ${format}-to-${targetFormat} conversion reported no DocumentPackage, so there is no tree to outline`);
825
+ const structuredContent = {
826
+ sourceFormat: format,
827
+ kind: pkg.kind,
828
+ outline: toOutlineJson(buildOutline(pkg))
829
+ };
830
+ return {
831
+ content: [{
832
+ type: "text",
833
+ text: JSON.stringify(structuredContent)
834
+ }],
835
+ structuredContent
836
+ };
837
+ });
838
+ }
839
+ //#endregion
776
840
  //#region src/tools/pdf-inspect.ts
777
841
  function buildItemKindHistogram(items) {
778
842
  const histogram = /* @__PURE__ */ new Map();
@@ -838,6 +902,7 @@ function createServer() {
838
902
  registerOdbTools(server);
839
903
  registerOdbRenderReportTools(server);
840
904
  registerOdmTools(server);
905
+ registerOutlineTools(server);
841
906
  registerPdfInspectTools(server);
842
907
  return server;
843
908
  }
package/dist/index.cjs CHANGED
@@ -5,8 +5,9 @@ let zod = require("zod");
5
5
  let node_fs_promises = require("node:fs/promises");
6
6
  let node_fs = require("node:fs");
7
7
  let node_path = require("node:path");
8
+ let document_outline_js = require("document-outline.js");
8
9
  //#region package.json
9
- var version = "2.0.9";
10
+ var version = "2.1.0";
10
11
  //#endregion
11
12
  //#region src/io/document-input.ts
12
13
  const EXTENSION_TO_FORMAT = {
@@ -772,6 +773,69 @@ function registerOdmTools(server) {
772
773
  });
773
774
  }
774
775
  //#endregion
776
+ //#region src/tools/outline.ts
777
+ const OUTLINE_PROBE_TARGETS = {
778
+ csv: "ods",
779
+ docx: "odt",
780
+ odf: "pdf",
781
+ odg: "pdf",
782
+ odt: "docx",
783
+ odp: "pptx",
784
+ ods: "xlsx",
785
+ markdown: "docx",
786
+ pdf: "docx",
787
+ pptx: "odp",
788
+ svg: "odg",
789
+ xlsx: "ods"
790
+ };
791
+ function leafKind(leaf) {
792
+ if ("kind" in leaf) return leaf.kind;
793
+ if ("mathml" in leaf) return "formula";
794
+ return "embeddedObject";
795
+ }
796
+ function toOutlineJson(children) {
797
+ return children.map((child) => (0, document_outline_js.isOutlineNode)(child) ? {
798
+ text: child.text,
799
+ level: child.level,
800
+ children: toOutlineJson(child.children)
801
+ } : {
802
+ kind: leafKind(child),
803
+ text: (0, document_outline_js.outlineLeafText)(child)
804
+ });
805
+ }
806
+ function registerOutlineTools(server) {
807
+ const converter = (0, documents_js.createLocalDocumentConverter)();
808
+ server.registerTool("outline_document", {
809
+ title: "Outline document",
810
+ description: "Projects a document's table of contents as a structured outline: reads the source through documents.js's DocumentConverter port, takes the tree-form DocumentPackage the conversion reports, and runs document-outline.js's buildOutline over it. Groups carry { text, level, children } (a heading's, list item's, slide's, sheet's, or page's own label plus nested children); leaves carry { kind, text }. The outline is over the source's own content -- the internal conversion target preserves it and is pdf only for odf (the one format that only converts to pdf) and odg (whose alternative, svg, cannot represent more than one page).",
811
+ inputSchema: zod.z.object({ source: DocumentInputSchema.describe("The document to outline.") })
812
+ }, async ({ source }, ctx) => {
813
+ const { signal } = ctx.mcpReq;
814
+ const { bytes, format } = await resolveDocumentInput(source, { signal });
815
+ const targetFormat = OUTLINE_PROBE_TARGETS[format];
816
+ const pkg = (await converter.convert({
817
+ source: {
818
+ format,
819
+ bytes
820
+ },
821
+ targetFormat
822
+ }, { signal })).package;
823
+ if (pkg === void 0) throw new Error(`the ${format}-to-${targetFormat} conversion reported no DocumentPackage, so there is no tree to outline`);
824
+ const structuredContent = {
825
+ sourceFormat: format,
826
+ kind: pkg.kind,
827
+ outline: toOutlineJson((0, document_outline_js.buildOutline)(pkg))
828
+ };
829
+ return {
830
+ content: [{
831
+ type: "text",
832
+ text: JSON.stringify(structuredContent)
833
+ }],
834
+ structuredContent
835
+ };
836
+ });
837
+ }
838
+ //#endregion
775
839
  //#region src/tools/pdf-inspect.ts
776
840
  function buildItemKindHistogram(items) {
777
841
  const histogram = /* @__PURE__ */ new Map();
@@ -837,6 +901,7 @@ function createServer() {
837
901
  registerOdbTools(server);
838
902
  registerOdbRenderReportTools(server);
839
903
  registerOdmTools(server);
904
+ registerOutlineTools(server);
840
905
  registerPdfInspectTools(server);
841
906
  return server;
842
907
  }
package/dist/index.js CHANGED
@@ -4,8 +4,9 @@ import { z } from "zod";
4
4
  import { readFile, writeFile } from "node:fs/promises";
5
5
  import { existsSync, readFileSync } from "node:fs";
6
6
  import { basename, join } from "node:path";
7
+ import { buildOutline, isOutlineNode, outlineLeafText } from "document-outline.js";
7
8
  //#region package.json
8
- var version = "2.0.9";
9
+ var version = "2.1.0";
9
10
  //#endregion
10
11
  //#region src/io/document-input.ts
11
12
  const EXTENSION_TO_FORMAT = {
@@ -771,6 +772,69 @@ function registerOdmTools(server) {
771
772
  });
772
773
  }
773
774
  //#endregion
775
+ //#region src/tools/outline.ts
776
+ const OUTLINE_PROBE_TARGETS = {
777
+ csv: "ods",
778
+ docx: "odt",
779
+ odf: "pdf",
780
+ odg: "pdf",
781
+ odt: "docx",
782
+ odp: "pptx",
783
+ ods: "xlsx",
784
+ markdown: "docx",
785
+ pdf: "docx",
786
+ pptx: "odp",
787
+ svg: "odg",
788
+ xlsx: "ods"
789
+ };
790
+ function leafKind(leaf) {
791
+ if ("kind" in leaf) return leaf.kind;
792
+ if ("mathml" in leaf) return "formula";
793
+ return "embeddedObject";
794
+ }
795
+ function toOutlineJson(children) {
796
+ return children.map((child) => isOutlineNode(child) ? {
797
+ text: child.text,
798
+ level: child.level,
799
+ children: toOutlineJson(child.children)
800
+ } : {
801
+ kind: leafKind(child),
802
+ text: outlineLeafText(child)
803
+ });
804
+ }
805
+ function registerOutlineTools(server) {
806
+ const converter = createLocalDocumentConverter();
807
+ server.registerTool("outline_document", {
808
+ title: "Outline document",
809
+ description: "Projects a document's table of contents as a structured outline: reads the source through documents.js's DocumentConverter port, takes the tree-form DocumentPackage the conversion reports, and runs document-outline.js's buildOutline over it. Groups carry { text, level, children } (a heading's, list item's, slide's, sheet's, or page's own label plus nested children); leaves carry { kind, text }. The outline is over the source's own content -- the internal conversion target preserves it and is pdf only for odf (the one format that only converts to pdf) and odg (whose alternative, svg, cannot represent more than one page).",
810
+ inputSchema: z.object({ source: DocumentInputSchema.describe("The document to outline.") })
811
+ }, async ({ source }, ctx) => {
812
+ const { signal } = ctx.mcpReq;
813
+ const { bytes, format } = await resolveDocumentInput(source, { signal });
814
+ const targetFormat = OUTLINE_PROBE_TARGETS[format];
815
+ const pkg = (await converter.convert({
816
+ source: {
817
+ format,
818
+ bytes
819
+ },
820
+ targetFormat
821
+ }, { signal })).package;
822
+ if (pkg === void 0) throw new Error(`the ${format}-to-${targetFormat} conversion reported no DocumentPackage, so there is no tree to outline`);
823
+ const structuredContent = {
824
+ sourceFormat: format,
825
+ kind: pkg.kind,
826
+ outline: toOutlineJson(buildOutline(pkg))
827
+ };
828
+ return {
829
+ content: [{
830
+ type: "text",
831
+ text: JSON.stringify(structuredContent)
832
+ }],
833
+ structuredContent
834
+ };
835
+ });
836
+ }
837
+ //#endregion
774
838
  //#region src/tools/pdf-inspect.ts
775
839
  function buildItemKindHistogram(items) {
776
840
  const histogram = /* @__PURE__ */ new Map();
@@ -836,6 +900,7 @@ function createServer() {
836
900
  registerOdbTools(server);
837
901
  registerOdbRenderReportTools(server);
838
902
  registerOdmTools(server);
903
+ registerOutlineTools(server);
839
904
  registerPdfInspectTools(server);
840
905
  return server;
841
906
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document-mcp",
3
- "version": "2.0.9",
3
+ "version": "2.1.0",
4
4
  "description": "MCP (Model Context Protocol) server exposing documents.js's document-conversion, .odb, metadata, and font tooling as MCP tools.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -85,7 +85,8 @@
85
85
  "packageManager": "pnpm@11.6.0",
86
86
  "dependencies": {
87
87
  "@modelcontextprotocol/server": "^2.0.0",
88
- "documents.js": "^3.1.1",
88
+ "document-outline.js": "^1.0.0",
89
+ "documents.js": "^3.1.2",
89
90
  "zod": "^4.2.0"
90
91
  },
91
92
  "devDependencies": {