@json-to-office/mcp-server 1.4.0 → 1.8.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 +33 -15
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +846 -109
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +284 -3
- package/dist/index.js +815 -107
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -80,13 +80,16 @@ Pin a version by asking for one — `npx -y @json-to-office/mcp-server@1.0.0`
|
|
|
80
80
|
|
|
81
81
|
## Options
|
|
82
82
|
|
|
83
|
-
| Flag / variable
|
|
84
|
-
|
|
|
85
|
-
| `--output-dir <path>`
|
|
86
|
-
| `JTO_MCP_OUTPUT_DIR`
|
|
87
|
-
| _(neither)_
|
|
88
|
-
|
|
|
89
|
-
| `
|
|
83
|
+
| Flag / variable | Meaning |
|
|
84
|
+
| ------------------------ | --------------------------------------------------------------------------------- |
|
|
85
|
+
| `--output-dir <path>` | Where generated files are written. Highest precedence. |
|
|
86
|
+
| `JTO_MCP_OUTPUT_DIR` | Same, when the flag is absent. |
|
|
87
|
+
| _(neither output flag)_ | A per-connection directory under the system temp dir. |
|
|
88
|
+
| `--workspace-dir <path>` | Where workspace revisions are mirrored, so they outlive the connection. |
|
|
89
|
+
| `JTO_MCP_WORKSPACE_DIR` | Same, when the flag is absent. |
|
|
90
|
+
| _(neither ws flag)_ | Workspaces are memory-only: handles end with the connection. This is the default. |
|
|
91
|
+
| `LIBREOFFICE_PATH` | LibreOffice binary, for preview and the docx `visual` component. |
|
|
92
|
+
| `PDFTOPPM_PATH` | poppler `pdftoppm` binary, for preview. |
|
|
90
93
|
|
|
91
94
|
## Contracts
|
|
92
95
|
|
|
@@ -102,7 +105,7 @@ Pin a version by asking for one — `npx -y @json-to-office/mcp-server@1.0.0`
|
|
|
102
105
|
|
|
103
106
|
## The loop this server expects
|
|
104
107
|
|
|
105
|
-
1. **`jto_info`** — what this host can actually do: versions, formats and their renderer ids, the output root, whether workspaces are on, and whether preview has its binaries.
|
|
108
|
+
1. **`jto_info`** — what this host can actually do: versions, formats and their renderer ids, the output root, whether workspaces are on and whether they survive a lost connection, and whether preview has its binaries.
|
|
106
109
|
2. **`jto_discover`** (or the `jto://catalog` resource) — components per format, renderer profiles, built-in themes, and starter documents to copy. **`jto_describe_component`** for the exact schema of the one component you are about to write.
|
|
107
110
|
3. **Author** — start from a starter, or `jto_workspace_create` and patch content in.
|
|
108
111
|
4. **`jto_validate`** after each edit, not once at the end. Diagnostics are path-addressed, so a pointer is a patch target.
|
|
@@ -140,7 +143,7 @@ Call this first.
|
|
|
140
143
|
|
|
141
144
|
**In** — `includePreviewDependencies` (boolean, default true): probe the filesystem for LibreOffice and poppler.
|
|
142
145
|
|
|
143
|
-
**Out** — `server` `{name, package, version, protocolTransport}`; `runtime` `{node, platform, arch}`; `packages` (installed versions of `jto-ops`, `shared*`, `core-docx`, `core-pptx` — a render is a function of the document plus these); `formats[]` `{name, extension, label, rendererIds}` with defaults first; `workspaces` `{available, open}`; `output` `{root, ephemeral, maxInlineArtifactBytes}`; `previewDependencies` `{libreoffice, pdftoppm}`, each `{available, path?, envVar, searched[]}`, absent when not probed.
|
|
146
|
+
**Out** — `server` `{name, package, version, protocolTransport}`; `runtime` `{node, platform, arch}`; `packages` (installed versions of `jto-ops`, `shared*`, `core-docx`, `core-pptx` — a render is a function of the document plus these); `formats[]` `{name, extension, label, rendererIds}` with defaults first; `workspaces` `{available, open, persistent, root?}`; `output` `{root, ephemeral, maxInlineArtifactBytes}`; `previewDependencies` `{libreoffice, pdftoppm}`, each `{available, path?, envVar, searched[]}`, absent when not probed.
|
|
144
147
|
|
|
145
148
|
The probe is a PATH walk, not a `--version` spawn: `jto_info` is a per-connection discovery call and a cold `soffice --version` costs about a second. It therefore reports a binary that exists, not a binary that works; a real preview settles the rest.
|
|
146
149
|
|
|
@@ -164,12 +167,14 @@ Nested components collapse to their names on purpose — describe those separate
|
|
|
164
167
|
|
|
165
168
|
### `jto_validate`
|
|
166
169
|
|
|
167
|
-
**In** — `format` (required); document source; `renderer` (validate against this profile instead of the document's own, for this check only); `maxDiagnostics` (1–1000, default 100 — errors are kept ahead of warnings when the cap bites).
|
|
170
|
+
**In** — `format` (required); document source; `renderer` (validate against this profile instead of the document's own, for this check only); `quality` `{profile?, policy?}`; `maxDiagnostics` (1–1000, default 100 — errors are kept ahead of warnings when the cap bites).
|
|
168
171
|
|
|
169
172
|
**Out** — `valid`, `format`, `renderer` (when one was requested), `source` `{origin, handle?, revision?}`, `counts` `{error, warning, info}` (before any cap), `truncated`.
|
|
170
173
|
|
|
171
174
|
`ok` mirrors the gate generation applies: schema and semantic errors block it, renderer-profile findings (`W_UNSUPPORTED_RENDERER_FEATURE`) come back as warnings, because the renderer has the last word on those.
|
|
172
175
|
|
|
176
|
+
Design-quality findings ride the same envelope as `W_QUALITY_*` warnings and infos: an undeclared slide canvas, estimated text overflow, overcrowding, unreadable type, table overflow, or a skipped heading. They carry category, certainty, evidence, suggestion, and optional fixes. They are advisory by default; `quality.policy.gate: "warning"` makes warning-or-higher findings set `ok: false` without turning the tool call into a protocol error.
|
|
177
|
+
|
|
173
178
|
The gate, not `jto://schema/{format}/document`. The two agree except on component nodes whose props are all optional, where the generated schema asks for a `props: {}` the validator and both renderers treat as omissible; validate follows the renderer, because that is what `jto_generate` runs.
|
|
174
179
|
|
|
175
180
|
### `jto_generate`
|
|
@@ -202,10 +207,20 @@ Both sides are validated before the walk, and their diagnostics are tagged with
|
|
|
202
207
|
|
|
203
208
|
### Workspaces
|
|
204
209
|
|
|
205
|
-
Optional, and an optimization rather than a second source of truth: the JSON stays authoritative, and every tool that takes a `handle` also takes inline `document`. The point is the round trip you do not make — open a document once, patch the two paths a diagnostic pointed at, validate or preview by handle, without putting the whole tree back through the model.
|
|
210
|
+
Optional, and an optimization rather than a second source of truth: the JSON stays authoritative, and every tool that takes a `handle` also takes inline `document`. The point is the round trip you do not make — open a document once, patch the two paths a diagnostic pointed at, validate or preview by handle, without putting the whole tree back through the model. By default handles are memory-only and scoped to one stdio connection.
|
|
206
211
|
|
|
207
212
|
Default budget per connection: 16 open documents, 16 MiB per document, 64 MiB in total, 8 pinned revisions each, and a 30-minute idle TTL that any use resets. Capacity overflow refuses with `E_WORKSPACE_LIMIT`; it never evicts another live workspace or an older pin. `jto_workspace_list` reports the live figures.
|
|
208
213
|
|
|
214
|
+
**Surviving a lost session.** Start the server with `--workspace-dir` (or `JTO_MCP_WORKSPACE_DIR`) and every committed revision is mirrored there, so a client restart, a host session reset or a crash no longer takes the authoring with it. Memory stays the fast path; disk is durability. On a connection that has it:
|
|
215
|
+
|
|
216
|
+
- Each `jto_workspace_create`, `jto_workspace_patch` and `jto_workspace_snapshot` writes before it answers, and the `workspace` record carries `persisted`.
|
|
217
|
+
- `jto_workspace_list` reports every workspace under the root, including ones this connection never opened — that is how a reconnecting agent gets its handles back. `jto_workspace_inspect` (and any other handle-taking tool) then loads the document on demand.
|
|
218
|
+
- The idle TTL still releases memory, but no longer loses anything: the handle comes back from disk on next use.
|
|
219
|
+
- `jto_workspace_close` deletes the durable copy too. It is the one operation that is meant to destroy work, so it still does.
|
|
220
|
+
- The root is bounded: 32 workspaces (least-recently-updated dropped first), 9 revision files per workspace — the head plus its pins — and 16 MiB per revision. A revision that cannot be written comes back with a `W_WORKSPACE_NOT_PERSISTED` warning and the edit still applies: durability degrades loudly, never silently, and never at the cost of the edit.
|
|
221
|
+
|
|
222
|
+
Handles become cross-connection when this is on, so point it at a directory you would be comfortable leaving documents in: it holds the JSON in the clear until something closes the workspace or the workspace ceiling evicts it. The server creates the root `0o700` and writes files `0o600`, but it does not touch the permissions of a directory that already exists, and Windows does not honour those bits at all — so pick a private location and check its ACLs rather than relying on the server for that. Two connections sharing a root also share its handles, and each keeps its own memory copy: `baseRevision` still guards a write against the connection that made it, not against another one editing the same handle at the same time. One root per client is the arrangement this is built for.
|
|
223
|
+
|
|
209
224
|
**`jto_workspace_create`** — in: `format` (required), `document` (omit to open an empty skeleton and patch into it), `title` (your own label). Out: `workspace`. Nothing is validated here.
|
|
210
225
|
|
|
211
226
|
**`jto_workspace_inspect`** — in: `handle` (required), `revision` (the current one, or one pinned by a snapshot; anything else fails rather than quietly returning newer JSON), `paths[]` (project only these pointers), `includeDocument` (default false when `paths` is given, true otherwise). Out: `workspace`, `document`, `projection` (pointer → value), `missingPaths[]` — so "absent" stays distinguishable from "present and null".
|
|
@@ -214,11 +229,11 @@ Default budget per connection: 16 open documents, 16 MiB per document, 64 MiB in
|
|
|
214
229
|
|
|
215
230
|
**`jto_workspace_snapshot`** — in: `handle` (required), `filename` (write the JSON under the output root instead of returning it inline). Out: `workspace`, `document` or `artifact`. Pins the revision so `jto_workspace_inspect` can still read that exact tree after later patches; a full pin budget downgrades to an export with a `W_SNAPSHOT_NOT_PINNED` warning.
|
|
216
231
|
|
|
217
|
-
**`jto_workspace_list`** — no input. Out: `workspaces[]`, `available`, `limits`, `usage` `{workspaces, bytes}
|
|
232
|
+
**`jto_workspace_list`** — no input. Out: `workspaces[]`, `available`, `limits`, `usage` `{workspaces, bytes}`, and `persistence` `{root, maxWorkspaces, maxRevisionsPerWorkspace, maxEntryBytes}` when the connection has a workspace directory. The cheapest way back after losing track of a handle — including across a reconnect.
|
|
218
233
|
|
|
219
|
-
**`jto_workspace_close`** — in: `handle` (required). Out: `handle`, `closed`. Idempotent; closing something already gone reports `closed: false` rather than failing. Not recoverable — snapshot first.
|
|
234
|
+
**`jto_workspace_close`** — in: `handle` (required). Out: `handle`, `closed`. Idempotent; closing something already gone reports `closed: false` rather than failing. Not recoverable — it removes the durable copy as well, so snapshot first.
|
|
220
235
|
|
|
221
|
-
A `workspace` record is `{handle, format, revision, bytes, createdAt, updatedAt, title?, pinnedRevisions[]}`.
|
|
236
|
+
A `workspace` record is `{handle, format, revision, bytes, createdAt, updatedAt, title?, pinnedRevisions[], persisted?}`.
|
|
222
237
|
|
|
223
238
|
## Resources
|
|
224
239
|
|
|
@@ -229,6 +244,7 @@ The same catalogues, for clients that read resources. URIs are stable.
|
|
|
229
244
|
| `jto://catalog` | The resource form of `jto_discover`: every format, in full. |
|
|
230
245
|
| `jto://renderers` | Renderer ids per format, which is default, what each profile can draw. |
|
|
231
246
|
| `jto://themes` | Built-in theme names per format. |
|
|
247
|
+
| `jto://themes/values` | What each built-in theme actually is: palette, fonts, style tables. |
|
|
232
248
|
| `jto://templates` | Every starter document. |
|
|
233
249
|
| `jto://schema/docx/document` | Generated JSON Schema for a complete `.docx` document, by renderer. |
|
|
234
250
|
| `jto://schema/pptx/document` | The same for `.pptx`. |
|
|
@@ -283,6 +299,7 @@ The cores name their generation warnings in a dialect of their own too — bare
|
|
|
283
299
|
| `E_WORKSPACES_UNAVAILABLE` | Workspaces are switched off for this connection. |
|
|
284
300
|
| `E_WORKSPACE_EVICTED` | The handle was released by the idle TTL. |
|
|
285
301
|
| `E_WORKSPACE_LIMIT` | The connection's workspace or byte budget is full. |
|
|
302
|
+
| `E_WORKSPACE_NOT_CLOSED` | The durable copy could not be deleted, so nothing was closed. The workspace is still open. |
|
|
286
303
|
| `E_DOCUMENT_TOO_LARGE` | One document is over `maxDocumentBytes`. |
|
|
287
304
|
| `E_PATCH_SYNTAX` | An operation is malformed. |
|
|
288
305
|
| `E_INVALID_POINTER` | A JSON Pointer is not RFC 6901. |
|
|
@@ -307,6 +324,7 @@ The cores name their generation warnings in a dialect of their own too — bare
|
|
|
307
324
|
| `W_BLANK_DOCUMENT` | A workspace was opened on an empty skeleton, with no content yet. |
|
|
308
325
|
| `W_PATH_NOT_FOUND` | A pointer a read asked for does not resolve in that revision. |
|
|
309
326
|
| `W_SNAPSHOT_NOT_PINNED` | A snapshot was exported but not pinned: the workspace budget is full. |
|
|
327
|
+
| `W_WORKSPACE_NOT_PERSISTED` | The edit applied, but the revision did not reach the workspace directory. |
|
|
310
328
|
| `W_GENERATION` | A generation warning with no code of its own. |
|
|
311
329
|
| `W_<CORE_CODE>` | A generation warning the core named: `W_FONT_UNRESOLVED`, `W_CHART_NO_DATA`, `W_UNKNOWN_SHAPE` and friends. |
|
|
312
330
|
|
|
@@ -333,7 +351,7 @@ const server = createServer(createToolDeps({ outputDir: './out' }));
|
|
|
333
351
|
await server.connect(myTransport);
|
|
334
352
|
```
|
|
335
353
|
|
|
336
|
-
`setWorkspaceStore` before `createServer` installs your own store — with tighter limits, or `unavailableWorkspaceStore` to switch workspaces off entirely.
|
|
354
|
+
`setWorkspaceStore` before `createServer` installs your own store — with tighter limits, or `unavailableWorkspaceStore` to switch workspaces off entirely. `createToolDeps({ workspaceDir })`, or a `workspacePersistence` you build with `createWorkspacePersistenceAt`, gives the connection's own store disk backing instead.
|
|
337
355
|
|
|
338
356
|
## License
|
|
339
357
|
|
package/dist/cli.d.ts
CHANGED
|
@@ -11,9 +11,12 @@
|
|
|
11
11
|
*/
|
|
12
12
|
interface ParsedArgs {
|
|
13
13
|
outputDir?: string;
|
|
14
|
+
workspaceDir?: string;
|
|
14
15
|
version: boolean;
|
|
15
16
|
help: boolean;
|
|
16
17
|
unknown: string[];
|
|
18
|
+
/** Flags that were given without a usable value. */
|
|
19
|
+
missingValue: string[];
|
|
17
20
|
}
|
|
18
21
|
declare function parseArgs(argv: readonly string[]): ParsedArgs;
|
|
19
22
|
|