@webspire/mcp 0.12.0 → 0.13.1
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/README.md +7 -3
- package/css/webspire-components.css +55 -0
- package/data/registry.json +12210 -719
- package/dist/registration.js +283 -3
- package/dist/search.d.ts +26 -0
- package/dist/search.js +217 -4
- package/dist/search.test.d.ts +1 -0
- package/dist/search.test.js +351 -0
- package/dist/types.d.ts +68 -0
- package/package.json +11 -8
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { searchPatterns, searchSnippets } from './search.js';
|
|
4
|
+
// --- Minimal fixture builders ---
|
|
5
|
+
function makeSnippet(overrides) {
|
|
6
|
+
const { meta: metaOverrides, ...rest } = overrides;
|
|
7
|
+
return {
|
|
8
|
+
title: overrides.title ?? 'Untitled',
|
|
9
|
+
description: overrides.description ?? '',
|
|
10
|
+
category: overrides.category ?? 'glass',
|
|
11
|
+
tags: overrides.tags ?? [],
|
|
12
|
+
responsive: overrides.responsive ?? false,
|
|
13
|
+
darkMode: overrides.darkMode ?? false,
|
|
14
|
+
updatedAt: '2025-01-01',
|
|
15
|
+
css: '',
|
|
16
|
+
...rest,
|
|
17
|
+
meta: {
|
|
18
|
+
lines: 10,
|
|
19
|
+
bytes: 200,
|
|
20
|
+
tailwind: '',
|
|
21
|
+
browser: 'baseline-2024',
|
|
22
|
+
accessibility: {
|
|
23
|
+
prefersReducedMotion: false,
|
|
24
|
+
prefersColorScheme: false,
|
|
25
|
+
supportsCheck: false,
|
|
26
|
+
},
|
|
27
|
+
customProperties: [],
|
|
28
|
+
classes: [],
|
|
29
|
+
useCases: [],
|
|
30
|
+
solves: [],
|
|
31
|
+
usageExample: '',
|
|
32
|
+
...metaOverrides,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function makePattern(overrides) {
|
|
37
|
+
return {
|
|
38
|
+
title: overrides.title ?? 'Untitled',
|
|
39
|
+
summary: overrides.summary ?? '',
|
|
40
|
+
description: overrides.description,
|
|
41
|
+
kind: overrides.kind ?? 'section',
|
|
42
|
+
tier: overrides.tier ?? 'base',
|
|
43
|
+
extends: null,
|
|
44
|
+
tags: overrides.tags ?? [],
|
|
45
|
+
semantics: overrides.semantics ?? {
|
|
46
|
+
domains: [],
|
|
47
|
+
tones: [],
|
|
48
|
+
uxGoals: [],
|
|
49
|
+
avoidForTones: [],
|
|
50
|
+
avoidForScenarios: [],
|
|
51
|
+
neighbors: { before: [], after: [], neverWith: [] },
|
|
52
|
+
},
|
|
53
|
+
search: overrides.search ?? { intent: [], keywords: [], useCases: [] },
|
|
54
|
+
frameworks: { html: true, astro: false, webComponent: false },
|
|
55
|
+
tokens: { color: '', radius: '', shadow: '', spacing: '', typography: '' },
|
|
56
|
+
capabilities: {
|
|
57
|
+
responsive: true,
|
|
58
|
+
darkMode: false,
|
|
59
|
+
animated: false,
|
|
60
|
+
interactive: false,
|
|
61
|
+
dependencies: [],
|
|
62
|
+
animationPreset: 'none',
|
|
63
|
+
},
|
|
64
|
+
slots: [],
|
|
65
|
+
props: [],
|
|
66
|
+
files: { html: '', css: null, js: null },
|
|
67
|
+
install: { copyPasteReady: true, ssrSafe: true, tailwindOnly: true, notes: [] },
|
|
68
|
+
governance: {
|
|
69
|
+
status: 'published',
|
|
70
|
+
quality: 'stable',
|
|
71
|
+
owner: 'test',
|
|
72
|
+
updatedAt: '2025-01-01',
|
|
73
|
+
},
|
|
74
|
+
ai: overrides.ai,
|
|
75
|
+
qualityTier: overrides.qualityTier,
|
|
76
|
+
html: '',
|
|
77
|
+
css: null,
|
|
78
|
+
js: null,
|
|
79
|
+
...overrides,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
// --- Snippet search tests ---
|
|
83
|
+
test('searchSnippets: exact ID match ranks first', () => {
|
|
84
|
+
const snippets = [
|
|
85
|
+
makeSnippet({ id: 'glass/frosted', title: 'Frosted Glass', category: 'glass' }),
|
|
86
|
+
makeSnippet({ id: 'text/gradient', title: 'Gradient Text', category: 'text' }),
|
|
87
|
+
makeSnippet({ id: 'animations/fade-in', title: 'Fade In Animation', category: 'animations' }),
|
|
88
|
+
];
|
|
89
|
+
const results = searchSnippets(snippets, { query: 'frosted' });
|
|
90
|
+
assert.equal(results.length >= 1, true);
|
|
91
|
+
assert.equal(results[0].id, 'glass/frosted');
|
|
92
|
+
});
|
|
93
|
+
test('searchSnippets: search by title keyword', () => {
|
|
94
|
+
const snippets = [
|
|
95
|
+
makeSnippet({ id: 'glass/frosted', title: 'Frosted Glass Effect', category: 'glass' }),
|
|
96
|
+
makeSnippet({ id: 'text/headline', title: 'Headline Typography', category: 'text' }),
|
|
97
|
+
];
|
|
98
|
+
const results = searchSnippets(snippets, { query: 'frosted' });
|
|
99
|
+
assert.equal(results.length, 1);
|
|
100
|
+
assert.equal(results[0].id, 'glass/frosted');
|
|
101
|
+
});
|
|
102
|
+
test('searchSnippets: search by tag', () => {
|
|
103
|
+
const snippets = [
|
|
104
|
+
makeSnippet({
|
|
105
|
+
id: 'glass/panel',
|
|
106
|
+
title: 'Glass Panel',
|
|
107
|
+
category: 'glass',
|
|
108
|
+
tags: ['glassmorphism', 'backdrop'],
|
|
109
|
+
}),
|
|
110
|
+
makeSnippet({ id: 'text/clip', title: 'Text Clip', category: 'text', tags: ['typography'] }),
|
|
111
|
+
];
|
|
112
|
+
const results = searchSnippets(snippets, { query: 'glassmorphism' });
|
|
113
|
+
assert.equal(results.length, 1);
|
|
114
|
+
assert.equal(results[0].id, 'glass/panel');
|
|
115
|
+
});
|
|
116
|
+
test('searchSnippets: search by solves field', () => {
|
|
117
|
+
const snippets = [
|
|
118
|
+
makeSnippet({
|
|
119
|
+
id: 'glass/overlay',
|
|
120
|
+
title: 'Overlay',
|
|
121
|
+
category: 'glass',
|
|
122
|
+
meta: { solves: ['adds depth to modal backgrounds'], useCases: [] },
|
|
123
|
+
}),
|
|
124
|
+
makeSnippet({ id: 'text/label', title: 'Label Style', category: 'text' }),
|
|
125
|
+
];
|
|
126
|
+
const results = searchSnippets(snippets, { query: 'modal backgrounds' });
|
|
127
|
+
assert.equal(results.length, 1);
|
|
128
|
+
assert.equal(results[0].id, 'glass/overlay');
|
|
129
|
+
});
|
|
130
|
+
test('searchSnippets: search by useCases field', () => {
|
|
131
|
+
const snippets = [
|
|
132
|
+
makeSnippet({
|
|
133
|
+
id: 'scroll/reveal',
|
|
134
|
+
title: 'Scroll Reveal',
|
|
135
|
+
category: 'scroll',
|
|
136
|
+
meta: { useCases: ['animate elements into view on scroll into viewport'], solves: [] },
|
|
137
|
+
}),
|
|
138
|
+
makeSnippet({ id: 'decorative/divider', title: 'Section Divider', category: 'decorative' }),
|
|
139
|
+
];
|
|
140
|
+
const results = searchSnippets(snippets, { query: 'animate elements viewport' });
|
|
141
|
+
assert.equal(results.length, 1);
|
|
142
|
+
assert.equal(results[0].id, 'scroll/reveal');
|
|
143
|
+
});
|
|
144
|
+
test('searchSnippets: filter by category', () => {
|
|
145
|
+
const snippets = [
|
|
146
|
+
makeSnippet({ id: 'glass/frosted', title: 'Frosted Glass', category: 'glass' }),
|
|
147
|
+
makeSnippet({ id: 'glass/tinted', title: 'Tinted Glass', category: 'glass' }),
|
|
148
|
+
makeSnippet({ id: 'text/gradient', title: 'Gradient Text', category: 'text' }),
|
|
149
|
+
];
|
|
150
|
+
const results = searchSnippets(snippets, { query: 'glass', category: 'glass' });
|
|
151
|
+
assert.equal(results.every((s) => s.category === 'glass'), true);
|
|
152
|
+
assert.equal(results.some((s) => s.id === 'text/gradient'), false);
|
|
153
|
+
});
|
|
154
|
+
test('searchSnippets: empty query returns empty array', () => {
|
|
155
|
+
const snippets = [
|
|
156
|
+
makeSnippet({ id: 'glass/frosted', title: 'Frosted Glass', category: 'glass' }),
|
|
157
|
+
];
|
|
158
|
+
const results = searchSnippets(snippets, { query: '' });
|
|
159
|
+
assert.equal(results.length, 0);
|
|
160
|
+
});
|
|
161
|
+
// --- Pattern search tests ---
|
|
162
|
+
test('searchPatterns: search by family name ranks first', () => {
|
|
163
|
+
const patterns = [
|
|
164
|
+
makePattern({
|
|
165
|
+
id: 'hero/base',
|
|
166
|
+
family: 'hero',
|
|
167
|
+
title: 'Hero Base',
|
|
168
|
+
search: { intent: ['full-width hero section'], keywords: ['hero'], useCases: [] },
|
|
169
|
+
}),
|
|
170
|
+
makePattern({
|
|
171
|
+
id: 'pricing/base',
|
|
172
|
+
family: 'pricing',
|
|
173
|
+
title: 'Pricing Base',
|
|
174
|
+
search: { intent: ['pricing table'], keywords: ['pricing'], useCases: [] },
|
|
175
|
+
}),
|
|
176
|
+
makePattern({
|
|
177
|
+
id: 'faq/base',
|
|
178
|
+
family: 'faq',
|
|
179
|
+
title: 'FAQ Base',
|
|
180
|
+
search: { intent: ['questions and answers'], keywords: ['faq'], useCases: [] },
|
|
181
|
+
}),
|
|
182
|
+
];
|
|
183
|
+
const results = searchPatterns(patterns, { query: 'hero' });
|
|
184
|
+
assert.equal(results.length >= 1, true);
|
|
185
|
+
assert.equal(results[0].family, 'hero');
|
|
186
|
+
});
|
|
187
|
+
test('searchPatterns: filter by tier base only', () => {
|
|
188
|
+
const patterns = [
|
|
189
|
+
makePattern({ id: 'hero/base', family: 'hero', title: 'Hero Base', tier: 'base' }),
|
|
190
|
+
makePattern({
|
|
191
|
+
id: 'hero/animated',
|
|
192
|
+
family: 'hero',
|
|
193
|
+
title: 'Hero Animated',
|
|
194
|
+
tier: 'enhanced',
|
|
195
|
+
search: { intent: ['animated hero'], keywords: ['hero', 'animation'], useCases: [] },
|
|
196
|
+
}),
|
|
197
|
+
makePattern({ id: 'pricing/base', family: 'pricing', title: 'Pricing Base', tier: 'base' }),
|
|
198
|
+
];
|
|
199
|
+
const results = searchPatterns(patterns, { query: 'hero', tier: 'base' });
|
|
200
|
+
assert.equal(results.every((p) => p.tier === 'base'), true);
|
|
201
|
+
assert.equal(results.some((p) => p.id === 'hero/animated'), false);
|
|
202
|
+
});
|
|
203
|
+
test('searchPatterns: filter by domain', () => {
|
|
204
|
+
const saasSemantics = {
|
|
205
|
+
domains: ['saas'],
|
|
206
|
+
tones: ['modern'],
|
|
207
|
+
uxGoals: ['drive_signup'],
|
|
208
|
+
avoidForTones: [],
|
|
209
|
+
avoidForScenarios: [],
|
|
210
|
+
neighbors: { before: [], after: [], neverWith: [] },
|
|
211
|
+
};
|
|
212
|
+
const otherSemantics = {
|
|
213
|
+
domains: ['legal'],
|
|
214
|
+
tones: ['serious'],
|
|
215
|
+
uxGoals: ['build_trust'],
|
|
216
|
+
avoidForTones: [],
|
|
217
|
+
avoidForScenarios: [],
|
|
218
|
+
neighbors: { before: [], after: [], neverWith: [] },
|
|
219
|
+
};
|
|
220
|
+
const patterns = [
|
|
221
|
+
makePattern({
|
|
222
|
+
id: 'hero/base',
|
|
223
|
+
family: 'hero',
|
|
224
|
+
title: 'Hero',
|
|
225
|
+
semantics: saasSemantics,
|
|
226
|
+
search: { intent: ['hero for saas landing'], keywords: ['hero', 'saas'], useCases: [] },
|
|
227
|
+
}),
|
|
228
|
+
makePattern({
|
|
229
|
+
id: 'legal/base',
|
|
230
|
+
family: 'legal',
|
|
231
|
+
title: 'Legal Page',
|
|
232
|
+
semantics: otherSemantics,
|
|
233
|
+
search: { intent: ['legal information page'], keywords: ['legal'], useCases: [] },
|
|
234
|
+
}),
|
|
235
|
+
];
|
|
236
|
+
const results = searchPatterns(patterns, { query: 'hero', domain: 'saas' });
|
|
237
|
+
assert.equal(results.length, 1);
|
|
238
|
+
assert.equal(results[0].id, 'hero/base');
|
|
239
|
+
});
|
|
240
|
+
test('searchPatterns: filter by tone', () => {
|
|
241
|
+
const modernSemantics = {
|
|
242
|
+
domains: ['saas'],
|
|
243
|
+
tones: ['modern'],
|
|
244
|
+
uxGoals: [],
|
|
245
|
+
avoidForTones: [],
|
|
246
|
+
avoidForScenarios: [],
|
|
247
|
+
neighbors: { before: [], after: [], neverWith: [] },
|
|
248
|
+
};
|
|
249
|
+
const seriousSemantics = {
|
|
250
|
+
domains: ['legal'],
|
|
251
|
+
tones: ['serious'],
|
|
252
|
+
uxGoals: [],
|
|
253
|
+
avoidForTones: [],
|
|
254
|
+
avoidForScenarios: [],
|
|
255
|
+
neighbors: { before: [], after: [], neverWith: [] },
|
|
256
|
+
};
|
|
257
|
+
const patterns = [
|
|
258
|
+
makePattern({
|
|
259
|
+
id: 'hero/modern',
|
|
260
|
+
family: 'hero',
|
|
261
|
+
title: 'Modern Hero',
|
|
262
|
+
semantics: modernSemantics,
|
|
263
|
+
search: { intent: ['modern hero section'], keywords: ['hero', 'modern'], useCases: [] },
|
|
264
|
+
}),
|
|
265
|
+
makePattern({
|
|
266
|
+
id: 'hero/serious',
|
|
267
|
+
family: 'hero',
|
|
268
|
+
title: 'Serious Hero',
|
|
269
|
+
semantics: seriousSemantics,
|
|
270
|
+
search: { intent: ['hero for law firm'], keywords: ['hero', 'serious'], useCases: [] },
|
|
271
|
+
}),
|
|
272
|
+
];
|
|
273
|
+
const results = searchPatterns(patterns, { query: 'hero', tone: 'modern' });
|
|
274
|
+
assert.equal(results.length, 1);
|
|
275
|
+
assert.equal(results[0].id, 'hero/modern');
|
|
276
|
+
});
|
|
277
|
+
test('searchPatterns: filter by uxGoal', () => {
|
|
278
|
+
const signupSemantics = {
|
|
279
|
+
domains: ['saas'],
|
|
280
|
+
tones: ['modern'],
|
|
281
|
+
uxGoals: ['drive_signup'],
|
|
282
|
+
avoidForTones: [],
|
|
283
|
+
avoidForScenarios: [],
|
|
284
|
+
neighbors: { before: [], after: [], neverWith: [] },
|
|
285
|
+
};
|
|
286
|
+
const trustSemantics = {
|
|
287
|
+
domains: ['saas'],
|
|
288
|
+
tones: ['modern'],
|
|
289
|
+
uxGoals: ['build_trust'],
|
|
290
|
+
avoidForTones: [],
|
|
291
|
+
avoidForScenarios: [],
|
|
292
|
+
neighbors: { before: [], after: [], neverWith: [] },
|
|
293
|
+
};
|
|
294
|
+
const patterns = [
|
|
295
|
+
makePattern({
|
|
296
|
+
id: 'cta/base',
|
|
297
|
+
family: 'cta',
|
|
298
|
+
title: 'CTA Section',
|
|
299
|
+
semantics: signupSemantics,
|
|
300
|
+
search: { intent: ['call to action for signups'], keywords: ['cta', 'signup'], useCases: [] },
|
|
301
|
+
}),
|
|
302
|
+
makePattern({
|
|
303
|
+
id: 'testimonials/base',
|
|
304
|
+
family: 'testimonials',
|
|
305
|
+
title: 'Testimonials',
|
|
306
|
+
semantics: trustSemantics,
|
|
307
|
+
search: {
|
|
308
|
+
intent: ['social proof section'],
|
|
309
|
+
keywords: ['testimonials', 'trust'],
|
|
310
|
+
useCases: [],
|
|
311
|
+
},
|
|
312
|
+
}),
|
|
313
|
+
];
|
|
314
|
+
const results = searchPatterns(patterns, { query: 'cta', uxGoal: 'drive_signup' });
|
|
315
|
+
assert.equal(results.length, 1);
|
|
316
|
+
assert.equal(results[0].id, 'cta/base');
|
|
317
|
+
});
|
|
318
|
+
test('searchPatterns: empty query returns empty array', () => {
|
|
319
|
+
const patterns = [
|
|
320
|
+
makePattern({
|
|
321
|
+
id: 'hero/base',
|
|
322
|
+
family: 'hero',
|
|
323
|
+
title: 'Hero',
|
|
324
|
+
search: { intent: [], keywords: ['hero'], useCases: [] },
|
|
325
|
+
}),
|
|
326
|
+
];
|
|
327
|
+
const results = searchPatterns(patterns, { query: '' });
|
|
328
|
+
assert.equal(results.length, 0);
|
|
329
|
+
});
|
|
330
|
+
test('searchPatterns: recommended qualityTier gets score boost', () => {
|
|
331
|
+
const baseSearch = { intent: ['landing section'], keywords: ['section'], useCases: [] };
|
|
332
|
+
const patterns = [
|
|
333
|
+
makePattern({
|
|
334
|
+
id: 'hero/base',
|
|
335
|
+
family: 'hero',
|
|
336
|
+
title: 'Hero Base',
|
|
337
|
+
qualityTier: 'recommended',
|
|
338
|
+
search: baseSearch,
|
|
339
|
+
}),
|
|
340
|
+
makePattern({
|
|
341
|
+
id: 'hero/plain',
|
|
342
|
+
family: 'hero-plain',
|
|
343
|
+
title: 'Hero Plain',
|
|
344
|
+
qualityTier: 'usable',
|
|
345
|
+
search: baseSearch,
|
|
346
|
+
}),
|
|
347
|
+
];
|
|
348
|
+
const results = searchPatterns(patterns, { query: 'section' });
|
|
349
|
+
// recommended gets +15 score boost, so hero/base should rank first
|
|
350
|
+
assert.equal(results[0].id, 'hero/base');
|
|
351
|
+
});
|
package/dist/types.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export interface SnippetEntry {
|
|
|
32
32
|
updatedAt: string;
|
|
33
33
|
meta: SnippetMeta;
|
|
34
34
|
css: string;
|
|
35
|
+
polishLayer?: 'motion' | 'surface' | 'depth' | 'interaction' | 'text' | 'decorative' | null;
|
|
35
36
|
}
|
|
36
37
|
export interface PatternEntry {
|
|
37
38
|
id: string;
|
|
@@ -114,6 +115,18 @@ export interface PatternEntry {
|
|
|
114
115
|
owner: string;
|
|
115
116
|
updatedAt: string;
|
|
116
117
|
};
|
|
118
|
+
qualityTier?: 'recommended' | 'usable' | 'experimental';
|
|
119
|
+
quality?: {
|
|
120
|
+
checks: {
|
|
121
|
+
responsive: boolean;
|
|
122
|
+
darkMode: boolean;
|
|
123
|
+
a11y: boolean;
|
|
124
|
+
reducedMotion: boolean;
|
|
125
|
+
tokenized: boolean;
|
|
126
|
+
};
|
|
127
|
+
} | null;
|
|
128
|
+
subtype?: string | null;
|
|
129
|
+
suggestedSnippets?: string[];
|
|
117
130
|
html: string;
|
|
118
131
|
css: string | null;
|
|
119
132
|
js: string | null;
|
|
@@ -150,6 +163,11 @@ export interface TemplateEntry {
|
|
|
150
163
|
owner: string;
|
|
151
164
|
updatedAt: string;
|
|
152
165
|
};
|
|
166
|
+
composition?: {
|
|
167
|
+
patterns: string[];
|
|
168
|
+
snippets: string[];
|
|
169
|
+
tokens: string[];
|
|
170
|
+
} | null;
|
|
153
171
|
html: string;
|
|
154
172
|
}
|
|
155
173
|
export interface FontEntry {
|
|
@@ -248,6 +266,54 @@ export interface MotionRecipeEntry {
|
|
|
248
266
|
};
|
|
249
267
|
html: string;
|
|
250
268
|
}
|
|
269
|
+
export interface SkillEntry {
|
|
270
|
+
id: string;
|
|
271
|
+
title: string;
|
|
272
|
+
summary: string;
|
|
273
|
+
tone: string;
|
|
274
|
+
domains: string[];
|
|
275
|
+
uxGoals: string[];
|
|
276
|
+
tags: string[];
|
|
277
|
+
colors: Record<string, string>;
|
|
278
|
+
typography: {
|
|
279
|
+
heading: string;
|
|
280
|
+
body: string;
|
|
281
|
+
mono: string;
|
|
282
|
+
scale: number[];
|
|
283
|
+
weight: {
|
|
284
|
+
heading: number;
|
|
285
|
+
body: number;
|
|
286
|
+
ui: number;
|
|
287
|
+
};
|
|
288
|
+
};
|
|
289
|
+
spacing: {
|
|
290
|
+
base: number;
|
|
291
|
+
scale: number[];
|
|
292
|
+
};
|
|
293
|
+
radius: Record<string, string>;
|
|
294
|
+
relatedPatterns: string[];
|
|
295
|
+
suggestedTemplate: string | null;
|
|
296
|
+
governance: {
|
|
297
|
+
updatedAt: string;
|
|
298
|
+
};
|
|
299
|
+
/** Full markdown body — the design rules AI agents should follow */
|
|
300
|
+
body: string;
|
|
301
|
+
}
|
|
302
|
+
export interface StarterPathEntry {
|
|
303
|
+
id: string;
|
|
304
|
+
title: string;
|
|
305
|
+
description: string;
|
|
306
|
+
goal: string;
|
|
307
|
+
patterns: string[];
|
|
308
|
+
snippets: string[];
|
|
309
|
+
fonts: string[];
|
|
310
|
+
tokenPreset: string | null;
|
|
311
|
+
tags: string[];
|
|
312
|
+
governance: {
|
|
313
|
+
status: 'draft' | 'review' | 'published' | 'deprecated';
|
|
314
|
+
updatedAt: string;
|
|
315
|
+
};
|
|
316
|
+
}
|
|
251
317
|
export interface Registry {
|
|
252
318
|
version: string;
|
|
253
319
|
generated: string;
|
|
@@ -256,6 +322,8 @@ export interface Registry {
|
|
|
256
322
|
templates?: TemplateEntry[];
|
|
257
323
|
canvasEffects?: CanvasEffectEntry[];
|
|
258
324
|
motionRecipes?: MotionRecipeEntry[];
|
|
325
|
+
skills?: SkillEntry[];
|
|
326
|
+
starterPaths?: StarterPathEntry[];
|
|
259
327
|
fonts?: FontsData;
|
|
260
328
|
mountCanvas?: string;
|
|
261
329
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webspire/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "MCP server for Webspire — AI-native discovery of CSS snippets, UI patterns, canvas effects, page templates, motion recipes, and font recommendations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "tsc && node scripts/postbuild.mjs && node scripts/bundle-registry.mjs",
|
|
19
19
|
"dev": "tsx src/index.ts",
|
|
20
|
-
"clean": "rm -rf dist"
|
|
20
|
+
"clean": "rm -rf dist",
|
|
21
|
+
"test:registry": "node --test src/registry.test.mjs",
|
|
22
|
+
"test:search": "node --import tsx/esm --test src/search.test.ts",
|
|
23
|
+
"test": "node --test src/registry.test.mjs && node --import tsx/esm --test src/search.test.ts"
|
|
21
24
|
},
|
|
22
25
|
"files": [
|
|
23
26
|
"dist",
|
|
@@ -25,13 +28,13 @@
|
|
|
25
28
|
"css"
|
|
26
29
|
],
|
|
27
30
|
"dependencies": {
|
|
28
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
29
|
-
"zod": "^4.
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
32
|
+
"zod": "^4.4.3"
|
|
30
33
|
},
|
|
31
34
|
"devDependencies": {
|
|
32
|
-
"@types/node": "^
|
|
33
|
-
"tsx": "^4.
|
|
34
|
-
"typescript": "^
|
|
35
|
+
"@types/node": "^25.8.0",
|
|
36
|
+
"tsx": "^4.22.1",
|
|
37
|
+
"typescript": "^6.0.3"
|
|
35
38
|
},
|
|
36
39
|
"keywords": [
|
|
37
40
|
"mcp",
|
|
@@ -54,7 +57,7 @@
|
|
|
54
57
|
"engines": {
|
|
55
58
|
"node": ">=22.12.0"
|
|
56
59
|
},
|
|
57
|
-
"license": "
|
|
60
|
+
"license": "UNLICENSED",
|
|
58
61
|
"publishConfig": {
|
|
59
62
|
"access": "public"
|
|
60
63
|
}
|