@icogenie/mcp 0.1.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.
Files changed (3) hide show
  1. package/README.md +10 -7
  2. package/dist/index.js +98 -69
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -64,7 +64,7 @@ generate_icon({
64
64
  })
65
65
  ```
66
66
 
67
- **Returns:** `{ generationId, preview, creditsUsed, creditsRemaining, metadata }`
67
+ **Returns:** `{ sessionId, preview, previews, creditsRemaining, sessionData, suggestions }`
68
68
 
69
69
  ### regenerate_icon
70
70
  Regenerate a specific icon variation with a custom refinement prompt.
@@ -73,12 +73,15 @@ Regenerate a specific icon variation with a custom refinement prompt.
73
73
 
74
74
  ```
75
75
  regenerate_icon({
76
- generationId: "abc123", // or bundleId for bundles
77
- index: 0, // which variation (0-based)
76
+ sessionId: "abc123", // for single icons
77
+ bundleId: "xyz789", // for bundles (use one or the other)
78
+ index: 0, // which variation (0-based)
78
79
  prompt: "Make it more 3D" // optional refinement
79
80
  })
80
81
  ```
81
82
 
83
+ **Returns:** `{ success, index, preview, creditsRemaining }`
84
+
82
85
  ### check_credits
83
86
  Check your current credit balance.
84
87
 
@@ -132,7 +135,7 @@ generate_bundle({
132
135
  })
133
136
  ```
134
137
 
135
- **Returns:** `{ bundleId, icons: [{ name, preview }], credits }`
138
+ **Returns:** `{ bundleId, iconCount, icons: [{ name, description, preview }], pricing, creditsUsed, creditsRemaining }`
136
139
 
137
140
  ## Example Workflow
138
141
 
