@mixio-pro/kalaasetu-mcp 1.0.12 → 1.0.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mixio-pro/kalaasetu-mcp",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
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",
@@ -1,6 +1,10 @@
1
1
  import { GoogleAuth } from "google-auth-library";
2
2
  import type { StorageProvider } from "./interface";
3
3
  import * as path from "path";
4
+ import {
5
+ getGoogleAuthClient,
6
+ getGoogleAccessToken,
7
+ } from "../utils/google-auth";
4
8
 
5
9
  export class GCSStorageProvider implements StorageProvider {
6
10
  private bucket: string;
@@ -8,7 +12,7 @@ export class GCSStorageProvider implements StorageProvider {
8
12
 
9
13
  constructor(bucket: string) {
10
14
  this.bucket = bucket;
11
- this.auth = new GoogleAuth({
15
+ this.auth = getGoogleAuthClient({
12
16
  scopes: ["https://www.googleapis.com/auth/cloud-platform"],
13
17
  });
14
18
  }
@@ -26,12 +30,12 @@ export class GCSStorageProvider implements StorageProvider {
26
30
  }
27
31
 
28
32
  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;
33
+ // Use the centralized helper which includes gcloud fallback if needed,
34
+ // although GCSStorageProvider primarily relies on the initialized GoogleAuth.
35
+ // However, if we want to support the same fallback logic as tools:
36
+ return getGoogleAccessToken({
37
+ scopes: ["https://www.googleapis.com/auth/cloud-platform"],
38
+ });
35
39
  }
36
40
 
37
41
  async readFile(filePath: string): Promise<Buffer> {
@@ -1,4 +1,3 @@
1
- import * as fs from "fs";
2
1
  import { GoogleAuth } from "google-auth-library";
3
2
  import { exec } from "child_process";
4
3
  import * as path from "path";
@@ -6,50 +5,12 @@ import { z } from "zod";
6
5
  import { getStorage } from "../storage";
7
6
  import { generateTimestampedFilename } from "../utils/filename";
8
7
 
8
+ import { getGoogleAccessToken } from "../utils/google-auth";
9
+
9
10
  async function wait(ms: number): Promise<void> {
10
11
  return new Promise((resolve) => setTimeout(resolve, ms));
11
12
  }
12
13
 
13
- async function fetchAccessToken(): Promise<string> {
14
- try {
15
- const auth = new GoogleAuth({
16
- scopes: ["https://www.googleapis.com/auth/cloud-platform"],
17
- });
18
- const client = await auth.getClient();
19
- const token = await client.getAccessToken();
20
- if (!token || typeof token !== "string") {
21
- throw new Error("No token from GoogleAuth");
22
- }
23
- return token;
24
- } catch (e) {
25
- // Fallback to gcloud
26
- return await new Promise((resolve, reject) => {
27
- exec("gcloud auth print-access-token", (err, stdout, stderr) => {
28
- if (err) {
29
- reject(
30
- new Error(
31
- `Failed to fetch an access token (ADC and gcloud): ${
32
- stderr || err.message
33
- }`
34
- )
35
- );
36
- return;
37
- }
38
- const t = (stdout || "").trim();
39
- if (!t) {
40
- reject(
41
- new Error(
42
- "Failed to fetch an access token: empty token from gcloud"
43
- )
44
- );
45
- return;
46
- }
47
- resolve(t);
48
- });
49
- });
50
- }
51
- }
52
-
53
14
  async function fileToBase64(
54
15
  filePath: string
55
16
  ): Promise<{ data: string; mimeType: string }> {
@@ -99,7 +60,9 @@ export const imageToVideo = {
99
60
  duration_seconds: z
100
61
  .string()
101
62
  .optional()
102
- .describe("Video duration in seconds: '4', '6', or '8' (default: '6')"),
63
+ .describe(
64
+ "Video duration in seconds. MUST be one of: '4', '6', or '8' (default: '6'). Other values will be rejected by Vertex AI."
65
+ ),
103
66
  resolution: z
104
67
  .string()
105
68
  .optional()
@@ -164,7 +127,33 @@ export const imageToVideo = {
164
127
  const location = args.location_id || "us-central1";
165
128
  const modelId = args.model_id || "veo-3.1-fast-generate-preview";
166
129
 
167
- const token = await fetchAccessToken();
130
+ // Validate and parse duration_seconds - snap to nearest 4, 6, or 8
131
+ let durationSeconds = parseInt(args.duration_seconds || "6");
132
+ if (isNaN(durationSeconds)) durationSeconds = 6;
133
+
134
+ const validDurations = [4, 6, 8];
135
+ // Find nearest valid duration
136
+ durationSeconds = validDurations.reduce((prev, curr) => {
137
+ return Math.abs(curr - durationSeconds) < Math.abs(prev - durationSeconds)
138
+ ? curr
139
+ : prev;
140
+ });
141
+
142
+ // Tie-breaking: if equidistant (e.g. 5), the reduce above keeps the first one (4) because < is strict.
143
+ // However, user requested "nearest duration with the ceil", effectively meaning round up if equidistant.
144
+ // Let's explicitly handle the equidistant cases or just use a custom finder.
145
+ // 5 -> equidistant to 4 and 6. "With ceil" implies 6.
146
+ // 7 -> equidistant to 6 and 8. "With ceil" implies 8.
147
+
148
+ // Simpler logic for these specific values:
149
+ if (durationSeconds === 4 && parseInt(args.duration_seconds || "6") === 5) {
150
+ durationSeconds = 6;
151
+ }
152
+ if (durationSeconds === 6 && parseInt(args.duration_seconds || "6") === 7) {
153
+ durationSeconds = 8;
154
+ }
155
+
156
+ const token = await getGoogleAccessToken();
168
157
 
169
158
  const url = `https://${location}-aiplatform.googleapis.com/v1/projects/${projectId}/locations/${location}/publishers/google/models/${modelId}:predictLongRunning`;
170
159
 
@@ -242,7 +231,7 @@ export const imageToVideo = {
242
231
 
243
232
  const parameters: any = {
244
233
  aspectRatio: args.aspect_ratio || "9:16",
245
- durationSeconds: parseInt(args.duration_seconds || "6") || 6,
234
+ durationSeconds: durationSeconds,
246
235
  resolution: args.resolution || "720p",
247
236
  negativePrompt: args.negative_prompt,
248
237
  generateAudio: args.generate_audio || false,
@@ -302,14 +291,15 @@ export const imageToVideo = {
302
291
  [];
303
292
  const saveVideo = async (base64: string, index: number) => {
304
293
  if (!base64) return;
305
-
294
+
306
295
  // Use provided output path or generate default with timestamp
307
296
  let filePath: string;
308
297
  if (args.output_path) {
309
298
  // User provided path - use as-is for first video, add index for subsequent
310
- filePath = index === 0
311
- ? args.output_path
312
- : args.output_path.replace(/\.mp4$/i, `_${index}.mp4`);
299
+ filePath =
300
+ index === 0
301
+ ? args.output_path
302
+ : args.output_path.replace(/\.mp4$/i, `_${index}.mp4`);
313
303
  } else {
314
304
  // No path provided - generate timestamped default
315
305
  const defaultName = `video_output${index > 0 ? `_${index}` : ""}.mp4`;
@@ -0,0 +1,83 @@
1
+ import { GoogleAuth } from "google-auth-library";
2
+ import { exec } from "child_process";
3
+
4
+ export interface GoogleAuthOptions {
5
+ scopes?: string | string[];
6
+ }
7
+
8
+ /**
9
+ * Returns a GoogleAuth client, prioritizing GOOGLE_SERVICE_ACCOUNT_JSON env var.
10
+ */
11
+ export function getGoogleAuthClient(
12
+ options: GoogleAuthOptions = {}
13
+ ): GoogleAuth {
14
+ const opts: any = {
15
+ scopes: options.scopes || [
16
+ "https://www.googleapis.com/auth/cloud-platform",
17
+ ],
18
+ };
19
+
20
+ if (process.env.GOOGLE_SERVICE_ACCOUNT_JSON) {
21
+ try {
22
+ const credentials = JSON.parse(process.env.GOOGLE_SERVICE_ACCOUNT_JSON);
23
+ opts.credentials = credentials;
24
+ if (credentials.project_id) {
25
+ opts.projectId = credentials.project_id;
26
+ }
27
+ } catch (error) {
28
+ console.warn(
29
+ "Failed to parse GOOGLE_SERVICE_ACCOUNT_JSON, falling back to other methods:",
30
+ error
31
+ );
32
+ }
33
+ } else if (process.env.GOOGLE_SERVICE_ACCOUNT_JSON_PATH) {
34
+ // GoogleAuth natively supports keyFile/keyFilename
35
+ opts.keyFile = process.env.GOOGLE_SERVICE_ACCOUNT_JSON_PATH;
36
+ }
37
+
38
+ return new GoogleAuth(opts);
39
+ }
40
+
41
+ /**
42
+ * Returns an access token, trying GoogleAuth first (JSON > ADC), then gcloud CLI.
43
+ */
44
+ export async function getGoogleAccessToken(
45
+ options: GoogleAuthOptions = {}
46
+ ): Promise<string> {
47
+ try {
48
+ const auth = getGoogleAuthClient(options);
49
+ const client = await auth.getClient();
50
+ const token = await client.getAccessToken();
51
+ if (token.token) {
52
+ return token.token;
53
+ }
54
+ throw new Error("No token returned from GoogleAuth");
55
+ } catch (e) {
56
+ // Fallback to gcloud
57
+ console.warn("GoogleAuth failed, falling back to gcloud CLI:", e);
58
+ return await new Promise((resolve, reject) => {
59
+ exec("gcloud auth print-access-token", (err, stdout, stderr) => {
60
+ if (err) {
61
+ reject(
62
+ new Error(
63
+ `Failed to fetch an access token (Auth Library and gcloud): ${
64
+ stderr || err.message
65
+ }`
66
+ )
67
+ );
68
+ return;
69
+ }
70
+ const t = (stdout || "").trim();
71
+ if (!t) {
72
+ reject(
73
+ new Error(
74
+ "Failed to fetch an access token: empty token from gcloud"
75
+ )
76
+ );
77
+ return;
78
+ }
79
+ resolve(t);
80
+ });
81
+ });
82
+ }
83
+ }