@forumone/throughline 1.2.0 → 1.3.0-pr-285-01cb291

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @forumone/throughline
2
2
 
3
+ ## 1.3.0-pr-285-01cb291
4
+
5
+ ### Minor Changes
6
+
7
+ - f920034: Adds `@forumone/throughline/content` and a `content:` option on `throughline()`, with the MCP tools an agent uses to read before it writes a draft: `describe_content_type`, `find_documents`, `find_related` and `get_document`. Each call runs as the person behind the API key. Field types, limits and a blocks field's allowed blocks come from the live config. Draft-writing tools follow in a later release.
8
+
9
+ **Upgrading: a migration.** The audit log's `mcpServer` gains `content`, a new value on `enum_audit_events_mcp_server`. A site that turns the tools on also gets four new checkbox columns on `payload-mcp-api-keys`. Run `migrate:create` and apply it before deploying.
10
+
11
+ ### Patch Changes
12
+
13
+ - @forumone/throughline-design-system@1.3.0-pr-285-01cb291
14
+
3
15
  ## 1.2.0
4
16
 
5
17
  ### Minor Changes
@@ -11,6 +11,6 @@ export type AuditAction = (typeof AUDIT_ACTIONS)[number];
11
11
  * The last four arrived in 1.1, so that a throw in those tools is recorded as a
12
12
  * `system.error` like everyone else's.
13
13
  */
14
- export declare const AUDIT_MCP_SERVERS: readonly ["payload", "component", "publishing", "approvals", "audit", "forms", "integrations", "editorial", "references", "fields", "observability"];
14
+ export declare const AUDIT_MCP_SERVERS: readonly ["payload", "component", "publishing", "approvals", "audit", "forms", "integrations", "editorial", "references", "fields", "observability", "content"];
15
15
  export type AuditMcpServer = (typeof AUDIT_MCP_SERVERS)[number];
16
16
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/audit/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,aAAa,6jBA4BhB,CAAA;AAEV,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAExD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,sJAYpB,CAAA;AAEV,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/audit/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,aAAa,6jBA4BhB,CAAA;AAEV,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAExD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,iKAapB,CAAA;AAEV,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAA"}
@@ -50,5 +50,6 @@ export const AUDIT_MCP_SERVERS = [
50
50
  'references',
51
51
  'fields',
52
52
  'observability',
53
+ 'content',
53
54
  ];
