@doclift/workflows-mcp 0.1.0 → 0.1.2
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/dist/guidance.js +28 -2
- package/dist/index.js +6 -1
- package/dist/tools/checks.js +7 -0
- package/dist/tools/sections.js +4 -1
- package/dist/tools/variables.js +5 -1
- package/package.json +2 -2
package/dist/guidance.js
CHANGED
|
@@ -69,6 +69,18 @@ The name is the **inner text**; only an *image* token reads \`data-src\`. A
|
|
|
69
69
|
integrity check, and prints the name of the variable instead of its value.
|
|
70
70
|
\`workflow_validate\` reports those as \`inert_tokens\`.
|
|
71
71
|
|
|
72
|
+
Which of \`text\` and \`checkbox\` you pick is a **different** matter, and one
|
|
73
|
+
you cannot get wrong: for a name the workflow declares, the renderer reads the
|
|
74
|
+
genre off the variable's \`field_type\`, not off the class. Declare a box and
|
|
75
|
+
write it with either token and a box is drawn. Get the *type* right in
|
|
76
|
+
\`variable_create\` — that is the declaration everything follows — and stop
|
|
77
|
+
worrying about which of the two classes you wrote.
|
|
78
|
+
|
|
79
|
+
Two places the class still decides, because nothing declares a type there: a
|
|
80
|
+
row field (\`collection.field\`, which no \`fields\` entry types) and a name no
|
|
81
|
+
variable declares at all. A box inside a repeated table row is therefore
|
|
82
|
+
written with the checkbox class and only with it.
|
|
83
|
+
|
|
72
84
|
**2 quinquies. A CSS property is dropped from inside a \`style\` that survives.**
|
|
73
85
|
The attribute is on the allow list, its contents are filtered against a list of
|
|
74
86
|
their own. \`text-transform\` goes, \`style\` stays, and nothing about the element
|
|
@@ -89,6 +101,17 @@ makes no network request at render time, so every picture has to go through
|
|
|
89
101
|
\`image_upload\` and be cited by the URL it answers with. \`workflow_validate\`
|
|
90
102
|
reports the others under \`unreachable_images\`.
|
|
91
103
|
|
|
104
|
+
**2 sexies. A running title band is clipped at the height of its margin.** The
|
|
105
|
+
band is framed at the margin reserved for its side — never less than
|
|
106
|
+
\`capabilities.authoring.band.reserved_mm_minimum\` — and what does not fit is
|
|
107
|
+
hidden. Measured at the default margins: it holds
|
|
108
|
+
\`capabilities.authoring.band.usable_lines_at_minimum\` lines and loses the rest,
|
|
109
|
+
**from the top for a footer**, so a paragraph of legal text comes back starting
|
|
110
|
+
mid-sentence. No write refuses it, \`workflow_validate\` sees nothing wrong with
|
|
111
|
+
the markup, and the render answers 200. \`workflow_selfcheck\` reports these as
|
|
112
|
+
\`clipped_bands\`. A band is a line, not a paragraph: legal text belongs in a
|
|
113
|
+
content section, which flows onto the next page instead of being cut.
|
|
114
|
+
|
|
92
115
|
**3. Declaring inline what the theme already says freezes the document.** It
|
|
93
116
|
looks identical today and stops following the theme forever. Set the theme, then
|
|
94
117
|
write only what departs from it. This is the mistake that costs the most and
|
|
@@ -194,10 +217,13 @@ satisfied.
|
|
|
194
217
|
the integrity check, and is invisible everywhere else.
|
|
195
218
|
3. **No picture is unreachable.** The engine makes no network request, and a
|
|
196
219
|
token that no longer names a stored image is removed at render time.
|
|
197
|
-
4. **No
|
|
220
|
+
4. **No band is longer than the margin reserved for it.** A running head or
|
|
221
|
+
foot is clipped, without an error, and a footer loses its opening rather
|
|
222
|
+
than its end.
|
|
223
|
+
5. **No variable was declared and never used.** The mirror of a broken
|
|
198
224
|
reference: it usually means a token was written with a typo, and the typo
|
|
199
225
|
became a second, dead name.
|
|
200
|
-
|
|
226
|
+
6. **A PDF exists, and it is newer than your last edit.** If you rendered before
|
|
201
227
|
your last change, what you looked at is not what you have.
|
|
202
228
|
|
|
203
229
|
## What it cannot check, and you must
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// organization the key belongs to. The only knowledge that lives here is the
|
|
7
7
|
// knowledge the API cannot express — the order to do things in, and the failures
|
|
8
8
|
// that succeed.
|
|
9
|
+
import { createRequire } from 'node:module';
|
|
9
10
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
10
11
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
11
12
|
import { z } from 'zod';
|
|
@@ -13,6 +14,10 @@ import { ApiClient } from './client.js';
|
|
|
13
14
|
import { registerTools } from './tools/index.js';
|
|
14
15
|
import { ANOMALIES, BEFORE_YOU_FINISH, CONDITIONS, HOW_TO_BUILD, REPETITIONS } from './guidance.js';
|
|
15
16
|
const DEFAULT_BASE_URL = 'https://app.doclift.io';
|
|
17
|
+
// Read rather than written a second time: the first publish that changed the
|
|
18
|
+
// guides shipped as 0.1.1 and still announced 0.1.0 in the handshake, which is
|
|
19
|
+
// the version a client shows its user and a bug report quotes.
|
|
20
|
+
const { version } = createRequire(import.meta.url)('../package.json');
|
|
16
21
|
const readConfig = () => {
|
|
17
22
|
const apiKey = process.env['DOCLIFT_API_KEY'];
|
|
18
23
|
if (!apiKey) {
|
|
@@ -55,7 +60,7 @@ const GUIDES = [
|
|
|
55
60
|
];
|
|
56
61
|
const main = async () => {
|
|
57
62
|
const { baseUrl, apiKey } = readConfig();
|
|
58
|
-
const server = new McpServer({ name: 'doclift-workflows', version
|
|
63
|
+
const server = new McpServer({ name: 'doclift-workflows', version }, {
|
|
59
64
|
instructions: 'Authoring of Doclift workflows: conditional PDF documents assembled from a tree of sections. ' +
|
|
60
65
|
'Call `workflow_capabilities` before writing anything — it carries the catalogues and the ' +
|
|
61
66
|
'limits for this organization, and nothing in this server duplicates them. Read the ' +
|
package/dist/tools/checks.js
CHANGED
|
@@ -99,6 +99,13 @@ const selfcheck = (validation, template, payload) => {
|
|
|
99
99
|
};
|
|
100
100
|
lines.push(listLine('inert_tokens', 'No variable token is inert', 'Rewrite each token with the class capabilities.content.tokens gives, then section_update.'));
|
|
101
101
|
lines.push(listLine('unreachable_images', 'No picture is unreachable', 'Upload each picture with image_upload and cite the URL it answers with.'));
|
|
102
|
+
// A band is framed at the height of the margin reserved for it and clipped.
|
|
103
|
+
// Measured: at the default margins it holds two lines and loses the third,
|
|
104
|
+
// from the top for a footer — so a paragraph of legal text comes back
|
|
105
|
+
// starting mid-sentence, and nothing else in this API says a word about it.
|
|
106
|
+
lines.push(listLine('clipped_bands', 'No running title band is longer than the margin reserved for it', 'Shorten the band, or widen the margin on that side. A band is not a place ' +
|
|
107
|
+
'for a paragraph: move legal text into a content section, which flows and ' +
|
|
108
|
+
'is never cut.'));
|
|
102
109
|
lines.push(listLine('layout_tables', 'No table is used for a column layout with its rules showing', 'Put capabilities.authoring.table.layout_row_class on the <tr>: it takes the rules away and ' +
|
|
103
110
|
'leaves the columns. A ruled table around two blocks set side by side is a shape no ' +
|
|
104
111
|
'hand-made document has.'));
|
package/dist/tools/sections.js
CHANGED
|
@@ -31,7 +31,10 @@ export const registerSections = (server, api, base) => {
|
|
|
31
31
|
'CLASS, not by its tag: <variable class="editor-text-variable non-editable-content editor-parsed">name</variable>, the name being ' +
|
|
32
32
|
'the inner text. Take the exact string from capabilities.content.tokens — a token ' +
|
|
33
33
|
'without the editor-parsed class prints the name instead of the value, and every ' +
|
|
34
|
-
'other check passes.'
|
|
34
|
+
'other check passes. Which of text and checkbox you write does NOT decide: for a ' +
|
|
35
|
+
"declared name the renderer reads the genre off the variable's field_type. The " +
|
|
36
|
+
'class still decides for a row field and for a name nothing declares, since no type ' +
|
|
37
|
+
'is attached to either.'),
|
|
35
38
|
parent_id: z.number().int().nullable().optional().describe('Must name a group of the same workflow.'),
|
|
36
39
|
position: z
|
|
37
40
|
.number()
|
package/dist/tools/variables.js
CHANGED
|
@@ -17,7 +17,11 @@ export const registerVariables = (server, api, base) => {
|
|
|
17
17
|
inputSchema: {
|
|
18
18
|
template_id: templateId,
|
|
19
19
|
name: z.string().min(1),
|
|
20
|
-
field_type: z
|
|
20
|
+
field_type: z
|
|
21
|
+
.string()
|
|
22
|
+
.describe('See capabilities.variable.field_types. This is what decides how a token for this ' +
|
|
23
|
+
'name renders: a `checkbox` prints a box whichever of the two token classes the ' +
|
|
24
|
+
'content was written with, anything else prints the value.'),
|
|
21
25
|
description: z.string().min(1).describe('Required. What the variable holds, for whoever fills it in.'),
|
|
22
26
|
required: z.boolean().optional(),
|
|
23
27
|
seed_value: z.string().optional().describe('What the preview shows in its place.'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doclift/workflows-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "MCP server over the Doclift workflow authoring API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://www.doclift.io/docs/getting-started",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"type": "module",
|
|
16
16
|
"bin": {
|
|
17
|
-
"doclift-workflows-mcp": "
|
|
17
|
+
"doclift-workflows-mcp": "dist/index.js"
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"dist"
|