@creatica/mcp 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +159 -20
- package/dist/index.js.map +1 -1
- package/dist/stdio.js +159 -20
- package/dist/stdio.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -28,22 +28,122 @@ var paletteShape = z.object({
|
|
|
28
28
|
palette: z.string().optional().describe(
|
|
29
29
|
'Id of a built-in palette from list_palettes, e.g. "underTheSea".'
|
|
30
30
|
),
|
|
31
|
-
primaryColor: hexColor.optional(),
|
|
32
|
-
secondaryColor: hexColor.optional(),
|
|
33
|
-
backgroundColor: hexColor.optional(),
|
|
31
|
+
primaryColor: hexColor.optional().describe("Main hex colour when not passing colors/palette."),
|
|
32
|
+
secondaryColor: hexColor.optional().describe("Second hex colour for two-tone structures."),
|
|
33
|
+
backgroundColor: hexColor.optional().describe("Hex colour of the canvas behind the shapes."),
|
|
34
34
|
gradientColor: z.boolean().optional().describe("Gradient fills instead of flat colour."),
|
|
35
35
|
useMultiColor: z.boolean().optional().describe("false = everything in primaryColor."),
|
|
36
36
|
colorSchemeType: z.enum(["Mono", "Tetrade", "Analogic"]).optional().describe("Derive a palette from primaryColor."),
|
|
37
|
-
opaqueColors: z.boolean().optional()
|
|
37
|
+
opaqueColors: z.boolean().optional().describe("true = no transparency in the shape fills.")
|
|
38
38
|
}).strict();
|
|
39
39
|
var shapeShape = z.record(z.string(), z.union([z.number(), z.boolean(), z.string()])).describe(
|
|
40
40
|
"Structure-specific options; call describe_background for the exact keys, ranges and what they do."
|
|
41
41
|
);
|
|
42
|
+
var SERVER_DESCRIPTION = "Deterministic procedural SVG/PNG backgrounds for websites, slides, social cards and apps: 58 generators, curated palettes and brand kits, every render reproducible from its seed.";
|
|
43
|
+
var sizeShape = z.object({
|
|
44
|
+
width: z.number().describe("Native frame width in px."),
|
|
45
|
+
height: z.number().describe("Native frame height in px.")
|
|
46
|
+
});
|
|
47
|
+
var catalogEntryShape = z.looseObject({
|
|
48
|
+
name: z.string().describe("Structure id for describe_background / generate_background."),
|
|
49
|
+
title: z.string().describe("Human-readable name."),
|
|
50
|
+
summary: z.string().describe("One-line description of the look."),
|
|
51
|
+
tags: z.array(z.string()).describe("Style tags, e.g. geometric, organic."),
|
|
52
|
+
mood: z.array(z.string()).describe("Mood words, e.g. calm, playful, dark."),
|
|
53
|
+
useCases: z.array(z.string()).describe("Where it works well."),
|
|
54
|
+
previewUrl: z.string().describe("Preview image of the default render."),
|
|
55
|
+
defaultSize: sizeShape
|
|
56
|
+
});
|
|
57
|
+
var suggestionShape = z.looseObject({
|
|
58
|
+
name: z.string().describe("Structure id."),
|
|
59
|
+
title: z.string(),
|
|
60
|
+
summary: z.string(),
|
|
61
|
+
score: z.number().describe("Relevance to the brief; higher is better."),
|
|
62
|
+
matched: z.array(z.string()).describe("Why it matched, for explaining."),
|
|
63
|
+
example: z.looseObject({ structure: z.string() }).describe("A ready-to-run generate_background request for this brief."),
|
|
64
|
+
palettes: z.array(z.string()).describe("Built-in palette ids whose mood fits the brief, best first.")
|
|
65
|
+
});
|
|
66
|
+
var paletteDescriptionShape = z.looseObject({
|
|
67
|
+
id: z.string().describe("Value to pass as palette.palette."),
|
|
68
|
+
name: z.string(),
|
|
69
|
+
colors: z.array(z.string()).describe("Hex colours."),
|
|
70
|
+
mood: z.array(z.string()).describe("Derived mood tags."),
|
|
71
|
+
lightness: z.number().describe("Average perceived lightness 0-1."),
|
|
72
|
+
saturation: z.number().describe("Average saturation 0-1.")
|
|
73
|
+
});
|
|
74
|
+
var paramDocShape = z.looseObject({
|
|
75
|
+
key: z.string().describe("Option key to pass under shape/palette."),
|
|
76
|
+
group: z.enum(["shape", "palette", "frame"]),
|
|
77
|
+
title: z.string(),
|
|
78
|
+
type: z.string(),
|
|
79
|
+
description: z.string(),
|
|
80
|
+
default: z.unknown().optional(),
|
|
81
|
+
min: z.number().optional(),
|
|
82
|
+
max: z.number().optional(),
|
|
83
|
+
step: z.number().optional(),
|
|
84
|
+
enum: z.array(z.string()).optional()
|
|
85
|
+
});
|
|
86
|
+
var structureDescriptionShape = z.looseObject({
|
|
87
|
+
name: z.string(),
|
|
88
|
+
title: z.string(),
|
|
89
|
+
summary: z.string(),
|
|
90
|
+
description: z.string(),
|
|
91
|
+
tags: z.array(z.string()),
|
|
92
|
+
mood: z.array(z.string()),
|
|
93
|
+
useCases: z.array(z.string()),
|
|
94
|
+
tips: z.array(z.string()),
|
|
95
|
+
defaultSize: sizeShape,
|
|
96
|
+
previewUrl: z.string(),
|
|
97
|
+
params: z.array(paramDocShape).describe("Every option this structure honours.")
|
|
98
|
+
});
|
|
99
|
+
var brandKitShape = z.looseObject({
|
|
100
|
+
slug: z.string().describe("Value to pass as brand to generate_background."),
|
|
101
|
+
name: z.string(),
|
|
102
|
+
colors: z.array(z.string()).describe("Hex colours."),
|
|
103
|
+
backgroundColor: z.string().optional(),
|
|
104
|
+
defaultStructure: z.string().optional()
|
|
105
|
+
});
|
|
106
|
+
var generateOutputShape = {
|
|
107
|
+
structure: z.string().describe("Structure that was rendered."),
|
|
108
|
+
seed: z.union([z.string(), z.number()]).describe("Seed used; reuse it to reproduce the exact image."),
|
|
109
|
+
width: z.number().describe("Render width in px."),
|
|
110
|
+
height: z.number().describe("Render height in px."),
|
|
111
|
+
format: z.enum(["svg", "png"]).describe("Delivered format."),
|
|
112
|
+
svgBytes: z.number().describe("Size of the SVG source in bytes."),
|
|
113
|
+
renderMs: z.number().describe("Render time in milliseconds."),
|
|
114
|
+
request: z.looseObject({ structure: z.string() }).describe("Resolved request incl. seed; replay it to reproduce or tweak."),
|
|
115
|
+
url: z.string().optional().describe(
|
|
116
|
+
"Hosted only: permanent, cacheable image URL in the requested format."
|
|
117
|
+
),
|
|
118
|
+
svgUrl: z.string().optional().describe("Hosted only: permanent URL of the SVG source."),
|
|
119
|
+
savedTo: z.string().optional().describe("Local only: absolute path the file was written to."),
|
|
120
|
+
bytes: z.number().optional().describe("Local only: bytes written to disk."),
|
|
121
|
+
warnings: z.array(z.string()).optional().describe("Options that were ignored or clamped, and why."),
|
|
122
|
+
note: z.string().optional().describe("Anything else worth knowing.")
|
|
123
|
+
};
|
|
42
124
|
function createMcpServer(options = {}) {
|
|
43
125
|
const { hosted } = options;
|
|
44
126
|
const maxInline = options.maxInlineBytes ?? 6e5;
|
|
45
127
|
const server = new McpServer(
|
|
46
|
-
{
|
|
128
|
+
{
|
|
129
|
+
name: "creatica-backgrounds",
|
|
130
|
+
title: "Creatica Backgrounds",
|
|
131
|
+
version: ENGINE_VERSION,
|
|
132
|
+
description: SERVER_DESCRIPTION,
|
|
133
|
+
websiteUrl: "https://creatica.app",
|
|
134
|
+
icons: [
|
|
135
|
+
{
|
|
136
|
+
src: "https://creatica.app/site/android-chrome-512x512.png",
|
|
137
|
+
mimeType: "image/png",
|
|
138
|
+
sizes: ["512x512"]
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
src: "https://creatica.app/site/android-chrome-192x192.png",
|
|
142
|
+
mimeType: "image/png",
|
|
143
|
+
sizes: ["192x192"]
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
},
|
|
47
147
|
{
|
|
48
148
|
instructions: [
|
|
49
149
|
"Creatica generates abstract, procedural SVG/PNG backgrounds (waves, blobs, bauhaus grids, topo lines, aurora glows\u2026) for websites, slides, social cards and apps.",
|
|
@@ -60,8 +160,12 @@ function createMcpServer(options = {}) {
|
|
|
60
160
|
inputSchema: {
|
|
61
161
|
query: z.string().optional().describe('Free text, e.g. "calm waves" or "geometric poster".'),
|
|
62
162
|
mood: z.string().optional().describe("A mood word, e.g. dark, playful, minimal, retro."),
|
|
63
|
-
tag: z.string().optional(),
|
|
64
|
-
limit: z.number().int().min(1).max(100).optional()
|
|
163
|
+
tag: z.string().optional().describe("A style tag, e.g. geometric, organic, grid, gradient."),
|
|
164
|
+
limit: z.number().int().min(1).max(100).optional().describe("Maximum number of results (default 100).")
|
|
165
|
+
},
|
|
166
|
+
outputSchema: {
|
|
167
|
+
count: z.number().describe("Number of backgrounds returned."),
|
|
168
|
+
backgrounds: z.array(catalogEntryShape)
|
|
65
169
|
},
|
|
66
170
|
annotations: {
|
|
67
171
|
readOnlyHint: true,
|
|
@@ -93,8 +197,13 @@ function createMcpServer(options = {}) {
|
|
|
93
197
|
title: "Suggest backgrounds for a brief",
|
|
94
198
|
description: 'Rank structures for a natural-language brief ("dark aurora glow for an AI landing page"). Returns why each matched, matching built-in palettes, and a ready-to-run example request for generate_background.',
|
|
95
199
|
inputSchema: {
|
|
96
|
-
brief: z.string().min(2)
|
|
97
|
-
|
|
200
|
+
brief: z.string().min(2).describe(
|
|
201
|
+
"What the background is for, in plain words: subject, mood, colours, where it goes."
|
|
202
|
+
),
|
|
203
|
+
limit: z.number().int().min(1).max(20).optional().describe("How many suggestions to return (default 5).")
|
|
204
|
+
},
|
|
205
|
+
outputSchema: {
|
|
206
|
+
suggestions: z.array(suggestionShape).describe("Best match first.")
|
|
98
207
|
},
|
|
99
208
|
annotations: {
|
|
100
209
|
readOnlyHint: true,
|
|
@@ -115,6 +224,13 @@ function createMcpServer(options = {}) {
|
|
|
115
224
|
"markdown (default) is best for reading; json includes the JSON Schema."
|
|
116
225
|
)
|
|
117
226
|
},
|
|
227
|
+
outputSchema: {
|
|
228
|
+
name: z.string().describe("Structure id."),
|
|
229
|
+
format: z.enum(["markdown", "json"]).describe("Which form was returned."),
|
|
230
|
+
card: z.string().optional().describe("markdown format: the style card as Markdown."),
|
|
231
|
+
description: structureDescriptionShape.optional().describe("json format: the structured description."),
|
|
232
|
+
jsonSchema: z.looseObject({}).optional().describe("json format: JSON Schema for a generate request.")
|
|
233
|
+
},
|
|
118
234
|
annotations: {
|
|
119
235
|
readOnlyHint: true,
|
|
120
236
|
idempotentHint: true,
|
|
@@ -124,8 +240,17 @@ function createMcpServer(options = {}) {
|
|
|
124
240
|
async ({ name, format }) => {
|
|
125
241
|
try {
|
|
126
242
|
if (format === "json")
|
|
127
|
-
return json({
|
|
128
|
-
|
|
243
|
+
return json({
|
|
244
|
+
name,
|
|
245
|
+
format: "json",
|
|
246
|
+
description: describe(name),
|
|
247
|
+
jsonSchema: jsonSchemaFor(name)
|
|
248
|
+
});
|
|
249
|
+
const card = styleCard(name);
|
|
250
|
+
return {
|
|
251
|
+
content: [{ type: "text", text: card }],
|
|
252
|
+
structuredContent: { name, format: "markdown", card }
|
|
253
|
+
};
|
|
129
254
|
} catch (e) {
|
|
130
255
|
return error(e.message);
|
|
131
256
|
}
|
|
@@ -139,6 +264,10 @@ function createMcpServer(options = {}) {
|
|
|
139
264
|
inputSchema: {
|
|
140
265
|
mood: z.string().optional().describe('Filter by a mood tag, e.g. "dark" or "pastel".')
|
|
141
266
|
},
|
|
267
|
+
outputSchema: {
|
|
268
|
+
count: z.number().describe("Number of palettes returned."),
|
|
269
|
+
palettes: z.array(paletteDescriptionShape)
|
|
270
|
+
},
|
|
142
271
|
annotations: {
|
|
143
272
|
readOnlyHint: true,
|
|
144
273
|
idempotentHint: true,
|
|
@@ -159,6 +288,9 @@ function createMcpServer(options = {}) {
|
|
|
159
288
|
title: "List brand kits",
|
|
160
289
|
description: "Saved brand palettes on this account. Pass a kit's slug as `brand` to generate_background to render on-brand without repeating hex colours.",
|
|
161
290
|
inputSchema: {},
|
|
291
|
+
outputSchema: {
|
|
292
|
+
brandKits: z.array(brandKitShape).describe("Saved kits, may be empty.")
|
|
293
|
+
},
|
|
162
294
|
annotations: {
|
|
163
295
|
readOnlyHint: true,
|
|
164
296
|
idempotentHint: true,
|
|
@@ -182,9 +314,13 @@ function createMcpServer(options = {}) {
|
|
|
182
314
|
seed: z.string().optional().describe(
|
|
183
315
|
"Any string. Omit for a fresh random seed (returned in the result)."
|
|
184
316
|
),
|
|
185
|
-
width: z.number().int().min(1).max(8192).optional()
|
|
186
|
-
|
|
187
|
-
|
|
317
|
+
width: z.number().int().min(1).max(8192).optional().describe(
|
|
318
|
+
"Frame width in px. Defaults to the structure's native size (see describe_background)."
|
|
319
|
+
),
|
|
320
|
+
height: z.number().int().min(1).max(8192).optional().describe("Frame height in px. Defaults to the native size."),
|
|
321
|
+
palette: paletteShape.optional().describe(
|
|
322
|
+
"Colour choices: explicit hex colours, a built-in palette id, or a primary colour to derive from."
|
|
323
|
+
),
|
|
188
324
|
shape: shapeShape.optional(),
|
|
189
325
|
format: z.enum(["svg", "png"]).optional().describe(
|
|
190
326
|
"png (default) returns a viewable image; svg returns the vector source."
|
|
@@ -203,6 +339,7 @@ function createMcpServer(options = {}) {
|
|
|
203
339
|
)
|
|
204
340
|
}
|
|
205
341
|
},
|
|
342
|
+
outputSchema: generateOutputShape,
|
|
206
343
|
annotations: {
|
|
207
344
|
readOnlyHint: true,
|
|
208
345
|
idempotentHint: true,
|
|
@@ -275,7 +412,9 @@ function createMcpServer(options = {}) {
|
|
|
275
412
|
if (saveTo) {
|
|
276
413
|
const path = resolve(saveTo);
|
|
277
414
|
await mkdir(dirname(path), { recursive: true });
|
|
278
|
-
const bytes = format === "png" ? await renderPng(result.svg, {
|
|
415
|
+
const bytes = format === "png" ? await renderPng(result.svg, {
|
|
416
|
+
width: args.pngWidth ?? result.width
|
|
417
|
+
}) : Buffer.from(result.svg, "utf8");
|
|
279
418
|
await writeFile(path, bytes);
|
|
280
419
|
meta.savedTo = path;
|
|
281
420
|
meta.bytes = bytes.length;
|
|
@@ -312,7 +451,7 @@ function createMcpServer(options = {}) {
|
|
|
312
451
|
meta.note = `Preview downscaled to 480px (full PNG would be ${png.length} bytes).`;
|
|
313
452
|
}
|
|
314
453
|
content.unshift({ type: "text", text: JSON.stringify(meta, null, 2) });
|
|
315
|
-
return { content };
|
|
454
|
+
return { content, structuredContent: meta };
|
|
316
455
|
}
|
|
317
456
|
);
|
|
318
457
|
return server;
|
|
@@ -335,10 +474,10 @@ function renderUrl(baseUrl, req, format) {
|
|
|
335
474
|
return u.toString();
|
|
336
475
|
}
|
|
337
476
|
function json(value) {
|
|
338
|
-
return {
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
|
|
477
|
+
return {
|
|
478
|
+
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
479
|
+
structuredContent: value
|
|
480
|
+
};
|
|
342
481
|
}
|
|
343
482
|
function error(message) {
|
|
344
483
|
return { isError: true, content: [{ type: "text", text: message }] };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import {\n ENGINE_VERSION,\n renderPng,\n renderSvg,\n type RenderRequest,\n type RenderResult,\n} from '@creatica/engine';\nimport {\n catalog,\n describe,\n jsonSchemaFor,\n listPalettes,\n normalizeRequest,\n styleCard,\n suggest,\n} from '@creatica/schema';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, resolve } from 'node:path';\nimport { z } from 'zod';\n\nexport interface BrandKitSummary {\n slug: string;\n name: string;\n colors: string[];\n backgroundColor?: string;\n defaultStructure?: string;\n}\n\nexport interface HostedOptions {\n /** Public origin of the API, e.g. https://api.creatica.app */\n publicBaseUrl: string;\n /** Brand kits of the calling account; enables `brand` on generate and list_brand_kits. */\n listBrandKits?: () => Promise<BrandKitSummary[]>;\n /**\n * Called before rendering. Throw to refuse (e.g. quota exhausted); the\n * message is returned to the model as a tool error, so discovery tools keep\n * working and the model can explain what to do next.\n */\n beforeRender?: (request: RenderRequest) => void | Promise<void>;\n /** Called after a successful render so the host can meter usage. */\n onRender?: (\n result: RenderResult,\n format: 'svg' | 'png',\n ) => void | Promise<void>;\n}\n\nexport interface McpServerOptions {\n /**\n * Local mode (default) renders in-process and can write files to disk.\n * Hosted mode additionally returns permanent, cacheable image URLs and\n * never touches the filesystem.\n */\n hosted?: HostedOptions;\n /** Inline PNG previews above this many bytes are downscaled. */\n maxInlineBytes?: number;\n}\n\nconst hexColor = z\n .string()\n .regex(\n /^#?([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/,\n 'hex colour like #1e90ff',\n );\n\nconst paletteShape = z\n .object({\n colors: z\n .array(hexColor)\n .min(1)\n .max(12)\n .optional()\n .describe(\n 'Explicit hex colours to draw from (best for brand matching). Overrides palette/scheme.',\n ),\n palette: z\n .string()\n .optional()\n .describe(\n 'Id of a built-in palette from list_palettes, e.g. \"underTheSea\".',\n ),\n primaryColor: hexColor.optional(),\n secondaryColor: hexColor.optional(),\n backgroundColor: hexColor.optional(),\n gradientColor: z\n .boolean()\n .optional()\n .describe('Gradient fills instead of flat colour.'),\n useMultiColor: z\n .boolean()\n .optional()\n .describe('false = everything in primaryColor.'),\n colorSchemeType: z\n .enum(['Mono', 'Tetrade', 'Analogic'])\n .optional()\n .describe('Derive a palette from primaryColor.'),\n opaqueColors: z.boolean().optional(),\n })\n .strict();\n\n// Shape options are a loose record: which keys apply depends on the structure\n// (see describe_background). normalizeRequest() validates against that.\nconst shapeShape = z\n .record(z.string(), z.union([z.number(), z.boolean(), z.string()]))\n .describe(\n 'Structure-specific options; call describe_background for the exact keys, ranges and what they do.',\n );\n\nexport function createMcpServer(options: McpServerOptions = {}): McpServer {\n const { hosted } = options;\n const maxInline = options.maxInlineBytes ?? 600_000;\n\n const server = new McpServer(\n { name: 'creatica-backgrounds', version: ENGINE_VERSION },\n {\n instructions: [\n 'Creatica generates abstract, procedural SVG/PNG backgrounds (waves, blobs, bauhaus grids, topo lines, aurora glows…) for websites, slides, social cards and apps.',\n 'Workflow: suggest_backgrounds (or list_backgrounds) with the brief → describe_background for the chosen one → generate_background with a seed. Every render is deterministic: same structure + params + seed = identical image, so keep the seed to reproduce or make on-brand variants.',\n 'For brand colours pass palette.colors as hex. For a curated look pass palette.palette from list_palettes. Prefer PNG when you need a preview; SVG for the final asset.',\n ].join('\\n'),\n },\n );\n\n server.registerTool(\n 'list_backgrounds',\n {\n title: 'List backgrounds',\n description:\n 'Browse the catalog of background structures. Optionally filter by a free-text query, tag or mood word. Returns name, one-line summary, mood, use cases and a preview URL for each.',\n inputSchema: {\n query: z\n .string()\n .optional()\n .describe('Free text, e.g. \"calm waves\" or \"geometric poster\".'),\n mood: z\n .string()\n .optional()\n .describe('A mood word, e.g. dark, playful, minimal, retro.'),\n tag: z.string().optional(),\n limit: z.number().int().min(1).max(100).optional(),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ query, mood, tag, limit }) => {\n let items = catalog();\n if (mood)\n items = items.filter((c) =>\n c.mood.some((m) => m.toLowerCase().includes(mood.toLowerCase())),\n );\n if (tag)\n items = items.filter((c) =>\n c.tags.some((t) => t.toLowerCase().includes(tag.toLowerCase())),\n );\n if (query) {\n const ranked = suggest(query, { limit: 100 }).map((s) => s.name);\n items = items\n .filter((c) => ranked.includes(c.name))\n .sort((a, b) => ranked.indexOf(a.name) - ranked.indexOf(b.name));\n }\n items = items.slice(0, limit ?? 100);\n return json({ count: items.length, backgrounds: items });\n },\n );\n\n server.registerTool(\n 'suggest_backgrounds',\n {\n title: 'Suggest backgrounds for a brief',\n description:\n 'Rank structures for a natural-language brief (\"dark aurora glow for an AI landing page\"). Returns why each matched, matching built-in palettes, and a ready-to-run example request for generate_background.',\n inputSchema: {\n brief: z.string().min(2),\n limit: z.number().int().min(1).max(20).optional(),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ brief, limit }) =>\n json({ suggestions: suggest(brief, { limit: limit ?? 5 }) }),\n );\n\n server.registerTool(\n 'describe_background',\n {\n title: 'Describe a background',\n description:\n 'Full style card for one structure: what it looks like, mood, where text can go, tips, and every parameter it honours with ranges, defaults and the visual effect of low/high values. Also returns the JSON Schema for a generate request.',\n inputSchema: {\n name: z\n .string()\n .describe('Structure name from list_backgrounds, e.g. \"wave\".'),\n format: z\n .enum(['markdown', 'json'])\n .optional()\n .describe(\n 'markdown (default) is best for reading; json includes the JSON Schema.',\n ),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ name, format }) => {\n try {\n if (format === 'json')\n return json({ ...describe(name), jsonSchema: jsonSchemaFor(name) });\n return text(styleCard(name));\n } catch (e) {\n return error((e as Error).message);\n }\n },\n );\n\n server.registerTool(\n 'list_palettes',\n {\n title: 'List palettes',\n description:\n 'Built-in curated colour palettes with their hex colours and derived mood tags (dark, pastel, vivid, warm, cool…). Pass the id as palette.palette to generate_background.',\n inputSchema: {\n mood: z\n .string()\n .optional()\n .describe('Filter by a mood tag, e.g. \"dark\" or \"pastel\".'),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ mood }) => {\n let items = listPalettes();\n if (mood)\n items = items.filter((p) => p.mood.includes(mood.toLowerCase()));\n return json({ count: items.length, palettes: items });\n },\n );\n\n if (hosted?.listBrandKits) {\n server.registerTool(\n 'list_brand_kits',\n {\n title: 'List brand kits',\n description:\n \"Saved brand palettes on this account. Pass a kit's slug as `brand` to generate_background to render on-brand without repeating hex colours.\",\n inputSchema: {},\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async () => json({ brandKits: await hosted.listBrandKits!() }),\n );\n }\n\n server.registerTool(\n 'generate_background',\n {\n title: 'Generate a background',\n description: [\n 'Render a background as SVG or PNG. Deterministic: the same structure, options and seed always give the same image, so store the returned seed.',\n hosted\n ? 'Returns a permanent, cacheable image URL you can use directly in <img>/CSS, plus an inline PNG preview.'\n : 'Returns the image inline (PNG previews are visible to you); pass saveTo to also write the file to disk.',\n 'Call describe_background first to learn which shape options the structure honours.',\n ].join(' '),\n inputSchema: {\n structure: z.string().describe('Structure name, e.g. \"wave\".'),\n seed: z\n .string()\n .optional()\n .describe(\n 'Any string. Omit for a fresh random seed (returned in the result).',\n ),\n width: z.number().int().min(1).max(8192).optional(),\n height: z.number().int().min(1).max(8192).optional(),\n palette: paletteShape.optional(),\n shape: shapeShape.optional(),\n format: z\n .enum(['svg', 'png'])\n .optional()\n .describe(\n 'png (default) returns a viewable image; svg returns the vector source.',\n ),\n pngWidth: z\n .number()\n .int()\n .min(16)\n .max(8192)\n .optional()\n .describe(\n 'Raster width for PNG output/preview. Defaults to the render width, capped for inline previews.',\n ),\n ...(hosted?.listBrandKits\n ? {\n brand: z\n .string()\n .optional()\n .describe(\n 'Slug of a saved brand kit (see list_brand_kits); its colours become the palette baseline.',\n ),\n }\n : {}),\n ...(hosted\n ? {}\n : {\n saveTo: z\n .string()\n .optional()\n .describe(\n 'Local file path to write the result to (extension decides svg/png if format is omitted).',\n ),\n }),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async (args) => {\n const input = { ...(args as Record<string, unknown>) };\n const brandSlug =\n typeof input.brand === 'string' ? input.brand : undefined;\n delete input.brand;\n if (brandSlug) {\n const kit = (await hosted?.listBrandKits?.())?.find(\n (k) => k.slug === brandSlug,\n );\n if (!kit)\n return error(\n `Unknown brand kit \"${brandSlug}\". Call list_brand_kits to see the available ones.`,\n );\n const basePalette: Record<string, unknown> = { colors: kit.colors };\n if (kit.backgroundColor)\n basePalette.backgroundColor = kit.backgroundColor;\n input.palette = {\n ...basePalette,\n ...((input.palette as Record<string, unknown>) ?? {}),\n };\n if (!input.structure && kit.defaultStructure)\n input.structure = kit.defaultStructure;\n }\n let normalized;\n try {\n normalized = normalizeRequest(input);\n } catch (e) {\n return error((e as Error).message);\n }\n const { request, warnings } = normalized;\n const saveTo = hosted ? undefined : (args as { saveTo?: string }).saveTo;\n const format: 'svg' | 'png' =\n (args.format as 'svg' | 'png' | undefined) ??\n (saveTo?.toLowerCase().endsWith('.svg') ? 'svg' : 'png');\n\n try {\n await hosted?.beforeRender?.(request);\n } catch (e) {\n return error((e as Error).message);\n }\n const result = await renderSvg(request);\n await hosted?.onRender?.(result, format);\n\n const content: CallToolResult['content'] = [];\n const meta: Record<string, unknown> = {\n structure: result.structure,\n seed: result.seed,\n width: result.width,\n height: result.height,\n format,\n svgBytes: result.svg.length,\n renderMs: result.durationMs,\n // Echo the resolved options so the caller can reproduce or tweak.\n request: { ...request, seed: result.seed },\n ...(warnings.length ? { warnings } : {}),\n };\n\n if (hosted) {\n meta.url = renderUrl(\n hosted.publicBaseUrl,\n { ...request, seed: result.seed },\n format,\n );\n meta.svgUrl = renderUrl(\n hosted.publicBaseUrl,\n { ...request, seed: result.seed },\n 'svg',\n );\n }\n\n if (saveTo) {\n const path = resolve(saveTo);\n await mkdir(dirname(path), { recursive: true });\n const bytes =\n format === 'png'\n ? await renderPng(result.svg, { width: args.pngWidth ?? result.width })\n : Buffer.from(result.svg, 'utf8');\n await writeFile(path, bytes);\n meta.savedTo = path;\n meta.bytes = bytes.length;\n }\n\n if (format === 'svg') {\n if (result.svg.length <= maxInline && !saveTo) {\n content.push({\n type: 'resource',\n resource: {\n uri: `creatica://render/${result.structure}/${result.seed}.svg`,\n mimeType: 'image/svg+xml',\n text: result.svg,\n },\n });\n } else if (!saveTo && !hosted) {\n meta.note = `SVG is ${result.svg.length} bytes; pass saveTo to write it to disk instead of returning it inline.`;\n }\n }\n\n // Always attach a PNG preview so the model can see what it made. Keep\n // it modest: previews are for judging composition, not final delivery.\n const previewWidth = Math.min(args.pngWidth ?? 800, 1200);\n const png = await renderPng(result.svg, { width: previewWidth });\n if (png.length <= maxInline) {\n content.push({\n type: 'image',\n data: png.toString('base64'),\n mimeType: 'image/png',\n });\n } else {\n const small = await renderPng(result.svg, { width: 480 });\n content.push({\n type: 'image',\n data: small.toString('base64'),\n mimeType: 'image/png',\n });\n meta.note = `Preview downscaled to 480px (full PNG would be ${png.length} bytes).`;\n }\n\n content.unshift({ type: 'text', text: JSON.stringify(meta, null, 2) });\n return { content };\n },\n );\n\n return server;\n}\n\nexport function renderUrl(\n baseUrl: string,\n req: RenderRequest,\n format: 'svg' | 'png',\n): string {\n const u = new URL(\n `/v1/render/${encodeURIComponent(req.structure)}.${format}`,\n baseUrl,\n );\n if (req.seed !== undefined) u.searchParams.set('seed', String(req.seed));\n if (req.width) u.searchParams.set('width', String(req.width));\n if (req.height) u.searchParams.set('height', String(req.height));\n for (const [k, v] of Object.entries(req.palette ?? {})) {\n if (v === undefined) continue;\n u.searchParams.set(k, Array.isArray(v) ? v.join(',') : String(v));\n }\n for (const [k, v] of Object.entries(req.shape ?? {})) {\n if (v !== undefined) u.searchParams.set(k, String(v));\n }\n return u.toString();\n}\n\nfunction json(value: unknown): CallToolResult {\n return { content: [{ type: 'text', text: JSON.stringify(value, null, 2) }] };\n}\nfunction text(value: string): CallToolResult {\n return { content: [{ type: 'text', text: value }] };\n}\nfunction error(message: string): CallToolResult {\n return { isError: true, content: [{ type: 'text', text: message }] };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import {\n ENGINE_VERSION,\n renderPng,\n renderSvg,\n type RenderRequest,\n type RenderResult,\n} from '@creatica/engine';\nimport {\n catalog,\n describe,\n jsonSchemaFor,\n listPalettes,\n normalizeRequest,\n styleCard,\n suggest,\n} from '@creatica/schema';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, resolve } from 'node:path';\nimport { z } from 'zod';\n\nexport interface BrandKitSummary {\n slug: string;\n name: string;\n colors: string[];\n backgroundColor?: string;\n defaultStructure?: string;\n}\n\nexport interface HostedOptions {\n /** Public origin of the API, e.g. https://api.creatica.app */\n publicBaseUrl: string;\n /** Brand kits of the calling account; enables `brand` on generate and list_brand_kits. */\n listBrandKits?: () => Promise<BrandKitSummary[]>;\n /**\n * Called before rendering. Throw to refuse (e.g. quota exhausted); the\n * message is returned to the model as a tool error, so discovery tools keep\n * working and the model can explain what to do next.\n */\n beforeRender?: (request: RenderRequest) => void | Promise<void>;\n /** Called after a successful render so the host can meter usage. */\n onRender?: (\n result: RenderResult,\n format: 'svg' | 'png',\n ) => void | Promise<void>;\n}\n\nexport interface McpServerOptions {\n /**\n * Local mode (default) renders in-process and can write files to disk.\n * Hosted mode additionally returns permanent, cacheable image URLs and\n * never touches the filesystem.\n */\n hosted?: HostedOptions;\n /** Inline PNG previews above this many bytes are downscaled. */\n maxInlineBytes?: number;\n}\n\nconst hexColor = z\n .string()\n .regex(\n /^#?([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/,\n 'hex colour like #1e90ff',\n );\n\nconst paletteShape = z\n .object({\n colors: z\n .array(hexColor)\n .min(1)\n .max(12)\n .optional()\n .describe(\n 'Explicit hex colours to draw from (best for brand matching). Overrides palette/scheme.',\n ),\n palette: z\n .string()\n .optional()\n .describe(\n 'Id of a built-in palette from list_palettes, e.g. \"underTheSea\".',\n ),\n primaryColor: hexColor\n .optional()\n .describe('Main hex colour when not passing colors/palette.'),\n secondaryColor: hexColor\n .optional()\n .describe('Second hex colour for two-tone structures.'),\n backgroundColor: hexColor\n .optional()\n .describe('Hex colour of the canvas behind the shapes.'),\n gradientColor: z\n .boolean()\n .optional()\n .describe('Gradient fills instead of flat colour.'),\n useMultiColor: z\n .boolean()\n .optional()\n .describe('false = everything in primaryColor.'),\n colorSchemeType: z\n .enum(['Mono', 'Tetrade', 'Analogic'])\n .optional()\n .describe('Derive a palette from primaryColor.'),\n opaqueColors: z\n .boolean()\n .optional()\n .describe('true = no transparency in the shape fills.'),\n })\n .strict();\n\n// Shape options are a loose record: which keys apply depends on the structure\n// (see describe_background). normalizeRequest() validates against that.\nconst shapeShape = z\n .record(z.string(), z.union([z.number(), z.boolean(), z.string()]))\n .describe(\n 'Structure-specific options; call describe_background for the exact keys, ranges and what they do.',\n );\n\nconst SERVER_DESCRIPTION =\n 'Deterministic procedural SVG/PNG backgrounds for websites, slides, social cards and apps: 58 generators, curated palettes and brand kits, every render reproducible from its seed.';\n\n// Output schemas: what each tool puts in structuredContent (the text content\n// carries the same JSON, so clients without structured-output support lose\n// nothing).\nconst sizeShape = z.object({\n width: z.number().describe('Native frame width in px.'),\n height: z.number().describe('Native frame height in px.'),\n});\n\nconst catalogEntryShape = z.looseObject({\n name: z\n .string()\n .describe('Structure id for describe_background / generate_background.'),\n title: z.string().describe('Human-readable name.'),\n summary: z.string().describe('One-line description of the look.'),\n tags: z.array(z.string()).describe('Style tags, e.g. geometric, organic.'),\n mood: z.array(z.string()).describe('Mood words, e.g. calm, playful, dark.'),\n useCases: z.array(z.string()).describe('Where it works well.'),\n previewUrl: z.string().describe('Preview image of the default render.'),\n defaultSize: sizeShape,\n});\n\nconst suggestionShape = z.looseObject({\n name: z.string().describe('Structure id.'),\n title: z.string(),\n summary: z.string(),\n score: z.number().describe('Relevance to the brief; higher is better.'),\n matched: z.array(z.string()).describe('Why it matched, for explaining.'),\n example: z\n .looseObject({ structure: z.string() })\n .describe('A ready-to-run generate_background request for this brief.'),\n palettes: z\n .array(z.string())\n .describe('Built-in palette ids whose mood fits the brief, best first.'),\n});\n\nconst paletteDescriptionShape = z.looseObject({\n id: z.string().describe('Value to pass as palette.palette.'),\n name: z.string(),\n colors: z.array(z.string()).describe('Hex colours.'),\n mood: z.array(z.string()).describe('Derived mood tags.'),\n lightness: z.number().describe('Average perceived lightness 0-1.'),\n saturation: z.number().describe('Average saturation 0-1.'),\n});\n\nconst paramDocShape = z.looseObject({\n key: z.string().describe('Option key to pass under shape/palette.'),\n group: z.enum(['shape', 'palette', 'frame']),\n title: z.string(),\n type: z.string(),\n description: z.string(),\n default: z.unknown().optional(),\n min: z.number().optional(),\n max: z.number().optional(),\n step: z.number().optional(),\n enum: z.array(z.string()).optional(),\n});\n\nconst structureDescriptionShape = z.looseObject({\n name: z.string(),\n title: z.string(),\n summary: z.string(),\n description: z.string(),\n tags: z.array(z.string()),\n mood: z.array(z.string()),\n useCases: z.array(z.string()),\n tips: z.array(z.string()),\n defaultSize: sizeShape,\n previewUrl: z.string(),\n params: z\n .array(paramDocShape)\n .describe('Every option this structure honours.'),\n});\n\nconst brandKitShape = z.looseObject({\n slug: z.string().describe('Value to pass as brand to generate_background.'),\n name: z.string(),\n colors: z.array(z.string()).describe('Hex colours.'),\n backgroundColor: z.string().optional(),\n defaultStructure: z.string().optional(),\n});\n\nconst generateOutputShape = {\n structure: z.string().describe('Structure that was rendered.'),\n seed: z\n .union([z.string(), z.number()])\n .describe('Seed used; reuse it to reproduce the exact image.'),\n width: z.number().describe('Render width in px.'),\n height: z.number().describe('Render height in px.'),\n format: z.enum(['svg', 'png']).describe('Delivered format.'),\n svgBytes: z.number().describe('Size of the SVG source in bytes.'),\n renderMs: z.number().describe('Render time in milliseconds.'),\n request: z\n .looseObject({ structure: z.string() })\n .describe('Resolved request incl. seed; replay it to reproduce or tweak.'),\n url: z\n .string()\n .optional()\n .describe(\n 'Hosted only: permanent, cacheable image URL in the requested format.',\n ),\n svgUrl: z\n .string()\n .optional()\n .describe('Hosted only: permanent URL of the SVG source.'),\n savedTo: z\n .string()\n .optional()\n .describe('Local only: absolute path the file was written to.'),\n bytes: z.number().optional().describe('Local only: bytes written to disk.'),\n warnings: z\n .array(z.string())\n .optional()\n .describe('Options that were ignored or clamped, and why.'),\n note: z.string().optional().describe('Anything else worth knowing.'),\n};\n\nexport function createMcpServer(options: McpServerOptions = {}): McpServer {\n const { hosted } = options;\n const maxInline = options.maxInlineBytes ?? 600_000;\n\n const server = new McpServer(\n {\n name: 'creatica-backgrounds',\n title: 'Creatica Backgrounds',\n version: ENGINE_VERSION,\n description: SERVER_DESCRIPTION,\n websiteUrl: 'https://creatica.app',\n icons: [\n {\n src: 'https://creatica.app/site/android-chrome-512x512.png',\n mimeType: 'image/png',\n sizes: ['512x512'],\n },\n {\n src: 'https://creatica.app/site/android-chrome-192x192.png',\n mimeType: 'image/png',\n sizes: ['192x192'],\n },\n ],\n },\n {\n instructions: [\n 'Creatica generates abstract, procedural SVG/PNG backgrounds (waves, blobs, bauhaus grids, topo lines, aurora glows…) for websites, slides, social cards and apps.',\n 'Workflow: suggest_backgrounds (or list_backgrounds) with the brief → describe_background for the chosen one → generate_background with a seed. Every render is deterministic: same structure + params + seed = identical image, so keep the seed to reproduce or make on-brand variants.',\n 'For brand colours pass palette.colors as hex. For a curated look pass palette.palette from list_palettes. Prefer PNG when you need a preview; SVG for the final asset.',\n ].join('\\n'),\n },\n );\n\n server.registerTool(\n 'list_backgrounds',\n {\n title: 'List backgrounds',\n description:\n 'Browse the catalog of background structures. Optionally filter by a free-text query, tag or mood word. Returns name, one-line summary, mood, use cases and a preview URL for each.',\n inputSchema: {\n query: z\n .string()\n .optional()\n .describe('Free text, e.g. \"calm waves\" or \"geometric poster\".'),\n mood: z\n .string()\n .optional()\n .describe('A mood word, e.g. dark, playful, minimal, retro.'),\n tag: z\n .string()\n .optional()\n .describe('A style tag, e.g. geometric, organic, grid, gradient.'),\n limit: z\n .number()\n .int()\n .min(1)\n .max(100)\n .optional()\n .describe('Maximum number of results (default 100).'),\n },\n outputSchema: {\n count: z.number().describe('Number of backgrounds returned.'),\n backgrounds: z.array(catalogEntryShape),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ query, mood, tag, limit }) => {\n let items = catalog();\n if (mood)\n items = items.filter((c) =>\n c.mood.some((m) => m.toLowerCase().includes(mood.toLowerCase())),\n );\n if (tag)\n items = items.filter((c) =>\n c.tags.some((t) => t.toLowerCase().includes(tag.toLowerCase())),\n );\n if (query) {\n const ranked = suggest(query, { limit: 100 }).map((s) => s.name);\n items = items\n .filter((c) => ranked.includes(c.name))\n .sort((a, b) => ranked.indexOf(a.name) - ranked.indexOf(b.name));\n }\n items = items.slice(0, limit ?? 100);\n return json({ count: items.length, backgrounds: items });\n },\n );\n\n server.registerTool(\n 'suggest_backgrounds',\n {\n title: 'Suggest backgrounds for a brief',\n description:\n 'Rank structures for a natural-language brief (\"dark aurora glow for an AI landing page\"). Returns why each matched, matching built-in palettes, and a ready-to-run example request for generate_background.',\n inputSchema: {\n brief: z\n .string()\n .min(2)\n .describe(\n 'What the background is for, in plain words: subject, mood, colours, where it goes.',\n ),\n limit: z\n .number()\n .int()\n .min(1)\n .max(20)\n .optional()\n .describe('How many suggestions to return (default 5).'),\n },\n outputSchema: {\n suggestions: z.array(suggestionShape).describe('Best match first.'),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ brief, limit }) =>\n json({ suggestions: suggest(brief, { limit: limit ?? 5 }) }),\n );\n\n server.registerTool(\n 'describe_background',\n {\n title: 'Describe a background',\n description:\n 'Full style card for one structure: what it looks like, mood, where text can go, tips, and every parameter it honours with ranges, defaults and the visual effect of low/high values. Also returns the JSON Schema for a generate request.',\n inputSchema: {\n name: z\n .string()\n .describe('Structure name from list_backgrounds, e.g. \"wave\".'),\n format: z\n .enum(['markdown', 'json'])\n .optional()\n .describe(\n 'markdown (default) is best for reading; json includes the JSON Schema.',\n ),\n },\n outputSchema: {\n name: z.string().describe('Structure id.'),\n format: z\n .enum(['markdown', 'json'])\n .describe('Which form was returned.'),\n card: z\n .string()\n .optional()\n .describe('markdown format: the style card as Markdown.'),\n description: structureDescriptionShape\n .optional()\n .describe('json format: the structured description.'),\n jsonSchema: z\n .looseObject({})\n .optional()\n .describe('json format: JSON Schema for a generate request.'),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ name, format }) => {\n try {\n if (format === 'json')\n return json({\n name,\n format: 'json',\n description: describe(name),\n jsonSchema: jsonSchemaFor(name),\n });\n const card = styleCard(name);\n return {\n content: [{ type: 'text', text: card }],\n structuredContent: { name, format: 'markdown', card },\n };\n } catch (e) {\n return error((e as Error).message);\n }\n },\n );\n\n server.registerTool(\n 'list_palettes',\n {\n title: 'List palettes',\n description:\n 'Built-in curated colour palettes with their hex colours and derived mood tags (dark, pastel, vivid, warm, cool…). Pass the id as palette.palette to generate_background.',\n inputSchema: {\n mood: z\n .string()\n .optional()\n .describe('Filter by a mood tag, e.g. \"dark\" or \"pastel\".'),\n },\n outputSchema: {\n count: z.number().describe('Number of palettes returned.'),\n palettes: z.array(paletteDescriptionShape),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ mood }) => {\n let items = listPalettes();\n if (mood)\n items = items.filter((p) => p.mood.includes(mood.toLowerCase()));\n return json({ count: items.length, palettes: items });\n },\n );\n\n if (hosted?.listBrandKits) {\n server.registerTool(\n 'list_brand_kits',\n {\n title: 'List brand kits',\n description:\n \"Saved brand palettes on this account. Pass a kit's slug as `brand` to generate_background to render on-brand without repeating hex colours.\",\n inputSchema: {},\n outputSchema: {\n brandKits: z\n .array(brandKitShape)\n .describe('Saved kits, may be empty.'),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async () => json({ brandKits: await hosted.listBrandKits!() }),\n );\n }\n\n server.registerTool(\n 'generate_background',\n {\n title: 'Generate a background',\n description: [\n 'Render a background as SVG or PNG. Deterministic: the same structure, options and seed always give the same image, so store the returned seed.',\n hosted\n ? 'Returns a permanent, cacheable image URL you can use directly in <img>/CSS, plus an inline PNG preview.'\n : 'Returns the image inline (PNG previews are visible to you); pass saveTo to also write the file to disk.',\n 'Call describe_background first to learn which shape options the structure honours.',\n ].join(' '),\n inputSchema: {\n structure: z.string().describe('Structure name, e.g. \"wave\".'),\n seed: z\n .string()\n .optional()\n .describe(\n 'Any string. Omit for a fresh random seed (returned in the result).',\n ),\n width: z\n .number()\n .int()\n .min(1)\n .max(8192)\n .optional()\n .describe(\n \"Frame width in px. Defaults to the structure's native size (see describe_background).\",\n ),\n height: z\n .number()\n .int()\n .min(1)\n .max(8192)\n .optional()\n .describe('Frame height in px. Defaults to the native size.'),\n palette: paletteShape\n .optional()\n .describe(\n 'Colour choices: explicit hex colours, a built-in palette id, or a primary colour to derive from.',\n ),\n shape: shapeShape.optional(),\n format: z\n .enum(['svg', 'png'])\n .optional()\n .describe(\n 'png (default) returns a viewable image; svg returns the vector source.',\n ),\n pngWidth: z\n .number()\n .int()\n .min(16)\n .max(8192)\n .optional()\n .describe(\n 'Raster width for PNG output/preview. Defaults to the render width, capped for inline previews.',\n ),\n ...(hosted?.listBrandKits\n ? {\n brand: z\n .string()\n .optional()\n .describe(\n 'Slug of a saved brand kit (see list_brand_kits); its colours become the palette baseline.',\n ),\n }\n : {}),\n ...(hosted\n ? {}\n : {\n saveTo: z\n .string()\n .optional()\n .describe(\n 'Local file path to write the result to (extension decides svg/png if format is omitted).',\n ),\n }),\n },\n outputSchema: generateOutputShape,\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async (args) => {\n const input = { ...(args as Record<string, unknown>) };\n const brandSlug =\n typeof input.brand === 'string' ? input.brand : undefined;\n delete input.brand;\n if (brandSlug) {\n const kit = (await hosted?.listBrandKits?.())?.find(\n (k) => k.slug === brandSlug,\n );\n if (!kit)\n return error(\n `Unknown brand kit \"${brandSlug}\". Call list_brand_kits to see the available ones.`,\n );\n const basePalette: Record<string, unknown> = { colors: kit.colors };\n if (kit.backgroundColor)\n basePalette.backgroundColor = kit.backgroundColor;\n input.palette = {\n ...basePalette,\n ...((input.palette as Record<string, unknown>) ?? {}),\n };\n if (!input.structure && kit.defaultStructure)\n input.structure = kit.defaultStructure;\n }\n let normalized;\n try {\n normalized = normalizeRequest(input);\n } catch (e) {\n return error((e as Error).message);\n }\n const { request, warnings } = normalized;\n const saveTo = hosted ? undefined : (args as { saveTo?: string }).saveTo;\n const format: 'svg' | 'png' =\n (args.format as 'svg' | 'png' | undefined) ??\n (saveTo?.toLowerCase().endsWith('.svg') ? 'svg' : 'png');\n\n try {\n await hosted?.beforeRender?.(request);\n } catch (e) {\n return error((e as Error).message);\n }\n const result = await renderSvg(request);\n await hosted?.onRender?.(result, format);\n\n const content: CallToolResult['content'] = [];\n const meta: Record<string, unknown> = {\n structure: result.structure,\n seed: result.seed,\n width: result.width,\n height: result.height,\n format,\n svgBytes: result.svg.length,\n renderMs: result.durationMs,\n // Echo the resolved options so the caller can reproduce or tweak.\n request: { ...request, seed: result.seed },\n ...(warnings.length ? { warnings } : {}),\n };\n\n if (hosted) {\n meta.url = renderUrl(\n hosted.publicBaseUrl,\n { ...request, seed: result.seed },\n format,\n );\n meta.svgUrl = renderUrl(\n hosted.publicBaseUrl,\n { ...request, seed: result.seed },\n 'svg',\n );\n }\n\n if (saveTo) {\n const path = resolve(saveTo);\n await mkdir(dirname(path), { recursive: true });\n const bytes =\n format === 'png'\n ? await renderPng(result.svg, {\n width: args.pngWidth ?? result.width,\n })\n : Buffer.from(result.svg, 'utf8');\n await writeFile(path, bytes);\n meta.savedTo = path;\n meta.bytes = bytes.length;\n }\n\n if (format === 'svg') {\n if (result.svg.length <= maxInline && !saveTo) {\n content.push({\n type: 'resource',\n resource: {\n uri: `creatica://render/${result.structure}/${result.seed}.svg`,\n mimeType: 'image/svg+xml',\n text: result.svg,\n },\n });\n } else if (!saveTo && !hosted) {\n meta.note = `SVG is ${result.svg.length} bytes; pass saveTo to write it to disk instead of returning it inline.`;\n }\n }\n\n // Always attach a PNG preview so the model can see what it made. Keep\n // it modest: previews are for judging composition, not final delivery.\n const previewWidth = Math.min(args.pngWidth ?? 800, 1200);\n const png = await renderPng(result.svg, { width: previewWidth });\n if (png.length <= maxInline) {\n content.push({\n type: 'image',\n data: png.toString('base64'),\n mimeType: 'image/png',\n });\n } else {\n const small = await renderPng(result.svg, { width: 480 });\n content.push({\n type: 'image',\n data: small.toString('base64'),\n mimeType: 'image/png',\n });\n meta.note = `Preview downscaled to 480px (full PNG would be ${png.length} bytes).`;\n }\n\n content.unshift({ type: 'text', text: JSON.stringify(meta, null, 2) });\n return { content, structuredContent: meta };\n },\n );\n\n return server;\n}\n\nexport function renderUrl(\n baseUrl: string,\n req: RenderRequest,\n format: 'svg' | 'png',\n): string {\n const u = new URL(\n `/v1/render/${encodeURIComponent(req.structure)}.${format}`,\n baseUrl,\n );\n if (req.seed !== undefined) u.searchParams.set('seed', String(req.seed));\n if (req.width) u.searchParams.set('width', String(req.width));\n if (req.height) u.searchParams.set('height', String(req.height));\n for (const [k, v] of Object.entries(req.palette ?? {})) {\n if (v === undefined) continue;\n u.searchParams.set(k, Array.isArray(v) ? v.join(',') : String(v));\n }\n for (const [k, v] of Object.entries(req.shape ?? {})) {\n if (v !== undefined) u.searchParams.set(k, String(v));\n }\n return u.toString();\n}\n\nfunction json(value: Record<string, unknown>): CallToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(value, null, 2) }],\n structuredContent: value,\n };\n}\nfunction error(message: string): CallToolResult {\n return { isError: true, content: [{ type: 'text', text: message }] };\n}\n"],"mappings":"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","names":[]}
|
package/dist/stdio.js
CHANGED
|
@@ -33,22 +33,122 @@ var paletteShape = z.object({
|
|
|
33
33
|
palette: z.string().optional().describe(
|
|
34
34
|
'Id of a built-in palette from list_palettes, e.g. "underTheSea".'
|
|
35
35
|
),
|
|
36
|
-
primaryColor: hexColor.optional(),
|
|
37
|
-
secondaryColor: hexColor.optional(),
|
|
38
|
-
backgroundColor: hexColor.optional(),
|
|
36
|
+
primaryColor: hexColor.optional().describe("Main hex colour when not passing colors/palette."),
|
|
37
|
+
secondaryColor: hexColor.optional().describe("Second hex colour for two-tone structures."),
|
|
38
|
+
backgroundColor: hexColor.optional().describe("Hex colour of the canvas behind the shapes."),
|
|
39
39
|
gradientColor: z.boolean().optional().describe("Gradient fills instead of flat colour."),
|
|
40
40
|
useMultiColor: z.boolean().optional().describe("false = everything in primaryColor."),
|
|
41
41
|
colorSchemeType: z.enum(["Mono", "Tetrade", "Analogic"]).optional().describe("Derive a palette from primaryColor."),
|
|
42
|
-
opaqueColors: z.boolean().optional()
|
|
42
|
+
opaqueColors: z.boolean().optional().describe("true = no transparency in the shape fills.")
|
|
43
43
|
}).strict();
|
|
44
44
|
var shapeShape = z.record(z.string(), z.union([z.number(), z.boolean(), z.string()])).describe(
|
|
45
45
|
"Structure-specific options; call describe_background for the exact keys, ranges and what they do."
|
|
46
46
|
);
|
|
47
|
+
var SERVER_DESCRIPTION = "Deterministic procedural SVG/PNG backgrounds for websites, slides, social cards and apps: 58 generators, curated palettes and brand kits, every render reproducible from its seed.";
|
|
48
|
+
var sizeShape = z.object({
|
|
49
|
+
width: z.number().describe("Native frame width in px."),
|
|
50
|
+
height: z.number().describe("Native frame height in px.")
|
|
51
|
+
});
|
|
52
|
+
var catalogEntryShape = z.looseObject({
|
|
53
|
+
name: z.string().describe("Structure id for describe_background / generate_background."),
|
|
54
|
+
title: z.string().describe("Human-readable name."),
|
|
55
|
+
summary: z.string().describe("One-line description of the look."),
|
|
56
|
+
tags: z.array(z.string()).describe("Style tags, e.g. geometric, organic."),
|
|
57
|
+
mood: z.array(z.string()).describe("Mood words, e.g. calm, playful, dark."),
|
|
58
|
+
useCases: z.array(z.string()).describe("Where it works well."),
|
|
59
|
+
previewUrl: z.string().describe("Preview image of the default render."),
|
|
60
|
+
defaultSize: sizeShape
|
|
61
|
+
});
|
|
62
|
+
var suggestionShape = z.looseObject({
|
|
63
|
+
name: z.string().describe("Structure id."),
|
|
64
|
+
title: z.string(),
|
|
65
|
+
summary: z.string(),
|
|
66
|
+
score: z.number().describe("Relevance to the brief; higher is better."),
|
|
67
|
+
matched: z.array(z.string()).describe("Why it matched, for explaining."),
|
|
68
|
+
example: z.looseObject({ structure: z.string() }).describe("A ready-to-run generate_background request for this brief."),
|
|
69
|
+
palettes: z.array(z.string()).describe("Built-in palette ids whose mood fits the brief, best first.")
|
|
70
|
+
});
|
|
71
|
+
var paletteDescriptionShape = z.looseObject({
|
|
72
|
+
id: z.string().describe("Value to pass as palette.palette."),
|
|
73
|
+
name: z.string(),
|
|
74
|
+
colors: z.array(z.string()).describe("Hex colours."),
|
|
75
|
+
mood: z.array(z.string()).describe("Derived mood tags."),
|
|
76
|
+
lightness: z.number().describe("Average perceived lightness 0-1."),
|
|
77
|
+
saturation: z.number().describe("Average saturation 0-1.")
|
|
78
|
+
});
|
|
79
|
+
var paramDocShape = z.looseObject({
|
|
80
|
+
key: z.string().describe("Option key to pass under shape/palette."),
|
|
81
|
+
group: z.enum(["shape", "palette", "frame"]),
|
|
82
|
+
title: z.string(),
|
|
83
|
+
type: z.string(),
|
|
84
|
+
description: z.string(),
|
|
85
|
+
default: z.unknown().optional(),
|
|
86
|
+
min: z.number().optional(),
|
|
87
|
+
max: z.number().optional(),
|
|
88
|
+
step: z.number().optional(),
|
|
89
|
+
enum: z.array(z.string()).optional()
|
|
90
|
+
});
|
|
91
|
+
var structureDescriptionShape = z.looseObject({
|
|
92
|
+
name: z.string(),
|
|
93
|
+
title: z.string(),
|
|
94
|
+
summary: z.string(),
|
|
95
|
+
description: z.string(),
|
|
96
|
+
tags: z.array(z.string()),
|
|
97
|
+
mood: z.array(z.string()),
|
|
98
|
+
useCases: z.array(z.string()),
|
|
99
|
+
tips: z.array(z.string()),
|
|
100
|
+
defaultSize: sizeShape,
|
|
101
|
+
previewUrl: z.string(),
|
|
102
|
+
params: z.array(paramDocShape).describe("Every option this structure honours.")
|
|
103
|
+
});
|
|
104
|
+
var brandKitShape = z.looseObject({
|
|
105
|
+
slug: z.string().describe("Value to pass as brand to generate_background."),
|
|
106
|
+
name: z.string(),
|
|
107
|
+
colors: z.array(z.string()).describe("Hex colours."),
|
|
108
|
+
backgroundColor: z.string().optional(),
|
|
109
|
+
defaultStructure: z.string().optional()
|
|
110
|
+
});
|
|
111
|
+
var generateOutputShape = {
|
|
112
|
+
structure: z.string().describe("Structure that was rendered."),
|
|
113
|
+
seed: z.union([z.string(), z.number()]).describe("Seed used; reuse it to reproduce the exact image."),
|
|
114
|
+
width: z.number().describe("Render width in px."),
|
|
115
|
+
height: z.number().describe("Render height in px."),
|
|
116
|
+
format: z.enum(["svg", "png"]).describe("Delivered format."),
|
|
117
|
+
svgBytes: z.number().describe("Size of the SVG source in bytes."),
|
|
118
|
+
renderMs: z.number().describe("Render time in milliseconds."),
|
|
119
|
+
request: z.looseObject({ structure: z.string() }).describe("Resolved request incl. seed; replay it to reproduce or tweak."),
|
|
120
|
+
url: z.string().optional().describe(
|
|
121
|
+
"Hosted only: permanent, cacheable image URL in the requested format."
|
|
122
|
+
),
|
|
123
|
+
svgUrl: z.string().optional().describe("Hosted only: permanent URL of the SVG source."),
|
|
124
|
+
savedTo: z.string().optional().describe("Local only: absolute path the file was written to."),
|
|
125
|
+
bytes: z.number().optional().describe("Local only: bytes written to disk."),
|
|
126
|
+
warnings: z.array(z.string()).optional().describe("Options that were ignored or clamped, and why."),
|
|
127
|
+
note: z.string().optional().describe("Anything else worth knowing.")
|
|
128
|
+
};
|
|
47
129
|
function createMcpServer(options = {}) {
|
|
48
130
|
const { hosted } = options;
|
|
49
131
|
const maxInline = options.maxInlineBytes ?? 6e5;
|
|
50
132
|
const server2 = new McpServer(
|
|
51
|
-
{
|
|
133
|
+
{
|
|
134
|
+
name: "creatica-backgrounds",
|
|
135
|
+
title: "Creatica Backgrounds",
|
|
136
|
+
version: ENGINE_VERSION,
|
|
137
|
+
description: SERVER_DESCRIPTION,
|
|
138
|
+
websiteUrl: "https://creatica.app",
|
|
139
|
+
icons: [
|
|
140
|
+
{
|
|
141
|
+
src: "https://creatica.app/site/android-chrome-512x512.png",
|
|
142
|
+
mimeType: "image/png",
|
|
143
|
+
sizes: ["512x512"]
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
src: "https://creatica.app/site/android-chrome-192x192.png",
|
|
147
|
+
mimeType: "image/png",
|
|
148
|
+
sizes: ["192x192"]
|
|
149
|
+
}
|
|
150
|
+
]
|
|
151
|
+
},
|
|
52
152
|
{
|
|
53
153
|
instructions: [
|
|
54
154
|
"Creatica generates abstract, procedural SVG/PNG backgrounds (waves, blobs, bauhaus grids, topo lines, aurora glows\u2026) for websites, slides, social cards and apps.",
|
|
@@ -65,8 +165,12 @@ function createMcpServer(options = {}) {
|
|
|
65
165
|
inputSchema: {
|
|
66
166
|
query: z.string().optional().describe('Free text, e.g. "calm waves" or "geometric poster".'),
|
|
67
167
|
mood: z.string().optional().describe("A mood word, e.g. dark, playful, minimal, retro."),
|
|
68
|
-
tag: z.string().optional(),
|
|
69
|
-
limit: z.number().int().min(1).max(100).optional()
|
|
168
|
+
tag: z.string().optional().describe("A style tag, e.g. geometric, organic, grid, gradient."),
|
|
169
|
+
limit: z.number().int().min(1).max(100).optional().describe("Maximum number of results (default 100).")
|
|
170
|
+
},
|
|
171
|
+
outputSchema: {
|
|
172
|
+
count: z.number().describe("Number of backgrounds returned."),
|
|
173
|
+
backgrounds: z.array(catalogEntryShape)
|
|
70
174
|
},
|
|
71
175
|
annotations: {
|
|
72
176
|
readOnlyHint: true,
|
|
@@ -98,8 +202,13 @@ function createMcpServer(options = {}) {
|
|
|
98
202
|
title: "Suggest backgrounds for a brief",
|
|
99
203
|
description: 'Rank structures for a natural-language brief ("dark aurora glow for an AI landing page"). Returns why each matched, matching built-in palettes, and a ready-to-run example request for generate_background.',
|
|
100
204
|
inputSchema: {
|
|
101
|
-
brief: z.string().min(2)
|
|
102
|
-
|
|
205
|
+
brief: z.string().min(2).describe(
|
|
206
|
+
"What the background is for, in plain words: subject, mood, colours, where it goes."
|
|
207
|
+
),
|
|
208
|
+
limit: z.number().int().min(1).max(20).optional().describe("How many suggestions to return (default 5).")
|
|
209
|
+
},
|
|
210
|
+
outputSchema: {
|
|
211
|
+
suggestions: z.array(suggestionShape).describe("Best match first.")
|
|
103
212
|
},
|
|
104
213
|
annotations: {
|
|
105
214
|
readOnlyHint: true,
|
|
@@ -120,6 +229,13 @@ function createMcpServer(options = {}) {
|
|
|
120
229
|
"markdown (default) is best for reading; json includes the JSON Schema."
|
|
121
230
|
)
|
|
122
231
|
},
|
|
232
|
+
outputSchema: {
|
|
233
|
+
name: z.string().describe("Structure id."),
|
|
234
|
+
format: z.enum(["markdown", "json"]).describe("Which form was returned."),
|
|
235
|
+
card: z.string().optional().describe("markdown format: the style card as Markdown."),
|
|
236
|
+
description: structureDescriptionShape.optional().describe("json format: the structured description."),
|
|
237
|
+
jsonSchema: z.looseObject({}).optional().describe("json format: JSON Schema for a generate request.")
|
|
238
|
+
},
|
|
123
239
|
annotations: {
|
|
124
240
|
readOnlyHint: true,
|
|
125
241
|
idempotentHint: true,
|
|
@@ -129,8 +245,17 @@ function createMcpServer(options = {}) {
|
|
|
129
245
|
async ({ name, format }) => {
|
|
130
246
|
try {
|
|
131
247
|
if (format === "json")
|
|
132
|
-
return json({
|
|
133
|
-
|
|
248
|
+
return json({
|
|
249
|
+
name,
|
|
250
|
+
format: "json",
|
|
251
|
+
description: describe(name),
|
|
252
|
+
jsonSchema: jsonSchemaFor(name)
|
|
253
|
+
});
|
|
254
|
+
const card = styleCard(name);
|
|
255
|
+
return {
|
|
256
|
+
content: [{ type: "text", text: card }],
|
|
257
|
+
structuredContent: { name, format: "markdown", card }
|
|
258
|
+
};
|
|
134
259
|
} catch (e) {
|
|
135
260
|
return error(e.message);
|
|
136
261
|
}
|
|
@@ -144,6 +269,10 @@ function createMcpServer(options = {}) {
|
|
|
144
269
|
inputSchema: {
|
|
145
270
|
mood: z.string().optional().describe('Filter by a mood tag, e.g. "dark" or "pastel".')
|
|
146
271
|
},
|
|
272
|
+
outputSchema: {
|
|
273
|
+
count: z.number().describe("Number of palettes returned."),
|
|
274
|
+
palettes: z.array(paletteDescriptionShape)
|
|
275
|
+
},
|
|
147
276
|
annotations: {
|
|
148
277
|
readOnlyHint: true,
|
|
149
278
|
idempotentHint: true,
|
|
@@ -164,6 +293,9 @@ function createMcpServer(options = {}) {
|
|
|
164
293
|
title: "List brand kits",
|
|
165
294
|
description: "Saved brand palettes on this account. Pass a kit's slug as `brand` to generate_background to render on-brand without repeating hex colours.",
|
|
166
295
|
inputSchema: {},
|
|
296
|
+
outputSchema: {
|
|
297
|
+
brandKits: z.array(brandKitShape).describe("Saved kits, may be empty.")
|
|
298
|
+
},
|
|
167
299
|
annotations: {
|
|
168
300
|
readOnlyHint: true,
|
|
169
301
|
idempotentHint: true,
|
|
@@ -187,9 +319,13 @@ function createMcpServer(options = {}) {
|
|
|
187
319
|
seed: z.string().optional().describe(
|
|
188
320
|
"Any string. Omit for a fresh random seed (returned in the result)."
|
|
189
321
|
),
|
|
190
|
-
width: z.number().int().min(1).max(8192).optional()
|
|
191
|
-
|
|
192
|
-
|
|
322
|
+
width: z.number().int().min(1).max(8192).optional().describe(
|
|
323
|
+
"Frame width in px. Defaults to the structure's native size (see describe_background)."
|
|
324
|
+
),
|
|
325
|
+
height: z.number().int().min(1).max(8192).optional().describe("Frame height in px. Defaults to the native size."),
|
|
326
|
+
palette: paletteShape.optional().describe(
|
|
327
|
+
"Colour choices: explicit hex colours, a built-in palette id, or a primary colour to derive from."
|
|
328
|
+
),
|
|
193
329
|
shape: shapeShape.optional(),
|
|
194
330
|
format: z.enum(["svg", "png"]).optional().describe(
|
|
195
331
|
"png (default) returns a viewable image; svg returns the vector source."
|
|
@@ -208,6 +344,7 @@ function createMcpServer(options = {}) {
|
|
|
208
344
|
)
|
|
209
345
|
}
|
|
210
346
|
},
|
|
347
|
+
outputSchema: generateOutputShape,
|
|
211
348
|
annotations: {
|
|
212
349
|
readOnlyHint: true,
|
|
213
350
|
idempotentHint: true,
|
|
@@ -280,7 +417,9 @@ function createMcpServer(options = {}) {
|
|
|
280
417
|
if (saveTo) {
|
|
281
418
|
const path = resolve(saveTo);
|
|
282
419
|
await mkdir(dirname(path), { recursive: true });
|
|
283
|
-
const bytes = format === "png" ? await renderPng(result.svg, {
|
|
420
|
+
const bytes = format === "png" ? await renderPng(result.svg, {
|
|
421
|
+
width: args.pngWidth ?? result.width
|
|
422
|
+
}) : Buffer.from(result.svg, "utf8");
|
|
284
423
|
await writeFile(path, bytes);
|
|
285
424
|
meta.savedTo = path;
|
|
286
425
|
meta.bytes = bytes.length;
|
|
@@ -317,7 +456,7 @@ function createMcpServer(options = {}) {
|
|
|
317
456
|
meta.note = `Preview downscaled to 480px (full PNG would be ${png.length} bytes).`;
|
|
318
457
|
}
|
|
319
458
|
content.unshift({ type: "text", text: JSON.stringify(meta, null, 2) });
|
|
320
|
-
return { content };
|
|
459
|
+
return { content, structuredContent: meta };
|
|
321
460
|
}
|
|
322
461
|
);
|
|
323
462
|
return server2;
|
|
@@ -340,10 +479,10 @@ function renderUrl(baseUrl, req, format) {
|
|
|
340
479
|
return u.toString();
|
|
341
480
|
}
|
|
342
481
|
function json(value) {
|
|
343
|
-
return {
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
|
|
482
|
+
return {
|
|
483
|
+
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
484
|
+
structuredContent: value
|
|
485
|
+
};
|
|
347
486
|
}
|
|
348
487
|
function error(message) {
|
|
349
488
|
return { isError: true, content: [{ type: "text", text: message }] };
|
package/dist/stdio.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stdio.ts","../src/server.ts"],"sourcesContent":["#!/usr/bin/env node\n// Local MCP server over stdio. Runs the whole engine on this machine; no\n// network, no account. Add to Claude Desktop / Claude Code / Cursor as:\n// { \"command\": \"npx\", \"args\": [\"-y\", \"@creatica/mcp\"] }\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createMcpServer } from './server.js';\n\nif (process.argv.includes('--help') || process.argv.includes('-h')) {\n process.stdout.write(\n [\n 'creatica-mcp — Model Context Protocol server for Creatica backgrounds (stdio).',\n '',\n 'Tools: list_backgrounds, suggest_backgrounds, describe_background, list_palettes, generate_background',\n '',\n 'Claude Code: claude mcp add creatica -- npx -y @creatica/mcp',\n 'Claude Desktop: add {\"mcpServers\":{\"creatica\":{\"command\":\"npx\",\"args\":[\"-y\",\"@creatica/mcp\"]}}}',\n '',\n ].join('\\n'),\n );\n process.exit(0);\n}\n\nconst server = createMcpServer();\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","import {\n ENGINE_VERSION,\n renderPng,\n renderSvg,\n type RenderRequest,\n type RenderResult,\n} from '@creatica/engine';\nimport {\n catalog,\n describe,\n jsonSchemaFor,\n listPalettes,\n normalizeRequest,\n styleCard,\n suggest,\n} from '@creatica/schema';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, resolve } from 'node:path';\nimport { z } from 'zod';\n\nexport interface BrandKitSummary {\n slug: string;\n name: string;\n colors: string[];\n backgroundColor?: string;\n defaultStructure?: string;\n}\n\nexport interface HostedOptions {\n /** Public origin of the API, e.g. https://api.creatica.app */\n publicBaseUrl: string;\n /** Brand kits of the calling account; enables `brand` on generate and list_brand_kits. */\n listBrandKits?: () => Promise<BrandKitSummary[]>;\n /**\n * Called before rendering. Throw to refuse (e.g. quota exhausted); the\n * message is returned to the model as a tool error, so discovery tools keep\n * working and the model can explain what to do next.\n */\n beforeRender?: (request: RenderRequest) => void | Promise<void>;\n /** Called after a successful render so the host can meter usage. */\n onRender?: (\n result: RenderResult,\n format: 'svg' | 'png',\n ) => void | Promise<void>;\n}\n\nexport interface McpServerOptions {\n /**\n * Local mode (default) renders in-process and can write files to disk.\n * Hosted mode additionally returns permanent, cacheable image URLs and\n * never touches the filesystem.\n */\n hosted?: HostedOptions;\n /** Inline PNG previews above this many bytes are downscaled. */\n maxInlineBytes?: number;\n}\n\nconst hexColor = z\n .string()\n .regex(\n /^#?([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/,\n 'hex colour like #1e90ff',\n );\n\nconst paletteShape = z\n .object({\n colors: z\n .array(hexColor)\n .min(1)\n .max(12)\n .optional()\n .describe(\n 'Explicit hex colours to draw from (best for brand matching). Overrides palette/scheme.',\n ),\n palette: z\n .string()\n .optional()\n .describe(\n 'Id of a built-in palette from list_palettes, e.g. \"underTheSea\".',\n ),\n primaryColor: hexColor.optional(),\n secondaryColor: hexColor.optional(),\n backgroundColor: hexColor.optional(),\n gradientColor: z\n .boolean()\n .optional()\n .describe('Gradient fills instead of flat colour.'),\n useMultiColor: z\n .boolean()\n .optional()\n .describe('false = everything in primaryColor.'),\n colorSchemeType: z\n .enum(['Mono', 'Tetrade', 'Analogic'])\n .optional()\n .describe('Derive a palette from primaryColor.'),\n opaqueColors: z.boolean().optional(),\n })\n .strict();\n\n// Shape options are a loose record: which keys apply depends on the structure\n// (see describe_background). normalizeRequest() validates against that.\nconst shapeShape = z\n .record(z.string(), z.union([z.number(), z.boolean(), z.string()]))\n .describe(\n 'Structure-specific options; call describe_background for the exact keys, ranges and what they do.',\n );\n\nexport function createMcpServer(options: McpServerOptions = {}): McpServer {\n const { hosted } = options;\n const maxInline = options.maxInlineBytes ?? 600_000;\n\n const server = new McpServer(\n { name: 'creatica-backgrounds', version: ENGINE_VERSION },\n {\n instructions: [\n 'Creatica generates abstract, procedural SVG/PNG backgrounds (waves, blobs, bauhaus grids, topo lines, aurora glows…) for websites, slides, social cards and apps.',\n 'Workflow: suggest_backgrounds (or list_backgrounds) with the brief → describe_background for the chosen one → generate_background with a seed. Every render is deterministic: same structure + params + seed = identical image, so keep the seed to reproduce or make on-brand variants.',\n 'For brand colours pass palette.colors as hex. For a curated look pass palette.palette from list_palettes. Prefer PNG when you need a preview; SVG for the final asset.',\n ].join('\\n'),\n },\n );\n\n server.registerTool(\n 'list_backgrounds',\n {\n title: 'List backgrounds',\n description:\n 'Browse the catalog of background structures. Optionally filter by a free-text query, tag or mood word. Returns name, one-line summary, mood, use cases and a preview URL for each.',\n inputSchema: {\n query: z\n .string()\n .optional()\n .describe('Free text, e.g. \"calm waves\" or \"geometric poster\".'),\n mood: z\n .string()\n .optional()\n .describe('A mood word, e.g. dark, playful, minimal, retro.'),\n tag: z.string().optional(),\n limit: z.number().int().min(1).max(100).optional(),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ query, mood, tag, limit }) => {\n let items = catalog();\n if (mood)\n items = items.filter((c) =>\n c.mood.some((m) => m.toLowerCase().includes(mood.toLowerCase())),\n );\n if (tag)\n items = items.filter((c) =>\n c.tags.some((t) => t.toLowerCase().includes(tag.toLowerCase())),\n );\n if (query) {\n const ranked = suggest(query, { limit: 100 }).map((s) => s.name);\n items = items\n .filter((c) => ranked.includes(c.name))\n .sort((a, b) => ranked.indexOf(a.name) - ranked.indexOf(b.name));\n }\n items = items.slice(0, limit ?? 100);\n return json({ count: items.length, backgrounds: items });\n },\n );\n\n server.registerTool(\n 'suggest_backgrounds',\n {\n title: 'Suggest backgrounds for a brief',\n description:\n 'Rank structures for a natural-language brief (\"dark aurora glow for an AI landing page\"). Returns why each matched, matching built-in palettes, and a ready-to-run example request for generate_background.',\n inputSchema: {\n brief: z.string().min(2),\n limit: z.number().int().min(1).max(20).optional(),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ brief, limit }) =>\n json({ suggestions: suggest(brief, { limit: limit ?? 5 }) }),\n );\n\n server.registerTool(\n 'describe_background',\n {\n title: 'Describe a background',\n description:\n 'Full style card for one structure: what it looks like, mood, where text can go, tips, and every parameter it honours with ranges, defaults and the visual effect of low/high values. Also returns the JSON Schema for a generate request.',\n inputSchema: {\n name: z\n .string()\n .describe('Structure name from list_backgrounds, e.g. \"wave\".'),\n format: z\n .enum(['markdown', 'json'])\n .optional()\n .describe(\n 'markdown (default) is best for reading; json includes the JSON Schema.',\n ),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ name, format }) => {\n try {\n if (format === 'json')\n return json({ ...describe(name), jsonSchema: jsonSchemaFor(name) });\n return text(styleCard(name));\n } catch (e) {\n return error((e as Error).message);\n }\n },\n );\n\n server.registerTool(\n 'list_palettes',\n {\n title: 'List palettes',\n description:\n 'Built-in curated colour palettes with their hex colours and derived mood tags (dark, pastel, vivid, warm, cool…). Pass the id as palette.palette to generate_background.',\n inputSchema: {\n mood: z\n .string()\n .optional()\n .describe('Filter by a mood tag, e.g. \"dark\" or \"pastel\".'),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ mood }) => {\n let items = listPalettes();\n if (mood)\n items = items.filter((p) => p.mood.includes(mood.toLowerCase()));\n return json({ count: items.length, palettes: items });\n },\n );\n\n if (hosted?.listBrandKits) {\n server.registerTool(\n 'list_brand_kits',\n {\n title: 'List brand kits',\n description:\n \"Saved brand palettes on this account. Pass a kit's slug as `brand` to generate_background to render on-brand without repeating hex colours.\",\n inputSchema: {},\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async () => json({ brandKits: await hosted.listBrandKits!() }),\n );\n }\n\n server.registerTool(\n 'generate_background',\n {\n title: 'Generate a background',\n description: [\n 'Render a background as SVG or PNG. Deterministic: the same structure, options and seed always give the same image, so store the returned seed.',\n hosted\n ? 'Returns a permanent, cacheable image URL you can use directly in <img>/CSS, plus an inline PNG preview.'\n : 'Returns the image inline (PNG previews are visible to you); pass saveTo to also write the file to disk.',\n 'Call describe_background first to learn which shape options the structure honours.',\n ].join(' '),\n inputSchema: {\n structure: z.string().describe('Structure name, e.g. \"wave\".'),\n seed: z\n .string()\n .optional()\n .describe(\n 'Any string. Omit for a fresh random seed (returned in the result).',\n ),\n width: z.number().int().min(1).max(8192).optional(),\n height: z.number().int().min(1).max(8192).optional(),\n palette: paletteShape.optional(),\n shape: shapeShape.optional(),\n format: z\n .enum(['svg', 'png'])\n .optional()\n .describe(\n 'png (default) returns a viewable image; svg returns the vector source.',\n ),\n pngWidth: z\n .number()\n .int()\n .min(16)\n .max(8192)\n .optional()\n .describe(\n 'Raster width for PNG output/preview. Defaults to the render width, capped for inline previews.',\n ),\n ...(hosted?.listBrandKits\n ? {\n brand: z\n .string()\n .optional()\n .describe(\n 'Slug of a saved brand kit (see list_brand_kits); its colours become the palette baseline.',\n ),\n }\n : {}),\n ...(hosted\n ? {}\n : {\n saveTo: z\n .string()\n .optional()\n .describe(\n 'Local file path to write the result to (extension decides svg/png if format is omitted).',\n ),\n }),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async (args) => {\n const input = { ...(args as Record<string, unknown>) };\n const brandSlug =\n typeof input.brand === 'string' ? input.brand : undefined;\n delete input.brand;\n if (brandSlug) {\n const kit = (await hosted?.listBrandKits?.())?.find(\n (k) => k.slug === brandSlug,\n );\n if (!kit)\n return error(\n `Unknown brand kit \"${brandSlug}\". Call list_brand_kits to see the available ones.`,\n );\n const basePalette: Record<string, unknown> = { colors: kit.colors };\n if (kit.backgroundColor)\n basePalette.backgroundColor = kit.backgroundColor;\n input.palette = {\n ...basePalette,\n ...((input.palette as Record<string, unknown>) ?? {}),\n };\n if (!input.structure && kit.defaultStructure)\n input.structure = kit.defaultStructure;\n }\n let normalized;\n try {\n normalized = normalizeRequest(input);\n } catch (e) {\n return error((e as Error).message);\n }\n const { request, warnings } = normalized;\n const saveTo = hosted ? undefined : (args as { saveTo?: string }).saveTo;\n const format: 'svg' | 'png' =\n (args.format as 'svg' | 'png' | undefined) ??\n (saveTo?.toLowerCase().endsWith('.svg') ? 'svg' : 'png');\n\n try {\n await hosted?.beforeRender?.(request);\n } catch (e) {\n return error((e as Error).message);\n }\n const result = await renderSvg(request);\n await hosted?.onRender?.(result, format);\n\n const content: CallToolResult['content'] = [];\n const meta: Record<string, unknown> = {\n structure: result.structure,\n seed: result.seed,\n width: result.width,\n height: result.height,\n format,\n svgBytes: result.svg.length,\n renderMs: result.durationMs,\n // Echo the resolved options so the caller can reproduce or tweak.\n request: { ...request, seed: result.seed },\n ...(warnings.length ? { warnings } : {}),\n };\n\n if (hosted) {\n meta.url = renderUrl(\n hosted.publicBaseUrl,\n { ...request, seed: result.seed },\n format,\n );\n meta.svgUrl = renderUrl(\n hosted.publicBaseUrl,\n { ...request, seed: result.seed },\n 'svg',\n );\n }\n\n if (saveTo) {\n const path = resolve(saveTo);\n await mkdir(dirname(path), { recursive: true });\n const bytes =\n format === 'png'\n ? await renderPng(result.svg, { width: args.pngWidth ?? result.width })\n : Buffer.from(result.svg, 'utf8');\n await writeFile(path, bytes);\n meta.savedTo = path;\n meta.bytes = bytes.length;\n }\n\n if (format === 'svg') {\n if (result.svg.length <= maxInline && !saveTo) {\n content.push({\n type: 'resource',\n resource: {\n uri: `creatica://render/${result.structure}/${result.seed}.svg`,\n mimeType: 'image/svg+xml',\n text: result.svg,\n },\n });\n } else if (!saveTo && !hosted) {\n meta.note = `SVG is ${result.svg.length} bytes; pass saveTo to write it to disk instead of returning it inline.`;\n }\n }\n\n // Always attach a PNG preview so the model can see what it made. Keep\n // it modest: previews are for judging composition, not final delivery.\n const previewWidth = Math.min(args.pngWidth ?? 800, 1200);\n const png = await renderPng(result.svg, { width: previewWidth });\n if (png.length <= maxInline) {\n content.push({\n type: 'image',\n data: png.toString('base64'),\n mimeType: 'image/png',\n });\n } else {\n const small = await renderPng(result.svg, { width: 480 });\n content.push({\n type: 'image',\n data: small.toString('base64'),\n mimeType: 'image/png',\n });\n meta.note = `Preview downscaled to 480px (full PNG would be ${png.length} bytes).`;\n }\n\n content.unshift({ type: 'text', text: JSON.stringify(meta, null, 2) });\n return { content };\n },\n );\n\n return server;\n}\n\nexport function renderUrl(\n baseUrl: string,\n req: RenderRequest,\n format: 'svg' | 'png',\n): string {\n const u = new URL(\n `/v1/render/${encodeURIComponent(req.structure)}.${format}`,\n baseUrl,\n );\n if (req.seed !== undefined) u.searchParams.set('seed', String(req.seed));\n if (req.width) u.searchParams.set('width', String(req.width));\n if (req.height) u.searchParams.set('height', String(req.height));\n for (const [k, v] of Object.entries(req.palette ?? {})) {\n if (v === undefined) continue;\n u.searchParams.set(k, Array.isArray(v) ? v.join(',') : String(v));\n }\n for (const [k, v] of Object.entries(req.shape ?? {})) {\n if (v !== undefined) u.searchParams.set(k, String(v));\n }\n return u.toString();\n}\n\nfunction json(value: unknown): CallToolResult {\n return { content: [{ type: 'text', text: JSON.stringify(value, null, 2) }] };\n}\nfunction text(value: string): CallToolResult {\n return { content: [{ type: 'text', text: value }] };\n}\nfunction error(message: string): CallToolResult {\n return { isError: true, content: [{ type: 'text', text: message }] };\n}\n"],"mappings":"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","names":["server"]}
|
|
1
|
+
{"version":3,"sources":["../src/stdio.ts","../src/server.ts"],"sourcesContent":["#!/usr/bin/env node\n// Local MCP server over stdio. Runs the whole engine on this machine; no\n// network, no account. Add to Claude Desktop / Claude Code / Cursor as:\n// { \"command\": \"npx\", \"args\": [\"-y\", \"@creatica/mcp\"] }\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createMcpServer } from './server.js';\n\nif (process.argv.includes('--help') || process.argv.includes('-h')) {\n process.stdout.write(\n [\n 'creatica-mcp — Model Context Protocol server for Creatica backgrounds (stdio).',\n '',\n 'Tools: list_backgrounds, suggest_backgrounds, describe_background, list_palettes, generate_background',\n '',\n 'Claude Code: claude mcp add creatica -- npx -y @creatica/mcp',\n 'Claude Desktop: add {\"mcpServers\":{\"creatica\":{\"command\":\"npx\",\"args\":[\"-y\",\"@creatica/mcp\"]}}}',\n '',\n ].join('\\n'),\n );\n process.exit(0);\n}\n\nconst server = createMcpServer();\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","import {\n ENGINE_VERSION,\n renderPng,\n renderSvg,\n type RenderRequest,\n type RenderResult,\n} from '@creatica/engine';\nimport {\n catalog,\n describe,\n jsonSchemaFor,\n listPalettes,\n normalizeRequest,\n styleCard,\n suggest,\n} from '@creatica/schema';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, resolve } from 'node:path';\nimport { z } from 'zod';\n\nexport interface BrandKitSummary {\n slug: string;\n name: string;\n colors: string[];\n backgroundColor?: string;\n defaultStructure?: string;\n}\n\nexport interface HostedOptions {\n /** Public origin of the API, e.g. https://api.creatica.app */\n publicBaseUrl: string;\n /** Brand kits of the calling account; enables `brand` on generate and list_brand_kits. */\n listBrandKits?: () => Promise<BrandKitSummary[]>;\n /**\n * Called before rendering. Throw to refuse (e.g. quota exhausted); the\n * message is returned to the model as a tool error, so discovery tools keep\n * working and the model can explain what to do next.\n */\n beforeRender?: (request: RenderRequest) => void | Promise<void>;\n /** Called after a successful render so the host can meter usage. */\n onRender?: (\n result: RenderResult,\n format: 'svg' | 'png',\n ) => void | Promise<void>;\n}\n\nexport interface McpServerOptions {\n /**\n * Local mode (default) renders in-process and can write files to disk.\n * Hosted mode additionally returns permanent, cacheable image URLs and\n * never touches the filesystem.\n */\n hosted?: HostedOptions;\n /** Inline PNG previews above this many bytes are downscaled. */\n maxInlineBytes?: number;\n}\n\nconst hexColor = z\n .string()\n .regex(\n /^#?([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/,\n 'hex colour like #1e90ff',\n );\n\nconst paletteShape = z\n .object({\n colors: z\n .array(hexColor)\n .min(1)\n .max(12)\n .optional()\n .describe(\n 'Explicit hex colours to draw from (best for brand matching). Overrides palette/scheme.',\n ),\n palette: z\n .string()\n .optional()\n .describe(\n 'Id of a built-in palette from list_palettes, e.g. \"underTheSea\".',\n ),\n primaryColor: hexColor\n .optional()\n .describe('Main hex colour when not passing colors/palette.'),\n secondaryColor: hexColor\n .optional()\n .describe('Second hex colour for two-tone structures.'),\n backgroundColor: hexColor\n .optional()\n .describe('Hex colour of the canvas behind the shapes.'),\n gradientColor: z\n .boolean()\n .optional()\n .describe('Gradient fills instead of flat colour.'),\n useMultiColor: z\n .boolean()\n .optional()\n .describe('false = everything in primaryColor.'),\n colorSchemeType: z\n .enum(['Mono', 'Tetrade', 'Analogic'])\n .optional()\n .describe('Derive a palette from primaryColor.'),\n opaqueColors: z\n .boolean()\n .optional()\n .describe('true = no transparency in the shape fills.'),\n })\n .strict();\n\n// Shape options are a loose record: which keys apply depends on the structure\n// (see describe_background). normalizeRequest() validates against that.\nconst shapeShape = z\n .record(z.string(), z.union([z.number(), z.boolean(), z.string()]))\n .describe(\n 'Structure-specific options; call describe_background for the exact keys, ranges and what they do.',\n );\n\nconst SERVER_DESCRIPTION =\n 'Deterministic procedural SVG/PNG backgrounds for websites, slides, social cards and apps: 58 generators, curated palettes and brand kits, every render reproducible from its seed.';\n\n// Output schemas: what each tool puts in structuredContent (the text content\n// carries the same JSON, so clients without structured-output support lose\n// nothing).\nconst sizeShape = z.object({\n width: z.number().describe('Native frame width in px.'),\n height: z.number().describe('Native frame height in px.'),\n});\n\nconst catalogEntryShape = z.looseObject({\n name: z\n .string()\n .describe('Structure id for describe_background / generate_background.'),\n title: z.string().describe('Human-readable name.'),\n summary: z.string().describe('One-line description of the look.'),\n tags: z.array(z.string()).describe('Style tags, e.g. geometric, organic.'),\n mood: z.array(z.string()).describe('Mood words, e.g. calm, playful, dark.'),\n useCases: z.array(z.string()).describe('Where it works well.'),\n previewUrl: z.string().describe('Preview image of the default render.'),\n defaultSize: sizeShape,\n});\n\nconst suggestionShape = z.looseObject({\n name: z.string().describe('Structure id.'),\n title: z.string(),\n summary: z.string(),\n score: z.number().describe('Relevance to the brief; higher is better.'),\n matched: z.array(z.string()).describe('Why it matched, for explaining.'),\n example: z\n .looseObject({ structure: z.string() })\n .describe('A ready-to-run generate_background request for this brief.'),\n palettes: z\n .array(z.string())\n .describe('Built-in palette ids whose mood fits the brief, best first.'),\n});\n\nconst paletteDescriptionShape = z.looseObject({\n id: z.string().describe('Value to pass as palette.palette.'),\n name: z.string(),\n colors: z.array(z.string()).describe('Hex colours.'),\n mood: z.array(z.string()).describe('Derived mood tags.'),\n lightness: z.number().describe('Average perceived lightness 0-1.'),\n saturation: z.number().describe('Average saturation 0-1.'),\n});\n\nconst paramDocShape = z.looseObject({\n key: z.string().describe('Option key to pass under shape/palette.'),\n group: z.enum(['shape', 'palette', 'frame']),\n title: z.string(),\n type: z.string(),\n description: z.string(),\n default: z.unknown().optional(),\n min: z.number().optional(),\n max: z.number().optional(),\n step: z.number().optional(),\n enum: z.array(z.string()).optional(),\n});\n\nconst structureDescriptionShape = z.looseObject({\n name: z.string(),\n title: z.string(),\n summary: z.string(),\n description: z.string(),\n tags: z.array(z.string()),\n mood: z.array(z.string()),\n useCases: z.array(z.string()),\n tips: z.array(z.string()),\n defaultSize: sizeShape,\n previewUrl: z.string(),\n params: z\n .array(paramDocShape)\n .describe('Every option this structure honours.'),\n});\n\nconst brandKitShape = z.looseObject({\n slug: z.string().describe('Value to pass as brand to generate_background.'),\n name: z.string(),\n colors: z.array(z.string()).describe('Hex colours.'),\n backgroundColor: z.string().optional(),\n defaultStructure: z.string().optional(),\n});\n\nconst generateOutputShape = {\n structure: z.string().describe('Structure that was rendered.'),\n seed: z\n .union([z.string(), z.number()])\n .describe('Seed used; reuse it to reproduce the exact image.'),\n width: z.number().describe('Render width in px.'),\n height: z.number().describe('Render height in px.'),\n format: z.enum(['svg', 'png']).describe('Delivered format.'),\n svgBytes: z.number().describe('Size of the SVG source in bytes.'),\n renderMs: z.number().describe('Render time in milliseconds.'),\n request: z\n .looseObject({ structure: z.string() })\n .describe('Resolved request incl. seed; replay it to reproduce or tweak.'),\n url: z\n .string()\n .optional()\n .describe(\n 'Hosted only: permanent, cacheable image URL in the requested format.',\n ),\n svgUrl: z\n .string()\n .optional()\n .describe('Hosted only: permanent URL of the SVG source.'),\n savedTo: z\n .string()\n .optional()\n .describe('Local only: absolute path the file was written to.'),\n bytes: z.number().optional().describe('Local only: bytes written to disk.'),\n warnings: z\n .array(z.string())\n .optional()\n .describe('Options that were ignored or clamped, and why.'),\n note: z.string().optional().describe('Anything else worth knowing.'),\n};\n\nexport function createMcpServer(options: McpServerOptions = {}): McpServer {\n const { hosted } = options;\n const maxInline = options.maxInlineBytes ?? 600_000;\n\n const server = new McpServer(\n {\n name: 'creatica-backgrounds',\n title: 'Creatica Backgrounds',\n version: ENGINE_VERSION,\n description: SERVER_DESCRIPTION,\n websiteUrl: 'https://creatica.app',\n icons: [\n {\n src: 'https://creatica.app/site/android-chrome-512x512.png',\n mimeType: 'image/png',\n sizes: ['512x512'],\n },\n {\n src: 'https://creatica.app/site/android-chrome-192x192.png',\n mimeType: 'image/png',\n sizes: ['192x192'],\n },\n ],\n },\n {\n instructions: [\n 'Creatica generates abstract, procedural SVG/PNG backgrounds (waves, blobs, bauhaus grids, topo lines, aurora glows…) for websites, slides, social cards and apps.',\n 'Workflow: suggest_backgrounds (or list_backgrounds) with the brief → describe_background for the chosen one → generate_background with a seed. Every render is deterministic: same structure + params + seed = identical image, so keep the seed to reproduce or make on-brand variants.',\n 'For brand colours pass palette.colors as hex. For a curated look pass palette.palette from list_palettes. Prefer PNG when you need a preview; SVG for the final asset.',\n ].join('\\n'),\n },\n );\n\n server.registerTool(\n 'list_backgrounds',\n {\n title: 'List backgrounds',\n description:\n 'Browse the catalog of background structures. Optionally filter by a free-text query, tag or mood word. Returns name, one-line summary, mood, use cases and a preview URL for each.',\n inputSchema: {\n query: z\n .string()\n .optional()\n .describe('Free text, e.g. \"calm waves\" or \"geometric poster\".'),\n mood: z\n .string()\n .optional()\n .describe('A mood word, e.g. dark, playful, minimal, retro.'),\n tag: z\n .string()\n .optional()\n .describe('A style tag, e.g. geometric, organic, grid, gradient.'),\n limit: z\n .number()\n .int()\n .min(1)\n .max(100)\n .optional()\n .describe('Maximum number of results (default 100).'),\n },\n outputSchema: {\n count: z.number().describe('Number of backgrounds returned.'),\n backgrounds: z.array(catalogEntryShape),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ query, mood, tag, limit }) => {\n let items = catalog();\n if (mood)\n items = items.filter((c) =>\n c.mood.some((m) => m.toLowerCase().includes(mood.toLowerCase())),\n );\n if (tag)\n items = items.filter((c) =>\n c.tags.some((t) => t.toLowerCase().includes(tag.toLowerCase())),\n );\n if (query) {\n const ranked = suggest(query, { limit: 100 }).map((s) => s.name);\n items = items\n .filter((c) => ranked.includes(c.name))\n .sort((a, b) => ranked.indexOf(a.name) - ranked.indexOf(b.name));\n }\n items = items.slice(0, limit ?? 100);\n return json({ count: items.length, backgrounds: items });\n },\n );\n\n server.registerTool(\n 'suggest_backgrounds',\n {\n title: 'Suggest backgrounds for a brief',\n description:\n 'Rank structures for a natural-language brief (\"dark aurora glow for an AI landing page\"). Returns why each matched, matching built-in palettes, and a ready-to-run example request for generate_background.',\n inputSchema: {\n brief: z\n .string()\n .min(2)\n .describe(\n 'What the background is for, in plain words: subject, mood, colours, where it goes.',\n ),\n limit: z\n .number()\n .int()\n .min(1)\n .max(20)\n .optional()\n .describe('How many suggestions to return (default 5).'),\n },\n outputSchema: {\n suggestions: z.array(suggestionShape).describe('Best match first.'),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ brief, limit }) =>\n json({ suggestions: suggest(brief, { limit: limit ?? 5 }) }),\n );\n\n server.registerTool(\n 'describe_background',\n {\n title: 'Describe a background',\n description:\n 'Full style card for one structure: what it looks like, mood, where text can go, tips, and every parameter it honours with ranges, defaults and the visual effect of low/high values. Also returns the JSON Schema for a generate request.',\n inputSchema: {\n name: z\n .string()\n .describe('Structure name from list_backgrounds, e.g. \"wave\".'),\n format: z\n .enum(['markdown', 'json'])\n .optional()\n .describe(\n 'markdown (default) is best for reading; json includes the JSON Schema.',\n ),\n },\n outputSchema: {\n name: z.string().describe('Structure id.'),\n format: z\n .enum(['markdown', 'json'])\n .describe('Which form was returned.'),\n card: z\n .string()\n .optional()\n .describe('markdown format: the style card as Markdown.'),\n description: structureDescriptionShape\n .optional()\n .describe('json format: the structured description.'),\n jsonSchema: z\n .looseObject({})\n .optional()\n .describe('json format: JSON Schema for a generate request.'),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ name, format }) => {\n try {\n if (format === 'json')\n return json({\n name,\n format: 'json',\n description: describe(name),\n jsonSchema: jsonSchemaFor(name),\n });\n const card = styleCard(name);\n return {\n content: [{ type: 'text', text: card }],\n structuredContent: { name, format: 'markdown', card },\n };\n } catch (e) {\n return error((e as Error).message);\n }\n },\n );\n\n server.registerTool(\n 'list_palettes',\n {\n title: 'List palettes',\n description:\n 'Built-in curated colour palettes with their hex colours and derived mood tags (dark, pastel, vivid, warm, cool…). Pass the id as palette.palette to generate_background.',\n inputSchema: {\n mood: z\n .string()\n .optional()\n .describe('Filter by a mood tag, e.g. \"dark\" or \"pastel\".'),\n },\n outputSchema: {\n count: z.number().describe('Number of palettes returned.'),\n palettes: z.array(paletteDescriptionShape),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async ({ mood }) => {\n let items = listPalettes();\n if (mood)\n items = items.filter((p) => p.mood.includes(mood.toLowerCase()));\n return json({ count: items.length, palettes: items });\n },\n );\n\n if (hosted?.listBrandKits) {\n server.registerTool(\n 'list_brand_kits',\n {\n title: 'List brand kits',\n description:\n \"Saved brand palettes on this account. Pass a kit's slug as `brand` to generate_background to render on-brand without repeating hex colours.\",\n inputSchema: {},\n outputSchema: {\n brandKits: z\n .array(brandKitShape)\n .describe('Saved kits, may be empty.'),\n },\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async () => json({ brandKits: await hosted.listBrandKits!() }),\n );\n }\n\n server.registerTool(\n 'generate_background',\n {\n title: 'Generate a background',\n description: [\n 'Render a background as SVG or PNG. Deterministic: the same structure, options and seed always give the same image, so store the returned seed.',\n hosted\n ? 'Returns a permanent, cacheable image URL you can use directly in <img>/CSS, plus an inline PNG preview.'\n : 'Returns the image inline (PNG previews are visible to you); pass saveTo to also write the file to disk.',\n 'Call describe_background first to learn which shape options the structure honours.',\n ].join(' '),\n inputSchema: {\n structure: z.string().describe('Structure name, e.g. \"wave\".'),\n seed: z\n .string()\n .optional()\n .describe(\n 'Any string. Omit for a fresh random seed (returned in the result).',\n ),\n width: z\n .number()\n .int()\n .min(1)\n .max(8192)\n .optional()\n .describe(\n \"Frame width in px. Defaults to the structure's native size (see describe_background).\",\n ),\n height: z\n .number()\n .int()\n .min(1)\n .max(8192)\n .optional()\n .describe('Frame height in px. Defaults to the native size.'),\n palette: paletteShape\n .optional()\n .describe(\n 'Colour choices: explicit hex colours, a built-in palette id, or a primary colour to derive from.',\n ),\n shape: shapeShape.optional(),\n format: z\n .enum(['svg', 'png'])\n .optional()\n .describe(\n 'png (default) returns a viewable image; svg returns the vector source.',\n ),\n pngWidth: z\n .number()\n .int()\n .min(16)\n .max(8192)\n .optional()\n .describe(\n 'Raster width for PNG output/preview. Defaults to the render width, capped for inline previews.',\n ),\n ...(hosted?.listBrandKits\n ? {\n brand: z\n .string()\n .optional()\n .describe(\n 'Slug of a saved brand kit (see list_brand_kits); its colours become the palette baseline.',\n ),\n }\n : {}),\n ...(hosted\n ? {}\n : {\n saveTo: z\n .string()\n .optional()\n .describe(\n 'Local file path to write the result to (extension decides svg/png if format is omitted).',\n ),\n }),\n },\n outputSchema: generateOutputShape,\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n },\n async (args) => {\n const input = { ...(args as Record<string, unknown>) };\n const brandSlug =\n typeof input.brand === 'string' ? input.brand : undefined;\n delete input.brand;\n if (brandSlug) {\n const kit = (await hosted?.listBrandKits?.())?.find(\n (k) => k.slug === brandSlug,\n );\n if (!kit)\n return error(\n `Unknown brand kit \"${brandSlug}\". Call list_brand_kits to see the available ones.`,\n );\n const basePalette: Record<string, unknown> = { colors: kit.colors };\n if (kit.backgroundColor)\n basePalette.backgroundColor = kit.backgroundColor;\n input.palette = {\n ...basePalette,\n ...((input.palette as Record<string, unknown>) ?? {}),\n };\n if (!input.structure && kit.defaultStructure)\n input.structure = kit.defaultStructure;\n }\n let normalized;\n try {\n normalized = normalizeRequest(input);\n } catch (e) {\n return error((e as Error).message);\n }\n const { request, warnings } = normalized;\n const saveTo = hosted ? undefined : (args as { saveTo?: string }).saveTo;\n const format: 'svg' | 'png' =\n (args.format as 'svg' | 'png' | undefined) ??\n (saveTo?.toLowerCase().endsWith('.svg') ? 'svg' : 'png');\n\n try {\n await hosted?.beforeRender?.(request);\n } catch (e) {\n return error((e as Error).message);\n }\n const result = await renderSvg(request);\n await hosted?.onRender?.(result, format);\n\n const content: CallToolResult['content'] = [];\n const meta: Record<string, unknown> = {\n structure: result.structure,\n seed: result.seed,\n width: result.width,\n height: result.height,\n format,\n svgBytes: result.svg.length,\n renderMs: result.durationMs,\n // Echo the resolved options so the caller can reproduce or tweak.\n request: { ...request, seed: result.seed },\n ...(warnings.length ? { warnings } : {}),\n };\n\n if (hosted) {\n meta.url = renderUrl(\n hosted.publicBaseUrl,\n { ...request, seed: result.seed },\n format,\n );\n meta.svgUrl = renderUrl(\n hosted.publicBaseUrl,\n { ...request, seed: result.seed },\n 'svg',\n );\n }\n\n if (saveTo) {\n const path = resolve(saveTo);\n await mkdir(dirname(path), { recursive: true });\n const bytes =\n format === 'png'\n ? await renderPng(result.svg, {\n width: args.pngWidth ?? result.width,\n })\n : Buffer.from(result.svg, 'utf8');\n await writeFile(path, bytes);\n meta.savedTo = path;\n meta.bytes = bytes.length;\n }\n\n if (format === 'svg') {\n if (result.svg.length <= maxInline && !saveTo) {\n content.push({\n type: 'resource',\n resource: {\n uri: `creatica://render/${result.structure}/${result.seed}.svg`,\n mimeType: 'image/svg+xml',\n text: result.svg,\n },\n });\n } else if (!saveTo && !hosted) {\n meta.note = `SVG is ${result.svg.length} bytes; pass saveTo to write it to disk instead of returning it inline.`;\n }\n }\n\n // Always attach a PNG preview so the model can see what it made. Keep\n // it modest: previews are for judging composition, not final delivery.\n const previewWidth = Math.min(args.pngWidth ?? 800, 1200);\n const png = await renderPng(result.svg, { width: previewWidth });\n if (png.length <= maxInline) {\n content.push({\n type: 'image',\n data: png.toString('base64'),\n mimeType: 'image/png',\n });\n } else {\n const small = await renderPng(result.svg, { width: 480 });\n content.push({\n type: 'image',\n data: small.toString('base64'),\n mimeType: 'image/png',\n });\n meta.note = `Preview downscaled to 480px (full PNG would be ${png.length} bytes).`;\n }\n\n content.unshift({ type: 'text', text: JSON.stringify(meta, null, 2) });\n return { content, structuredContent: meta };\n },\n );\n\n return server;\n}\n\nexport function renderUrl(\n baseUrl: string,\n req: RenderRequest,\n format: 'svg' | 'png',\n): string {\n const u = new URL(\n `/v1/render/${encodeURIComponent(req.structure)}.${format}`,\n baseUrl,\n );\n if (req.seed !== undefined) u.searchParams.set('seed', String(req.seed));\n if (req.width) u.searchParams.set('width', String(req.width));\n if (req.height) u.searchParams.set('height', String(req.height));\n for (const [k, v] of Object.entries(req.palette ?? {})) {\n if (v === undefined) continue;\n u.searchParams.set(k, Array.isArray(v) ? v.join(',') : String(v));\n }\n for (const [k, v] of Object.entries(req.shape ?? {})) {\n if (v !== undefined) u.searchParams.set(k, String(v));\n }\n return u.toString();\n}\n\nfunction json(value: Record<string, unknown>): CallToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(value, null, 2) }],\n structuredContent: value,\n };\n}\nfunction error(message: string): CallToolResult {\n return { isError: true, content: [{ type: 'text', text: message }] };\n}\n"],"mappings":"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","names":["server"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@creatica/mcp",
|
|
3
|
-
"
|
|
3
|
+
"mcpName": "app.creatica/backgrounds",
|
|
4
|
+
"version": "0.1.2",
|
|
4
5
|
"description": "MCP server for Creatica: let AI agents browse, describe and generate procedural SVG/PNG backgrounds on demand.",
|
|
5
6
|
"license": "MIT",
|
|
6
7
|
"type": "module",
|