@goonnguyen/human-mcp 2.8.0 → 2.8.2

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 (2) hide show
  1. package/dist/index.js +168 -141
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -167922,29 +167922,48 @@ async function editImage(geminiClient, options, config) {
167922
167922
  const editingPrompt = buildEditingPrompt(options);
167923
167923
  logger2.info(`Image editing operation: ${options.operation}`);
167924
167924
  logger2.info(`Editing prompt: "${editingPrompt}"`);
167925
- const model = geminiClient.getModel("detailed");
167925
+ const model = geminiClient.getImageGenerationModel();
167926
167926
  const requestContent = await buildRequestContent(options, processedInputImage, editingPrompt);
167927
167927
  const response = await model.generateContent(requestContent);
167928
167928
  const result = response.response;
167929
167929
  const candidates = result.candidates;
167930
+ logger2.debug(`Gemini API response structure: ${JSON.stringify({
167931
+ hasCandidates: !!candidates,
167932
+ candidatesLength: candidates?.length,
167933
+ firstCandidate: candidates?.[0] ? {
167934
+ hasContent: !!candidates[0].content,
167935
+ hasParts: !!candidates[0].content?.parts,
167936
+ partsLength: candidates[0].content?.parts?.length
167937
+ } : null
167938
+ })}`);
167930
167939
  if (!candidates || candidates.length === 0) {
167931
- throw new Error("No image candidates returned from Gemini API");
167940
+ logger2.error("No candidates in Gemini response. Full response:", JSON.stringify(result, null, 2));
167941
+ throw new Error("No image candidates returned from Gemini API. This may indicate the API doesn't support image editing yet, or the request format is incorrect.");
167932
167942
  }
167933
167943
  const candidate = candidates[0];
167934
- if (!candidate || !candidate.content || !candidate.content.parts) {
167935
- throw new Error("Invalid response format from Gemini API");
167944
+ if (!candidate || !candidate.content) {
167945
+ logger2.error("Invalid candidate structure:", JSON.stringify(candidate, null, 2));
167946
+ throw new Error("Invalid response format from Gemini API: missing candidate content");
167947
+ }
167948
+ if (!candidate.content.parts || candidate.content.parts.length === 0) {
167949
+ logger2.error("No parts in candidate content:", JSON.stringify(candidate.content, null, 2));
167950
+ throw new Error("Invalid response format from Gemini API: missing content parts. Note: Gemini image editing may not be available in the current API version.");
167936
167951
  }
167937
167952
  let imageData = null;
167938
167953
  let mimeType = "image/jpeg";
167954
+ logger2.debug(`Searching for image data in ${candidate.content.parts.length} parts`);
167939
167955
  for (const part of candidate.content.parts) {
167956
+ logger2.debug(`Part type: ${JSON.stringify(Object.keys(part))}`);
167940
167957
  if ("inlineData" in part && part.inlineData) {
167941
167958
  imageData = part.inlineData.data;
167942
167959
  mimeType = part.inlineData.mimeType || "image/jpeg";
167960
+ logger2.info(`Found image data: ${imageData.length} bytes, type: ${mimeType}`);
167943
167961
  break;
167944
167962
  }
167945
167963
  }
167946
167964
  if (!imageData) {
167947
- throw new Error("No image data found in Gemini response");
167965
+ logger2.error("No image data found in response parts:", JSON.stringify(candidate.content.parts, null, 2));
167966
+ throw new Error("No image data found in Gemini response. The API may have returned text instead of an edited image.");
167948
167967
  }
167949
167968
  const processingTime = Date.now() - startTime;
167950
167969
  let resultData;
@@ -168161,6 +168180,68 @@ function estimateImageSize2(base64Data) {
168161
168180
  // src/tools/hands/index.ts
168162
168181
  init_logger();
168163
168182
  init_errors();
168183
+
168184
+ // src/utils/response-formatter.ts
168185
+ function formatMediaResponse(result, config, contextText) {
168186
+ const isHttpTransport = config.transport.type === "http" || config.transport.type === "both" && config.transport.http?.enabled;
168187
+ if (isHttpTransport && result.url) {
168188
+ const response = [];
168189
+ response.push({
168190
+ type: "resource",
168191
+ resource: {
168192
+ uri: result.url,
168193
+ mimeType: result.mimeType || "image/png",
168194
+ text: contextText || `Generated media available at: ${result.url}`
168195
+ }
168196
+ });
168197
+ const details = [];
168198
+ if (result.size) {
168199
+ details.push(`Size: ${(result.size / 1024).toFixed(2)} KB`);
168200
+ }
168201
+ if (result.width && result.height) {
168202
+ details.push(`Dimensions: ${result.width}x${result.height}`);
168203
+ }
168204
+ response.push({
168205
+ type: "text",
168206
+ text: `✅ Media generated successfully!
168207
+
168208
+ URL: ${result.url}${details.length > 0 ? `
168209
+ ` + details.join(", ") : ""}`
168210
+ });
168211
+ return response;
168212
+ }
168213
+ if (result.base64) {
168214
+ return [
168215
+ {
168216
+ type: "image",
168217
+ data: result.base64,
168218
+ mimeType: result.mimeType || "image/png"
168219
+ },
168220
+ {
168221
+ type: "text",
168222
+ text: contextText || (result.url ? `Image URL: ${result.url}` : "Image generated successfully")
168223
+ }
168224
+ ];
168225
+ }
168226
+ if (result.url) {
168227
+ return [
168228
+ {
168229
+ type: "text",
168230
+ text: `${contextText || "Media generated successfully"}
168231
+
168232
+ URL: ${result.url}`
168233
+ }
168234
+ ];
168235
+ }
168236
+ return [
168237
+ {
168238
+ type: "text",
168239
+ text: contextText || "Media generated successfully"
168240
+ }
168241
+ ];
168242
+ }
168243
+
168244
+ // src/tools/hands/index.ts
168164
168245
  async function registerHandsTool(server, config) {
168165
168246
  const geminiClient = new GeminiClient(config);
168166
168247
  server.registerTool("gemini_gen_image", {
@@ -168440,21 +168521,16 @@ async function handleImageGeneration(geminiClient, args, config) {
168440
168521
  filePrefix: "gemini-image"
168441
168522
  };
168442
168523
  const result = await generateImage(geminiClient, generationOptions, config);
168524
+ let base64Data;
168525
+ let mimeType;
168443
168526
  if (result.imageData.startsWith("data:")) {
168444
168527
  const matches = result.imageData.match(/data:([^;]+);base64,(.+)/);
168445
168528
  if (matches && matches[1] && matches[2]) {
168446
- const mimeType = matches[1];
168447
- const base64Data = matches[2];
168448
- return {
168449
- content: [
168450
- {
168451
- type: "image",
168452
- data: base64Data,
168453
- mimeType
168454
- },
168455
- {
168456
- type: "text",
168457
- text: `✅ Image generated successfully using ${result.model}
168529
+ mimeType = matches[1];
168530
+ base64Data = matches[2];
168531
+ }
168532
+ }
168533
+ const contextText = `✅ Image generated successfully using ${result.model}
168458
168534
 
168459
168535
  **Generation Details:**
168460
168536
  - Prompt: "${prompt}"
@@ -168468,35 +168544,16 @@ async function handleImageGeneration(geminiClient, args, config) {
168468
168544
  - File Path: ${result.filePath}
168469
168545
  - File Name: ${result.fileName}
168470
168546
  - File Size: ${result.fileSize} bytes` : ""}${result.fileUrl ? `
168471
- - Public URL: ${result.fileUrl}` : ""}`
168472
- }
168473
- ],
168474
- isError: false
168475
- };
168476
- }
168477
- }
168547
+ - Public URL: ${result.fileUrl}` : ""}`;
168548
+ const formattedResponse = formatMediaResponse({
168549
+ url: result.fileUrl,
168550
+ filePath: result.filePath,
168551
+ base64: base64Data,
168552
+ mimeType,
168553
+ size: result.fileSize
168554
+ }, config, contextText);
168478
168555
  return {
168479
- content: [
168480
- {
168481
- type: "text",
168482
- text: `✅ Image generated successfully!
168483
-
168484
- **Generation Details:**
168485
- - Prompt: "${prompt}"
168486
- - Model: ${result.model}
168487
- - Format: ${result.format}
168488
- - Size: ${result.size}
168489
- - Generation Time: ${result.generationTime}ms${result.filePath ? `
168490
-
168491
- **File Information:**
168492
- - File Path: ${result.filePath}
168493
- - File Name: ${result.fileName}
168494
- - File Size: ${result.fileSize} bytes` : ""}${result.fileUrl ? `
168495
- - Public URL: ${result.fileUrl}` : ""}
168496
-
168497
- **Image Data:** ${result.imageData.substring(0, 100)}...`
168498
- }
168499
- ],
168556
+ content: formattedResponse,
168500
168557
  isError: false
168501
168558
  };
168502
168559
  }
@@ -168521,34 +168578,32 @@ async function handleVideoGeneration(geminiClient, args, config) {
168521
168578
  filePrefix: "gemini-video"
168522
168579
  };
168523
168580
  const result = await generateVideo(geminiClient, generationOptions, config);
168581
+ const contextText = `✅ Video generated successfully!
168582
+
168583
+ **Generation Details:**
168584
+ - Prompt: "${prompt}"
168585
+ - Model: ${result.model}
168586
+ - Format: ${result.format}
168587
+ - Duration: ${result.duration}
168588
+ - Aspect Ratio: ${result.aspectRatio}
168589
+ - FPS: ${result.fps}
168590
+ - Generation Time: ${result.generationTime}ms
168591
+ - Operation ID: ${result.operationId}
168592
+ - Timestamp: ${new Date().toISOString()}${result.filePath ? `
168593
+
168594
+ **File Information:**
168595
+ - File Path: ${result.filePath}
168596
+ - File Name: ${result.fileName}
168597
+ - File Size: ${result.fileSize} bytes` : ""}${result.fileUrl ? `
168598
+ - Public URL: ${result.fileUrl}` : ""}`;
168599
+ const formattedResponse = formatMediaResponse({
168600
+ url: result.fileUrl,
168601
+ filePath: result.filePath,
168602
+ mimeType: `video/${result.format}`,
168603
+ size: result.fileSize
168604
+ }, config, contextText);
168524
168605
  return {
168525
- content: [
168526
- {
168527
- type: "text",
168528
- text: JSON.stringify({
168529
- success: true,
168530
- video: result.filePath ? `File saved to: ${result.filePath}` : result.videoData.substring(0, 100) + "...",
168531
- format: result.format,
168532
- model: result.model,
168533
- prompt,
168534
- operation_id: result.operationId,
168535
- file_info: result.filePath ? {
168536
- file_path: result.filePath,
168537
- file_name: result.fileName,
168538
- file_size: result.fileSize,
168539
- public_url: result.fileUrl
168540
- } : null,
168541
- metadata: {
168542
- timestamp: new Date().toISOString(),
168543
- generation_time: result.generationTime,
168544
- duration: result.duration,
168545
- aspect_ratio: result.aspectRatio,
168546
- fps: result.fps,
168547
- size: result.size
168548
- }
168549
- }, null, 2)
168550
- }
168551
- ],
168606
+ content: formattedResponse,
168552
168607
  isError: false
168553
168608
  };
168554
168609
  }
@@ -168584,35 +168639,32 @@ async function handleImageToVideoGeneration(geminiClient, args, config) {
168584
168639
  filePrefix: "gemini-image-to-video"
168585
168640
  };
168586
168641
  const result = await generateImageToVideo(geminiClient, prompt, image_input, generationOptions, config);
168642
+ const contextText = `✅ Video generated from image successfully!
168643
+
168644
+ **Generation Details:**
168645
+ - Prompt: "${prompt}"
168646
+ - Model: ${result.model}
168647
+ - Format: ${result.format}
168648
+ - Duration: ${result.duration}
168649
+ - Aspect Ratio: ${result.aspectRatio}
168650
+ - FPS: ${result.fps}
168651
+ - Generation Time: ${result.generationTime}ms
168652
+ - Operation ID: ${result.operationId}
168653
+ - Timestamp: ${new Date().toISOString()}${result.filePath ? `
168654
+
168655
+ **File Information:**
168656
+ - File Path: ${result.filePath}
168657
+ - File Name: ${result.fileName}
168658
+ - File Size: ${result.fileSize} bytes` : ""}${result.fileUrl ? `
168659
+ - Public URL: ${result.fileUrl}` : ""}`;
168660
+ const formattedResponse = formatMediaResponse({
168661
+ url: result.fileUrl,
168662
+ filePath: result.filePath,
168663
+ mimeType: `video/${result.format}`,
168664
+ size: result.fileSize
168665
+ }, config, contextText);
168587
168666
  return {
168588
- content: [
168589
- {
168590
- type: "text",
168591
- text: JSON.stringify({
168592
- success: true,
168593
- video: result.filePath ? `File saved to: ${result.filePath}` : result.videoData.substring(0, 100) + "...",
168594
- format: result.format,
168595
- model: result.model,
168596
- prompt,
168597
- image_input,
168598
- operation_id: result.operationId,
168599
- file_info: result.filePath ? {
168600
- file_path: result.filePath,
168601
- file_name: result.fileName,
168602
- file_size: result.fileSize,
168603
- public_url: result.fileUrl
168604
- } : null,
168605
- metadata: {
168606
- timestamp: new Date().toISOString(),
168607
- generation_time: result.generationTime,
168608
- duration: result.duration,
168609
- aspect_ratio: result.aspectRatio,
168610
- fps: result.fps,
168611
- size: result.size
168612
- }
168613
- }, null, 2)
168614
- }
168615
- ],
168667
+ content: formattedResponse,
168616
168668
  isError: false
168617
168669
  };
168618
168670
  }
@@ -168670,21 +168722,16 @@ async function handleImageEditing(geminiClient, args, config) {
168670
168722
  filePrefix: `edited-${operation}`
168671
168723
  };
168672
168724
  const result = await editImage(geminiClient, editingOptions, config);
168725
+ let base64Data;
168726
+ let mimeType;
168673
168727
  if (result.editedImageData.startsWith("data:")) {
168674
168728
  const matches = result.editedImageData.match(/data:([^;]+);base64,(.+)/);
168675
168729
  if (matches && matches[1] && matches[2]) {
168676
- const mimeType = matches[1];
168677
- const base64Data = matches[2];
168678
- return {
168679
- content: [
168680
- {
168681
- type: "image",
168682
- data: base64Data,
168683
- mimeType
168684
- },
168685
- {
168686
- type: "text",
168687
- text: `✅ Image edited successfully using ${operation} operation
168730
+ mimeType = matches[1];
168731
+ base64Data = matches[2];
168732
+ }
168733
+ }
168734
+ const contextText = `✅ Image edited successfully using ${operation} operation
168688
168735
 
168689
168736
  **Editing Details:**
168690
168737
  - Operation: ${operation}
@@ -168705,36 +168752,16 @@ async function handleImageEditing(geminiClient, args, config) {
168705
168752
  **Operation Metadata:**
168706
168753
  - Strength: ${result.metadata.strength}
168707
168754
  - Guidance Scale: ${result.metadata.guidanceScale}
168708
- - Seed: ${result.metadata.seed || "random"}` : ""}`
168709
- }
168710
- ],
168711
- isError: false
168712
- };
168713
- }
168714
- }
168755
+ - Seed: ${result.metadata.seed || "random"}` : ""}`;
168756
+ const formattedResponse = formatMediaResponse({
168757
+ url: result.fileUrl,
168758
+ filePath: result.filePath,
168759
+ base64: base64Data,
168760
+ mimeType,
168761
+ size: result.fileSize
168762
+ }, config, contextText);
168715
168763
  return {
168716
- content: [
168717
- {
168718
- type: "text",
168719
- text: `✅ Image edited successfully!
168720
-
168721
- **Editing Details:**
168722
- - Operation: ${operation}
168723
- - Prompt: "${prompt}"
168724
- - Format: ${result.format}
168725
- - Original Size: ${result.originalSize}
168726
- - Edited Size: ${result.editedSize}
168727
- - Processing Time: ${result.processingTime}ms${result.filePath ? `
168728
-
168729
- **File Information:**
168730
- - File Path: ${result.filePath}
168731
- - File Name: ${result.fileName}
168732
- - File Size: ${result.fileSize} bytes` : ""}${result.fileUrl ? `
168733
- - Public URL: ${result.fileUrl}` : ""}
168734
-
168735
- **Edited Image Data:** ${result.editedImageData.substring(0, 100)}...`
168736
- }
168737
- ],
168764
+ content: formattedResponse,
168738
168765
  isError: false
168739
168766
  };
168740
168767
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goonnguyen/human-mcp",
3
- "version": "2.8.0",
3
+ "version": "2.8.2",
4
4
  "description": "Human MCP: Bringing Human Capabilities to Coding Agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",