@json-to-office/mcp-server 1.5.0 → 1.8.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 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 | 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)_ | A per-connection directory under the system temp dir. |
88
- | `LIBREOFFICE_PATH` | LibreOffice binary, for preview and the docx `visual` component. |
89
- | `PDFTOPPM_PATH` | poppler `pdftoppm` binary, for preview. |
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
 
@@ -204,10 +207,20 @@ Both sides are validated before the walk, and their diagnostics are tagged with
204
207
 
205
208
  ### Workspaces
206
209
 
207
- 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. Handles are memory-only and scoped to one stdio connection.
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.
208
211
 
209
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.
210
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
+
211
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.
212
225
 
213
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".
@@ -216,11 +229,11 @@ Default budget per connection: 16 open documents, 16 MiB per document, 64 MiB in
216
229
 
217
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.
218
231
 
219
- **`jto_workspace_list`** — no input. Out: `workspaces[]`, `available`, `limits`, `usage` `{workspaces, bytes}`. The cheapest way back after losing track of a handle.
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.
220
233
 
221
- **`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.
222
235
 
223
- 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?}`.
224
237
 
225
238
  ## Resources
226
239
 
@@ -286,6 +299,7 @@ The cores name their generation warnings in a dialect of their own too — bare
286
299
  | `E_WORKSPACES_UNAVAILABLE` | Workspaces are switched off for this connection. |
287
300
  | `E_WORKSPACE_EVICTED` | The handle was released by the idle TTL. |
288
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. |
289
303
  | `E_DOCUMENT_TOO_LARGE` | One document is over `maxDocumentBytes`. |
290
304
  | `E_PATCH_SYNTAX` | An operation is malformed. |
291
305
  | `E_INVALID_POINTER` | A JSON Pointer is not RFC 6901. |
@@ -310,6 +324,7 @@ The cores name their generation warnings in a dialect of their own too — bare
310
324
  | `W_BLANK_DOCUMENT` | A workspace was opened on an empty skeleton, with no content yet. |
311
325
  | `W_PATH_NOT_FOUND` | A pointer a read asked for does not resolve in that revision. |
312
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. |
313
328
  | `W_GENERATION` | A generation warning with no code of its own. |
314
329
  | `W_<CORE_CODE>` | A generation warning the core named: `W_FONT_UNRESOLVED`, `W_CHART_NO_DATA`, `W_UNKNOWN_SHAPE` and friends. |
315
330
 
@@ -336,7 +351,7 @@ const server = createServer(createToolDeps({ outputDir: './out' }));
336
351
  await server.connect(myTransport);
337
352
  ```
338
353
 
339
- `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.
340
355
 
341
356
  ## License
342
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