@@ -145,19 +148,19 @@ generate_bundle({
145
148
  2. **Generate a single icon:**
146
149
  ```
147
150
  generate_icon({ prompt: "notification bell icon", style: "outline" })
148
- → { generationId: "abc123", preview: "...", creditsRemaining: 49 }
151
+ → { sessionId: "abc123", preview: "...", creditsRemaining: 49 }
149
152
  ```
150
153
 
151
154
  3. **Refine if needed:**
152
155
  ```
153
- regenerate_icon({ generationId: "abc123", index: 0, prompt: "Add a dot indicator" })
156
+ regenerate_icon({ sessionId: "abc123", index: 0, prompt: "Add a dot indicator" })
154
157
  → { preview: "...", creditsRemaining: 48 }
155
158
  ```
156
159
 
157
160
  4. **Download final package:**
158
161
  ```
159
162
  download_icon({ generationId: "abc123", outputPath: "./bell-icon.zip" })
160
- → { savedTo: "./bell-icon.zip", creditsRemaining: 43 }
163
+ → { savedTo: "./bell-icon.zip" }
161
164
  ```
162
165
 
163
166
  ## Bundle Workflow
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
5
5
 
6
6
  // src/server.ts
7
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
+ import { CREDIT_CONFIG } from "@icogenie/core";
8
9
 
9
10
  // src/tools/generate.ts
10
11
  import { z } from "zod";
@@ -162,15 +163,32 @@ function readReferenceImage(filePath) {
162
163
  const mimeType = ext === ".png" ? "image/png" : ext === ".webp" ? "image/webp" : "image/jpeg";
163
164
  return { data: buffer.toString("base64"), mimeType };
164
165
  }
165
- async function request2(endpoint, body, requireAuth = true) {
166
- const url = `${getApiUrl()}/api/cli${endpoint}`;
166
+ async function request2(endpoint, options = {}, requireAuth = true) {
167
+ const { method = "POST", body, params } = options;
168
+ let url = `${getApiUrl()}/api${endpoint}`;
169
+ if (params && Object.keys(params).length > 0) {
170
+ url += `?${new URLSearchParams(params).toString()}`;
171
+ }
167
172
  const makeRequest = async (sessionToken) => {
168
- const requestBody = sessionToken ? { ...body, sessionToken } : body;
173
+ const headers = {};
174
+ if (sessionToken) {
175
+ headers["Authorization"] = `Bearer ${sessionToken}`;
176
+ }
177
+ if (body) {
178
+ headers["Content-Type"] = "application/json";
179
+ }
169
180
  const response = await fetch(url, {
170
- method: "POST",
171
- headers: { "Content-Type": "application/json" },
172
- body: JSON.stringify(requestBody)
181
+ method,
182
+ headers,
183
+ ...body && { body: JSON.stringify(body) }
173
184
  });
185
+ const contentType = response.headers.get("content-type");
186
+ if (contentType?.includes("application/zip")) {
187
+ if (!response.ok) {
188
+ throw new ApiError("Download failed", response.status);
189
+ }
190
+ return response;
191
+ }
174
192
  const data = await response.json();
175
193
  if (response.status === 401 && requireAuth) {
176
194
  const newCreds = await handleAuthError();
@@ -192,51 +210,66 @@ async function request2(endpoint, body, requireAuth = true) {
192
210
  return makeRequest();
193
211
  }
194
212
  async function generate(options) {
195
- return request2("/generate", {
196
- prompt: options.prompt,
197
- variations: options.variations || 1,
198
- style: options.style || "solid",
199
- ...options.referenceImage && { referenceImage: options.referenceImage }
213
+ return request2("/generate-preview", {
214
+ body: {
215
+ prompt: options.prompt,
216
+ variations: options.variations || 1,
217
+ style: options.style || "solid",
218
+ ...options.referenceImage && { referenceImage: options.referenceImage }
219
+ }
200
220
  });
201
221
  }
202
222
  async function regenerate(options) {
203
- return request2("/regenerate", {
204
- generationId: options.generationId,
205
- bundleId: options.bundleId,
206
- index: options.index,
207
- prompt: options.prompt
223
+ return request2("/regenerate-icon", {
224
+ body: {
225
+ sessionId: options.sessionId,
226
+ bundleId: options.bundleId,
227
+ index: options.index,
228
+ prompt: options.prompt
229
+ }
208
230
  });
209
231
  }
210
232
  async function getCredits() {
211
- return request2("/credits", {});
233
+ return request2("/auth/session", { method: "GET" });
212
234
  }
213
235
  async function download(options) {
214
- return request2("/download", {
215
- generationId: options.generationId,
216
- bundleId: options.bundleId
236
+ const params = {};
237
+ if (options.generationId) params.generation_id = options.generationId;
238
+ if (options.bundleId) params.bundle_id = options.bundleId;
239
+ const response = await request2("/download", {
240
+ method: "GET",
241
+ params
217
242
  });
243
+ const contentDisposition = response.headers.get("content-disposition");
244
+ const filenameMatch = contentDisposition?.match(/filename="?([^";\n]+)"?/);
245
+ const filename = filenameMatch?.[1] || `icogenie-${(options.generationId || options.bundleId || "export").slice(0, 8)}.zip`;
246
+ return { response, filename };
218
247
  }
219
248
  async function normalizeBundle(options) {
220
- return request2("/normalize", {
221
- description: options.description,
222
- targetCount: options.targetCount,
223
- style: options.style
249
+ return request2("/normalize-bundle", {
250
+ body: {
251
+ description: options.description,
252
+ targetCount: options.targetCount,
253
+ style: options.style
254
+ }
224
255
  });
225
256
  }
226
257
  async function generateBundle(options) {
227
- return request2("/bundle", {
228
- description: options.description,
229
- targetCount: options.targetCount,
230
- icons: options.icons,
231
- style: options.style || "solid",
232
- ...options.referenceImage && { referenceImage: options.referenceImage }
258
+ const bundleId = options.bundleId || crypto.randomUUID();
259
+ return request2("/generate-bundle-preview", {
260
+ body: {
261
+ bundleId,
262
+ icons: options.icons,
263
+ style: options.style || "solid",
264
+ ...options.referenceImage && { referenceImage: options.referenceImage }
265
+ }
233
266
  });
234
267
  }
235
268
 
236
269
  // src/tools/generate.ts
237
270
  var generateIconSchema = {
238
271
  prompt: z.string().describe("Description of the icon to generate"),
239
- style: z.enum(["solid", "outline"]).default("solid").describe("Icon style"),
272
+ style: z.enum(["solid", "outline", "illustration"]).default("solid").describe("Icon style: solid (filled shapes), outline (stroked lines), or illustration (colorful, detailed)"),
240
273
  variations: z.union([z.literal(1), z.literal(2), z.literal(4)]).default(1).describe("Number of variations (1, 2, or 4)"),
241
274
  referenceImagePath: z.string().optional().describe("Local file path to reference image for style extraction"),
242
275
  referenceImage: z.object({
@@ -258,37 +291,38 @@ async function generateIcon(args) {
258
291
  referenceImage: refImage
259
292
  });
260
293
  return {
261
- generationId: result.generationId,
294
+ sessionId: result.sessionId,
262
295
  preview: result.preview,
263
- creditsUsed: result.creditsUsed,
264
- creditsRemaining: result.creditsRemaining,
265
- metadata: result.metadata
296
+ previews: result.previews,
297
+ creditsRemaining: result.credits,
298
+ sessionData: result.sessionData,
299
+ suggestions: result.suggestions
266
300
  };
267
301
  }
268
302
 
269
303
  // src/tools/regenerate.ts
270
304
  import { z as z2 } from "zod";
271
305
  var regenerateIconSchema = {
272
- generationId: z2.string().optional().describe("For single icon variations"),
306
+ sessionId: z2.string().optional().describe("For single icon variations"),
273
307
  bundleId: z2.string().optional().describe("For bundle icons"),
274
308
  index: z2.number().describe("Which variation/icon to regenerate (0-based)"),
275
309
  prompt: z2.string().optional().describe("Custom refinement prompt")
276
310
  };
277
311
  async function regenerateIcon(args) {
278
- if (!args.generationId && !args.bundleId) {
279
- throw new Error("Must provide either generationId or bundleId");
312
+ if (!args.sessionId && !args.bundleId) {
313
+ throw new Error("Must provide either sessionId or bundleId");
280
314
  }
281
315
  const result = await regenerate({
282
- generationId: args.generationId,
316
+ sessionId: args.sessionId,
283
317
  bundleId: args.bundleId,
284
318
  index: args.index,
285
319
  prompt: args.prompt
286
320
  });
287
321
  return {
288
322
  success: result.success,
323
+ index: result.index,
289
324
  preview: result.preview,
290
- creditsUsed: result.creditsUsed,
291
- creditsRemaining: result.creditsRemaining
325
+ creditsRemaining: result.credits
292
326
  };
293
327
  }
294
328
 
@@ -315,27 +349,23 @@ async function downloadIcon(args) {
315
349
  if (!args.generationId && !args.bundleId) {
316
350
  throw new Error("Must provide either generationId or bundleId");
317
351
  }
318
- const result = await download({
352
+ const { response, filename } = await download({
319
353
  generationId: args.generationId,
320
354
  bundleId: args.bundleId
321
355
  });
322
- const response = {
323
- success: result.success,
324
- creditsUsed: result.creditsUsed,
325
- creditsRemaining: result.creditsRemaining,
326
- iconCount: result.iconCount
356
+ const arrayBuffer = await response.arrayBuffer();
357
+ const zipBuffer = Buffer.from(arrayBuffer);
358
+ const result = {
359
+ success: true,
360
+ filename
327
361
  };
328
362
  if (args.outputPath) {
329
- const buffer = Buffer.from(result.bundle.data, "base64");
330
- writeFileSync(args.outputPath, buffer);
331
- response.savedTo = args.outputPath;
363
+ writeFileSync(args.outputPath, zipBuffer);
364
+ result.savedTo = args.outputPath;
332
365
  } else {
333
- response.bundle = {
334
- data: result.bundle.data,
335
- filename: result.bundle.filename
336
- };
366
+ result.bundleData = zipBuffer.toString("base64");
337
367
  }
338
- return response;
368
+ return result;
339
369
  }
340
370
 
341
371
  // src/tools/normalize-bundle.ts
@@ -343,7 +373,7 @@ import { z as z4 } from "zod";
343
373
  var normalizeBundleSchema = {
344
374
  description: z4.string().describe("Description of the icon bundle to create"),
345
375
  targetCount: z4.number().optional().describe("Target number of icons (2-20)"),
346
- style: z4.enum(["solid", "outline"]).optional().describe("Preferred icon style")
376
+ style: z4.enum(["solid", "outline", "illustration"]).optional().describe("Preferred icon style")
347
377
  };
348
378
  async function normalizeBundleTool(args) {
349
379
  const result = await normalizeBundle({
@@ -371,7 +401,7 @@ var generateBundleSchema = {
371
401
  ).optional().describe("Icon list from normalize_bundle or custom"),
372
402
  description: z5.string().optional().describe("Alternative: describe the bundle to auto-generate icon list"),
373
403
  targetCount: z5.number().optional().describe("Target icon count when using description"),
374
- style: z5.enum(["solid", "outline"]).default("solid").describe("Icon style"),
404
+ style: z5.enum(["solid", "outline", "illustration"]).default("solid").describe("Icon style: solid (filled shapes), outline (stroked lines), or illustration (colorful, detailed)"),
375
405
  referenceImagePath: z5.string().optional().describe("Local file path to reference image"),
376
406
  referenceImage: z5.object({
377
407
  data: z5.string(),
@@ -379,8 +409,8 @@ var generateBundleSchema = {
379
409
  }).optional().describe("Base64-encoded reference image")
380
410
  };
381
411
  async function generateBundleTool(args) {
382
- if (!args.icons && !args.description) {
383
- throw new Error("Must provide either icons array or description");
412
+ if (!args.icons) {
413
+ throw new Error("Must provide icons array (use normalize_bundle first to get icon list)");
384
414
  }
385
415
  let refImage;
386
416
  if (args.referenceImagePath) {
@@ -390,21 +420,20 @@ async function generateBundleTool(args) {
390
420
  }
391
421
  const result = await generateBundle({
392
422
  icons: args.icons,
393
- description: args.description,
394
- targetCount: args.targetCount,
395
423
  style: args.style || "solid",
396
424
  referenceImage: refImage
397
425
  });
398
426
  return {
399
427
  bundleId: result.bundleId,
400
- iconCount: result.iconCount,
401
- icons: result.icons.map((icon) => ({
428
+ iconCount: result.previews.length,
429
+ icons: result.previews.map((icon) => ({
402
430
  name: icon.name,
403
431
  description: icon.description,
404
432
  preview: icon.preview
405
433
  })),
406
- credits: result.credits,
407
- nextStep: result.nextStep
434
+ pricing: result.pricing,
435
+ creditsUsed: result.creditsUsed,
436
+ creditsRemaining: result.credits
408
437
  };
409
438
  }
410
439
 
@@ -418,7 +447,7 @@ function createServer() {
418
447
  "generate_icon",
419
448
  {
420
449
  title: "Generate Icon",
421
- description: "Generate an AI-powered icon preview from a text description. Costs 1 credit. Returns a generationId for download.",
450
+ description: `Generate an AI-powered icon preview from a text description. Costs ${CREDIT_CONFIG.costs.preview} credit. Returns a generationId for download. After generation, save the preview image to the user's working directory and display it.`,
422
451
  inputSchema: generateIconSchema
423
452
  },
424
453
  async (args) => {
@@ -433,7 +462,7 @@ function createServer() {
433
462
  "regenerate_icon",
434
463
  {
435
464
  title: "Regenerate Icon",
436
- description: "Regenerate a specific icon variation with an optional custom prompt. Costs 1 credit. Use with generationId (single) or bundleId (bundle).",
465
+ description: `Regenerate a specific icon variation with an optional custom prompt. Costs ${CREDIT_CONFIG.costs.preview} credit. Use with generationId (single) or bundleId (bundle). After regeneration, save and display the new preview.`,
437
466
  inputSchema: regenerateIconSchema
438
467
  },
439
468
  async (args) => {
@@ -463,7 +492,7 @@ function createServer() {
463
492
  "download_icon",
464
493
  {
465
494
  title: "Download Icon",
466
- description: "Download the final SVG + PNG package for an icon. Costs 5 credits (single) or 4 credits/icon (bundle). Provide outputPath to save to file.",
495
+ description: `Download the final SVG + PNG package for an icon. Costs ${CREDIT_CONFIG.costs.vectorDownload} credits (single) or ${CREDIT_CONFIG.costs.bundleDownload} credits/icon (bundle). Provide outputPath to save to file. Always save to the user's working directory with a descriptive filename.`,
467
496
  inputSchema: downloadIconSchema
468
497
  },
469
498
  async (args) => {
@@ -493,7 +522,7 @@ function createServer() {
493
522
  "generate_bundle",
494
523
  {
495
524
  title: "Generate Bundle",
496
- description: "Generate a bundle of icons from an icon list. Costs 1 credit per icon. Use normalize_bundle first to plan, or provide icons directly.",
525
+ description: `Generate a bundle of icons from an icon list. Costs ${CREDIT_CONFIG.costs.bundlePreview} credit per icon. Use normalize_bundle first to plan, or provide icons directly. After generation, save preview images to the user's working directory and display them.`,
497
526
  inputSchema: generateBundleSchema
498
527
  },
499
528
  async (args) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icogenie/mcp",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "MCP server for IcoGenie - Enable AI agents to generate icons programmatically",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,7 +18,8 @@
18
18
  "@modelcontextprotocol/sdk": "^1.12.0",
19
19
  "conf": "^12.0.0",
20
20
  "open": "^10.0.0",
21
- "zod": "^3.23.0"
21
+ "zod": "^3.23.0",
22
+ "@icogenie/core": "0.1.0"
22
23
  },
23
24
  "devDependencies": {
24
25
  "@types/node": "^20.0.0",