@forumone/throughline 1.9.1 → 2.0.0-next-a30f5dd
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/CHANGELOG.md +30 -0
- package/dist/authoring/descriptors.d.ts +44 -0
- package/dist/authoring/descriptors.d.ts.map +1 -0
- package/dist/authoring/descriptors.js +80 -0
- package/dist/authoring/descriptors.js.map +1 -0
- package/dist/authoring/prompts.d.ts +19 -0
- package/dist/authoring/prompts.d.ts.map +1 -0
- package/dist/authoring/prompts.js +63 -0
- package/dist/authoring/prompts.js.map +1 -0
- package/dist/authoring/surface.d.ts +17 -0
- package/dist/authoring/surface.d.ts.map +1 -0
- package/dist/authoring/surface.js +58 -0
- package/dist/authoring/surface.js.map +1 -0
- package/dist/authoring/tools.d.ts +27 -0
- package/dist/authoring/tools.d.ts.map +1 -0
- package/dist/authoring/tools.js +351 -0
- package/dist/authoring/tools.js.map +1 -0
- package/dist/content/blocks.d.ts +156 -0
- package/dist/content/blocks.d.ts.map +1 -1
- package/dist/content/blocks.js +203 -86
- package/dist/content/blocks.js.map +1 -1
- package/dist/content/guards.js +1 -1
- package/dist/content/guards.js.map +1 -1
- package/dist/content/tools.js +1 -1
- package/dist/content/tools.js.map +1 -1
- package/dist/content/write.js +4 -4
- package/dist/content/write.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/audit-server.d.ts.map +1 -1
- package/dist/mcp/audit-server.js +2 -0
- package/dist/mcp/audit-server.js.map +1 -1
- package/dist/mcp/collector.d.ts +11 -0
- package/dist/mcp/collector.d.ts.map +1 -1
- package/dist/mcp/collector.js +7 -0
- package/dist/mcp/collector.js.map +1 -1
- package/dist/migrate/exports.json +7 -0
- package/dist/publishing/pipeline/steps/approval.js +1 -1
- package/dist/publishing/pipeline/steps/approval.js.map +1 -1
- package/dist/publishing/tools/rollback.d.ts.map +1 -1
- package/dist/publishing/tools/rollback.js +16 -0
- package/dist/publishing/tools/rollback.js.map +1 -1
- package/dist/throughline.d.ts +13 -0
- package/dist/throughline.d.ts.map +1 -1
- package/dist/throughline.js +19 -1
- package/dist/throughline.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @forumone/throughline
|
|
2
2
|
|
|
3
|
+
## 2.0.0-next-a30f5dd
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- a30f5dd: **`/api/mcp` serves eight authoring tools instead of every module's tools.** (throughline#302, forumone-2026#830)
|
|
8
|
+
|
|
9
|
+
A full install used to serve 48 tools, about 9k tokens. That's more than a model chooses between well, and more than some clients allow: Cursor stops at 40. The server is now shaped by what it's for, which is drafting and publishing content from a client such as Claude Desktop:
|
|
10
|
+
|
|
11
|
+
- `find`, `get`, `save_draft`, `edit_blocks`, `check`, `publish`, `design_guide` and `compose_section`.
|
|
12
|
+
- Each is a thin wrapper. The modules still build their own tools, into a collector `plugin-mcp` never sees, and the eight call those tools' handlers with the caller's context. Access, audit and every check are unchanged.
|
|
13
|
+
- **`publish` takes an `action`:** `now`, `schedule`, `unpublish`, `rollback` or `request_approval`.
|
|
14
|
+
- Taking something live needs an admin or an editor. Set `mcp.canPublish` to use a different rule.
|
|
15
|
+
- A document that needs approval cannot go live until approval is granted. `now` says so, names the approver groups, and files the request itself when given `approval`.
|
|
16
|
+
- **`edit_blocks` applies a list of block operations** (insert, update, move, remove) to one field, and checks and saves them together.
|
|
17
|
+
- **The operations tools stay, under their own names, for admins only.** These are the audit queries, integrations, job failures, the approvals queue, the calendar, health and the reference checks. `mcp: { ops: false }` turns them off.
|
|
18
|
+
- **`suite.mcpPrompts` adds three MCP prompts:** `draft_post`, `build_landing_page` and `get_ready_to_publish`. Pass them to `mcpPlugin({ mcp: { tools: suite.mcpTools, prompts: suite.mcpPrompts } })`.
|
|
19
|
+
|
|
20
|
+
**Breaking:** the module tools are no longer served by name. The host needs:
|
|
21
|
+
|
|
22
|
+
- a migration, because `plugin-mcp` drops the old per-tool checkbox columns on `payload-mcp-api-keys` and adds the new ones;
|
|
23
|
+
- every key set up again;
|
|
24
|
+
- its key defaults updated to the new tool names.
|
|
25
|
+
|
|
26
|
+
Runtime messages now name the new tools: `get`, `check` and `publish`. The module tool factories are still exported.
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- a30f5dd: `rollback` restores the version as a draft, as its description promises. Before, rolling back wrote the version's own `_status` without saying it came from the publishing server, so on a site that blocks direct status writes every rollback was refused.
|
|
31
|
+
- @forumone/throughline-design-system@2.0.0-next-a30f5dd
|
|
32
|
+
|
|
3
33
|
## 1.9.1
|
|
4
34
|
|
|
5
35
|
### Patch Changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { McpToolDescriptor } from '../mcp/collector.js';
|
|
2
|
+
export declare const AUTHORING_TOOLS: {
|
|
3
|
+
readonly find: {
|
|
4
|
+
readonly name: "find";
|
|
5
|
+
readonly description: "Finds content to work on or link to. With a query across every content type, or within one `collection`. With `kind` to find something to link or attach instead (a person, an image, a tag). With `mine` for your own unpublished drafts and scheduled publishes. Results carry the id `get`, `save_draft` and `publish` take.";
|
|
6
|
+
};
|
|
7
|
+
readonly get: {
|
|
8
|
+
readonly name: "get";
|
|
9
|
+
readonly description: "Reads content. With `collection` and `id`: the document, in the shape `save_draft` takes, with its admin and preview links; `versions` adds its recent versions, for a rollback. With only `collection`: what you can write to that content type — every field, its limits, the blocks each blocks field accepts and what publishing it requires. With neither: the content types there are.";
|
|
10
|
+
};
|
|
11
|
+
readonly saveDraft: {
|
|
12
|
+
readonly name: "save_draft";
|
|
13
|
+
readonly description: "Saves a draft. Without `id` it creates one: a new document from `data`, with a slug made from the title if none is given. With `id` it changes only the fields in `data` (a group is merged, an array or blocks field is replaced) and leaves the published version alone. Never publishes. Rich text takes { markdown } or { html }. Returns the id and a preview link.";
|
|
14
|
+
};
|
|
15
|
+
readonly editBlocks: {
|
|
16
|
+
readonly name: "edit_blocks";
|
|
17
|
+
readonly description: "Changes the blocks in one blocks field of a draft — `layout`, or e.g. `approach.blocks` — without resending the rest: insert, update, move and remove, as a list applied in order and saved together. Every new or changed block is checked against what the field accepts, its own validation and the composition rules first; if any check fails, nothing is saved. Block ids come from `get`.";
|
|
18
|
+
};
|
|
19
|
+
readonly check: {
|
|
20
|
+
readonly name: "check";
|
|
21
|
+
readonly description: "Whether a document would publish right now, without publishing it: every check `publish` makes, with all the blockers at once so they can be fixed together, and the preview link a person opens to see the draft (signed in). Run it before offering to publish.";
|
|
22
|
+
};
|
|
23
|
+
readonly publish: {
|
|
24
|
+
readonly name: "publish";
|
|
25
|
+
readonly description: "Takes a document live, or changes what is live. `action`: \"now\" publishes the current draft; \"schedule\" publishes it at `at`; \"unpublish\" takes it down; \"rollback\" restores an earlier version (`versionId`, from `get` with `versions`) as the draft; \"request_approval\" asks approvers to sign it off. Publishing needs an editor or admin, and a document whose policy requires approval cannot go live until it is granted: \"now\" says so, and files the request itself if `approval` is given. Always confirm with the person first.";
|
|
26
|
+
};
|
|
27
|
+
readonly designGuide: {
|
|
28
|
+
readonly name: "design_guide";
|
|
29
|
+
readonly description: "The design system, for choosing and filling blocks. With `intent`: the components that suit what the person wants, ranked, with reasons. With `component`: that component's full contract — its fields, variants, composition rules, accessibility and anti-examples. With `recipes`: what a composed section may be built from. With none: every component, by category.";
|
|
30
|
+
};
|
|
31
|
+
readonly composeSection: {
|
|
32
|
+
readonly name: "compose_section";
|
|
33
|
+
readonly description: "For a section no component fits: checks a composed section's recipe (its contract and tree of primitives and inline components) against the design system, or with `save` saves it as a draft recipe. A person must approve a recipe before a page using it can publish; you cannot. Ask `design_guide` with `recipes` for the building blocks first.";
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
export declare const AUTHORING_TOOL_DESCRIPTORS: readonly McpToolDescriptor[];
|
|
37
|
+
/**
|
|
38
|
+
* The module tools that stay on the server, behind the admin-only check:
|
|
39
|
+
* operations an administrator asks about, and none an author needs. A key sees
|
|
40
|
+
* them only when an admin ticks them. Named rather than computed, so a module
|
|
41
|
+
* that grows a tool does not quietly put it in front of authors or admins.
|
|
42
|
+
*/
|
|
43
|
+
export declare const OPS_TOOL_NAMES: readonly string[];
|
|
44
|
+
//# sourceMappingURL=descriptors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptors.d.ts","sourceRoot":"","sources":["../../src/authoring/descriptors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAc5D,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyC0B,CAAA;AAEtD,eAAO,MAAM,0BAA0B,EAAE,SAAS,iBAAiB,EACnC,CAAA;AAEhC;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EA0B3C,CAAA"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The authoring surface: what an MCP client is offered for drafting and
|
|
3
|
+
publishing content. forumone-2026#830, throughline#302.
|
|
4
|
+
|
|
5
|
+
Eight tools in place of the forty-odd the modules each publish. They are shaped
|
|
6
|
+
by the job — find something, read it, draft it, check it, publish it — rather
|
|
7
|
+
than by which module implements each step, and each is a thin wrapper over the
|
|
8
|
+
module tools it replaces, so no behaviour moved. Those module tools still exist;
|
|
9
|
+
they are built into a collector `plugin-mcp` never sees, and these call their
|
|
10
|
+
handlers. See `surface.ts`.
|
|
11
|
+
*/
|
|
12
|
+
export const AUTHORING_TOOLS = {
|
|
13
|
+
find: {
|
|
14
|
+
name: 'find',
|
|
15
|
+
description: 'Finds content to work on or link to. With a query across every content type, or within one `collection`. With `kind` to find something to link or attach instead (a person, an image, a tag). With `mine` for your own unpublished drafts and scheduled publishes. Results carry the id `get`, `save_draft` and `publish` take.',
|
|
16
|
+
},
|
|
17
|
+
get: {
|
|
18
|
+
name: 'get',
|
|
19
|
+
description: 'Reads content. With `collection` and `id`: the document, in the shape `save_draft` takes, with its admin and preview links; `versions` adds its recent versions, for a rollback. With only `collection`: what you can write to that content type — every field, its limits, the blocks each blocks field accepts and what publishing it requires. With neither: the content types there are.',
|
|
20
|
+
},
|
|
21
|
+
saveDraft: {
|
|
22
|
+
name: 'save_draft',
|
|
23
|
+
description: 'Saves a draft. Without `id` it creates one: a new document from `data`, with a slug made from the title if none is given. With `id` it changes only the fields in `data` (a group is merged, an array or blocks field is replaced) and leaves the published version alone. Never publishes. Rich text takes { markdown } or { html }. Returns the id and a preview link.',
|
|
24
|
+
},
|
|
25
|
+
editBlocks: {
|
|
26
|
+
name: 'edit_blocks',
|
|
27
|
+
description: 'Changes the blocks in one blocks field of a draft — `layout`, or e.g. `approach.blocks` — without resending the rest: insert, update, move and remove, as a list applied in order and saved together. Every new or changed block is checked against what the field accepts, its own validation and the composition rules first; if any check fails, nothing is saved. Block ids come from `get`.',
|
|
28
|
+
},
|
|
29
|
+
check: {
|
|
30
|
+
name: 'check',
|
|
31
|
+
description: 'Whether a document would publish right now, without publishing it: every check `publish` makes, with all the blockers at once so they can be fixed together, and the preview link a person opens to see the draft (signed in). Run it before offering to publish.',
|
|
32
|
+
},
|
|
33
|
+
publish: {
|
|
34
|
+
name: 'publish',
|
|
35
|
+
description: 'Takes a document live, or changes what is live. `action`: "now" publishes the current draft; "schedule" publishes it at `at`; "unpublish" takes it down; "rollback" restores an earlier version (`versionId`, from `get` with `versions`) as the draft; "request_approval" asks approvers to sign it off. Publishing needs an editor or admin, and a document whose policy requires approval cannot go live until it is granted: "now" says so, and files the request itself if `approval` is given. Always confirm with the person first.',
|
|
36
|
+
},
|
|
37
|
+
designGuide: {
|
|
38
|
+
name: 'design_guide',
|
|
39
|
+
description: "The design system, for choosing and filling blocks. With `intent`: the components that suit what the person wants, ranked, with reasons. With `component`: that component's full contract — its fields, variants, composition rules, accessibility and anti-examples. With `recipes`: what a composed section may be built from. With none: every component, by category.",
|
|
40
|
+
},
|
|
41
|
+
composeSection: {
|
|
42
|
+
name: 'compose_section',
|
|
43
|
+
description: "For a section no component fits: checks a composed section's recipe (its contract and tree of primitives and inline components) against the design system, or with `save` saves it as a draft recipe. A person must approve a recipe before a page using it can publish; you cannot. Ask `design_guide` with `recipes` for the building blocks first.",
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
export const AUTHORING_TOOL_DESCRIPTORS = Object.values(AUTHORING_TOOLS);
|
|
47
|
+
/**
|
|
48
|
+
* The module tools that stay on the server, behind the admin-only check:
|
|
49
|
+
* operations an administrator asks about, and none an author needs. A key sees
|
|
50
|
+
* them only when an admin ticks them. Named rather than computed, so a module
|
|
51
|
+
* that grows a tool does not quietly put it in front of authors or admins.
|
|
52
|
+
*/
|
|
53
|
+
export const OPS_TOOL_NAMES = [
|
|
54
|
+
// audit
|
|
55
|
+
'query_audit',
|
|
56
|
+
'get_change_history',
|
|
57
|
+
'who_changed_what',
|
|
58
|
+
'what_changed_in_range',
|
|
59
|
+
'get_recent_failures',
|
|
60
|
+
// integrations
|
|
61
|
+
'list_integrations',
|
|
62
|
+
'get_integration_status',
|
|
63
|
+
'trigger_sync',
|
|
64
|
+
'test_integration',
|
|
65
|
+
'list_integration_types',
|
|
66
|
+
// observability
|
|
67
|
+
'list_job_failures',
|
|
68
|
+
// approvals, beyond requesting one
|
|
69
|
+
'respond_to_approval',
|
|
70
|
+
'get_approval_status',
|
|
71
|
+
'list_pending_approvals',
|
|
72
|
+
'list_my_requests',
|
|
73
|
+
// editorial overviews
|
|
74
|
+
'get_content_calendar',
|
|
75
|
+
'find_content_needing_attention',
|
|
76
|
+
// references
|
|
77
|
+
'find_references',
|
|
78
|
+
'can_delete',
|
|
79
|
+
];
|
|
80
|
+
//# sourceMappingURL=descriptors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptors.js","sourceRoot":"","sources":["../../src/authoring/descriptors.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;EAUE;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM;QACZ,WAAW,EACT,iUAAiU;KACpU;IACD,GAAG,EAAE;QACH,IAAI,EAAE,KAAK;QACX,WAAW,EACT,8XAA8X;KACjY;IACD,SAAS,EAAE;QACT,IAAI,EAAE,YAAY;QAClB,WAAW,EACT,0WAA0W;KAC7W;IACD,UAAU,EAAE;QACV,IAAI,EAAE,aAAa;QACnB,WAAW,EACT,kYAAkY;KACrY;IACD,KAAK,EAAE;QACL,IAAI,EAAE,OAAO;QACb,WAAW,EACT,mQAAmQ;KACtQ;IACD,OAAO,EAAE;QACP,IAAI,EAAE,SAAS;QACf,WAAW,EACT,4gBAA4gB;KAC/gB;IACD,WAAW,EAAE;QACX,IAAI,EAAE,cAAc;QACpB,WAAW,EACT,2WAA2W;KAC9W;IACD,cAAc,EAAE;QACd,IAAI,EAAE,iBAAiB;QACvB,WAAW,EACT,uVAAuV;KAC1V;CACmD,CAAA;AAEtD,MAAM,CAAC,MAAM,0BAA0B,GACrC,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAA;AAEhC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAsB;IAC/C,QAAQ;IACR,aAAa;IACb,oBAAoB;IACpB,kBAAkB;IAClB,uBAAuB;IACvB,qBAAqB;IACrB,eAAe;IACf,mBAAmB;IACnB,wBAAwB;IACxB,cAAc;IACd,kBAAkB;IAClB,wBAAwB;IACxB,gBAAgB;IAChB,mBAAmB;IACnB,mCAAmC;IACnC,qBAAqB;IACrB,qBAAqB;IACrB,wBAAwB;IACxB,kBAAkB;IAClB,sBAAsB;IACtB,sBAAsB;IACtB,gCAAgC;IAChC,aAAa;IACb,iBAAiB;IACjB,YAAY;CACb,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/** The shape `@payloadcms/plugin-mcp` takes in `mcp.prompts`. */
|
|
3
|
+
export interface PayloadMcpPrompt {
|
|
4
|
+
name: string;
|
|
5
|
+
title: string;
|
|
6
|
+
description: string;
|
|
7
|
+
argsSchema: z.ZodRawShape;
|
|
8
|
+
handler: (args: Record<string, unknown>) => {
|
|
9
|
+
messages: {
|
|
10
|
+
role: 'user' | 'assistant';
|
|
11
|
+
content: {
|
|
12
|
+
type: 'text';
|
|
13
|
+
text: string;
|
|
14
|
+
};
|
|
15
|
+
}[];
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export declare const AUTHORING_PROMPTS: PayloadMcpPrompt[];
|
|
19
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/authoring/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAYvB,iEAAiE;AACjE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,CAAC,CAAC,WAAW,CAAA;IACzB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK;QAC1C,QAAQ,EAAE;YAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;YAAC,OAAO,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,EAAE,CAAA;KACpF,CAAA;CACF;AAeD,eAAO,MAAM,iBAAiB,EAAE,gBAAgB,EA+D/C,CAAA"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
function say(text) {
|
|
3
|
+
return { messages: [{ role: 'user', content: { type: 'text', text } }] };
|
|
4
|
+
}
|
|
5
|
+
const str = (value) => typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined;
|
|
6
|
+
const ALWAYS = [
|
|
7
|
+
'Nothing goes live without my say-so: save drafts, run `check`, show me the preview link, and ask before calling `publish`.',
|
|
8
|
+
'Use only fields and blocks `get` lists for the content type, and fill each block from its contract in `design_guide`.',
|
|
9
|
+
'If `check` reports blockers, fix them all, then run it again.',
|
|
10
|
+
].join(' ');
|
|
11
|
+
export const AUTHORING_PROMPTS = [
|
|
12
|
+
{
|
|
13
|
+
name: 'draft_post',
|
|
14
|
+
title: 'Draft a post',
|
|
15
|
+
description: 'Write a new post (or another article-like content type) from a topic and notes, as a draft.',
|
|
16
|
+
argsSchema: {
|
|
17
|
+
topic: z.string().describe('What the post is about.'),
|
|
18
|
+
notes: z.string().optional().describe('Notes, an outline, or source text to work from.'),
|
|
19
|
+
collection: z.string().optional().describe('The content type, if not posts.'),
|
|
20
|
+
},
|
|
21
|
+
handler: (args) => say([
|
|
22
|
+
`Draft a ${str(args['collection']) ?? 'posts'} entry about: ${str(args['topic']) ?? '(ask me)'}.`,
|
|
23
|
+
str(args['notes'])
|
|
24
|
+
? `Work from these notes:\n\n${str(args['notes'])}`
|
|
25
|
+
: 'Ask me for notes or an outline if you need them.',
|
|
26
|
+
`Start with \`get\` for the content type, so you know its fields and what publishing it requires. Use \`find\` with a \`kind\` to attach authors, tags or an image rather than inventing them. Write the body as { markdown }. Save it with \`save_draft\`.`,
|
|
27
|
+
ALWAYS,
|
|
28
|
+
].join('\n\n')),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'build_landing_page',
|
|
32
|
+
title: 'Build a landing page',
|
|
33
|
+
description: 'Compose a landing page from design-system blocks for a goal and an audience, as a draft.',
|
|
34
|
+
argsSchema: {
|
|
35
|
+
goal: z.string().describe('What the page should get a reader to do.'),
|
|
36
|
+
audience: z.string().optional().describe('Who it is for.'),
|
|
37
|
+
material: z.string().optional().describe('Copy, facts or links to use.'),
|
|
38
|
+
},
|
|
39
|
+
handler: (args) => say([
|
|
40
|
+
`Build a landing page whose goal is: ${str(args['goal']) ?? '(ask me)'}.${str(args['audience']) ? ` It is for ${str(args['audience'])}.` : ''}`,
|
|
41
|
+
str(args['material']) ? `Use this material:\n\n${str(args['material'])}` : '',
|
|
42
|
+
"Plan the sections first and show me the outline. For each, ask `design_guide` with an `intent` and the blocks already chosen, and read the chosen component's contract before filling it. Only when no component fits a section, build one with `compose_section` — and tell me, because a person has to approve it before the page can publish.",
|
|
43
|
+
'Save the page with `save_draft`, then refine individual sections with `edit_blocks` rather than resending the layout.',
|
|
44
|
+
ALWAYS,
|
|
45
|
+
]
|
|
46
|
+
.filter(Boolean)
|
|
47
|
+
.join('\n\n')),
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'get_ready_to_publish',
|
|
51
|
+
title: 'Get it ready to publish',
|
|
52
|
+
description: 'Take an existing draft through every publishing check and, with your go-ahead, publish or request approval.',
|
|
53
|
+
argsSchema: {
|
|
54
|
+
what: z.string().describe('The document: its title, or a collection and id.'),
|
|
55
|
+
},
|
|
56
|
+
handler: (args) => say([
|
|
57
|
+
`Get this ready to publish: ${str(args['what']) ?? '(ask me which document)'}.`,
|
|
58
|
+
'Find it with `find`, then run `check`. Fix every blocker it reports — with `save_draft` for fields and `edit_blocks` for blocks — and run `check` again until it is clear.',
|
|
59
|
+
'Then show me the preview link and a short summary of what changed, and ask whether to publish now, schedule it, or request approval. If `publish` says approval is required, ask me who should approve it and what to tell them, and file the request.',
|
|
60
|
+
].join('\n\n')),
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/authoring/prompts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Plugin } from 'payload';
|
|
2
|
+
import type { McpToolCollector } from '../mcp/collector.js';
|
|
3
|
+
import type { McpToolContext, McpToolDefinition } from '../plugin-contract/index.js';
|
|
4
|
+
import { type AuthoringDeps } from './tools.js';
|
|
5
|
+
export interface SurfaceOptions extends Omit<AuthoringDeps, 'inner' | 'payload'> {
|
|
6
|
+
/** Where the module tools were built. */
|
|
7
|
+
inner: McpToolCollector;
|
|
8
|
+
/** What `plugin-mcp` serves. */
|
|
9
|
+
served: McpToolCollector;
|
|
10
|
+
/** Re-expose the operations tools, for admins. Default true. */
|
|
11
|
+
ops?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function isAdmin(ctx: McpToolContext): boolean;
|
|
14
|
+
/** A module tool, refused to anyone who is not an admin. */
|
|
15
|
+
export declare function adminOnly(tool: McpToolDefinition): McpToolDefinition;
|
|
16
|
+
export declare function surfacePlugin(options: SurfaceOptions): Plugin;
|
|
17
|
+
//# sourceMappingURL=surface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../src/authoring/surface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,MAAM,EAAE,MAAM,SAAS,CAAA;AAE7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAE3D,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AAEpF,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAA;AAkBrE,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,aAAa,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9E,yCAAyC;IACzC,KAAK,EAAE,gBAAgB,CAAA;IACvB,gCAAgC;IAChC,MAAM,EAAE,gBAAgB,CAAA;IACxB,gEAAgE;IAChE,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAEpD;AAED,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,IAAI,EAAE,iBAAiB,GAAG,iBAAiB,CAQpE;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CA8C7D"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { findAuditWriter } from '../audit/plugin.js';
|
|
2
|
+
import { deniedEnvelope } from '../mcp/envelope.js';
|
|
3
|
+
import { AUTHORING_TOOL_DESCRIPTORS, OPS_TOOL_NAMES } from './descriptors.js';
|
|
4
|
+
import { createAuthoringTools } from './tools.js';
|
|
5
|
+
export function isAdmin(ctx) {
|
|
6
|
+
return Boolean(ctx.user?.roles.includes('admin'));
|
|
7
|
+
}
|
|
8
|
+
/** A module tool, refused to anyone who is not an admin. */
|
|
9
|
+
export function adminOnly(tool) {
|
|
10
|
+
return {
|
|
11
|
+
...tool,
|
|
12
|
+
handler: (input, ctx) => isAdmin(ctx)
|
|
13
|
+
? tool.handler(input, ctx)
|
|
14
|
+
: Promise.resolve(deniedEnvelope(`${tool.name} is an administrator's tool.`)),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function surfacePlugin(options) {
|
|
18
|
+
return (incoming) => {
|
|
19
|
+
options.served.declare(AUTHORING_TOOL_DESCRIPTORS, { serverName: 'authoring' });
|
|
20
|
+
/*
|
|
21
|
+
Grouped by the module that declared each, so a `system.error` row still
|
|
22
|
+
names the module that threw. Only those that exist: a module that is off
|
|
23
|
+
declared nothing.
|
|
24
|
+
*/
|
|
25
|
+
const ops = new Map();
|
|
26
|
+
if (options.ops !== false) {
|
|
27
|
+
for (const tool of options.inner.declared) {
|
|
28
|
+
if (!OPS_TOOL_NAMES.includes(tool.name))
|
|
29
|
+
continue;
|
|
30
|
+
ops.set(tool.serverName, [...(ops.get(tool.serverName) ?? []), tool.name]);
|
|
31
|
+
options.served.declare([{ name: tool.name, description: `${tool.description} Admin only.` }], { serverName: tool.serverName });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
...incoming,
|
|
36
|
+
onInit: async (payload) => {
|
|
37
|
+
if (incoming.onInit)
|
|
38
|
+
await incoming.onInit(payload);
|
|
39
|
+
const audit = findAuditWriter(payload);
|
|
40
|
+
const withAudit = audit ? { audit } : {};
|
|
41
|
+
const { inner, served, ops: _ops, ...deps } = options;
|
|
42
|
+
served.add(createAuthoringTools({ ...deps, inner, payload }), {
|
|
43
|
+
serverName: 'authoring',
|
|
44
|
+
...withAudit,
|
|
45
|
+
});
|
|
46
|
+
for (const [serverName, names] of ops) {
|
|
47
|
+
served.add(names.map((name) => {
|
|
48
|
+
const tool = inner.definition(name);
|
|
49
|
+
if (!tool)
|
|
50
|
+
throw new Error(`${serverName} declared "${name}" and never built it.`);
|
|
51
|
+
return adminOnly(tool);
|
|
52
|
+
}), { serverName, ...withAudit });
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=surface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surface.js","sourceRoot":"","sources":["../../src/authoring/surface.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Payload } from 'payload';
|
|
2
|
+
import type { McpToolContext, McpToolDefinition } from '../plugin-contract/index.js';
|
|
3
|
+
import type { McpToolCollector } from '../mcp/collector.js';
|
|
4
|
+
export interface AuthoringDeps {
|
|
5
|
+
/** Where the module tools were built. Only `definition` is used. */
|
|
6
|
+
inner: Pick<McpToolCollector, 'definition'>;
|
|
7
|
+
payload: Payload;
|
|
8
|
+
/** The content types the content tools write, by slug, for the descriptions. */
|
|
9
|
+
contentTypes: readonly string[];
|
|
10
|
+
/** What `find` can look up with `kind`, e.g. people, media. */
|
|
11
|
+
kinds: readonly string[];
|
|
12
|
+
/** The approver groups a request may go to. Empty when approvals are off. */
|
|
13
|
+
approverGroups: readonly {
|
|
14
|
+
slug: string;
|
|
15
|
+
name: string;
|
|
16
|
+
}[];
|
|
17
|
+
/** Who may take something live. Default: an admin or an editor. */
|
|
18
|
+
canPublish?: (ctx: McpToolContext) => boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare function defaultCanPublish(ctx: McpToolContext): boolean;
|
|
21
|
+
type Result = Record<string, unknown>;
|
|
22
|
+
/** Calls a module tool's handler, parsing the input against its own schema. */
|
|
23
|
+
export declare function delegate(inner: AuthoringDeps['inner'], name: string, input: Record<string, unknown>, ctx: McpToolContext): Promise<Result>;
|
|
24
|
+
/** The eight, in the order a client lists them. */
|
|
25
|
+
export declare function createAuthoringTools(deps: AuthoringDeps): McpToolDefinition[];
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/authoring/tools.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAkB,OAAO,EAAE,MAAM,SAAS,CAAA;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AACpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAe3D,MAAM,WAAW,aAAa;IAC5B,oEAAoE;IACpE,KAAK,EAAE,IAAI,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAA;IAC3C,OAAO,EAAE,OAAO,CAAA;IAChB,gFAAgF;IAChF,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;IAC/B,+DAA+D;IAC/D,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,6EAA6E;IAC7E,cAAc,EAAE,SAAS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACzD,mEAAmE;IACnE,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAA;CAC9C;AAID,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAE9D;AAED,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAErC,+EAA+E;AAC/E,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,EAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,MAAM,CAAC,CAMjB;AA2aD,mDAAmD;AACnD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,GAAG,iBAAiB,EAAE,CAW7E"}
|