@vadenai/mcp-server 0.2.1 → 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.
@@ -12,4 +12,7 @@ export declare class VadenMcpApiClient {
12
12
  private headers;
13
13
  private buildUrl;
14
14
  get<T>(path: string): Promise<T>;
15
+ post<T>(path: string, body: unknown): Promise<T>;
16
+ patch<T>(path: string, body: unknown): Promise<T>;
17
+ private request;
15
18
  }
@@ -24,18 +24,28 @@ export class VadenMcpApiClient {
24
24
  return new URL(cleanPath, base).toString();
25
25
  }
26
26
  async get(path) {
27
+ return this.request("GET", path);
28
+ }
29
+ async post(path, body) {
30
+ return this.request("POST", path, body);
31
+ }
32
+ async patch(path, body) {
33
+ return this.request("PATCH", path, body);
34
+ }
35
+ async request(method, path, body) {
27
36
  const url = this.buildUrl(path);
28
37
  const controller = new AbortController();
29
38
  const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
30
39
  try {
31
40
  const response = await fetch(url, {
32
- method: "GET",
41
+ method,
33
42
  headers: this.headers(),
34
43
  signal: controller.signal,
44
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
35
45
  });
36
46
  if (!response.ok) {
37
- const body = await response.text();
38
- throw new Error(`API error (${response.status}): ${body}`);
47
+ const text = await response.text();
48
+ throw new Error(`API error (${response.status}): ${text}`);
39
49
  }
40
50
  return (await response.json());
41
51
  }
package/dist/index.js CHANGED
@@ -13,11 +13,14 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
13
13
  import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
14
14
  import { VadenMcpApiClient } from "./api-client.js";
15
15
  import { handleGetComponentList, handleGetComponentSpec, handleSearchComponents, } from "./tools/components.js";
16
- import { handleGetConcept, handleGetDesignRationale } from "./tools/concept.js";
16
+ import { handleGetConcept, handleGetDesignRationale, handleUpdateConcept, } from "./tools/concept.js";
17
17
  import { handleGetDesignTokens, handleGetThemeCss, } from "./tools/design-tokens.js";
18
- import { handleGetWireframeDetail, handleGetWireframes, } from "./tools/wireframes.js";
18
+ import { handleGetWireframeDetail, handleGetWireframes, handlePushWireframes, handleUpdateWireframe, } from "./tools/wireframes.js";
19
+ import { handleUpdateComponentStyle } from "./tools/write-components.js";
20
+ import { handleUpdateDesignTokens } from "./tools/write-tokens.js";
21
+ import { validateArrayAddArgs, validateArrayRemoveArgs, validateFieldUpdateArgs, validatePushWireframesArgs, } from "./validation.js";
19
22
  // --- コマンドライン引数パース ---
20
- const VERSION = "0.2.1";
23
+ const VERSION = "0.3.0";
21
24
  function parseArgs() {
22
25
  const args = process.argv.slice(2);
23
26
  if (args.includes("--help") || args.includes("-h")) {
@@ -159,6 +162,187 @@ const TOOLS = [
159
162
  required: [],
160
163
  },
161
164
  },
165
+ // --- Write ツール ---
166
+ {
167
+ name: "update_design_tokens",
168
+ description: "デザイントークン(色・spacing・radius等)を部分更新します。指定したフィールドのみ deep merge で上書きされます",
169
+ inputSchema: {
170
+ type: "object",
171
+ properties: {
172
+ tokens: {
173
+ type: "object",
174
+ description: "更新するトークン(deep merge)。例: { colors: { light: { semantic: { primary: '#3B82F6' } } } }",
175
+ },
176
+ },
177
+ required: ["tokens"],
178
+ },
179
+ annotations: {
180
+ readOnlyHint: false,
181
+ destructiveHint: false,
182
+ idempotentHint: true,
183
+ openWorldHint: false,
184
+ },
185
+ },
186
+ {
187
+ name: "update_component_style",
188
+ description: "指定したコンポーネントのスタイル設定をオーバーライドします。ベーススタイルは保持され、オーバーライドレイヤーとして保存されます",
189
+ inputSchema: {
190
+ type: "object",
191
+ properties: {
192
+ component: {
193
+ type: "string",
194
+ description: "コンポーネント名(例: button, card)",
195
+ },
196
+ updatedStyleConfig: {
197
+ type: "object",
198
+ description: "更新するスタイル設定。Single型: { baseStyles, variants }、Multipart型: { slots, variants }",
199
+ },
200
+ },
201
+ required: ["component", "updatedStyleConfig"],
202
+ },
203
+ annotations: {
204
+ readOnlyHint: false,
205
+ destructiveHint: false,
206
+ idempotentHint: true,
207
+ openWorldHint: false,
208
+ },
209
+ },
210
+ {
211
+ name: "update_concept",
212
+ description: "コンセプトの特定フィールドをドット区切りパスで更新します。例: serviceName, brandPersonality.0, targetUsers.2",
213
+ inputSchema: {
214
+ type: "object",
215
+ properties: {
216
+ field_path: {
217
+ type: "string",
218
+ description: "更新するフィールドのドット区切りパス(例: 'serviceName', 'brandPersonality.0')",
219
+ },
220
+ value: {
221
+ oneOf: [
222
+ { type: "string" },
223
+ { type: "number" },
224
+ { type: "boolean" },
225
+ { type: "null" },
226
+ ],
227
+ description: "新しい値(文字列・数値・真偽値・null)",
228
+ },
229
+ },
230
+ required: ["field_path", "value"],
231
+ },
232
+ annotations: {
233
+ readOnlyHint: false,
234
+ destructiveHint: false,
235
+ idempotentHint: true,
236
+ openWorldHint: false,
237
+ },
238
+ },
239
+ {
240
+ name: "update_wireframe",
241
+ description: "ワイヤーフレームの特定フィールドをドット区切りパスで更新します。例: screens.0.name, transitions.1.label",
242
+ inputSchema: {
243
+ type: "object",
244
+ properties: {
245
+ field_path: {
246
+ type: "string",
247
+ description: "更新するフィールドのドット区切りパス(例: 'screens.0.name')",
248
+ },
249
+ value: {
250
+ oneOf: [
251
+ { type: "string" },
252
+ { type: "number" },
253
+ { type: "boolean" },
254
+ { type: "null" },
255
+ ],
256
+ description: "新しい値(文字列・数値・真偽値・null)",
257
+ },
258
+ },
259
+ required: ["field_path", "value"],
260
+ },
261
+ annotations: {
262
+ readOnlyHint: false,
263
+ destructiveHint: false,
264
+ idempotentHint: true,
265
+ openWorldHint: false,
266
+ },
267
+ },
268
+ {
269
+ name: "add_wireframe_element",
270
+ description: "ワイヤーフレームの配列に要素を追加します。画面追加(screens)、コンポーネント追加(screens.0.components)、遷移追加(transitions)などに使用",
271
+ inputSchema: {
272
+ type: "object",
273
+ properties: {
274
+ field_path: {
275
+ type: "string",
276
+ description: "追加先の配列パス(例: 'screens', 'screens.0.components', 'transitions')",
277
+ },
278
+ value: {
279
+ type: "object",
280
+ description: "追加する要素。画面: { id, name, purpose, width: 800, components: [], group?, journeyStage? }、コンポーネント: { id, type, label }、遷移: { id, fromScreenId, toScreenId, triggerAction?, label? }",
281
+ },
282
+ },
283
+ required: ["field_path", "value"],
284
+ },
285
+ annotations: {
286
+ readOnlyHint: false,
287
+ destructiveHint: false,
288
+ idempotentHint: false,
289
+ openWorldHint: false,
290
+ },
291
+ },
292
+ {
293
+ name: "remove_wireframe_element",
294
+ description: "ワイヤーフレームの配列から要素を削除します。画面削除(screens.2)、コンポーネント削除(screens.0.components.3)、遷移削除(transitions.1)などに使用",
295
+ inputSchema: {
296
+ type: "object",
297
+ properties: {
298
+ field_path: {
299
+ type: "string",
300
+ description: "削除する要素のパス(最後のセグメントが配列インデックス。例: 'screens.2', 'screens.0.components.3', 'transitions.1')",
301
+ },
302
+ },
303
+ required: ["field_path"],
304
+ },
305
+ annotations: {
306
+ readOnlyHint: false,
307
+ destructiveHint: true,
308
+ idempotentHint: false,
309
+ openWorldHint: false,
310
+ },
311
+ },
312
+ {
313
+ name: "push_wireframes",
314
+ description: "UXIR(YAML形式)ファイルを一括プッシュしてワイヤーフレームを更新します。既存のワイヤーフレームは置換されます",
315
+ inputSchema: {
316
+ type: "object",
317
+ properties: {
318
+ files: {
319
+ type: "array",
320
+ items: {
321
+ type: "object",
322
+ properties: {
323
+ filename: {
324
+ type: "string",
325
+ description: "ファイル名(.uxir 拡張子必須)",
326
+ },
327
+ content: {
328
+ type: "string",
329
+ description: "UXIR YAML コンテンツ",
330
+ },
331
+ },
332
+ required: ["filename", "content"],
333
+ },
334
+ description: "UXIR ファイル一覧(最大50ファイル、各1MB以下)",
335
+ },
336
+ },
337
+ required: ["files"],
338
+ },
339
+ annotations: {
340
+ readOnlyHint: false,
341
+ destructiveHint: true,
342
+ idempotentHint: false,
343
+ openWorldHint: false,
344
+ },
345
+ },
162
346
  ];
163
347
  // --- メイン ---
164
348
  async function main() {
@@ -171,6 +355,7 @@ async function main() {
171
355
  name: t.name,
172
356
  description: t.description,
173
357
  inputSchema: t.inputSchema,
358
+ ...("annotations" in t ? { annotations: t.annotations } : {}),
174
359
  })),
