@ncds/ui-admin-mcp 1.0.0-alpha.6 → 1.0.0-alpha.8
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/bin/definitions/instructions.md +3 -3
- package/bin/server.js +27 -13
- package/data/_tokens.json +8 -8
- package/package.json +1 -1
|
@@ -5,12 +5,12 @@ You are an agent that builds UI using NCUA (NCDS UI Admin) design system compone
|
|
|
5
5
|
## Absolute Rules (VIOLATION = CRITICAL FAILURE)
|
|
6
6
|
|
|
7
7
|
1. Call ping ONCE at the start of every session before using any other tool. This loads version info and usage rules.
|
|
8
|
-
2. NEVER define
|
|
9
|
-
3. NEVER write SVG icons or icon markup manually. ALL icons MUST come from search_icon or list_icons. If you write a single
|
|
8
|
+
2. NEVER define or guess NCUA CSS variables (--ncua-*). NCUA component styles are controlled by CDN CSS only.
|
|
9
|
+
3. NEVER write SVG icons or icon markup manually. ALL icons MUST come from search_icon or list_icons. If you write a single svg tag by hand, you have FAILED.
|
|
10
10
|
4. NEVER use emoji in generated HTML, CSS, or any output. No exceptions.
|
|
11
11
|
5. You MUST generate NCUA component HTML using render_to_html or render_to_html_batch only. Never write component HTML/CSS manually.
|
|
12
12
|
6. If an NCUA component exists for the use case, you MUST use it. Do NOT recreate it manually.
|
|
13
|
-
7. For custom areas
|
|
13
|
+
7. For custom areas, call get_design_tokens FIRST and prefer NCUA tokens for colors, spacing, typography, and shadows to maintain visual consistency. If no suitable token exists, you may use standard CSS values (hex, rgb, px).
|
|
14
14
|
|
|
15
15
|
## Required Workflow (follow this order strictly)
|
|
16
16
|
|
package/bin/server.js
CHANGED
|
@@ -103,21 +103,37 @@ const main = async () => {
|
|
|
103
103
|
const listIconsResponse = (0, listIcons_js_1.buildListIconsResponse)(iconSummary);
|
|
104
104
|
// ── MCP 서버 생성 + tool 등록 ──
|
|
105
105
|
const server = new mcp_js_1.McpServer({ name: 'ncds-ui-admin', version: version_js_1.VERSION }, { instructions });
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
// ping 호출 추적 — 미호출 시 다른 tool 응답에 핵심 rules 주입
|
|
107
|
+
let pinged = false;
|
|
108
|
+
const PING_REMINDER = '\n\n⚠ IMPORTANT: You have NOT called ping yet. Call ping ONCE before proceeding. ' +
|
|
109
|
+
'Rules: (1) NEVER hardcode hex/rgb colors — use get_design_tokens. ' +
|
|
110
|
+
'(2) NEVER write SVG icons — use search_icon. ' +
|
|
111
|
+
'(3) Use render_to_html or render_to_html_batch for all NCUA components. ' +
|
|
112
|
+
'(4) For custom CSS, prefer NCUA tokens. Call get_design_tokens first.';
|
|
113
|
+
const withPingReminder = (response) => {
|
|
114
|
+
if (pinged)
|
|
115
|
+
return response;
|
|
116
|
+
const text = response.content[0].text + PING_REMINDER;
|
|
117
|
+
return { ...response, content: [{ type: 'text', text }] };
|
|
118
|
+
};
|
|
119
|
+
server.registerTool('ping', { description: descriptions['ping'] }, () => {
|
|
120
|
+
pinged = true;
|
|
121
|
+
return (0, ping_js_1.ping)({ componentMap, cdnMeta, iconMeta, version: version_js_1.VERSION, rules, capabilities });
|
|
122
|
+
});
|
|
123
|
+
server.registerTool('list_icons', { description: descriptions['list_icons'] }, () => withPingReminder((0, listIcons_js_1.listIcons)(listIconsResponse)));
|
|
108
124
|
server.registerTool('search_icon', {
|
|
109
125
|
description: descriptions['search_icon'],
|
|
110
126
|
inputSchema: { query: zod_1.z.string().describe('Search keyword (e.g. "search", "alert", "arrow")') },
|
|
111
|
-
}, ({ query }) => (0, searchIcon_js_1.searchIcon)(iconData, query));
|
|
112
|
-
server.registerTool('list_components', { description: descriptions['list_components'] }, () => (0, listComponents_js_1.listComponents)(listComponentsResponse));
|
|
127
|
+
}, ({ query }) => withPingReminder((0, searchIcon_js_1.searchIcon)(iconData, query)));
|
|
128
|
+
server.registerTool('list_components', { description: descriptions['list_components'] }, () => withPingReminder((0, listComponents_js_1.listComponents)(listComponentsResponse)));
|
|
113
129
|
server.registerTool('search_component', {
|
|
114
130
|
description: descriptions['search_component'],
|
|
115
131
|
inputSchema: { query: zod_1.z.string().describe('Search keyword (Korean/English, case-insensitive)') },
|
|
116
|
-
}, ({ query }) => (0, searchComponent_js_1.searchComponent)(componentMap, query));
|
|
132
|
+
}, ({ query }) => withPingReminder((0, searchComponent_js_1.searchComponent)(componentMap, query)));
|
|
117
133
|
server.registerTool('get_component_props', {
|
|
118
134
|
description: descriptions['get_component_props'],
|
|
119
135
|
inputSchema: { name: zod_1.z.string().min(1).describe('Component name (e.g. "button", "password-input")') },
|
|
120
|
-
}, ({ name }) => (0, getComponentProps_js_1.getComponentProps)(componentMap, name));
|
|
136
|
+
}, ({ name }) => withPingReminder((0, getComponentProps_js_1.getComponentProps)(componentMap, name)));
|
|
121
137
|
server.registerTool('validate_html', {
|
|
122
138
|
description: descriptions['validate_html'],
|
|
123
139
|
inputSchema: {
|
|
@@ -126,7 +142,7 @@ const main = async () => {
|
|
|
126
142
|
.min(1)
|
|
127
143
|
.describe('HTML markup to validate (e.g. \'<button class="ncua-btn ncua-btn--primary"></button>\')'),
|
|
128
144
|
},
|
|
129
|
-
}, ({ html }) => (0, validateHtml_js_1.validateHtml)({ componentMap, rootClassMap, html, cdnMeta, tokenData, complianceRules, tokenValueMap }));
|
|
145
|
+
}, ({ html }) => withPingReminder((0, validateHtml_js_1.validateHtml)({ componentMap, rootClassMap, html, cdnMeta, tokenData, complianceRules, tokenValueMap })));
|
|
130
146
|
server.registerTool('render_to_html', {
|
|
131
147
|
description: descriptions['render_to_html'],
|
|
132
148
|
inputSchema: {
|
|
@@ -136,9 +152,7 @@ const main = async () => {
|
|
|
136
152
|
.optional()
|
|
137
153
|
.describe('Component props (e.g. { label: "OK", hierarchy: "primary" })'),
|
|
138
154
|
},
|
|
139
|
-
},
|
|
140
|
-
// MCP SDK의 zod 추론 타입이 Record<string, unknown> | undefined와 미스매치하여 as 필요
|
|
141
|
-
({ name, props }) => (0, renderToHtml_js_1.renderToHtml)({
|
|
155
|
+
}, ({ name, props }) => withPingReminder((0, renderToHtml_js_1.renderToHtml)({
|
|
142
156
|
componentMap,
|
|
143
157
|
bundle,
|
|
144
158
|
cdnMeta,
|
|
@@ -147,7 +161,7 @@ const main = async () => {
|
|
|
147
161
|
jsApiMap,
|
|
148
162
|
name,
|
|
149
163
|
props: props,
|
|
150
|
-
}));
|
|
164
|
+
})));
|
|
151
165
|
server.registerTool('render_to_html_batch', {
|
|
152
166
|
description: descriptions['render_to_html_batch'],
|
|
153
167
|
inputSchema: {
|
|
@@ -175,7 +189,7 @@ const main = async () => {
|
|
|
175
189
|
const text = r.content[0].text;
|
|
176
190
|
return r.isError ? { error: JSON.parse(text) } : JSON.parse(text);
|
|
177
191
|
});
|
|
178
|
-
return (0, response_js_1.successResponse)(parsed);
|
|
192
|
+
return withPingReminder((0, response_js_1.successResponse)(parsed));
|
|
179
193
|
});
|
|
180
194
|
server.registerTool('get_design_tokens', {
|
|
181
195
|
description: descriptions['get_design_tokens'],
|
|
@@ -185,7 +199,7 @@ const main = async () => {
|
|
|
185
199
|
.optional()
|
|
186
200
|
.describe('Token category filter (e.g. "color", "typography", "shadow", "spacing"). Omit to get all tokens.'),
|
|
187
201
|
},
|
|
188
|
-
}, ({ category }) => (0, getDesignTokens_js_1.getDesignTokens)({ tokenData, category }));
|
|
202
|
+
}, ({ category }) => withPingReminder((0, getDesignTokens_js_1.getDesignTokens)({ tokenData, category })));
|
|
189
203
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
190
204
|
await server.connect(transport);
|
|
191
205
|
logger_js_1.logger.info(`NCUA MCP 서버 시작됨 (ncds-ui-admin v${version_js_1.VERSION})`);
|
package/data/_tokens.json
CHANGED
|
@@ -557,42 +557,42 @@
|
|
|
557
557
|
]
|
|
558
558
|
},
|
|
559
559
|
{
|
|
560
|
-
"name": "shadow
|
|
560
|
+
"name": "shadow",
|
|
561
561
|
"category": "shadow",
|
|
562
562
|
"description": "기본 Elevation. 내부 요소는 외부 요소보다 낮은 단계를 사용.",
|
|
563
563
|
"tokens": [
|
|
564
564
|
{
|
|
565
|
-
"name": "--shadow-
|
|
565
|
+
"name": "--shadow-xs",
|
|
566
566
|
"value": "0 1px 2px 0 rgba(16, 24, 40, 0.05)",
|
|
567
567
|
"description": "내부 카드, 버튼, Input"
|
|
568
568
|
},
|
|
569
569
|
{
|
|
570
|
-
"name": "--shadow-
|
|
570
|
+
"name": "--shadow-sm",
|
|
571
571
|
"value": "0 1px 3px 0 rgba(16, 24, 40, 0.10), 0 1px 2px 0 rgba(16, 24, 40, 0.06)",
|
|
572
572
|
"description": "Block Container, PageTitle"
|
|
573
573
|
},
|
|
574
574
|
{
|
|
575
|
-
"name": "--shadow-
|
|
575
|
+
"name": "--shadow-md",
|
|
576
576
|
"value": "0 4px 8px -2px rgba(16, 24, 40, 0.10), 0 2px 4px -2px rgba(16, 24, 40, 0.06)",
|
|
577
577
|
"description": "드래그 중, 플로팅"
|
|
578
578
|
},
|
|
579
579
|
{
|
|
580
|
-
"name": "--shadow-
|
|
580
|
+
"name": "--shadow-lg",
|
|
581
581
|
"value": "0 12px 16px -4px rgba(16, 24, 40, 0.08), 0 4px 6px -2px rgba(16, 24, 40, 0.03)",
|
|
582
582
|
"description": "Dropdown 오버레이"
|
|
583
583
|
},
|
|
584
584
|
{
|
|
585
|
-
"name": "--shadow-
|
|
585
|
+
"name": "--shadow-xl",
|
|
586
586
|
"value": "0 20px 24px -4px rgba(16, 24, 40, 0.08), 0 8px 8px -4px rgba(16, 24, 40, 0.03)",
|
|
587
587
|
"description": ""
|
|
588
588
|
},
|
|
589
589
|
{
|
|
590
|
-
"name": "--shadow-
|
|
590
|
+
"name": "--shadow-2xl",
|
|
591
591
|
"value": "0 24px 48px -12px rgba(16, 24, 40, 0.18)",
|
|
592
592
|
"description": ""
|
|
593
593
|
},
|
|
594
594
|
{
|
|
595
|
-
"name": "--shadow-
|
|
595
|
+
"name": "--shadow-3xl",
|
|
596
596
|
"value": "0 32px 64px -12px rgba(16, 24, 40, 0.14)",
|
|
597
597
|
"description": ""
|
|
598
598
|
}
|