@monoharada/wcf-mcp 0.10.0 → 0.11.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/README.md CHANGED
@@ -78,7 +78,7 @@ claude mcp add wcf -- npx @monoharada/wcf-mcp
78
78
  }
79
79
  ```
80
80
 
81
- ## 提供機能(19 tools + 1 prompt + 5 resources)
81
+ ## 提供機能(19 tools + 2 prompts + 5 resources)
82
82
 
83
83
  ### ガードレール
84
84
 
@@ -240,6 +240,7 @@ claude mcp add wcf -- npx @monoharada/wcf-mcp
240
240
 
241
241
  | 名前 | 説明 |
242
242
  |------|------|
243
+ | `build_page` | パターンID またはコンポーネントリストから no-build HTML ページを構築するガイド付きプロンプト |
243
244
  | `figma_to_wcf` | Figma URL を入力に、`overview → tokens → component api → snippet → validate` の実行順を返す |
244
245
 
245
246
  ### Resources (`wcf://`)
package/core/cem.mjs CHANGED
@@ -899,7 +899,7 @@ export function resolveDeclByComponent(indexes, component, prefix) {
899
899
  return undefined;
900
900
  }
901
901
 
902
- export function buildComponentNotFoundError(component, indexes, prefix) {
902
+ export function buildComponentNotFoundError(component, indexes, prefix, installRegistry) {
903
903
  const comp = typeof component === 'string' ? component.trim() : '';
904
904
  const p = normalizePrefix(prefix);
905
905
  const suggestions = [];
@@ -916,8 +916,19 @@ export function buildComponentNotFoundError(component, indexes, prefix) {
916
916
  suggestions.push(suggested);
917
917
  }
918
918
 
919
- const msg = suggestions.length > 0
920
- ? `Component not found: ${comp}. Did you mean: ${suggestions.join(', ')}?`
921
- : `Component not found: ${comp}`;
922
- return { content: [{ type: 'text', text: msg }], isError: true };
919
+ const parts = [];
920
+ if (suggestions.length > 0) {
921
+ parts.push(`Component not found: ${comp}. Did you mean: ${suggestions.join(', ')}?`);
922
+ } else {
923
+ parts.push(`Component not found: ${comp}`);
924
+ }
925
+
926
+ const validIds = installRegistry?.components && typeof installRegistry.components === 'object'
927
+ ? Object.keys(installRegistry.components).sort()
928
+ : [];
929
+ if (validIds.length > 0) {
930
+ parts.push(`\nAvailable component IDs (${validIds.length}): ${validIds.join(', ')}`);
931
+ }
932
+
933
+ return { content: [{ type: 'text', text: parts.join('') }], isError: true };
923
934
  }
@@ -81,6 +81,7 @@ export const CATEGORY_MAP = {
81
81
  };
82
82
 
83
83
  export const FIGMA_TO_WCF_PROMPT = 'figma_to_wcf';
84
+ export const BUILD_PAGE_PROMPT = 'build_page';
84
85
  export const WCF_RESOURCE_URIS = Object.freeze({
85
86
  components: 'wcf://components',
86
87
  tokens: 'wcf://tokens',
@@ -94,6 +95,53 @@ const NPX_TEMPLATE = Object.freeze({
94
95
  args: ['@monoharada/wcf-mcp'],
95
96
  });
96
97
 
98
+ /**
99
+ * Build the server-level instructions string sent to MCP clients on initialization.
100
+ * This enables AI agents to immediately understand what the server offers and how to
101
+ * build pages without any prior knowledge.
102
+ */
103
+ export function buildServerInstructions(prefix, installRegistry, patterns) {
104
+ const components = installRegistry?.components && typeof installRegistry.components === 'object'
105
+ ? installRegistry.components : {};
106
+ const componentIds = Object.keys(components).sort();
107
+ const patternIds = patterns && typeof patterns === 'object'
108
+ ? Object.keys(patterns).sort() : [];
109
+
110
+ return [
111
+ '# wcf-mcp: DADS Web Components Design System MCP Server',
112
+ '',
113
+ '## Capabilities',
114
+ 'This server provides a no-build / no-CDN Web Components design system.',
115
+ 'You can generate complete HTML pages using MCP tools alone — no CLI installation required.',
116
+ '',
117
+ '## Quickest Workflow (build a page)',
118
+ '1. get_pattern_recipe({ patternId: "<id>", include: ["fullPage"] })',
119
+ ' → Returns a complete <!DOCTYPE html> page in one call',
120
+ '2. validate_markup({ html: "<the full page HTML>" }) → Validate the full page (catches missing importmap / boot script)',
121
+ '3. Save the fullPageHtml result to a file and serve via HTTP',
122
+ '',
123
+ `## Available Pattern IDs (${patternIds.length})`,
124
+ patternIds.length > 0 ? patternIds.join(', ') : '(none)',
125
+ '',
126
+ `## Available Component IDs (${componentIds.length})`,
127
+ componentIds.length > 0 ? componentIds.join(', ') : '(none)',
128
+ '',
129
+ '## Custom Page Construction',
130
+ '1. generate_usage_snippet({ component: "<componentId>" }) → Get HTML for each component',
131
+ '2. generate_full_page_html({ html: "<combined fragments>" }) → Wrap into a complete page',
132
+ '3. validate_markup({ html: "<the full page HTML>" }) → Validate the full page (catches missing importmap / boot script)',
133
+ '',
134
+ '## Important Notes',
135
+ '- No CDN exists. All files are served from a local vendor directory.',
136
+ `- CLI setup: npm install web-components-factory → npx wcf init --prefix ${prefix}`,
137
+ '- file:// protocol does not work. An HTTP server is required.',
138
+ '',
139
+ '## Prompts',
140
+ '- build_page: Guided prompt for building a no-build HTML page',
141
+ '- figma_to_wcf: Convert Figma designs to WCF implementation',
142
+ ].join('\n');
143
+ }
144
+
97
145
  export const IDE_SETUP_TEMPLATES = Object.freeze([
98
146
  {
99
147
  ide: 'Claude Desktop',
package/core/plugins.mjs CHANGED
@@ -41,6 +41,7 @@ export const BUILTIN_TOOL_NAMES = Object.freeze(new Set([
41
41
  ]));
42
42
  const BUILTIN_PROMPT_NAMES = Object.freeze(new Set([
43
43
  'figma_to_wcf',
44
+ 'build_page',
44
45
  ]));
45
46
  const BUILTIN_RESOURCE_URIS = Object.freeze(new Set([
46
47
  'wcf://components',
package/core/register.mjs CHANGED
@@ -6,7 +6,7 @@ import fs from 'node:fs/promises';
6
6
  import path from 'node:path';
7
7
  import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
8
8
  import { z } from 'zod';
9
- import { CANONICAL_PREFIX, PACKAGE_VERSION, PLUGIN_TOOL_NOTICE, FIGMA_TO_WCF_PROMPT, WCF_RESOURCE_URIS, IDE_SETUP_TEMPLATES } from './constants.mjs';
9
+ import { CANONICAL_PREFIX, PACKAGE_VERSION, PLUGIN_TOOL_NOTICE, FIGMA_TO_WCF_PROMPT, BUILD_PAGE_PROMPT, WCF_RESOURCE_URIS, IDE_SETUP_TEMPLATES } from './constants.mjs';
10
10
  import { normalizePrefix, withPrefix, toCanonicalTagName, getCategory, buildDiagnosticSuggestion, applyPrefixToHtml, applyPrefixToTagMap, mergeWithPrefixed } from './prefix.mjs';
11
11
  import { buildJsonToolResponse, buildJsonToolErrorResponse, expandQueryWithSynonyms, finalizeToolResult } from './response.mjs';
12
12
  import { normalizePlugins, buildPluginDataSourceMap, toPassthroughSchema } from './plugins.mjs';
@@ -130,6 +130,77 @@ function buildFigmaToWcfPromptText({ figmaUrl, userIntent }) {
130
130
  ].join('\n');
131
131
  }
132
132
 
133
+ function buildBuildPagePromptText({ patternId, components: componentsCsv, userIntent, detectedPrefix, installRegistry, patterns }) {
134
+ const p = normalizePrefix(detectedPrefix);
135
+ const intent = String(userIntent ?? '').trim();
136
+ const registryComponents = installRegistry?.components && typeof installRegistry.components === 'object'
137
+ ? installRegistry.components : {};
138
+ const componentIds = Object.keys(registryComponents).sort();
139
+ const patternIds = patterns && typeof patterns === 'object'
140
+ ? Object.keys(patterns).sort() : [];
141
+
142
+ const lines = [
143
+ intent ? `Page goal: ${intent}` : 'Page goal: (not specified)',
144
+ '',
145
+ ];
146
+
147
+ // patternId takes priority over components when both are specified
148
+ if (patternId) {
149
+ lines.push(
150
+ '## Using a Pattern',
151
+ `1. get_pattern_recipe({ patternId: "${patternId}", include: ["fullPage"] })`,
152
+ '2. validate_markup({ html: "<the full page HTML>" }) — pass the entire page to catch missing importmap / boot script',
153
+ '3. Save the fullPageHtml to a .html file',
154
+ '',
155
+ );
156
+ if (componentsCsv) {
157
+ lines.push(
158
+ '> Note: patternId was specified, so the components argument is ignored. Remove patternId to use individual components instead.',
159
+ '',
160
+ );
161
+ }
162
+ } else if (componentsCsv) {
163
+ const ids = componentsCsv.split(',').map((s) => s.trim()).filter(Boolean);
164
+ lines.push(
165
+ '## Using Specific Components',
166
+ ...ids.map((id) => `- generate_usage_snippet({ component: "${id}" })`),
167
+ '- Combine the HTML fragments',
168
+ '- generate_full_page_html({ html: "<combined fragments>" }) → returns fullHtml',
169
+ '- validate_markup({ html: "<the full page HTML>" }) — pass the entire page to catch missing importmap / boot script',
170
+ '',
171
+ );
172
+ } else {
173
+ lines.push(
174
+ '## Workflow Options',
175
+ '',
176
+ '### Option A: Use a pattern (recommended)',
177
+ '1. get_pattern_recipe({ patternId: "<id>", include: ["fullPage"] })',
178
+ '2. validate_markup({ html: "<the full page HTML>" }) — pass the entire page to catch missing importmap / boot script',
179
+ '3. Save the fullPageHtml to a .html file',
180
+ '',
181
+ '### Option B: Build from individual components',
182
+ '1. generate_usage_snippet({ component: "<componentId>" }) for each component',
183
+ '2. Combine the HTML fragments',
184
+ '3. generate_full_page_html({ html: "<combined fragments>" }) → returns fullHtml',
185
+ '4. validate_markup({ html: "<the full page HTML>" }) — pass the entire page to catch missing importmap / boot script',
186
+ '',
187
+ );
188
+ }
189
+
190
+ lines.push(
191
+ `## Available Pattern IDs (${patternIds.length})`,
192
+ patternIds.length > 0 ? patternIds.join(', ') : '(none)',
193
+ '',
194
+ `## Available Component IDs (${componentIds.length})`,
195
+ componentIds.length > 0 ? componentIds.join(', ') : '(none)',
196
+ '',
197
+ '## CLI Vendor Setup (alternative)',
198
+ `npx web-components-factory init --prefix ${p} --dir .` + (patternId ? ` --pattern ${patternId}` : ''),
199
+ );
200
+
201
+ return lines.join('\n');
202
+ }
203
+
133
204
  function escapeHtmlTitle(s) {
134
205
  return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
135
206
  }
@@ -770,6 +841,32 @@ export function registerAll(context) {
770
841
  }),
771
842
  );
772
843
 
844
+ // -----------------------------------------------------------------------
845
+ // Prompt: build_page
846
+ // -----------------------------------------------------------------------
847
+ server.registerPrompt(
848
+ BUILD_PAGE_PROMPT,
849
+ {
850
+ title: 'Build Page',
851
+ description:
852
+ 'Guided prompt for building a no-build HTML page from a pattern or component list.',
853
+ argsSchema: {
854
+ patternId: z.string().optional().describe('Pattern ID (e.g., "search-results", "card-grid"). Use list_patterns to see all.'),
855
+ components: z.string().optional().describe('Comma-separated component IDs if not using a pattern'),
856
+ userIntent: z.string().optional().describe('What the page should accomplish'),
857
+ },
858
+ },
859
+ async ({ patternId, components: componentsCsv, userIntent }) => ({
860
+ messages: [{
861
+ role: 'user',
862
+ content: {
863
+ type: 'text',
864
+ text: buildBuildPagePromptText({ patternId, components: componentsCsv, userIntent, detectedPrefix, installRegistry, patterns }),
865
+ },
866
+ }],
867
+ }),
868
+ );
869
+
773
870
  // -----------------------------------------------------------------------
774
871
  // Resource: wcf://components
775
872
  // -----------------------------------------------------------------------
@@ -987,6 +1084,10 @@ export function registerAll(context) {
987
1084
  name: FIGMA_TO_WCF_PROMPT,
988
1085
  purpose: 'Figma-to-WCF conversion workflow prompt',
989
1086
  },
1087
+ {
1088
+ name: BUILD_PAGE_PROMPT,
1089
+ purpose: 'Build a no-build HTML page from a pattern or component list',
1090
+ },
990
1091
  ],
991
1092
  availableResources: [
992
1093
  { uri: WCF_RESOURCE_URIS.components, purpose: 'Component catalog snapshot' },
@@ -1179,7 +1280,7 @@ export function registerAll(context) {
1179
1280
 
1180
1281
  if (!decl) {
1181
1282
  const identifier = component || tagName || className || '';
1182
- return buildComponentNotFoundError(identifier, indexes, p);
1283
+ return buildComponentNotFoundError(identifier, indexes, p, installRegistry);
1183
1284
  }
1184
1285
 
1185
1286
  const canonicalTag = typeof decl.tagName === 'string' ? decl.tagName.toLowerCase() : undefined;
@@ -1232,7 +1333,7 @@ export function registerAll(context) {
1232
1333
  const decl = resolved?.decl;
1233
1334
 
1234
1335
  if (!decl) {
1235
- return buildComponentNotFoundError(component, indexes, p);
1336
+ return buildComponentNotFoundError(component, indexes, p, installRegistry);
1236
1337
  }
1237
1338
 
1238
1339
  const canonicalTag = typeof decl.tagName === 'string' ? decl.tagName.toLowerCase() : undefined;
@@ -1265,10 +1366,7 @@ export function registerAll(context) {
1265
1366
  const decl = resolved?.decl;
1266
1367
 
1267
1368
  if (!decl) {
1268
- return {
1269
- content: [{ type: 'text', text: `Component not found: ${component}` }],
1270
- isError: true,
1271
- };
1369
+ return buildComponentNotFoundError(component, indexes, p, installRegistry);
1272
1370
  }
1273
1371
 
1274
1372
  const canonicalTag = typeof decl.tagName === 'string' ? decl.tagName.toLowerCase() : undefined;
package/core.mjs CHANGED
@@ -21,8 +21,10 @@ export {
21
21
  PLUGIN_TOOL_NOTICE,
22
22
  CATEGORY_MAP,
23
23
  FIGMA_TO_WCF_PROMPT,
24
+ BUILD_PAGE_PROMPT,
24
25
  WCF_RESOURCE_URIS,
25
26
  IDE_SETUP_TEMPLATES,
27
+ buildServerInstructions,
26
28
  } from './core/constants.mjs';
27
29
 
28
30
  // --- core/response.mjs ---
@@ -103,7 +105,7 @@ export {
103
105
  import { normalizePlugins, buildPluginDataSourceMap } from './core/plugins.mjs';
104
106
  import { buildIndexes, extractPrefixFromIndexes, loadPatternRegistryShape, buildRelatedComponentMap, buildPatternFrequencyMap } from './core/cem.mjs';
105
107
  import { buildTokenSuggestionMap, buildComponentTokenReferencedBy } from './core/tokens.mjs';
106
- import { MAX_TOOL_RESULT_BYTES, PACKAGE_VERSION } from './core/constants.mjs';
108
+ import { MAX_TOOL_RESULT_BYTES, PACKAGE_VERSION, buildServerInstructions } from './core/constants.mjs';
107
109
 
108
110
  // ---------------------------------------------------------------------------
109
111
  // createMcpServer — builds the McpServer with all tools registered, but does
@@ -202,10 +204,15 @@ export async function createMcpServer(loadJsonData, loadValidator, options = {})
202
204
  // component-selector-guide.json may not exist yet
203
205
  }
204
206
 
205
- const server = new McpServer({
206
- name: 'web-components-factory-design-system',
207
- version: PACKAGE_VERSION,
208
- });
207
+ const server = new McpServer(
208
+ {
209
+ name: 'web-components-factory-design-system',
210
+ version: PACKAGE_VERSION,
211
+ },
212
+ {
213
+ instructions: buildServerInstructions(detectedPrefix, installRegistry, patterns),
214
+ },
215
+ );
209
216
 
210
217
  // Delegate all tool / resource / prompt registration to register.mjs (DD-08)
211
218
  registerAll({
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "0.3.0",
3
- "extractedAt": "2026-03-10T05:48:39.504Z",
3
+ "extractedAt": "2026-03-16T07:35:00.524Z",
4
4
  "themes": {
5
5
  "default": "light",
6
6
  "available": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "0.1.0",
3
- "indexedAt": "2026-03-10T05:48:39.920Z",
3
+ "indexedAt": "2026-03-16T07:35:00.939Z",
4
4
  "documents": [
5
5
  {
6
6
  "id": ".claude/skills/css-writing-rules/references/core-principles.md",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monoharada/wcf-mcp",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "MCP server for the web-components-factory design system. Provides component discovery, validation, and pattern-based UI composition without cloning the repository.",
5
5
  "type": "module",
6
6
  "bin": {