@mixio-pro/kalaasetu-mcp 2.3.31 → 2.3.33

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/fal-config.json CHANGED
@@ -143,6 +143,40 @@
143
143
  "default": "replace_audio_and_video"
144
144
  }
145
145
  }
146
+ },
147
+ {
148
+ "presetName": "multi_angle",
149
+ "intent": "Generate a single multi-angle image edit using Qwen Image Edit with the Mixio multi-angle LoRA.",
150
+ "modelId": "fal-ai/qwen-image-edit-2509-lora",
151
+ "inputType": "image",
152
+ "outputType": "image",
153
+ "enabled": false,
154
+ "pricing": {
155
+ "baseCredits": 15,
156
+ "rounding": "ceil"
157
+ }
158
+ },
159
+ {
160
+ "presetName": "multi_angle_batch",
161
+ "intent": "Generate multiple multi-angle image edits using Qwen Image Edit with the Mixio multi-angle LoRA.",
162
+ "modelId": "fal-ai/qwen-image-edit-2509-lora",
163
+ "inputType": "image",
164
+ "outputType": "image",
165
+ "enabled": false,
166
+ "pricing": {
167
+ "baseCredits": 15,
168
+ "modifiers": [
169
+ {
170
+ "field": "batch_count",
171
+ "mode": "step",
172
+ "operation": "add",
173
+ "step": 1,
174
+ "offset": 1,
175
+ "valuePerStep": 15
176
+ }
177
+ ],
178
+ "rounding": "ceil"
179
+ }
146
180
  }
147
181
  ]
