@doclift/workflows-mcp 0.1.2 → 0.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/README.md CHANGED
@@ -48,6 +48,34 @@ carries the same rights it carries on the REST API — no more.
48
48
  }
49
49
  ```
50
50
 
51
+ ### Authorise the whole server, once
52
+
53
+ This server carries 35 tools, 22 of which write. A client that asks per tool
54
+ interrupts a build dozens of times, and a prompt that fails to surface is
55
+ indistinguishable from a server that has stopped answering — the call simply
56
+ waits until the client's tool timeout, having never reached the API. Grant the
57
+ server in one rule instead.
58
+
59
+ In Claude Code, add it to `permissions.allow` — in `.claude/settings.json` to
60
+ share it with the repository, `.claude/settings.local.json` to keep it to
61
+ yourself, or `~/.claude/settings.json` for every project:
62
+
63
+ ```json
64
+ {
65
+ "permissions": {
66
+ "allow": ["mcp__doclift-workflows"]
67
+ }
68
+ }
69
+ ```
70
+
71
+ The bare server name covers every tool it exposes, now and after an upgrade;
72
+ `mcp__doclift-workflows__*` is the same rule spelled with a wildcard. The server
73
+ segment is the key you gave the server under `mcpServers`, not the package name:
74
+ declare it as `doclift` and the rule reads `mcp__doclift`.
75
+
76
+ Other clients name their permissions differently; the rule above is the one this
77
+ project has verified.
78
+
51
79
  ## Calling it from a terminal
52
80
 
53
81
  `bin/mcp-call.mjs` speaks to the server over the same stdio transport a real
package/dist/guidance.js CHANGED
@@ -112,6 +112,19 @@ the markup, and the render answers 200. \`workflow_selfcheck\` reports these as
112
112
  \`clipped_bands\`. A band is a line, not a paragraph: legal text belongs in a
113
113
  content section, which flows onto the next page instead of being cut.
114
114
 
115
+ **2 septies. A list on a variable is a rule, not a hint.** \`allowed_values\`
116
+ used to be decorative on a workflow and is not any more: a generation sending a
117
+ value the list does not hold is refused with a 422 naming the variable, the
118
+ value and what was expected. The comparison is trimmed and case-insensitive —
119
+ \`"FR"\` satisfies a list holding \`"fr"\` — because it is the comparison the
120
+ conditions make, and a payload the engine would render correctly must not be
121
+ turned away. \`workflow_payload_contract\` lists the constrained names under
122
+ \`constrained\`.
123
+
124
+ Which makes the list worth declaring for a value a condition tests. A condition
125
+ comparing against something the list does not hold can never match, and nothing
126
+ warns you: the section simply never appears.
127
+
115
128
  **3. Declaring inline what the theme already says freezes the document.** It
116
129
  looks identical today and stops following the theme forever. Set the theme, then
117
130
  write only what departs from it. This is the mistake that costs the most and
@@ -33,8 +33,9 @@ export const registerChecks = (server, api, base) => {
33
33
  })));
34
34
  server.registerTool('workflow_payload_contract', {
35
35
  title: 'What a generation payload must carry',
36
- description: 'The variables a generation call has to send for this workflow, which are required, which rows ' +
37
- 'a collection must carry, and the limits on them. Read this before calling workflow_render.',
36
+ description: 'The variables a generation call has to send for this workflow: which are required, which carry ' +
37
+ 'a list of allowed values the generation enforces, which rows a collection must carry, and the ' +
38
+ 'limits on them. Read this before calling workflow_render.',
38
39
  inputSchema: { id: templateId },
39
40
  annotations: { readOnlyHint: true },
40
41
  }, async ({ id }) => respond(await api.get(`${base}/templates/${id}/payload_contract`)));
@@ -234,6 +235,7 @@ const selfcheck = (validation, template, payload) => {
234
235
  workflow: template === null ? null : { id: template['id'], title: template['title'], published: template['published'] },
235
236
  publishable: validation.publishable ?? null,
236
237
  required_variables: payload === null ? null : payload['required'],
238
+ constrained_variables: payload === null ? null : payload['constrained'],
237
239
  checklist: lines,
238
240
  outstanding: lines.filter((line) => line.status === 'todo').length,
239
241
  };
@@ -25,7 +25,11 @@ export const registerVariables = (server, api, base) => {
25
25
  description: z.string().min(1).describe('Required. What the variable holds, for whoever fills it in.'),
26
26
  required: z.boolean().optional(),
27
27
  seed_value: z.string().optional().describe('What the preview shows in its place.'),
28
- allowed_values: z.array(z.string()).optional().describe('select and radio only.'),
28
+ allowed_values: z
29
+ .array(z.string())
30
+ .optional()
31
+ .describe('select and radio only. Enforced at generation: a value outside the list is refused ' +
32
+ 'with a 422, compared trimmed and case-insensitively, exactly as a condition compares it.'),
29
33
  fields: z.array(z.record(z.unknown())).optional().describe('The row columns of a collection.'),
30
34
  },
31
35
  annotations: { readOnlyHint: false, destructiveHint: false },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doclift/workflows-mcp",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
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",
@@ -36,7 +36,7 @@
36
36
  "devDependencies": {
37
37
  "@types/node": "^22.10.0",
38
38
  "typescript": "^5.7.0",
39
- "vitest": "^3.0.0"
39
+ "vitest": "^5.0.1"
40
40
  },
41
41
  "engines": {
42
42
  "node": ">=20"