@creatica/mcp 0.1.1 → 0.1.3
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 +164 -22
- package/dist/index.js.map +1 -1
- package/dist/stdio.js +164 -22
- package/dist/stdio.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28,22 +28,123 @@ 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
|
+
hint: z.string().optional().describe("Hosted, no API key: how to get permanent URLs.")
|
|
124
|
+
};
|
|
42
125
|
function createMcpServer(options = {}) {
|
|
43
126
|
const { hosted } = options;
|
|
44
127
|
const maxInline = options.maxInlineBytes ?? 6e5;
|
|
45
128
|
const server = new McpServer(
|
|
46
|
-
{
|
|
129
|
+
{
|
|
130
|
+
name: "creatica-backgrounds",
|
|
131
|
+
title: "Creatica Backgrounds",
|
|
132
|
+
version: ENGINE_VERSION,
|
|
133
|
+
description: SERVER_DESCRIPTION,
|
|
134
|
+
websiteUrl: "https://creatica.app",
|
|
135
|
+
icons: [
|
|
136
|
+
{
|
|
137
|
+
src: "https://creatica.app/site/android-chrome-512x512.png",
|
|
138
|
+
mimeType: "image/png",
|
|
139
|
+
sizes: ["512x512"]
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
src: "https://creatica.app/site/android-chrome-192x192.png",
|
|
143
|
+
mimeType: "image/png",
|
|
144
|
+
sizes: ["192x192"]
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
},
|
|
47
148
|
{
|
|
48
149
|
instructions: [
|
|
49
150
|
"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 +161,12 @@ function createMcpServer(options = {}) {
|
|
|
60
161
|
inputSchema: {
|
|
61
162
|
query: z.string().optional().describe('Free text, e.g. "calm waves" or "geometric poster".'),
|
|
62
163
|
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()
|
|
164
|
+
tag: z.string().optional().describe("A style tag, e.g. geometric, organic, grid, gradient."),
|
|
165
|
+
limit: z.number().int().min(1).max(100).optional().describe("Maximum number of results (default 100).")
|
|
166
|
+
},
|
|
167
|
+
outputSchema: {
|
|
168
|
+
count: z.number().describe("Number of backgrounds returned."),
|
|
169
|
+
backgrounds: z.array(catalogEntryShape)
|
|
65
170
|
},
|
|
66
171
|
annotations: {
|
|
67
172
|
readOnlyHint: true,
|
|
@@ -93,8 +198,13 @@ function createMcpServer(options = {}) {
|
|
|
93
198
|
title: "Suggest backgrounds for a brief",
|
|
94
199
|
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
200
|
inputSchema: {
|
|
96
|
-
brief: z.string().min(2)
|
|
97
|
-
|
|
201
|
+
brief: z.string().min(2).describe(
|
|
202
|
+
"What the background is for, in plain words: subject, mood, colours, where it goes."
|
|
203
|
+
),
|
|
204
|
+
limit: z.number().int().min(1).max(20).optional().describe("How many suggestions to return (default 5).")
|
|
205
|
+
},
|
|
206
|
+
outputSchema: {
|
|
207
|
+
suggestions: z.array(suggestionShape).describe("Best match first.")
|
|
98
208
|
},
|
|
99
209
|
annotations: {
|
|
100
210
|
readOnlyHint: true,
|
|
@@ -115,6 +225,13 @@ function createMcpServer(options = {}) {
|
|
|
115
225
|
"markdown (default) is best for reading; json includes the JSON Schema."
|
|
116
226
|
)
|
|
117
227
|
},
|
|
228
|
+
outputSchema: {
|
|
229
|
+
name: z.string().describe("Structure id."),
|
|
230
|
+
format: z.enum(["markdown", "json"]).describe("Which form was returned."),
|
|
231
|
+
card: z.string().optional().describe("markdown format: the style card as Markdown."),
|
|
232
|
+
description: structureDescriptionShape.optional().describe("json format: the structured description."),
|
|
233
|
+
jsonSchema: z.looseObject({}).optional().describe("json format: JSON Schema for a generate request.")
|
|
234
|
+
},
|
|
118
235
|
annotations: {
|
|
119
236
|
readOnlyHint: true,
|
|
120
237
|
idempotentHint: true,
|
|
@@ -124,8 +241,17 @@ function createMcpServer(options = {}) {
|
|
|
124
241
|
async ({ name, format }) => {
|
|
125
242
|
try {
|
|
126
243
|
if (format === "json")
|
|
127
|
-
return json({
|
|
128
|
-
|
|
244
|
+
return json({
|
|
245
|
+
name,
|
|
246
|
+
format: "json",
|
|
247
|
+
description: describe(name),
|
|
248
|
+
jsonSchema: jsonSchemaFor(name)
|
|
249
|
+
});
|
|
250
|
+
const card = styleCard(name);
|
|
251
|
+
return {
|
|
252
|
+
content: [{ type: "text", text: card }],
|
|
253
|
+
structuredContent: { name, format: "markdown", card }
|
|
254
|
+
};
|
|
129
255
|
} catch (e) {
|
|
130
256
|
return error(e.message);
|
|
131
257
|
}
|
|
@@ -139,6 +265,10 @@ function createMcpServer(options = {}) {
|
|
|
139
265
|
inputSchema: {
|
|
140
266
|
mood: z.string().optional().describe('Filter by a mood tag, e.g. "dark" or "pastel".')
|
|
141
267
|
},
|
|
268
|
+
outputSchema: {
|
|
269
|
+
count: z.number().describe("Number of palettes returned."),
|
|
270
|
+
palettes: z.array(paletteDescriptionShape)
|
|
271
|
+
},
|
|
142
272
|
annotations: {
|
|
143
273
|
readOnlyHint: true,
|
|
144
274
|
idempotentHint: true,
|
|
@@ -159,6 +289,9 @@ function createMcpServer(options = {}) {
|
|
|
159
289
|
title: "List brand kits",
|
|
160
290
|
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
291
|
inputSchema: {},
|
|
292
|
+
outputSchema: {
|
|
293
|
+
brandKits: z.array(brandKitShape).describe("Saved kits, may be empty.")
|
|
294
|
+
},
|
|
162
295
|
annotations: {
|
|
163
296
|
readOnlyHint: true,
|
|
164
297
|
idempotentHint: true,
|
|
@@ -174,7 +307,7 @@ function createMcpServer(options = {}) {
|
|
|
174
307
|
title: "Generate a background",
|
|
175
308
|
description: [
|
|
176
309
|
"Render a background as SVG or PNG. Deterministic: the same structure, options and seed always give the same image, so store the returned seed.",
|
|
177
|
-
hosted ? "
|
|
310
|
+
hosted ? "With an API key, returns a permanent, cacheable image URL you can use directly in <img>/CSS, plus an inline PNG preview." : "Returns the image inline (PNG previews are visible to you); pass saveTo to also write the file to disk.",
|
|
178
311
|
"Call describe_background first to learn which shape options the structure honours."
|
|
179
312
|
].join(" "),
|
|
180
313
|
inputSchema: {
|
|
@@ -182,9 +315,13 @@ function createMcpServer(options = {}) {
|
|
|
182
315
|
seed: z.string().optional().describe(
|
|
183
316
|
"Any string. Omit for a fresh random seed (returned in the result)."
|
|
184
317
|
),
|
|
185
|
-
width: z.number().int().min(1).max(8192).optional()
|
|
186
|
-
|
|
187
|
-
|
|
318
|
+
width: z.number().int().min(1).max(8192).optional().describe(
|
|
319
|
+
"Frame width in px. Defaults to the structure's native size (see describe_background)."
|
|
320
|
+
),
|
|
321
|
+
height: z.number().int().min(1).max(8192).optional().describe("Frame height in px. Defaults to the native size."),
|
|
322
|
+
palette: paletteShape.optional().describe(
|
|
323
|
+
"Colour choices: explicit hex colours, a built-in palette id, or a primary colour to derive from."
|
|
324
|
+
),
|
|
188
325
|
shape: shapeShape.optional(),
|
|
189
326
|
format: z.enum(["svg", "png"]).optional().describe(
|
|
190
327
|
"png (default) returns a viewable image; svg returns the vector source."
|
|
@@ -203,6 +340,7 @@ function createMcpServer(options = {}) {
|
|
|
203
340
|
)
|
|
204
341
|
}
|
|
205
342
|
},
|
|
343
|
+
outputSchema: generateOutputShape,
|
|
206
344
|
annotations: {
|
|
207
345
|
readOnlyHint: true,
|
|
208
346
|
idempotentHint: true,
|
|
@@ -260,7 +398,9 @@ function createMcpServer(options = {}) {
|
|
|
260
398
|
request: { ...request, seed: result.seed },
|
|
261
399
|
...warnings.length ? { warnings } : {}
|
|
262
400
|
};
|
|
263
|
-
if (hosted) {
|
|
401
|
+
if (hosted && hosted.hostedUrls === false) {
|
|
402
|
+
meta.hint = `Permanent hosted URLs need an API key (free keys include 200 renders a month and brand kits)${hosted.keysUrl ? ": " + hosted.keysUrl : ""}.`;
|
|
403
|
+
} else if (hosted) {
|
|
264
404
|
meta.url = renderUrl(
|
|
265
405
|
hosted.publicBaseUrl,
|
|
266
406
|
{ ...request, seed: result.seed },
|
|
@@ -275,7 +415,9 @@ function createMcpServer(options = {}) {
|
|
|
275
415
|
if (saveTo) {
|
|
276
416
|
const path = resolve(saveTo);
|
|
277
417
|
await mkdir(dirname(path), { recursive: true });
|
|
278
|
-
const bytes = format === "png" ? await renderPng(result.svg, {
|
|
418
|
+
const bytes = format === "png" ? await renderPng(result.svg, {
|
|
419
|
+
width: args.pngWidth ?? result.width
|
|
420
|
+
}) : Buffer.from(result.svg, "utf8");
|
|
279
421
|
await writeFile(path, bytes);
|
|
280
422
|
meta.savedTo = path;
|
|
281
423
|
meta.bytes = bytes.length;
|
|
@@ -312,7 +454,7 @@ function createMcpServer(options = {}) {
|
|
|
312
454
|
meta.note = `Preview downscaled to 480px (full PNG would be ${png.length} bytes).`;
|
|
313
455
|
}
|
|
314
456
|
content.unshift({ type: "text", text: JSON.stringify(meta, null, 2) });
|
|
315
|
-
return { content };
|
|
457
|
+
return { content, structuredContent: meta };
|
|
316
458
|
}
|
|
317
459
|
);
|
|
318
460
|
return server;
|
|
@@ -335,10 +477,10 @@ function renderUrl(baseUrl, req, format) {
|
|
|
335
477
|
return u.toString();
|
|
336
478
|
}
|
|
337
479
|
function json(value) {
|
|
338
|
-
return {
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
|
|
480
|
+
return {
|
|
481
|
+
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
482
|
+
structuredContent: value
|
|
483
|
+
};
|
|
342
484
|
}
|
|
343
485
|
function error(message) {
|
|
344
486
|
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 /** Return permanent hosted URLs (default true). Hosts set false for anonymous callers. */\n hostedUrls?: boolean;\n /** Where to get a free key; mentioned when hostedUrls is false. */\n keysUrl?: 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 hint: z\n .string()\n .optional()\n .describe('Hosted, no API key: how to get permanent URLs.'),\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 ? 'With an API key, 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 && hosted.hostedUrls === false) {\n meta.hint = `Permanent hosted URLs need an API key (free keys include 200 renders a month and brand kits)${hosted.keysUrl ? ': ' + hosted.keysUrl : ''}.`;\n } else 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,123 @@ 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
|
+
hint: z.string().optional().describe("Hosted, no API key: how to get permanent URLs.")
|
|
129
|
+
};
|
|
47
130
|
function createMcpServer(options = {}) {
|
|
48
131
|
const { hosted } = options;
|
|
49
132
|
const maxInline = options.maxInlineBytes ?? 6e5;
|
|
50
133
|
const server2 = new McpServer(
|
|
51
|
-
{
|
|
134
|
+
{
|
|
135
|
+
name: "creatica-backgrounds",
|
|
136
|
+
title: "Creatica Backgrounds",
|
|
137
|
+
version: ENGINE_VERSION,
|
|
138
|
+
description: SERVER_DESCRIPTION,
|
|
139
|
+
websiteUrl: "https://creatica.app",
|
|
140
|
+
icons: [
|
|
141
|
+
{
|
|
142
|
+
src: "https://creatica.app/site/android-chrome-512x512.png",
|
|
143
|
+
mimeType: "image/png",
|
|
144
|
+
sizes: ["512x512"]
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
src: "https://creatica.app/site/android-chrome-192x192.png",
|
|
148
|
+
mimeType: "image/png",
|
|
149
|
+
sizes: ["192x192"]
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
},
|
|
52
153
|
{
|
|
53
154
|
instructions: [
|
|
54
155
|
"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 +166,12 @@ function createMcpServer(options = {}) {
|
|
|
65
166
|
inputSchema: {
|
|
66
167
|
query: z.string().optional().describe('Free text, e.g. "calm waves" or "geometric poster".'),
|
|
67
168
|
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()
|
|
169
|
+
tag: z.string().optional().describe("A style tag, e.g. geometric, organic, grid, gradient."),
|
|
170
|
+
limit: z.number().int().min(1).max(100).optional().describe("Maximum number of results (default 100).")
|
|
171
|
+
},
|
|
172
|
+
outputSchema: {
|
|
173
|
+
count: z.number().describe("Number of backgrounds returned."),
|
|
174
|
+
backgrounds: z.array(catalogEntryShape)
|
|
70
175
|
},
|
|
71
176
|
annotations: {
|
|
72
177
|
readOnlyHint: true,
|
|
@@ -98,8 +203,13 @@ function createMcpServer(options = {}) {
|
|
|
98
203
|
title: "Suggest backgrounds for a brief",
|
|
99
204
|
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
205
|
inputSchema: {
|
|
101
|
-
brief: z.string().min(2)
|
|
102
|
-
|
|
206
|
+
brief: z.string().min(2).describe(
|
|
207
|
+
"What the background is for, in plain words: subject, mood, colours, where it goes."
|
|
208
|
+
),
|
|
209
|
+
limit: z.number().int().min(1).max(20).optional().describe("How many suggestions to return (default 5).")
|
|
210
|
+
},
|
|
211
|
+
outputSchema: {
|
|
212
|
+
suggestions: z.array(suggestionShape).describe("Best match first.")
|
|
103
213
|
},
|
|
104
214
|
annotations: {
|
|
105
215
|
readOnlyHint: true,
|
|
@@ -120,6 +230,13 @@ function createMcpServer(options = {}) {
|
|
|
120
230
|
"markdown (default) is best for reading; json includes the JSON Schema."
|
|
121
231
|
)
|
|
122
232
|
},
|
|
233
|
+
outputSchema: {
|
|
234
|
+
name: z.string().describe("Structure id."),
|
|
235
|
+
format: z.enum(["markdown", "json"]).describe("Which form was returned."),
|
|
236
|
+
card: z.string().optional().describe("markdown format: the style card as Markdown."),
|
|
237
|
+
description: structureDescriptionShape.optional().describe("json format: the structured description."),
|
|
238
|
+
jsonSchema: z.looseObject({}).optional().describe("json format: JSON Schema for a generate request.")
|
|
239
|
+
},
|
|
123
240
|
annotations: {
|
|
124
241
|
readOnlyHint: true,
|
|
125
242
|
idempotentHint: true,
|
|
@@ -129,8 +246,17 @@ function createMcpServer(options = {}) {
|
|
|
129
246
|
async ({ name, format }) => {
|
|
130
247
|
try {
|
|
131
248
|
if (format === "json")
|
|
132
|
-
return json({
|
|
133
|
-
|
|
249
|
+
return json({
|
|
250
|
+
name,
|
|
251
|
+
format: "json",
|
|
252
|
+
description: describe(name),
|
|
253
|
+
jsonSchema: jsonSchemaFor(name)
|
|
254
|
+
});
|
|
255
|
+
const card = styleCard(name);
|
|
256
|
+
return {
|
|
257
|
+
content: [{ type: "text", text: card }],
|
|
258
|
+
structuredContent: { name, format: "markdown", card }
|
|
259
|
+
};
|
|
134
260
|
} catch (e) {
|
|
135
261
|
return error(e.message);
|
|
136
262
|
}
|
|
@@ -144,6 +270,10 @@ function createMcpServer(options = {}) {
|
|
|
144
270
|
inputSchema: {
|
|
145
271
|
mood: z.string().optional().describe('Filter by a mood tag, e.g. "dark" or "pastel".')
|
|
146
272
|
},
|
|
273
|
+
outputSchema: {
|
|
274
|
+
count: z.number().describe("Number of palettes returned."),
|
|
275
|
+
palettes: z.array(paletteDescriptionShape)
|
|
276
|
+
},
|
|
147
277
|
annotations: {
|
|
148
278
|
readOnlyHint: true,
|
|
149
279
|
idempotentHint: true,
|
|
@@ -164,6 +294,9 @@ function createMcpServer(options = {}) {
|
|
|
164
294
|
title: "List brand kits",
|
|
165
295
|
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
296
|
inputSchema: {},
|
|
297
|
+
outputSchema: {
|
|
298
|
+
brandKits: z.array(brandKitShape).describe("Saved kits, may be empty.")
|
|
299
|
+
},
|
|
167
300
|
annotations: {
|
|
168
301
|
readOnlyHint: true,
|
|
169
302
|
idempotentHint: true,
|
|
@@ -179,7 +312,7 @@ function createMcpServer(options = {}) {
|
|
|
179
312
|
title: "Generate a background",
|
|
180
313
|
description: [
|
|
181
314
|
"Render a background as SVG or PNG. Deterministic: the same structure, options and seed always give the same image, so store the returned seed.",
|
|
182
|
-
hosted ? "
|
|
315
|
+
hosted ? "With an API key, returns a permanent, cacheable image URL you can use directly in <img>/CSS, plus an inline PNG preview." : "Returns the image inline (PNG previews are visible to you); pass saveTo to also write the file to disk.",
|
|
183
316
|
"Call describe_background first to learn which shape options the structure honours."
|
|
184
317
|
].join(" "),
|
|
185
318
|
inputSchema: {
|
|
@@ -187,9 +320,13 @@ function createMcpServer(options = {}) {
|
|
|
187
320
|
seed: z.string().optional().describe(
|
|
188
321
|
"Any string. Omit for a fresh random seed (returned in the result)."
|
|
189
322
|
),
|
|
190
|
-
width: z.number().int().min(1).max(8192).optional()
|
|
191
|
-
|
|
192
|
-
|
|
323
|
+
width: z.number().int().min(1).max(8192).optional().describe(
|
|
324
|
+
"Frame width in px. Defaults to the structure's native size (see describe_background)."
|
|
325
|
+
),
|
|
326
|
+
height: z.number().int().min(1).max(8192).optional().describe("Frame height in px. Defaults to the native size."),
|
|
327
|
+
palette: paletteShape.optional().describe(
|
|
328
|
+
"Colour choices: explicit hex colours, a built-in palette id, or a primary colour to derive from."
|
|
329
|
+
),
|
|
193
330
|
shape: shapeShape.optional(),
|
|
194
331
|
format: z.enum(["svg", "png"]).optional().describe(
|
|
195
332
|
"png (default) returns a viewable image; svg returns the vector source."
|
|
@@ -208,6 +345,7 @@ function createMcpServer(options = {}) {
|
|
|
208
345
|
)
|
|
209
346
|
}
|
|
210
347
|
},
|
|
348
|
+
outputSchema: generateOutputShape,
|
|
211
349
|
annotations: {
|
|
212
350
|
readOnlyHint: true,
|
|
213
351
|
idempotentHint: true,
|
|
@@ -265,7 +403,9 @@ function createMcpServer(options = {}) {
|
|
|
265
403
|
request: { ...request, seed: result.seed },
|
|
266
404
|
...warnings.length ? { warnings } : {}
|
|
267
405
|
};
|
|
268
|
-
if (hosted) {
|
|
406
|
+
if (hosted && hosted.hostedUrls === false) {
|
|
407
|
+
meta.hint = `Permanent hosted URLs need an API key (free keys include 200 renders a month and brand kits)${hosted.keysUrl ? ": " + hosted.keysUrl : ""}.`;
|
|
408
|
+
} else if (hosted) {
|
|
269
409
|
meta.url = renderUrl(
|
|
270
410
|
hosted.publicBaseUrl,
|
|
271
411
|
{ ...request, seed: result.seed },
|
|
@@ -280,7 +420,9 @@ function createMcpServer(options = {}) {
|
|
|
280
420
|
if (saveTo) {
|
|
281
421
|
const path = resolve(saveTo);
|
|
282
422
|
await mkdir(dirname(path), { recursive: true });
|
|
283
|
-
const bytes = format === "png" ? await renderPng(result.svg, {
|
|
423
|
+
const bytes = format === "png" ? await renderPng(result.svg, {
|
|
424
|
+
width: args.pngWidth ?? result.width
|
|
425
|
+
}) : Buffer.from(result.svg, "utf8");
|
|
284
426
|
await writeFile(path, bytes);
|
|
285
427
|
meta.savedTo = path;
|
|
286
428
|
meta.bytes = bytes.length;
|
|
@@ -317,7 +459,7 @@ function createMcpServer(options = {}) {
|
|
|
317
459
|
meta.note = `Preview downscaled to 480px (full PNG would be ${png.length} bytes).`;
|
|
318
460
|
}
|
|
319
461
|
content.unshift({ type: "text", text: JSON.stringify(meta, null, 2) });
|
|
320
|
-
return { content };
|
|
462
|
+
return { content, structuredContent: meta };
|
|
321
463
|
}
|
|
322
464
|
);
|
|
323
465
|
return server2;
|
|
@@ -340,10 +482,10 @@ function renderUrl(baseUrl, req, format) {
|
|
|
340
482
|
return u.toString();
|
|
341
483
|
}
|
|
342
484
|
function json(value) {
|
|
343
|
-
return {
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
|
|
485
|
+
return {
|
|
486
|
+
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
487
|
+
structuredContent: value
|
|
488
|
+
};
|
|
347
489
|
}
|
|
348
490
|
function error(message) {
|
|
349
491
|
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":";;;AAIA,SAAS,4BAA4B;;;ACJrC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB;AAE1B,SAAS,OAAO,iBAAiB;AACjC,SAAS,SAAS,eAAe;AACjC,SAAS,SAAS;AAuClB,IAAM,WAAW,EACd,OAAO,EACP;AAAA,EACC;AAAA,EACA;AACF;AAEF,IAAM,eAAe,EAClB,OAAO;AAAA,EACN,QAAQ,EACL,MAAM,QAAQ,EACd,IAAI,CAAC,EACL,IAAI,EAAE,EACN,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,SAAS,EACN,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,cAAc,SAAS,SAAS;AAAA,EAChC,gBAAgB,SAAS,SAAS;AAAA,EAClC,iBAAiB,SAAS,SAAS;AAAA,EACnC,eAAe,EACZ,QAAQ,EACR,SAAS,EACT,SAAS,wCAAwC;AAAA,EACpD,eAAe,EACZ,QAAQ,EACR,SAAS,EACT,SAAS,qCAAqC;AAAA,EACjD,iBAAiB,EACd,KAAK,CAAC,QAAQ,WAAW,UAAU,CAAC,EACpC,SAAS,EACT,SAAS,qCAAqC;AAAA,EACjD,cAAc,EAAE,QAAQ,EAAE,SAAS;AACrC,CAAC,EACA,OAAO;AAIV,IAAM,aAAa,EAChB,OAAO,EAAE,OAAO,GAAG,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,QAAQ,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EACjE;AAAA,EACC;AACF;AAEK,SAAS,gBAAgB,UAA4B,CAAC,GAAc;AACzE,QAAM,EAAE,OAAO,IAAI;AACnB,QAAM,YAAY,QAAQ,kBAAkB;AAE5C,QAAMA,UAAS,IAAI;AAAA,IACjB,EAAE,MAAM,wBAAwB,SAAS,eAAe;AAAA,IACxD;AAAA,MACE,cAAc;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,EACF;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MACF,aAAa;AAAA,QACX,OAAO,EACJ,OAAO,EACP,SAAS,EACT,SAAS,qDAAqD;AAAA,QACjE,MAAM,EACH,OAAO,EACP,SAAS,EACT,SAAS,kDAAkD;AAAA,QAC9D,KAAK,EAAE,OAAO,EAAE,SAAS;AAAA,QACzB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,MACnD;AAAA,MACA,aAAa;AAAA,QACX,cAAc;AAAA,QACd,gBAAgB;AAAA,QAChB,eAAe;AAAA,MACjB;AAAA,IACF;AAAA,IACA,OAAO,EAAE,OAAO,MAAM,KAAK,MAAM,MAAM;AACrC,UAAI,QAAQ,QAAQ;AACpB,UAAI;AACF,gBAAQ,MAAM;AAAA,UAAO,CAAC,MACpB,EAAE,KAAK,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,KAAK,YAAY,CAAC,CAAC;AAAA,QACjE;AACF,UAAI;AACF,gBAAQ,MAAM;AAAA,UAAO,CAAC,MACpB,EAAE,KAAK,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,IAAI,YAAY,CAAC,CAAC;AAAA,QAChE;AACF,UAAI,OAAO;AACT,cAAM,SAAS,QAAQ,OAAO,EAAE,OAAO,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAC/D,gBAAQ,MACL,OAAO,CAAC,MAAM,OAAO,SAAS,EAAE,IAAI,CAAC,EACrC,KAAK,CAAC,GAAG,MAAM,OAAO,QAAQ,EAAE,IAAI,IAAI,OAAO,QAAQ,EAAE,IAAI,CAAC;AAAA,MACnE;AACA,cAAQ,MAAM,MAAM,GAAG,SAAS,GAAG;AACnC,aAAO,KAAK,EAAE,OAAO,MAAM,QAAQ,aAAa,MAAM,CAAC;AAAA,IACzD;AAAA,EACF;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MACF,aAAa;AAAA,QACX,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,QACvB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS;AAAA,MAClD;AAAA,MACA,aAAa;AAAA,QACX,cAAc;AAAA,QACd,gBAAgB;AAAA,QAChB,eAAe;AAAA,MACjB;AAAA,IACF;AAAA,IACA,OAAO,EAAE,OAAO,MAAM,MACpB,KAAK,EAAE,aAAa,QAAQ,OAAO,EAAE,OAAO,SAAS,EAAE,CAAC,EAAE,CAAC;AAAA,EAC/D;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MACF,aAAa;AAAA,QACX,MAAM,EACH,OAAO,EACP,SAAS,oDAAoD;AAAA,QAChE,QAAQ,EACL,KAAK,CAAC,YAAY,MAAM,CAAC,EACzB,SAAS,EACT;AAAA,UACC;AAAA,QACF;AAAA,MACJ;AAAA,MACA,aAAa;AAAA,QACX,cAAc;AAAA,QACd,gBAAgB;AAAA,QAChB,eAAe;AAAA,MACjB;AAAA,IACF;AAAA,IACA,OAAO,EAAE,MAAM,OAAO,MAAM;AAC1B,UAAI;AACF,YAAI,WAAW;AACb,iBAAO,KAAK,EAAE,GAAG,SAAS,IAAI,GAAG,YAAY,cAAc,IAAI,EAAE,CAAC;AACpE,eAAO,KAAK,UAAU,IAAI,CAAC;AAAA,MAC7B,SAAS,GAAG;AACV,eAAO,MAAO,EAAY,OAAO;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MACF,aAAa;AAAA,QACX,MAAM,EACH,OAAO,EACP,SAAS,EACT,SAAS,gDAAgD;AAAA,MAC9D;AAAA,MACA,aAAa;AAAA,QACX,cAAc;AAAA,QACd,gBAAgB;AAAA,QAChB,eAAe;AAAA,MACjB;AAAA,IACF;AAAA,IACA,OAAO,EAAE,KAAK,MAAM;AAClB,UAAI,QAAQ,aAAa;AACzB,UAAI;AACF,gBAAQ,MAAM,OAAO,CAAC,MAAM,EAAE,KAAK,SAAS,KAAK,YAAY,CAAC,CAAC;AACjE,aAAO,KAAK,EAAE,OAAO,MAAM,QAAQ,UAAU,MAAM,CAAC;AAAA,IACtD;AAAA,EACF;AAEA,MAAI,QAAQ,eAAe;AACzB,IAAAA,QAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa,CAAC;AAAA,QACd,aAAa;AAAA,UACX,cAAc;AAAA,UACd,gBAAgB;AAAA,UAChB,eAAe;AAAA,QACjB;AAAA,MACF;AAAA,MACA,YAAY,KAAK,EAAE,WAAW,MAAM,OAAO,cAAe,EAAE,CAAC;AAAA,IAC/D;AAAA,EACF;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,QACX;AAAA,QACA,SACI,4GACA;AAAA,QACJ;AAAA,MACF,EAAE,KAAK,GAAG;AAAA,MACV,aAAa;AAAA,QACX,WAAW,EAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,QAC7D,MAAM,EACH,OAAO,EACP,SAAS,EACT;AAAA,UACC;AAAA,QACF;AAAA,QACF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,SAAS;AAAA,QAClD,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,SAAS;AAAA,QACnD,SAAS,aAAa,SAAS;AAAA,QAC/B,OAAO,WAAW,SAAS;AAAA,QAC3B,QAAQ,EACL,KAAK,CAAC,OAAO,KAAK,CAAC,EACnB,SAAS,EACT;AAAA,UACC;AAAA,QACF;AAAA,QACF,UAAU,EACP,OAAO,EACP,IAAI,EACJ,IAAI,EAAE,EACN,IAAI,IAAI,EACR,SAAS,EACT;AAAA,UACC;AAAA,QACF;AAAA,QACF,GAAI,QAAQ,gBACR;AAAA,UACE,OAAO,EACJ,OAAO,EACP,SAAS,EACT;AAAA,YACC;AAAA,UACF;AAAA,QACJ,IACA,CAAC;AAAA,QACL,GAAI,SACA,CAAC,IACD;AAAA,UACE,QAAQ,EACL,OAAO,EACP,SAAS,EACT;AAAA,YACC;AAAA,UACF;AAAA,QACJ;AAAA,MACN;AAAA,MACA,aAAa;AAAA,QACX,cAAc;AAAA,QACd,gBAAgB;AAAA,QAChB,eAAe;AAAA,MACjB;AAAA,IACF;AAAA,IACA,OAAO,SAAS;AACd,YAAM,QAAQ,EAAE,GAAI,KAAiC;AACrD,YAAM,YACJ,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ;AAClD,aAAO,MAAM;AACb,UAAI,WAAW;AACb,cAAM,OAAO,MAAM,QAAQ,gBAAgB,IAAI;AAAA,UAC7C,CAAC,MAAM,EAAE,SAAS;AAAA,QACpB;AACA,YAAI,CAAC;AACH,iBAAO;AAAA,YACL,sBAAsB,SAAS;AAAA,UACjC;AACF,cAAM,cAAuC,EAAE,QAAQ,IAAI,OAAO;AAClE,YAAI,IAAI;AACN,sBAAY,kBAAkB,IAAI;AACpC,cAAM,UAAU;AAAA,UACd,GAAG;AAAA,UACH,GAAK,MAAM,WAAuC,CAAC;AAAA,QACrD;AACA,YAAI,CAAC,MAAM,aAAa,IAAI;AAC1B,gBAAM,YAAY,IAAI;AAAA,MAC1B;AACA,UAAI;AACJ,UAAI;AACF,qBAAa,iBAAiB,KAAK;AAAA,MACrC,SAAS,GAAG;AACV,eAAO,MAAO,EAAY,OAAO;AAAA,MACnC;AACA,YAAM,EAAE,SAAS,SAAS,IAAI;AAC9B,YAAM,SAAS,SAAS,SAAa,KAA6B;AAClE,YAAM,SACH,KAAK,WACL,QAAQ,YAAY,EAAE,SAAS,MAAM,IAAI,QAAQ;AAEpD,UAAI;AACF,cAAM,QAAQ,eAAe,OAAO;AAAA,MACtC,SAAS,GAAG;AACV,eAAO,MAAO,EAAY,OAAO;AAAA,MACnC;AACA,YAAM,SAAS,MAAM,UAAU,OAAO;AACtC,YAAM,QAAQ,WAAW,QAAQ,MAAM;AAEvC,YAAM,UAAqC,CAAC;AAC5C,YAAM,OAAgC;AAAA,QACpC,WAAW,OAAO;AAAA,QAClB,MAAM,OAAO;AAAA,QACb,OAAO,OAAO;AAAA,QACd,QAAQ,OAAO;AAAA,QACf;AAAA,QACA,UAAU,OAAO,IAAI;AAAA,QACrB,UAAU,OAAO;AAAA;AAAA,QAEjB,SAAS,EAAE,GAAG,SAAS,MAAM,OAAO,KAAK;AAAA,QACzC,GAAI,SAAS,SAAS,EAAE,SAAS,IAAI,CAAC;AAAA,MACxC;AAEA,UAAI,QAAQ;AACV,aAAK,MAAM;AAAA,UACT,OAAO;AAAA,UACP,EAAE,GAAG,SAAS,MAAM,OAAO,KAAK;AAAA,UAChC;AAAA,QACF;AACA,aAAK,SAAS;AAAA,UACZ,OAAO;AAAA,UACP,EAAE,GAAG,SAAS,MAAM,OAAO,KAAK;AAAA,UAChC;AAAA,QACF;AAAA,MACF;AAEA,UAAI,QAAQ;AACV,cAAM,OAAO,QAAQ,MAAM;AAC3B,cAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,cAAM,QACJ,WAAW,QACP,MAAM,UAAU,OAAO,KAAK,EAAE,OAAO,KAAK,YAAY,OAAO,MAAM,CAAC,IACpE,OAAO,KAAK,OAAO,KAAK,MAAM;AACpC,cAAM,UAAU,MAAM,KAAK;AAC3B,aAAK,UAAU;AACf,aAAK,QAAQ,MAAM;AAAA,MACrB;AAEA,UAAI,WAAW,OAAO;AACpB,YAAI,OAAO,IAAI,UAAU,aAAa,CAAC,QAAQ;AAC7C,kBAAQ,KAAK;AAAA,YACX,MAAM;AAAA,YACN,UAAU;AAAA,cACR,KAAK,qBAAqB,OAAO,SAAS,IAAI,OAAO,IAAI;AAAA,cACzD,UAAU;AAAA,cACV,MAAM,OAAO;AAAA,YACf;AAAA,UACF,CAAC;AAAA,QACH,WAAW,CAAC,UAAU,CAAC,QAAQ;AAC7B,eAAK,OAAO,UAAU,OAAO,IAAI,MAAM;AAAA,QACzC;AAAA,MACF;AAIA,YAAM,eAAe,KAAK,IAAI,KAAK,YAAY,KAAK,IAAI;AACxD,YAAM,MAAM,MAAM,UAAU,OAAO,KAAK,EAAE,OAAO,aAAa,CAAC;AAC/D,UAAI,IAAI,UAAU,WAAW;AAC3B,gBAAQ,KAAK;AAAA,UACX,MAAM;AAAA,UACN,MAAM,IAAI,SAAS,QAAQ;AAAA,UAC3B,UAAU;AAAA,QACZ,CAAC;AAAA,MACH,OAAO;AACL,cAAM,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,OAAO,IAAI,CAAC;AACxD,gBAAQ,KAAK;AAAA,UACX,MAAM;AAAA,UACN,MAAM,MAAM,SAAS,QAAQ;AAAA,UAC7B,UAAU;AAAA,QACZ,CAAC;AACD,aAAK,OAAO,kDAAkD,IAAI,MAAM;AAAA,MAC1E;AAEA,cAAQ,QAAQ,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,CAAC;AACrE,aAAO,EAAE,QAAQ;AAAA,IACnB;AAAA,EACF;AAEA,SAAOA;AACT;AAEO,SAAS,UACd,SACA,KACA,QACQ;AACR,QAAM,IAAI,IAAI;AAAA,IACZ,cAAc,mBAAmB,IAAI,SAAS,CAAC,IAAI,MAAM;AAAA,IACzD;AAAA,EACF;AACA,MAAI,IAAI,SAAS,OAAW,GAAE,aAAa,IAAI,QAAQ,OAAO,IAAI,IAAI,CAAC;AACvE,MAAI,IAAI,MAAO,GAAE,aAAa,IAAI,SAAS,OAAO,IAAI,KAAK,CAAC;AAC5D,MAAI,IAAI,OAAQ,GAAE,aAAa,IAAI,UAAU,OAAO,IAAI,MAAM,CAAC;AAC/D,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,IAAI,WAAW,CAAC,CAAC,GAAG;AACtD,QAAI,MAAM,OAAW;AACrB,MAAE,aAAa,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC;AAAA,EAClE;AACA,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,IAAI,SAAS,CAAC,CAAC,GAAG;AACpD,QAAI,MAAM,OAAW,GAAE,aAAa,IAAI,GAAG,OAAO,CAAC,CAAC;AAAA,EACtD;AACA,SAAO,EAAE,SAAS;AACpB;AAEA,SAAS,KAAK,OAAgC;AAC5C,SAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,EAAE,CAAC,EAAE;AAC7E;AACA,SAAS,KAAK,OAA+B;AAC3C,SAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,MAAM,CAAC,EAAE;AACpD;AACA,SAAS,MAAM,SAAiC;AAC9C,SAAO,EAAE,SAAS,MAAM,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,QAAQ,CAAC,EAAE;AACrE;;;ADheA,IAAI,QAAQ,KAAK,SAAS,QAAQ,KAAK,QAAQ,KAAK,SAAS,IAAI,GAAG;AAClE,UAAQ,OAAO;AAAA,IACb;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AACA,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,SAAS,gBAAgB;AAC/B,IAAM,YAAY,IAAI,qBAAqB;AAC3C,MAAM,OAAO,QAAQ,SAAS;","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 /** Return permanent hosted URLs (default true). Hosts set false for anonymous callers. */\n hostedUrls?: boolean;\n /** Where to get a free key; mentioned when hostedUrls is false. */\n keysUrl?: 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 hint: z\n .string()\n .optional()\n .describe('Hosted, no API key: how to get permanent URLs.'),\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 ? 'With an API key, 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 && hosted.hostedUrls === false) {\n meta.hint = `Permanent hosted URLs need an API key (free keys include 200 renders a month and brand kits)${hosted.keysUrl ? ': ' + hosted.keysUrl : ''}.`;\n } else 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,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@creatica/mcp",
|
|
3
3
|
"mcpName": "app.creatica/backgrounds",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"description": "MCP server for Creatica: let AI agents browse, describe and generate procedural SVG/PNG backgrounds on demand.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|