148
182
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mixio-pro/kalaasetu-mcp",
3
- "version": "2.3.31",
3
+ "version": "2.3.33",
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",
package/src/index.ts CHANGED
@@ -100,6 +100,8 @@ async function main() {
100
100
  server.addTool(falGetPresetDetails);
101
101
  server.addTool(falUploadFile);
102
102
  server.addTool(falGenerate);
103
+ server.addTool(mixioMultiAngle);
104
+ server.addTool(mixioMultiAngleBatch);
103
105
  // 4. Register Dynamic FAL AI Tools
104
106
  // These are now based on potentially synced remote config
105
107
  const falTools = createAllFalTools();
@@ -109,8 +111,6 @@ async function main() {
109
111
  }
110
112
 
111
113
  // 4. Add Mixio Tools
112
- // server.addTool(mixioMultiAngle);
113
- // server.addTool(mixioMultiAngleBatch);
114
114
  // server.addTool(mixioNextScene);
115
115
  // server.addTool(mixioFaceSwap);
116
116
  if (process.env.GROK_ENABLED === "true") {
@@ -10,6 +10,7 @@ import { getGoogleAccessToken } from "../utils/google-auth";
10
10
  import { sanitizeResponse } from "../utils/sanitize";
11
11
  import { getStorage } from "../storage";
12
12
  import * as path from "path";
13
+ import { parseVertexEndpoint, buildVertexCompositeEndpoint } from "../utils/vertex-endpoint";
13
14
 
14
15
  function getFalKey(): string {
15
16
  const falKey = process.env.FAL_KEY;
@@ -352,9 +353,12 @@ export const getGenerationStatus = {
352
353
  if (resume_endpoint.includes("||")) {
353
354
  const parts = resume_endpoint.split("||");
354
355
 
355
- // For FAL jobs starting with https://
356
- if (parts?.[0]?.startsWith("https://")) {
357
- originalEndpoint = parts[0];
356
+ // Domain-specific detection
357
+ const isFal = parts?.[0]?.includes("fal.run");
358
+ const isVertex = parts?.[0]?.includes("googleapis.com") || parts?.[1]?.includes("v1/projects/");
359
+
360
+ if (isFal) {
361
+ originalEndpoint = parts[0]!;
358
362
  // Part 1 is tracking context (base64)
359
363
  if (parts.length >= 2 && parts[1]) {
360
364
  try {
@@ -370,27 +374,40 @@ export const getGenerationStatus = {
370
374
  if (parts.length >= 3 && parts[2] && !outputPath) {
371
375
  outputPath = parts[2];
372
376
  }
377
+ } else if (isVertex) {
378
+ // New robust Vertex parsing
379
+ // For get_generation_status, we don't necessarily know the modelId here,
380
+ // but parseVertexEndpoint uses it mainly for constructing the default op name.
381
+ // Since we HAVE a composite endpoint, the modelId passed to parseVertexEndpoint won't be used for the op name.
382
+ const parsed = parseVertexEndpoint(resume_endpoint, "veo-3.1-lite-generate-001");
383
+ originalEndpoint = buildVertexCompositeEndpoint(parsed.fetchUrl, parsed.operationName);
384
+ outputPath = args.output_path || parsed.outputPath || outputPath;
385
+
386
+ if (parsed.trackingContext) {
387
+ try {
388
+ const { decodeTrackingContext } = await import("../utils/endpoint-encoder");
389
+ trackingContext = decodeTrackingContext(parsed.trackingContext);
390
+ } catch (err) {}
391
+ }
373
392
  } else {
374
- // Vertex logic (legacy/other)
393
+ // Legacy/Fallback Vertex logic
375
394
  const lastPart = parts[parts.length - 1];
376
395
  if (
377
396
  lastPart &&
378
397
  !lastPart.startsWith("http") &&
379
398
  !lastPart.includes("/") &&
380
- !lastPart.includes("mixio-pro") // Ensure it doesn't look like an operation path or URL
399
+ !lastPart.includes("mixio-pro")
381
400
  ) {
382
401
  try {
383
402
  const { decodeTrackingContext } =
384
403
  await import("../utils/endpoint-encoder");
385
404
  trackingContext = decodeTrackingContext(lastPart);
386
405
  if (trackingContext?.toolName) {
387
- // Remove the tracking piece from the endpoint we pass to handlers
388
406
  originalEndpoint = parts.slice(0, -1).join("||");
389
407
  }
390
408
  } catch (err) {}
391
409
  }
392
410
 
393
- // For Vertex, if outputPath is not explicitly provided, it might be in the 3rd part of originalEndpoint
394
411
  if (!outputPath) {
395
412
  const vertexParts = originalEndpoint.split("||");
396
413
  if (vertexParts.length >= 3) {
@@ -5,6 +5,7 @@ import { safeToolExecute, extractPrimitiveArgs } from "../utils/tool-wrapper";
5
5
 
6
6
  import { getGoogleAccessToken } from "../utils/google-auth";
7
7
  import { checkVertexStatus } from "./get-status";
8
+ import { parseVertexEndpoint, buildVertexCompositeEndpoint, buildVertexFetchUrl } from "../utils/vertex-endpoint";
8
9
 
9
10
  async function wait(ms: number): Promise<void> {
10
11
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -222,18 +223,17 @@ export const imageToVideo = {
222
223
  throw new Error(`Google Cloud authentication failed: ${errorMsg}`);
223
224
  }
224
225
 
225
- const fetchUrl = `https://${location}-aiplatform.googleapis.com/v1/projects/${projectId}/locations/${location}/publishers/google/models/${modelId}:fetchPredictOperation`;
226
-
227
- // If resuming, reconstruct the full operation path from the UUID
226
+ let fetchUrl = buildVertexFetchUrl(modelId, projectId, location);
228
227
  let operationName: string | undefined;
228
+ let outputPath = args.output_path || "";
229
+ let preservedTrackingContext: string | undefined;
230
+
229
231
  if (args.resume_endpoint) {
230
- // Support both UUID-only and full path formats
231
- if (args.resume_endpoint.includes("/")) {
232
- operationName = args.resume_endpoint; // Already a full path
233
- } else {
234
- // Reconstruct full path from UUID
235
- operationName = `projects/${projectId}/locations/${location}/publishers/google/models/${modelId}/operations/${args.resume_endpoint}`;
236
- }
232
+ const parsed = parseVertexEndpoint(args.resume_endpoint, modelId, projectId, location);
233
+ fetchUrl = parsed.fetchUrl;
234
+ operationName = parsed.operationName;
235
+ outputPath = parsed.outputPath || outputPath;
236
+ preservedTrackingContext = parsed.trackingContext;
237
237
  }
238
238
  let current: any;
239
239
 
@@ -367,8 +367,7 @@ export const imageToVideo = {
367
367
 
368
368
  // Construct the composite resume_endpoint: fetchUrl||operationName||outputPath
369
369
  // This allows get_generation_status to use the URL directly and preserve output_path
370
- const outputPathPart = args.output_path || "";
371
- const compositeResumeEndpoint = `${fetchUrl}||${operationName}||${outputPathPart}`;
370
+ const compositeResumeEndpoint = buildVertexCompositeEndpoint(fetchUrl, operationName, outputPath);
372
371
 
373
372
  // Stream the resume_endpoint to the LLM immediately (before polling starts)
374
373
  // This way the LLM has it even if MCP client times out during polling
@@ -428,7 +427,7 @@ export const imageToVideo = {
428
427
  return JSON.stringify({
429
428
  status: "IN_PROGRESS",
430
429
  request_id: operationName,
431
- resume_endpoint: `${compositeResumeEndpoint}||${packedTracking}`,
430
+ resume_endpoint: buildVertexCompositeEndpoint(fetchUrl, operationName, outputPath, packedTracking),
432
431
  message:
433
432
  "Still in progress. Call this tool again with resume_endpoint to continue checking.",
434
433
  });
@@ -6,6 +6,11 @@ import { getGoogleAccessToken } from "../utils/google-auth";
6
6
  import { ensureLocalFile } from "../utils/url-file";
7
7
  import { encodeTrackingContext } from "../utils/endpoint-encoder";
8
8
  import { checkVertexStatus } from "./get-status";
9
+ import {
10
+ parseVertexEndpoint,
11
+ buildVertexCompositeEndpoint,
12
+ buildVertexFetchUrl
13
+ } from "../utils/vertex-endpoint";
9
14
 
10
15
  const DEFAULT_PROJECT_ID = "mixio-pro";
11
16
  const DEFAULT_LOCATION = "us-central1";
@@ -24,12 +29,7 @@ type ToolContext = {
24
29
  requestId?: string;
25
30
  };
26
31
 
27
- interface ParsedResumeEndpoint {
28
- fetchUrl: string;
29
- operationName?: string;
30
- outputPath?: string;
31
- trackingContext?: string;
32
- }
32
+
33
33
 
34
34
  async function wait(ms: number): Promise<void> {
35
35
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -84,43 +84,7 @@ async function fileToBase64(
84
84
  }
85
85
  }
86
86
 
87
- function buildFetchUrl(modelId: string): string {
88
- return `https://${DEFAULT_LOCATION}-aiplatform.googleapis.com/v1/projects/${DEFAULT_PROJECT_ID}/locations/${DEFAULT_LOCATION}/publishers/google/models/${modelId}:fetchPredictOperation`;
89
- }
90
-
91
- function parseResumeEndpoint(
92
- resumeEndpoint: string,
93
- modelId: string,
94
- ): ParsedResumeEndpoint {
95
- const defaultFetchUrl = buildFetchUrl(modelId);
96
-
97
- if (resumeEndpoint.includes("||")) {
98
- const parts = resumeEndpoint.split("||");
99
- const fetchUrl = parts[0] || "";
100
- const operationName = parts[1] || "";
101
-
102
- if (fetchUrl.startsWith("http") && operationName) {
103
- return {
104
- fetchUrl,
105
- operationName,
106
- outputPath: parts[2] || "",
107
- trackingContext: parts.length > 3 ? parts.slice(3).join("||") : undefined,
108
- };
109
- }
110
- }
111
-
112
- if (resumeEndpoint.includes("/")) {
113
- return {
114
- fetchUrl: defaultFetchUrl,
115
- operationName: resumeEndpoint,
116
- };
117
- }
118
87
 
119
- return {
120
- fetchUrl: defaultFetchUrl,
121
- operationName: `projects/${DEFAULT_PROJECT_ID}/locations/${DEFAULT_LOCATION}/publishers/google/models/${modelId}/operations/${resumeEndpoint}`,
122
- };
123
- }
124
88
 
125
89
  function normalizeDurationSeconds(durationSecondsRaw?: string): number {
126
90
  const requestedDuration = parseInt(durationSecondsRaw || "6");
@@ -343,14 +307,14 @@ export const ingredientsToVideo = {
343
307
  throw new Error(`Google Cloud authentication failed: ${errorMessage}`);
344
308
  }
345
309
 
346
- let fetchUrl = buildFetchUrl(modelId);
310
+ let fetchUrl = buildVertexFetchUrl(modelId);
347
311
  let operationName: string | undefined;
348
312
  let outputPath = args.output_path || "";
349
313
  let preservedTrackingContext: string | undefined;
350
314
  let current: any;
351
315
 
352
316
  if (args.resume_endpoint) {
353
- const parsedResume = parseResumeEndpoint(args.resume_endpoint, modelId);
317
+ const parsedResume = parseVertexEndpoint(args.resume_endpoint, modelId);
354
318
  fetchUrl = parsedResume.fetchUrl;
355
319
  operationName = parsedResume.operationName;
356
320
  outputPath = parsedResume.outputPath || outputPath;
@@ -418,7 +382,7 @@ export const ingredientsToVideo = {
418
382
  );
419
383
  }
420
384
 
421
- const compositeResumeEndpoint = `${fetchUrl}||${operationName}||${outputPath}`;
385
+ const compositeResumeEndpoint = buildVertexCompositeEndpoint(fetchUrl, operationName, outputPath);
422
386
 
423
387
  if (context?.streamContent) {
424
388
  await context.streamContent({
@@ -471,7 +435,7 @@ export const ingredientsToVideo = {
471
435
  return JSON.stringify({
472
436
  status: "IN_PROGRESS",
473
437
  request_id: operationName,
474
- resume_endpoint: `${compositeResumeEndpoint}||${packedTracking}`,
438
+ resume_endpoint: buildVertexCompositeEndpoint(fetchUrl, operationName, outputPath, packedTracking),
475
439
  message:
476
440
  "Still in progress. Call this tool again with resume_endpoint to continue checking.",
477
441
  });