@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.
@@ -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, invent, or guess CSS variables, design tokens, or color values. Use ONLY tokens returned by get_design_tokens. If you write a custom CSS variable (e.g. --custom-anything) or hardcode a hex/rgb value (e.g. #5B5BD6, rgb(91,91,214)), you have FAILED.
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.
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 not covered by NCUA, use ONLY tokens from get_design_tokens for all colors, spacing, typography, and shadows. Call get_design_tokens BEFORE writing any custom CSS.
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
- server.registerTool('ping', { description: descriptions['ping'] }, () => (0, ping_js_1.ping)({ componentMap, cdnMeta, iconMeta, version: version_js_1.VERSION, rules, capabilities }));
107
- server.registerTool('list_icons', { description: descriptions['list_icons'] }, () => (0, listIcons_js_1.listIcons)(listIconsResponse));
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-elevation",
560
+ "name": "shadow",
561
561
  "category": "shadow",
562
562
  "description": "기본 Elevation. 내부 요소는 외부 요소보다 낮은 단계를 사용.",
563
563
  "tokens": [
564
564
  {
565
- "name": "--shadow-elevation-xs",
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-elevation-sm",
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-elevation-md",
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-elevation-lg",
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-elevation-xl",
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-elevation-2xl",
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-elevation-3xl",
595
+ "name": "--shadow-3xl",
596
596
  "value": "0 32px 64px -12px rgba(16, 24, 40, 0.14)",
597
597
  "description": ""
598
598
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ncds/ui-admin-mcp",
3
- "version": "1.0.0-alpha.6",
3
+ "version": "1.0.0-alpha.8",
4
4
  "description": "NCDS UI Admin MCP 서버 — AI 에이전트가 NCUA 컴포넌트를 조회하고 HTML을 검증할 수 있는 MCP 서버",
5
5
  "bin": {
6
6
  "ncua-mcp": "./bin/server.mjs"