@mixio-pro/kalaasetu-mcp 1.0.6 → 1.0.7

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/README.md CHANGED
@@ -68,7 +68,9 @@ Add to your Cursor settings (`~/.cursor/config.json` or via Settings → MCP):
68
68
  "env": {
69
69
  "GEMINI_API_KEY": "your-gemini-api-key",
70
70
  "FAL_KEY": "your-fal-api-key",
71
- "PERPLEXITY_API_KEY": "your-perplexity-api-key"
71
+ "PERPLEXITY_API_KEY": "your-perplexity-api-key",
72
+ "STORAGE_PROVIDER":"gcs",
73
+ "GCS_BUCKET":"your-gcs-bucket-name"
72
74
  }
73
75
  }
74
76
  }
@@ -88,7 +90,9 @@ Add to your OpenCode MCP configuration:
88
90
  "environment": {
89
91
  "GEMINI_API_KEY": "your-gemini-api-key",
90
92
  "FAL_KEY": "your-fal-api-key",
91
- "PERPLEXITY_API_KEY": "your-perplexity-api-key"
93
+ "PERPLEXITY_API_KEY": "your-perplexity-api-key",
94
+ "STORAGE_PROVIDER":"gcs",
95
+ "GCS_BUCKET":"your-bucket-name"
92
96
  }
93
97
  }
94
98
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mixio-pro/kalaasetu-mcp",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "A powerful Model Context Protocol server providing AI tools for content generation and analysis",
5
5
  "type": "module",
6
6
  "module": "src/index.ts",