54
55
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/audit/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,cAAc;IACd,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,aAAa;IACb,gBAAgB;IAChB,iBAAiB;IACjB,qBAAqB;IACrB,0BAA0B;IAC1B,kBAAkB;IAClB,oBAAoB;IACpB,sBAAsB;IACtB,qBAAqB;IACrB,qBAAqB;IACrB,oBAAoB;IACpB,kBAAkB;IAClB,mBAAmB;IACnB,4BAA4B;IAC5B,kBAAkB;IAClB,oBAAoB;IACpB,cAAc;IACd,cAAc;IACd,0BAA0B;IAC1B,oBAAoB;IACpB,oBAAoB;IACpB,cAAc;IACd,oBAAoB;CACZ,CAAA;AAIV;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,SAAS;IACT,WAAW;IACX,YAAY;IACZ,WAAW;IACX,OAAO;IACP,OAAO;IACP,cAAc;IACd,WAAW;IACX,YAAY;IACZ,QAAQ;IACR,eAAe;CACP,CAAA"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/audit/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,cAAc;IACd,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,aAAa;IACb,gBAAgB;IAChB,iBAAiB;IACjB,qBAAqB;IACrB,0BAA0B;IAC1B,kBAAkB;IAClB,oBAAoB;IACpB,sBAAsB;IACtB,qBAAqB;IACrB,qBAAqB;IACrB,oBAAoB;IACpB,kBAAkB;IAClB,mBAAmB;IACnB,4BAA4B;IAC5B,kBAAkB;IAClB,oBAAoB;IACpB,cAAc;IACd,cAAc;IACd,0BAA0B;IAC1B,oBAAoB;IACpB,oBAAoB;IACpB,cAAc;IACd,oBAAoB;CACZ,CAAA;AAIV;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,SAAS;IACT,WAAW;IACX,YAAY;IACZ,WAAW;IACX,OAAO;IACP,OAAO;IACP,cAAc;IACd,WAAW;IACX,YAAY;IACZ,QAAQ;IACR,eAAe;IACf,SAAS;CACD,CAAA"}
@@ -0,0 +1,61 @@
1
+ import type { Block, Field, PayloadRequest } from 'payload';
2
+ /** One field an author may set. */
3
+ export interface FieldDescription {
4
+ name: string;
5
+ type: string;
6
+ label?: string;
7
+ description?: string;
8
+ required?: true;
9
+ hasMany?: true;
10
+ minLength?: number;
11
+ maxLength?: number;
12
+ min?: number;
13
+ max?: number;
14
+ minRows?: number;
15
+ maxRows?: number;
16
+ /** A select's or radio's values. */
17
+ options?: string[];
18
+ /** A relationship's or upload's target collection(s). */
19
+ relationTo?: string | string[];
20
+ /** How to find a value for it: the `find_related` kind that searches its target. */
21
+ findWith?: string;
22
+ /**
23
+ * The exact matches the field itself requires of what it links to, ready to
24
+ * pass as `find_related`'s `where` — a topics field that takes only terms of
25
+ * type `topic`, say. From the field's own `filterOptions`.
26
+ */
27
+ findWhere?: Record<string, string | number | boolean>;
28
+ /** The field's `filterOptions` when it is more than exact matches, as a Payload `where`. */
29
+ filter?: unknown;
30
+ /** A group's or array row's fields. */
31
+ fields?: FieldDescription[];
32
+ /** A blocks field's blocks: what may be added. */
33
+ blocks?: BlockDescription[];
34
+ }
35
+ export interface BlockDescription {
36
+ slug: string;
37
+ label?: string;
38
+ /** The picker shelf it sits on. */
39
+ group?: string;
40
+ }
41
+ export interface DescribeContext {
42
+ /** The config's block registry, for a blocks field that refers to blocks by slug. */
43
+ blocks: ReadonlyMap<string, Block>;
44
+ /** For a `filterOptions` that reads the request. */
45
+ req: PayloadRequest;
46
+ /** Collection slug → the `find_related` kind that searches it. */
47
+ findWith: ReadonlyMap<string, string>;
48
+ }
49
+ /**
50
+ * The named fields at the top of a document, looking through unnamed tabs,
51
+ * collapsibles and rows: the names a document is stored under.
52
+ */
53
+ export declare function topLevelFields(fields: readonly Field[]): Field[];
54
+ /** The fields a collection lets an author set, in the order the admin shows them. */
55
+ export declare function writableFields(fields: readonly Field[], names: readonly string[]): Field[];
56
+ /** The names in `names` that are not top-level fields of `fields`. */
57
+ export declare function unknownFieldNames(fields: readonly Field[], names: readonly string[]): string[];
58
+ /** One field, and what is inside it. */
59
+ export declare function describeField(field: Field, ctx: DescribeContext): Promise<FieldDescription | undefined>;
60
+ export declare function describeFields(fields: readonly Field[], ctx: DescribeContext): Promise<FieldDescription[]>;
61
+ //# sourceMappingURL=describe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"describe.d.ts","sourceRoot":"","sources":["../../src/content/describe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAY3D,mCAAmC;AACnC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,IAAI,CAAA;IACf,OAAO,CAAC,EAAE,IAAI,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC9B,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAA;IACrD,4FAA4F;IAC5F,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,uCAAuC;IACvC,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC3B,kDAAkD;IAClD,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAA;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,qFAAqF;IACrF,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAClC,oDAAoD;IACpD,GAAG,EAAE,cAAc,CAAA;IACnB,kEAAkE;IAClE,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACtC;AAOD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,KAAK,EAAE,CAkBhE;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,EAAE,CAK1F;AAED,sEAAsE;AACtE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAK9F;AAgHD,wCAAwC;AACxC,wBAAsB,aAAa,CACjC,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,eAAe,GACnB,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CA0CvC;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,KAAK,EAAE,EACxB,GAAG,EAAE,eAAe,GACnB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAO7B"}
@@ -0,0 +1,201 @@
1
+ /** A field that stores nothing of its own and only arranges others. */
2
+ function isWrapper(field) {
3
+ return !('name' in field) || typeof field.name !== 'string' || field.name === '';
4
+ }
5
+ /**
6
+ * The named fields at the top of a document, looking through unnamed tabs,
7
+ * collapsibles and rows: the names a document is stored under.
8
+ */
9
+ export function topLevelFields(fields) {
10
+ const out = [];
11
+ for (const field of fields) {
12
+ if (field.type === 'tabs') {
13
+ for (const tab of field.tabs) {
14
+ if ('name' in tab && typeof tab.name === 'string' && tab.name !== '') {
15
+ out.push({ ...tab, type: 'group' });
16
+ }
17
+ else {
18
+ out.push(...topLevelFields(tab.fields));
19
+ }
20
+ }
21
+ }
22
+ else if (isWrapper(field) && 'fields' in field && Array.isArray(field.fields)) {
23
+ out.push(...topLevelFields(field.fields));
24
+ }
25
+ else {
26
+ out.push(field);
27
+ }
28
+ }
29
+ return out;
30
+ }
31
+ /** The fields a collection lets an author set, in the order the admin shows them. */
32
+ export function writableFields(fields, names) {
33
+ const wanted = new Set(names);
34
+ return topLevelFields(fields).filter((field) => 'name' in field && wanted.has(field.name));
35
+ }
36
+ /** The names in `names` that are not top-level fields of `fields`. */
37
+ export function unknownFieldNames(fields, names) {
38
+ const known = new Set(topLevelFields(fields).map((field) => ('name' in field ? field.name : '')));
39
+ return names.filter((name) => !known.has(name));
40
+ }
41
+ function text(value) {
42
+ if (typeof value === 'string' && value.trim() !== '')
43
+ return value;
44
+ if (value && typeof value === 'object' && typeof value.en === 'string') {
45
+ return value.en;
46
+ }
47
+ return undefined;
48
+ }
49
+ /** Whether an author can set this field at all: it stores something and is not hidden or read-only. */
50
+ function isAuthored(field) {
51
+ if (field.type === 'ui' || field.type === 'join')
52
+ return false;
53
+ if ('hidden' in field && field.hidden)
54
+ return false;
55
+ const admin = field
56
+ .admin;
57
+ if (admin?.hidden || admin?.readOnly || admin?.disabled)
58
+ return false;
59
+ if ('virtual' in field && field.virtual)
60
+ return false;
61
+ return true;
62
+ }
63
+ function optionValues(options) {
64
+ return options.map((option) => typeof option === 'string' ? option : String(option.value));
65
+ }
66
+ async function blocksOf(field, ctx) {
67
+ const all = [
68
+ ...(field.blocks ?? []),
69
+ ...(field.blockReferences ?? []).flatMap((ref) => {
70
+ const block = typeof ref === 'string' ? ctx.blocks.get(ref) : ref;
71
+ return block ? [block] : [];
72
+ }),
73
+ ];
74
+ /*
75
+ The picker's own question, asked for a document that holds nothing yet. A
76
+ `filterOptions` that keeps whatever a document already holds (so an existing
77
+ one stays saveable) adds nothing here, which is the point: this is what may be
78
+ *added*.
79
+ */
80
+ let allowed;
81
+ if (typeof field.filterOptions === 'function') {
82
+ const answer = await field.filterOptions({
83
+ data: {},
84
+ siblingData: {},
85
+ id: undefined,
86
+ req: ctx.req,
87
+ user: ctx.req.user,
88
+ });
89
+ if (Array.isArray(answer))
90
+ allowed = new Set(answer);
91
+ }
92
+ else if (Array.isArray(field.filterOptions)) {
93
+ allowed = new Set(field.filterOptions);
94
+ }
95
+ return all
96
+ .filter((block) => !allowed || allowed.has(block.slug))
97
+ .map((block) => {
98
+ const label = text(block.labels?.singular);
99
+ const group = text(block.admin?.group);
100
+ return { slug: block.slug, ...(label ? { label } : {}), ...(group ? { group } : {}) };
101
+ });
102
+ }
103
+ /*
104
+ A relationship's `filterOptions` is what narrows the admin's picker — a topics
105
+ field offering only terms of type `topic`. Asked the way the picker asks it, for
106
+ a document that holds nothing yet. `true` means no narrowing, and so does an
107
+ answer per collection for a field that links to several, which is rare enough
108
+ to leave to the agent and the save-time validation.
109
+ */
110
+ async function relationFilter(filterOptions, relationTo, ctx) {
111
+ if (typeof relationTo !== 'string' || filterOptions === undefined)
112
+ return undefined;
113
+ const answer = typeof filterOptions === 'function'
114
+ ? await filterOptions({
115
+ data: {},
116
+ siblingData: {},
117
+ blockData: undefined,
118
+ id: undefined,
119
+ relationTo,
120
+ req: ctx.req,
121
+ user: ctx.req.user,
122
+ })
123
+ : filterOptions;
124
+ return answer && typeof answer === 'object' && Object.keys(answer).length > 0
125
+ ? answer
126
+ : undefined;
127
+ }
128
+ /** `{ type: { equals: 'topic' } }` as `{ type: 'topic' }`; `undefined` for anything more. */
129
+ function exactMatches(where) {
130
+ const out = {};
131
+ for (const [field, condition] of Object.entries(where)) {
132
+ if (field === 'and' || field === 'or' || !condition || typeof condition !== 'object')
133
+ return undefined;
134
+ const keys = Object.keys(condition);
135
+ const value = condition.equals;
136
+ if (keys.length !== 1 || keys[0] !== 'equals')
137
+ return undefined;
138
+ if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean')
139
+ return undefined;
140
+ out[field] = value;
141
+ }
142
+ return out;
143
+ }
144
+ /** One field, and what is inside it. */
145
+ export async function describeField(field, ctx) {
146
+ if (!('name' in field) || !isAuthored(field))
147
+ return undefined;
148
+ const f = field;
149
+ const admin = f['admin'];
150
+ const out = { name: field.name, type: field.type };
151
+ const label = text(f['label']);
152
+ if (label)
153
+ out.label = label;
154
+ const description = text(admin?.description);
155
+ if (description)
156
+ out.description = description;
157
+ if (f['required'] === true)
158
+ out.required = true;
159
+ if (f['hasMany'] === true)
160
+ out.hasMany = true;
161
+ for (const key of ['minLength', 'maxLength', 'min', 'max', 'minRows', 'maxRows']) {
162
+ if (typeof f[key] === 'number')
163
+ out[key] = f[key];
164
+ }
165
+ if ((field.type === 'select' || field.type === 'radio') && Array.isArray(f['options'])) {
166
+ out.options = optionValues(f['options']);
167
+ }
168
+ if (field.type === 'relationship' || field.type === 'upload') {
169
+ const relationTo = f['relationTo'];
170
+ out.relationTo = relationTo;
171
+ const kind = typeof relationTo === 'string' ? ctx.findWith.get(relationTo) : undefined;
172
+ if (kind)
173
+ out.findWith = kind;
174
+ const where = await relationFilter(f['filterOptions'], relationTo, ctx);
175
+ if (where) {
176
+ const exact = exactMatches(where);
177
+ if (exact)
178
+ out.findWhere = exact;
179
+ else
180
+ out.filter = where;
181
+ }
182
+ }
183
+ if (field.type === 'group' || field.type === 'array') {
184
+ const children = await describeFields(topLevelFields(f['fields'] ?? []), ctx);
185
+ out.fields = children;
186
+ }
187
+ if (field.type === 'blocks') {
188
+ out.blocks = await blocksOf(field, ctx);
189
+ }
190
+ return out;
191
+ }
192
+ export async function describeFields(fields, ctx) {
193
+ const out = [];
194
+ for (const field of fields) {
195
+ const described = await describeField(field, ctx);
196
+ if (described)
197
+ out.push(described);
198
+ }
199
+ return out;
200
+ }
201
+ //# sourceMappingURL=describe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"describe.js","sourceRoot":"","sources":["../../src/content/describe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export { contentPlugin } from './plugin.js';
2
+ export type { ContentCollectionOptions, ContentPluginOptions, RelatedKindOptions, } from './options.js';
3
+ export { describeField, describeFields, topLevelFields, unknownFieldNames, writableFields, } from './describe.js';
4
+ export type { BlockDescription, DescribeContext, FieldDescription } from './describe.js';
5
+ export { CONTENT_READ_TOOLS, CONTENT_READ_TOOL_DESCRIPTORS, createContentReadTools, createDescribeContentTypeTool, createFindDocumentsTool, createFindRelatedTool, createGetDocumentTool, toWriteShape, } from './tools.js';
6
+ export type { ContentToolDeps } from './tools.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/content/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,YAAY,EACV,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,aAAa,EACb,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,cAAc,GACf,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACxF,OAAO,EACL,kBAAkB,EAClB,6BAA6B,EAC7B,sBAAsB,EACtB,6BAA6B,EAC7B,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,GACb,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,4 @@
1
+ export { contentPlugin } from './plugin.js';
2
+ export { describeField, describeFields, topLevelFields, unknownFieldNames, writableFields, } from './describe.js';
3
+ export { CONTENT_READ_TOOLS, CONTENT_READ_TOOL_DESCRIPTORS, createContentReadTools, createDescribeContentTypeTool, createFindDocumentsTool, createFindRelatedTool, createGetDocumentTool, toWriteShape, } from './tools.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/content/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAM3C,OAAO,EACL,aAAa,EACb,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,cAAc,GACf,MAAM,eAAe,CAAA;AAEtB,OAAO,EACL,kBAAkB,EAClB,6BAA6B,EAC7B,sBAAsB,EACtB,6BAA6B,EAC7B,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,GACb,MAAM,YAAY,CAAA"}
@@ -0,0 +1,52 @@
1
+ import type { McpToolContext } from '../plugin-contract/index.js';
2
+ import type { McpToolCollector } from '../mcp/collector.js';
3
+ export interface ContentCollectionOptions {
4
+ /**
5
+ * The top-level fields an author may set, by name: what an agent is told
6
+ * about and, from phase 2, what a write may contain. A group or an array is
7
+ * named whole. Fields inside tabs and collapsibles are named directly, as
8
+ * they are stored.
9
+ *
10
+ * An allow-list rather than a deny-list: a field added to the collection
11
+ * later is not writable over MCP until somebody decides it should be.
12
+ */
13
+ fields: readonly string[];
14
+ /**
15
+ * Blocks the collection's template already draws from the document's own
16
+ * fields, so an agent does not repeat them in `layout`. By slug. A blocks
17
+ * field's `filterOptions` already keeps the ones that may appear only once
18
+ * out of the picker; this says why, and covers those that may repeat.
19
+ */
20
+ templateDraws?: readonly string[];
21
+ /** What publishing this collection requires beyond its fields, in the words an agent reads. */
22
+ publishRequires?: readonly string[];
23
+ }
24
+ export interface RelatedKindOptions {
25
+ /** The collection a kind searches. */
26
+ collection: string;
27
+ /** Fields a lookup may also match on exactly, e.g. a taxonomy's `type`. */
28
+ filters?: readonly string[];
29
+ /**
30
+ * Fields the query is matched against, OR'd. Default: the collection's
31
+ * `useAsTitle`. For media, whose title is usually a filename, add `alt`.
32
+ */
33
+ searchFields?: readonly string[];
34
+ /** What the kind is, for the tool's description. */
35
+ description?: string;
36
+ }
37
+ export interface ContentPluginOptions {
38
+ enabled?: boolean;
39
+ /** The collections the tools read and write, by slug. */
40
+ collections: Readonly<Record<string, ContentCollectionOptions>>;
41
+ /**
42
+ * What `find_related` looks up, by the name an agent passes as `kind`:
43
+ * `{ people: { collection: 'people' }, media: { collection: 'media' } }`.
44
+ * An upload collection's results carry the file's alt text and dimensions.
45
+ */
46
+ related?: Readonly<Record<string, RelatedKindOptions>>;
47
+ /** Where the tools go. Omit for none. Must come before `mcpPlugin`. */
48
+ mcpTools?: McpToolCollector;
49
+ /** Who may call them. Default: a user with the `admin` or `editor` role. */
50
+ canUse?: (ctx: McpToolContext) => boolean;
51
+ }
52
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/content/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAa3D,MAAM,WAAW,wBAAwB;IACvC;;;;;;;;OAQG;IACH,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IACzB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,+FAA+F;IAC/F,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAChC,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,yDAAyD;IACzD,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAA;IAC/D;;;;OAIG;IACH,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAA;IACtD,uEAAuE;IACvE,QAAQ,CAAC,EAAE,gBAAgB,CAAA;IAC3B,4EAA4E;IAC5E,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAA;CAC1C"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/content/options.ts"],"names":[],"mappings":""}
@@ -0,0 +1,23 @@
1
+ import type { Plugin } from 'payload';
2
+ import type { ContentPluginOptions } from './options.js';
3
+ /**
4
+ * The content tools: what an agent reads before it writes a draft, and — in
5
+ * the phases that follow — the drafts themselves. forumone-2026#803.
6
+ *
7
+ * Checks its options against the config as it loads, so a misspelt field or a
8
+ * collection that was renamed stops the boot rather than quietly leaving an
9
+ * agent unable to set something:
10
+ *
11
+ * ```ts
12
+ * contentPlugin({
13
+ * collections: {
14
+ * pages: { fields: ['title', 'slug', 'layout', 'meta'] },
15
+ * posts: { fields: ['title', 'slug', 'intro', 'body', 'authors'], templateDraws: ['ArticleBody'] },
16
+ * },
17
+ * related: { people: { collection: 'people' }, media: { collection: 'media' } },
18
+ * mcpTools,
19
+ * })
20
+ * ```
21
+ */
22
+ export declare function contentPlugin(options: ContentPluginOptions): Plugin;
23
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/content/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,MAAM,EAAE,MAAM,SAAS,CAAA;AAG7C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAGxD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,CAiDnE"}
@@ -0,0 +1,68 @@
1
+ import { findAuditWriter } from '../audit/plugin.js';
2
+ import { unknownFieldNames } from './describe.js';
3
+ import { CONTENT_READ_TOOL_DESCRIPTORS, createContentReadTools } from './tools.js';
4
+ /**
5
+ * The content tools: what an agent reads before it writes a draft, and — in
6
+ * the phases that follow — the drafts themselves. forumone-2026#803.
7
+ *
8
+ * Checks its options against the config as it loads, so a misspelt field or a
9
+ * collection that was renamed stops the boot rather than quietly leaving an
10
+ * agent unable to set something:
11
+ *
12
+ * ```ts
13
+ * contentPlugin({
14
+ * collections: {
15
+ * pages: { fields: ['title', 'slug', 'layout', 'meta'] },
16
+ * posts: { fields: ['title', 'slug', 'intro', 'body', 'authors'], templateDraws: ['ArticleBody'] },
17
+ * },
18
+ * related: { people: { collection: 'people' }, media: { collection: 'media' } },
19
+ * mcpTools,
20
+ * })
21
+ * ```
22
+ */
23
+ export function contentPlugin(options) {
24
+ return (incoming) => {
25
+ if (options.enabled === false)
26
+ return incoming;
27
+ const bySlug = new Map((incoming.collections ?? []).map((c) => [c.slug, c]));
28
+ const problems = [];
29
+ for (const [slug, entry] of Object.entries(options.collections)) {
30
+ const collection = bySlug.get(slug);
31
+ if (!collection) {
32
+ problems.push(`no collection "${slug}"`);
33
+ continue;
34
+ }
35
+ const unknown = unknownFieldNames(collection.fields, entry.fields);
36
+ if (unknown.length > 0) {
37
+ problems.push(`"${slug}" has no top-level field ${unknown.map((n) => `"${n}"`).join(', ')}`);
38
+ }
39
+ }
40
+ for (const [kind, entry] of Object.entries(options.related ?? {})) {
41
+ if (!bySlug.has(entry.collection)) {
42
+ problems.push(`related kind "${kind}" names no collection "${entry.collection}"`);
43
+ }
44
+ }
45
+ if (problems.length > 0) {
46
+ throw new Error(`contentPlugin: ${problems.join('; ')}. Register it after the plugins that add those collections, and name fields as they are stored.`);
47
+ }
48
+ // Declared at config time so `mcpPlugin` makes a checkbox per tool; bound at `onInit`.
49
+ options.mcpTools?.declare(CONTENT_READ_TOOL_DESCRIPTORS, { serverName: 'content' });
50
+ return {
51
+ ...incoming,
52
+ onInit: async (payload) => {
53
+ if (incoming.onInit)
54
+ await incoming.onInit(payload);
55
+ if (!options.mcpTools)
56
+ return;
57
+ const audit = findAuditWriter(payload);
58
+ options.mcpTools.add(createContentReadTools({
59
+ payload,
60
+ collections: options.collections,
61
+ related: options.related ?? {},
62
+ ...(options.canUse ? { canUse: options.canUse } : {}),
63
+ }), { serverName: 'content', ...(audit ? { audit } : {}) });
64
+ },
65
+ };
66
+ };
67
+ }
68
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/content/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ import { z } from 'zod';
2
+ import { type Payload } from 'payload';
3
+ import type { McpToolContext, McpToolDefinition } from '../plugin-contract/index.js';
4
+ import type { McpToolDescriptor } from '../mcp/collector.js';
5
+ import type { ContentCollectionOptions, RelatedKindOptions } from './options.js';
6
+ export declare const CONTENT_READ_TOOLS: {
7
+ readonly describeContentType: {
8
+ readonly name: "describe_content_type";
9
+ readonly description: "What a content type is made of, before creating or editing one: every field an author may set, with its type, limits, whether it is required, a select's options, and for a link to another document the `find_related` kind that finds one. A blocks field lists the blocks it accepts (look each up with `get_contract`). Also what the page template already draws, so the layout does not repeat it, and what publishing requires. Read from the live configuration.";
10
+ };
11
+ readonly findDocuments: {
12
+ readonly name: "find_documents";
13
+ readonly description: "Finds documents of one content type by title or slug, drafts included, newest first. Each result has its id, title, slug, status (draft or published, of the latest version), when it was last changed, and its admin and preview links. Use before creating something, so an existing document is edited rather than duplicated.";
14
+ };
15
+ readonly findRelated: {
16
+ readonly name: "find_related";
17
+ readonly description: "Looks up something a document links to — an author, a client, a taxonomy term, an image — by a few letters of its name, to get the id a field needs. Images come back with their alt text and dimensions. Which kinds exist is in each field's `findWith` from `describe_content_type`. Read only.";
18
+ };
19
+ readonly getDocument: {
20
+ readonly name: "get_document";
21
+ readonly description: "Reads one document in the shape the write tools accept: the fields an author may set, relationships as ids, and blocks with their ids. The latest draft by default; pass draft: false for the published version. Also its status and its admin and preview links.";
22
+ };
23
+ };
24
+ export declare const CONTENT_READ_TOOL_DESCRIPTORS: readonly McpToolDescriptor[];
25
+ export interface ContentToolDeps {
26
+ payload: Payload;
27
+ collections: Readonly<Record<string, ContentCollectionOptions>>;
28
+ related: Readonly<Record<string, RelatedKindOptions>>;
29
+ canUse?: (ctx: McpToolContext) => boolean;
30
+ }
31
+ declare const describeInput: z.ZodObject<{
32
+ collection: z.ZodString;
33
+ }, "strip", z.ZodTypeAny, {
34
+ collection: string;
35
+ }, {
36
+ collection: string;
37
+ }>;
38
+ export declare function createDescribeContentTypeTool(deps: ContentToolDeps): McpToolDefinition<typeof describeInput>;
39
+ declare const findInput: z.ZodObject<{
40
+ collection: z.ZodString;
41
+ query: z.ZodOptional<z.ZodString>;
42
+ limit: z.ZodOptional<z.ZodNumber>;
43
+ }, "strip", z.ZodTypeAny, {
44
+ collection: string;
45
+ limit?: number | undefined;
46
+ query?: string | undefined;
47
+ }, {
48
+ collection: string;
49
+ limit?: number | undefined;
50
+ query?: string | undefined;
51
+ }>;
52
+ export declare function createFindDocumentsTool(deps: ContentToolDeps): McpToolDefinition<typeof findInput>;
53
+ declare const relatedInput: z.ZodObject<{
54
+ kind: z.ZodString;
55
+ query: z.ZodOptional<z.ZodString>;
56
+ where: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
57
+ limit: z.ZodOptional<z.ZodNumber>;
58
+ }, "strip", z.ZodTypeAny, {
59
+ kind: string;
60
+ where?: Record<string, string | number | boolean> | undefined;
61
+ limit?: number | undefined;
62
+ query?: string | undefined;
63
+ }, {
64
+ kind: string;
65
+ where?: Record<string, string | number | boolean> | undefined;
66
+ limit?: number | undefined;
67
+ query?: string | undefined;
68
+ }>;
69
+ export declare function createFindRelatedTool(deps: ContentToolDeps): McpToolDefinition<typeof relatedInput>;
70
+ declare const getInput: z.ZodObject<{
71
+ collection: z.ZodString;
72
+ id: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
73
+ draft: z.ZodOptional<z.ZodBoolean>;
74
+ }, "strip", z.ZodTypeAny, {
75
+ collection: string;
76
+ id: string | number;
77
+ draft?: boolean | undefined;
78
+ }, {
79
+ collection: string;
80
+ id: string | number;
81
+ draft?: boolean | undefined;
82
+ }>;
83
+ /** The authored fields of a document, as the write tools take them. */
84
+ export declare function toWriteShape(doc: Record<string, unknown>, fieldNames: readonly string[]): Record<string, unknown>;
85
+ export declare function createGetDocumentTool(deps: ContentToolDeps): McpToolDefinition<typeof getInput>;
86
+ export declare function createContentReadTools(deps: ContentToolDeps): McpToolDefinition[];
87
+ export {};
88
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/content/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAIL,KAAK,OAAO,EAGb,MAAM,SAAS,CAAA;AAChB,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AACpF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAI5D,OAAO,KAAK,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAWhF,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;CAqBuB,CAAA;AAEtD,eAAO,MAAM,6BAA6B,EAAE,SAAS,iBAAiB,EACnC,CAAA;AAEnC,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAA;IAC/D,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAA;IACrD,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAA;CAC1C;AAoFD,QAAA,MAAM,aAAa;;;;;;EAEjB,CAAA;AAEF,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,eAAe,GACpB,iBAAiB,CAAC,OAAO,aAAa,CAAC,CAsCzC;AAID,QAAA,MAAM,SAAS;;;;;;;;;;;;EAOb,CAAA;AAEF,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,eAAe,GACpB,iBAAiB,CAAC,OAAO,SAAS,CAAC,CA6DrC;AAID,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;EAQhB,CAAA;AAWF,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,eAAe,GACpB,iBAAiB,CAAC,OAAO,YAAY,CAAC,CAgExC;AAID,QAAA,MAAM,QAAQ;;;;;;;;;;;;EAOZ,CAAA;AAEF,uEAAuE;AACvE,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,UAAU,EAAE,SAAS,MAAM,EAAE,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,eAAe,GACpB,iBAAiB,CAAC,OAAO,QAAQ,CAAC,CA0CpC;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,eAAe,GAAG,iBAAiB,EAAE,CAOjF"}