@mixio-pro/kalaasetu-mcp 2.1.0 → 2.1.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.
@@ -6,6 +6,7 @@ import {
6
6
  DEFAULT_TIMEOUT,
7
7
  } from "./config";
8
8
  import { safeToolExecute } from "../../utils/tool-wrapper";
9
+ import { logger } from "../../utils/logger";
9
10
 
10
11
  /**
11
12
  * Extract simplified input schema from FAL OpenAPI response.
@@ -20,7 +21,7 @@ function extractInputSchema(openApiSchema: any): Record<string, any> | null {
20
21
  const inputSchemaKey = Object.keys(schemas).find(
21
22
  (key) =>
22
23
  key.toLowerCase().includes("input") &&
23
- !key.toLowerCase().includes("output")
24
+ !key.toLowerCase().includes("output"),
24
25
  );
25
26
 
26
27
  if (!inputSchemaKey) return null;
@@ -31,7 +32,7 @@ function extractInputSchema(openApiSchema: any): Record<string, any> | null {
31
32
  // Extract simplified properties
32
33
  const simplified: Record<string, any> = {};
33
34
  for (const [propName, propDef] of Object.entries(
34
- inputSchema.properties as Record<string, any>
35
+ inputSchema.properties as Record<string, any>,
35
36
  )) {
36
37
  simplified[propName] = {
37
38
  type: propDef.type,
@@ -66,64 +67,81 @@ export const falListPresets = {
66
67
  }),
67
68
  timeoutMs: 60000, // Allow more time for schema fetching
68
69
  execute: async (args: { refresh_schemas?: boolean }) => {
69
- return safeToolExecute(async () => {
70
- const config = loadFalConfig();
71
- let updated = false;
72
-
73
- // Fetch schemas for presets that don't have them (or if refresh requested)
74
- for (const preset of config.presets) {
75
- const hasSchema =
76
- preset.input_schema && Object.keys(preset.input_schema).length > 0;
77
- const shouldFetch = !hasSchema || args.refresh_schemas;
78
-
79
-
80
- if (shouldFetch) {
81
- try {
82
- const url = `https://fal.ai/api/openapi/queue/openapi.json?endpoint_id=${preset.modelId}`;
83
-
84
- const response = await fetch(url, {
85
- method: "GET",
86
- signal: AbortSignal.timeout(10000),
87
- });
88
-
89
- if (response.ok) {
90
- const openApiSchema = await response.json();
91
- const simplified = extractInputSchema(openApiSchema);
92
-
93
-
94
- if (simplified) {
95
- preset.input_schema = simplified;
96
- updated = true;
70
+ return safeToolExecute(
71
+ async () => {
72
+ const config = loadFalConfig();
73
+ let updated = false;
74
+
75
+ // Fetch schemas for presets that don't have them (or if refresh requested)
76
+ for (const preset of config.presets) {
77
+ const hasSchema =
78
+ preset.input_schema && Object.keys(preset.input_schema).length > 0;
79
+ const shouldFetch = !hasSchema || args.refresh_schemas;
80
+ logger.debug(
81
+ `[fal_list_presets] ${
82
+ preset.presetName
83
+ }: shouldFetch=${shouldFetch}, hasSchema=${!!preset.input_schema}, refresh=${
84
+ args.refresh_schemas
85
+ }`,
86
+ );
87
+
88
+ if (shouldFetch) {
89
+ try {
90
+ const url = `https://fal.ai/api/openapi/queue/openapi.json?endpoint_id=${preset.modelId}`;
91
+ logger.debug(`[fal_list_presets] Fetching schema from: ${url}`);
92
+ const response = await fetch(url, {
93
+ method: "GET",
94
+ signal: AbortSignal.timeout(10000),
95
+ });
96
+
97
+ if (response.ok) {
98
+ const openApiSchema = await response.json();
99
+ const simplified = extractInputSchema(openApiSchema);
100
+ logger.debug(
101
+ `[fal_list_presets] Extracted schema for ${preset.presetName}:`,
102
+ simplified ? Object.keys(simplified) : null,
103
+ );
104
+
105
+ if (simplified) {
106
+ preset.input_schema = simplified;
107
+ updated = true;
108
+ }
109
+ } else {
110
+ logger.warn(
111
+ `[fal_list_presets] Fetch failed: ${response.status}`,
112
+ );
97
113
  }
98
- } else {
99
-
114
+ } catch (e: any) {
115
+ logger.warn(
116
+ `[fal_list_presets] Error fetching schema for ${preset.presetName}:`,
117
+ e.message,
118
+ );
100
119
  }
101
- } catch (e: any) {
102
- console.error(
103
- `[fal_list_presets] Error fetching schema for ${preset.presetName}:`,
104
- e.message
105
- );
106
120
  }
107
121
  }
108
- }
109
122
 
110
- // Save updated config if schemas were fetched
111
- if (updated) {
112
- saveFalConfig(config);
113
- }
114
-
115
- // Return enriched preset list
116
- const summary = config.presets.map((p) => ({
117
- presetName: p.presetName,
118
- intent: p.intent,
119
- inputType: p.inputType,
120
- outputType: p.outputType,
121
- description: p.description,
122
- inputSchema: p.input_schema,
123
- }));
123
+ // Save updated config if schemas were fetched
124
+ if (updated) {
125
+ logger.info(`[fal_list_presets] Saving updated config...`);
126
+ const saved = saveFalConfig(config);
127
+ logger.info(`[fal_list_presets] Config saved: ${saved}`);
128
+ }
124
129
 
125
- return JSON.stringify(summary, null, 2);
126
- }, "fal_list_presets");
130
+ // Return enriched preset list
131
+ const summary = config.presets.map((p) => ({
132
+ presetName: p.presetName,
133
+ intent: p.intent,
134
+ inputType: p.inputType,
135
+ outputType: p.outputType,
136
+ description: p.description,
137
+ inputSchema: p.input_schema,
138
+ }));
139
+
140
+ return JSON.stringify(summary, null, 2);
141
+ },
142
+ "fal_list_presets",
143
+ { toolName: "fal_list_presets" },
144
+ );
127
145
  },
128
146
  };
129
147
 
@@ -146,52 +164,56 @@ export const falGetPresetDetails = {
146
164
  }),
147
165
  timeoutMs: 30000,
148
166
  execute: async (args: { preset_name: string }) => {
149
- return safeToolExecute(async () => {
150
- const config = loadFalConfig();
151
- const preset = config.presets.find(
152
- (p) => p.presetName === args.preset_name
153
- );
154
- if (!preset) {
155
- throw new Error(`Preset '${args.preset_name}' not found.`);
156
- }
167
+ return safeToolExecute(
168
+ async () => {
169
+ const config = loadFalConfig();
170
+ const preset = config.presets.find(
171
+ (p) => p.presetName === args.preset_name,
172
+ );
173
+ if (!preset) {
174
+ throw new Error(`Preset '${args.preset_name}' not found.`);
175
+ }
157
176
 
158
- // Fetch live model metadata/schema from fal.ai API
159
- // Based on: https://fal.ai/api/openapi/queue/openapi.json?endpoint_id={model_id}
160
- let modelMetadata: any = null;
161
- let schemaSource = "none";
177
+ // Fetch live model metadata/schema from fal.ai API
178
+ // Based on: https://fal.ai/api/openapi/queue/openapi.json?endpoint_id={model_id}
179
+ let modelMetadata: any = null;
180
+ let schemaSource = "none";
162
181
 
163
- try {
164
- const url = `https://fal.ai/api/openapi/queue/openapi.json?endpoint_id=${preset.modelId}`;
165
- const schema = await publicRequest(url);
182
+ try {
183
+ const url = `https://fal.ai/api/openapi/queue/openapi.json?endpoint_id=${preset.modelId}`;
184
+ const schema = await publicRequest(url);
166
185
 
167
- // Extract relevant schema parts if possible, or return full schema
168
- if (schema) {
169
- modelMetadata = schema;
170
- schemaSource = "api";
186
+ // Extract relevant schema parts if possible, or return full schema
187
+ if (schema) {
188
+ modelMetadata = schema;
189
+ schemaSource = "api";
190
+ }
191
+ } catch (e) {
192
+ // console.error(`Failed to fetch schema for ${preset.modelId}:`, e);
193
+ // Fallback to locally defined schema if available
171
194
  }
172
- } catch (e) {
173
- // console.error(`Failed to fetch schema for ${preset.modelId}:`, e);
174
- // Fallback to locally defined schema if available
175
- }
176
195
 
177
- // Fallback: If API failed or returned nothing, use manual schema from config
178
- if (!modelMetadata && preset.input_schema) {
179
- modelMetadata = preset.input_schema;
180
- schemaSource = "local_config";
181
- }
196
+ // Fallback: If API failed or returned nothing, use manual schema from config
197
+ if (!modelMetadata && preset.input_schema) {
198
+ modelMetadata = preset.input_schema;
199
+ schemaSource = "local_config";
200
+ }
182
201
 
183
- return JSON.stringify(
184
- {
185
- preset,
186
- modelMetadata,
187
- _meta: {
188
- schemaSource,
202
+ return JSON.stringify(
203
+ {
204
+ preset,
205
+ modelMetadata,
206
+ _meta: {
207
+ schemaSource,
208
+ },
189
209
  },
190
- },
191
- null,
192
- 2
193
- );
194
- }, "fal_get_preset_details");
210
+ null,
211
+ 2,
212
+ );
213
+ },
214
+ "fal_get_preset_details",
215
+ { toolName: "fal_get_preset_details" },
216
+ );
195
217
  },
196
218
  };
197
219
 
@@ -8,6 +8,7 @@ import * as fs from "fs";
8
8
  import { safeToolExecute } from "../../utils/tool-wrapper";
9
9
  import * as path from "path";
10
10
  import { FAL_REST_URL, AUTHENTICATED_TIMEOUT, getApiKey } from "./config";
11
+ import { logger } from "../../utils/logger";
11
12
 
12
13
  /**
13
14
  * Get MIME type from file extension.
@@ -46,82 +47,86 @@ export const falUploadFile = {
46
47
  path: z
47
48
  .string()
48
49
  .describe(
49
- "The absolute local path to the file to upload (e.g., '/Users/name/images/input.jpg')."
50
+ "The absolute local path to the file to upload (e.g., '/Users/name/images/input.jpg').",
50
51
  ),
51
52
  }),
52
53
  timeoutMs: 300000,
53
54
  execute: async (args: { path: string }) => {
54
- return safeToolExecute(async () => {
55
- // Validate file exists
56
- if (!fs.existsSync(args.path)) {
57
- throw new Error(`File not found: ${args.path}`);
58
- }
55
+ return safeToolExecute(
56
+ async () => {
57
+ // Validate file exists
58
+ if (!fs.existsSync(args.path)) {
59
+ throw new Error(`File not found: ${args.path}`);
60
+ }
59
61
 
60
- const filename = path.basename(args.path);
61
- const fileSize = fs.statSync(args.path).size;
62
- const contentType = getMimeType(args.path);
62
+ const filename = path.basename(args.path);
63
+ const fileSize = fs.statSync(args.path).size;
64
+ const contentType = getMimeType(args.path);
63
65
 
64
- // Step 1: Initiate upload
65
- const initiateUrl = `${FAL_REST_URL}/storage/upload/initiate?storage_type=fal-cdn-v3`;
66
- const initiatePayload = {
67
- content_type: contentType,
68
- file_name: filename,
69
- };
66
+ // Step 1: Initiate upload
67
+ const initiateUrl = `${FAL_REST_URL}/storage/upload/initiate?storage_type=fal-cdn-v3`;
68
+ const initiatePayload = {
69
+ content_type: contentType,
70
+ file_name: filename,
71
+ };
70
72
 
71
- console.error(`[fal_upload] Initiating upload for ${filename}...`);
73
+ logger.debug(`[fal_upload] Initiating upload for ${filename}...`);
72
74
 
73
- const initiateResponse = await fetch(initiateUrl, {
74
- method: "POST",
75
- headers: {
76
- Authorization: `Key ${getApiKey()}`,
77
- "Content-Type": "application/json",
78
- },
79
- body: JSON.stringify(initiatePayload),
80
- signal: AbortSignal.timeout(AUTHENTICATED_TIMEOUT),
81
- });
75
+ const initiateResponse = await fetch(initiateUrl, {
76
+ method: "POST",
77
+ headers: {
78
+ Authorization: `Key ${getApiKey()}`,
79
+ "Content-Type": "application/json",
80
+ },
81
+ body: JSON.stringify(initiatePayload),
82
+ signal: AbortSignal.timeout(AUTHENTICATED_TIMEOUT),
83
+ });
82
84
 
83
- if (!initiateResponse.ok) {
84
- const errorText = await initiateResponse.text();
85
- throw new Error(
86
- `[${initiateResponse.status}] Failed to initiate upload: ${errorText}`
87
- );
88
- }
85
+ if (!initiateResponse.ok) {
86
+ const errorText = await initiateResponse.text();
87
+ throw new Error(
88
+ `[${initiateResponse.status}] Failed to initiate upload: ${errorText}`,
89
+ );
90
+ }
89
91
 
90
- const initiateData = (await initiateResponse.json()) as {
91
- file_url: string;
92
- upload_url: string;
93
- };
94
- const { file_url, upload_url } = initiateData;
92
+ const initiateData = (await initiateResponse.json()) as {
93
+ file_url: string;
94
+ upload_url: string;
95
+ };
96
+ const { file_url, upload_url } = initiateData;
95
97
 
96
- // Step 2: Upload file content
97
- console.error(`[fal_upload] Uploading file content...`);
98
+ // Step 2: Upload file content
99
+ logger.debug(`[fal_upload] Uploading file content...`);
98
100
 
99
- const fileContent = fs.readFileSync(args.path);
101
+ const fileContent = fs.readFileSync(args.path);
100
102
 
101
- const uploadResponse = await fetch(upload_url, {
102
- method: "PUT",
103
- headers: {
104
- "Content-Type": contentType,
105
- },
106
- body: fileContent,
107
- signal: AbortSignal.timeout(AUTHENTICATED_TIMEOUT),
108
- });
103
+ const uploadResponse = await fetch(upload_url, {
104
+ method: "PUT",
105
+ headers: {
106
+ "Content-Type": contentType,
107
+ },
108
+ body: fileContent,
109
+ signal: AbortSignal.timeout(AUTHENTICATED_TIMEOUT),
110
+ });
109
111
 
110
- if (!uploadResponse.ok) {
111
- const errorText = await uploadResponse.text();
112
- throw new Error(
113
- `[${uploadResponse.status}] Failed to upload file: ${errorText}`
114
- );
115
- }
112
+ if (!uploadResponse.ok) {
113
+ const errorText = await uploadResponse.text();
114
+ throw new Error(
115
+ `[${uploadResponse.status}] Failed to upload file: ${errorText}`,
116
+ );
117
+ }
116
118
 
117
- console.error(`[fal_upload] Upload completed successfully`);
119
+ logger.info(`[fal_upload] Upload completed successfully`);
118
120
 
119
- return JSON.stringify({
120
- file_url,
121
- file_name: filename,
122
- file_size: fileSize,
123
- content_type: contentType,
124
- });
125
- }, "fal_upload_file");
121
+ return JSON.stringify({
122
+ file_url,
123
+ file_name: filename,
124
+ file_size: fileSize,
125
+ content_type: contentType,
126
+ });
127
+ },
128
+ "fal_upload_file",
129
+ { toolName: "fal_upload_file" },
130
+ );
126
131
  },
127
132
  };