document-mcp 4.4.3 → 4.5.1
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 +33 -24
- package/dist/bin.js +156 -5
- package/dist/index.cjs +97 -1
- package/dist/index.js +98 -2
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/ExaDev/documents.js/tree/main/packages/document-mcp) [](https://www.npmjs.com/package/document-mcp) [](https://www.npmjs.com/package/document-mcp) [](https://github.com/ExaDev/documents.js/actions)
|
|
4
4
|
|
|
5
|
-
> An MCP (Model Context Protocol) server exposing [`documents.js`](https://github.com/ExaDev/documents.js)'s document-conversion, `.odb`, metadata, and font tooling as MCP tools, so an MCP-speaking agent can convert, inspect, and
|
|
5
|
+
> An MCP (Model Context Protocol) server exposing [`documents.js`](https://github.com/ExaDev/documents.js)'s document-conversion, `.odb`, metadata, and font tooling — plus [`document-compute.js`](../document-compute.js/README.md)'s units-typed formula evaluation — as MCP tools, so an MCP-speaking agent can convert, inspect, edit, and compute over docx/pptx/odt/odp/ods/odg/odf/pdf/odb/xlsx/markdown documents without writing TypeScript against either library directly.
|
|
6
6
|
|
|
7
7
|
`document-mcp` adds no conversion or editing logic of its own — it is a dispatch layer over `documents.js`'s existing conversion functions, `DocumentConverter` port, and `.odb`/PDF readers, wired up as MCP tools served over stdio. [`document-cli`](../document-cli/README.md) is the sibling frontend over the identical `documents.js` library — a terminal CLI/TUI rather than an MCP server — so the two are independent consumers of one shared implementation and can expose different subsets of it. A `convert_document` call's fidelity — which `(source, targetFormat)` pairs round-trip losslessly, which are a best-effort reconstruction, and why — is exactly what [`documents.js`'s own Fidelity section](../documents.js/README.md#fidelity) documents, table included; it is not restated here.
|
|
8
8
|
|
|
@@ -16,11 +16,15 @@ graph TD
|
|
|
16
16
|
bytecodec("byte-codec")
|
|
17
17
|
documents("documents.js")
|
|
18
18
|
outline("document-outline.js")
|
|
19
|
+
compute("document-compute.js")
|
|
19
20
|
mcp("document-mcp")
|
|
20
21
|
cli("document-cli")
|
|
21
22
|
|
|
22
23
|
schema --> outline
|
|
23
24
|
outline --> mcp
|
|
25
|
+
schema --> compute
|
|
26
|
+
compute --> mcp
|
|
27
|
+
schema --> mcp
|
|
24
28
|
schema --> ooxml
|
|
25
29
|
schema --> odf
|
|
26
30
|
schema --> pdfcodec
|
|
@@ -46,6 +50,7 @@ graph TD
|
|
|
46
50
|
click bytecodec "https://github.com/ExaDev/documents.js/tree/main/packages/byte-codec" "byte-codec"
|
|
47
51
|
click documents "https://github.com/ExaDev/documents.js" "documents.js"
|
|
48
52
|
click outline "https://github.com/ExaDev/documents.js/tree/main/packages/document-outline.js" "document-outline.js"
|
|
53
|
+
click compute "https://github.com/ExaDev/documents.js/tree/main/packages/document-compute.js" "document-compute.js"
|
|
49
54
|
click mcp "https://github.com/ExaDev/documents.js/tree/main/packages/document-mcp" "document-mcp"
|
|
50
55
|
click cli "https://github.com/ExaDev/documents.js/tree/main/packages/document-cli" "document-cli"
|
|
51
56
|
|
|
@@ -174,9 +179,11 @@ Claude Web, Claude Mobile, and ChatGPT only accept **remote** (HTTP/SSE) MCP ser
|
|
|
174
179
|
npx document-mcp --transport http --port 3000
|
|
175
180
|
```
|
|
176
181
|
|
|
177
|
-
|
|
182
|
+
This starts a real [Streamable HTTP](https://modelcontextprotocol.io) listener bound to `127.0.0.1` (loopback only) on the given `--port` (default `3000`), serving the identical tool set the stdio transport registers — one MCP server instance per request, built from the same `createServer()` factory. `--port 0` asks the OS for a free port; the server reports the port it actually bound on stderr before serving requests. It answers on `/mcp` and 404s everywhere else; a request from an older HTTP+SSE-era client is served transparently alongside current clients on that same endpoint, so there is no separate `--transport sse` to choose.
|
|
178
183
|
|
|
179
|
-
|
|
184
|
+
Then add the server URL (e.g., `https://your-host:3000/mcp`) as a connector in Claude Web (**claude.ai/customize/connectors**) or ChatGPT (**Settings → Connectors → Advanced → Enable Developer Mode → Create**). Use a tunnel (Cloudflare Tunnel, ngrok) or deploy to a server with TLS — both platforms require HTTPS; loopback-only binding means the tunnel or reverse proxy must run on the same host as `document-mcp` itself.
|
|
185
|
+
|
|
186
|
+
> **Security note:** this HTTP listener has no authentication and no Host/Origin allowlisting of its own — anyone who can reach it can call every tool, including ones that read and write arbitrary filesystem paths. It is safe by default only because it binds to loopback; whatever fronts it for remote access (tunnel, reverse proxy) is responsible for authenticating callers before traffic ever reaches this process.
|
|
180
187
|
|
|
181
188
|
### Development
|
|
182
189
|
|
|
@@ -202,37 +209,39 @@ Every tool that takes or produces document bytes goes through the same two hybri
|
|
|
202
209
|
|
|
203
210
|
## Tools
|
|
204
211
|
|
|
205
|
-
| Tool | Description
|
|
206
|
-
| --------------------------- |
|
|
207
|
-
| `convert_document` | Converts a document from one supported format to another via `documents.js`'s `DocumentConverter` port — docx, pptx, xlsx, odt, odp, ods, odg, odf, markdown, and pdf. Not every `(source, targetFormat)` pair is direct; call `list_document_conversions` first.
|
|
208
|
-
| `list_document_conversions` | Lists every `(source, target)` format pair `convert_document` actually supports.
|
|
209
|
-
| `metadata_read` | Reads a document's title/author/subject/keywords/creator/producer/created-and-modified timestamps. Works across every supported format, including xlsx and odf.
|
|
210
|
-
| `metadata_write` | Patches a document's title/author/subject/keywords in place. Does not convert format — source and target format must match (or both be `pdf`); odf (a standalone formula document) is rejected as either, since it has no write path back out at all.
|
|
211
|
-
| `fonts` | Lists every source-embedded font face a docx/pptx/odt/odp/ods/odg document carries (family, weight/style, byte length).
|
|
212
|
-
| `describe_font_file` | Reads a standalone `.ttf`/`.otf` font file and reports the family/bold/italic triple it declares about itself.
|
|
213
|
-
| `docx_extras` | Reads a docx's own comments, footnotes, header/footer parts, and numbering definitions — data the `ContentDocument` pivot cannot carry, so no other tool sees it.
|
|
214
|
-
| `pdf_inspect` | Parses a PDF and reports a summary (page count, per-page size and item-kind histogram, metadata, embedded image formats), or with `full: true` the entire parsed `LayoutDocument` as plain JSON — no `$schema` stamp, since that family moved to `pdf-codec` at `document-schema.js` 4.0.0 and lost its schema-stamped envelope.
|
|
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.
|
|
216
|
-
| `from_package` | Rebuilds real document bytes in a target format from a `DocumentTree` previously serialised to JSON (e.g. by a conversion tool's own `onDocument`/package-dump step). Only a package genuinely written by a current dump round-trips: the `$schema` URI it carries pins the `document-schema.js` release that wrote it, and a pre-4.0.0 dump (the flat `{ formatVersion, content, pages }` envelope) is rejected with an error naming the pinned release, the flat-to-tree change, and the remedy — a layout-document dump gets its own pointer, naming the move to `pdf-codec`.
|
|
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 native content — read directly via `documents.js`'s `readNativeDocumentTree` (no bridging conversion, no discarded output bytes) and built by `document-outline.js`'s `buildOutline`.
|
|
218
|
-
| `
|
|
219
|
-
| `
|
|
220
|
-
| `
|
|
221
|
-
| `
|
|
222
|
-
| `
|
|
223
|
-
| `
|
|
224
|
-
| `
|
|
212
|
+
| Tool | Description |
|
|
213
|
+
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
214
|
+
| `convert_document` | Converts a document from one supported format to another via `documents.js`'s `DocumentConverter` port — docx, pptx, xlsx, odt, odp, ods, odg, odf, markdown, and pdf. Not every `(source, targetFormat)` pair is direct; call `list_document_conversions` first. |
|
|
215
|
+
| `list_document_conversions` | Lists every `(source, target)` format pair `convert_document` actually supports. |
|
|
216
|
+
| `metadata_read` | Reads a document's title/author/subject/keywords/creator/producer/created-and-modified timestamps. Works across every supported format, including xlsx and odf. |
|
|
217
|
+
| `metadata_write` | Patches a document's title/author/subject/keywords in place. Does not convert format — source and target format must match (or both be `pdf`); odf (a standalone formula document) is rejected as either, since it has no write path back out at all. |
|
|
218
|
+
| `fonts` | Lists every source-embedded font face a docx/pptx/odt/odp/ods/odg document carries (family, weight/style, byte length). |
|
|
219
|
+
| `describe_font_file` | Reads a standalone `.ttf`/`.otf` font file and reports the family/bold/italic triple it declares about itself. |
|
|
220
|
+
| `docx_extras` | Reads a docx's own comments, footnotes, header/footer parts, and numbering definitions — data the `ContentDocument` pivot cannot carry, so no other tool sees it. |
|
|
221
|
+
| `pdf_inspect` | Parses a PDF and reports a summary (page count, per-page size and item-kind histogram, metadata, embedded image formats), or with `full: true` the entire parsed `LayoutDocument` as plain JSON — no `$schema` stamp, since that family moved to `pdf-codec` at `document-schema.js` 4.0.0 and lost its schema-stamped envelope. |
|
|
222
|
+
| `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. |
|
|
223
|
+
| `from_package` | Rebuilds real document bytes in a target format from a `DocumentTree` previously serialised to JSON (e.g. by a conversion tool's own `onDocument`/package-dump step). Only a package genuinely written by a current dump round-trips: the `$schema` URI it carries pins the `document-schema.js` release that wrote it, and a pre-4.0.0 dump (the flat `{ formatVersion, content, pages }` envelope) is rejected with an error naming the pinned release, the flat-to-tree change, and the remedy — a layout-document dump gets its own pointer, naming the move to `pdf-codec`. |
|
|
224
|
+
| `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 native content — read directly via `documents.js`'s `readNativeDocumentTree` (no bridging conversion, no discarded output bytes) and built by `document-outline.js`'s `buildOutline`. |
|
|
225
|
+
| `compute_formula` | Reads every formula a document embeds (docx/odt/markdown paragraphs and pptx/odp slide shapes; a table cell's own blocks too — docx's own reader recovers a real equation nested inside a table cell; structurally, the walk also covers a drawing page's own shape flow, though no writer in the family populates a formula there today; a spreadsheet's own cell-anchored formula objects; a standalone `.odf` formula document; and a formula nested inside another embedded object at any depth — e.g. a formula embedded in a drawing embedded in a spreadsheet) and evaluates each through `document-compute.js`'s units-typed `evaluate()` — an agent's way to check whether a document's stated arithmetic actually checks out. A formula referencing a symbol needs that symbol's value supplied via `bindings`, keyed by the document's own symbol-table id; each formula reports its own outcome independently (`evaluated`, `no-content`, or `error`), so one formula needing more bindings never blocks the others. |
|
|
226
|
+
| `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). |
|
|
227
|
+
| `odb_forms` | Lists every form an `.odb` database declares, with each form's own data source and field-bound controls. |
|
|
228
|
+
| `odb_reports` | Lists every report an `.odb` database declares, with each report's own data-source command, band/group structure, and `rpt:` formula expressions. |
|
|
229
|
+
| `odb_query` | Runs a bounded single-table `SELECT` over an embedded `.odb` database's extracted tables — given directly as SQL or by naming a saved query. No database engine involved; an unsupported construct is reported as a tool error naming it, never silently ignored. |
|
|
230
|
+
| `odb_to_csv` | Extracts exactly one named table from an embedded `.odb` database as CSV bytes. The table name is required whenever the database declares more than one table. |
|
|
231
|
+
| `odb_to_xlsx` | Extracts every table an embedded `.odb` database declares into one xlsx workbook, one sheet per table. |
|
|
232
|
+
| `odb_render_report` | Resolves one of an `.odb` database's own reports — its data-bound command run through the bounded SQL engine, its `rpt:` formulas evaluated, its bands laid out — and renders the result to docx, odt, or pdf. |
|
|
225
233
|
|
|
226
234
|
## References
|
|
227
235
|
|
|
228
236
|
- [documents.js](https://github.com/ExaDev/documents.js) — the library this server exposes.
|
|
229
237
|
- [document-outline.js](../document-outline.js/README.md) — the artefact-utilities package over document-schema.js's tree-form `DocumentTree` whose `buildOutline` powers `outline_document`.
|
|
238
|
+
- [document-compute.js](../document-compute.js/README.md) — the units-typed `MathExpression` evaluator whose `evaluate()` powers `compute_formula`.
|
|
230
239
|
- [document-cli](../document-cli/README.md) — the sibling CLI/TUI over the same library, whose toolchain this repository's scaffold mirrors.
|
|
231
240
|
- [Model Context Protocol](https://modelcontextprotocol.io) — the protocol this server implements, via [`@modelcontextprotocol/server`](https://www.npmjs.com/package/@modelcontextprotocol/server).
|
|
232
241
|
|
|
233
242
|
## Gotchas
|
|
234
243
|
|
|
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/
|
|
244
|
+
- **Runtime dependencies are `documents.js` + `document-outline.js` + `document-compute.js` + `document-schema.js` + `@modelcontextprotocol/server` + `@modelcontextprotocol/node` + `zod` only; `pdf-codec` and `odf.js` are devDependencies (test-support only).** `@modelcontextprotocol/node` is the Node adapter (`toNodeHandler`) the `--transport http` listener uses to bridge `@modelcontextprotocol/server`'s web-standard `createMcpHandler` onto a plain `node:http` server — see [Remote transport](#remote-transport-http) above. `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). `document-compute.js` and `document-schema.js` are the pair `compute_formula` needs for the identical reason: `evaluate()` lives in `document-compute.js` (documents.js has never depended on it, being the very gap [ExaDev/documents.js#928](https://github.com/ExaDev/documents.js/issues/928) closed), and documents.js re-exports neither `flattenTree` (the tree-to-flat transform turning `readNativeDocumentTree`'s `DocumentTree` back into the `ContentDocument` a formula walk needs) nor the `ContentFormula`/`MathExpression`/`FormulaBindings`/`Quantity`/`Interval`/`SymbolTable` types `evaluate()`'s own inputs and outputs are shaped by — both come from `document-schema.js` directly, exactly as that package's own README already documents for any `evaluate()` caller. 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`, `src/test-support/embedded-font-fixture.ts`, and `src/test-support/odf-formula-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 none of these fixture modules ever ships in `dist/`.
|
|
236
245
|
|
|
237
246
|
## Contributing
|
|
238
247
|
|
package/dist/bin.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { toNodeHandler } from "@modelcontextprotocol/node";
|
|
5
|
+
import { McpServer, createMcpHandler } from "@modelcontextprotocol/server";
|
|
6
|
+
import { evaluate } from "document-compute.js";
|
|
7
|
+
import { ContentDocumentSchema, EvaluationValueSchema, FormulaBindingsSchema, flattenTree } from "document-schema.js";
|
|
8
|
+
import { DocumentFormatSchema, OdbReportNotSpecifiedError, OdmUnresolvedSectionError, UnrecognizedDocumentSchemaError, base64ToBytes, buildDocumentBytes, bytesToBase64, collectDocumentFormulas, createLocalDocumentConverter, decodeOdbPackage, decodePackage, describeFontFace, documentFromJson, documentSchemaKindOf, evaluateSelect, extractSourceFontsForFormat, odbReportToDocx, odbReportToOdt, odbReportToPdf, odbToCsv, odbToXlsx, odmToPdf, parseSelect, readDocumentMetadata, readDocxExtras, readNativeDocumentTree, readOdbForms, readOdbInventory, readOdbReportContent, readOdbReports, readOdbTables, readPdf, setDocumentMetadata } from "documents.js";
|
|
5
9
|
import { z } from "zod";
|
|
6
10
|
import { readFile, writeFile } from "node:fs/promises";
|
|
7
11
|
import { existsSync, readFileSync } from "node:fs";
|
|
8
12
|
import { basename, join } from "node:path";
|
|
9
13
|
import { buildOutline, isOutlineNode, outlineLeafText } from "document-outline.js";
|
|
10
14
|
//#region package.json
|
|
11
|
-
var version = "4.
|
|
15
|
+
var version = "4.5.1";
|
|
12
16
|
//#endregion
|
|
13
17
|
//#region src/io/document-input.ts
|
|
14
18
|
const EXTENSION_TO_FORMAT = {
|
|
@@ -72,6 +76,99 @@ async function resolveDocumentInput(input, options) {
|
|
|
72
76
|
};
|
|
73
77
|
}
|
|
74
78
|
//#endregion
|
|
79
|
+
//#region src/tools/compute-formula.ts
|
|
80
|
+
function toErrorResult$2(error) {
|
|
81
|
+
return {
|
|
82
|
+
content: [{
|
|
83
|
+
type: "text",
|
|
84
|
+
text: error instanceof Error ? error.message : String(error)
|
|
85
|
+
}],
|
|
86
|
+
isError: true
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function evaluateFormula(formula, bindings, symbolTable) {
|
|
90
|
+
if (formula.content === void 0) return { status: "no-content" };
|
|
91
|
+
try {
|
|
92
|
+
return {
|
|
93
|
+
status: "evaluated",
|
|
94
|
+
result: evaluate(formula.content, bindings, symbolTable)
|
|
95
|
+
};
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return {
|
|
98
|
+
status: "error",
|
|
99
|
+
errorType: error instanceof Error ? error.name : String(error),
|
|
100
|
+
message: error instanceof Error ? error.message : String(error)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const FormulaOutcomeSchema = z.union([
|
|
105
|
+
z.object({
|
|
106
|
+
status: z.literal("evaluated"),
|
|
107
|
+
result: EvaluationValueSchema
|
|
108
|
+
}),
|
|
109
|
+
z.object({ status: z.literal("no-content") }),
|
|
110
|
+
z.object({
|
|
111
|
+
status: z.literal("error"),
|
|
112
|
+
errorType: z.string(),
|
|
113
|
+
message: z.string()
|
|
114
|
+
})
|
|
115
|
+
]);
|
|
116
|
+
const documentKindValues = ContentDocumentSchema.options.map((option) => option.shape.kind.value);
|
|
117
|
+
const ComputeFormulaOutputSchema = z.object({
|
|
118
|
+
sourceFormat: DocumentFormatSchema,
|
|
119
|
+
documentKind: z.enum(documentKindValues),
|
|
120
|
+
formulaCount: z.number(),
|
|
121
|
+
formulas: z.array(z.object({
|
|
122
|
+
index: z.number(),
|
|
123
|
+
sourcePath: z.string().optional(),
|
|
124
|
+
locate: z.string(),
|
|
125
|
+
latex: z.string().optional(),
|
|
126
|
+
outcome: FormulaOutcomeSchema
|
|
127
|
+
}))
|
|
128
|
+
});
|
|
129
|
+
function registerComputeFormulaTools(server) {
|
|
130
|
+
server.registerTool("compute_formula", {
|
|
131
|
+
title: "Compute document formulas",
|
|
132
|
+
description: "Reads every formula a document embeds (docx/odt/markdown paragraphs and pptx/odp slide shapes; a table cell's own blocks too -- docx's own reader recovers a real equation nested inside a table cell; structurally, the walk also covers a drawing page's own shape flow, though no writer in the family populates a formula there today; a spreadsheet's own cell-anchored formula objects; a standalone .odf formula document; and a formula nested inside another embedded object at any depth, e.g. a formula embedded in a drawing embedded in a spreadsheet) and evaluates each through document-compute.js's units-typed evaluate() -- an agent's way to check whether a document's stated arithmetic actually checks out. A formula referencing a symbol (e.g. F = m * a) needs that symbol's value supplied via bindings, keyed by the document's own symbol-table id (see the document's symbolTable.symbols[].id, or a returned formula's own latex to identify which symbol is which); a formula with no free symbols (units and numeric literals only) evaluates with no bindings at all. Each formula reports its own outcome independently -- 'evaluated' with the result, 'no-content' when the formula was never lowered to a computable MathExpression (the common case for a spreadsheet's own embedded formula object, or any format other than markdown, none of which yet stores the semantic layer on disk), or 'error' naming which document-compute.js error it hit (e.g. UnboundSymbolError) -- so one formula needing more bindings never blocks the others. Each entry's own `locate` field is the reliable way to tell two formulas in the same document apart -- a structural path guaranteed unique per formula -- unlike `sourcePath`, which several formats leave undefined or stamp with the identical constant across sibling formulas (markdown's own display-math lowering among them).",
|
|
133
|
+
inputSchema: z.object({
|
|
134
|
+
source: DocumentInputSchema.describe("The document to read formulas from."),
|
|
135
|
+
bindings: FormulaBindingsSchema.optional().describe("Known values for symbols the document's formulas reference, keyed by symbol-table id (a Quantity { kind: 'quantity', magnitude, dimension } or an Interval { kind: 'interval', min, max, dimension } per symbol). Omit for a document whose formulas are fully closed (units and numeric literals only, no 'sym' nodes).")
|
|
136
|
+
}),
|
|
137
|
+
outputSchema: ComputeFormulaOutputSchema
|
|
138
|
+
}, async ({ source, bindings }, ctx) => {
|
|
139
|
+
const { signal } = ctx.mcpReq;
|
|
140
|
+
try {
|
|
141
|
+
const { bytes, format } = await resolveDocumentInput(source, { signal });
|
|
142
|
+
const tree = readNativeDocumentTree(format, bytes, { signal });
|
|
143
|
+
const document = flattenTree(tree);
|
|
144
|
+
const entries = collectDocumentFormulas(document);
|
|
145
|
+
const resolvedBindings = bindings ?? {};
|
|
146
|
+
const formulas = entries.map((entry, index) => ({
|
|
147
|
+
index,
|
|
148
|
+
sourcePath: entry.sourcePath,
|
|
149
|
+
locate: entry.locate,
|
|
150
|
+
latex: entry.formula.presentation?.latex,
|
|
151
|
+
outcome: evaluateFormula(entry.formula, resolvedBindings, entry.symbolTable)
|
|
152
|
+
}));
|
|
153
|
+
const structuredContent = {
|
|
154
|
+
sourceFormat: format,
|
|
155
|
+
documentKind: document.kind,
|
|
156
|
+
formulaCount: formulas.length,
|
|
157
|
+
formulas
|
|
158
|
+
};
|
|
159
|
+
return {
|
|
160
|
+
content: [{
|
|
161
|
+
type: "text",
|
|
162
|
+
text: JSON.stringify(structuredContent)
|
|
163
|
+
}],
|
|
164
|
+
structuredContent
|
|
165
|
+
};
|
|
166
|
+
} catch (error) {
|
|
167
|
+
return toErrorResult$2(error);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
//#endregion
|
|
75
172
|
//#region src/io/document-output.ts
|
|
76
173
|
/**
|
|
77
174
|
* The hybrid output shape every MCP tool that produces a document accepts: an optional filesystem path to write the result to. Omitting it returns the bytes inline, base64-encoded, instead.
|
|
@@ -871,11 +968,12 @@ function registerPdfInspectTools(server) {
|
|
|
871
968
|
}
|
|
872
969
|
//#endregion
|
|
873
970
|
//#region src/server.ts
|
|
874
|
-
function createServer() {
|
|
971
|
+
function createServer$1() {
|
|
875
972
|
const server = new McpServer({
|
|
876
973
|
name: "document-mcp",
|
|
877
974
|
version
|
|
878
975
|
});
|
|
976
|
+
registerComputeFormulaTools(server);
|
|
879
977
|
registerConvertTools(server);
|
|
880
978
|
registerDocxExtrasTools(server);
|
|
881
979
|
registerFontTools(server);
|
|
@@ -889,7 +987,60 @@ function createServer() {
|
|
|
889
987
|
return server;
|
|
890
988
|
}
|
|
891
989
|
//#endregion
|
|
990
|
+
//#region src/serve-http.ts
|
|
991
|
+
const MCP_HTTP_PATH = "/mcp";
|
|
992
|
+
function serveHttp(port) {
|
|
993
|
+
const handler = createMcpHandler(createServer$1);
|
|
994
|
+
const nodeHandler = toNodeHandler(handler);
|
|
995
|
+
const httpServer = createServer((req, res) => {
|
|
996
|
+
if (req.url === void 0) throw new Error("serveHttp: request has no url");
|
|
997
|
+
if (new URL(req.url, "http://localhost").pathname !== "/mcp") {
|
|
998
|
+
res.writeHead(404, { "content-type": "text/plain" }).end("Not found");
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
nodeHandler(req, res);
|
|
1002
|
+
});
|
|
1003
|
+
return new Promise((resolve) => {
|
|
1004
|
+
httpServer.listen(port, "127.0.0.1", () => {
|
|
1005
|
+
resolve(httpServer);
|
|
1006
|
+
});
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
//#endregion
|
|
892
1010
|
//#region src/bin.ts
|
|
893
|
-
|
|
1011
|
+
const DEFAULT_HTTP_PORT = 3e3;
|
|
1012
|
+
function readFlag(args, name) {
|
|
1013
|
+
const prefix = `--${name}=`;
|
|
1014
|
+
for (const [index, arg] of args.entries()) {
|
|
1015
|
+
if (arg.startsWith(prefix)) return arg.slice(prefix.length);
|
|
1016
|
+
if (arg === `--${name}`) {
|
|
1017
|
+
const value = args[index + 1];
|
|
1018
|
+
if (value === void 0) throw new Error(`--${name} requires a value`);
|
|
1019
|
+
return value;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
function parsePort(raw) {
|
|
1024
|
+
const port = Number.parseInt(raw, 10);
|
|
1025
|
+
if (!Number.isInteger(port) || String(port) !== raw.trim() || port < 0 || port > 65535) throw new Error(`--port must be an integer between 0 and 65535, got "${raw}"`);
|
|
1026
|
+
return port;
|
|
1027
|
+
}
|
|
1028
|
+
async function main() {
|
|
1029
|
+
const args = process.argv.slice(2);
|
|
1030
|
+
const transport = readFlag(args, "transport") ?? "stdio";
|
|
1031
|
+
if (transport === "stdio") {
|
|
1032
|
+
serveStdio(createServer$1);
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
if (transport === "http") {
|
|
1036
|
+
const portArg = readFlag(args, "port");
|
|
1037
|
+
const address = (await serveHttp(portArg === void 0 ? DEFAULT_HTTP_PORT : parsePort(portArg))).address();
|
|
1038
|
+
if (address === null || typeof address === "string") throw new Error("Expected the HTTP server to bind a TCP address, not a pipe or Unix socket");
|
|
1039
|
+
console.error(`document-mcp listening on http://127.0.0.1:${String(address.port)}${MCP_HTTP_PATH}`);
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
throw new Error(`Unknown --transport "${transport}". Supported values: stdio, http.`);
|
|
1043
|
+
}
|
|
1044
|
+
await main();
|
|
894
1045
|
//#endregion
|
|
895
1046
|
export {};
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
let _modelcontextprotocol_server = require("@modelcontextprotocol/server");
|
|
3
|
+
let document_compute_js = require("document-compute.js");
|
|
4
|
+
let document_schema_js = require("document-schema.js");
|
|
3
5
|
let documents_js = require("documents.js");
|
|
4
6
|
let zod = require("zod");
|
|
5
7
|
let node_fs_promises = require("node:fs/promises");
|
|
@@ -7,7 +9,7 @@ let node_fs = require("node:fs");
|
|
|
7
9
|
let node_path = require("node:path");
|
|
8
10
|
let document_outline_js = require("document-outline.js");
|
|
9
11
|
//#region package.json
|
|
10
|
-
var version = "4.
|
|
12
|
+
var version = "4.5.1";
|
|
11
13
|
//#endregion
|
|
12
14
|
//#region src/io/document-input.ts
|
|
13
15
|
const EXTENSION_TO_FORMAT = {
|
|
@@ -71,6 +73,99 @@ async function resolveDocumentInput(input, options) {
|
|
|
71
73
|
};
|
|
72
74
|
}
|
|
73
75
|
//#endregion
|
|
76
|
+
//#region src/tools/compute-formula.ts
|
|
77
|
+
function toErrorResult$2(error) {
|
|
78
|
+
return {
|
|
79
|
+
content: [{
|
|
80
|
+
type: "text",
|
|
81
|
+
text: error instanceof Error ? error.message : String(error)
|
|
82
|
+
}],
|
|
83
|
+
isError: true
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function evaluateFormula(formula, bindings, symbolTable) {
|
|
87
|
+
if (formula.content === void 0) return { status: "no-content" };
|
|
88
|
+
try {
|
|
89
|
+
return {
|
|
90
|
+
status: "evaluated",
|
|
91
|
+
result: (0, document_compute_js.evaluate)(formula.content, bindings, symbolTable)
|
|
92
|
+
};
|
|
93
|
+
} catch (error) {
|
|
94
|
+
return {
|
|
95
|
+
status: "error",
|
|
96
|
+
errorType: error instanceof Error ? error.name : String(error),
|
|
97
|
+
message: error instanceof Error ? error.message : String(error)
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const FormulaOutcomeSchema = zod.z.union([
|
|
102
|
+
zod.z.object({
|
|
103
|
+
status: zod.z.literal("evaluated"),
|
|
104
|
+
result: document_schema_js.EvaluationValueSchema
|
|
105
|
+
}),
|
|
106
|
+
zod.z.object({ status: zod.z.literal("no-content") }),
|
|
107
|
+
zod.z.object({
|
|
108
|
+
status: zod.z.literal("error"),
|
|
109
|
+
errorType: zod.z.string(),
|
|
110
|
+
message: zod.z.string()
|
|
111
|
+
})
|
|
112
|
+
]);
|
|
113
|
+
const documentKindValues = document_schema_js.ContentDocumentSchema.options.map((option) => option.shape.kind.value);
|
|
114
|
+
const ComputeFormulaOutputSchema = zod.z.object({
|
|
115
|
+
sourceFormat: documents_js.DocumentFormatSchema,
|
|
116
|
+
documentKind: zod.z.enum(documentKindValues),
|
|
117
|
+
formulaCount: zod.z.number(),
|
|
118
|
+
formulas: zod.z.array(zod.z.object({
|
|
119
|
+
index: zod.z.number(),
|
|
120
|
+
sourcePath: zod.z.string().optional(),
|
|
121
|
+
locate: zod.z.string(),
|
|
122
|
+
latex: zod.z.string().optional(),
|
|
123
|
+
outcome: FormulaOutcomeSchema
|
|
124
|
+
}))
|
|
125
|
+
});
|
|
126
|
+
function registerComputeFormulaTools(server) {
|
|
127
|
+
server.registerTool("compute_formula", {
|
|
128
|
+
title: "Compute document formulas",
|
|
129
|
+
description: "Reads every formula a document embeds (docx/odt/markdown paragraphs and pptx/odp slide shapes; a table cell's own blocks too -- docx's own reader recovers a real equation nested inside a table cell; structurally, the walk also covers a drawing page's own shape flow, though no writer in the family populates a formula there today; a spreadsheet's own cell-anchored formula objects; a standalone .odf formula document; and a formula nested inside another embedded object at any depth, e.g. a formula embedded in a drawing embedded in a spreadsheet) and evaluates each through document-compute.js's units-typed evaluate() -- an agent's way to check whether a document's stated arithmetic actually checks out. A formula referencing a symbol (e.g. F = m * a) needs that symbol's value supplied via bindings, keyed by the document's own symbol-table id (see the document's symbolTable.symbols[].id, or a returned formula's own latex to identify which symbol is which); a formula with no free symbols (units and numeric literals only) evaluates with no bindings at all. Each formula reports its own outcome independently -- 'evaluated' with the result, 'no-content' when the formula was never lowered to a computable MathExpression (the common case for a spreadsheet's own embedded formula object, or any format other than markdown, none of which yet stores the semantic layer on disk), or 'error' naming which document-compute.js error it hit (e.g. UnboundSymbolError) -- so one formula needing more bindings never blocks the others. Each entry's own `locate` field is the reliable way to tell two formulas in the same document apart -- a structural path guaranteed unique per formula -- unlike `sourcePath`, which several formats leave undefined or stamp with the identical constant across sibling formulas (markdown's own display-math lowering among them).",
|
|
130
|
+
inputSchema: zod.z.object({
|
|
131
|
+
source: DocumentInputSchema.describe("The document to read formulas from."),
|
|
132
|
+
bindings: document_schema_js.FormulaBindingsSchema.optional().describe("Known values for symbols the document's formulas reference, keyed by symbol-table id (a Quantity { kind: 'quantity', magnitude, dimension } or an Interval { kind: 'interval', min, max, dimension } per symbol). Omit for a document whose formulas are fully closed (units and numeric literals only, no 'sym' nodes).")
|
|
133
|
+
}),
|
|
134
|
+
outputSchema: ComputeFormulaOutputSchema
|
|
135
|
+
}, async ({ source, bindings }, ctx) => {
|
|
136
|
+
const { signal } = ctx.mcpReq;
|
|
137
|
+
try {
|
|
138
|
+
const { bytes, format } = await resolveDocumentInput(source, { signal });
|
|
139
|
+
const tree = (0, documents_js.readNativeDocumentTree)(format, bytes, { signal });
|
|
140
|
+
const document = (0, document_schema_js.flattenTree)(tree);
|
|
141
|
+
const entries = (0, documents_js.collectDocumentFormulas)(document);
|
|
142
|
+
const resolvedBindings = bindings ?? {};
|
|
143
|
+
const formulas = entries.map((entry, index) => ({
|
|
144
|
+
index,
|
|
145
|
+
sourcePath: entry.sourcePath,
|
|
146
|
+
locate: entry.locate,
|
|
147
|
+
latex: entry.formula.presentation?.latex,
|
|
148
|
+
outcome: evaluateFormula(entry.formula, resolvedBindings, entry.symbolTable)
|
|
149
|
+
}));
|
|
150
|
+
const structuredContent = {
|
|
151
|
+
sourceFormat: format,
|
|
152
|
+
documentKind: document.kind,
|
|
153
|
+
formulaCount: formulas.length,
|
|
154
|
+
formulas
|
|
155
|
+
};
|
|
156
|
+
return {
|
|
157
|
+
content: [{
|
|
158
|
+
type: "text",
|
|
159
|
+
text: JSON.stringify(structuredContent)
|
|
160
|
+
}],
|
|
161
|
+
structuredContent
|
|
162
|
+
};
|
|
163
|
+
} catch (error) {
|
|
164
|
+
return toErrorResult$2(error);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
//#endregion
|
|
74
169
|
//#region src/io/document-output.ts
|
|
75
170
|
/**
|
|
76
171
|
* The hybrid output shape every MCP tool that produces a document accepts: an optional filesystem path to write the result to. Omitting it returns the bytes inline, base64-encoded, instead.
|
|
@@ -875,6 +970,7 @@ function createServer() {
|
|
|
875
970
|
name: "document-mcp",
|
|
876
971
|
version
|
|
877
972
|
});
|
|
973
|
+
registerComputeFormulaTools(server);
|
|
878
974
|
registerConvertTools(server);
|
|
879
975
|
registerDocxExtrasTools(server);
|
|
880
976
|
registerFontTools(server);
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/server";
|
|
2
|
-
import {
|
|
2
|
+
import { evaluate } from "document-compute.js";
|
|
3
|
+
import { ContentDocumentSchema, EvaluationValueSchema, FormulaBindingsSchema, flattenTree } from "document-schema.js";
|
|
4
|
+
import { DocumentFormatSchema, OdbReportNotSpecifiedError, OdmUnresolvedSectionError, UnrecognizedDocumentSchemaError, base64ToBytes, buildDocumentBytes, bytesToBase64, collectDocumentFormulas, createLocalDocumentConverter, decodeOdbPackage, decodePackage, describeFontFace, documentFromJson, documentSchemaKindOf, evaluateSelect, extractSourceFontsForFormat, odbReportToDocx, odbReportToOdt, odbReportToPdf, odbToCsv, odbToXlsx, odmToPdf, parseSelect, readDocumentMetadata, readDocxExtras, readNativeDocumentTree, readOdbForms, readOdbInventory, readOdbReportContent, readOdbReports, readOdbTables, readPdf, setDocumentMetadata } from "documents.js";
|
|
3
5
|
import { z } from "zod";
|
|
4
6
|
import { readFile, writeFile } from "node:fs/promises";
|
|
5
7
|
import { existsSync, readFileSync } from "node:fs";
|
|
6
8
|
import { basename, join } from "node:path";
|
|
7
9
|
import { buildOutline, isOutlineNode, outlineLeafText } from "document-outline.js";
|
|
8
10
|
//#region package.json
|
|
9
|
-
var version = "4.
|
|
11
|
+
var version = "4.5.1";
|
|
10
12
|
//#endregion
|
|
11
13
|
//#region src/io/document-input.ts
|
|
12
14
|
const EXTENSION_TO_FORMAT = {
|
|
@@ -70,6 +72,99 @@ async function resolveDocumentInput(input, options) {
|
|
|
70
72
|
};
|
|
71
73
|
}
|
|
72
74
|
//#endregion
|
|
75
|
+
//#region src/tools/compute-formula.ts
|
|
76
|
+
function toErrorResult$2(error) {
|
|
77
|
+
return {
|
|
78
|
+
content: [{
|
|
79
|
+
type: "text",
|
|
80
|
+
text: error instanceof Error ? error.message : String(error)
|
|
81
|
+
}],
|
|
82
|
+
isError: true
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function evaluateFormula(formula, bindings, symbolTable) {
|
|
86
|
+
if (formula.content === void 0) return { status: "no-content" };
|
|
87
|
+
try {
|
|
88
|
+
return {
|
|
89
|
+
status: "evaluated",
|
|
90
|
+
result: evaluate(formula.content, bindings, symbolTable)
|
|
91
|
+
};
|
|
92
|
+
} catch (error) {
|
|
93
|
+
return {
|
|
94
|
+
status: "error",
|
|
95
|
+
errorType: error instanceof Error ? error.name : String(error),
|
|
96
|
+
message: error instanceof Error ? error.message : String(error)
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const FormulaOutcomeSchema = z.union([
|
|
101
|
+
z.object({
|
|
102
|
+
status: z.literal("evaluated"),
|
|
103
|
+
result: EvaluationValueSchema
|
|
104
|
+
}),
|
|
105
|
+
z.object({ status: z.literal("no-content") }),
|
|
106
|
+
z.object({
|
|
107
|
+
status: z.literal("error"),
|
|
108
|
+
errorType: z.string(),
|
|
109
|
+
message: z.string()
|
|
110
|
+
})
|
|
111
|
+
]);
|
|
112
|
+
const documentKindValues = ContentDocumentSchema.options.map((option) => option.shape.kind.value);
|
|
113
|
+
const ComputeFormulaOutputSchema = z.object({
|
|
114
|
+
sourceFormat: DocumentFormatSchema,
|
|
115
|
+
documentKind: z.enum(documentKindValues),
|
|
116
|
+
formulaCount: z.number(),
|
|
117
|
+
formulas: z.array(z.object({
|
|
118
|
+
index: z.number(),
|
|
119
|
+
sourcePath: z.string().optional(),
|
|
120
|
+
locate: z.string(),
|
|
121
|
+
latex: z.string().optional(),
|
|
122
|
+
outcome: FormulaOutcomeSchema
|
|
123
|
+
}))
|
|
124
|
+
});
|
|
125
|
+
function registerComputeFormulaTools(server) {
|
|
126
|
+
server.registerTool("compute_formula", {
|
|
127
|
+
title: "Compute document formulas",
|
|
128
|
+
description: "Reads every formula a document embeds (docx/odt/markdown paragraphs and pptx/odp slide shapes; a table cell's own blocks too -- docx's own reader recovers a real equation nested inside a table cell; structurally, the walk also covers a drawing page's own shape flow, though no writer in the family populates a formula there today; a spreadsheet's own cell-anchored formula objects; a standalone .odf formula document; and a formula nested inside another embedded object at any depth, e.g. a formula embedded in a drawing embedded in a spreadsheet) and evaluates each through document-compute.js's units-typed evaluate() -- an agent's way to check whether a document's stated arithmetic actually checks out. A formula referencing a symbol (e.g. F = m * a) needs that symbol's value supplied via bindings, keyed by the document's own symbol-table id (see the document's symbolTable.symbols[].id, or a returned formula's own latex to identify which symbol is which); a formula with no free symbols (units and numeric literals only) evaluates with no bindings at all. Each formula reports its own outcome independently -- 'evaluated' with the result, 'no-content' when the formula was never lowered to a computable MathExpression (the common case for a spreadsheet's own embedded formula object, or any format other than markdown, none of which yet stores the semantic layer on disk), or 'error' naming which document-compute.js error it hit (e.g. UnboundSymbolError) -- so one formula needing more bindings never blocks the others. Each entry's own `locate` field is the reliable way to tell two formulas in the same document apart -- a structural path guaranteed unique per formula -- unlike `sourcePath`, which several formats leave undefined or stamp with the identical constant across sibling formulas (markdown's own display-math lowering among them).",
|
|
129
|
+
inputSchema: z.object({
|
|
130
|
+
source: DocumentInputSchema.describe("The document to read formulas from."),
|
|
131
|
+
bindings: FormulaBindingsSchema.optional().describe("Known values for symbols the document's formulas reference, keyed by symbol-table id (a Quantity { kind: 'quantity', magnitude, dimension } or an Interval { kind: 'interval', min, max, dimension } per symbol). Omit for a document whose formulas are fully closed (units and numeric literals only, no 'sym' nodes).")
|
|
132
|
+
}),
|
|
133
|
+
outputSchema: ComputeFormulaOutputSchema
|
|
134
|
+
}, async ({ source, bindings }, ctx) => {
|
|
135
|
+
const { signal } = ctx.mcpReq;
|
|
136
|
+
try {
|
|
137
|
+
const { bytes, format } = await resolveDocumentInput(source, { signal });
|
|
138
|
+
const tree = readNativeDocumentTree(format, bytes, { signal });
|
|
139
|
+
const document = flattenTree(tree);
|
|
140
|
+
const entries = collectDocumentFormulas(document);
|
|
141
|
+
const resolvedBindings = bindings ?? {};
|
|
142
|
+
const formulas = entries.map((entry, index) => ({
|
|
143
|
+
index,
|
|
144
|
+
sourcePath: entry.sourcePath,
|
|
145
|
+
locate: entry.locate,
|
|
146
|
+
latex: entry.formula.presentation?.latex,
|
|
147
|
+
outcome: evaluateFormula(entry.formula, resolvedBindings, entry.symbolTable)
|
|
148
|
+
}));
|
|
149
|
+
const structuredContent = {
|
|
150
|
+
sourceFormat: format,
|
|
151
|
+
documentKind: document.kind,
|
|
152
|
+
formulaCount: formulas.length,
|
|
153
|
+
formulas
|
|
154
|
+
};
|
|
155
|
+
return {
|
|
156
|
+
content: [{
|
|
157
|
+
type: "text",
|
|
158
|
+
text: JSON.stringify(structuredContent)
|
|
159
|
+
}],
|
|
160
|
+
structuredContent
|
|
161
|
+
};
|
|
162
|
+
} catch (error) {
|
|
163
|
+
return toErrorResult$2(error);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
//#endregion
|
|
73
168
|
//#region src/io/document-output.ts
|
|
74
169
|
/**
|
|
75
170
|
* The hybrid output shape every MCP tool that produces a document accepts: an optional filesystem path to write the result to. Omitting it returns the bytes inline, base64-encoded, instead.
|
|
@@ -874,6 +969,7 @@ function createServer() {
|
|
|
874
969
|
name: "document-mcp",
|
|
875
970
|
version
|
|
876
971
|
});
|
|
972
|
+
registerComputeFormulaTools(server);
|
|
877
973
|
registerConvertTools(server);
|
|
878
974
|
registerDocxExtrasTools(server);
|
|
879
975
|
registerFontTools(server);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "document-mcp",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.1",
|
|
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": {
|
|
@@ -86,9 +86,12 @@
|
|
|
86
86
|
"license": "MIT",
|
|
87
87
|
"packageManager": "pnpm@11.6.0",
|
|
88
88
|
"dependencies": {
|
|
89
|
+
"@modelcontextprotocol/node": "^2.0.0",
|
|
89
90
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
90
|
-
"document-
|
|
91
|
-
"
|
|
91
|
+
"document-compute.js": "^1.2.21",
|
|
92
|
+
"document-outline.js": "^3.3.0",
|
|
93
|
+
"document-schema.js": "^6.0.0",
|
|
94
|
+
"documents.js": "^7.0.1",
|
|
92
95
|
"zod": "^4.2.0"
|
|
93
96
|
},
|
|
94
97
|
"devDependencies": {
|
|
@@ -99,13 +102,13 @@
|
|
|
99
102
|
"@vitest/coverage-v8": "^4.1.10",
|
|
100
103
|
"eslint": "^10.8.0",
|
|
101
104
|
"husky": "^9.1.7",
|
|
102
|
-
"odf.js": "^
|
|
105
|
+
"odf.js": "^7.0.0",
|
|
103
106
|
"publint": "^0.3.22",
|
|
104
107
|
"semantic-release": "^25.0.8",
|
|
105
108
|
"tsdown": "^0.22.14",
|
|
106
109
|
"turbo": "^2.10.8",
|
|
107
110
|
"typescript": "^6.0.3",
|
|
108
111
|
"vitest": "^4.1.10",
|
|
109
|
-
"wpd-codec": "^
|
|
112
|
+
"wpd-codec": "^2.0.0"
|
|
110
113
|
}
|
|
111
114
|
}
|