175
360
  }));
176
361
  // ツール呼び出し
@@ -225,6 +410,53 @@ async function main() {
225
410
  const content = await handleGetDesignRationale(client, projectId);
226
411
  return { content };
227
412
  }
413
+ // --- Write ツール ---
414
+ case "update_design_tokens": {
415
+ const tokens = args?.tokens;
416
+ if (!tokens || typeof tokens !== "object") {
417
+ throw new Error("tokens パラメータが必要です(オブジェクト)");
418
+ }
419
+ const content = await handleUpdateDesignTokens(client, projectId, tokens);
420
+ return { content };
421
+ }
422
+ case "update_component_style": {
423
+ const componentName = String(args?.component ?? "");
424
+ const updatedStyleConfig = args
425
+ ?.updatedStyleConfig;
426
+ if (!componentName) {
427
+ throw new Error("component パラメータが必要です");
428
+ }
429
+ if (!updatedStyleConfig || typeof updatedStyleConfig !== "object") {
430
+ throw new Error("updatedStyleConfig パラメータが必要です(オブジェクト)");
431
+ }
432
+ const content = await handleUpdateComponentStyle(client, projectId, componentName, updatedStyleConfig);
433
+ return { content };
434
+ }
435
+ case "update_concept": {
436
+ const { fieldPath, value } = validateFieldUpdateArgs(args);
437
+ const content = await handleUpdateConcept(client, projectId, fieldPath, value);
438
+ return { content };
439
+ }
440
+ case "update_wireframe": {
441
+ const { fieldPath, value } = validateFieldUpdateArgs(args);
442
+ const content = await handleUpdateWireframe(client, projectId, fieldPath, value);
443
+ return { content };
444
+ }
445
+ case "add_wireframe_element": {
446
+ const { fieldPath, value } = validateArrayAddArgs(args);
447
+ const content = await handleUpdateWireframe(client, projectId, fieldPath, value, "add");
448
+ return { content };
449
+ }
450
+ case "remove_wireframe_element": {
451
+ const { fieldPath } = validateArrayRemoveArgs(args);
452
+ const content = await handleUpdateWireframe(client, projectId, fieldPath, null, "remove");
453
+ return { content };
454
+ }
455
+ case "push_wireframes": {
456
+ const validatedFiles = validatePushWireframesArgs(args);
457
+ const content = await handlePushWireframes(client, projectId, validatedFiles);
458
+ return { content };
459
+ }
228
460
  default:
