@mixio-pro/kalaasetu-mcp 2.0.1-beta → 2.0.2-beta
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 +1 -1
- package/src/index.ts +5 -0
- package/src/tools/fal/generate.ts +2 -2
- package/src/tools/get-status.ts +174 -0
- package/src/tools/image-to-video.ts +2 -2
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -36,11 +36,16 @@ server.addTool(imageToVideo);
|
|
|
36
36
|
// server.addTool(perplexityImages);
|
|
37
37
|
// server.addTool(perplexityVideos);
|
|
38
38
|
|
|
39
|
+
import { getGenerationStatus } from "./tools/get-status";
|
|
40
|
+
|
|
39
41
|
// Fal AI Tools
|
|
40
42
|
server.addTool(falListPresets);
|
|
41
43
|
server.addTool(falGenerate);
|
|
42
44
|
server.addTool(falUploadFile);
|
|
43
45
|
|
|
46
|
+
// Unified Status Tool (works with both FAL and Vertex AI)
|
|
47
|
+
server.addTool(getGenerationStatus);
|
|
48
|
+
|
|
44
49
|
server.start({
|
|
45
50
|
transportType: "stdio",
|
|
46
51
|
});
|
|
@@ -119,7 +119,7 @@ export const falGenerate = {
|
|
|
119
119
|
"Use the 'request_id' returned in an 'IN_PROGRESS' response or after a timeout error."
|
|
120
120
|
),
|
|
121
121
|
}),
|
|
122
|
-
timeoutMs:
|
|
122
|
+
timeoutMs: 90000, // 90 seconds MCP timeout (internal timeout is 60s)
|
|
123
123
|
execute: async (
|
|
124
124
|
args: {
|
|
125
125
|
preset_name?: string;
|
|
@@ -362,7 +362,7 @@ export const falGenerate = {
|
|
|
362
362
|
}
|
|
363
363
|
|
|
364
364
|
const startTime = Date.now();
|
|
365
|
-
const MAX_POLL_TIME =
|
|
365
|
+
const MAX_POLL_TIME = 60000; // 60 seconds internal timeout - then return resume_id
|
|
366
366
|
let pollCount = 0;
|
|
367
367
|
const POLL_INTERVAL = 3000;
|
|
368
368
|
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified status checking tool for FAL and Vertex AI generation operations.
|
|
3
|
+
* This tool allows resuming/checking status of operations that timed out.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { safeToolExecute } from "../utils/tool-wrapper";
|
|
8
|
+
import { getGoogleAccessToken } from "../utils/google-auth";
|
|
9
|
+
|
|
10
|
+
const FAL_KEY = process.env.FAL_KEY;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Check FAL generation status using the status URL
|
|
14
|
+
*/
|
|
15
|
+
async function checkFalStatus(statusUrl: string): Promise<any> {
|
|
16
|
+
if (!FAL_KEY) {
|
|
17
|
+
throw new Error("FAL_KEY environment variable not set");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const response = await fetch(statusUrl, {
|
|
21
|
+
method: "GET",
|
|
22
|
+
headers: {
|
|
23
|
+
Authorization: `Key ${FAL_KEY}`,
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
const errorText = await response.text();
|
|
30
|
+
throw new Error(`FAL API error [${response.status}]: ${errorText}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const statusResult = (await response.json()) as { status?: string };
|
|
34
|
+
|
|
35
|
+
if (statusResult.status === "COMPLETED") {
|
|
36
|
+
// Fetch the actual result
|
|
37
|
+
const responseUrl = statusUrl.replace(/\/status$/, "");
|
|
38
|
+
const resultResponse = await fetch(responseUrl, {
|
|
39
|
+
method: "GET",
|
|
40
|
+
headers: {
|
|
41
|
+
Authorization: `Key ${FAL_KEY}`,
|
|
42
|
+
"Content-Type": "application/json",
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (resultResponse.ok) {
|
|
47
|
+
return await resultResponse.json();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return statusResult;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check Vertex AI operation status
|
|
56
|
+
*/
|
|
57
|
+
async function checkVertexStatus(
|
|
58
|
+
operationName: string,
|
|
59
|
+
projectId: string,
|
|
60
|
+
locationId: string
|
|
61
|
+
): Promise<any> {
|
|
62
|
+
const accessToken = await getGoogleAccessToken();
|
|
63
|
+
|
|
64
|
+
const operationsUrl = `https://${locationId}-aiplatform.googleapis.com/v1/projects/${projectId}/locations/${locationId}/publishers/google/models/veo-3.1-generate-preview/operations/${operationName}`;
|
|
65
|
+
|
|
66
|
+
const response = await fetch(operationsUrl, {
|
|
67
|
+
method: "GET",
|
|
68
|
+
headers: {
|
|
69
|
+
Authorization: `Bearer ${accessToken}`,
|
|
70
|
+
"Content-Type": "application/json",
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
const errorText = await response.text();
|
|
76
|
+
throw new Error(`Vertex AI API error [${response.status}]: ${errorText}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return await response.json();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const getGenerationStatus = {
|
|
83
|
+
name: "get_generation_status",
|
|
84
|
+
description:
|
|
85
|
+
"Check the status or retrieve the result of a generation operation that was started by 'fal_generate' or 'generateVideoi2v'. " +
|
|
86
|
+
"Use this when the original generation tool returned an 'IN_PROGRESS' status with a 'resume_id'. " +
|
|
87
|
+
"Pass the resume_id exactly as it was returned. " +
|
|
88
|
+
"For FAL operations, the resume_id is a full URL. " +
|
|
89
|
+
"For Vertex AI operations, the resume_id is an operation name.",
|
|
90
|
+
parameters: z.object({
|
|
91
|
+
resume_id: z
|
|
92
|
+
.string()
|
|
93
|
+
.describe(
|
|
94
|
+
"The resume_id returned by the original generation tool. " +
|
|
95
|
+
"For FAL: This is a full URL (starts with 'https://queue.fal.run/...'). " +
|
|
96
|
+
"For Vertex AI: This is an operation name."
|
|
97
|
+
),
|
|
98
|
+
source: z
|
|
99
|
+
.enum(["fal", "vertex", "auto"])
|
|
100
|
+
.optional()
|
|
101
|
+
.default("auto")
|
|
102
|
+
.describe(
|
|
103
|
+
"Source of the operation: 'fal' for FAL AI, 'vertex' for Google Vertex AI, or 'auto' to auto-detect based on resume_id format."
|
|
104
|
+
),
|
|
105
|
+
project_id: z
|
|
106
|
+
.string()
|
|
107
|
+
.optional()
|
|
108
|
+
.default("mixio-pro")
|
|
109
|
+
.describe("GCP Project ID (only needed for Vertex AI operations)."),
|
|
110
|
+
location_id: z
|
|
111
|
+
.string()
|
|
112
|
+
.optional()
|
|
113
|
+
.default("us-central1")
|
|
114
|
+
.describe("GCP region (only needed for Vertex AI operations)."),
|
|
115
|
+
}),
|
|
116
|
+
timeoutMs: 30000, // 30 seconds for status check
|
|
117
|
+
execute: async (args: {
|
|
118
|
+
resume_id: string;
|
|
119
|
+
source?: "fal" | "vertex" | "auto";
|
|
120
|
+
project_id?: string;
|
|
121
|
+
location_id?: string;
|
|
122
|
+
}) => {
|
|
123
|
+
return safeToolExecute(async () => {
|
|
124
|
+
const {
|
|
125
|
+
resume_id,
|
|
126
|
+
source = "auto",
|
|
127
|
+
project_id = "mixio-pro",
|
|
128
|
+
location_id = "us-central1",
|
|
129
|
+
} = args;
|
|
130
|
+
|
|
131
|
+
// Auto-detect source based on resume_id format
|
|
132
|
+
let detectedSource = source;
|
|
133
|
+
if (source === "auto") {
|
|
134
|
+
if (
|
|
135
|
+
resume_id.startsWith("https://queue.fal.run") ||
|
|
136
|
+
resume_id.startsWith("https://fal.run")
|
|
137
|
+
) {
|
|
138
|
+
detectedSource = "fal";
|
|
139
|
+
} else {
|
|
140
|
+
detectedSource = "vertex";
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let result: any;
|
|
145
|
+
|
|
146
|
+
if (detectedSource === "fal") {
|
|
147
|
+
result = await checkFalStatus(resume_id);
|
|
148
|
+
} else {
|
|
149
|
+
result = await checkVertexStatus(resume_id, project_id, location_id);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Normalize the response
|
|
153
|
+
const status =
|
|
154
|
+
result.status || (result.done ? "COMPLETED" : "IN_PROGRESS");
|
|
155
|
+
|
|
156
|
+
return JSON.stringify(
|
|
157
|
+
{
|
|
158
|
+
source: detectedSource,
|
|
159
|
+
status,
|
|
160
|
+
resume_id,
|
|
161
|
+
result,
|
|
162
|
+
message:
|
|
163
|
+
status === "COMPLETED"
|
|
164
|
+
? "Generation completed! The result is included in the 'result' field."
|
|
165
|
+
: status === "FAILED"
|
|
166
|
+
? "Generation failed. Check the 'result' field for error details."
|
|
167
|
+
: "Generation is still in progress. Call this tool again with the same resume_id to check later.",
|
|
168
|
+
},
|
|
169
|
+
null,
|
|
170
|
+
2
|
|
171
|
+
);
|
|
172
|
+
}, "get_generation_status");
|
|
173
|
+
},
|
|
174
|
+
};
|
|
@@ -152,7 +152,7 @@ export const imageToVideo = {
|
|
|
152
152
|
"When using Veo, setting this to 'veo' (or setting auto_enhance=true) will trigger the LLM-based enhancer."
|
|
153
153
|
),
|
|
154
154
|
}),
|
|
155
|
-
timeoutMs:
|
|
155
|
+
timeoutMs: 90000, // 90 seconds MCP timeout (internal timeout is 60s)
|
|
156
156
|
async execute(
|
|
157
157
|
args: {
|
|
158
158
|
prompt?: string;
|
|
@@ -477,7 +477,7 @@ export const imageToVideo = {
|
|
|
477
477
|
// Resume_id was already streamed, so if MCP client times out the LLM still has it
|
|
478
478
|
let done = current ? !!current.done || !!current.response : false;
|
|
479
479
|
const startTime = Date.now();
|
|
480
|
-
const MAX_POLL_TIME =
|
|
480
|
+
const MAX_POLL_TIME = 60000; // 60 seconds internal timeout - then return resume_id
|
|
481
481
|
|
|
482
482
|
while (!done && Date.now() - startTime < MAX_POLL_TIME) {
|
|
483
483
|
await wait(10000); // 10 second intervals
|