@webspire/mcp 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/registry.json +1097 -2
- package/dist/index.js +1 -1
- package/dist/registration.js +159 -0
- package/dist/types.d.ts +35 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
package/dist/registration.js
CHANGED
|
@@ -58,6 +58,39 @@ function formatSnippetFull(s) {
|
|
|
58
58
|
.filter((line) => line !== '')
|
|
59
59
|
.join('\n');
|
|
60
60
|
}
|
|
61
|
+
function formatTemplateBrief(t) {
|
|
62
|
+
return [
|
|
63
|
+
`**${t.title}** (${t.id})`,
|
|
64
|
+
` ${t.summary}`,
|
|
65
|
+
` Category: ${t.category} | Style: ${t.style}`,
|
|
66
|
+
t.tags.length > 0 ? ` Tags: ${t.tags.join(', ')}` : '',
|
|
67
|
+
]
|
|
68
|
+
.filter(Boolean)
|
|
69
|
+
.join('\n');
|
|
70
|
+
}
|
|
71
|
+
function formatTemplateFull(t) {
|
|
72
|
+
return [
|
|
73
|
+
`# ${t.title}`,
|
|
74
|
+
'',
|
|
75
|
+
t.description ?? t.summary,
|
|
76
|
+
'',
|
|
77
|
+
'## Identity',
|
|
78
|
+
`ID: ${t.id}`,
|
|
79
|
+
`Category: ${t.category}`,
|
|
80
|
+
`Style: ${t.style}`,
|
|
81
|
+
'',
|
|
82
|
+
t.tags.length > 0 ? `## Tags\n${t.tags.join(', ')}` : '',
|
|
83
|
+
t.sections.length > 0 ? `## Sections\n${t.sections.join(', ')}` : '',
|
|
84
|
+
t.patterns.length > 0 ? `## Uses Patterns\n${t.patterns.join(', ')}` : '',
|
|
85
|
+
'',
|
|
86
|
+
'## HTML',
|
|
87
|
+
'```html',
|
|
88
|
+
t.html,
|
|
89
|
+
'```',
|
|
90
|
+
]
|
|
91
|
+
.filter((line) => line !== '')
|
|
92
|
+
.join('\n');
|
|
93
|
+
}
|
|
61
94
|
function formatPatternBrief(p) {
|
|
62
95
|
return [
|
|
63
96
|
`**${p.title}** (${p.id})`,
|
|
@@ -344,6 +377,84 @@ export function registerToolsWithProvider(server, getRegistry) {
|
|
|
344
377
|
content: [{ type: 'text', text: formatPatternFull(pattern) }],
|
|
345
378
|
};
|
|
346
379
|
});
|
|
380
|
+
server.tool('list_templates', 'List all available page templates grouped by category', {}, async () => {
|
|
381
|
+
const registry = await getRegistry();
|
|
382
|
+
const templates = registry.templates ?? [];
|
|
383
|
+
const counts = {};
|
|
384
|
+
for (const t of templates) {
|
|
385
|
+
counts[t.category] = (counts[t.category] ?? 0) + 1;
|
|
386
|
+
}
|
|
387
|
+
const result = Object.entries(counts)
|
|
388
|
+
.sort(([, a], [, b]) => b - a)
|
|
389
|
+
.map(([cat, count]) => `${cat}: ${count} template${count > 1 ? 's' : ''}`)
|
|
390
|
+
.join('\n');
|
|
391
|
+
return {
|
|
392
|
+
content: [
|
|
393
|
+
{
|
|
394
|
+
type: 'text',
|
|
395
|
+
text: `${templates.length} templates across ${Object.keys(counts).length} categories:\n\n${result}`,
|
|
396
|
+
},
|
|
397
|
+
],
|
|
398
|
+
};
|
|
399
|
+
});
|
|
400
|
+
server.tool('search_templates', 'Search Webspire page templates by keyword, category, or style.', {
|
|
401
|
+
query: z.string().describe('Search query, e.g. "saas landing", "portfolio dark", "shop"'),
|
|
402
|
+
category: z.string().optional().describe('Filter by category, e.g. saas-landing, agency, shop'),
|
|
403
|
+
style: z.string().optional().describe('Filter by style, e.g. modern, bold, minimal, corporate'),
|
|
404
|
+
}, async ({ query, category, style }) => {
|
|
405
|
+
const registry = await getRegistry();
|
|
406
|
+
let templates = registry.templates ?? [];
|
|
407
|
+
if (category)
|
|
408
|
+
templates = templates.filter((t) => t.category === category);
|
|
409
|
+
if (style)
|
|
410
|
+
templates = templates.filter((t) => t.style === style);
|
|
411
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
412
|
+
const results = templates
|
|
413
|
+
.map((t) => {
|
|
414
|
+
const haystack = [t.title, t.summary, t.description ?? '', t.category, t.style, ...t.tags, ...t.sections]
|
|
415
|
+
.join(' ')
|
|
416
|
+
.toLowerCase();
|
|
417
|
+
const score = terms.reduce((acc, term) => acc + (haystack.includes(term) ? 1 : 0), 0);
|
|
418
|
+
return { template: t, score };
|
|
419
|
+
})
|
|
420
|
+
.filter((row) => row.score > 0)
|
|
421
|
+
.sort((a, b) => b.score - a.score)
|
|
422
|
+
.slice(0, 10)
|
|
423
|
+
.map((row) => row.template);
|
|
424
|
+
if (results.length === 0) {
|
|
425
|
+
return {
|
|
426
|
+
content: [{ type: 'text', text: 'No templates found matching your query.' }],
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
content: [
|
|
431
|
+
{
|
|
432
|
+
type: 'text',
|
|
433
|
+
text: `Found ${results.length} template${results.length > 1 ? 's' : ''}:\n\n${results.map(formatTemplateBrief).join('\n\n')}`,
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
};
|
|
437
|
+
});
|
|
438
|
+
server.tool('get_template', 'Get full template HTML for a specific page template. Returns standalone HTML ready to use.', {
|
|
439
|
+
id: z.string().describe('Template ID, e.g. "saas-landing/modern", "shop/catalog"'),
|
|
440
|
+
}, async ({ id }) => {
|
|
441
|
+
const registry = await getRegistry();
|
|
442
|
+
const template = (registry.templates ?? []).find((t) => t.id === id);
|
|
443
|
+
if (!template) {
|
|
444
|
+
const available = (registry.templates ?? []).map((t) => t.id).join(', ');
|
|
445
|
+
return {
|
|
446
|
+
content: [
|
|
447
|
+
{
|
|
448
|
+
type: 'text',
|
|
449
|
+
text: `Template "${id}" not found. Available templates: ${available}`,
|
|
450
|
+
},
|
|
451
|
+
],
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
return {
|
|
455
|
+
content: [{ type: 'text', text: formatTemplateFull(template) }],
|
|
456
|
+
};
|
|
457
|
+
});
|
|
347
458
|
}
|
|
348
459
|
export function registerResourcesWithProvider(server, getRegistry) {
|
|
349
460
|
server.resource('categories', 'webspire://categories', {
|
|
@@ -468,4 +579,52 @@ export function registerResourcesWithProvider(server, getRegistry) {
|
|
|
468
579
|
],
|
|
469
580
|
};
|
|
470
581
|
});
|
|
582
|
+
server.resource('templates', 'webspire://templates', { description: 'List all page templates', mimeType: 'application/json' }, async () => {
|
|
583
|
+
const registry = await getRegistry();
|
|
584
|
+
const templates = (registry.templates ?? []).map((t) => ({
|
|
585
|
+
id: t.id,
|
|
586
|
+
title: t.title,
|
|
587
|
+
summary: t.summary,
|
|
588
|
+
category: t.category,
|
|
589
|
+
style: t.style,
|
|
590
|
+
}));
|
|
591
|
+
return {
|
|
592
|
+
contents: [
|
|
593
|
+
{
|
|
594
|
+
uri: 'webspire://templates',
|
|
595
|
+
mimeType: 'application/json',
|
|
596
|
+
text: JSON.stringify(templates, null, 2),
|
|
597
|
+
},
|
|
598
|
+
],
|
|
599
|
+
};
|
|
600
|
+
});
|
|
601
|
+
server.resource('template', 'webspire://template/{id}', {
|
|
602
|
+
description: 'Get full template data including HTML source',
|
|
603
|
+
mimeType: 'application/json',
|
|
604
|
+
}, async (uri) => {
|
|
605
|
+
const parts = uri.pathname.replace('//', '').split('/');
|
|
606
|
+
const id = parts.slice(1).join('/');
|
|
607
|
+
const registry = await getRegistry();
|
|
608
|
+
const template = (registry.templates ?? []).find((t) => t.id === id);
|
|
609
|
+
if (!template) {
|
|
610
|
+
return {
|
|
611
|
+
contents: [
|
|
612
|
+
{
|
|
613
|
+
uri: uri.href,
|
|
614
|
+
mimeType: 'text/plain',
|
|
615
|
+
text: `Template "${id}" not found`,
|
|
616
|
+
},
|
|
617
|
+
],
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
return {
|
|
621
|
+
contents: [
|
|
622
|
+
{
|
|
623
|
+
uri: uri.href,
|
|
624
|
+
mimeType: 'application/json',
|
|
625
|
+
text: JSON.stringify(template, null, 2),
|
|
626
|
+
},
|
|
627
|
+
],
|
|
628
|
+
};
|
|
629
|
+
});
|
|
471
630
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -106,9 +106,44 @@ export interface PatternEntry {
|
|
|
106
106
|
css: string | null;
|
|
107
107
|
js: string | null;
|
|
108
108
|
}
|
|
109
|
+
export interface TemplateEntry {
|
|
110
|
+
id: string;
|
|
111
|
+
title: string;
|
|
112
|
+
summary: string;
|
|
113
|
+
description?: string;
|
|
114
|
+
category: string;
|
|
115
|
+
style: string;
|
|
116
|
+
tags: string[];
|
|
117
|
+
patterns: string[];
|
|
118
|
+
sections: string[];
|
|
119
|
+
features: {
|
|
120
|
+
responsive: boolean;
|
|
121
|
+
darkMode: boolean;
|
|
122
|
+
animations: boolean;
|
|
123
|
+
formHandling: boolean;
|
|
124
|
+
};
|
|
125
|
+
files: {
|
|
126
|
+
html: string;
|
|
127
|
+
preview: string;
|
|
128
|
+
};
|
|
129
|
+
install: {
|
|
130
|
+
copyPasteReady: boolean;
|
|
131
|
+
tailwindCdn: boolean;
|
|
132
|
+
vanillaJs: boolean;
|
|
133
|
+
notes: string[];
|
|
134
|
+
};
|
|
135
|
+
governance: {
|
|
136
|
+
status: 'draft' | 'review' | 'published' | 'deprecated';
|
|
137
|
+
quality: 'experimental' | 'stable' | 'flagship';
|
|
138
|
+
owner: string;
|
|
139
|
+
updatedAt: string;
|
|
140
|
+
};
|
|
141
|
+
html: string;
|
|
142
|
+
}
|
|
109
143
|
export interface Registry {
|
|
110
144
|
version: string;
|
|
111
145
|
generated: string;
|
|
112
146
|
snippets: SnippetEntry[];
|
|
113
147
|
patterns?: PatternEntry[];
|
|
148
|
+
templates?: TemplateEntry[];
|
|
114
149
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webspire/mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server for Webspire
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "MCP server for Webspire — AI-native discovery of CSS snippets, UI patterns, and page templates",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./dist/index.js",
|