229
461
  throw new Error(`Unknown tool: ${name}`);
230
462
  }
@@ -4,6 +4,18 @@
4
4
  * コンセプト API: GET /api/projects/{projectId}/concept
5
5
  */
6
6
  import type { VadenMcpApiClient } from "../api-client.js";
7
+ /**
8
+ * コンセプトの特定フィールドを更新する
9
+ *
10
+ * @param client - Vaden MCP API クライアント
11
+ * @param projectId - プロジェクトID
12
+ * @param fieldPath - ドット区切りのフィールドパス(例: 'brandPersonality.0', 'serviceName')
13
+ * @param value - 新しい値
14
+ */
15
+ export declare function handleUpdateConcept(client: VadenMcpApiClient, projectId: string, fieldPath: string, value: unknown, operation?: "update" | "add" | "remove"): Promise<{
16
+ type: "text";
17
+ text: string;
18
+ }[]>;
7
19
  /**
8
20
  * コンセプト情報を返す
9
21
  *
@@ -48,6 +48,24 @@ function generateConceptGuide(data) {
48
48
  }
49
49
  lines.push("");
50
50
  }
51
+ if (data.colorEmotions) {
52
+ lines.push("## Color Emotions");
53
+ lines.push("");
54
+ if (data.colorEmotions.primary) {
55
+ lines.push(`**Primary**: ${data.colorEmotions.primary}`);
56
+ }
57
+ if (data.colorEmotions.secondary) {
58
+ lines.push(`**Secondary**: ${data.colorEmotions.secondary}`);
59
+ }
60
+ if (data.colorEmotions.emotional?.length) {
61
+ lines.push("");
62
+ lines.push("### Emotional Effects");
63
+ for (const effect of data.colorEmotions.emotional) {
64
+ lines.push(`- ${effect}`);
65
+ }
66
+ }
67
+ lines.push("");
68
+ }
51
69
  lines.push("## Design Principles");
52
70
  lines.push("");
53
71
  for (const principle of data.designPrinciples) {
@@ -66,6 +84,32 @@ function generateConceptGuide(data) {
66
84
  lines.push(`- ${journey}`);
67
85
  }
68
86
  lines.push("");
87
+ if (data.layoutPatterns?.length) {
88
+ lines.push("## Layout Patterns");
89
+ lines.push("");
90
+ for (const pattern of data.layoutPatterns) {
91
+ lines.push(`### ${pattern.name}`);
92
+ lines.push("");
93
+ lines.push(pattern.description);
94
+ if (pattern.basedOn) {
95
+ if (pattern.basedOn.interfaceApproach?.length) {
96
+ lines.push(`- **Interface Approach**: ${pattern.basedOn.interfaceApproach.join(", ")}`);
97
+ }
98
+ if (pattern.basedOn.layoutStrategy?.length) {
99
+ lines.push(`- **Layout Strategy**: ${pattern.basedOn.layoutStrategy.join(", ")}`);
100
+ }
101
+ }
102
+ if (pattern.specs) {
103
+ if (pattern.specs.grid) {
104
+ lines.push(`- **Grid**: ${pattern.specs.grid.columns ?? "?"}col, gap ${String(pattern.specs.grid.gap ?? "?")}`);
105
+ }
106
+ if (pattern.specs.nav?.type) {
107
+ lines.push(`- **Navigation**: ${pattern.specs.nav.type}`);
108
+ }
109
+ }
110
+ lines.push("");
111
+ }
112
+ }
69
113
  lines.push("> Use `get_design_rationale` to understand the reasoning behind design decisions.");
70
114
  return lines.join("\n");
71
115
  }
@@ -120,6 +164,86 @@ function generateDesignRationaleGuide(data) {
120
164
  lines.push(`- ${principle}`);
121
165
  }
122
166
  lines.push("");
167
+ if (da.visualEffects?.length) {
168
+ lines.push("## Visual Effects");
169
+ lines.push("");
170
+ for (const effect of da.visualEffects) {
171
+ lines.push(`- **${effect.name}**: ${effect.intent}`);
172
+ }
173
+ lines.push("");
174
+ }
175
+ if (da.interactionDesign?.length) {
176
+ lines.push("## Interaction Design");
177
+ lines.push("");
178
+ for (const item of da.interactionDesign) {
179
+ lines.push(`- **${item.feature}**: ${item.purpose}`);
180
+ }
181
+ lines.push("");
182
+ }
183
+ if (da.layoutStrategy?.length) {
184
+ lines.push("## Layout Strategy");
185
+ lines.push("");
186
+ for (const item of da.layoutStrategy) {
187
+ lines.push(`- **${item.aspect}**: ${item.rationale}`);
188
+ }
189
+ lines.push("");
190
+ }
191
+ if (da.motionDesign?.length) {
192
+ lines.push("## Motion Design");
193
+ lines.push("");
194
+ for (const item of da.motionDesign) {
195
+ lines.push(`- ${item}`);
196
+ }
197
+ lines.push("");
198
+ }
199
+ if (da.differentiation?.length) {
200
+ lines.push("## Differentiation");
201
+ lines.push("");
202
+ for (const item of da.differentiation) {
203
+ lines.push(`- vs **${item.competitor}**: ${item.distinction}`);
204
+ }
205
+ lines.push("");
206
+ }
207
+ if (da.behavioralPsychology?.length) {
208
+ lines.push("## Behavioral Psychology");
209
+ lines.push("");
210
+ for (const item of da.behavioralPsychology) {
211
+ lines.push(`- **${item.principle}**: ${item.application}`);
212
+ }
213
+ lines.push("");
214
+ }
215
+ if (da.iconography?.length) {
216
+ lines.push("## Iconography");
217
+ lines.push("");
218
+ for (const item of da.iconography) {
219
+ lines.push(`- **${item.element}**: ${item.symbolism}`);
220
+ }
221
+ lines.push("");
222
+ }
223
+ if (da.culturalResonance?.length) {
224
+ lines.push("## Cultural Resonance");
225
+ lines.push("");
226
+ for (const item of da.culturalResonance) {
227
+ lines.push(`- ${item}`);
228
+ }
229
+ lines.push("");
230
+ }
231
+ if (da.informationArchitecture?.length) {
232
+ lines.push("## Information Architecture");
233
+ lines.push("");
234
+ for (const item of da.informationArchitecture) {
235
+ lines.push(`- ${item}`);
236
+ }
237
+ lines.push("");
238
+ }
239
+ if (da.brandConsistency?.length) {
240
+ lines.push("## Brand Consistency");
241
+ lines.push("");
242
+ for (const item of da.brandConsistency) {
243
+ lines.push(`- ${item}`);
244
+ }
245
+ lines.push("");
246
+ }
123
247
  lines.push("## Success Metrics");
124
248
  lines.push("");
125
249
  for (const metric of data.successMetrics) {
@@ -134,7 +258,29 @@ function generateDesignRationaleGuide(data) {
134
258
  lines.push("");
135
259
  return lines.join("\n");
136
260
  }
137
- // --- ツールハンドラー ---
261
+ /**
262
+ * コンセプトの特定フィールドを更新する
263
+ *
264
+ * @param client - Vaden MCP API クライアント
265
+ * @param projectId - プロジェクトID
266
+ * @param fieldPath - ドット区切りのフィールドパス(例: 'brandPersonality.0', 'serviceName')
267
+ * @param value - 新しい値
268
+ */
269
+ export async function handleUpdateConcept(client, projectId, fieldPath, value, operation = "update") {
270
+ await client.patch(`/api/projects/${encodeURIComponent(projectId)}/concept`, { fieldPath, value, operation });
271
+ const actionLabel = operation === "add"
272
+ ? "added to"
273
+ : operation === "remove"
274
+ ? "removed from"
275
+ : "updated in";
276
+ return [
277
+ {
278
+ type: "text",
279
+ text: `Concept: element ${actionLabel} "${fieldPath}"${operation !== "remove" ? ` — ${JSON.stringify(value)}` : ""}`,
280
+ },
281
+ ];
282
+ }
283
+ // --- Read ハンドラー ---
138
284
  /**
139
285
  * コンセプト情報を返す
140
286
  *
@@ -5,6 +5,32 @@
5
5
  * 画面詳細 API: GET /api/projects/{projectId}/wireframe/{screenId}
6
6
  */
