payload-mcp-toolkit 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.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +133 -0
  3. package/dist/__tests__/introspection.test.js +364 -0
  4. package/dist/__tests__/introspection.test.js.map +1 -0
  5. package/dist/__tests__/url-validator.test.js +326 -0
  6. package/dist/__tests__/url-validator.test.js.map +1 -0
  7. package/dist/draft-workflow.d.ts +60 -0
  8. package/dist/draft-workflow.js +93 -0
  9. package/dist/draft-workflow.js.map +1 -0
  10. package/dist/index.d.ts +24 -0
  11. package/dist/index.js +142 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/introspection.d.ts +23 -0
  14. package/dist/introspection.js +238 -0
  15. package/dist/introspection.js.map +1 -0
  16. package/dist/prompts.d.ts +21 -0
  17. package/dist/prompts.js +215 -0
  18. package/dist/prompts.js.map +1 -0
  19. package/dist/rate-limiter.d.ts +25 -0
  20. package/dist/rate-limiter.js +51 -0
  21. package/dist/rate-limiter.js.map +1 -0
  22. package/dist/resources.d.ts +18 -0
  23. package/dist/resources.js +77 -0
  24. package/dist/resources.js.map +1 -0
  25. package/dist/tools/compose-helpers.d.ts +117 -0
  26. package/dist/tools/compose-helpers.js +236 -0
  27. package/dist/tools/compose-helpers.js.map +1 -0
  28. package/dist/tools/compose-layout.d.ts +139 -0
  29. package/dist/tools/compose-layout.js +61 -0
  30. package/dist/tools/compose-layout.js.map +1 -0
  31. package/dist/tools/patch-layout.d.ts +107 -0
  32. package/dist/tools/patch-layout.js +123 -0
  33. package/dist/tools/patch-layout.js.map +1 -0
  34. package/dist/tools/publish-draft.d.ts +24 -0
  35. package/dist/tools/publish-draft.js +69 -0
  36. package/dist/tools/publish-draft.js.map +1 -0
  37. package/dist/tools/resolve-reference.d.ts +31 -0
  38. package/dist/tools/resolve-reference.js +169 -0
  39. package/dist/tools/resolve-reference.js.map +1 -0
  40. package/dist/tools/safe-delete.d.ts +37 -0
  41. package/dist/tools/safe-delete.js +161 -0
  42. package/dist/tools/safe-delete.js.map +1 -0
  43. package/dist/tools/schedule-publish.d.ts +49 -0
  44. package/dist/tools/schedule-publish.js +120 -0
  45. package/dist/tools/schedule-publish.js.map +1 -0
  46. package/dist/tools/search-content.d.ts +43 -0
  47. package/dist/tools/search-content.js +210 -0
  48. package/dist/tools/search-content.js.map +1 -0
  49. package/dist/tools/update-document.d.ts +32 -0
  50. package/dist/tools/update-document.js +114 -0
  51. package/dist/tools/update-document.js.map +1 -0
  52. package/dist/tools/upload-media.d.ts +26 -0
  53. package/dist/tools/upload-media.js +115 -0
  54. package/dist/tools/upload-media.js.map +1 -0
  55. package/dist/tools/versions.d.ts +50 -0
  56. package/dist/tools/versions.js +159 -0
  57. package/dist/tools/versions.js.map +1 -0
  58. package/dist/types.d.ts +118 -0
  59. package/dist/types.js +3 -0
  60. package/dist/types.js.map +1 -0
  61. package/dist/url-validator.d.ts +36 -0
  62. package/dist/url-validator.js +222 -0
  63. package/dist/url-validator.js.map +1 -0
  64. package/package.json +85 -0
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Generate MCP prompts that teach the AI about the content model.
3
+ *
4
+ * Auto-generates three prompts (content model overview, block composition guide,
5
+ * draft workflow guide) and merges with any user-provided domain prompts.
6
+ */ export function generatePrompts(schemas, catalog, relationships, domainPrompts) {
7
+ const prompts = [
8
+ buildContentModelOverview(schemas, relationships),
9
+ buildBlockCompositionGuide(catalog),
10
+ buildDraftWorkflowGuide(schemas)
11
+ ];
12
+ if (domainPrompts?.length) {
13
+ for (const dp of domainPrompts){
14
+ prompts.push({
15
+ name: dp.name,
16
+ title: dp.title,
17
+ description: dp.description,
18
+ handler () {
19
+ return {
20
+ messages: [
21
+ {
22
+ content: {
23
+ type: 'text',
24
+ text: dp.content
25
+ },
26
+ role: 'user'
27
+ }
28
+ ]
29
+ };
30
+ }
31
+ });
32
+ }
33
+ }
34
+ return prompts;
35
+ }
36
+ // ─── Prompt builders ──────────────────────────────────────────────
37
+ function buildContentModelOverview(schemas, relationships) {
38
+ return {
39
+ name: 'contentModelOverview',
40
+ title: 'Content Model Overview',
41
+ description: 'Describes every collection in the CMS — its purpose, fields, and relationships to other collections.',
42
+ handler () {
43
+ const lines = [
44
+ '# Content Model Overview',
45
+ ''
46
+ ];
47
+ for (const [slug, schema] of schemas){
48
+ lines.push(`## Collection: ${slug}`);
49
+ lines.push(`Draft support: ${schema.hasDrafts ? 'yes' : 'no'}`);
50
+ lines.push(`Live preview: ${schema.hasLivePreview ? 'yes' : 'no'}`);
51
+ lines.push('');
52
+ // Fields summary
53
+ lines.push('### Fields');
54
+ for (const field of schema.fields){
55
+ const parts = [
56
+ `- **${field.name}** (${field.type})`
57
+ ];
58
+ if (field.required) parts.push(' *required*');
59
+ if (field.hasMany) parts.push(' hasMany');
60
+ if (field.relationTo) {
61
+ const targets = Array.isArray(field.relationTo) ? field.relationTo.join(', ') : field.relationTo;
62
+ parts.push(` → ${targets}`);
63
+ }
64
+ if (field.options?.length) {
65
+ const vals = field.options.map((o)=>o.value).join(', ');
66
+ parts.push(` [${vals}]`);
67
+ }
68
+ if (field.maxRows) parts.push(` maxRows: ${field.maxRows}`);
69
+ lines.push(parts.join(''));
70
+ }
71
+ lines.push('');
72
+ // Relationships
73
+ const collRels = relationships.filter((r)=>r.fromCollection === slug);
74
+ if (collRels.length > 0) {
75
+ lines.push('### Relationships');
76
+ for (const rel of collRels){
77
+ const targets = Array.isArray(rel.toCollection) ? rel.toCollection.join(', ') : rel.toCollection;
78
+ lines.push(`- ${rel.fieldName} → ${targets}${rel.hasMany ? ' (hasMany)' : ''}`);
79
+ }
80
+ lines.push('');
81
+ }
82
+ }
83
+ return {
84
+ messages: [
85
+ {
86
+ content: {
87
+ type: 'text',
88
+ text: lines.join('\n')
89
+ },
90
+ role: 'user'
91
+ }
92
+ ]
93
+ };
94
+ }
95
+ };
96
+ }
97
+ function buildBlockCompositionGuide(catalog) {
98
+ return {
99
+ name: 'blockCompositionGuide',
100
+ title: 'Block Composition Guide',
101
+ description: 'Explains the section/leaf block hierarchy, valid nesting rules, and how to compose page layouts.',
102
+ handler () {
103
+ const lines = [
104
+ '# Block Composition Guide',
105
+ '',
106
+ 'Pages are built from **section** blocks. Each section can contain **leaf** blocks according to its nesting rules.',
107
+ ''
108
+ ];
109
+ // Section blocks
110
+ lines.push('## Section Blocks');
111
+ for (const section of catalog.sections){
112
+ lines.push(`### ${section.slug} (${section.nestingType})`);
113
+ if (section.nestingType === 'fixed') {
114
+ lines.push('This section has no nested blocks — configure it with its own fields only.');
115
+ } else if (section.nestingType === 'constrained') {
116
+ lines.push(`Accepts only: ${section.acceptedLeafSlugs.join(', ')}`);
117
+ if (section.maxRows) {
118
+ lines.push(`Maximum ${section.maxRows} leaf block(s).`);
119
+ }
120
+ } else {
121
+ lines.push(`Accepts all leaf blocks: ${section.acceptedLeafSlugs.join(', ')}`);
122
+ }
123
+ if (section.fields.length > 0) {
124
+ lines.push('Section-level fields:');
125
+ for (const f of section.fields){
126
+ lines.push(` - ${f.name} (${f.type})${f.required ? ' *required*' : ''}`);
127
+ }
128
+ }
129
+ lines.push('');
130
+ }
131
+ // Leaf blocks
132
+ lines.push('## Leaf Blocks');
133
+ for (const leaf of catalog.leaves){
134
+ lines.push(`### ${leaf.slug}`);
135
+ if (leaf.fields.length > 0) {
136
+ for (const f of leaf.fields){
137
+ lines.push(` - ${f.name} (${f.type})${f.required ? ' *required*' : ''}`);
138
+ }
139
+ }
140
+ lines.push('');
141
+ }
142
+ return {
143
+ messages: [
144
+ {
145
+ content: {
146
+ type: 'text',
147
+ text: lines.join('\n')
148
+ },
149
+ role: 'user'
150
+ }
151
+ ]
152
+ };
153
+ }
154
+ };
155
+ }
156
+ function buildDraftWorkflowGuide(schemas) {
157
+ return {
158
+ name: 'draftWorkflowGuide',
159
+ title: 'Draft Workflow Guide',
160
+ description: 'Explains which collections support drafts, how to create drafts, review them, and publish.',
161
+ handler () {
162
+ const draftCollections = [];
163
+ const publishCollections = [];
164
+ for (const [slug, schema] of schemas){
165
+ if (schema.hasDrafts) {
166
+ draftCollections.push(slug);
167
+ } else {
168
+ publishCollections.push(slug);
169
+ }
170
+ }
171
+ const lines = [
172
+ '# Draft Workflow Guide',
173
+ '',
174
+ '## Collections with draft support',
175
+ ''
176
+ ];
177
+ if (draftCollections.length === 0) {
178
+ lines.push('No collections have draft support enabled.');
179
+ } else {
180
+ for (const slug of draftCollections){
181
+ lines.push(`- **${slug}**`);
182
+ }
183
+ lines.push('');
184
+ lines.push('### How drafts work');
185
+ lines.push('1. When you create or update a document in a draft-enabled collection, set `_status: "draft"` to keep it unpublished.');
186
+ lines.push('2. Draft documents are only visible via preview URLs or the admin panel — they are not public.');
187
+ lines.push('3. To publish a draft, update the document with `_status: "published"` or use the `publishDraft` tool.');
188
+ lines.push('4. You can review a draft via its preview URL before publishing.');
189
+ }
190
+ lines.push('');
191
+ lines.push('## Collections without draft support');
192
+ lines.push('');
193
+ if (publishCollections.length === 0) {
194
+ lines.push('All collections support drafts.');
195
+ } else {
196
+ for (const slug of publishCollections){
197
+ lines.push(`- **${slug}** — changes are published immediately`);
198
+ }
199
+ }
200
+ return {
201
+ messages: [
202
+ {
203
+ content: {
204
+ type: 'text',
205
+ text: lines.join('\n')
206
+ },
207
+ role: 'user'
208
+ }
209
+ ]
210
+ };
211
+ }
212
+ };
213
+ }
214
+
215
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/prompts.ts"],"sourcesContent":["import type {\n BlockCatalog,\n CollectionSchema,\n DomainPrompt,\n RelationshipEdge,\n} from './types'\n\n/**\n * Generate MCP prompts that teach the AI about the content model.\n *\n * Auto-generates three prompts (content model overview, block composition guide,\n * draft workflow guide) and merges with any user-provided domain prompts.\n */\nexport function generatePrompts(\n schemas: Map<string, CollectionSchema>,\n catalog: BlockCatalog,\n relationships: RelationshipEdge[],\n domainPrompts?: DomainPrompt[],\n) {\n const prompts = [\n buildContentModelOverview(schemas, relationships),\n buildBlockCompositionGuide(catalog),\n buildDraftWorkflowGuide(schemas),\n ]\n\n if (domainPrompts?.length) {\n for (const dp of domainPrompts) {\n prompts.push({\n name: dp.name,\n title: dp.title,\n description: dp.description,\n handler() {\n return {\n messages: [\n {\n content: { type: 'text' as const, text: dp.content },\n role: 'user' as const,\n },\n ],\n }\n },\n })\n }\n }\n\n return prompts\n}\n\n// ─── Prompt builders ──────────────────────────────────────────────\n\nfunction buildContentModelOverview(\n schemas: Map<string, CollectionSchema>,\n relationships: RelationshipEdge[],\n) {\n return {\n name: 'contentModelOverview',\n title: 'Content Model Overview',\n description:\n 'Describes every collection in the CMS — its purpose, fields, and relationships to other collections.',\n handler() {\n const lines: string[] = ['# Content Model Overview', '']\n\n for (const [slug, schema] of schemas) {\n lines.push(`## Collection: ${slug}`)\n lines.push(`Draft support: ${schema.hasDrafts ? 'yes' : 'no'}`)\n lines.push(`Live preview: ${schema.hasLivePreview ? 'yes' : 'no'}`)\n lines.push('')\n\n // Fields summary\n lines.push('### Fields')\n for (const field of schema.fields) {\n const parts = [`- **${field.name}** (${field.type})`]\n if (field.required) parts.push(' *required*')\n if (field.hasMany) parts.push(' hasMany')\n if (field.relationTo) {\n const targets = Array.isArray(field.relationTo)\n ? field.relationTo.join(', ')\n : field.relationTo\n parts.push(` → ${targets}`)\n }\n if (field.options?.length) {\n const vals = field.options.map((o) => o.value).join(', ')\n parts.push(` [${vals}]`)\n }\n if (field.maxRows) parts.push(` maxRows: ${field.maxRows}`)\n lines.push(parts.join(''))\n }\n lines.push('')\n\n // Relationships\n const collRels = relationships.filter((r) => r.fromCollection === slug)\n if (collRels.length > 0) {\n lines.push('### Relationships')\n for (const rel of collRels) {\n const targets = Array.isArray(rel.toCollection)\n ? rel.toCollection.join(', ')\n : rel.toCollection\n lines.push(\n `- ${rel.fieldName} → ${targets}${rel.hasMany ? ' (hasMany)' : ''}`,\n )\n }\n lines.push('')\n }\n }\n\n return {\n messages: [\n {\n content: { type: 'text' as const, text: lines.join('\\n') },\n role: 'user' as const,\n },\n ],\n }\n },\n }\n}\n\nfunction buildBlockCompositionGuide(catalog: BlockCatalog) {\n return {\n name: 'blockCompositionGuide',\n title: 'Block Composition Guide',\n description:\n 'Explains the section/leaf block hierarchy, valid nesting rules, and how to compose page layouts.',\n handler() {\n const lines: string[] = [\n '# Block Composition Guide',\n '',\n 'Pages are built from **section** blocks. Each section can contain **leaf** blocks according to its nesting rules.',\n '',\n ]\n\n // Section blocks\n lines.push('## Section Blocks')\n for (const section of catalog.sections) {\n lines.push(`### ${section.slug} (${section.nestingType})`)\n\n if (section.nestingType === 'fixed') {\n lines.push('This section has no nested blocks — configure it with its own fields only.')\n } else if (section.nestingType === 'constrained') {\n lines.push(\n `Accepts only: ${section.acceptedLeafSlugs.join(', ')}`,\n )\n if (section.maxRows) {\n lines.push(`Maximum ${section.maxRows} leaf block(s).`)\n }\n } else {\n lines.push(\n `Accepts all leaf blocks: ${section.acceptedLeafSlugs.join(', ')}`,\n )\n }\n\n if (section.fields.length > 0) {\n lines.push('Section-level fields:')\n for (const f of section.fields) {\n lines.push(` - ${f.name} (${f.type})${f.required ? ' *required*' : ''}`)\n }\n }\n lines.push('')\n }\n\n // Leaf blocks\n lines.push('## Leaf Blocks')\n for (const leaf of catalog.leaves) {\n lines.push(`### ${leaf.slug}`)\n if (leaf.fields.length > 0) {\n for (const f of leaf.fields) {\n lines.push(` - ${f.name} (${f.type})${f.required ? ' *required*' : ''}`)\n }\n }\n lines.push('')\n }\n\n return {\n messages: [\n {\n content: { type: 'text' as const, text: lines.join('\\n') },\n role: 'user' as const,\n },\n ],\n }\n },\n }\n}\n\nfunction buildDraftWorkflowGuide(schemas: Map<string, CollectionSchema>) {\n return {\n name: 'draftWorkflowGuide',\n title: 'Draft Workflow Guide',\n description:\n 'Explains which collections support drafts, how to create drafts, review them, and publish.',\n handler() {\n const draftCollections: string[] = []\n const publishCollections: string[] = []\n\n for (const [slug, schema] of schemas) {\n if (schema.hasDrafts) {\n draftCollections.push(slug)\n } else {\n publishCollections.push(slug)\n }\n }\n\n const lines: string[] = [\n '# Draft Workflow Guide',\n '',\n '## Collections with draft support',\n '',\n ]\n\n if (draftCollections.length === 0) {\n lines.push('No collections have draft support enabled.')\n } else {\n for (const slug of draftCollections) {\n lines.push(`- **${slug}**`)\n }\n lines.push('')\n lines.push('### How drafts work')\n lines.push(\n '1. When you create or update a document in a draft-enabled collection, set `_status: \"draft\"` to keep it unpublished.',\n )\n lines.push(\n '2. Draft documents are only visible via preview URLs or the admin panel — they are not public.',\n )\n lines.push(\n '3. To publish a draft, update the document with `_status: \"published\"` or use the `publishDraft` tool.',\n )\n lines.push(\n '4. You can review a draft via its preview URL before publishing.',\n )\n }\n\n lines.push('')\n lines.push('## Collections without draft support')\n lines.push('')\n\n if (publishCollections.length === 0) {\n lines.push('All collections support drafts.')\n } else {\n for (const slug of publishCollections) {\n lines.push(`- **${slug}** — changes are published immediately`)\n }\n }\n\n return {\n messages: [\n {\n content: { type: 'text' as const, text: lines.join('\\n') },\n role: 'user' as const,\n },\n ],\n }\n },\n }\n}\n"],"names":["generatePrompts","schemas","catalog","relationships","domainPrompts","prompts","buildContentModelOverview","buildBlockCompositionGuide","buildDraftWorkflowGuide","length","dp","push","name","title","description","handler","messages","content","type","text","role","lines","slug","schema","hasDrafts","hasLivePreview","field","fields","parts","required","hasMany","relationTo","targets","Array","isArray","join","options","vals","map","o","value","maxRows","collRels","filter","r","fromCollection","rel","toCollection","fieldName","section","sections","nestingType","acceptedLeafSlugs","f","leaf","leaves","draftCollections","publishCollections"],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Simple in-memory sliding window rate limiter per API key.
3
+ * State resets on server restart — not suitable for serverless.
4
+ */
5
+ interface RateLimitOptions {
6
+ /** Window duration in milliseconds (default: 60000 = 1 minute) */
7
+ windowMs?: number;
8
+ /** Maximum requests per window (default: 60) */
9
+ maxRequests?: number;
10
+ }
11
+ export declare function createRateLimiter(options?: RateLimitOptions): {
12
+ /**
13
+ * Check if a request is allowed for the given key.
14
+ * Returns { allowed: true } or { allowed: false, retryAfterMs }.
15
+ */
16
+ check(key: string): {
17
+ allowed: true;
18
+ } | {
19
+ allowed: false;
20
+ retryAfterMs: number;
21
+ };
22
+ /** Reset the store (for testing) */
23
+ reset(): void;
24
+ };
25
+ export {};
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Simple in-memory sliding window rate limiter per API key.
3
+ * State resets on server restart — not suitable for serverless.
4
+ */ const DEFAULT_WINDOW_MS = 60_000;
5
+ const DEFAULT_MAX_REQUESTS = 60;
6
+ export function createRateLimiter(options) {
7
+ const windowMs = options?.windowMs ?? DEFAULT_WINDOW_MS;
8
+ const maxRequests = options?.maxRequests ?? DEFAULT_MAX_REQUESTS;
9
+ const store = new Map();
10
+ // Periodically clean up expired entries (every 5 minutes)
11
+ const cleanupInterval = setInterval(()=>{
12
+ const now = Date.now();
13
+ for (const [key, entry] of store){
14
+ entry.timestamps = entry.timestamps.filter((t)=>now - t < windowMs);
15
+ if (entry.timestamps.length === 0) store.delete(key);
16
+ }
17
+ }, 5 * 60_000);
18
+ // Allow garbage collection if the module is unloaded
19
+ if (cleanupInterval.unref) cleanupInterval.unref();
20
+ return {
21
+ /**
22
+ * Check if a request is allowed for the given key.
23
+ * Returns { allowed: true } or { allowed: false, retryAfterMs }.
24
+ */ check (key) {
25
+ const now = Date.now();
26
+ const entry = store.get(key) ?? {
27
+ timestamps: []
28
+ };
29
+ // Remove timestamps outside the window
30
+ entry.timestamps = entry.timestamps.filter((t)=>now - t < windowMs);
31
+ if (entry.timestamps.length >= maxRequests) {
32
+ const oldest = entry.timestamps[0];
33
+ const retryAfterMs = oldest + windowMs - now;
34
+ return {
35
+ allowed: false,
36
+ retryAfterMs: Math.max(retryAfterMs, 1000)
37
+ };
38
+ }
39
+ entry.timestamps.push(now);
40
+ store.set(key, entry);
41
+ return {
42
+ allowed: true
43
+ };
44
+ },
45
+ /** Reset the store (for testing) */ reset () {
46
+ store.clear();
47
+ }
48
+ };
49
+ }
50
+
51
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rate-limiter.ts"],"sourcesContent":["/**\n * Simple in-memory sliding window rate limiter per API key.\n * State resets on server restart — not suitable for serverless.\n */\n\ninterface RateLimitEntry {\n timestamps: number[]\n}\n\ninterface RateLimitOptions {\n /** Window duration in milliseconds (default: 60000 = 1 minute) */\n windowMs?: number\n /** Maximum requests per window (default: 60) */\n maxRequests?: number\n}\n\nconst DEFAULT_WINDOW_MS = 60_000\nconst DEFAULT_MAX_REQUESTS = 60\n\nexport function createRateLimiter(options?: RateLimitOptions) {\n const windowMs = options?.windowMs ?? DEFAULT_WINDOW_MS\n const maxRequests = options?.maxRequests ?? DEFAULT_MAX_REQUESTS\n const store = new Map<string, RateLimitEntry>()\n\n // Periodically clean up expired entries (every 5 minutes)\n const cleanupInterval = setInterval(() => {\n const now = Date.now()\n for (const [key, entry] of store) {\n entry.timestamps = entry.timestamps.filter((t) => now - t < windowMs)\n if (entry.timestamps.length === 0) store.delete(key)\n }\n }, 5 * 60_000)\n\n // Allow garbage collection if the module is unloaded\n if (cleanupInterval.unref) cleanupInterval.unref()\n\n return {\n /**\n * Check if a request is allowed for the given key.\n * Returns { allowed: true } or { allowed: false, retryAfterMs }.\n */\n check(key: string): { allowed: true } | { allowed: false; retryAfterMs: number } {\n const now = Date.now()\n const entry = store.get(key) ?? { timestamps: [] }\n\n // Remove timestamps outside the window\n entry.timestamps = entry.timestamps.filter((t) => now - t < windowMs)\n\n if (entry.timestamps.length >= maxRequests) {\n const oldest = entry.timestamps[0]\n const retryAfterMs = oldest + windowMs - now\n return { allowed: false, retryAfterMs: Math.max(retryAfterMs, 1000) }\n }\n\n entry.timestamps.push(now)\n store.set(key, entry)\n return { allowed: true }\n },\n\n /** Reset the store (for testing) */\n reset() {\n store.clear()\n },\n }\n}\n"],"names":["DEFAULT_WINDOW_MS","DEFAULT_MAX_REQUESTS","createRateLimiter","options","windowMs","maxRequests","store","Map","cleanupInterval","setInterval","now","Date","key","entry","timestamps","filter","t","length","delete","unref","check","get","oldest","retryAfterMs","allowed","Math","max","push","set","reset","clear"],"mappings":"AAAA;;;CAGC,GAaD,MAAMA,oBAAoB;AAC1B,MAAMC,uBAAuB;AAE7B,OAAO,SAASC,kBAAkBC,OAA0B;IAC1D,MAAMC,WAAWD,SAASC,YAAYJ;IACtC,MAAMK,cAAcF,SAASE,eAAeJ;IAC5C,MAAMK,QAAQ,IAAIC;IAElB,0DAA0D;IAC1D,MAAMC,kBAAkBC,YAAY;QAClC,MAAMC,MAAMC,KAAKD,GAAG;QACpB,KAAK,MAAM,CAACE,KAAKC,MAAM,IAAIP,MAAO;YAChCO,MAAMC,UAAU,GAAGD,MAAMC,UAAU,CAACC,MAAM,CAAC,CAACC,IAAMN,MAAMM,IAAIZ;YAC5D,IAAIS,MAAMC,UAAU,CAACG,MAAM,KAAK,GAAGX,MAAMY,MAAM,CAACN;QAClD;IACF,GAAG,IAAI;IAEP,qDAAqD;IACrD,IAAIJ,gBAAgBW,KAAK,EAAEX,gBAAgBW,KAAK;IAEhD,OAAO;QACL;;;KAGC,GACDC,OAAMR,GAAW;YACf,MAAMF,MAAMC,KAAKD,GAAG;YACpB,MAAMG,QAAQP,MAAMe,GAAG,CAACT,QAAQ;gBAAEE,YAAY,EAAE;YAAC;YAEjD,uCAAuC;YACvCD,MAAMC,UAAU,GAAGD,MAAMC,UAAU,CAACC,MAAM,CAAC,CAACC,IAAMN,MAAMM,IAAIZ;YAE5D,IAAIS,MAAMC,UAAU,CAACG,MAAM,IAAIZ,aAAa;gBAC1C,MAAMiB,SAAST,MAAMC,UAAU,CAAC,EAAE;gBAClC,MAAMS,eAAeD,SAASlB,WAAWM;gBACzC,OAAO;oBAAEc,SAAS;oBAAOD,cAAcE,KAAKC,GAAG,CAACH,cAAc;gBAAM;YACtE;YAEAV,MAAMC,UAAU,CAACa,IAAI,CAACjB;YACtBJ,MAAMsB,GAAG,CAAChB,KAAKC;YACf,OAAO;gBAAEW,SAAS;YAAK;QACzB;QAEA,kCAAkC,GAClCK;YACEvB,MAAMwB,KAAK;QACb;IACF;AACF"}
@@ -0,0 +1,18 @@
1
+ import type { BlockCatalog, CollectionSchema, RelationshipEdge } from './types';
2
+ /**
3
+ * Generate MCP resources that expose the block catalog,
4
+ * collection schemas, and relationship graph as static JSON.
5
+ */
6
+ export declare function generateResources(schemas: Map<string, CollectionSchema>, catalog: BlockCatalog, relationships: RelationshipEdge[]): {
7
+ name: string;
8
+ title: string;
9
+ description: string;
10
+ uri: string;
11
+ mimeType: string;
12
+ handler(uri: URL): {
13
+ contents: {
14
+ uri: string;
15
+ text: string;
16
+ }[];
17
+ };
18
+ }[];
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Generate MCP resources that expose the block catalog,
3
+ * collection schemas, and relationship graph as static JSON.
4
+ */ export function generateResources(schemas, catalog, relationships) {
5
+ return [
6
+ buildBlockCatalogResource(catalog),
7
+ buildCollectionSchemaResource(schemas),
8
+ buildRelationshipGraphResource(relationships)
9
+ ];
10
+ }
11
+ // ─── Resource builders ────────────────────────────────────────────
12
+ function buildBlockCatalogResource(catalog) {
13
+ const json = JSON.stringify(catalog, null, 2);
14
+ return {
15
+ name: 'blockCatalog',
16
+ title: 'Block Catalog',
17
+ description: 'JSON catalog of all block types — section/leaf hierarchy, nesting rules, and required fields.',
18
+ uri: 'blocks://catalog',
19
+ mimeType: 'application/json',
20
+ handler (uri) {
21
+ return {
22
+ contents: [
23
+ {
24
+ uri: uri.href,
25
+ text: json
26
+ }
27
+ ]
28
+ };
29
+ }
30
+ };
31
+ }
32
+ function buildCollectionSchemaResource(schemas) {
33
+ const obj = {};
34
+ for (const [slug, schema] of schemas){
35
+ obj[slug] = schema;
36
+ }
37
+ const json = JSON.stringify(obj, null, 2);
38
+ return {
39
+ name: 'collectionSchema',
40
+ title: 'Collection Schema',
41
+ description: 'JSON schema of all collections — fields, select options, and relationship targets.',
42
+ uri: 'collections://schema',
43
+ mimeType: 'application/json',
44
+ handler (uri) {
45
+ return {
46
+ contents: [
47
+ {
48
+ uri: uri.href,
49
+ text: json
50
+ }
51
+ ]
52
+ };
53
+ }
54
+ };
55
+ }
56
+ function buildRelationshipGraphResource(relationships) {
57
+ const json = JSON.stringify(relationships, null, 2);
58
+ return {
59
+ name: 'relationshipGraph',
60
+ title: 'Relationship Graph',
61
+ description: 'JSON representation of the collection relationship graph — which collections link to which.',
62
+ uri: 'collections://relationships',
63
+ mimeType: 'application/json',
64
+ handler (uri) {
65
+ return {
66
+ contents: [
67
+ {
68
+ uri: uri.href,
69
+ text: json
70
+ }
71
+ ]
72
+ };
73
+ }
74
+ };
75
+ }
76
+
77
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/resources.ts"],"sourcesContent":["import type {\n BlockCatalog,\n CollectionSchema,\n RelationshipEdge,\n} from './types'\n\n/**\n * Generate MCP resources that expose the block catalog,\n * collection schemas, and relationship graph as static JSON.\n */\nexport function generateResources(\n schemas: Map<string, CollectionSchema>,\n catalog: BlockCatalog,\n relationships: RelationshipEdge[],\n) {\n return [\n buildBlockCatalogResource(catalog),\n buildCollectionSchemaResource(schemas),\n buildRelationshipGraphResource(relationships),\n ]\n}\n\n// ─── Resource builders ────────────────────────────────────────────\n\nfunction buildBlockCatalogResource(catalog: BlockCatalog) {\n const json = JSON.stringify(catalog, null, 2)\n\n return {\n name: 'blockCatalog',\n title: 'Block Catalog',\n description:\n 'JSON catalog of all block types — section/leaf hierarchy, nesting rules, and required fields.',\n uri: 'blocks://catalog',\n mimeType: 'application/json',\n handler(uri: URL) {\n return {\n contents: [{ uri: uri.href, text: json }],\n }\n },\n }\n}\n\nfunction buildCollectionSchemaResource(schemas: Map<string, CollectionSchema>) {\n const obj: Record<string, CollectionSchema> = {}\n for (const [slug, schema] of schemas) {\n obj[slug] = schema\n }\n const json = JSON.stringify(obj, null, 2)\n\n return {\n name: 'collectionSchema',\n title: 'Collection Schema',\n description:\n 'JSON schema of all collections — fields, select options, and relationship targets.',\n uri: 'collections://schema',\n mimeType: 'application/json',\n handler(uri: URL) {\n return {\n contents: [{ uri: uri.href, text: json }],\n }\n },\n }\n}\n\nfunction buildRelationshipGraphResource(relationships: RelationshipEdge[]) {\n const json = JSON.stringify(relationships, null, 2)\n\n return {\n name: 'relationshipGraph',\n title: 'Relationship Graph',\n description:\n 'JSON representation of the collection relationship graph — which collections link to which.',\n uri: 'collections://relationships',\n mimeType: 'application/json',\n handler(uri: URL) {\n return {\n contents: [{ uri: uri.href, text: json }],\n }\n },\n }\n}\n"],"names":["generateResources","schemas","catalog","relationships","buildBlockCatalogResource","buildCollectionSchemaResource","buildRelationshipGraphResource","json","JSON","stringify","name","title","description","uri","mimeType","handler","contents","href","text","obj","slug","schema"],"mappings":"AAMA;;;CAGC,GACD,OAAO,SAASA,kBACdC,OAAsC,EACtCC,OAAqB,EACrBC,aAAiC;IAEjC,OAAO;QACLC,0BAA0BF;QAC1BG,8BAA8BJ;QAC9BK,+BAA+BH;KAChC;AACH;AAEA,qEAAqE;AAErE,SAASC,0BAA0BF,OAAqB;IACtD,MAAMK,OAAOC,KAAKC,SAAS,CAACP,SAAS,MAAM;IAE3C,OAAO;QACLQ,MAAM;QACNC,OAAO;QACPC,aACE;QACFC,KAAK;QACLC,UAAU;QACVC,SAAQF,GAAQ;YACd,OAAO;gBACLG,UAAU;oBAAC;wBAAEH,KAAKA,IAAII,IAAI;wBAAEC,MAAMX;oBAAK;iBAAE;YAC3C;QACF;IACF;AACF;AAEA,SAASF,8BAA8BJ,OAAsC;IAC3E,MAAMkB,MAAwC,CAAC;IAC/C,KAAK,MAAM,CAACC,MAAMC,OAAO,IAAIpB,QAAS;QACpCkB,GAAG,CAACC,KAAK,GAAGC;IACd;IACA,MAAMd,OAAOC,KAAKC,SAAS,CAACU,KAAK,MAAM;IAEvC,OAAO;QACLT,MAAM;QACNC,OAAO;QACPC,aACE;QACFC,KAAK;QACLC,UAAU;QACVC,SAAQF,GAAQ;YACd,OAAO;gBACLG,UAAU;oBAAC;wBAAEH,KAAKA,IAAII,IAAI;wBAAEC,MAAMX;oBAAK;iBAAE;YAC3C;QACF;IACF;AACF;AAEA,SAASD,+BAA+BH,aAAiC;IACvE,MAAMI,OAAOC,KAAKC,SAAS,CAACN,eAAe,MAAM;IAEjD,OAAO;QACLO,MAAM;QACNC,OAAO;QACPC,aACE;QACFC,KAAK;QACLC,UAAU;QACVC,SAAQF,GAAQ;YACd,OAAO;gBACLG,UAAU;oBAAC;wBAAEH,KAAKA,IAAII,IAAI;wBAAEC,MAAMX;oBAAK;iBAAE;YAC3C;QACF;IACF;AACF"}
@@ -0,0 +1,117 @@
1
+ import { z } from 'zod';
2
+ import type { BlockCatalog } from '../types';
3
+ /** Schema for a single leaf block within a section */
4
+ export declare const leafBlockSchema: z.ZodObject<{
5
+ blockType: z.ZodString;
6
+ fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
7
+ }, "strip", z.ZodTypeAny, {
8
+ blockType: string;
9
+ fields?: Record<string, unknown> | undefined;
10
+ }, {
11
+ blockType: string;
12
+ fields?: Record<string, unknown> | undefined;
13
+ }>;
14
+ /** Schema for a single section in a layout */
15
+ export declare const sectionSchema: z.ZodObject<{
16
+ sectionType: z.ZodString;
17
+ config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
18
+ /** Leaf blocks for single-content sections */
19
+ content: z.ZodOptional<z.ZodArray<z.ZodObject<{
20
+ blockType: z.ZodString;
21
+ fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
22
+ }, "strip", z.ZodTypeAny, {
23
+ blockType: string;
24
+ fields?: Record<string, unknown> | undefined;
25
+ }, {
26
+ blockType: string;
27
+ fields?: Record<string, unknown> | undefined;
28
+ }>, "many">>;
29
+ /** Left column leaf blocks (for two-column sections) */
30
+ leftColumn: z.ZodOptional<z.ZodArray<z.ZodObject<{
31
+ blockType: z.ZodString;
32
+ fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
33
+ }, "strip", z.ZodTypeAny, {
34
+ blockType: string;
35
+ fields?: Record<string, unknown> | undefined;
36
+ }, {
37
+ blockType: string;
38
+ fields?: Record<string, unknown> | undefined;
39
+ }>, "many">>;
40
+ /** Right column leaf blocks (for two-column sections) */
41
+ rightColumn: z.ZodOptional<z.ZodArray<z.ZodObject<{
42
+ blockType: z.ZodString;
43
+ fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
44
+ }, "strip", z.ZodTypeAny, {
45
+ blockType: string;
46
+ fields?: Record<string, unknown> | undefined;
47
+ }, {
48
+ blockType: string;
49
+ fields?: Record<string, unknown> | undefined;
50
+ }>, "many">>;
51
+ /** Flat field values for fixed sections */
52
+ fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
53
+ }, "strip", z.ZodTypeAny, {
54
+ sectionType: string;
55
+ fields?: Record<string, unknown> | undefined;
56
+ config?: Record<string, unknown> | undefined;
57
+ content?: {
58
+ blockType: string;
59
+ fields?: Record<string, unknown> | undefined;
60
+ }[] | undefined;
61
+ leftColumn?: {
62
+ blockType: string;
63
+ fields?: Record<string, unknown> | undefined;
64
+ }[] | undefined;
65
+ rightColumn?: {
66
+ blockType: string;
67
+ fields?: Record<string, unknown> | undefined;
68
+ }[] | undefined;
69
+ }, {
70
+ sectionType: string;
71
+ fields?: Record<string, unknown> | undefined;
72
+ config?: Record<string, unknown> | undefined;
73
+ content?: {
74
+ blockType: string;
75
+ fields?: Record<string, unknown> | undefined;
76
+ }[] | undefined;
77
+ leftColumn?: {
78
+ blockType: string;
79
+ fields?: Record<string, unknown> | undefined;
80
+ }[] | undefined;
81
+ rightColumn?: {
82
+ blockType: string;
83
+ fields?: Record<string, unknown> | undefined;
84
+ }[] | undefined;
85
+ }>;
86
+ export type LeafBlockInput = z.infer<typeof leafBlockSchema>;
87
+ export type SectionInput = z.infer<typeof sectionSchema>;
88
+ export interface ValidationError {
89
+ sectionIndex: number;
90
+ message: string;
91
+ validAlternatives?: string[];
92
+ }
93
+ export interface ComposeResult {
94
+ blocks: Record<string, unknown>[];
95
+ errors: ValidationError[];
96
+ }
97
+ /**
98
+ * Validate and compose an array of section inputs into block JSON.
99
+ * Returns either composed blocks or a list of validation errors.
100
+ */
101
+ export declare function composeSections(sections: SectionInput[], catalog: BlockCatalog): ComposeResult;
102
+ /** Build a hint payload describing the available section/leaf vocabulary. */
103
+ export declare function buildHint(catalog: BlockCatalog): {
104
+ availableSections: string[];
105
+ availableLeaves: string[];
106
+ sectionDetails: {
107
+ slug: string;
108
+ nestingType: import("../types").BlockNestingType;
109
+ acceptedLeaves: string[];
110
+ maxRows: number | undefined;
111
+ }[];
112
+ };
113
+ /**
114
+ * Apply a list operation against an existing array of blocks.
115
+ * `full` always replaces; the rest preserve the existing array.
116
+ */
117
+ export declare function applyOperation(composedBlocks: Record<string, unknown>[], operation: 'full' | 'append' | 'prepend' | 'insertAt' | 'replaceAt', insertIndex: number | undefined, existingLayout: Record<string, unknown>[] | undefined): Record<string, unknown>[];