@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/dist/index.js CHANGED
@@ -11,7 +11,7 @@ const registryOptions = {
11
11
  };
12
12
  const server = new McpServer({
13
13
  name: 'webspire',
14
- version: '0.2.0',
14
+ version: '0.3.0',
15
15
  });
16
16
  registerTools(server, registryOptions);
17
17
  registerResources(server, registryOptions);
@@ -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.2.0",
4
- "description": "MCP server for Webspire CSS snippets — AI-native snippet discovery and matching",
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",