@json-to-office/mcp-server 1.2.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/LICENSE +18 -0
- package/README.md +340 -0
- package/dist/cli.d.ts +20 -0
- package/dist/cli.js +5797 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +718 -0
- package/dist/index.js +5779 -0
- package/dist/index.js.map +1 -0
- package/package.json +79 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,718 @@
|
|
|
1
|
+
import { McpServer, McpServerFactory, JsonSchemaType, StandardSchemaWithJSON } from '@modelcontextprotocol/server';
|
|
2
|
+
import { FormatAdapter, FormatName } from '@json-to-office/jto-ops';
|
|
3
|
+
export { FormatAdapter, FormatName } from '@json-to-office/jto-ops';
|
|
4
|
+
import { ValidationError } from '@json-to-office/shared';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Structured results, not protocol errors.
|
|
8
|
+
*
|
|
9
|
+
* A JSON-RPC error tells the agent "the call failed" and nothing it can act
|
|
10
|
+
* on. Every defect this server can describe — a bad document, an unknown
|
|
11
|
+
* handle, a missing host binary — is therefore a normal tool RESULT carrying
|
|
12
|
+
* path-addressed diagnostics, which the agent can read, repair and retry.
|
|
13
|
+
* Protocol errors stay reserved for transport and server failures (#202).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
type DiagnosticSeverity = 'error' | 'warning' | 'info';
|
|
17
|
+
/**
|
|
18
|
+
* One machine-actionable defect.
|
|
19
|
+
*
|
|
20
|
+
* `path` is an RFC 6901 JSON Pointer into the document the tool was given, so
|
|
21
|
+
* an agent holding a workspace handle can turn a diagnostic straight into a
|
|
22
|
+
* JSON Patch target (#271).
|
|
23
|
+
*/
|
|
24
|
+
interface Diagnostic {
|
|
25
|
+
severity: DiagnosticSeverity;
|
|
26
|
+
/** Stable machine code — see `ERROR_CODES`. */
|
|
27
|
+
code: string;
|
|
28
|
+
message: string;
|
|
29
|
+
/** JSON Pointer into the offending document, when the defect has a location. */
|
|
30
|
+
path?: string;
|
|
31
|
+
/** What to do about it, in one sentence. */
|
|
32
|
+
suggestion?: string;
|
|
33
|
+
/** Free-form extras (offending value, component name, renderer id, …). */
|
|
34
|
+
context?: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The envelope every tool's `structuredContent` starts from.
|
|
38
|
+
*
|
|
39
|
+
* `ok` is the single field a caller must branch on; `diagnostics` is always
|
|
40
|
+
* present (possibly empty) so clients never special-case its absence.
|
|
41
|
+
*/
|
|
42
|
+
interface ToolEnvelope {
|
|
43
|
+
ok: boolean;
|
|
44
|
+
diagnostics: Diagnostic[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Stable codes. Callers — including our own tests and downstream agents —
|
|
48
|
+
* branch on these, so treat them as API: add freely, rename never.
|
|
49
|
+
*
|
|
50
|
+
* `E_` for something that blocks, `W_` for something that does not. Every
|
|
51
|
+
* `code` this server puts on the wire is one of these: the validators speak
|
|
52
|
+
* three private dialects of their own (TypeBox ordinals, snake_case, the
|
|
53
|
+
* cores' own names) and `normalizeCode` maps all three in here at the
|
|
54
|
+
* boundary, so an agent has exactly one vocabulary to branch on.
|
|
55
|
+
*/
|
|
56
|
+
declare const ERROR_CODES: {
|
|
57
|
+
/** An exception escaped a tool handler. Always a bug here, never the caller's. */
|
|
58
|
+
readonly INTERNAL: "E_INTERNAL";
|
|
59
|
+
/** Neither `document` nor `handle` was supplied. */
|
|
60
|
+
readonly DOC_SOURCE_MISSING: "E_DOC_SOURCE_MISSING";
|
|
61
|
+
/** Both `document` and `handle` were supplied. */
|
|
62
|
+
readonly DOC_SOURCE_AMBIGUOUS: "E_DOC_SOURCE_AMBIGUOUS";
|
|
63
|
+
/** `handle` names no open workspace on this connection. */
|
|
64
|
+
readonly UNKNOWN_HANDLE: "E_UNKNOWN_HANDLE";
|
|
65
|
+
/** `revision` does not match the workspace's current revision. */
|
|
66
|
+
readonly STALE_REVISION: "E_STALE_REVISION";
|
|
67
|
+
/** A handle was used but no workspace store is installed (#271 not wired). */
|
|
68
|
+
readonly WORKSPACES_UNAVAILABLE: "E_WORKSPACES_UNAVAILABLE";
|
|
69
|
+
/** A requested output name resolved outside the output root. */
|
|
70
|
+
readonly OUTPUT_ROOT_ESCAPE: "E_OUTPUT_ROOT_ESCAPE";
|
|
71
|
+
/** Inline base64 was requested for an artifact over the size limit. */
|
|
72
|
+
readonly ARTIFACT_TOO_LARGE: "E_ARTIFACT_TOO_LARGE";
|
|
73
|
+
/** The document failed a rule that has no more specific code. */
|
|
74
|
+
readonly INVALID_DOCUMENT: "E_INVALID_DOCUMENT";
|
|
75
|
+
/** The document could not be parsed as JSON. */
|
|
76
|
+
readonly INVALID_JSON: "E_INVALID_JSON";
|
|
77
|
+
/** A property the schema requires is absent. */
|
|
78
|
+
readonly REQUIRED_PROPERTY: "E_REQUIRED_PROPERTY";
|
|
79
|
+
/** A property the component does not declare. */
|
|
80
|
+
readonly UNEXPECTED_PROPERTY: "E_UNEXPECTED_PROPERTY";
|
|
81
|
+
/** A value of the wrong JSON type. */
|
|
82
|
+
readonly TYPE_MISMATCH: "E_TYPE_MISMATCH";
|
|
83
|
+
/** No branch of a union accepted the value. */
|
|
84
|
+
readonly UNION_MISMATCH: "E_UNION_MISMATCH";
|
|
85
|
+
/** Right type, outside the schema's bounds, length, pattern or format. */
|
|
86
|
+
readonly VALUE_CONSTRAINT: "E_VALUE_CONSTRAINT";
|
|
87
|
+
/** Right type and shape, but not a value this position accepts. */
|
|
88
|
+
readonly INVALID_VALUE: "E_INVALID_VALUE";
|
|
89
|
+
/** `name` is not a component this format registers, or not one allowed here. */
|
|
90
|
+
readonly UNKNOWN_COMPONENT: "E_UNKNOWN_COMPONENT";
|
|
91
|
+
/** Two props that exclude each other were both set. */
|
|
92
|
+
readonly MUTUALLY_EXCLUSIVE: "E_MUTUALLY_EXCLUSIVE";
|
|
93
|
+
/** A theme the document names does not exist. */
|
|
94
|
+
readonly THEME_NOT_FOUND: "E_THEME_NOT_FOUND";
|
|
95
|
+
/** The document is empty. */
|
|
96
|
+
readonly EMPTY_DOCUMENT: "E_EMPTY_DOCUMENT";
|
|
97
|
+
/** The renderer could draw the document, but not this one feature of it. */
|
|
98
|
+
readonly UNSUPPORTED_RENDERER_FEATURE: "W_UNSUPPORTED_RENDERER_FEATURE";
|
|
99
|
+
/** A note the render host emitted mid-run (unknown theme, unreadable theme file). */
|
|
100
|
+
readonly HOST_NOTE: "W_HOST_NOTE";
|
|
101
|
+
/** A generation warning the core raised without a code of its own. */
|
|
102
|
+
readonly GENERATION: "W_GENERATION";
|
|
103
|
+
/** A required host binary (LibreOffice, poppler) is absent. */
|
|
104
|
+
readonly DEPENDENCY_MISSING: "E_DEPENDENCY_MISSING";
|
|
105
|
+
/** The client cancelled the request. */
|
|
106
|
+
readonly CANCELLED: "E_CANCELLED";
|
|
107
|
+
};
|
|
108
|
+
type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
|
|
109
|
+
/** Build one diagnostic; `severity` defaults to `error`. */
|
|
110
|
+
declare function diagnostic(code: string, message: string, extra?: Omit<Diagnostic, 'code' | 'message' | 'severity'> & {
|
|
111
|
+
severity?: DiagnosticSeverity;
|
|
112
|
+
}): Diagnostic;
|
|
113
|
+
/**
|
|
114
|
+
* Adapt the repo's `ValidationError` to a diagnostic.
|
|
115
|
+
*
|
|
116
|
+
* The two shapes already agree on `path`/`message`/`suggestion`; the mapping
|
|
117
|
+
* exists to give every diagnostic a code from one published vocabulary, so
|
|
118
|
+
* clients can always switch on `code`.
|
|
119
|
+
*/
|
|
120
|
+
declare function fromValidationError(error: ValidationError, severity?: DiagnosticSeverity): Diagnostic;
|
|
121
|
+
declare function fromValidationErrors(errors: readonly ValidationError[] | undefined, severity?: DiagnosticSeverity): Diagnostic[];
|
|
122
|
+
/** A failed operation: `ok: false` plus at least one diagnostic. */
|
|
123
|
+
interface Failure extends ToolEnvelope {
|
|
124
|
+
ok: false;
|
|
125
|
+
}
|
|
126
|
+
declare function failure(code: string, message: string, extra?: Omit<Diagnostic, 'code' | 'message' | 'severity'> & {
|
|
127
|
+
severity?: DiagnosticSeverity;
|
|
128
|
+
}): Failure;
|
|
129
|
+
declare function failureFrom(diagnostics: Diagnostic[]): Failure;
|
|
130
|
+
/** A successful operation, with room for the non-fatal diagnostics it collected. */
|
|
131
|
+
declare function success<T extends object>(payload: T, diagnostics?: Diagnostic[]): T & ToolEnvelope;
|
|
132
|
+
/**
|
|
133
|
+
* The two-channel result every tool returns.
|
|
134
|
+
*
|
|
135
|
+
* `structuredContent` is what the schema-aware client reads; the text block is
|
|
136
|
+
* the same object stringified, which is what a client without structured
|
|
137
|
+
* output support (and every transcript) sees. They are never allowed to
|
|
138
|
+
* disagree, hence one argument.
|
|
139
|
+
*/
|
|
140
|
+
declare function toolResult<T extends object>(payload: T): {
|
|
141
|
+
content: [{
|
|
142
|
+
type: 'text';
|
|
143
|
+
text: string;
|
|
144
|
+
}];
|
|
145
|
+
structuredContent: T;
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Run a tool body, converting anything that escapes into a diagnostic and
|
|
149
|
+
* collecting the warnings the run emitted along the way.
|
|
150
|
+
*
|
|
151
|
+
* Without the first half an exception becomes a JSON-RPC error, which is
|
|
152
|
+
* exactly the signal we reserve for transport failures — the agent would be
|
|
153
|
+
* told the server broke when in fact one document did.
|
|
154
|
+
*
|
|
155
|
+
* The second half is why the sink is installed here and not once per
|
|
156
|
+
* connection: `runWithDiagnosticSink` is `AsyncLocalStorage.run`, so a sink
|
|
157
|
+
* wrapped around server setup is long gone by the time a request arrives on a
|
|
158
|
+
* later turn of the loop, and every "Unknown theme …" `jto-ops` emitted was
|
|
159
|
+
* dropped. One request is the largest scope that actually holds, and it is
|
|
160
|
+
* also the one the agent can read — the notes come back in `diagnostics`
|
|
161
|
+
* beside the result they belong to instead of on a stderr no client parses.
|
|
162
|
+
*/
|
|
163
|
+
declare function guarded<T extends object>(body: () => Promise<T>): Promise<T | Failure>;
|
|
164
|
+
/**
|
|
165
|
+
* Codes for defects in the REQUEST rather than in the document.
|
|
166
|
+
*
|
|
167
|
+
* `ERROR_CODES` covers the document and the transport; these cover the options
|
|
168
|
+
* an agent chose, which are a third thing — an agent that asked for a renderer
|
|
169
|
+
* that does not exist has nothing to repair in its JSON.
|
|
170
|
+
*/
|
|
171
|
+
declare const OPTION_ERROR_CODES: {
|
|
172
|
+
/** `renderer` names no renderer this format registers. */
|
|
173
|
+
readonly UNKNOWN_RENDERER: "E_UNKNOWN_RENDERER";
|
|
174
|
+
/** `date` is not parseable as a date. */
|
|
175
|
+
readonly INVALID_DATE: "E_INVALID_DATE";
|
|
176
|
+
/** `themePath` is not a data-only JSON theme path. */
|
|
177
|
+
readonly INVALID_THEME_PATH: "E_INVALID_THEME_PATH";
|
|
178
|
+
/** The tool does not support the requested format. */
|
|
179
|
+
readonly UNSUPPORTED_FORMAT: "E_UNSUPPORTED_FORMAT";
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Normalize a validator path to an RFC 6901 JSON Pointer.
|
|
183
|
+
*
|
|
184
|
+
* The validators speak two dialects. The document validators emit
|
|
185
|
+
* pointer-shaped strings already (`/children/0/props/text`) but leave `~`
|
|
186
|
+
* unescaped and spell the root `root`; the older component validators emit
|
|
187
|
+
* JavaScript-ish paths (`children[0].props.text`, `name`). Both end up here so
|
|
188
|
+
* that every diagnostic this server hands back can be used verbatim as a JSON
|
|
189
|
+
* Patch target against a workspace document (#271).
|
|
190
|
+
*/
|
|
191
|
+
declare function toJsonPointer(path: string | undefined): string | undefined;
|
|
192
|
+
/** Adapt validator errors to diagnostics, with pointers and gate-faithful severity. */
|
|
193
|
+
declare function validationDiagnostics(errors: readonly ValidationError[] | undefined): Diagnostic[];
|
|
194
|
+
/**
|
|
195
|
+
* Diagnostics out of an exception, when the exception is really a bad document.
|
|
196
|
+
*
|
|
197
|
+
* Both cores gate generation by throwing — `JsonValidationError` carrying
|
|
198
|
+
* `validationErrors`, `PresentationValidationError` carrying `errors`. Left
|
|
199
|
+
* alone those become `E_INTERNAL`, which tells an agent the server broke when
|
|
200
|
+
* in fact its JSON did. Duck-typed rather than `instanceof`, because the
|
|
201
|
+
* classes live inside the cores that `jto-ops` deliberately imports on demand.
|
|
202
|
+
*/
|
|
203
|
+
declare function diagnosticsFromThrown(error: unknown): Diagnostic[] | undefined;
|
|
204
|
+
interface DiagnosticCounts {
|
|
205
|
+
error: number;
|
|
206
|
+
warning: number;
|
|
207
|
+
info: number;
|
|
208
|
+
}
|
|
209
|
+
/** Diagnostics by severity. `error > 0` is what every tool gates `ok` on. */
|
|
210
|
+
declare function countDiagnostics(diagnostics: readonly Diagnostic[]): DiagnosticCounts;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Access to the `@json-to-office/jto-ops` format adapters.
|
|
214
|
+
*
|
|
215
|
+
* The adapters themselves import their core on demand, so constructing one is
|
|
216
|
+
* cheap; memoizing is about identity rather than cost — the DOCX adapter keeps
|
|
217
|
+
* per-instance caches (theme resolution, visual pre-pass counters) that a
|
|
218
|
+
* fresh instance per tool call would throw away every request.
|
|
219
|
+
*/
|
|
220
|
+
|
|
221
|
+
/** The adapter for `format`, constructed on first use and reused after. */
|
|
222
|
+
declare function getAdapter(format: FormatName): FormatAdapter;
|
|
223
|
+
/** Drop the memoized adapters. Tests use this to isolate their caches. */
|
|
224
|
+
declare function resetAdapters(): void;
|
|
225
|
+
/**
|
|
226
|
+
* Reject an unknown renderer before any work happens.
|
|
227
|
+
*
|
|
228
|
+
* The ids come from the core's own registry, so this can never advertise a
|
|
229
|
+
* stale set — same check `jto generate` makes, for the same reason: a typo
|
|
230
|
+
* should cost a message, not a full render followed by one.
|
|
231
|
+
*/
|
|
232
|
+
declare function checkRenderer(adapter: FormatAdapter, renderer: string | undefined): Promise<Failure | undefined>;
|
|
233
|
+
/**
|
|
234
|
+
* The document as the requested renderer profile sees it.
|
|
235
|
+
*
|
|
236
|
+
* Both formats discriminate their schema on the document's own top-level
|
|
237
|
+
* `renderer`, so asking "does this validate as office-open?" means validating
|
|
238
|
+
* a copy that says so. A copy, not a mutation: the caller's tree — which may
|
|
239
|
+
* be a workspace document — must come back unchanged.
|
|
240
|
+
*/
|
|
241
|
+
declare function withRenderer(document: unknown, renderer: string | undefined): unknown;
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* The one directory this server is allowed to write to.
|
|
245
|
+
*
|
|
246
|
+
* #204's contract is "no writes outside the root", and the caller names the
|
|
247
|
+
* file. Anything a caller can name, a caller can point somewhere else — so the
|
|
248
|
+
* check has to survive `..`, absolute paths, Windows drive letters and, once
|
|
249
|
+
* the root is a real directory on disk, symlinks planted inside it. Every
|
|
250
|
+
* write therefore goes through `resolveOutputPath`, which answers a path only
|
|
251
|
+
* when it provably lands inside the root.
|
|
252
|
+
*/
|
|
253
|
+
|
|
254
|
+
/** Env var that names the output root, below the `--output-dir` flag. */
|
|
255
|
+
declare const OUTPUT_DIR_ENV = "JTO_MCP_OUTPUT_DIR";
|
|
256
|
+
interface OutputRoot {
|
|
257
|
+
/** Absolute path of the root. May not exist on disk until first use. */
|
|
258
|
+
readonly path: string;
|
|
259
|
+
/**
|
|
260
|
+
* True when this root is a temp directory this process invented, and so is
|
|
261
|
+
* safe to delete on shutdown. A configured root is never removed.
|
|
262
|
+
*/
|
|
263
|
+
readonly ephemeral: boolean;
|
|
264
|
+
/** Create the root if absent; returns its real (symlink-resolved) path. */
|
|
265
|
+
ensure(): Promise<string>;
|
|
266
|
+
/**
|
|
267
|
+
* Absolute path for `name` inside the root, with parent directories created.
|
|
268
|
+
* Fails structurally rather than throwing — the result is a tool result.
|
|
269
|
+
*/
|
|
270
|
+
resolveOutputPath(name: string): Promise<ResolvedOutputPath>;
|
|
271
|
+
/** Remove the root, but only when `ephemeral`. */
|
|
272
|
+
dispose(): Promise<void>;
|
|
273
|
+
}
|
|
274
|
+
type ResolvedOutputPath = {
|
|
275
|
+
ok: true;
|
|
276
|
+
path: string;
|
|
277
|
+
relative: string;
|
|
278
|
+
} | Failure;
|
|
279
|
+
interface OutputRootOptions {
|
|
280
|
+
/** `--output-dir` value, highest precedence. */
|
|
281
|
+
flagDir?: string;
|
|
282
|
+
/** Defaults to `process.env`; injectable so the precedence is testable. */
|
|
283
|
+
env?: NodeJS.ProcessEnv;
|
|
284
|
+
/** Defaults to `os.tmpdir()`; injectable for the same reason. */
|
|
285
|
+
tmpDir?: string;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Reject a name before it ever touches the filesystem.
|
|
289
|
+
*
|
|
290
|
+
* Cheap, synchronous and exhaustive about the shapes that cannot possibly be
|
|
291
|
+
* inside the root, so the expensive realpath check below only ever has to
|
|
292
|
+
* worry about symlinks.
|
|
293
|
+
*/
|
|
294
|
+
declare function checkOutputName(name: string): Failure | undefined;
|
|
295
|
+
/**
|
|
296
|
+
* Resolve the root: `--output-dir`, then `JTO_MCP_OUTPUT_DIR`, then a temp
|
|
297
|
+
* directory of our own.
|
|
298
|
+
*
|
|
299
|
+
* Creation is deferred — a connection that never generates anything should not
|
|
300
|
+
* leave a directory behind — so the temp root's name is decided here but the
|
|
301
|
+
* `mkdir` happens on first use.
|
|
302
|
+
*/
|
|
303
|
+
declare function createOutputRoot(options?: OutputRootOptions): OutputRoot;
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* The workspace contract.
|
|
307
|
+
*
|
|
308
|
+
* #271 adds connection-scoped documents an agent edits by JSON Patch instead
|
|
309
|
+
* of resending the whole tree. This module is the seam: the interface, the
|
|
310
|
+
* holder for a host's process-wide override, and a stand-in that answers every
|
|
311
|
+
* call with a structured "unavailable" so the inline path works untouched
|
|
312
|
+
* before #271 lands and on any connection where workspaces are switched off.
|
|
313
|
+
*
|
|
314
|
+
* The holder is deliberately NOT where a connection's own store lives. Handles
|
|
315
|
+
* are scoped to one connection, and a module-global default would hand the
|
|
316
|
+
* second `createServer` in a process the first one's documents; the connection
|
|
317
|
+
* store hangs off its `ToolDeps` instead (`../tools/workspace.ts`).
|
|
318
|
+
*
|
|
319
|
+
* The real store lives in `../workspace/store.ts`.
|
|
320
|
+
*
|
|
321
|
+
* Every method resolves rather than throws. A missing handle and a stale
|
|
322
|
+
* revision are ordinary answers a tool reports to the agent, not server
|
|
323
|
+
* failures (see `errors.ts`).
|
|
324
|
+
*/
|
|
325
|
+
|
|
326
|
+
type WorkspaceResult<T> = ({
|
|
327
|
+
ok: true;
|
|
328
|
+
} & T) | Failure;
|
|
329
|
+
/** RFC 6902 operation. Paths are RFC 6901 pointers — no private dialect (#271). */
|
|
330
|
+
interface JsonPatchOperation {
|
|
331
|
+
op: 'add' | 'remove' | 'replace' | 'move' | 'copy' | 'test';
|
|
332
|
+
path: string;
|
|
333
|
+
from?: string;
|
|
334
|
+
value?: unknown;
|
|
335
|
+
}
|
|
336
|
+
/** What `jto_workspace_list` shows, and what every mutation returns. */
|
|
337
|
+
interface WorkspaceRecord {
|
|
338
|
+
/** Opaque, server-generated, meaningless outside this connection. */
|
|
339
|
+
handle: string;
|
|
340
|
+
format: FormatName;
|
|
341
|
+
/** Starts at 1 on create; +1 per committed patch. Never reused, never decreases. */
|
|
342
|
+
revision: number;
|
|
343
|
+
/** Size of the serialized document, for the agent's own budgeting. */
|
|
344
|
+
bytes: number;
|
|
345
|
+
createdAt: string;
|
|
346
|
+
updatedAt: string;
|
|
347
|
+
/** Caller-supplied label, echoed back verbatim. */
|
|
348
|
+
title?: string;
|
|
349
|
+
/** Revisions pinned by `snapshot`, still retrievable through `get`. */
|
|
350
|
+
pinnedRevisions: number[];
|
|
351
|
+
}
|
|
352
|
+
interface WorkspaceStore {
|
|
353
|
+
/**
|
|
354
|
+
* False on the stand-in. Tools read it to describe the connection's
|
|
355
|
+
* capabilities (`jto_info`) without provoking an error.
|
|
356
|
+
*/
|
|
357
|
+
readonly available: boolean;
|
|
358
|
+
/** Open a document. The returned revision is 1. */
|
|
359
|
+
create(input: {
|
|
360
|
+
format: FormatName;
|
|
361
|
+
document: unknown;
|
|
362
|
+
title?: string;
|
|
363
|
+
}): Promise<WorkspaceResult<{
|
|
364
|
+
record: WorkspaceRecord;
|
|
365
|
+
}>>;
|
|
366
|
+
/**
|
|
367
|
+
* Read a document.
|
|
368
|
+
*
|
|
369
|
+
* With `revision`, the read is checked: a revision that is neither current
|
|
370
|
+
* nor pinned fails `E_STALE_REVISION` rather than quietly returning newer
|
|
371
|
+
* JSON than the caller reasoned about. With `paths`, only those JSON
|
|
372
|
+
* Pointers are projected, keyed by pointer — that is `jto_workspace_inspect`
|
|
373
|
+
* on a large document.
|
|
374
|
+
*/
|
|
375
|
+
get(handle: string, options?: {
|
|
376
|
+
revision?: number;
|
|
377
|
+
paths?: readonly string[];
|
|
378
|
+
}): Promise<WorkspaceResult<{
|
|
379
|
+
record: WorkspaceRecord;
|
|
380
|
+
document: unknown;
|
|
381
|
+
/** Present only when `paths` was given. */
|
|
382
|
+
projection?: Record<string, unknown>;
|
|
383
|
+
}>>;
|
|
384
|
+
/**
|
|
385
|
+
* Apply a patch atomically.
|
|
386
|
+
*
|
|
387
|
+
* Syntax and paths are checked, the patch is applied to a copy, and only a
|
|
388
|
+
* clean apply commits and bumps the revision — a half-applied document is
|
|
389
|
+
* never observable. `baseRevision` makes the write conditional; omitting it
|
|
390
|
+
* is a deliberate last-writer-wins.
|
|
391
|
+
*/
|
|
392
|
+
patch(input: {
|
|
393
|
+
handle: string;
|
|
394
|
+
operations: readonly JsonPatchOperation[];
|
|
395
|
+
baseRevision?: number;
|
|
396
|
+
}): Promise<WorkspaceResult<{
|
|
397
|
+
record: WorkspaceRecord;
|
|
398
|
+
}>>;
|
|
399
|
+
/**
|
|
400
|
+
* Export the document and pin the revision it was taken at.
|
|
401
|
+
*
|
|
402
|
+
* The pin is what makes "snapshot before risky changes" real: after further
|
|
403
|
+
* patches, `get(handle, pinnedRevision)` still returns this exact tree, so
|
|
404
|
+
* an agent can compare or roll back without having kept the JSON in context.
|
|
405
|
+
*/
|
|
406
|
+
snapshot(handle: string): Promise<WorkspaceResult<{
|
|
407
|
+
record: WorkspaceRecord;
|
|
408
|
+
document: unknown;
|
|
409
|
+
}>>;
|
|
410
|
+
/** Every open handle on this connection. Recovers references after context loss. */
|
|
411
|
+
list(): Promise<WorkspaceResult<{
|
|
412
|
+
records: WorkspaceRecord[];
|
|
413
|
+
}>>;
|
|
414
|
+
/** Release a handle and its memory. Idempotent: closing twice is not an error. */
|
|
415
|
+
close(handle: string): Promise<WorkspaceResult<{
|
|
416
|
+
handle: string;
|
|
417
|
+
closed: boolean;
|
|
418
|
+
}>>;
|
|
419
|
+
/**
|
|
420
|
+
* Release every handle at once.
|
|
421
|
+
*
|
|
422
|
+
* A connection's store is reachable only through its `ToolDeps`, so the
|
|
423
|
+
* documents go when the connection's deps do; this is for a host that wants
|
|
424
|
+
* the memory back at a moment of its own choosing.
|
|
425
|
+
*/
|
|
426
|
+
closeAll(): Promise<void>;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* The no-workspaces implementation.
|
|
430
|
+
*
|
|
431
|
+
* Note `list` succeeds with an empty array: "no open documents" is a true and
|
|
432
|
+
* useful answer, and failing it would make an agent think its own bookkeeping
|
|
433
|
+
* broke.
|
|
434
|
+
*/
|
|
435
|
+
declare const unavailableWorkspaceStore: WorkspaceStore;
|
|
436
|
+
/** Install a process-wide store. Passing `undefined` removes the override. */
|
|
437
|
+
declare function setWorkspaceStore(store: WorkspaceStore | undefined): void;
|
|
438
|
+
declare function getWorkspaceStore(): WorkspaceStore;
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* What every tool module is handed at registration.
|
|
442
|
+
*
|
|
443
|
+
* Tools reach for nothing else process-wide: the output root, the adapters and
|
|
444
|
+
* the workspace store all arrive here, so a test can stand a whole server up
|
|
445
|
+
* on a temp directory and a fake store without mutating global state.
|
|
446
|
+
*/
|
|
447
|
+
|
|
448
|
+
interface ToolDeps {
|
|
449
|
+
/** `@json-to-office/mcp-server`'s own version, as reported by `jto_info`. */
|
|
450
|
+
serverVersion: string;
|
|
451
|
+
/** The only directory the server writes to. */
|
|
452
|
+
outputRoot: OutputRoot;
|
|
453
|
+
/** Memoized format adapters from `@json-to-office/jto-ops`. */
|
|
454
|
+
getAdapter(format: FormatName): FormatAdapter;
|
|
455
|
+
/**
|
|
456
|
+
* The connection's workspace store.
|
|
457
|
+
*
|
|
458
|
+
* A function, not a value: #271 installs the real store after the tools are
|
|
459
|
+
* registered, and a snapshot taken at registration would pin the stand-in.
|
|
460
|
+
*/
|
|
461
|
+
workspaces(): WorkspaceStore;
|
|
462
|
+
/** Ceiling for `outputMode: 'base64'`, in bytes. */
|
|
463
|
+
maxInlineArtifactBytes: number;
|
|
464
|
+
}
|
|
465
|
+
interface CreateToolDepsOptions {
|
|
466
|
+
/** `--output-dir`, or an already-built root (tests hand one in). */
|
|
467
|
+
outputDir?: string;
|
|
468
|
+
outputRoot?: OutputRoot;
|
|
469
|
+
env?: NodeJS.ProcessEnv;
|
|
470
|
+
serverVersion?: string;
|
|
471
|
+
workspaces?: () => WorkspaceStore;
|
|
472
|
+
getAdapter?: (format: FormatName) => FormatAdapter;
|
|
473
|
+
maxInlineArtifactBytes?: number;
|
|
474
|
+
}
|
|
475
|
+
declare function createToolDeps(options?: CreateToolDepsOptions): ToolDeps;
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* The server, assembled once.
|
|
479
|
+
*
|
|
480
|
+
* Every tool module exposes the same `register(server, deps)` and is listed
|
|
481
|
+
* here in a fixed order, so the modules can be written independently and this
|
|
482
|
+
* file never has to change again as they land. Registration order is the order
|
|
483
|
+
* `tools/list` reports, which is also the order an agent reads them in — hence
|
|
484
|
+
* info, then discovery, then the authoring loop, then workspaces.
|
|
485
|
+
*/
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* The server's own prompt, surfaced to the client at initialize.
|
|
489
|
+
*
|
|
490
|
+
* These are the invariants an agent gets wrong without being told: that the
|
|
491
|
+
* JSON is the artifact and the file is a build product, that large rewrites
|
|
492
|
+
* lose more than they fix, and that looking at a rendered page is cheaper than
|
|
493
|
+
* reasoning about whether a layout worked (#271).
|
|
494
|
+
*/
|
|
495
|
+
declare const SERVER_INSTRUCTIONS = "Author Microsoft Word (.docx) and PowerPoint (.pptx) documents as JSON.\n\nThe JSON is authoritative. A generated file is a build product of the document JSON plus a renderer, a theme, fonts, assets and options \u2014 edit the JSON and regenerate; never treat the binary as the source.\n\nWorking rules:\n- Discover before authoring. Call jto_info first, then jto_discover and jto_describe_component (or read the jto:// resources) for the components and renderer ids a format actually supports.\n- Make small edits. With a workspace handle, patch precisely (RFC 6902 over RFC 6901 paths) instead of resending the whole document; without one, change one region at a time.\n- Validate often. Run jto_validate after each edit rather than once at the end; diagnostics are path-addressed, so they map straight back onto the JSON you just changed.\n- Preview when the answer is visual. jto_preview renders pages to PNG; use it whenever layout, overflow or fit is in question, not only before finishing.\n- Snapshot before risky changes. jto_workspace_snapshot pins the current revision so a restructuring you cannot cleanly undo is still recoverable.\n\nDocument defects come back as structured diagnostics with ok: false, not as errors \u2014 read them and repair. Generated files are written under the server's output root and returned as paths; ask for base64 only for small artifacts.";
|
|
496
|
+
/** Build a server with every tool and resource registered. */
|
|
497
|
+
declare function createServer(deps: ToolDeps): McpServer;
|
|
498
|
+
/**
|
|
499
|
+
* The factory `serveStdio` wants.
|
|
500
|
+
*
|
|
501
|
+
* It is called once per connection and, on a 2025-era opening, once more for
|
|
502
|
+
* the pinned legacy instance — so it must build a fresh `McpServer` every
|
|
503
|
+
* time. `deps` are deliberately shared: the output root and the workspace
|
|
504
|
+
* store belong to the process, which for stdio is the connection.
|
|
505
|
+
*/
|
|
506
|
+
declare function createServerFactory(deps: ToolDeps): McpServerFactory;
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Tool schemas, authored as plain JSON Schema.
|
|
510
|
+
*
|
|
511
|
+
* `fromJsonSchema` wraps a JSON Schema in the Standard Schema shape the SDK
|
|
512
|
+
* validates against, and passes the schema itself through to `tools/list`
|
|
513
|
+
* verbatim. Authoring JSON Schema directly therefore keeps zod out of our own
|
|
514
|
+
* source and guarantees that what an agent discovers is exactly what the
|
|
515
|
+
* server enforces — no conversion step in between to drift.
|
|
516
|
+
*/
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Wrap a JSON Schema for `registerTool`.
|
|
520
|
+
*
|
|
521
|
+
* The type parameter is the handler's view of the validated value — the SDK
|
|
522
|
+
* infers callback arguments from it and cannot derive it from a runtime schema
|
|
523
|
+
* object, so pass it explicitly: `S<{ format: FormatName }>({ … })`.
|
|
524
|
+
*/
|
|
525
|
+
declare function S<T = unknown>(schema: JsonSchemaType): StandardSchemaWithJSON<T, T>;
|
|
526
|
+
/** Every format this server can author. */
|
|
527
|
+
declare const FORMAT_NAMES: readonly FormatName[];
|
|
528
|
+
declare const formatSchema: JsonSchemaType;
|
|
529
|
+
/**
|
|
530
|
+
* The document a tool operates on: inline JSON, or a workspace reference.
|
|
531
|
+
*
|
|
532
|
+
* Both are spelled out on every document-taking tool rather than hidden behind
|
|
533
|
+
* a `oneOf`, because a discovering agent reads the flat property list and many
|
|
534
|
+
* clients render nothing else. `doc-source.ts` enforces the exclusivity that
|
|
535
|
+
* the schema deliberately does not.
|
|
536
|
+
*/
|
|
537
|
+
declare const documentSourceProperties: Record<string, JsonSchemaType>;
|
|
538
|
+
/** Human-readable restatement of the rule the schema cannot express. */
|
|
539
|
+
declare const DOCUMENT_SOURCE_RULE = "Supply exactly one of `document` (inline JSON) or `handle` (an open workspace).";
|
|
540
|
+
/**
|
|
541
|
+
* Renderer, theme and determinism knobs, mirroring `GeneratorOptions` from
|
|
542
|
+
* `@json-to-office/jto-ops`. Shared verbatim by validate/generate/preview/diff
|
|
543
|
+
* so the same document renders the same way whichever tool an agent reaches
|
|
544
|
+
* for.
|
|
545
|
+
*/
|
|
546
|
+
declare const renderOptionProperties: Record<string, JsonSchemaType>;
|
|
547
|
+
/** How a generated file comes back: written to disk, or inline. */
|
|
548
|
+
declare const artifactOutputProperties: Record<string, JsonSchemaType>;
|
|
549
|
+
declare const diagnosticSchema: JsonSchemaType;
|
|
550
|
+
declare const diagnosticsSchema: JsonSchemaType;
|
|
551
|
+
/** The `{ ok, diagnostics }` floor every tool output schema builds on. */
|
|
552
|
+
declare const envelopeProperties: Record<string, JsonSchemaType>;
|
|
553
|
+
/**
|
|
554
|
+
* A delivered file, exactly as `deliverArtifact` returns one.
|
|
555
|
+
*
|
|
556
|
+
* `relative` is not decoration: the SDK validates outgoing
|
|
557
|
+
* `structuredContent` against the declared schema, and this object closes
|
|
558
|
+
* `additionalProperties`, so omitting a field every path-mode artifact
|
|
559
|
+
* carries would turn each successful generation into an output-validation
|
|
560
|
+
* error. Generate, diff, preview and snapshot all report through this one
|
|
561
|
+
* definition.
|
|
562
|
+
*/
|
|
563
|
+
declare const artifactSchema: JsonSchemaType;
|
|
564
|
+
/**
|
|
565
|
+
* `documentSourceProperties` as one nested object.
|
|
566
|
+
*
|
|
567
|
+
* A tool that takes two documents cannot spell them both flat — `jto_docx_diff`
|
|
568
|
+
* would need two `document` keys — so `before`/`after` each carry a bag of
|
|
569
|
+
* this shape. Single-document tools stay flat, which is what an agent reading
|
|
570
|
+
* a property list expects.
|
|
571
|
+
*/
|
|
572
|
+
declare const documentSourceSchema: JsonSchemaType;
|
|
573
|
+
/** Where a tool actually read its document from, echoed back to the caller. */
|
|
574
|
+
declare const sourceSummarySchema: JsonSchemaType;
|
|
575
|
+
/** Compose an output schema from the standard envelope plus tool-specific fields. */
|
|
576
|
+
declare function outputSchema(properties: Record<string, JsonSchemaType>, required?: readonly string[]): JsonSchemaType;
|
|
577
|
+
/** The inline/handle pair, as the tool handlers see it after validation. */
|
|
578
|
+
interface DocumentSourceInput {
|
|
579
|
+
document?: unknown;
|
|
580
|
+
handle?: string;
|
|
581
|
+
revision?: number;
|
|
582
|
+
}
|
|
583
|
+
/** `sourceSummarySchema`, as the tools report it. */
|
|
584
|
+
interface SourceSummary {
|
|
585
|
+
origin: 'inline' | 'workspace';
|
|
586
|
+
handle?: string;
|
|
587
|
+
revision?: number;
|
|
588
|
+
}
|
|
589
|
+
/** `renderOptionProperties`, as the tool handlers see it after validation. */
|
|
590
|
+
interface RenderOptionsInput {
|
|
591
|
+
renderer?: string;
|
|
592
|
+
theme?: string;
|
|
593
|
+
themePath?: string;
|
|
594
|
+
deterministic?: boolean;
|
|
595
|
+
generatedAt?: string;
|
|
596
|
+
baseDir?: string;
|
|
597
|
+
}
|
|
598
|
+
/** `artifactOutputProperties`, as the tool handlers see it after validation. */
|
|
599
|
+
interface ArtifactOutputInput {
|
|
600
|
+
outputMode?: 'path' | 'base64';
|
|
601
|
+
filename?: string;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Delivery of generated bytes to the client.
|
|
606
|
+
*
|
|
607
|
+
* Default is a path under the output root: a .pptx inlined as base64 costs the
|
|
608
|
+
* agent its context window and the transport a multi-megabyte frame, for a
|
|
609
|
+
* payload it almost always just wants to hand to a viewer. Inlining is
|
|
610
|
+
* available, but only when asked for and only under a hard ceiling (#204).
|
|
611
|
+
*/
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Ceiling for `outputMode: 'base64'`.
|
|
615
|
+
*
|
|
616
|
+
* 4 MiB of binary is ~5.5 MiB of base64 — already past what most clients will
|
|
617
|
+
* put in a model context, and the point beyond which a path is strictly better
|
|
618
|
+
* for everyone.
|
|
619
|
+
*/
|
|
620
|
+
declare const MAX_INLINE_ARTIFACT_BYTES: number;
|
|
621
|
+
declare const MIME_TYPES: Record<string, string>;
|
|
622
|
+
type ArtifactMode = 'path' | 'base64';
|
|
623
|
+
type Artifact = {
|
|
624
|
+
mode: 'path';
|
|
625
|
+
/** Absolute path, always inside the output root. */
|
|
626
|
+
path: string;
|
|
627
|
+
/** Path relative to the output root, for display. */
|
|
628
|
+
relative: string;
|
|
629
|
+
bytes: number;
|
|
630
|
+
filename: string;
|
|
631
|
+
mimeType: string;
|
|
632
|
+
} | {
|
|
633
|
+
mode: 'base64';
|
|
634
|
+
base64: string;
|
|
635
|
+
bytes: number;
|
|
636
|
+
filename: string;
|
|
637
|
+
mimeType: string;
|
|
638
|
+
};
|
|
639
|
+
interface DeliverArtifactOptions {
|
|
640
|
+
/** Name inside the output root; rejected if it escapes (see `output-root.ts`). */
|
|
641
|
+
filename: string;
|
|
642
|
+
mimeType: string;
|
|
643
|
+
/** Defaults to `path`. */
|
|
644
|
+
mode?: ArtifactMode;
|
|
645
|
+
outputRoot: OutputRoot;
|
|
646
|
+
/** Override the inline ceiling; tests and hosts with tighter budgets use it. */
|
|
647
|
+
maxInlineBytes?: number;
|
|
648
|
+
}
|
|
649
|
+
type DeliverArtifactResult = {
|
|
650
|
+
ok: true;
|
|
651
|
+
artifact: Artifact;
|
|
652
|
+
} | Failure;
|
|
653
|
+
/**
|
|
654
|
+
* Turn a generated buffer into the payload a tool reports.
|
|
655
|
+
*
|
|
656
|
+
* Over-limit inlining is a refusal, not a silent downgrade to a path: an agent
|
|
657
|
+
* that asked for bytes and got a path would go looking for a file it cannot
|
|
658
|
+
* read on a host with no shared filesystem, and never learn why.
|
|
659
|
+
*/
|
|
660
|
+
declare function deliverArtifact(buffer: Buffer, options: DeliverArtifactOptions): Promise<DeliverArtifactResult>;
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* `{ document? | handle?, revision? }` → one concrete JSON document.
|
|
664
|
+
*
|
|
665
|
+
* Every document-taking tool starts here, so the inline and workspace paths
|
|
666
|
+
* cannot drift: whichever the agent used, the tool body sees the same tree and
|
|
667
|
+
* the same failure vocabulary.
|
|
668
|
+
*/
|
|
669
|
+
|
|
670
|
+
type ResolvedDocument = {
|
|
671
|
+
ok: true;
|
|
672
|
+
document: unknown;
|
|
673
|
+
origin: 'inline';
|
|
674
|
+
} | {
|
|
675
|
+
ok: true;
|
|
676
|
+
document: unknown;
|
|
677
|
+
origin: 'workspace';
|
|
678
|
+
handle: string;
|
|
679
|
+
/** The revision actually read, which may be older than current when pinned. */
|
|
680
|
+
revision: number;
|
|
681
|
+
} | Failure;
|
|
682
|
+
/**
|
|
683
|
+
* Resolve a document source.
|
|
684
|
+
*
|
|
685
|
+
* The store is a parameter rather than a module lookup so a tool can be tested
|
|
686
|
+
* against a fake without touching the process-wide holder; it defaults to the
|
|
687
|
+
* installed store, which is what production wants.
|
|
688
|
+
*/
|
|
689
|
+
declare function resolveDocumentSource(source: DocumentSourceInput, store?: WorkspaceStore): Promise<ResolvedDocument>;
|
|
690
|
+
/**
|
|
691
|
+
* Where the tool read the document from, for the caller's own bookkeeping.
|
|
692
|
+
*
|
|
693
|
+
* Every document-taking tool echoes this, so an agent can tell at a glance
|
|
694
|
+
* whether the answer describes the JSON it sent or the workspace revision the
|
|
695
|
+
* server holds — and, when pinned, which revision that actually was.
|
|
696
|
+
*/
|
|
697
|
+
declare function sourceSummary(resolved: Extract<ResolvedDocument, {
|
|
698
|
+
ok: true;
|
|
699
|
+
}>): SourceSummary;
|
|
700
|
+
/**
|
|
701
|
+
* Parse a document that arrived as a JSON string.
|
|
702
|
+
*
|
|
703
|
+
* Tools take `document` as an object, but a handful of callers (file contents,
|
|
704
|
+
* `jto_docx_diff`'s two sides) hold text; a parse failure is the agent's
|
|
705
|
+
* defect to fix, so it comes back structured like every other one.
|
|
706
|
+
*/
|
|
707
|
+
declare function parseDocumentJson(text: string, path?: string): {
|
|
708
|
+
ok: true;
|
|
709
|
+
document: unknown;
|
|
710
|
+
} | Failure;
|
|
711
|
+
|
|
712
|
+
declare const SERVER_VERSION: string;
|
|
713
|
+
/** MCP `serverInfo.name`. Stable: clients key configuration off it. */
|
|
714
|
+
declare const SERVER_NAME = "json-to-office";
|
|
715
|
+
/** npm identity, reported by `jto_info` next to the workspace packages. */
|
|
716
|
+
declare const PACKAGE_NAME = "@json-to-office/mcp-server";
|
|
717
|
+
|
|
718
|
+
export { type Artifact, type ArtifactMode, type ArtifactOutputInput, type CreateToolDepsOptions, DOCUMENT_SOURCE_RULE, type DeliverArtifactOptions, type DeliverArtifactResult, type Diagnostic, type DiagnosticCounts, type DiagnosticSeverity, type DocumentSourceInput, ERROR_CODES, type ErrorCode, FORMAT_NAMES, type Failure, type JsonPatchOperation, MAX_INLINE_ARTIFACT_BYTES, MIME_TYPES, OPTION_ERROR_CODES, OUTPUT_DIR_ENV, type OutputRoot, type OutputRootOptions, PACKAGE_NAME, type RenderOptionsInput, type ResolvedDocument, type ResolvedOutputPath, S, SERVER_INSTRUCTIONS, SERVER_NAME, SERVER_VERSION, type SourceSummary, type ToolDeps, type ToolEnvelope, type WorkspaceRecord, type WorkspaceResult, type WorkspaceStore, artifactOutputProperties, artifactSchema, checkOutputName, checkRenderer, countDiagnostics, createOutputRoot, createServer, createServerFactory, createToolDeps, deliverArtifact, diagnostic, diagnosticSchema, diagnosticsFromThrown, diagnosticsSchema, documentSourceProperties, documentSourceSchema, envelopeProperties, failure, failureFrom, formatSchema, fromValidationError, fromValidationErrors, getAdapter, getWorkspaceStore, guarded, outputSchema, parseDocumentJson, renderOptionProperties, resetAdapters, resolveDocumentSource, setWorkspaceStore, sourceSummary, sourceSummarySchema, success, toJsonPointer, toolResult, unavailableWorkspaceStore, validationDiagnostics, withRenderer };
|