@insforge/mcp 1.2.6-memory.2 → 1.2.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.
@@ -1,29 +1,37 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // src/shared/tools.ts
4
- import { z as z23 } from "zod";
5
- import fetch2 from "node-fetch";
6
- import { promises as fs } from "fs";
7
- import { exec } from "child_process";
8
- import { promisify } from "util";
9
- import { tmpdir } from "os";
10
- import archiver from "archiver";
3
+ // src/shared/tools/index.ts
4
+ import fetch7 from "node-fetch";
11
5
 
12
6
  // src/shared/response-handler.ts
7
+ function isErrorResponse(value) {
8
+ if (typeof value !== "object" || value === null) return false;
9
+ const v = value;
10
+ const hasMessage = typeof v.message === "string";
11
+ const hasError = typeof v.error === "string";
12
+ const hasStatusCode = typeof v.statusCode === "number";
13
+ const hasNextAction = !("nextAction" in v) || typeof v.nextAction === "string";
14
+ return (hasMessage || hasError || hasStatusCode) && hasNextAction;
15
+ }
13
16
  async function handleApiResponse(response) {
14
17
  const responseData = await response.json();
15
18
  if (!response.ok) {
16
- const errorData = responseData;
17
- let fullMessage = errorData.message || errorData.error || "Unknown error";
18
- if (errorData.nextAction) {
19
- fullMessage += `. ${errorData.nextAction}`;
19
+ if (isErrorResponse(responseData)) {
20
+ let fullMessage = responseData.message ?? responseData.error ?? "Unknown error";
21
+ if (typeof responseData.nextAction === "string" && responseData.nextAction.length > 0) {
22
+ fullMessage += `. ${responseData.nextAction}`;
23
+ }
24
+ throw new Error(fullMessage);
20
25
  }
21
- throw new Error(fullMessage);
26
+ throw new Error("Unknown error");
22
27
  }
23
28
  return responseData;
24
29
  }
30
+ function hasMessageProperty(value) {
31
+ return typeof value === "object" && value !== null && "message" in value && typeof value.message === "string";
32
+ }
25
33
  function formatSuccessMessage(operation, data) {
26
- if (data && typeof data === "object" && "message" in data) {
34
+ if (hasMessageProperty(data)) {
27
35
  return `${data.message}
28
36
  ${JSON.stringify(data, null, 2)}`;
29
37
  }
@@ -64,6 +72,10 @@ var UsageTracker = class {
64
72
  }
65
73
  };
66
74
 
75
+ // src/shared/tools/docs.ts
76
+ import { z as z25 } from "zod";
77
+ import fetch2 from "node-fetch";
78
+
67
79
  // node_modules/@insforge/shared-schemas/dist/database.schema.js
68
80
  import { z } from "zod";
69
81
  var ColumnType;
@@ -382,6 +394,12 @@ var storageBucketSchema = z5.object({
382
394
  public: z5.boolean(),
383
395
  createdAt: z5.string()
384
396
  });
397
+ var storageConfigSchema = z5.object({
398
+ id: z5.string().uuid(),
399
+ maxFileSizeMb: z5.number().int().positive(),
400
+ createdAt: z5.string(),
401
+ updatedAt: z5.string()
402
+ });
385
403
 
386
404
  // node_modules/@insforge/shared-schemas/dist/storage-api.schema.js
387
405
  import { z as z6 } from "zod";
@@ -428,6 +446,9 @@ var confirmUploadRequestSchema = z6.object({
428
446
  contentType: z6.string().optional(),
429
447
  etag: z6.string().optional()
430
448
  });
449
+ var updateStorageConfigRequestSchema = z6.object({
450
+ maxFileSizeMb: z6.number().int().min(1, "Must be at least 1 MB").max(200, "Must be at most 200 MB")
451
+ });
431
452
 
432
453
  // node_modules/@insforge/shared-schemas/dist/auth.schema.js
433
454
  import { z as z7 } from "zod";
@@ -485,6 +506,7 @@ var oAuthConfigSchema = z7.object({
485
506
  updatedAt: z7.string()
486
507
  // PostgreSQL timestamp
487
508
  });
509
+ var allowedRedirectUrlsRegex = /^(?:(?:https?:\/\/)(?:(?:\*\.)?[^\s/:?#]+|\[[0-9A-Fa-f:.]+\])(?::\d+)?(?:\/[^\s]*)?|(?!(?:https?|javascript|data|file|vbscript):)[a-zA-Z][a-zA-Z0-9+.-]*:(?:\/\/[^\s/]+(?:\/[^\s]*)?|\/[^\s]*))$/i;
488
510
  var authConfigSchema = z7.object({
489
511
  id: z7.string().uuid(),
490
512
  requireEmailVerification: z7.boolean(),
@@ -495,7 +517,7 @@ var authConfigSchema = z7.object({
495
517
  requireSpecialChar: z7.boolean(),
496
518
  verifyEmailMethod: verificationMethodSchema,
497
519
  resetPasswordMethod: verificationMethodSchema,
498
- signInRedirectTo: z7.union([z7.string().url(), z7.literal(""), z7.null()]).optional().transform((val) => val === "" ? null : val),
520
+ allowedRedirectUrls: z7.array(z7.string().regex(allowedRedirectUrlsRegex, { message: "Invalid URL or wildcard URL" })).optional().nullable(),
499
521
  createdAt: z7.string(),
500
522
  // PostgreSQL timestamp
501
523
  updatedAt: z7.string()
@@ -511,6 +533,16 @@ var tokenPayloadSchema = z7.object({
511
533
  exp: z7.number().optional()
512
534
  // Expiration
513
535
  });
536
+ var customOAuthKeySchema = z7.string().min(1).max(64).regex(/^[a-z0-9_-]+$/, "Key must contain only lowercase letters, numbers, hyphens, and underscores");
537
+ var customOAuthConfigSchema = z7.object({
538
+ id: z7.string().uuid(),
539
+ key: customOAuthKeySchema,
540
+ name: z7.string().min(1),
541
+ discoveryEndpoint: z7.string().url(),
542
+ clientId: z7.string().min(1),
543
+ createdAt: z7.string(),
544
+ updatedAt: z7.string()
545
+ });
514
546
 
515
547
  // node_modules/@insforge/shared-schemas/dist/auth-api.schema.js
516
548
  import { z as z8 } from "zod";
@@ -521,12 +553,16 @@ var paginationSchema = z8.object({
521
553
  var createUserRequestSchema = z8.object({
522
554
  email: emailSchema,
523
555
  password: passwordSchema,
524
- name: nameSchema.optional()
556
+ name: nameSchema.optional(),
557
+ redirectTo: z8.string().url().optional()
525
558
  });
526
559
  var createSessionRequestSchema = z8.object({
527
560
  email: emailSchema,
528
561
  password: passwordSchema
529
562
  });
563
+ var refreshSessionRequestSchema = z8.object({
564
+ refreshToken: z8.string().min(1, "refreshToken is required")
565
+ });
530
566
  var exchangeAdminSessionRequestSchema = z8.object({
531
567
  code: z8.string()
532
568
  });
@@ -540,20 +576,20 @@ var updateProfileRequestSchema = z8.object({
540
576
  profile: z8.record(z8.unknown())
541
577
  });
542
578
  var sendVerificationEmailRequestSchema = z8.object({
543
- email: emailSchema
579
+ email: emailSchema,
580
+ redirectTo: z8.string().url().optional()
544
581
  });
545
582
  var verifyEmailRequestSchema = z8.object({
546
- email: emailSchema.optional(),
547
- otp: z8.string().min(1)
548
- }).refine((data) => data.email || data.otp, {
549
- message: "Either email or otp must be provided"
583
+ email: emailSchema,
584
+ otp: z8.string().regex(/^\d{6}$/, "OTP code must be a 6-digit numeric code")
550
585
  });
551
586
  var sendResetPasswordEmailRequestSchema = z8.object({
552
- email: emailSchema
587
+ email: emailSchema,
588
+ redirectTo: z8.string().url().optional()
553
589
  });
554
590
  var exchangeResetPasswordTokenRequestSchema = z8.object({
555
591
  email: emailSchema,
556
- code: z8.string().min(1)
592
+ code: z8.string().regex(/^\d{6}$/, "Reset password code must be a 6-digit numeric code")
557
593
  });
558
594
  var resetPasswordRequestSchema = z8.object({
559
595
  newPassword: passwordSchema,
@@ -563,25 +599,31 @@ var createUserResponseSchema = z8.object({
563
599
  user: userSchema.optional(),
564
600
  accessToken: z8.string().nullable(),
565
601
  requireEmailVerification: z8.boolean().optional(),
566
- redirectTo: z8.string().url().optional(),
567
- csrfToken: z8.string().nullable().optional()
602
+ csrfToken: z8.string().nullable().optional(),
603
+ refreshToken: z8.string().optional()
604
+ // For mobile/desktop clients (no cookies)
568
605
  });
569
606
  var createSessionResponseSchema = z8.object({
570
607
  user: userSchema,
571
608
  accessToken: z8.string(),
572
- redirectTo: z8.string().url().optional(),
573
- csrfToken: z8.string().nullable().optional()
609
+ csrfToken: z8.string().nullable().optional(),
610
+ refreshToken: z8.string().optional()
611
+ // For mobile/desktop clients (no cookies)
574
612
  });
575
613
  var verifyEmailResponseSchema = z8.object({
576
614
  user: userSchema,
577
615
  accessToken: z8.string(),
578
- redirectTo: z8.string().url().optional(),
579
- csrfToken: z8.string().nullable().optional()
616
+ csrfToken: z8.string().nullable().optional(),
617
+ refreshToken: z8.string().optional()
618
+ // For mobile/desktop clients (no cookies)
580
619
  });
581
620
  var refreshSessionResponseSchema = z8.object({
582
621
  accessToken: z8.string(),
583
622
  user: userSchema,
584
- csrfToken: z8.string()
623
+ csrfToken: z8.string().optional(),
624
+ // For web clients (cookie-based)
625
+ refreshToken: z8.string().optional()
626
+ // For mobile/desktop clients (no cookies)
585
627
  });
586
628
  var exchangeResetPasswordTokenResponseSchema = z8.object({
587
629
  token: z8.string(),
@@ -627,6 +669,18 @@ var updateOAuthConfigRequestSchema = oAuthConfigSchema.omit({
627
669
  }).extend({
628
670
  clientSecret: z8.string().optional()
629
671
  }).partial();
672
+ var pkceRegex = /^[A-Za-z0-9._~-]+$/;
673
+ var oAuthInitRequestSchema = z8.object({
674
+ // eslint-disable-next-line @typescript-eslint/naming-convention
675
+ redirect_uri: z8.string().url().optional(),
676
+ // eslint-disable-next-line @typescript-eslint/naming-convention
677
+ code_challenge: z8.string().min(43, "Code challenge must be at least 43 characters").max(128, "Code challenge must be at most 128 characters").regex(pkceRegex, "Code challenge must be base64url encoded")
678
+ });
679
+ var oAuthCodeExchangeRequestSchema = z8.object({
680
+ code: z8.string().min(1, "Exchange code is required"),
681
+ // eslint-disable-next-line @typescript-eslint/naming-convention
682
+ code_verifier: z8.string().min(43, "Code verifier must be at least 43 characters").max(128, "Code verifier must be at most 128 characters").regex(pkceRegex, "Code verifier must be base64url encoded")
683
+ });
630
684
  var listOAuthConfigsResponseSchema = z8.object({
631
685
  data: z8.array(oAuthConfigSchema),
632
686
  count: z8.number()
@@ -638,11 +692,12 @@ var updateAuthConfigRequestSchema = authConfigSchema.omit({
638
692
  }).partial();
639
693
  var getPublicAuthConfigResponseSchema = z8.object({
640
694
  oAuthProviders: z8.array(oAuthProvidersSchema),
695
+ customOAuthProviders: z8.array(customOAuthKeySchema),
641
696
  ...authConfigSchema.omit({
642
697
  id: true,
643
698
  updatedAt: true,
644
699
  createdAt: true,
645
- signInRedirectTo: true
700
+ allowedRedirectUrls: true
646
701
  }).shape
647
702
  });
648
703
  var authErrorResponseSchema = z8.object({
@@ -651,6 +706,16 @@ var authErrorResponseSchema = z8.object({
651
706
  statusCode: z8.number().int(),
652
707
  nextActions: z8.string().optional()
653
708
  });
709
+ var createCustomOAuthConfigRequestSchema = customOAuthConfigSchema.omit({ id: true, createdAt: true, updatedAt: true }).extend({
710
+ clientSecret: z8.string().min(1, "Client secret is required")
711
+ });
712
+ var updateCustomOAuthConfigRequestSchema = customOAuthConfigSchema.omit({ id: true, key: true, createdAt: true, updatedAt: true }).extend({
713
+ clientSecret: z8.string().min(1).optional()
714
+ }).partial();
715
+ var listCustomOAuthConfigsResponseSchema = z8.object({
716
+ data: z8.array(customOAuthConfigSchema),
717
+ count: z8.number()
718
+ });
654
719
 
655
720
  // node_modules/@insforge/shared-schemas/dist/metadata.schema.js
656
721
  import { z as z11 } from "zod";
@@ -680,6 +745,9 @@ var realtimeMessageSchema = z9.object({
680
745
  whDeliveredCount: z9.number().int().min(0),
681
746
  createdAt: z9.string().datetime()
682
747
  });
748
+ var realtimeConfigSchema = z9.object({
749
+ retentionDays: z9.number().int().positive().nullable()
750
+ });
683
751
  var subscribeChannelPayloadSchema = z9.object({
684
752
  channel: z9.string().min(1)
685
753
  // The resolved channel instance, e.g., "order:123"
@@ -765,7 +833,8 @@ var messageStatsResponseSchema = z10.object({
765
833
  topEvents: z10.array(z10.object({
766
834
  eventName: z10.string(),
767
835
  count: z10.number().int().min(0)
768
- }))
836
+ })),
837
+ retentionDays: realtimeConfigSchema.shape.retentionDays
769
838
  });
770
839
  var rlsPolicySchema = z10.object({
771
840
  policyName: z10.string(),
@@ -845,6 +914,9 @@ var databasePasswordInfoSchema = z11.object({
845
914
  var apiKeyResponseSchema = z11.object({
846
915
  apiKey: z11.string()
847
916
  });
917
+ var projectIdResponseSchema = z11.object({
918
+ projectId: z11.string().nullable()
919
+ });
848
920
 
849
921
  // node_modules/@insforge/shared-schemas/dist/ai.schema.js
850
922
  import { z as z12 } from "zod";
@@ -919,13 +991,83 @@ var audioContentSchema = z13.object({
919
991
  format: z13.enum(["wav", "mp3", "aiff", "aac", "ogg", "flac", "m4a"])
920
992
  })
921
993
  });
922
- var contentSchema = z13.union([textContentSchema, imageContentSchema, audioContentSchema]);
994
+ var fileContentSchema = z13.object({
995
+ type: z13.literal("file"),
996
+ file: z13.object({
997
+ // Filename with extension (e.g., "document.pdf")
998
+ filename: z13.string(),
999
+ // File data can be:
1000
+ // - Public URL: "https://example.com/document.pdf"
1001
+ // - Base64 data URL: "data:application/pdf;base64,..."
1002
+ // eslint-disable-next-line @typescript-eslint/naming-convention
1003
+ file_data: z13.string()
1004
+ })
1005
+ });
1006
+ var contentSchema = z13.union([
1007
+ textContentSchema,
1008
+ imageContentSchema,
1009
+ audioContentSchema,
1010
+ fileContentSchema
1011
+ ]);
1012
+ var toolFunctionSchema = z13.object({
1013
+ name: z13.string(),
1014
+ description: z13.string().optional(),
1015
+ parameters: z13.record(z13.unknown()).optional()
1016
+ });
1017
+ var toolSchema = z13.object({
1018
+ type: z13.literal("function"),
1019
+ function: toolFunctionSchema
1020
+ });
1021
+ var toolChoiceSchema = z13.union([
1022
+ z13.enum(["auto", "none", "required"]),
1023
+ z13.object({
1024
+ type: z13.literal("function"),
1025
+ function: z13.object({ name: z13.string() })
1026
+ })
1027
+ ]);
1028
+ var toolCallSchema = z13.object({
1029
+ id: z13.string(),
1030
+ type: z13.literal("function"),
1031
+ function: z13.object({
1032
+ name: z13.string(),
1033
+ arguments: z13.string()
1034
+ })
1035
+ });
923
1036
  var chatMessageSchema = z13.object({
924
- role: z13.enum(["user", "assistant", "system"]),
1037
+ role: z13.enum(["user", "assistant", "system", "tool"]),
925
1038
  // New format: content can be string or array of content parts (OpenAI-compatible)
926
- content: z13.union([z13.string(), z13.array(contentSchema)]),
1039
+ content: z13.union([z13.string(), z13.array(contentSchema)]).nullable(),
927
1040
  // Legacy format: separate images field (deprecated but supported for backward compatibility)
928
- images: z13.array(z13.object({ url: z13.string() })).optional()
1041
+ images: z13.array(z13.object({ url: z13.string() })).optional(),
1042
+ // Tool calls made by the assistant
1043
+ // eslint-disable-next-line @typescript-eslint/naming-convention
1044
+ tool_calls: z13.array(toolCallSchema).optional(),
1045
+ // Tool call ID for tool response messages
1046
+ // eslint-disable-next-line @typescript-eslint/naming-convention
1047
+ tool_call_id: z13.string().optional()
1048
+ });
1049
+ var webSearchPluginSchema = z13.object({
1050
+ enabled: z13.boolean(),
1051
+ // Engine selection:
1052
+ // - "native": Always use provider's built-in web search (OpenAI, Anthropic, Perplexity, xAI)
1053
+ // - "exa": Use Exa's search API
1054
+ // - undefined: Auto-select (native if available, otherwise Exa)
1055
+ engine: z13.enum(["native", "exa"]).optional(),
1056
+ // Maximum number of search results (1-10, default: 5)
1057
+ maxResults: z13.number().min(1).max(10).optional(),
1058
+ // Custom prompt for attaching search results to the message
1059
+ searchPrompt: z13.string().optional()
1060
+ });
1061
+ var fileParserPluginSchema = z13.object({
1062
+ enabled: z13.boolean(),
1063
+ pdf: z13.object({
1064
+ // PDF processing engine:
1065
+ // - "pdf-text": Best for well-structured PDFs with clear text content (Free)
1066
+ // - "mistral-ocr": Best for scanned documents or PDFs with images ($2 per 1,000 pages)
1067
+ // - "native": Only available for models that support file input natively (charged as input tokens)
1068
+ // If not specified, defaults to native if available, otherwise mistral-ocr
1069
+ engine: z13.enum(["pdf-text", "mistral-ocr", "native"]).optional()
1070
+ }).optional()
929
1071
  });
930
1072
  var chatCompletionRequestSchema = z13.object({
931
1073
  model: z13.string(),
@@ -933,10 +1075,52 @@ var chatCompletionRequestSchema = z13.object({
933
1075
  temperature: z13.number().min(0).max(2).optional(),
934
1076
  maxTokens: z13.number().positive().optional(),
935
1077
  topP: z13.number().min(0).max(1).optional(),
936
- stream: z13.boolean().optional()
1078
+ stream: z13.boolean().optional(),
1079
+ // Web Search: Incorporate relevant web search results into the response
1080
+ // Results are returned in the annotations field
1081
+ webSearch: webSearchPluginSchema.optional(),
1082
+ // File Parser: Configure PDF processing for file content in messages
1083
+ // When files are included in messages, this controls how PDFs are parsed
1084
+ fileParser: fileParserPluginSchema.optional(),
1085
+ // Thinking/Reasoning mode: Enable extended reasoning capabilities
1086
+ // Appends ":thinking" to the model ID for chain-of-thought reasoning
1087
+ thinking: z13.boolean().optional(),
1088
+ // Tool calling: Define functions the AI can call
1089
+ tools: z13.array(toolSchema).optional(),
1090
+ // Tool choice: Control whether/which tool is called ('auto', 'none', 'required', or specific function)
1091
+ toolChoice: toolChoiceSchema.optional(),
1092
+ // Parallel tool calls: Allow the model to call multiple tools in parallel
1093
+ parallelToolCalls: z13.boolean().optional()
1094
+ });
1095
+ var urlCitationAnnotationSchema = z13.object({
1096
+ type: z13.literal("url_citation"),
1097
+ urlCitation: z13.object({
1098
+ url: z13.string(),
1099
+ title: z13.string().optional(),
1100
+ content: z13.string().optional(),
1101
+ // Character indices in the response text where this citation applies
1102
+ startIndex: z13.number().optional(),
1103
+ endIndex: z13.number().optional()
1104
+ })
1105
+ });
1106
+ var fileAnnotationSchema = z13.object({
1107
+ type: z13.literal("file"),
1108
+ file: z13.object({
1109
+ filename: z13.string(),
1110
+ // Parsed content from the PDF (used for caching)
1111
+ parsedContent: z13.string().optional(),
1112
+ // Additional metadata from the parser
1113
+ metadata: z13.record(z13.unknown()).optional()
1114
+ })
937
1115
  });
1116
+ var annotationSchema = z13.union([urlCitationAnnotationSchema, fileAnnotationSchema]);
938
1117
  var chatCompletionResponseSchema = z13.object({
939
1118
  text: z13.string(),
1119
+ // Tool calls from the assistant (present when the model invokes tools)
1120
+ // eslint-disable-next-line @typescript-eslint/naming-convention
1121
+ tool_calls: z13.array(toolCallSchema).optional(),
1122
+ // Annotations from web search or file parsing (can be URL citations or file annotations)
1123
+ annotations: z13.array(annotationSchema).optional(),
940
1124
  metadata: z13.object({
941
1125
  model: z13.string(),
942
1126
  usage: z13.object({
@@ -946,6 +1130,30 @@ var chatCompletionResponseSchema = z13.object({
946
1130
  }).optional()
947
1131
  }).optional()
948
1132
  });
1133
+ var embeddingsRequestSchema = z13.object({
1134
+ model: z13.string(),
1135
+ input: z13.union([z13.string(), z13.array(z13.string())]),
1136
+ // eslint-disable-next-line @typescript-eslint/naming-convention
1137
+ encoding_format: z13.enum(["float", "base64"]).optional(),
1138
+ dimensions: z13.number().int().min(0).optional()
1139
+ });
1140
+ var embeddingObjectSchema = z13.object({
1141
+ object: z13.literal("embedding"),
1142
+ // Embedding can be number[] (float format) or string (base64 format)
1143
+ embedding: z13.union([z13.array(z13.number()), z13.string()]),
1144
+ index: z13.number()
1145
+ });
1146
+ var embeddingsResponseSchema = z13.object({
1147
+ object: z13.literal("list"),
1148
+ data: z13.array(embeddingObjectSchema),
1149
+ metadata: z13.object({
1150
+ model: z13.string(),
1151
+ usage: z13.object({
1152
+ promptTokens: z13.number().optional(),
1153
+ totalTokens: z13.number().optional()
1154
+ }).optional()
1155
+ }).optional()
1156
+ });
949
1157
  var imageGenerationRequestSchema = z13.object({
950
1158
  model: z13.string(),
951
1159
  prompt: z13.string(),
@@ -974,7 +1182,10 @@ var aiModelSchema = z13.object({
974
1182
  outputModality: z13.array(modalitySchema).min(1),
975
1183
  provider: z13.string(),
976
1184
  modelId: z13.string(),
977
- priceLevel: z13.number().min(0).max(3).optional()
1185
+ inputPrice: z13.number().min(0).optional(),
1186
+ // Price per million tokens in USD
1187
+ outputPrice: z13.number().min(0).optional()
1188
+ // Price per million tokens in USD
978
1189
  });
979
1190
  var createAIConfigurationRequestSchema = aiConfigurationSchema.omit({
980
1191
  id: true
@@ -997,6 +1208,15 @@ var getAIUsageSummaryRequestSchema = z13.object({
997
1208
  startDate: z13.string().datetime().optional(),
998
1209
  endDate: z13.string().datetime().optional()
999
1210
  });
1211
+ var keySourceSchema = z13.enum(["byok", "cloud", "env", "unconfigured"]);
1212
+ var gatewayConfigResponseSchema = z13.object({
1213
+ keySource: keySourceSchema,
1214
+ hasByokKey: z13.boolean(),
1215
+ maskedKey: z13.string().optional()
1216
+ });
1217
+ var setGatewayBYOKKeyRequestSchema = z13.object({
1218
+ apiKey: z13.string().min(1, "API key is required")
1219
+ });
1000
1220
 
1001
1221
  // node_modules/@insforge/shared-schemas/dist/logs.schema.js
1002
1222
  import { z as z14 } from "zod";
@@ -1027,6 +1247,16 @@ var logStatsSchema = z14.object({
1027
1247
  count: z14.number(),
1028
1248
  lastActivity: z14.string()
1029
1249
  });
1250
+ var buildLogEntrySchema = z14.object({
1251
+ level: z14.string(),
1252
+ message: z14.string()
1253
+ });
1254
+ var getBuildLogsResponseSchema = z14.object({
1255
+ deploymentId: z14.string(),
1256
+ status: z14.enum(["pending", "success", "failed"]),
1257
+ logs: z14.array(buildLogEntrySchema),
1258
+ createdAt: z14.string()
1259
+ });
1030
1260
 
1031
1261
  // node_modules/@insforge/shared-schemas/dist/logs-api.schema.js
1032
1262
  import { z as z15 } from "zod";
@@ -1102,7 +1332,19 @@ var listFunctionsResponseSchema = z17.object({
1102
1332
  functions: z17.array(functionSchema),
1103
1333
  runtime: z17.object({
1104
1334
  status: z17.enum(["running", "unavailable"])
1105
- })
1335
+ }),
1336
+ deploymentUrl: z17.string().nullable().optional()
1337
+ });
1338
+ var deploymentResultSchema = z17.object({
1339
+ id: z17.string(),
1340
+ status: z17.enum(["success", "failed"]),
1341
+ url: z17.string().nullable(),
1342
+ buildLogs: z17.array(z17.string()).optional()
1343
+ });
1344
+ var functionResponseSchema = z17.object({
1345
+ success: z17.boolean(),
1346
+ function: functionSchema,
1347
+ deployment: deploymentResultSchema.nullable().optional()
1106
1348
  });
1107
1349
 
1108
1350
  // node_modules/@insforge/shared-schemas/dist/cloud-events.schema.js
@@ -1142,6 +1384,9 @@ var showContactModalEventSchema = z18.object({
1142
1384
  var showConnectOverlayEventSchema = z18.object({
1143
1385
  type: z18.literal("SHOW_CONNECT_OVERLAY")
1144
1386
  });
1387
+ var showPlanModalEventSchema = z18.object({
1388
+ type: z18.literal("SHOW_PLAN_MODAL")
1389
+ });
1145
1390
  var authorizationCodeEventSchema = z18.object({
1146
1391
  type: z18.literal("AUTHORIZATION_CODE"),
1147
1392
  code: z18.string()
@@ -1150,6 +1395,51 @@ var routeChangeEventSchema = z18.object({
1150
1395
  type: z18.literal("ROUTE_CHANGE"),
1151
1396
  path: z18.string()
1152
1397
  });
1398
+ var requestProjectInfoEventSchema = z18.object({
1399
+ type: z18.literal("REQUEST_PROJECT_INFO")
1400
+ });
1401
+ var projectInfoEventSchema = z18.object({
1402
+ type: z18.literal("PROJECT_INFO"),
1403
+ name: z18.string(),
1404
+ instanceType: z18.string(),
1405
+ region: z18.string(),
1406
+ latestVersion: z18.string().optional()
1407
+ });
1408
+ var requestInstanceInfoEventSchema = z18.object({
1409
+ type: z18.literal("REQUEST_INSTANCE_INFO")
1410
+ });
1411
+ var instanceInfoEventSchema = z18.object({
1412
+ type: z18.literal("INSTANCE_INFO"),
1413
+ currentInstanceType: z18.string(),
1414
+ planName: z18.string(),
1415
+ computeCredits: z18.number(),
1416
+ currentOrgComputeCost: z18.number(),
1417
+ instanceTypes: z18.array(z18.object({
1418
+ id: z18.string(),
1419
+ name: z18.string(),
1420
+ cpu: z18.string(),
1421
+ ram: z18.string(),
1422
+ pricePerHour: z18.number(),
1423
+ pricePerMonth: z18.number()
1424
+ })),
1425
+ projects: z18.array(z18.object({
1426
+ name: z18.string(),
1427
+ instanceType: z18.string(),
1428
+ monthlyCost: z18.number(),
1429
+ isCurrent: z18.boolean(),
1430
+ status: z18.string()
1431
+ }))
1432
+ });
1433
+ var requestInstanceTypeChangeEventSchema = z18.object({
1434
+ type: z18.literal("REQUEST_INSTANCE_TYPE_CHANGE"),
1435
+ instanceType: z18.string()
1436
+ });
1437
+ var instanceTypeChangeResultEventSchema = z18.object({
1438
+ type: z18.literal("INSTANCE_TYPE_CHANGE_RESULT"),
1439
+ success: z18.boolean(),
1440
+ instanceType: z18.string().optional(),
1441
+ error: z18.string().optional()
1442
+ });
1153
1443
  var cloudEventSchema = z18.discriminatedUnion("type", [
1154
1444
  appRouteChangeEventSchema,
1155
1445
  authSuccessEventSchema,
@@ -1161,8 +1451,15 @@ var cloudEventSchema = z18.discriminatedUnion("type", [
1161
1451
  navigateToUsageSchema,
1162
1452
  showContactModalEventSchema,
1163
1453
  showConnectOverlayEventSchema,
1454
+ showPlanModalEventSchema,
1164
1455
  authorizationCodeEventSchema,
1165
- routeChangeEventSchema
1456
+ routeChangeEventSchema,
1457
+ requestProjectInfoEventSchema,
1458
+ projectInfoEventSchema,
1459
+ requestInstanceInfoEventSchema,
1460
+ instanceInfoEventSchema,
1461
+ requestInstanceTypeChangeEventSchema,
1462
+ instanceTypeChangeResultEventSchema
1166
1463
  ]);
1167
1464
 
1168
1465
  // node_modules/@insforge/shared-schemas/dist/docs.schema.js
@@ -1198,8 +1495,6 @@ var docTypeSchema = z19.enum([
1198
1495
  "storage-sdk",
1199
1496
  "functions-sdk",
1200
1497
  "ai-integration-sdk",
1201
- "auth-components-react",
1202
- "auth-components-nextjs",
1203
1498
  "real-time",
1204
1499
  "deployment"
1205
1500
  ]).describe(`
@@ -1209,8 +1504,6 @@ var docTypeSchema = z19.enum([
1209
1504
  "storage-sdk" (file storage),
1210
1505
  "functions-sdk" (edge functions),
1211
1506
  "auth-sdk" (direct SDK methods for custom auth flows),
1212
- "auth-components-react" (authentication components for React+Vite applications),
1213
- "auth-components-nextjs" (authentication components for Next.js applications),
1214
1507
  "ai-integration-sdk" (AI features),
1215
1508
  "real-time" (real-time pub/sub through WebSockets),
1216
1509
  "deployment" (deploy frontend applications via MCP tool)
@@ -1295,109 +1588,182 @@ var listDeploymentsResponseSchema = z22.object({
1295
1588
  total: z22.number()
1296
1589
  })
1297
1590
  });
1298
-
1299
- // src/shared/tools.ts
1300
- import FormData from "form-data";
1301
- var execAsync = promisify(exec);
1302
- var TOOL_VERSION_REQUIREMENTS = {
1303
- // Schedule tools - require backend v1.1.1+
1304
- "upsert-schedule": { minVersion: "1.1.1" },
1305
- "delete-schedule": { minVersion: "1.1.1" },
1306
- // 'get-schedules': { minVersion: '1.1.1' },
1307
- // 'get-schedule-logs': { minVersion: '1.1.1' },
1308
- "create-deployment": { minVersion: "1.4.7" }
1309
- // Example of a deprecated tool (uncomment when needed):
1310
- // 'legacy-tool': { minVersion: '1.0.0', maxVersion: '1.5.0' },
1311
- };
1312
- function compareVersions(v1, v2) {
1313
- const clean1 = v1.replace(/^v/, "").split("-")[0];
1314
- const clean2 = v2.replace(/^v/, "").split("-")[0];
1315
- const parts1 = clean1.split(".").map(Number);
1316
- const parts2 = clean2.split(".").map(Number);
1317
- for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
1318
- const part1 = parts1[i] || 0;
1319
- const part2 = parts2[i] || 0;
1320
- if (part1 > part2) return 1;
1321
- if (part1 < part2) return -1;
1322
- }
1323
- return 0;
1324
- }
1325
- function shouldRegisterTool(toolName, backendVersion) {
1326
- const requirement = TOOL_VERSION_REQUIREMENTS[toolName];
1327
- if (!requirement) {
1328
- return true;
1329
- }
1330
- const { minVersion, maxVersion } = requirement;
1331
- if (minVersion && compareVersions(backendVersion, minVersion) < 0) {
1332
- return false;
1333
- }
1334
- if (maxVersion && compareVersions(backendVersion, maxVersion) > 0) {
1335
- return false;
1336
- }
1337
- return true;
1338
- }
1339
- async function fetchBackendVersion(apiBaseUrl) {
1340
- const response = await fetch2(`${apiBaseUrl}/api/health`, {
1341
- method: "GET",
1342
- headers: {
1343
- "Content-Type": "application/json"
1591
+ var deploymentEnvVarSchema = z22.object({
1592
+ id: z22.string(),
1593
+ // Vercel env var ID (needed for delete/get)
1594
+ key: z22.string(),
1595
+ type: z22.enum(["plain", "encrypted", "secret", "sensitive", "system"]),
1596
+ updatedAt: z22.number().optional()
1597
+ // Unix timestamp (milliseconds)
1598
+ });
1599
+ var deploymentEnvVarWithValueSchema = z22.object({
1600
+ id: z22.string(),
1601
+ key: z22.string(),
1602
+ value: z22.string(),
1603
+ type: z22.enum(["plain", "encrypted", "secret", "sensitive", "system"]),
1604
+ updatedAt: z22.number().optional()
1605
+ });
1606
+ var listEnvVarsResponseSchema = z22.object({
1607
+ envVars: z22.array(deploymentEnvVarSchema)
1608
+ });
1609
+ var getEnvVarResponseSchema = z22.object({
1610
+ envVar: deploymentEnvVarWithValueSchema
1611
+ });
1612
+ var upsertEnvVarRequestSchema = z22.object({
1613
+ key: z22.string().trim().min(1, "key is required"),
1614
+ value: z22.string()
1615
+ });
1616
+ var upsertEnvVarsRequestSchema = z22.object({
1617
+ envVars: z22.array(upsertEnvVarRequestSchema).min(1)
1618
+ }).superRefine(({ envVars }, ctx) => {
1619
+ const firstSeenByKey = /* @__PURE__ */ new Map();
1620
+ envVars.forEach((envVar, index) => {
1621
+ const existingIndex = firstSeenByKey.get(envVar.key);
1622
+ if (existingIndex !== void 0) {
1623
+ ctx.addIssue({
1624
+ code: z22.ZodIssueCode.custom,
1625
+ message: "duplicate environment variable key",
1626
+ path: ["envVars", index, "key"]
1627
+ });
1628
+ return;
1344
1629
  }
1630
+ firstSeenByKey.set(envVar.key, index);
1345
1631
  });
1346
- if (!response.ok) {
1347
- throw new Error(`Health check failed with status ${response.status}`);
1348
- }
1349
- const health = await response.json();
1350
- return health.version;
1351
- }
1352
- async function registerInsforgeTools(server, config = {}) {
1353
- const GLOBAL_API_KEY = config.apiKey || process.env.API_KEY || "";
1354
- const API_BASE_URL = config.apiBaseUrl || process.env.API_BASE_URL || "http://localhost:7130";
1355
- const usageTracker = new UsageTracker(API_BASE_URL, GLOBAL_API_KEY);
1356
- const backendVersion = await fetchBackendVersion(API_BASE_URL);
1357
- console.error(`Backend version: ${backendVersion}`);
1358
- let toolCount = 0;
1359
- const registerTool = (toolName, ...args) => {
1360
- if (shouldRegisterTool(toolName, backendVersion)) {
1361
- server.tool(toolName, ...args);
1362
- toolCount++;
1363
- return true;
1364
- } else {
1365
- const req = TOOL_VERSION_REQUIREMENTS[toolName];
1366
- const reason = req?.minVersion && compareVersions(backendVersion, req.minVersion) < 0 ? `requires backend >= ${req.minVersion}` : `deprecated after backend ${req?.maxVersion}`;
1367
- console.error(`Skipping tool '${toolName}': ${reason} (current: ${backendVersion})`);
1368
- return false;
1369
- }
1370
- };
1371
- async function trackToolUsage(toolName, success = true) {
1372
- if (GLOBAL_API_KEY) {
1373
- await usageTracker.trackUsage(toolName, success);
1374
- }
1375
- }
1376
- function withUsageTracking(toolName, handler) {
1377
- return async (...args) => {
1378
- try {
1379
- const result = await handler(...args);
1380
- await trackToolUsage(toolName, true);
1381
- return result;
1382
- } catch (error) {
1383
- await trackToolUsage(toolName, false);
1384
- throw error;
1385
- }
1386
- };
1387
- }
1388
- const getApiKey = (_toolApiKey) => {
1389
- if (!GLOBAL_API_KEY) {
1390
- throw new Error("API key is required. Pass --api_key when starting the MCP server.");
1391
- }
1392
- return GLOBAL_API_KEY;
1393
- };
1632
+ });
1633
+ var upsertEnvVarResponseSchema = z22.object({
1634
+ success: z22.literal(true),
1635
+ message: z22.string()
1636
+ });
1637
+ var upsertEnvVarsResponseSchema = z22.object({
1638
+ success: z22.literal(true),
1639
+ message: z22.string(),
1640
+ count: z22.number().int().positive()
1641
+ });
1642
+ var deleteEnvVarResponseSchema = z22.object({
1643
+ success: z22.literal(true),
1644
+ message: z22.string()
1645
+ });
1646
+ var updateSlugRequestSchema = z22.object({
1647
+ slug: z22.string().trim().min(3, "slug must be at least 3 characters").max(63, "slug must be at most 63 characters").regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, "slug must be lowercase alphanumeric with hyphens, not starting or ending with hyphen").nullable()
1648
+ });
1649
+ var updateSlugResponseSchema = z22.object({
1650
+ success: z22.boolean(),
1651
+ slug: z22.string().nullable(),
1652
+ domain: z22.string().nullable()
1653
+ });
1654
+ var deploymentMetadataResponseSchema = z22.object({
1655
+ currentDeploymentId: z22.string().uuid().nullable(),
1656
+ defaultDomainUrl: z22.string().nullable(),
1657
+ customDomainUrl: z22.string().nullable()
1658
+ });
1659
+ var domainVerificationRecordSchema = z22.object({
1660
+ type: z22.string(),
1661
+ domain: z22.string(),
1662
+ value: z22.string()
1663
+ });
1664
+ var customDomainSchema = z22.object({
1665
+ domain: z22.string(),
1666
+ apexDomain: z22.string(),
1667
+ verified: z22.boolean(),
1668
+ misconfigured: z22.boolean(),
1669
+ verification: z22.array(domainVerificationRecordSchema),
1670
+ cnameTarget: z22.string().nullable(),
1671
+ aRecordValue: z22.string().nullable()
1672
+ });
1673
+ var addCustomDomainRequestSchema = z22.object({
1674
+ domain: z22.string().trim().min(1, "Domain is required").regex(/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/i, "Invalid domain format (e.g. myapp.com or www.myapp.com)").refine((domain) => !domain.toLowerCase().endsWith(".insforge.site"), {
1675
+ message: "Domains ending with .insforge.site are reserved by InsForge"
1676
+ })
1677
+ });
1678
+ var listCustomDomainsResponseSchema = z22.object({
1679
+ domains: z22.array(customDomainSchema)
1680
+ });
1681
+
1682
+ // node_modules/@insforge/shared-schemas/dist/schedules.schema.js
1683
+ import { z as z23 } from "zod";
1684
+ var scheduleSchema = z23.object({
1685
+ id: z23.string().uuid(),
1686
+ name: z23.string(),
1687
+ cronSchedule: z23.string(),
1688
+ functionUrl: z23.string().url(),
1689
+ httpMethod: z23.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
1690
+ // Optional HTTP headers to include when invoking the scheduled function
1691
+ headers: z23.record(z23.string()).nullable(),
1692
+ // Body payload for the scheduled invocation. Can be a JSON object or a raw string.
1693
+ body: z23.union([z23.string(), z23.record(z23.unknown())]).nullable(),
1694
+ // cron_job_id is a BIGINT in postgres, which node-pg returns as a string.
1695
+ cronJobId: z23.string().nullable(),
1696
+ lastExecutedAt: z23.string().datetime().nullable(),
1697
+ // Whether the cron job is currently active (has a scheduled cron job)
1698
+ isActive: z23.boolean().default(true),
1699
+ // Next scheduled run time in ISO format (nullable if cron expression invalid)
1700
+ nextRun: z23.string().datetime().nullable(),
1701
+ createdAt: z23.string().datetime(),
1702
+ updatedAt: z23.string().datetime()
1703
+ });
1704
+ var scheduleLogSchema = z23.object({
1705
+ id: z23.string().uuid(),
1706
+ scheduleId: z23.string().uuid(),
1707
+ executedAt: z23.string().datetime(),
1708
+ statusCode: z23.number().int(),
1709
+ success: z23.boolean(),
1710
+ durationMs: z23.number().int(),
1711
+ message: z23.string().nullable()
1712
+ });
1713
+
1714
+ // node_modules/@insforge/shared-schemas/dist/schedules-api.schema.js
1715
+ import { z as z24 } from "zod";
1716
+ var cronScheduleSchema = z24.string().refine((value) => {
1717
+ const parts = value.split(" ");
1718
+ return parts.length === 5 || parts.length === 6;
1719
+ }, { message: 'Invalid cron schedule format. Use 5 or 6 parts (e.g., "* * * * *").' });
1720
+ var createScheduleRequestSchema = z24.object({
1721
+ name: z24.string().min(3, "Schedule name must be at least 3 characters long"),
1722
+ cronSchedule: cronScheduleSchema,
1723
+ functionUrl: z24.string().url("The function URL must be a valid URL."),
1724
+ httpMethod: z24.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
1725
+ headers: z24.record(z24.string()).optional().describe("Header values can reference secrets using ${{secrets.KEY_NAME}} syntax."),
1726
+ body: z24.record(z24.unknown()).optional().describe("The JSON body to send with the request.")
1727
+ });
1728
+ var updateScheduleRequestSchema = z24.object({
1729
+ name: z24.string().min(3, "Schedule name must be at least 3 characters long").optional(),
1730
+ cronSchedule: cronScheduleSchema.optional(),
1731
+ functionUrl: z24.string().url("The function URL must be a valid URL.").optional(),
1732
+ httpMethod: z24.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).optional(),
1733
+ headers: z24.record(z24.string()).optional().describe("Header values can reference secrets using ${{secrets.KEY_NAME}} syntax."),
1734
+ body: z24.record(z24.unknown()).optional().describe("The JSON body to send with the request."),
1735
+ isActive: z24.boolean().optional().describe("Enable or disable the schedule.")
1736
+ });
1737
+ var listSchedulesResponseSchema = z24.array(scheduleSchema);
1738
+ var executionLogResponseSchema = scheduleLogSchema;
1739
+ var listExecutionLogsResponseSchema = z24.object({
1740
+ logs: z24.array(executionLogResponseSchema),
1741
+ totalCount: z24.number().int().nonnegative(),
1742
+ limit: z24.number().int().positive(),
1743
+ offset: z24.number().int().nonnegative()
1744
+ });
1745
+ var createScheduleResponseSchema = z24.object({
1746
+ id: z24.string().uuid(),
1747
+ cronJobId: z24.string(),
1748
+ message: z24.string()
1749
+ });
1750
+ var updateScheduleResponseSchema = z24.object({
1751
+ id: z24.string().uuid(),
1752
+ cronJobId: z24.string().optional(),
1753
+ message: z24.string()
1754
+ });
1755
+ var deleteScheduleResponseSchema = z24.object({
1756
+ message: z24.string()
1757
+ });
1758
+
1759
+ // src/shared/tools/docs.ts
1760
+ function registerDocsTools(ctx) {
1761
+ const { API_BASE_URL, registerTool, withUsageTracking, getApiKey, addBackgroundContext } = ctx;
1394
1762
  const fetchDocumentation = async (docType) => {
1395
1763
  try {
1396
1764
  const response = await fetch2(`${API_BASE_URL}/api/docs/${docType}`, {
1397
1765
  method: "GET",
1398
- headers: {
1399
- "Content-Type": "application/json"
1400
- }
1766
+ headers: { "Content-Type": "application/json" }
1401
1767
  });
1402
1768
  if (response.status === 404) {
1403
1769
  throw new Error("Documentation not found. This feature may not be supported in your project version. Please contact the Insforge team for assistance.");
@@ -1407,55 +1773,47 @@ async function registerInsforgeTools(server, config = {}) {
1407
1773
  let content = result.content;
1408
1774
  content = content.replace(/http:\/\/localhost:7130/g, API_BASE_URL);
1409
1775
  content = content.replace(/https:\/\/your-app\.region\.insforge\.app/g, API_BASE_URL);
1776
+ content = content.replace(/https:\/\/your-app\.insforge\.app/g, API_BASE_URL);
1410
1777
  return content;
1411
1778
  }
1412
1779
  throw new Error("Invalid response format from documentation endpoint");
1413
1780
  } catch (error) {
1414
1781
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1415
- throw new Error(`Unable to retrieve ${docType} documentation: ${errMsg}`);
1782
+ throw new Error(`Unable to retrieve ${docType} documentation: ${errMsg}`, { cause: error });
1416
1783
  }
1417
1784
  };
1418
- const fetchInsforgeInstructionsContext = async () => {
1785
+ const fetchSDKDocumentation = async (feature, language) => {
1419
1786
  try {
1420
- return await fetchDocumentation("instructions");
1421
- } catch (error) {
1422
- console.error("Failed to fetch insforge-instructions.md:", error);
1423
- return null;
1424
- }
1425
- };
1426
- const addBackgroundContext = async (response) => {
1427
- const isLegacyVersion = compareVersions(backendVersion, "1.1.7") < 0;
1428
- if (isLegacyVersion) {
1429
- const context = await fetchInsforgeInstructionsContext();
1430
- if (context && response.content && Array.isArray(response.content)) {
1431
- response.content.push({
1432
- type: "text",
1433
- text: `
1434
-
1435
- ---
1436
- \u{1F527} INSFORGE DEVELOPMENT RULES (Auto-loaded):
1437
- ${context}`
1438
- });
1787
+ const response = await fetch2(`${API_BASE_URL}/api/docs/${feature}/${language}`, {
1788
+ method: "GET",
1789
+ headers: { "Content-Type": "application/json" }
1790
+ });
1791
+ if (response.status === 404) {
1792
+ throw new Error("Documentation not found. This feature may not be supported in your project version. Please contact the Insforge team for assistance.");
1793
+ }
1794
+ const result = await handleApiResponse(response);
1795
+ if (result && typeof result === "object" && "content" in result) {
1796
+ let content = result.content;
1797
+ content = content.replace(/http:\/\/localhost:7130/g, API_BASE_URL);
1798
+ content = content.replace(/https:\/\/your-app\.region\.insforge\.app/g, API_BASE_URL);
1799
+ content = content.replace(/https:\/\/your-app\.insforge\.app/g, API_BASE_URL);
1800
+ return content;
1439
1801
  }
1802
+ throw new Error("Invalid response format from documentation endpoint");
1803
+ } catch (error) {
1804
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1805
+ throw new Error(`Unable to retrieve ${feature}-${language} documentation: ${errMsg}`, { cause: error });
1440
1806
  }
1441
- return response;
1442
1807
  };
1443
1808
  registerTool(
1444
1809
  "fetch-docs",
1445
1810
  'Fetch Insforge documentation. Use "instructions" for essential backend setup (MANDATORY FIRST), or select specific SDK docs for database, auth, storage, functions, or AI integration.',
1446
- {
1447
- docType: docTypeSchema
1448
- },
1811
+ { docType: docTypeSchema },
1449
1812
  withUsageTracking("fetch-docs", async ({ docType }) => {
1450
1813
  try {
1451
1814
  const content = await fetchDocumentation(docType);
1452
1815
  return await addBackgroundContext({
1453
- content: [
1454
- {
1455
- type: "text",
1456
- text: content
1457
- }
1458
- ]
1816
+ content: [{ type: "text", text: content }]
1459
1817
  });
1460
1818
  } catch (error) {
1461
1819
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
@@ -1468,7 +1826,38 @@ ${context}`
1468
1826
  };
1469
1827
  }
1470
1828
  return {
1471
- content: [{ type: "text", text: `Error fetching ${docType} documentation: ${errMsg}` }]
1829
+ content: [{ type: "text", text: `Error fetching ${docType} documentation: ${errMsg}` }],
1830
+ isError: true
1831
+ };
1832
+ }
1833
+ })
1834
+ );
1835
+ registerTool(
1836
+ "fetch-sdk-docs",
1837
+ `Fetch Insforge SDK documentation for a specific feature and language combination.
1838
+
1839
+ Supported features: ${sdkFeatureSchema.options.join(", ")}
1840
+ Supported languages: ${sdkLanguageSchema.options.join(", ")}`,
1841
+ { sdkFeature: sdkFeatureSchema, sdkLanguage: sdkLanguageSchema },
1842
+ withUsageTracking("fetch-sdk-docs", async ({ sdkFeature, sdkLanguage }) => {
1843
+ try {
1844
+ const content = await fetchSDKDocumentation(sdkFeature, sdkLanguage);
1845
+ return await addBackgroundContext({
1846
+ content: [{ type: "text", text: content }]
1847
+ });
1848
+ } catch (error) {
1849
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1850
+ if (errMsg.includes("404") || errMsg.toLowerCase().includes("not found")) {
1851
+ return {
1852
+ content: [{
1853
+ type: "text",
1854
+ text: `Documentation for "${sdkFeature}-${sdkLanguage}" is not available. This is likely because your backend version is too old and doesn't support this documentation endpoint yet. This won't affect the functionality of the tools - they will still work correctly.`
1855
+ }]
1856
+ };
1857
+ }
1858
+ return {
1859
+ content: [{ type: "text", text: `Error fetching ${sdkFeature}-${sdkLanguage} documentation: ${errMsg}` }],
1860
+ isError: true
1472
1861
  };
1473
1862
  }
1474
1863
  })
@@ -1477,7 +1866,7 @@ ${context}`
1477
1866
  "get-anon-key",
1478
1867
  "Generate an anonymous JWT token that never expires. Requires admin API key. Use this for client-side applications that need public access.",
1479
1868
  {
1480
- apiKey: z23.string().optional().describe("API key for authentication (optional if provided via --api_key)")
1869
+ apiKey: z25.string().optional().describe("API key for authentication (optional if provided via --api_key)")
1481
1870
  },
1482
1871
  withUsageTracking("get-anon-key", async ({ apiKey }) => {
1483
1872
  try {
@@ -1491,61 +1880,74 @@ ${context}`
1491
1880
  });
1492
1881
  const result = await handleApiResponse(response);
1493
1882
  return await addBackgroundContext({
1494
- content: [
1495
- {
1496
- type: "text",
1497
- text: formatSuccessMessage("Anonymous token generated", result)
1498
- }
1499
- ]
1883
+ content: [{ type: "text", text: formatSuccessMessage("Anonymous token generated", result) }]
1500
1884
  });
1501
1885
  } catch (error) {
1502
1886
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1503
1887
  return {
1504
- content: [
1505
- {
1506
- type: "text",
1507
- text: `Error generating anonymous token: ${errMsg}`
1508
- }
1509
- ],
1888
+ content: [{ type: "text", text: `Error generating anonymous token: ${errMsg}` }],
1510
1889
  isError: true
1511
1890
  };
1512
1891
  }
1513
1892
  })
1514
1893
  );
1894
+ }
1895
+
1896
+ // src/shared/tools/database.ts
1897
+ import { z as z26 } from "zod";
1898
+ import fetch3 from "node-fetch";
1899
+ import { promises as fs } from "fs";
1900
+ import { execFile } from "child_process";
1901
+ import { promisify } from "util";
1902
+ import { tmpdir } from "os";
1903
+ import { join, basename } from "path";
1904
+ import FormData from "form-data";
1905
+
1906
+ // src/shared/tools/utils.ts
1907
+ var shellEsc = (s) => `'${s.replace(/'/g, "'\\''")}'`;
1908
+
1909
+ // src/shared/tools/database.ts
1910
+ var execFileAsync = promisify(execFile);
1911
+ function isAnonTokenResponse(obj) {
1912
+ return typeof obj === "object" && obj !== null && "accessToken" in obj && typeof obj.accessToken === "string" && obj.accessToken.length > 0;
1913
+ }
1914
+ function isBulkUpsertApiResult(obj) {
1915
+ if (typeof obj !== "object" || obj === null || !("success" in obj) || !("rowsAffected" in obj) || !("totalRecords" in obj) || !("table" in obj)) {
1916
+ return false;
1917
+ }
1918
+ const value = obj;
1919
+ if (typeof value.success !== "boolean" || typeof value.rowsAffected !== "number" || typeof value.totalRecords !== "number" || typeof value.table !== "string" || value.table.length === 0) {
1920
+ return false;
1921
+ }
1922
+ if (value.message !== void 0 && typeof value.message !== "string") {
1923
+ return false;
1924
+ }
1925
+ return true;
1926
+ }
1927
+ function registerDatabaseTools(ctx) {
1928
+ const { API_BASE_URL, isRemote, registerTool, withUsageTracking, getApiKey, addBackgroundContext } = ctx;
1515
1929
  registerTool(
1516
1930
  "get-table-schema",
1517
1931
  "Returns the detailed schema(including RLS, indexes, constraints, etc.) of a specific table",
1518
1932
  {
1519
- apiKey: z23.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
1520
- tableName: z23.string().describe("Name of the table")
1933
+ apiKey: z26.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
1934
+ tableName: z26.string().describe("Name of the table")
1521
1935
  },
1522
1936
  withUsageTracking("get-table-schema", async ({ apiKey, tableName }) => {
1523
1937
  try {
1524
1938
  const actualApiKey = getApiKey(apiKey);
1525
- const response = await fetch2(`${API_BASE_URL}/api/metadata/${tableName}`, {
1939
+ const response = await fetch3(`${API_BASE_URL}/api/metadata/${encodeURIComponent(tableName)}`, {
1526
1940
  method: "GET",
1527
- headers: {
1528
- "x-api-key": actualApiKey
1529
- }
1941
+ headers: { "x-api-key": actualApiKey }
1530
1942
  });
1531
1943
  const result = await handleApiResponse(response);
1532
1944
  return await addBackgroundContext({
1533
- content: [
1534
- {
1535
- type: "text",
1536
- text: formatSuccessMessage("Schema retrieved", result)
1537
- }
1538
- ]
1945
+ content: [{ type: "text", text: formatSuccessMessage("Schema retrieved", result) }]
1539
1946
  });
1540
1947
  } catch (error) {
1541
1948
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1542
1949
  return {
1543
- content: [
1544
- {
1545
- type: "text",
1546
- text: `Error getting table schema: ${errMsg}`
1547
- }
1548
- ],
1950
+ content: [{ type: "text", text: `Error getting table schema: ${errMsg}` }],
1549
1951
  isError: true
1550
1952
  };
1551
1953
  }
@@ -1555,37 +1957,25 @@ ${context}`
1555
1957
  "get-backend-metadata",
1556
1958
  "Index all backend metadata",
1557
1959
  {
1558
- apiKey: z23.string().optional().describe("API key for authentication (optional if provided via --api_key)")
1960
+ apiKey: z26.string().optional().describe("API key for authentication (optional if provided via --api_key)")
1559
1961
  },
1560
1962
  withUsageTracking("get-backend-metadata", async ({ apiKey }) => {
1561
1963
  try {
1562
1964
  const actualApiKey = getApiKey(apiKey);
1563
- const response = await fetch2(`${API_BASE_URL}/api/metadata?mcp=true`, {
1965
+ const response = await fetch3(`${API_BASE_URL}/api/metadata?mcp=true`, {
1564
1966
  method: "GET",
1565
- headers: {
1566
- "x-api-key": actualApiKey
1567
- }
1967
+ headers: { "x-api-key": actualApiKey }
1568
1968
  });
1569
1969
  const metadata = await handleApiResponse(response);
1570
1970
  return await addBackgroundContext({
1571
- content: [
1572
- {
1573
- type: "text",
1574
- text: `Backend metadata:
1971
+ content: [{ type: "text", text: `Backend metadata:
1575
1972
 
1576
- ${JSON.stringify(metadata, null, 2)}`
1577
- }
1578
- ]
1973
+ ${JSON.stringify(metadata, null, 2)}` }]
1579
1974
  });
1580
1975
  } catch (error) {
1581
1976
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1582
1977
  return {
1583
- content: [
1584
- {
1585
- type: "text",
1586
- text: `Error retrieving backend metadata: ${errMsg}`
1587
- }
1588
- ],
1978
+ content: [{ type: "text", text: `Error retrieving backend metadata: ${errMsg}` }],
1589
1979
  isError: true
1590
1980
  };
1591
1981
  }
@@ -1595,17 +1985,14 @@ ${JSON.stringify(metadata, null, 2)}`
1595
1985
  "run-raw-sql",
1596
1986
  "Execute raw SQL query with optional parameters. Admin access required. Use with caution as it can modify data directly.",
1597
1987
  {
1598
- apiKey: z23.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
1988
+ apiKey: z26.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
1599
1989
  ...rawSQLRequestSchema.shape
1600
1990
  },
1601
1991
  withUsageTracking("run-raw-sql", async ({ apiKey, query, params }) => {
1602
1992
  try {
1603
1993
  const actualApiKey = getApiKey(apiKey);
1604
- const requestBody = {
1605
- query,
1606
- params: params || []
1607
- };
1608
- const response = await fetch2(`${API_BASE_URL}/api/database/advance/rawsql`, {
1994
+ const requestBody = { query, params: params || [] };
1995
+ const response = await fetch3(`${API_BASE_URL}/api/database/advance/rawsql`, {
1609
1996
  method: "POST",
1610
1997
  headers: {
1611
1998
  "x-api-key": actualApiKey,
@@ -1615,75 +2002,103 @@ ${JSON.stringify(metadata, null, 2)}`
1615
2002
  });
1616
2003
  const result = await handleApiResponse(response);
1617
2004
  return await addBackgroundContext({
1618
- content: [
1619
- {
1620
- type: "text",
1621
- text: formatSuccessMessage("SQL query executed", result)
1622
- }
1623
- ]
2005
+ content: [{ type: "text", text: formatSuccessMessage("SQL query executed", result) }]
1624
2006
  });
1625
2007
  } catch (error) {
1626
2008
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1627
2009
  return {
1628
- content: [
1629
- {
1630
- type: "text",
1631
- text: `Error executing SQL query: ${errMsg}`
1632
- }
1633
- ],
2010
+ content: [{ type: "text", text: `Error executing SQL query: ${errMsg}` }],
1634
2011
  isError: true
1635
2012
  };
1636
2013
  }
1637
2014
  })
1638
2015
  );
1639
- registerTool(
1640
- "download-template",
1641
- "CRITICAL: MANDATORY FIRST STEP for all new InsForge projects. Download pre-configured starter template to a temporary directory. After download, you MUST copy files to current directory using the provided command.",
1642
- {
1643
- frame: z23.enum(["react", "nextjs"]).describe("Framework to use for the template (support React and Next.js)"),
1644
- projectName: z23.string().optional().describe('Name for the project directory (optional, defaults to "insforge-react")')
1645
- },
1646
- withUsageTracking("download-template", async ({ frame, projectName }) => {
1647
- try {
1648
- const response = await fetch2(`${API_BASE_URL}/api/auth/tokens/anon`, {
1649
- method: "POST",
1650
- headers: {
1651
- "x-api-key": getApiKey(),
1652
- "Content-Type": "application/json"
2016
+ if (isRemote) {
2017
+ registerTool(
2018
+ "download-template",
2019
+ "CRITICAL: MANDATORY FIRST STEP for all new InsForge projects. Fetches configuration and returns a command for you to run locally to scaffold a starter template.",
2020
+ {
2021
+ frame: z26.enum(["react", "nextjs"]).describe("Framework to use for the template (support React and Next.js)"),
2022
+ projectName: z26.string().optional().describe('Name for the project directory (optional, defaults to "insforge-{frame}")')
2023
+ },
2024
+ withUsageTracking("download-template", async ({ frame, projectName }) => {
2025
+ try {
2026
+ const response = await fetch3(`${API_BASE_URL}/api/auth/tokens/anon`, {
2027
+ method: "POST",
2028
+ headers: {
2029
+ "x-api-key": getApiKey(),
2030
+ "Content-Type": "application/json"
2031
+ }
2032
+ });
2033
+ const anonResult = await handleApiResponse(response);
2034
+ if (!isAnonTokenResponse(anonResult)) {
2035
+ throw new Error("Failed to retrieve anon key from backend");
1653
2036
  }
1654
- });
1655
- const result = await handleApiResponse(response);
1656
- const anonKey = result.accessToken;
1657
- if (!anonKey) {
1658
- throw new Error("Failed to retrieve anon key from backend");
2037
+ const anonKey = anonResult.accessToken;
2038
+ const rawDir = projectName || `insforge-${frame}`;
2039
+ if (!rawDir || rawDir === "." || rawDir === ".." || /[/\\]/.test(rawDir) || !/^[\w.-]+$/.test(rawDir)) {
2040
+ throw new Error("projectName must be a single directory name using only letters, numbers, hyphens, underscores, and dots");
2041
+ }
2042
+ const targetDir = rawDir;
2043
+ const instructions = `Template configuration ready. Please run the following command in your project's parent directory:
2044
+
2045
+ \`\`\`bash
2046
+ npx create-insforge-app ${shellEsc(targetDir)} --frame ${frame} --base-url ${shellEsc(API_BASE_URL)} --anon-key ${shellEsc(anonKey)}
2047
+ \`\`\`
2048
+
2049
+ After the command completes, \`cd ${shellEsc(targetDir)}\` and start developing.`;
2050
+ return {
2051
+ content: [{ type: "text", text: instructions }]
2052
+ };
2053
+ } catch (error) {
2054
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2055
+ return {
2056
+ content: [{ type: "text", text: `Error preparing template: ${errMsg}` }],
2057
+ isError: true
2058
+ };
1659
2059
  }
1660
- const tempDir = tmpdir();
1661
- const targetDir = projectName || `insforge-${frame}`;
1662
- const templatePath = `${tempDir}/${targetDir}`;
1663
- console.error(`[download-template] Target path: ${templatePath}`);
2060
+ })
2061
+ );
2062
+ } else {
2063
+ registerTool(
2064
+ "download-template",
2065
+ "CRITICAL: MANDATORY FIRST STEP for all new InsForge projects. Download pre-configured starter template to a temporary directory. After download, you MUST copy files to current directory using the provided command.",
2066
+ {
2067
+ frame: z26.enum(["react", "nextjs"]).describe("Framework to use for the template (support React and Next.js)"),
2068
+ projectName: z26.string().optional().describe('Name for the project directory (optional, defaults to "insforge-{frame}")')
2069
+ },
2070
+ withUsageTracking("download-template", async ({ frame, projectName }) => {
1664
2071
  try {
1665
- const stats = await fs.stat(templatePath);
1666
- if (stats.isDirectory()) {
1667
- console.error(`[download-template] Removing existing template at ${templatePath}`);
1668
- await fs.rm(templatePath, { recursive: true, force: true });
2072
+ const response = await fetch3(`${API_BASE_URL}/api/auth/tokens/anon`, {
2073
+ method: "POST",
2074
+ headers: {
2075
+ "x-api-key": getApiKey(),
2076
+ "Content-Type": "application/json"
2077
+ }
2078
+ });
2079
+ const anonResult = await handleApiResponse(response);
2080
+ if (!isAnonTokenResponse(anonResult)) {
2081
+ throw new Error("Failed to retrieve anon key from backend");
1669
2082
  }
1670
- } catch {
1671
- }
1672
- const command = `npx create-insforge-app ${targetDir} --frame ${frame} --base-url ${API_BASE_URL} --anon-key ${anonKey} --skip-install`;
1673
- const { stdout, stderr } = await execAsync(command, {
1674
- maxBuffer: 10 * 1024 * 1024,
1675
- // 10MB buffer
1676
- cwd: tempDir
1677
- });
1678
- const output = stdout || stderr || "";
1679
- if (output.toLowerCase().includes("error") && !output.includes("successfully")) {
1680
- throw new Error(`Failed to download template: ${output}`);
1681
- }
1682
- return await addBackgroundContext({
1683
- content: [
1684
- {
2083
+ const anonKey = anonResult.accessToken;
2084
+ const rawDir = projectName || `insforge-${frame}`;
2085
+ if (!rawDir || rawDir === "." || rawDir === ".." || /[/\\]/.test(rawDir) || !/^[\w.-]+$/.test(rawDir)) {
2086
+ throw new Error("projectName must be a single directory name using only letters, numbers, hyphens, underscores, and dots");
2087
+ }
2088
+ const targetDir = rawDir;
2089
+ const workspaceBase = await fs.mkdtemp(join(tmpdir(), "insforge-template-"));
2090
+ const templatePath = join(workspaceBase, targetDir);
2091
+ console.error(`[download-template] Target path: ${templatePath}`);
2092
+ await execFileAsync(
2093
+ "npx",
2094
+ ["create-insforge-app", targetDir, "--frame", frame, "--base-url", API_BASE_URL, "--anon-key", anonKey, "--skip-install"],
2095
+ { maxBuffer: 10 * 1024 * 1024, cwd: workspaceBase }
2096
+ );
2097
+ const frameName = frame === "nextjs" ? "Next.js" : "React";
2098
+ return await addBackgroundContext({
2099
+ content: [{
1685
2100
  type: "text",
1686
- text: `\u2705 React template downloaded successfully
2101
+ text: `\u2705 ${frameName} template downloaded successfully
1687
2102
 
1688
2103
  \u{1F4C1} Template Location: ${templatePath}
1689
2104
 
@@ -1695,43 +2110,38 @@ You MUST copy ALL files (INCLUDING HIDDEN FILES like .env, .gitignore, etc.) fro
1695
2110
  Copy all files from: ${templatePath}
1696
2111
  To: Your current project directory
1697
2112
  `
1698
- }
1699
- ]
1700
- });
1701
- } catch (error) {
1702
- const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1703
- return {
1704
- content: [
1705
- {
1706
- type: "text",
1707
- text: `Error downloading template: ${errMsg}`
1708
- }
1709
- ],
1710
- isError: true
1711
- };
1712
- }
1713
- })
1714
- );
2113
+ }]
2114
+ });
2115
+ } catch (error) {
2116
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2117
+ return {
2118
+ content: [{ type: "text", text: `Error downloading template: ${errMsg}` }],
2119
+ isError: true
2120
+ };
2121
+ }
2122
+ })
2123
+ );
2124
+ }
1715
2125
  registerTool(
1716
2126
  "bulk-upsert",
1717
2127
  "Bulk insert or update data from CSV or JSON file. Supports upsert operations with a unique key.",
1718
2128
  {
1719
- apiKey: z23.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
2129
+ apiKey: z26.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
1720
2130
  ...bulkUpsertRequestSchema.shape,
1721
- filePath: z23.string().describe("Path to CSV or JSON file containing data to import")
2131
+ filePath: z26.string().describe("Path to CSV or JSON file containing data to import")
1722
2132
  },
1723
2133
  withUsageTracking("bulk-upsert", async ({ apiKey, table, filePath, upsertKey }) => {
1724
2134
  try {
1725
2135
  const actualApiKey = getApiKey(apiKey);
1726
2136
  const fileBuffer = await fs.readFile(filePath);
1727
- const fileName = filePath.split("/").pop() || "data.csv";
2137
+ const fileName = basename(filePath) || "data.csv";
1728
2138
  const formData = new FormData();
1729
2139
  formData.append("file", fileBuffer, fileName);
1730
2140
  formData.append("table", table);
1731
2141
  if (upsertKey) {
1732
2142
  formData.append("upsertKey", upsertKey);
1733
2143
  }
1734
- const response = await fetch2(`${API_BASE_URL}/api/database/advance/bulk-upsert`, {
2144
+ const response = await fetch3(`${API_BASE_URL}/api/database/advance/bulk-upsert`, {
1735
2145
  method: "POST",
1736
2146
  headers: {
1737
2147
  "x-api-key": actualApiKey,
@@ -1739,47 +2149,50 @@ To: Your current project directory
1739
2149
  },
1740
2150
  body: formData
1741
2151
  });
1742
- const result = await handleApiResponse(response);
1743
- const message = result.success ? `Successfully processed ${result.rowsAffected} of ${result.totalRecords} records into table "${result.table}"` : result.message || "Bulk upsert operation completed";
2152
+ const rawResult = await handleApiResponse(response);
2153
+ if (!isBulkUpsertApiResult(rawResult)) {
2154
+ throw new Error("Unexpected response format from bulk-upsert endpoint");
2155
+ }
2156
+ const message = rawResult.success ? `Successfully processed ${rawResult.rowsAffected} of ${rawResult.totalRecords} records into table "${rawResult.table}"` : rawResult.message || "Bulk upsert operation completed";
1744
2157
  return await addBackgroundContext({
1745
- content: [
1746
- {
1747
- type: "text",
1748
- text: formatSuccessMessage("Bulk upsert completed", {
1749
- message,
1750
- table: result.table,
1751
- rowsAffected: result.rowsAffected,
1752
- totalRecords: result.totalRecords,
1753
- errors: result.errors
1754
- })
1755
- }
1756
- ]
2158
+ content: [{
2159
+ type: "text",
2160
+ text: formatSuccessMessage("Bulk upsert completed", {
2161
+ message,
2162
+ table: rawResult.table,
2163
+ rowsAffected: rawResult.rowsAffected,
2164
+ totalRecords: rawResult.totalRecords,
2165
+ errors: rawResult.errors
2166
+ })
2167
+ }]
1757
2168
  });
1758
2169
  } catch (error) {
1759
2170
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1760
2171
  return {
1761
- content: [
1762
- {
1763
- type: "text",
1764
- text: `Error performing bulk upsert: ${errMsg}`
1765
- }
1766
- ],
2172
+ content: [{ type: "text", text: `Error performing bulk upsert: ${errMsg}` }],
1767
2173
  isError: true
1768
2174
  };
1769
2175
  }
1770
2176
  })
1771
2177
  );
2178
+ }
2179
+
2180
+ // src/shared/tools/storage.ts
2181
+ import { z as z27 } from "zod";
2182
+ import fetch4 from "node-fetch";
2183
+ function registerStorageTools(ctx) {
2184
+ const { API_BASE_URL, registerTool, withUsageTracking, getApiKey, addBackgroundContext } = ctx;
1772
2185
  registerTool(
1773
2186
  "create-bucket",
1774
2187
  "Create new storage bucket",
1775
2188
  {
1776
- apiKey: z23.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
2189
+ apiKey: z27.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
1777
2190
  ...createBucketRequestSchema.shape
1778
2191
  },
1779
2192
  withUsageTracking("create-bucket", async ({ apiKey, bucketName, isPublic }) => {
1780
2193
  try {
1781
2194
  const actualApiKey = getApiKey(apiKey);
1782
- const response = await fetch2(`${API_BASE_URL}/api/storage/buckets`, {
2195
+ const response = await fetch4(`${API_BASE_URL}/api/storage/buckets`, {
1783
2196
  method: "POST",
1784
2197
  headers: {
1785
2198
  "x-api-key": actualApiKey,
@@ -1789,22 +2202,12 @@ To: Your current project directory
1789
2202
  });
1790
2203
  const result = await handleApiResponse(response);
1791
2204
  return await addBackgroundContext({
1792
- content: [
1793
- {
1794
- type: "text",
1795
- text: formatSuccessMessage("Bucket created", result)
1796
- }
1797
- ]
2205
+ content: [{ type: "text", text: formatSuccessMessage("Bucket created", result) }]
1798
2206
  });
1799
2207
  } catch (error) {
1800
2208
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1801
2209
  return {
1802
- content: [
1803
- {
1804
- type: "text",
1805
- text: `Error creating bucket: ${errMsg}`
1806
- }
1807
- ],
2210
+ content: [{ type: "text", text: `Error creating bucket: ${errMsg}` }],
1808
2211
  isError: true
1809
2212
  };
1810
2213
  }
@@ -1813,33 +2216,23 @@ To: Your current project directory
1813
2216
  registerTool(
1814
2217
  "list-buckets",
1815
2218
  "Lists all storage buckets",
1816
- {},
1817
- withUsageTracking("list-buckets", async () => {
2219
+ {
2220
+ apiKey: z27.string().optional().describe("API key for authentication (optional if provided via --api_key)")
2221
+ },
2222
+ withUsageTracking("list-buckets", async ({ apiKey }) => {
1818
2223
  try {
1819
- const response = await fetch2(`${API_BASE_URL}/api/storage/buckets`, {
2224
+ const response = await fetch4(`${API_BASE_URL}/api/storage/buckets`, {
1820
2225
  method: "GET",
1821
- headers: {
1822
- "x-api-key": getApiKey()
1823
- }
2226
+ headers: { "x-api-key": getApiKey(apiKey) }
1824
2227
  });
1825
2228
  const result = await handleApiResponse(response);
1826
2229
  return await addBackgroundContext({
1827
- content: [
1828
- {
1829
- type: "text",
1830
- text: formatSuccessMessage("Buckets retrieved", result)
1831
- }
1832
- ]
2230
+ content: [{ type: "text", text: formatSuccessMessage("Buckets retrieved", result) }]
1833
2231
  });
1834
2232
  } catch (error) {
1835
2233
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1836
2234
  return {
1837
- content: [
1838
- {
1839
- type: "text",
1840
- text: `Error listing buckets: ${errMsg}`
1841
- }
1842
- ],
2235
+ content: [{ type: "text", text: `Error listing buckets: ${errMsg}` }],
1843
2236
  isError: true
1844
2237
  };
1845
2238
  }
@@ -1849,541 +2242,736 @@ To: Your current project directory
1849
2242
  "delete-bucket",
1850
2243
  "Deletes a storage bucket",
1851
2244
  {
1852
- apiKey: z23.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
1853
- bucketName: z23.string().describe("Name of the bucket to delete")
2245
+ apiKey: z27.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
2246
+ // Reuse the same bucket name validation as create-bucket
2247
+ bucketName: createBucketRequestSchema.shape.bucketName
1854
2248
  },
1855
2249
  withUsageTracking("delete-bucket", async ({ apiKey, bucketName }) => {
1856
2250
  try {
1857
2251
  const actualApiKey = getApiKey(apiKey);
1858
- const response = await fetch2(`${API_BASE_URL}/api/storage/buckets/${bucketName}`, {
2252
+ const response = await fetch4(`${API_BASE_URL}/api/storage/buckets/${encodeURIComponent(bucketName)}`, {
1859
2253
  method: "DELETE",
1860
- headers: {
1861
- "x-api-key": actualApiKey
1862
- }
2254
+ headers: { "x-api-key": actualApiKey }
1863
2255
  });
1864
2256
  const result = await handleApiResponse(response);
1865
2257
  return await addBackgroundContext({
1866
- content: [
1867
- {
1868
- type: "text",
1869
- text: formatSuccessMessage("Bucket deleted", result)
1870
- }
1871
- ]
2258
+ content: [{ type: "text", text: formatSuccessMessage("Bucket deleted", result) }]
1872
2259
  });
1873
2260
  } catch (error) {
1874
2261
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1875
2262
  return {
1876
- content: [
1877
- {
1878
- type: "text",
1879
- text: `Error deleting bucket: ${errMsg}`
1880
- }
1881
- ],
2263
+ content: [{ type: "text", text: `Error deleting bucket: ${errMsg}` }],
1882
2264
  isError: true
1883
2265
  };
1884
2266
  }
1885
2267
  })
1886
2268
  );
1887
- registerTool(
1888
- "create-function",
1889
- "Create a new edge function that runs in Deno runtime. The code must be written to a file first for version control",
1890
- {
1891
- ...uploadFunctionRequestSchema.omit({ code: true }).shape,
1892
- codeFile: z23.string().describe(
1893
- "Path to JavaScript file containing the function code. Must export: module.exports = async function(request) { return new Response(...) }"
1894
- )
1895
- },
1896
- withUsageTracking("create-function", async (args) => {
1897
- try {
1898
- let code;
2269
+ }
2270
+
2271
+ // src/shared/tools/functions.ts
2272
+ import { z as z28 } from "zod";
2273
+ import fetch5 from "node-fetch";
2274
+ import { promises as fs2 } from "fs";
2275
+ function registerFunctionTools(ctx) {
2276
+ const { API_BASE_URL, isRemote, registerTool, withUsageTracking, getApiKey, addBackgroundContext } = ctx;
2277
+ if (isRemote) {
2278
+ registerTool(
2279
+ "create-function",
2280
+ "Create a new edge function that runs in Deno runtime",
2281
+ {
2282
+ apiKey: z28.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
2283
+ ...uploadFunctionRequestSchema.omit({ code: true }).shape,
2284
+ code: z28.string().describe(
2285
+ "The function code as a string. Must export: module.exports = async function(request) { return new Response(...) }"
2286
+ )
2287
+ },
2288
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2289
+ withUsageTracking("create-function", async (args) => {
1899
2290
  try {
1900
- code = await fs.readFile(args.codeFile, "utf-8");
1901
- } catch (fileError) {
1902
- throw new Error(
1903
- `Failed to read code file '${args.codeFile}': ${fileError instanceof Error ? fileError.message : "Unknown error"}`
1904
- );
2291
+ const response = await fetch5(`${API_BASE_URL}/api/functions`, {
2292
+ method: "POST",
2293
+ headers: {
2294
+ "Content-Type": "application/json",
2295
+ "x-api-key": getApiKey(args.apiKey)
2296
+ },
2297
+ body: JSON.stringify({
2298
+ slug: args.slug,
2299
+ name: args.name,
2300
+ code: args.code,
2301
+ description: args.description,
2302
+ status: args.status
2303
+ })
2304
+ });
2305
+ const result = await handleApiResponse(response);
2306
+ return await addBackgroundContext({
2307
+ content: [{ type: "text", text: formatSuccessMessage(`Edge function '${args.slug}' created successfully`, result) }]
2308
+ });
2309
+ } catch (error) {
2310
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2311
+ return {
2312
+ content: [{ type: "text", text: `Error creating function: ${errMsg}` }],
2313
+ isError: true
2314
+ };
1905
2315
  }
1906
- const response = await fetch2(`${API_BASE_URL}/api/functions`, {
1907
- method: "POST",
1908
- headers: {
1909
- "Content-Type": "application/json",
1910
- "x-api-key": getApiKey()
1911
- },
1912
- body: JSON.stringify({
1913
- slug: args.slug,
1914
- name: args.name,
1915
- code,
1916
- description: args.description,
1917
- status: args.status
1918
- })
1919
- });
1920
- const result = await handleApiResponse(response);
1921
- return await addBackgroundContext({
1922
- content: [
1923
- {
1924
- type: "text",
1925
- text: formatSuccessMessage(
1926
- `Edge function '${args.slug}' created successfully from ${args.codeFile}`,
1927
- result
1928
- )
1929
- }
1930
- ]
1931
- });
1932
- } catch (error) {
1933
- const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1934
- return {
1935
- content: [
1936
- {
1937
- type: "text",
1938
- text: `Error creating function: ${errMsg}`
1939
- }
1940
- ],
1941
- isError: true
1942
- };
1943
- }
1944
- })
1945
- );
2316
+ })
2317
+ );
2318
+ } else {
2319
+ registerTool(
2320
+ "create-function",
2321
+ "Create a new edge function that runs in Deno runtime. The code must be written to a file first for version control",
2322
+ {
2323
+ apiKey: z28.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
2324
+ ...uploadFunctionRequestSchema.omit({ code: true }).shape,
2325
+ codeFile: z28.string().describe(
2326
+ "Path to JavaScript file containing the function code. Must export: module.exports = async function(request) { return new Response(...) }"
2327
+ )
2328
+ },
2329
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2330
+ withUsageTracking("create-function", async (args) => {
2331
+ try {
2332
+ let code;
2333
+ try {
2334
+ code = await fs2.readFile(args.codeFile, "utf-8");
2335
+ } catch (fileError) {
2336
+ throw new Error(
2337
+ `Failed to read code file '${args.codeFile}': ${fileError instanceof Error ? fileError.message : "Unknown error"}`,
2338
+ { cause: fileError }
2339
+ );
2340
+ }
2341
+ const response = await fetch5(`${API_BASE_URL}/api/functions`, {
2342
+ method: "POST",
2343
+ headers: {
2344
+ "Content-Type": "application/json",
2345
+ "x-api-key": getApiKey(args.apiKey)
2346
+ },
2347
+ body: JSON.stringify({
2348
+ slug: args.slug,
2349
+ name: args.name,
2350
+ code,
2351
+ description: args.description,
2352
+ status: args.status
2353
+ })
2354
+ });
2355
+ const result = await handleApiResponse(response);
2356
+ return await addBackgroundContext({
2357
+ content: [{ type: "text", text: formatSuccessMessage(`Edge function '${args.slug}' created successfully from ${args.codeFile}`, result) }]
2358
+ });
2359
+ } catch (error) {
2360
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2361
+ return {
2362
+ content: [{ type: "text", text: `Error creating function: ${errMsg}` }],
2363
+ isError: true
2364
+ };
2365
+ }
2366
+ })
2367
+ );
2368
+ }
1946
2369
  registerTool(
1947
2370
  "get-function",
1948
2371
  "Get details of a specific edge function including its code",
1949
2372
  {
1950
- slug: z23.string().describe("The slug identifier of the function")
2373
+ apiKey: z28.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
2374
+ slug: functionSchema.shape.slug.describe("The slug identifier of the function")
1951
2375
  },
2376
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1952
2377
  withUsageTracking("get-function", async (args) => {
1953
2378
  try {
1954
- const response = await fetch2(`${API_BASE_URL}/api/functions/${args.slug}`, {
2379
+ const response = await fetch5(`${API_BASE_URL}/api/functions/${encodeURIComponent(args.slug)}`, {
1955
2380
  method: "GET",
1956
- headers: {
1957
- "x-api-key": getApiKey()
1958
- }
2381
+ headers: { "x-api-key": getApiKey(args.apiKey) }
1959
2382
  });
1960
2383
  const result = await handleApiResponse(response);
1961
2384
  return await addBackgroundContext({
1962
- content: [
1963
- {
1964
- type: "text",
1965
- text: formatSuccessMessage(`Edge function '${args.slug}' details`, result)
1966
- }
1967
- ]
2385
+ content: [{ type: "text", text: formatSuccessMessage(`Edge function '${args.slug}' details`, result) }]
1968
2386
  });
1969
2387
  } catch (error) {
1970
2388
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1971
2389
  return {
1972
- content: [
1973
- {
1974
- type: "text",
1975
- text: `Error getting function: ${errMsg}`
1976
- }
1977
- ],
2390
+ content: [{ type: "text", text: `Error getting function: ${errMsg}` }],
1978
2391
  isError: true
1979
2392
  };
1980
2393
  }
1981
2394
  })
1982
2395
  );
1983
- registerTool(
1984
- "update-function",
1985
- "Update an existing edge function code or metadata",
1986
- {
1987
- slug: z23.string().describe("The slug identifier of the function to update"),
1988
- ...updateFunctionRequestSchema.omit({ code: true }).shape,
1989
- codeFile: z23.string().optional().describe(
1990
- "Path to JavaScript file containing the new function code. Must export: module.exports = async function(request) { return new Response(...) }"
1991
- )
1992
- },
1993
- withUsageTracking("update-function", async (args) => {
1994
- try {
1995
- const updateData = {};
1996
- if (args.name) {
1997
- updateData.name = args.name;
2396
+ if (isRemote) {
2397
+ registerTool(
2398
+ "update-function",
2399
+ "Update an existing edge function code or metadata",
2400
+ {
2401
+ apiKey: z28.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
2402
+ slug: functionSchema.shape.slug.describe("The slug identifier of the function to update"),
2403
+ ...updateFunctionRequestSchema.omit({ code: true }).shape,
2404
+ code: z28.string().optional().describe(
2405
+ "The new function code as a string. Must export: module.exports = async function(request) { return new Response(...) }"
2406
+ )
2407
+ },
2408
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2409
+ withUsageTracking("update-function", async (args) => {
2410
+ try {
2411
+ const updateData = {};
2412
+ if (args.name) updateData.name = args.name;
2413
+ if (args.code) updateData.code = args.code;
2414
+ if (args.description !== void 0) updateData.description = args.description;
2415
+ if (args.status) updateData.status = args.status;
2416
+ const response = await fetch5(`${API_BASE_URL}/api/functions/${encodeURIComponent(args.slug)}`, {
2417
+ method: "PUT",
2418
+ headers: {
2419
+ "Content-Type": "application/json",
2420
+ "x-api-key": getApiKey(args.apiKey)
2421
+ },
2422
+ body: JSON.stringify(updateData)
2423
+ });
2424
+ const result = await handleApiResponse(response);
2425
+ return await addBackgroundContext({
2426
+ content: [{ type: "text", text: formatSuccessMessage(`Edge function '${args.slug}' updated successfully`, result) }]
2427
+ });
2428
+ } catch (error) {
2429
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2430
+ return {
2431
+ content: [{ type: "text", text: `Error updating function: ${errMsg}` }],
2432
+ isError: true
2433
+ };
1998
2434
  }
1999
- if (args.codeFile) {
2000
- try {
2001
- updateData.code = await fs.readFile(args.codeFile, "utf-8");
2002
- } catch (fileError) {
2003
- throw new Error(
2004
- `Failed to read code file '${args.codeFile}': ${fileError instanceof Error ? fileError.message : "Unknown error"}`
2005
- );
2435
+ })
2436
+ );
2437
+ } else {
2438
+ registerTool(
2439
+ "update-function",
2440
+ "Update an existing edge function code or metadata",
2441
+ {
2442
+ apiKey: z28.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
2443
+ slug: functionSchema.shape.slug.describe("The slug identifier of the function to update"),
2444
+ ...updateFunctionRequestSchema.omit({ code: true }).shape,
2445
+ codeFile: z28.string().optional().describe(
2446
+ "Path to JavaScript file containing the new function code. Must export: module.exports = async function(request) { return new Response(...) }"
2447
+ )
2448
+ },
2449
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2450
+ withUsageTracking("update-function", async (args) => {
2451
+ try {
2452
+ const updateData = {};
2453
+ if (args.name) updateData.name = args.name;
2454
+ if (args.codeFile) {
2455
+ try {
2456
+ updateData.code = await fs2.readFile(args.codeFile, "utf-8");
2457
+ } catch (fileError) {
2458
+ throw new Error(
2459
+ `Failed to read code file '${args.codeFile}': ${fileError instanceof Error ? fileError.message : "Unknown error"}`,
2460
+ { cause: fileError }
2461
+ );
2462
+ }
2006
2463
  }
2464
+ if (args.description !== void 0) updateData.description = args.description;
2465
+ if (args.status) updateData.status = args.status;
2466
+ const response = await fetch5(`${API_BASE_URL}/api/functions/${encodeURIComponent(args.slug)}`, {
2467
+ method: "PUT",
2468
+ headers: {
2469
+ "Content-Type": "application/json",
2470
+ "x-api-key": getApiKey(args.apiKey)
2471
+ },
2472
+ body: JSON.stringify(updateData)
2473
+ });
2474
+ const result = await handleApiResponse(response);
2475
+ const fileInfo = args.codeFile ? ` from ${args.codeFile}` : "";
2476
+ return await addBackgroundContext({
2477
+ content: [{ type: "text", text: formatSuccessMessage(`Edge function '${args.slug}' updated successfully${fileInfo}`, result) }]
2478
+ });
2479
+ } catch (error) {
2480
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2481
+ return {
2482
+ content: [{ type: "text", text: `Error updating function: ${errMsg}` }],
2483
+ isError: true
2484
+ };
2007
2485
  }
2008
- if (args.description !== void 0) {
2009
- updateData.description = args.description;
2010
- }
2011
- if (args.status) {
2012
- updateData.status = args.status;
2013
- }
2014
- const response = await fetch2(`${API_BASE_URL}/api/functions/${args.slug}`, {
2015
- method: "PUT",
2016
- headers: {
2017
- "Content-Type": "application/json",
2018
- "x-api-key": getApiKey()
2019
- },
2020
- body: JSON.stringify(updateData)
2021
- });
2022
- const result = await handleApiResponse(response);
2023
- const fileInfo = args.codeFile ? ` from ${args.codeFile}` : "";
2024
- return await addBackgroundContext({
2025
- content: [
2026
- {
2027
- type: "text",
2028
- text: formatSuccessMessage(
2029
- `Edge function '${args.slug}' updated successfully${fileInfo}`,
2030
- result
2031
- )
2032
- }
2033
- ]
2034
- });
2035
- } catch (error) {
2036
- const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2037
- return {
2038
- content: [
2039
- {
2040
- type: "text",
2041
- text: `Error updating function: ${errMsg}`
2042
- }
2043
- ],
2044
- isError: true
2045
- };
2046
- }
2047
- })
2048
- );
2486
+ })
2487
+ );
2488
+ }
2049
2489
  registerTool(
2050
2490
  "delete-function",
2051
2491
  "Delete an edge function permanently",
2052
2492
  {
2053
- slug: z23.string().describe("The slug identifier of the function to delete")
2493
+ apiKey: z28.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
2494
+ slug: functionSchema.shape.slug.describe("The slug identifier of the function to delete")
2054
2495
  },
2496
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2055
2497
  withUsageTracking("delete-function", async (args) => {
2056
2498
  try {
2057
- const response = await fetch2(`${API_BASE_URL}/api/functions/${args.slug}`, {
2499
+ const response = await fetch5(`${API_BASE_URL}/api/functions/${encodeURIComponent(args.slug)}`, {
2058
2500
  method: "DELETE",
2059
- headers: {
2060
- "x-api-key": getApiKey()
2061
- }
2501
+ headers: { "x-api-key": getApiKey(args.apiKey) }
2062
2502
  });
2063
2503
  const result = await handleApiResponse(response);
2064
2504
  return await addBackgroundContext({
2065
- content: [
2066
- {
2067
- type: "text",
2068
- text: formatSuccessMessage(`Edge function '${args.slug}' deleted successfully`, result)
2069
- }
2070
- ]
2505
+ content: [{ type: "text", text: formatSuccessMessage(`Edge function '${args.slug}' deleted successfully`, result) }]
2071
2506
  });
2072
2507
  } catch (error) {
2073
2508
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2074
2509
  return {
2075
- content: [
2076
- {
2077
- type: "text",
2078
- text: `Error deleting function: ${errMsg}`
2079
- }
2080
- ],
2510
+ content: [{ type: "text", text: `Error deleting function: ${errMsg}` }],
2081
2511
  isError: true
2082
2512
  };
2083
2513
  }
2084
2514
  })
2085
2515
  );
2516
+ }
2517
+
2518
+ // src/shared/tools/deployment.ts
2519
+ import { z as z29 } from "zod";
2520
+ import fetch6 from "node-fetch";
2521
+ import { promises as fs3, createWriteStream, createReadStream } from "fs";
2522
+ import { tmpdir as tmpdir2 } from "os";
2523
+ import { join as join2 } from "path";
2524
+ import archiver from "archiver";
2525
+ import FormData2 from "form-data";
2526
+ function isCreateDeploymentResponse(obj) {
2527
+ if (typeof obj !== "object" || obj === null) {
2528
+ return false;
2529
+ }
2530
+ const value = obj;
2531
+ const idOk = "id" in value && (typeof value.id === "string" || typeof value.id === "number");
2532
+ const urlOk = "uploadUrl" in value && typeof value.uploadUrl === "string" && value.uploadUrl.length > 0;
2533
+ const fieldsOk = "uploadFields" in value && typeof value.uploadFields === "object" && value.uploadFields !== null;
2534
+ return idOk && urlOk && fieldsOk;
2535
+ }
2536
+ function registerDeploymentTools(ctx) {
2537
+ const { API_BASE_URL, isRemote, registerTool, withUsageTracking, getApiKey, addBackgroundContext } = ctx;
2086
2538
  registerTool(
2087
2539
  "get-container-logs",
2088
2540
  "Get latest logs from a specific container/service. Use this to help debug problems with your app.",
2089
2541
  {
2090
- apiKey: z23.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
2091
- source: z23.enum(["insforge.logs", "postgREST.logs", "postgres.logs", "function.logs"]).describe("Log source to retrieve"),
2092
- limit: z23.number().optional().default(20).describe("Number of logs to return (default: 20)")
2542
+ apiKey: z29.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
2543
+ source: z29.enum(["insforge.logs", "postgREST.logs", "postgres.logs", "function.logs"]).describe("Log source to retrieve"),
2544
+ limit: z29.number().optional().default(20).describe("Number of logs to return (default: 20)")
2093
2545
  },
2094
2546
  withUsageTracking("get-container-logs", async ({ apiKey, source, limit }) => {
2095
2547
  try {
2096
2548
  const actualApiKey = getApiKey(apiKey);
2097
2549
  const queryParams = new URLSearchParams();
2098
2550
  if (limit) queryParams.append("limit", limit.toString());
2099
- let response = await fetch2(`${API_BASE_URL}/api/logs/${source}?${queryParams}`, {
2551
+ let response = await fetch6(`${API_BASE_URL}/api/logs/${source}?${queryParams}`, {
2100
2552
  method: "GET",
2101
- headers: {
2102
- "x-api-key": actualApiKey
2103
- }
2553
+ headers: { "x-api-key": actualApiKey }
2104
2554
  });
2105
2555
  if (response.status === 404) {
2106
- response = await fetch2(`${API_BASE_URL}/api/logs/analytics/${source}?${queryParams}`, {
2556
+ response = await fetch6(`${API_BASE_URL}/api/logs/analytics/${source}?${queryParams}`, {
2107
2557
  method: "GET",
2108
- headers: {
2109
- "x-api-key": actualApiKey
2110
- }
2558
+ headers: { "x-api-key": actualApiKey }
2111
2559
  });
2112
2560
  }
2113
2561
  const result = await handleApiResponse(response);
2114
2562
  return await addBackgroundContext({
2115
- content: [
2116
- {
2117
- type: "text",
2118
- text: formatSuccessMessage(`Latest logs from ${source}`, result)
2119
- }
2120
- ]
2563
+ content: [{ type: "text", text: formatSuccessMessage(`Latest logs from ${source}`, result) }]
2121
2564
  });
2122
2565
  } catch (error) {
2123
2566
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2124
2567
  return {
2125
- content: [
2126
- {
2127
- type: "text",
2128
- text: `Error retrieving container logs: ${errMsg}`
2129
- }
2130
- ],
2568
+ content: [{ type: "text", text: `Error retrieving container logs: ${errMsg}` }],
2131
2569
  isError: true
2132
2570
  };
2133
2571
  }
2134
2572
  })
2135
2573
  );
2136
- registerTool(
2137
- "create-deployment",
2138
- "Deploy source code from a directory. This tool zips files, uploads to cloud storage, and triggers deployment with optional environment variables and project settings.",
2139
- {
2140
- sourceDirectory: z23.string().describe('Absolute path to the source directory containing files to deploy (e.g., /Users/name/project or C:\\Users\\name\\project). Do not use relative paths like "."'),
2141
- ...startDeploymentRequestSchema.shape
2142
- },
2143
- withUsageTracking("create-deployment", async ({ sourceDirectory, projectSettings, envVars, meta }) => {
2144
- try {
2145
- const isAbsolutePath = sourceDirectory.startsWith("/") || /^[a-zA-Z]:[/\\]/.test(sourceDirectory);
2146
- if (!isAbsolutePath) {
2147
- return {
2148
- content: [
2149
- {
2150
- type: "text",
2151
- text: `Error: sourceDirectory must be an absolute path, not a relative path like "${sourceDirectory}". Please provide the full path to the source directory (e.g., /Users/name/project on macOS/Linux or C:\\Users\\name\\project on Windows).`
2152
- }
2153
- ],
2154
- isError: true
2155
- };
2156
- }
2574
+ if (isRemote) {
2575
+ registerTool(
2576
+ "create-deployment",
2577
+ "Prepare a deployment by creating a presigned upload URL. Returns shell commands for the agent to execute locally: zip the source directory and upload to cloud storage. After uploading, call the start-deployment tool to trigger the build.",
2578
+ {
2579
+ sourceDirectory: z29.string().describe('Absolute path to the source directory containing files to deploy (e.g., /Users/name/project). Do not use relative paths like "."')
2580
+ },
2581
+ withUsageTracking("create-deployment", async ({ sourceDirectory }) => {
2157
2582
  try {
2158
- const stats = await fs.stat(sourceDirectory);
2159
- if (!stats.isDirectory()) {
2583
+ const isAbsolutePath = sourceDirectory.startsWith("/") || /^[a-zA-Z]:[/\\]/.test(sourceDirectory);
2584
+ if (!isAbsolutePath) {
2160
2585
  return {
2161
- content: [
2162
- {
2163
- type: "text",
2164
- text: `Error: "${sourceDirectory}" is not a directory. Please provide a path to a directory containing the source code.`
2165
- }
2166
- ],
2586
+ content: [{
2587
+ type: "text",
2588
+ text: `Error: sourceDirectory must be an absolute path, not a relative path like "${sourceDirectory}". Please provide the full path to the source directory (e.g., /Users/name/project on macOS/Linux or C:\\Users\\name\\project on Windows).`
2589
+ }],
2167
2590
  isError: true
2168
2591
  };
2169
2592
  }
2170
- } catch (statError) {
2171
- return {
2172
- content: [
2173
- {
2174
- type: "text",
2175
- text: `Error: Directory "${sourceDirectory}" does not exist or is not accessible. Please verify the path is correct.`
2176
- }
2177
- ],
2178
- isError: true
2179
- };
2180
- }
2181
- const resolvedSourceDir = sourceDirectory;
2182
- const createResponse = await fetch2(`${API_BASE_URL}/api/deployments`, {
2183
- method: "POST",
2184
- headers: {
2185
- "x-api-key": getApiKey(),
2186
- "Content-Type": "application/json"
2187
- }
2188
- });
2189
- const createResult = await handleApiResponse(createResponse);
2190
- const { id: deploymentId, uploadUrl, uploadFields } = createResult;
2191
- const zipBuffer = await new Promise((resolve, reject) => {
2192
- const archive = archiver("zip", { zlib: { level: 9 } });
2193
- const chunks = [];
2194
- archive.on("data", (chunk) => chunks.push(chunk));
2195
- archive.on("end", () => resolve(Buffer.concat(chunks)));
2196
- archive.on("error", (err) => reject(err));
2197
- const excludePatterns = [
2198
- "node_modules",
2199
- ".git",
2200
- ".next",
2201
- ".env",
2202
- ".env.local",
2203
- "dist",
2204
- "build",
2205
- ".DS_Store"
2206
- ];
2207
- archive.directory(resolvedSourceDir, false, (entry) => {
2208
- const normalizedName = entry.name.replace(/\\/g, "/");
2209
- for (const pattern of excludePatterns) {
2210
- if (normalizedName.startsWith(pattern + "/") || normalizedName === pattern || normalizedName.endsWith("/" + pattern) || normalizedName.includes("/" + pattern + "/")) {
2211
- return false;
2212
- }
2213
- }
2214
- if (normalizedName.endsWith(".log")) {
2215
- return false;
2593
+ const createResponse = await fetch6(`${API_BASE_URL}/api/deployments`, {
2594
+ method: "POST",
2595
+ headers: {
2596
+ "x-api-key": getApiKey(),
2597
+ "Content-Type": "application/json"
2216
2598
  }
2217
- return entry;
2218
2599
  });
2219
- archive.finalize();
2220
- });
2221
- const uploadFormData = new FormData();
2222
- for (const [key, value] of Object.entries(uploadFields)) {
2223
- uploadFormData.append(key, value);
2224
- }
2225
- uploadFormData.append("file", zipBuffer, {
2226
- filename: "deployment.zip",
2227
- contentType: "application/zip"
2228
- });
2229
- const uploadResponse = await fetch2(uploadUrl, {
2230
- method: "POST",
2231
- body: uploadFormData,
2232
- headers: uploadFormData.getHeaders()
2233
- });
2234
- if (!uploadResponse.ok) {
2235
- const uploadError = await uploadResponse.text();
2236
- throw new Error(`Failed to upload zip file: ${uploadError}`);
2237
- }
2238
- const startBody = {};
2239
- if (projectSettings) startBody.projectSettings = projectSettings;
2240
- if (envVars) startBody.envVars = envVars;
2241
- if (meta) startBody.meta = meta;
2242
- const startResponse = await fetch2(`${API_BASE_URL}/api/deployments/${deploymentId}/start`, {
2243
- method: "POST",
2244
- headers: {
2245
- "x-api-key": getApiKey(),
2246
- "Content-Type": "application/json"
2247
- },
2248
- body: JSON.stringify(startBody)
2249
- });
2250
- const startResult = await handleApiResponse(startResponse);
2251
- return await addBackgroundContext({
2252
- content: [
2253
- {
2254
- type: "text",
2255
- text: formatSuccessMessage("Deployment started", startResult) + "\n\nNote: You can check deployment status by querying the system.deployments table."
2256
- }
2257
- ]
2258
- });
2259
- } catch (error) {
2260
- const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2261
- return {
2262
- content: [
2263
- {
2264
- type: "text",
2265
- text: `Error creating deployment: ${errMsg}`
2266
- }
2267
- ],
2268
- isError: true
2269
- };
2270
- }
2271
- })
2272
- );
2273
- registerTool(
2274
- "store-conversation",
2275
- `IMPORTANT: Call this tool automatically when a task or conversation is complete to save it for future reference.
2600
+ const createResult = await handleApiResponse(createResponse);
2601
+ if (!isCreateDeploymentResponse(createResult)) {
2602
+ throw new Error("Unexpected response format from deployments endpoint");
2603
+ }
2604
+ const { id: deploymentId, uploadUrl, uploadFields } = createResult;
2605
+ const esc = shellEsc;
2606
+ const curlFields = Object.entries(uploadFields).map(([key, value]) => `-F ${esc(`${key}=${value}`)}`).join(" \\\n ");
2607
+ const escapedDir = esc(sourceDirectory);
2608
+ const tmpZip = `/tmp/insforge-deploy-${deploymentId}.zip`;
2609
+ const instructions = `Deployment prepared successfully. Deployment ID: ${deploymentId}
2276
2610
 
2277
- Store a conversation with messages for semantic similarity search. Messages are embedded using pgvector for later retrieval.
2611
+ Please execute the following commands locally, then call the \`start-deployment\` tool:
2278
2612
 
2279
- Include a descriptive title and relevant metadata (e.g., topic, project, keywords) to improve search results later.`,
2280
- {
2281
- apiKey: z23.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
2282
- title: z23.string().optional().describe("Optional title for the conversation"),
2283
- metadata: z23.record(z23.unknown()).optional().describe("Optional metadata to associate with the conversation"),
2284
- messages: z23.array(
2285
- z23.object({
2286
- role: z23.enum(["user", "assistant", "system", "tool"]).describe("Message role"),
2287
- content: z23.string().describe("Message content"),
2288
- metadata: z23.record(z23.unknown()).optional().describe("Optional message metadata")
2289
- })
2290
- ).min(1).describe("Array of messages to store"),
2291
- embeddingModel: z23.string().optional().describe("Embedding model to use (uses project default if not specified)")
2292
- },
2293
- withUsageTracking(
2294
- "store-conversation",
2295
- async ({ apiKey, title, metadata, messages, embeddingModel }) => {
2613
+ ## Step 1: Zip the source directory
2614
+ \`\`\`bash
2615
+ cd ${escapedDir} && zip -r ${tmpZip} . -x "node_modules/*" ".git/*" ".next/*" ".env" ".env.local" "dist/*" "build/*" ".DS_Store" "*.log"
2616
+ \`\`\`
2617
+
2618
+ ## Step 2: Upload the zip file
2619
+ \`\`\`bash
2620
+ curl -X POST ${esc(uploadUrl)} ${curlFields} -F 'file=@${tmpZip};type=application/zip'
2621
+ \`\`\`
2622
+
2623
+ ## Step 3: Clean up
2624
+ \`\`\`bash
2625
+ rm /tmp/insforge-deploy-${deploymentId}.zip
2626
+ \`\`\`
2627
+
2628
+ ## Step 4: Trigger the build
2629
+ Call the \`start-deployment\` tool with deploymentId: "${deploymentId}"
2630
+
2631
+ Run each step in order. If any step fails, do not proceed to the next step.`;
2632
+ return {
2633
+ content: [{ type: "text", text: instructions }]
2634
+ };
2635
+ } catch (error) {
2636
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2637
+ return {
2638
+ content: [{ type: "text", text: `Error preparing deployment: ${errMsg}` }],
2639
+ isError: true
2640
+ };
2641
+ }
2642
+ })
2643
+ );
2644
+ registerTool(
2645
+ "start-deployment",
2646
+ "Trigger a deployment build after uploading source code. Use this after executing the upload commands from create-deployment.",
2647
+ {
2648
+ deploymentId: z29.string().describe("The deployment ID returned by create-deployment"),
2649
+ ...startDeploymentRequestSchema.shape
2650
+ },
2651
+ withUsageTracking("start-deployment", async ({ deploymentId, projectSettings, envVars, meta }) => {
2296
2652
  try {
2297
- const actualApiKey = getApiKey(apiKey);
2298
- const response = await fetch2(`${API_BASE_URL}/api/memory/conversations`, {
2653
+ const startBody = {};
2654
+ if (projectSettings) startBody.projectSettings = projectSettings;
2655
+ if (envVars) startBody.envVars = envVars;
2656
+ if (meta) startBody.meta = meta;
2657
+ const startResponse = await fetch6(`${API_BASE_URL}/api/deployments/${encodeURIComponent(deploymentId)}/start`, {
2299
2658
  method: "POST",
2300
2659
  headers: {
2301
- "x-api-key": actualApiKey,
2660
+ "x-api-key": getApiKey(),
2302
2661
  "Content-Type": "application/json"
2303
2662
  },
2304
- body: JSON.stringify({ title, metadata, messages, embeddingModel })
2663
+ body: JSON.stringify(startBody)
2305
2664
  });
2306
- const result = await handleApiResponse(response);
2665
+ const startResult = await handleApiResponse(startResponse);
2307
2666
  return await addBackgroundContext({
2308
- content: [
2309
- {
2310
- type: "text",
2311
- text: formatSuccessMessage("Conversation stored successfully", result)
2312
- }
2313
- ]
2667
+ content: [{
2668
+ type: "text",
2669
+ text: formatSuccessMessage("Deployment started", startResult) + "\n\nNote: You can check deployment status by querying the system.deployments table."
2670
+ }]
2314
2671
  });
2315
2672
  } catch (error) {
2316
2673
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2317
2674
  return {
2318
- content: [
2319
- {
2320
- type: "text",
2321
- text: `Error storing conversation: ${errMsg}`
2322
- }
2323
- ],
2675
+ content: [{ type: "text", text: `Error starting deployment: ${errMsg}` }],
2324
2676
  isError: true
2325
2677
  };
2326
2678
  }
2327
- }
2328
- )
2329
- );
2330
- registerTool(
2331
- "search-conversations",
2332
- `IMPORTANT: Call this tool automatically at the START of each new task or conversation to retrieve relevant context from past interactions.
2333
-
2334
- Search for conversations by semantic similarity using pgvector. Returns conversations ranked by relevance to the query.
2335
-
2336
- Use the user's question or task description as the query to find related past conversations that may provide helpful context.`,
2337
- {
2338
- apiKey: z23.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
2339
- query: z23.string().describe("Search query to find similar conversations"),
2340
- limit: z23.number().int().positive().max(100).optional().default(10).describe("Maximum number of results to return (default: 10)"),
2341
- threshold: z23.number().min(0).max(1).optional().default(0).describe("Minimum similarity threshold 0-1 (default: 0)"),
2342
- metadataFilter: z23.record(z23.unknown()).optional().describe("Filter conversations by metadata (JSONB containment)"),
2343
- embeddingModel: z23.string().optional().describe("Embedding model for the query (uses project default if not specified)")
2344
- },
2345
- withUsageTracking(
2346
- "search-conversations",
2347
- async ({ apiKey, query, limit, threshold, metadataFilter, embeddingModel }) => {
2679
+ })
2680
+ );
2681
+ } else {
2682
+ registerTool(
2683
+ "create-deployment",
2684
+ "Deploy source code from a directory. This tool zips files, uploads to cloud storage, and triggers deployment with optional environment variables and project settings.",
2685
+ {
2686
+ sourceDirectory: z29.string().describe('Absolute path to the source directory containing files to deploy (e.g., /Users/name/project or C:\\Users\\name\\project). Do not use relative paths like "."'),
2687
+ ...startDeploymentRequestSchema.shape
2688
+ },
2689
+ withUsageTracking("create-deployment", async ({ sourceDirectory, projectSettings, envVars, meta }) => {
2348
2690
  try {
2349
- const actualApiKey = getApiKey(apiKey);
2350
- const response = await fetch2(`${API_BASE_URL}/api/memory/search`, {
2691
+ const isAbsolutePath = sourceDirectory.startsWith("/") || /^[a-zA-Z]:[/\\]/.test(sourceDirectory);
2692
+ if (!isAbsolutePath) {
2693
+ return {
2694
+ content: [{
2695
+ type: "text",
2696
+ text: `Error: sourceDirectory must be an absolute path, not a relative path like "${sourceDirectory}". Please provide the full path to the source directory (e.g., /Users/name/project on macOS/Linux or C:\\Users\\name\\project on Windows).`
2697
+ }],
2698
+ isError: true
2699
+ };
2700
+ }
2701
+ try {
2702
+ const stats = await fs3.stat(sourceDirectory);
2703
+ if (!stats.isDirectory()) {
2704
+ return {
2705
+ content: [{
2706
+ type: "text",
2707
+ text: `Error: "${sourceDirectory}" is not a directory. Please provide a path to a directory containing the source code.`
2708
+ }],
2709
+ isError: true
2710
+ };
2711
+ }
2712
+ } catch {
2713
+ return {
2714
+ content: [{
2715
+ type: "text",
2716
+ text: `Error: Directory "${sourceDirectory}" does not exist or is not accessible. Please verify the path is correct.`
2717
+ }],
2718
+ isError: true
2719
+ };
2720
+ }
2721
+ const resolvedSourceDir = sourceDirectory;
2722
+ const createResponse = await fetch6(`${API_BASE_URL}/api/deployments`, {
2723
+ method: "POST",
2724
+ headers: {
2725
+ "x-api-key": getApiKey(),
2726
+ "Content-Type": "application/json"
2727
+ }
2728
+ });
2729
+ const createResult = await handleApiResponse(createResponse);
2730
+ if (!isCreateDeploymentResponse(createResult)) {
2731
+ throw new Error("Unexpected response format from deployments endpoint");
2732
+ }
2733
+ const { id: deploymentId, uploadUrl, uploadFields } = createResult;
2734
+ const tmpZipPath = join2(tmpdir2(), `insforge-deploy-${deploymentId}.zip`);
2735
+ try {
2736
+ await new Promise((resolve, reject) => {
2737
+ const archive = archiver("zip", { zlib: { level: 9 } });
2738
+ const output = createWriteStream(tmpZipPath);
2739
+ output.on("close", resolve);
2740
+ output.on("error", reject);
2741
+ archive.on("error", reject);
2742
+ const excludePatterns = ["node_modules", ".git", ".next", ".env", ".env.local", "dist", "build", ".DS_Store"];
2743
+ archive.directory(resolvedSourceDir, false, (entry) => {
2744
+ const normalizedName = entry.name.replace(/\\/g, "/");
2745
+ for (const pattern of excludePatterns) {
2746
+ if (normalizedName.startsWith(pattern + "/") || normalizedName === pattern || normalizedName.endsWith("/" + pattern) || normalizedName.includes("/" + pattern + "/")) {
2747
+ return false;
2748
+ }
2749
+ }
2750
+ if (normalizedName.endsWith(".log")) return false;
2751
+ return entry;
2752
+ });
2753
+ archive.pipe(output);
2754
+ archive.finalize();
2755
+ });
2756
+ const { size: zipSize } = await fs3.stat(tmpZipPath);
2757
+ const uploadFormData = new FormData2();
2758
+ for (const [key, value] of Object.entries(uploadFields)) {
2759
+ uploadFormData.append(key, value);
2760
+ }
2761
+ uploadFormData.append("file", createReadStream(tmpZipPath), {
2762
+ filename: "deployment.zip",
2763
+ contentType: "application/zip",
2764
+ knownLength: zipSize
2765
+ });
2766
+ const uploadResponse = await fetch6(uploadUrl, {
2767
+ method: "POST",
2768
+ body: uploadFormData,
2769
+ headers: uploadFormData.getHeaders()
2770
+ });
2771
+ if (!uploadResponse.ok) {
2772
+ const uploadError = await uploadResponse.text();
2773
+ throw new Error(`Failed to upload zip file: ${uploadError}`);
2774
+ }
2775
+ } finally {
2776
+ await fs3.rm(tmpZipPath, { force: true }).catch(() => void 0);
2777
+ }
2778
+ const startBody = {};
2779
+ if (projectSettings) startBody.projectSettings = projectSettings;
2780
+ if (envVars) startBody.envVars = envVars;
2781
+ if (meta) startBody.meta = meta;
2782
+ const startResponse = await fetch6(`${API_BASE_URL}/api/deployments/${encodeURIComponent(deploymentId)}/start`, {
2351
2783
  method: "POST",
2352
2784
  headers: {
2353
- "x-api-key": actualApiKey,
2785
+ "x-api-key": getApiKey(),
2354
2786
  "Content-Type": "application/json"
2355
2787
  },
2356
- body: JSON.stringify({ query, limit, threshold, metadataFilter, embeddingModel })
2788
+ body: JSON.stringify(startBody)
2357
2789
  });
2358
- const result = await handleApiResponse(response);
2790
+ const startResult = await handleApiResponse(startResponse);
2359
2791
  return await addBackgroundContext({
2360
- content: [
2361
- {
2362
- type: "text",
2363
- text: formatSuccessMessage("Conversation search completed", result)
2364
- }
2365
- ]
2792
+ content: [{
2793
+ type: "text",
2794
+ text: formatSuccessMessage("Deployment started", startResult) + "\n\nNote: You can check deployment status by querying the system.deployments table."
2795
+ }]
2366
2796
  });
2367
2797
  } catch (error) {
2368
2798
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2369
2799
  return {
2370
- content: [
2371
- {
2372
- type: "text",
2373
- text: `Error searching conversations: ${errMsg}`
2374
- }
2375
- ],
2800
+ content: [{ type: "text", text: `Error creating deployment: ${errMsg}` }],
2376
2801
  isError: true
2377
2802
  };
2378
2803
  }
2804
+ })
2805
+ );
2806
+ }
2807
+ }
2808
+
2809
+ // src/shared/tools/index.ts
2810
+ var TOOL_VERSION_REQUIREMENTS = {
2811
+ // Schedule tools - require backend v1.1.1+
2812
+ // 'upsert-schedule': { minVersion: '1.1.1' },
2813
+ // 'delete-schedule': { minVersion: '1.1.1' },
2814
+ // 'get-schedules': { minVersion: '1.1.1' },
2815
+ // 'get-schedule-logs': { minVersion: '1.1.1' },
2816
+ "create-deployment": { minVersion: "1.4.7" },
2817
+ "fetch-sdk-docs": { minVersion: "1.5.1" }
2818
+ // Example of a deprecated tool (uncomment when needed):
2819
+ // 'legacy-tool': { minVersion: '1.0.0', maxVersion: '1.5.0' },
2820
+ };
2821
+ var LOCAL_ONLY_TOOLS = /* @__PURE__ */ new Set([
2822
+ "bulk-upsert"
2823
+ // Requires reading local data file (filePath is required)
2824
+ ]);
2825
+ function compareVersions(v1, v2) {
2826
+ const clean1 = v1.replace(/^v/, "").split("-")[0];
2827
+ const clean2 = v2.replace(/^v/, "").split("-")[0];
2828
+ const parts1 = clean1.split(".").map(Number);
2829
+ const parts2 = clean2.split(".").map(Number);
2830
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
2831
+ const part1 = parts1[i] || 0;
2832
+ const part2 = parts2[i] || 0;
2833
+ if (part1 > part2) return 1;
2834
+ if (part1 < part2) return -1;
2835
+ }
2836
+ return 0;
2837
+ }
2838
+ function shouldRegisterTool(toolName, backendVersion) {
2839
+ const requirement = TOOL_VERSION_REQUIREMENTS[toolName];
2840
+ if (!requirement) return true;
2841
+ const { minVersion, maxVersion } = requirement;
2842
+ if (minVersion && compareVersions(backendVersion, minVersion) < 0) return false;
2843
+ if (maxVersion && compareVersions(backendVersion, maxVersion) > 0) return false;
2844
+ return true;
2845
+ }
2846
+ async function fetchBackendVersion(apiBaseUrl) {
2847
+ const controller = new AbortController();
2848
+ const timeoutId = setTimeout(() => controller.abort(), 1e4);
2849
+ try {
2850
+ const response = await fetch7(`${apiBaseUrl}/api/health`, {
2851
+ method: "GET",
2852
+ headers: { "Content-Type": "application/json" },
2853
+ signal: controller.signal
2854
+ });
2855
+ if (!response.ok) {
2856
+ throw new Error(`Health check failed with status ${response.status}`);
2857
+ }
2858
+ const health = await response.json();
2859
+ if (!health.version || typeof health.version !== "string") {
2860
+ throw new Error("Health check returned invalid version field");
2861
+ }
2862
+ return health.version;
2863
+ } catch (error) {
2864
+ if (error instanceof Error && error.name === "AbortError") {
2865
+ throw new Error(`Health check timed out after 10s \u2014 is the backend running at ${apiBaseUrl}?`, { cause: error });
2866
+ }
2867
+ throw error;
2868
+ } finally {
2869
+ clearTimeout(timeoutId);
2870
+ }
2871
+ }
2872
+ async function registerInsforgeTools(server, config = {}) {
2873
+ const GLOBAL_API_KEY = config.apiKey || process.env.API_KEY || "";
2874
+ const API_BASE_URL = config.apiBaseUrl || process.env.API_BASE_URL || "http://localhost:7130";
2875
+ const isRemote = config.mode === "remote";
2876
+ const usageTracker = new UsageTracker(API_BASE_URL, GLOBAL_API_KEY);
2877
+ let backendVersion;
2878
+ try {
2879
+ backendVersion = await fetchBackendVersion(API_BASE_URL);
2880
+ console.error(`Backend version: ${backendVersion}`);
2881
+ } catch (error) {
2882
+ const msg = error instanceof Error ? error.message : String(error);
2883
+ console.error(`Failed to fetch backend version: ${msg}`);
2884
+ throw new Error(`Cannot initialize tools: backend at ${API_BASE_URL} is unreachable. ${msg}`, { cause: error });
2885
+ }
2886
+ let toolCount = 0;
2887
+ const registerTool = (toolName, ...args) => {
2888
+ if (isRemote && LOCAL_ONLY_TOOLS.has(toolName)) {
2889
+ console.error(`Skipping tool '${toolName}': requires local filesystem (remote mode)`);
2890
+ return false;
2891
+ }
2892
+ if (shouldRegisterTool(toolName, backendVersion)) {
2893
+ server.tool(toolName, ...args);
2894
+ toolCount++;
2895
+ return true;
2896
+ } else {
2897
+ const req = TOOL_VERSION_REQUIREMENTS[toolName];
2898
+ const reason = req?.minVersion && compareVersions(backendVersion, req.minVersion) < 0 ? `requires backend >= ${req.minVersion}` : `deprecated after backend ${req?.maxVersion}`;
2899
+ console.error(`Skipping tool '${toolName}': ${reason} (current: ${backendVersion})`);
2900
+ return false;
2901
+ }
2902
+ };
2903
+ async function trackToolUsage(toolName, success = true) {
2904
+ if (GLOBAL_API_KEY) {
2905
+ await usageTracker.trackUsage(toolName, success).catch((err) => {
2906
+ console.error(`Failed to track usage for '${toolName}':`, err);
2907
+ });
2908
+ }
2909
+ }
2910
+ function withUsageTracking(toolName, handler) {
2911
+ return async (...args) => {
2912
+ try {
2913
+ const result = await handler(...args);
2914
+ const isStructuredError = result !== null && typeof result === "object" && "isError" in result && result["isError"] === true;
2915
+ void trackToolUsage(toolName, !isStructuredError);
2916
+ return result;
2917
+ } catch (error) {
2918
+ void trackToolUsage(toolName, false);
2919
+ throw error;
2920
+ }
2921
+ };
2922
+ }
2923
+ const getApiKey = (toolApiKey) => {
2924
+ const apiKey = toolApiKey?.trim() || GLOBAL_API_KEY;
2925
+ if (!apiKey) {
2926
+ throw new Error("API key is required. Pass --api_key when starting the MCP server.");
2927
+ }
2928
+ return apiKey;
2929
+ };
2930
+ const addBackgroundContext = async (response) => {
2931
+ const isLegacyVersion = compareVersions(backendVersion, "1.1.7") < 0;
2932
+ if (isLegacyVersion) {
2933
+ try {
2934
+ const docResponse = await fetch7(`${API_BASE_URL}/api/docs/instructions`, {
2935
+ method: "GET",
2936
+ headers: { "Content-Type": "application/json" }
2937
+ });
2938
+ if (docResponse.ok) {
2939
+ const result = await handleApiResponse(docResponse);
2940
+ if (result && typeof result === "object" && "content" in result) {
2941
+ response.content.push({
2942
+ type: "text",
2943
+ text: `
2944
+
2945
+ ---
2946
+ \u{1F527} INSFORGE DEVELOPMENT RULES (Auto-loaded):
2947
+ ${result.content}`
2948
+ });
2949
+ }
2950
+ }
2951
+ } catch (error) {
2952
+ console.error("Failed to fetch insforge-instructions.md:", error);
2379
2953
  }
2380
- )
2381
- );
2954
+ }
2955
+ return response;
2956
+ };
2957
+ const ctx = {
2958
+ API_BASE_URL,
2959
+ isRemote,
2960
+ registerTool,
2961
+ withUsageTracking,
2962
+ getApiKey,
2963
+ addBackgroundContext
2964
+ };
2965
+ registerDocsTools(ctx);
2966
+ registerDatabaseTools(ctx);
2967
+ registerStorageTools(ctx);
2968
+ registerFunctionTools(ctx);
2969
+ registerDeploymentTools(ctx);
2382
2970
  return {
2383
2971
  apiKey: GLOBAL_API_KEY,
2384
2972
  apiBaseUrl: API_BASE_URL,
2385
- toolCount,
2386
- backendVersion
2973
+ backendVersion,
2974
+ toolCount
2387
2975
  };
2388
2976
  }
2389
2977