@@ -49,8 +49,10 @@
49
49
  "dependencies": {
50
50
  "@fal-ai/client": "^1.7.2",
51
51
  "@google/genai": "^1.28.0",
52
+ "@types/node": "^24.10.1",
52
53
  "@types/wav": "^1.0.4",
53
54
  "fastmcp": "^3.22.0",
55
+ "form-data": "^4.0.5",
54
56
  "google-auth-library": "^10.5.0",
55
57
  "wav": "^1.0.2",
56
58
  "zod": "^4.1.12"
@@ -0,0 +1,116 @@
1
+ import { GoogleAuth } from "google-auth-library";
2
+ import type { StorageProvider } from "./interface";
3
+ import * as path from "path";
4
+
5
+ export class GCSStorageProvider implements StorageProvider {
6
+ private bucket: string;
7
+ private auth: GoogleAuth;
8
+
9
+ constructor(bucket: string) {
10
+ this.bucket = bucket;
11
+ this.auth = new GoogleAuth({
12
+ scopes: ["https://www.googleapis.com/auth/cloud-platform"],
13
+ });
14
+ }
15
+
16
+ async init(): Promise<void> {
17
+ console.log(
18
+ `Initializing GCS Storage Provider with bucket: ${this.bucket}`
19
+ );
20
+ // Verify we can get credentials
21
+ try {
22
+ await this.auth.getClient();
23
+ } catch (error) {
24
+ console.warn(`Warning: Could not initialize GCS client: ${error}`);
25
+ }
26
+ }
27
+
28
+ private async getAccessToken(): Promise<string> {
29
+ const client = await this.auth.getClient();
30
+ const token = await client.getAccessToken();
31
+ if (!token.token) {
32
+ throw new Error("Failed to get GCS access token");
33
+ }
34
+ return token.token;
35
+ }
36
+
37
+ async readFile(filePath: string): Promise<Buffer> {
38
+ const objectName = path.basename(filePath);
39
+ const url = `https://storage.googleapis.com/storage/v1/b/${
40
+ this.bucket
41
+ }/o/${encodeURIComponent(objectName)}?alt=media`;
42
+
43
+ const token = await this.getAccessToken();
44
+ const response = await fetch(url, {
45
+ headers: {
46
+ Authorization: `Bearer ${token}`,
47
+ },
48
+ });
49
+
50
+ if (!response.ok) {
51
+ throw new Error(
52
+ `Failed to read file from GCS: ${response.status} ${response.statusText}`
53
+ );
54
+ }
55
+
56
+ const arrayBuffer = await response.arrayBuffer();
57
+ return Buffer.from(arrayBuffer);
58
+ }
59
+
60
+ async writeFile(filePath: string, data: Buffer | string): Promise<string> {
61
+ const objectName = path.basename(filePath);
62
+ const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
63
+
64
+ // Upload using JSON API
65
+ const url = `https://storage.googleapis.com/upload/storage/v1/b/${
66
+ this.bucket
67
+ }/o?uploadType=media&name=${encodeURIComponent(objectName)}`;
68
+
69
+ const token = await this.getAccessToken();
70
+ const response = await fetch(url, {
71
+ method: "POST",
72
+ headers: {
73
+ Authorization: `Bearer ${token}`,
74
+ "Content-Type": "application/octet-stream",
75
+ "Content-Length": buffer.length.toString(),
76
+ },
77
+ body: buffer,
78
+ });
79
+
80
+ if (!response.ok) {
81
+ const errorText = await response.text();
82
+ throw new Error(
83
+ `Failed to upload to GCS: ${response.status} ${errorText}`
84
+ );
85
+ }
86
+
87
+ // Return public URL
88
+ return `https://storage.googleapis.com/${this.bucket}/${objectName}`;
89
+ }
90
+
91
+ async exists(filePath: string): Promise<boolean> {
92
+ try {
93
+ const objectName = path.basename(filePath);
94
+ const url = `https://storage.googleapis.com/storage/v1/b/${
95
+ this.bucket
96
+ }/o/${encodeURIComponent(objectName)}`;
97
+
98
+ const token = await this.getAccessToken();
99
+ const response = await fetch(url, {
100
+ method: "GET",
101
+ headers: {
102
+ Authorization: `Bearer ${token}`,
103
+ },
104
+ });
105
+
106
+ return response.ok;
107
+ } catch {
108
+ return false;
109
+ }
110
+ }
111
+
112
+ async getPublicUrl(filePath: string): Promise<string> {
113
+ const objectName = path.basename(filePath);
114
+ return `https://storage.googleapis.com/${this.bucket}/${objectName}`;
115
+ }
116
+ }
@@ -1,24 +1,27 @@
1
1
  import type { StorageProvider } from "./interface";
2
2
  import { LocalStorageProvider } from "./local";
3
- import { PayloadStorageProvider } from "./payload";
3
+ import { GCSStorageProvider } from "./gcs";
4
4
 
5
5
  let storageInstance: StorageProvider | null = null;
6
6
 
7
7
  export function getStorage(): StorageProvider {
8
8
  if (!storageInstance) {
9
9
  const type = process.env.STORAGE_PROVIDER || "local";
10
- console.error(`Initializing storage provider: ${type}`); // Log to stderr for debug
10
+ console.error(`Initializing storage provider: ${type}`);
11
11
 
12
- if (type === "payload") {
13
- storageInstance = new PayloadStorageProvider(
14
- process.env.PAYLOAD_API_URL || "http://localhost:3000",
15
- process.env.PAYLOAD_API_KEY || ""
16
- );
12
+ if (type === "gcs") {
13
+ const bucket = process.env.GCS_BUCKET;
14
+
15
+ if (!bucket) {
16
+ throw new Error("GCS_BUCKET is required when using gcs storage");
17
+ }
18
+
19
+ storageInstance = new GCSStorageProvider(bucket);
17
20
  } else {
18
21
  storageInstance = new LocalStorageProvider(process.cwd());
19
22
  }
20
23
 
21
- // Initialize async (fire and forget or handle properly in app startup)
24
+ // Initialize async
22
25
  storageInstance
23
26
  .init()
24
27
  .catch((err) => console.error("Failed to init storage:", err));
@@ -1,7 +1,7 @@
1
1
  export interface StorageProvider {
2
2
  init(): Promise<void>;
3
3
  readFile(path: string): Promise<Buffer>;
4
- writeFile(path: string, data: Buffer | string): Promise<string>; // Returns path or URL
4
+ writeFile(path: string, data: Buffer | string): Promise<string>; // Returns public URL
5
5
  exists(path: string): Promise<boolean>;
6
6
  getPublicUrl(path: string): Promise<string>;
7
7
  }
@@ -14,7 +14,6 @@ export class LocalStorageProvider implements StorageProvider {
14
14
  }
15
15
 
16
16
  async readFile(filePath: string): Promise<Buffer> {
17
- // Handle absolute paths by checking if it starts with basePath or just use it if it exists
18
17
  let fullPath = filePath;
19
18
  if (!path.isAbsolute(filePath)) {
20
19
  fullPath = path.resolve(this.basePath, filePath);
@@ -45,7 +44,6 @@ export class LocalStorageProvider implements StorageProvider {
45
44
  }
46
45
 
47
46
  async getPublicUrl(filePath: string): Promise<string> {
48
- // For local, we just return the absolute path
49
47
  let fullPath = filePath;
50
48
  if (!path.isAbsolute(filePath)) {
51
49
  fullPath = path.resolve(this.basePath, filePath);
@@ -164,7 +164,7 @@ async function processVideoInput(
164
164
  }
165
165
 
166
166
  export const geminiTextToImage = {
167
- name: "geminiTextToImage",
167
+ name: "generateImage",
168
168
  description:
169
169
  "Generate images from text prompts using Gemini 2.5 Flash Image model",
170
170
  parameters: z.object({
@@ -203,11 +203,11 @@ export const geminiTextToImage = {
203
203
  const imageData = part.inlineData.data;
204
204
  if (args.output_path) {
205
205
  const storage = getStorage();
206
- await storage.writeFile(
206
+ const url = await storage.writeFile(
207
207
  args.output_path,
208
208
  Buffer.from(imageData, "base64")
209
209
  );
210
- result += `\nImage saved to: ${args.output_path}`;
210
+ result += `\nImage saved to: ${url}`;
211
211
  } else {
212
212
  result += `\nGenerated image (base64): ${imageData.substring(
213
213
  0,
@@ -225,7 +225,7 @@ export const geminiTextToImage = {
225
225
  };
226
226
 
227
227
  export const geminiEditImage = {
228
- name: "geminiEditImage",
228
+ name: "editImage",
229
229
  description:
230
230
  "Edit existing images with text instructions using Gemini 2.5 Flash Image Preview",
231
231
  parameters: z.object({
@@ -270,11 +270,11 @@ export const geminiEditImage = {
270
270
  const imageData = part.inlineData.data;
271
271
  if (args.output_path) {
272
272
  const storage = getStorage();
273
- await storage.writeFile(
273
+ const url = await storage.writeFile(
274
274
  args.output_path,
275
275
  Buffer.from(imageData, "base64")
276
276
  );
277
- result += `\nEdited image saved to: ${args.output_path}`;
277
+ result += `\nEdited image saved to: ${url}`;
278
278
  } else {
279
279
  result += `\nEdited image (base64): ${imageData.substring(
280
280
  0,
@@ -292,7 +292,7 @@ export const geminiEditImage = {
292
292
  };
293
293
 
294
294
  export const geminiAnalyzeImages = {
295
- name: "geminiAnalyzeImages",
295
+ name: "analyzeImages",
296
296
  description:
297
297
  "Analyze and describe images using Gemini 2.5 Pro with advanced multimodal understanding",
298
298
  parameters: z.object({
@@ -358,7 +358,7 @@ export const geminiAnalyzeImages = {
358
358
  };
359
359
 
360
360
  export const geminiSingleSpeakerTts = {
361
- name: "geminiSingleSpeakerTts",
361
+ name: "generateSpeech",
362
362
  description:
363
363
  "Generate single speaker voice audio from text using Gemini 2.5 Pro Preview TTS model",
364
364
  parameters: z.object({
@@ -407,9 +407,10 @@ export const geminiSingleSpeakerTts = {
407
407
  // Generate output filename if not provided
408
408
  const outputPath = args.output_path || `voice_output_${Date.now()}.wav`;
409
409
 
410
- await saveWaveFile(outputPath, audioBuffer);
410
+ const storage = getStorage();
411
+ const url = await storage.writeFile(outputPath, audioBuffer);
411
412
 
412
- return `Audio generated successfully: ${outputPath}`;
413
+ return `Audio generated successfully: ${url}`;
413
414
  } catch (error: any) {
414
415
  throw new Error(`Voice generation failed: ${error.message}`);
415
416
  }
@@ -417,7 +418,7 @@ export const geminiSingleSpeakerTts = {
417
418
  };
418
419
 
419
420
  export const geminiAnalyzeVideos = {
420
- name: "geminiAnalyzeVideos",
421
+ name: "analyzeVideos",
421
422
  description:
422
423
  "Analyze and understand video content using Gemini 2.5 Flash model. Intelligently handles YouTube URLs and local videos (files <20MB processed inline, ≥20MB uploaded via File API). Supports timestamp queries, clipping, and custom frame rates with default 5 FPS for local videos to optimize processing.",
423
424
  parameters: z.object({
@@ -64,7 +64,7 @@ async function fileToBase64(
64
64
  }
65
65
 
66
66
  export const imageToVideo = {
67
- name: "image_to_video",
67
+ name: "generateVideoi2v",
68
68
  description:
69
69
  "Generate videos from an image as starting first frame using Vertex Veo models (predictLongRunning + fetchPredictOperation).",
70
70
  parameters: z.object({
@@ -291,14 +291,11 @@ export const imageToVideo = {
291
291
  ? args.output_path
292
292
  : args.output_path.replace(/\.mp4$/i, `_${index}.mp4`)
293
293
  : `video_output_${Date.now()}${index === 0 ? "" : "_" + index}.mp4`;
294
- // For storage provider, we use the path as is (relative or absolute)
295
- // If using LocalStorage, it handles resolving.
296
- // If using Payload, it handles the key.
297
294
 
298
295
  const buf = Buffer.from(base64, "base64");
299
296
  const storage = getStorage();
300
- await storage.writeFile(filePath, buf);
301
- outputs.push(filePath);
297
+ const url = await storage.writeFile(filePath, buf);
298
+ outputs.push(url);
302
299
  };
303
300
 
304
301
  if (Array.isArray(resp?.videos) && resp.videos.length > 0) {
@@ -310,7 +307,7 @@ export const imageToVideo = {
310
307
  }
311
308
  }
312
309
  if (outputs.length > 0) {
313
- return `Video(s) saved: ${outputs.join(", ")}`;
310
+ return `Video(s) saved to: ${outputs.join(", ")}`;
314
311
  }
315
312
 
316
313
  // If nothing saved, return a concise summary plus head/tail snippets of JSON
@@ -1,46 +0,0 @@
1
- import type { StorageProvider } from "./interface";
2
-
3
- export class PayloadStorageProvider implements StorageProvider {
4
- private apiUrl: string;
5
- private apiKey: string;
6
- private collection: string;
7
-
8
- constructor(apiUrl: string, apiKey: string, collection: string = "media") {
9
- this.apiUrl = apiUrl;
10
- this.apiKey = apiKey;
11
- this.collection = collection;
12
- }
13
-
14
- async init(): Promise<void> {
15
- console.log("Initializing Payload Storage Provider...");
16
- // TODO: Verify connection to Payload CMS
17
- }
18
-
19
- async readFile(filePath: string): Promise<Buffer> {
20
- // TODO: Implement fetching file from Payload CMS
21
- // 1. Search for file by filename or ID
22
- // 2. Download the file buffer
23
- console.log(`[Payload] Reading file: ${filePath}`);
24
- throw new Error("PayloadStorageProvider.readFile not implemented yet.");
25
- }
26
-
27
- async writeFile(filePath: string, data: Buffer | string): Promise<string> {
28
- // TODO: Implement uploading file to Payload CMS
29
- // 1. Create FormData
30
- // 2. POST to /api/{collection}
31
- console.log(`[Payload] Writing file: ${filePath}`);
32
- throw new Error("PayloadStorageProvider.writeFile not implemented yet.");
33
- }
34
-
35
- async exists(filePath: string): Promise<boolean> {
36
- // TODO: Check if file exists in Payload
37
- console.log(`[Payload] Checking existence: ${filePath}`);
38
- return false;
39
- }
40
-
41
- async getPublicUrl(filePath: string): Promise<string> {
42
- // TODO: Return the public URL of the file in Payload
43
- console.log(`[Payload] Getting public URL: ${filePath}`);
44
- return `${this.apiUrl}/${this.collection}/${filePath}`;
45
- }
46
- }