@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 +2 -1
- package/core/cem.mjs +16 -5
- package/core/constants.mjs +48 -0
- package/core/plugins.mjs +1 -0
- package/core/register.mjs +105 -7
- package/core.mjs +12 -5
- package/data/design-tokens.json +1 -1
- package/data/guidelines-index.json +1 -1
- package/package.json +1 -1
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 +
|
|
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
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
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
|
}
|
package/core/constants.mjs
CHANGED
|
@@ -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
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, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
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
|
-
|
|
207
|
-
|
|
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({
|
package/data/design-tokens.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monoharada/wcf-mcp",
|
|
3
|
-
"version": "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": {
|