7
7
  import type { VadenMcpApiClient } from "../api-client.js";
8
+ /**
9
+ * ワイヤーフレームの特定フィールドを更新する
10
+ *
11
+ * @param client - Vaden MCP API クライアント
12
+ * @param projectId - プロジェクトID
13
+ * @param fieldPath - ドット区切りのフィールドパス(例: 'screens.0.name')
14
+ * @param value - 新しい値
15
+ */
16
+ export declare function handleUpdateWireframe(client: VadenMcpApiClient, projectId: string, fieldPath: string, value: unknown, operation?: "update" | "add" | "remove"): Promise<{
17
+ type: "text";
18
+ text: string;
19
+ }[]>;
20
+ /**
21
+ * UXIR ファイルを一括プッシュしてワイヤーフレームを更新する
22
+ *
23
+ * @param client - Vaden MCP API クライアント
24
+ * @param projectId - プロジェクトID
25
+ * @param files - UXIR ファイル一覧 [{ filename, content }]
26
+ */
27
+ export declare function handlePushWireframes(client: VadenMcpApiClient, projectId: string, files: Array<{
28
+ filename: string;
29
+ content: string;
30
+ }>): Promise<{
31
+ type: "text";
32
+ text: string;
33
+ }[]>;
8
34
  /**
9
35
  * ワイヤーフレーム一覧を返す
10
36
  *
@@ -168,7 +168,45 @@ function generateWireframeDetailGuide(screen) {
168
168
  }
169
169
  return lines.join("\n");
170
170
  }
171
- // --- ツールハンドラー ---
171
+ /**
172
+ * ワイヤーフレームの特定フィールドを更新する
173
+ *
174
+ * @param client - Vaden MCP API クライアント
175
+ * @param projectId - プロジェクトID
176
+ * @param fieldPath - ドット区切りのフィールドパス(例: 'screens.0.name')
177
+ * @param value - 新しい値
178
+ */
179
+ export async function handleUpdateWireframe(client, projectId, fieldPath, value, operation = "update") {
180
+ await client.patch(`/api/projects/${encodeURIComponent(projectId)}/wireframe`, { fieldPath, value, operation });
181
+ const actionLabel = operation === "add"
182
+ ? "added to"
183
+ : operation === "remove"
184
+ ? "removed from"
185
+ : "updated in";
186
+ return [
187
+ {
188
+ type: "text",
189
+ text: `Wireframe: element ${actionLabel} "${fieldPath}"${operation !== "remove" ? ` — ${JSON.stringify(value)}` : ""}`,
190
+ },
191
+ ];
192
+ }
193
+ /**
194
+ * UXIR ファイルを一括プッシュしてワイヤーフレームを更新する
195
+ *
196
+ * @param client - Vaden MCP API クライアント
197
+ * @param projectId - プロジェクトID
198
+ * @param files - UXIR ファイル一覧 [{ filename, content }]
199
+ */
200
+ export async function handlePushWireframes(client, projectId, files) {
201
+ const result = await client.post(`/api/projects/${encodeURIComponent(projectId)}/uxir`, { files });
202
+ return [
203
+ {
204
+ type: "text",
205
+ text: `Wireframes pushed successfully.\n- Files accepted: ${String(result.accepted)}\n- Screens: ${String(result.screens)}\n- Transitions: ${String(result.transitions)}`,
206
+ },
207
+ ];
208
+ }
209
+ // --- Read ハンドラー ---
172
210
  /**
173
211
  * ワイヤーフレーム一覧を返す
174
212
  *
@@ -0,0 +1,18 @@
1
+ /**
2
+ * コンポーネントスタイル更新 MCP ツールハンドラー
3
+ *
4
+ * PATCH /api/projects/{projectId}/design-system/component-styles/{component}
5
+ */
6
+ import type { VadenMcpApiClient } from "../api-client.js";
7
+ /**
8
+ * コンポーネントスタイルオーバーライドを更新する
9
+ *
10
+ * @param client - Vaden MCP API クライアント
11
+ * @param projectId - プロジェクトID
12
+ * @param component - コンポーネント名(例: button, card)
13
+ * @param updatedStyleConfig - 更新するスタイル設定
14
+ */
15
+ export declare function handleUpdateComponentStyle(client: VadenMcpApiClient, projectId: string, component: string, updatedStyleConfig: Record<string, unknown>): Promise<{
16
+ type: "text";
17
+ text: string;
18
+ }[]>;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * コンポーネントスタイルオーバーライドを更新する
3
+ *
4
+ * @param client - Vaden MCP API クライアント
5
+ * @param projectId - プロジェクトID
6
+ * @param component - コンポーネント名(例: button, card)
7
+ * @param updatedStyleConfig - 更新するスタイル設定
8
+ */
9
+ export async function handleUpdateComponentStyle(client, projectId, component, updatedStyleConfig) {
10
+ const result = await client.patch(`/api/projects/${encodeURIComponent(projectId)}/design-system/component-styles/${encodeURIComponent(component)}`, { updatedStyleConfig });
11
+ return [
12
+ {
13
+ type: "text",
14
+ text: `Component style for "${result.component}" updated successfully. Override applied: ${String(result.overrideApplied)}`,
15
+ },
16
+ ];
17
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * デザイントークン更新 MCP ツールハンドラー
3
+ *
4
+ * PATCH /api/projects/{projectId}/design-system/tokens
5
+ */
6
+ import type { VadenMcpApiClient } from "../api-client.js";
7
+ /**
8
+ * デザイントークンを部分更新する
9
+ *
10
+ * @param client - Vaden MCP API クライアント
11
+ * @param projectId - プロジェクトID
12
+ * @param tokens - 更新するトークン(deep merge)
13
+ */
14
+ export declare function handleUpdateDesignTokens(client: VadenMcpApiClient, projectId: string, tokens: Record<string, unknown>): Promise<{
15
+ type: "text";
16
+ text: string;
17
+ }[]>;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * デザイントークンを部分更新する
3
+ *
4
+ * @param client - Vaden MCP API クライアント
5
+ * @param projectId - プロジェクトID
6
+ * @param tokens - 更新するトークン(deep merge)
7
+ */
8
+ export async function handleUpdateDesignTokens(client, projectId, tokens) {
9
+ const result = await client.patch(`/api/projects/${encodeURIComponent(projectId)}/design-system/tokens`, { tokens });
10
+ return [
11
+ {
12
+ type: "text",
13
+ text: `Design tokens updated successfully.\n\n\`\`\`json\n${JSON.stringify(result.tokens, null, 2)}\n\`\`\``,
14
+ },
15
+ ];
16
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * MCP ツール引数のバリデーションユーティリティ
3
+ */
4
+ /**
5
+ * update_concept / update_wireframe 用のフィールド更新引数を検証・抽出する
6
+ *
7
+ * @returns { fieldPath, value } — 検証済みの値
8
+ * @throws {Error} 引数が不正な場合
9
+ */
10
+ export declare function validateFieldUpdateArgs(args: Record<string, unknown>): {
11
+ fieldPath: string;
12
+ value: string | number | boolean | null;
13
+ };
14
+ /**
15
+ * add_wireframe_element / add_concept_element 用の引数を検証・抽出する
16
+ *
17
+ * @returns { fieldPath, value } — 検証済みの値
18
+ * @throws {Error} 引数が不正な場合
19
+ */
20
+ export declare function validateArrayAddArgs(args: Record<string, unknown>): {
21
+ fieldPath: string;
22
+ value: unknown;
23
+ };
24
+ /**
25
+ * remove_wireframe_element / remove_concept_element 用の引数を検証・抽出する
26
+ *
27
+ * @returns { fieldPath } — 検証済みの値
28
+ * @throws {Error} 引数が不正な場合
29
+ */
30
+ export declare function validateArrayRemoveArgs(args: Record<string, unknown>): {
31
+ fieldPath: string;
32
+ };
33
+ /**
34
+ * push_wireframes 用の files 引数を検証する
35
+ *
36
+ * @returns 検証済みの files 配列
37
+ * @throws {Error} 引数が不正な場合
38
+ */
39
+ export declare function validatePushWireframesArgs(args: Record<string, unknown>): Array<{
40
+ filename: string;
41
+ content: string;
42
+ }>;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * MCP ツール引数のバリデーションユーティリティ
3
+ */
4
+ /**
5
+ * update_concept / update_wireframe 用のフィールド更新引数を検証・抽出する
6
+ *
7
+ * @returns { fieldPath, value } — 検証済みの値
8
+ * @throws {Error} 引数が不正な場合
9
+ */
10
+ export function validateFieldUpdateArgs(args) {
11
+ const fieldPath = String(args.field_path ?? "");
12
+ if (!fieldPath) {
13
+ throw new Error("field_path パラメータが必要です");
14
+ }
15
+ const rawValue = args.value;
16
+ if (rawValue === undefined) {
17
+ throw new Error("value パラメータが必要です");
18
+ }
19
+ const valueType = typeof rawValue;
20
+ if (rawValue !== null &&
21
+ valueType !== "string" &&
22
+ valueType !== "number" &&
23
+ valueType !== "boolean") {
24
+ throw new Error("value パラメータは string, number, boolean, null のいずれかである必要があります");
25
+ }
26
+ return { fieldPath, value: rawValue };
27
+ }
28
+ /**
29
+ * add_wireframe_element / add_concept_element 用の引数を検証・抽出する
30
+ *
31
+ * @returns { fieldPath, value } — 検証済みの値
32
+ * @throws {Error} 引数が不正な場合
33
+ */
34
+ export function validateArrayAddArgs(args) {
35
+ const fieldPath = String(args.field_path ?? "");
36
+ if (!fieldPath) {
37
+ throw new Error("field_path パラメータが必要です");
38
+ }
39
+ const value = args.value;
40
+ if (value === undefined) {
41
+ throw new Error("value パラメータが必要です");
42
+ }
43
+ return { fieldPath, value };
44
+ }
45
+ /**
46
+ * remove_wireframe_element / remove_concept_element 用の引数を検証・抽出する
47
+ *
48
+ * @returns { fieldPath } — 検証済みの値
49
+ * @throws {Error} 引数が不正な場合
50
+ */
51
+ export function validateArrayRemoveArgs(args) {
52
+ const fieldPath = String(args.field_path ?? "");
53
+ if (!fieldPath) {
54
+ throw new Error("field_path パラメータが必要です");
55
+ }
56
+ // 最後のセグメントが数値であることを検証
57
+ const lastSegment = fieldPath.split(".").pop() ?? "";
58
+ if (!/^\d+$/.test(lastSegment)) {
59
+ throw new Error("field_path の最後のセグメントは数値インデックスである必要があります(例: screens.2, screens.0.components.3)");
60
+ }
61
+ return { fieldPath };
62
+ }
63
+ /**
64
+ * push_wireframes 用の files 引数を検証する
65
+ *
66
+ * @returns 検証済みの files 配列
67
+ * @throws {Error} 引数が不正な場合
68
+ */
69
+ export function validatePushWireframesArgs(args) {
70
+ const files = args.files;
71
+ if (!Array.isArray(files) || files.length === 0) {
72
+ throw new Error("files パラメータが必要です(非空の配列)");
73
+ }
74
+ for (const file of files) {
75
+ if (typeof file !== "object" ||
76
+ file === null ||
77
+ typeof file.filename !== "string" ||
78
+ typeof file.content !== "string") {
79
+ throw new Error("files の各要素は { filename: string, content: string } である必要があります");
80
+ }
81
+ }
82
+ return files;
83
+ }
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  ],
11
11
  "homepage": "https://vaden.ai",
12
12
  "license": "MIT",
13
- "version": "0.2.1",
13
+ "version": "0.3.0",
14
14
  "type": "module",
15
15
  "publishConfig": {
16
16
  "access": "public"