@insforge/mcp 1.2.6 → 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,110 +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
- "fetch-sdk-docs": { minVersion: "1.5.1" }
1310
- // Example of a deprecated tool (uncomment when needed):
1311
- // 'legacy-tool': { minVersion: '1.0.0', maxVersion: '1.5.0' },
1312
- };
1313
- function compareVersions(v1, v2) {
1314
- const clean1 = v1.replace(/^v/, "").split("-")[0];
1315
- const clean2 = v2.replace(/^v/, "").split("-")[0];
1316
- const parts1 = clean1.split(".").map(Number);
1317
- const parts2 = clean2.split(".").map(Number);
1318
- for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
1319
- const part1 = parts1[i] || 0;
1320
- const part2 = parts2[i] || 0;
1321
- if (part1 > part2) return 1;
1322
- if (part1 < part2) return -1;
1323
- }
1324
- return 0;
1325
- }
1326
- function shouldRegisterTool(toolName, backendVersion) {
1327
- const requirement = TOOL_VERSION_REQUIREMENTS[toolName];
1328
- if (!requirement) {
1329
- return true;
1330
- }
1331
- const { minVersion, maxVersion } = requirement;
1332
- if (minVersion && compareVersions(backendVersion, minVersion) < 0) {
1333
- return false;
1334
- }
1335
- if (maxVersion && compareVersions(backendVersion, maxVersion) > 0) {
1336
- return false;
1337
- }
1338
- return true;
1339
- }
1340
- async function fetchBackendVersion(apiBaseUrl) {
1341
- const response = await fetch2(`${apiBaseUrl}/api/health`, {
1342
- method: "GET",
1343
- headers: {
1344
- "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;
1345
1629
  }
1630
+ firstSeenByKey.set(envVar.key, index);
1346
1631
  });
1347
- if (!response.ok) {
1348
- throw new Error(`Health check failed with status ${response.status}`);
1349
- }
1350
- const health = await response.json();
1351
- return health.version;
1352
- }
1353
- async function registerInsforgeTools(server, config = {}) {
1354
- const GLOBAL_API_KEY = config.apiKey || process.env.API_KEY || "";
1355
- const API_BASE_URL = config.apiBaseUrl || process.env.API_BASE_URL || "http://localhost:7130";
1356
- const usageTracker = new UsageTracker(API_BASE_URL, GLOBAL_API_KEY);
1357
- const backendVersion = await fetchBackendVersion(API_BASE_URL);
1358
- console.error(`Backend version: ${backendVersion}`);
1359
- let toolCount = 0;
1360
- const registerTool = (toolName, ...args) => {
1361
- if (shouldRegisterTool(toolName, backendVersion)) {
1362
- server.tool(toolName, ...args);
1363
- toolCount++;
1364
- return true;
1365
- } else {
1366
- const req = TOOL_VERSION_REQUIREMENTS[toolName];
1367
- const reason = req?.minVersion && compareVersions(backendVersion, req.minVersion) < 0 ? `requires backend >= ${req.minVersion}` : `deprecated after backend ${req?.maxVersion}`;
1368
- console.error(`Skipping tool '${toolName}': ${reason} (current: ${backendVersion})`);
1369
- return false;
1370
- }
1371
- };
1372
- async function trackToolUsage(toolName, success = true) {
1373
- if (GLOBAL_API_KEY) {
1374
- await usageTracker.trackUsage(toolName, success);
1375
- }
1376
- }
1377
- function withUsageTracking(toolName, handler) {
1378
- return async (...args) => {
1379
- try {
1380
- const result = await handler(...args);
1381
- await trackToolUsage(toolName, true);
1382
- return result;
1383
- } catch (error) {
1384
- await trackToolUsage(toolName, false);
1385
- throw error;
1386
- }
1387
- };
1388
- }
1389
- const getApiKey = (_toolApiKey) => {
1390
- if (!GLOBAL_API_KEY) {
1391
- throw new Error("API key is required. Pass --api_key when starting the MCP server.");
1392
- }
1393
- return GLOBAL_API_KEY;
1394
- };
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;
1395
1762
  const fetchDocumentation = async (docType) => {
1396
1763
  try {
1397
1764
  const response = await fetch2(`${API_BASE_URL}/api/docs/${docType}`, {
1398
1765
  method: "GET",
1399
- headers: {
1400
- "Content-Type": "application/json"
1401
- }
1766
+ headers: { "Content-Type": "application/json" }
1402
1767
  });
1403
1768
  if (response.status === 404) {
1404
1769
  throw new Error("Documentation not found. This feature may not be supported in your project version. Please contact the Insforge team for assistance.");
@@ -1414,16 +1779,14 @@ async function registerInsforgeTools(server, config = {}) {
1414
1779
  throw new Error("Invalid response format from documentation endpoint");
1415
1780
  } catch (error) {
1416
1781
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1417
- throw new Error(`Unable to retrieve ${docType} documentation: ${errMsg}`);
1782
+ throw new Error(`Unable to retrieve ${docType} documentation: ${errMsg}`, { cause: error });
1418
1783
  }
1419
1784
  };
1420
1785
  const fetchSDKDocumentation = async (feature, language) => {
1421
1786
  try {
1422
1787
  const response = await fetch2(`${API_BASE_URL}/api/docs/${feature}/${language}`, {
1423
1788
  method: "GET",
1424
- headers: {
1425
- "Content-Type": "application/json"
1426
- }
1789
+ headers: { "Content-Type": "application/json" }
1427
1790
  });
1428
1791
  if (response.status === 404) {
1429
1792
  throw new Error("Documentation not found. This feature may not be supported in your project version. Please contact the Insforge team for assistance.");
@@ -1439,50 +1802,18 @@ async function registerInsforgeTools(server, config = {}) {
1439
1802
  throw new Error("Invalid response format from documentation endpoint");
1440
1803
  } catch (error) {
1441
1804
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1442
- throw new Error(`Unable to retrieve ${feature}-${language} documentation: ${errMsg}`);
1443
- }
1444
- };
1445
- const fetchInsforgeInstructionsContext = async () => {
1446
- try {
1447
- return await fetchDocumentation("instructions");
1448
- } catch (error) {
1449
- console.error("Failed to fetch insforge-instructions.md:", error);
1450
- return null;
1451
- }
1452
- };
1453
- const addBackgroundContext = async (response) => {
1454
- const isLegacyVersion = compareVersions(backendVersion, "1.1.7") < 0;
1455
- if (isLegacyVersion) {
1456
- const context = await fetchInsforgeInstructionsContext();
1457
- if (context && response.content && Array.isArray(response.content)) {
1458
- response.content.push({
1459
- type: "text",
1460
- text: `
1461
-
1462
- ---
1463
- \u{1F527} INSFORGE DEVELOPMENT RULES (Auto-loaded):
1464
- ${context}`
1465
- });
1466
- }
1805
+ throw new Error(`Unable to retrieve ${feature}-${language} documentation: ${errMsg}`, { cause: error });
1467
1806
  }
1468
- return response;
1469
1807
  };
1470
1808
  registerTool(
1471
1809
  "fetch-docs",
1472
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.',
1473
- {
1474
- docType: docTypeSchema
1475
- },
1811
+ { docType: docTypeSchema },
1476
1812
  withUsageTracking("fetch-docs", async ({ docType }) => {
1477
1813
  try {
1478
1814
  const content = await fetchDocumentation(docType);
1479
1815
  return await addBackgroundContext({
1480
- content: [
1481
- {
1482
- type: "text",
1483
- text: content
1484
- }
1485
- ]
1816
+ content: [{ type: "text", text: content }]
1486
1817
  });
1487
1818
  } catch (error) {
1488
1819
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
@@ -1495,7 +1826,8 @@ ${context}`
1495
1826
  };
1496
1827
  }
1497
1828
  return {
1498
- content: [{ type: "text", text: `Error fetching ${docType} documentation: ${errMsg}` }]
1829
+ content: [{ type: "text", text: `Error fetching ${docType} documentation: ${errMsg}` }],
1830
+ isError: true
1499
1831
  };
1500
1832
  }
1501
1833
  })
@@ -1506,20 +1838,12 @@ ${context}`
1506
1838
 
1507
1839
  Supported features: ${sdkFeatureSchema.options.join(", ")}
1508
1840
  Supported languages: ${sdkLanguageSchema.options.join(", ")}`,
1509
- {
1510
- sdkFeature: sdkFeatureSchema,
1511
- sdkLanguage: sdkLanguageSchema
1512
- },
1841
+ { sdkFeature: sdkFeatureSchema, sdkLanguage: sdkLanguageSchema },
1513
1842
  withUsageTracking("fetch-sdk-docs", async ({ sdkFeature, sdkLanguage }) => {
1514
1843
  try {
1515
1844
  const content = await fetchSDKDocumentation(sdkFeature, sdkLanguage);
1516
1845
  return await addBackgroundContext({
1517
- content: [
1518
- {
1519
- type: "text",
1520
- text: content
1521
- }
1522
- ]
1846
+ content: [{ type: "text", text: content }]
1523
1847
  });
1524
1848
  } catch (error) {
1525
1849
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
@@ -1532,7 +1856,8 @@ Supported languages: ${sdkLanguageSchema.options.join(", ")}`,
1532
1856
  };
1533
1857
  }
1534
1858
  return {
1535
- content: [{ type: "text", text: `Error fetching ${sdkFeature}-${sdkLanguage} documentation: ${errMsg}` }]
1859
+ content: [{ type: "text", text: `Error fetching ${sdkFeature}-${sdkLanguage} documentation: ${errMsg}` }],
1860
+ isError: true
1536
1861
  };
1537
1862
  }
1538
1863
  })
@@ -1541,7 +1866,7 @@ Supported languages: ${sdkLanguageSchema.options.join(", ")}`,
1541
1866
  "get-anon-key",
1542
1867
  "Generate an anonymous JWT token that never expires. Requires admin API key. Use this for client-side applications that need public access.",
1543
1868
  {
1544
- 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)")
1545
1870
  },
1546
1871
  withUsageTracking("get-anon-key", async ({ apiKey }) => {
1547
1872
  try {
@@ -1555,61 +1880,74 @@ Supported languages: ${sdkLanguageSchema.options.join(", ")}`,
1555
1880
  });
1556
1881
  const result = await handleApiResponse(response);
1557
1882
  return await addBackgroundContext({
1558
- content: [
1559
- {
1560
- type: "text",
1561
- text: formatSuccessMessage("Anonymous token generated", result)
1562
- }
1563
- ]
1883
+ content: [{ type: "text", text: formatSuccessMessage("Anonymous token generated", result) }]
1564
1884
  });
1565
1885
  } catch (error) {
1566
1886
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1567
1887
  return {
1568
- content: [
1569
- {
1570
- type: "text",
1571
- text: `Error generating anonymous token: ${errMsg}`
1572
- }
1573
- ],
1888
+ content: [{ type: "text", text: `Error generating anonymous token: ${errMsg}` }],
1574
1889
  isError: true
1575
1890
  };
1576
1891
  }
1577
1892
  })
1578
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;
1579
1929
  registerTool(
1580
1930
  "get-table-schema",
1581
1931
  "Returns the detailed schema(including RLS, indexes, constraints, etc.) of a specific table",
1582
1932
  {
1583
- apiKey: z23.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
1584
- 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")
1585
1935
  },
1586
1936
  withUsageTracking("get-table-schema", async ({ apiKey, tableName }) => {
1587
1937
  try {
1588
1938
  const actualApiKey = getApiKey(apiKey);
1589
- const response = await fetch2(`${API_BASE_URL}/api/metadata/${tableName}`, {
1939
+ const response = await fetch3(`${API_BASE_URL}/api/metadata/${encodeURIComponent(tableName)}`, {
1590
1940
  method: "GET",
1591
- headers: {
1592
- "x-api-key": actualApiKey
1593
- }
1941
+ headers: { "x-api-key": actualApiKey }
1594
1942
  });
1595
1943
  const result = await handleApiResponse(response);
1596
1944
  return await addBackgroundContext({
1597
- content: [
1598
- {
1599
- type: "text",
1600
- text: formatSuccessMessage("Schema retrieved", result)
1601
- }
1602
- ]
1945
+ content: [{ type: "text", text: formatSuccessMessage("Schema retrieved", result) }]
1603
1946
  });
1604
1947
  } catch (error) {
1605
1948
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1606
1949
  return {
1607
- content: [
1608
- {
1609
- type: "text",
1610
- text: `Error getting table schema: ${errMsg}`
1611
- }
1612
- ],
1950
+ content: [{ type: "text", text: `Error getting table schema: ${errMsg}` }],
1613
1951
  isError: true
1614
1952
  };
1615
1953
  }
@@ -1619,37 +1957,25 @@ Supported languages: ${sdkLanguageSchema.options.join(", ")}`,
1619
1957
  "get-backend-metadata",
1620
1958
  "Index all backend metadata",
1621
1959
  {
1622
- 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)")
1623
1961
  },
1624
1962
  withUsageTracking("get-backend-metadata", async ({ apiKey }) => {
1625
1963
  try {
1626
1964
  const actualApiKey = getApiKey(apiKey);
1627
- const response = await fetch2(`${API_BASE_URL}/api/metadata?mcp=true`, {
1965
+ const response = await fetch3(`${API_BASE_URL}/api/metadata?mcp=true`, {
1628
1966
  method: "GET",
1629
- headers: {
1630
- "x-api-key": actualApiKey
1631
- }
1967
+ headers: { "x-api-key": actualApiKey }
1632
1968
  });
1633
1969
  const metadata = await handleApiResponse(response);
1634
1970
  return await addBackgroundContext({
1635
- content: [
1636
- {
1637
- type: "text",
1638
- text: `Backend metadata:
1971
+ content: [{ type: "text", text: `Backend metadata:
1639
1972
 
1640
- ${JSON.stringify(metadata, null, 2)}`
1641
- }
1642
- ]
1973
+ ${JSON.stringify(metadata, null, 2)}` }]
1643
1974
  });
1644
1975
  } catch (error) {
1645
1976
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1646
1977
  return {
1647
- content: [
1648
- {
1649
- type: "text",
1650
- text: `Error retrieving backend metadata: ${errMsg}`
1651
- }
1652
- ],
1978
+ content: [{ type: "text", text: `Error retrieving backend metadata: ${errMsg}` }],
1653
1979
  isError: true
1654
1980
  };
1655
1981
  }
@@ -1659,17 +1985,14 @@ ${JSON.stringify(metadata, null, 2)}`
1659
1985
  "run-raw-sql",
1660
1986
  "Execute raw SQL query with optional parameters. Admin access required. Use with caution as it can modify data directly.",
1661
1987
  {
1662
- 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)"),
1663
1989
  ...rawSQLRequestSchema.shape
1664
1990
  },
1665
1991
  withUsageTracking("run-raw-sql", async ({ apiKey, query, params }) => {
1666
1992
  try {
1667
1993
  const actualApiKey = getApiKey(apiKey);
1668
- const requestBody = {
1669
- query,
1670
- params: params || []
1671
- };
1672
- 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`, {
1673
1996
  method: "POST",
1674
1997
  headers: {
1675
1998
  "x-api-key": actualApiKey,
@@ -1679,75 +2002,103 @@ ${JSON.stringify(metadata, null, 2)}`
1679
2002
  });
1680
2003
  const result = await handleApiResponse(response);
1681
2004
  return await addBackgroundContext({
1682
- content: [
1683
- {
1684
- type: "text",
1685
- text: formatSuccessMessage("SQL query executed", result)
1686
- }
1687
- ]
2005
+ content: [{ type: "text", text: formatSuccessMessage("SQL query executed", result) }]
1688
2006
  });
1689
2007
  } catch (error) {
1690
2008
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1691
2009
  return {
1692
- content: [
1693
- {
1694
- type: "text",
1695
- text: `Error executing SQL query: ${errMsg}`
1696
- }
1697
- ],
2010
+ content: [{ type: "text", text: `Error executing SQL query: ${errMsg}` }],
1698
2011
  isError: true
1699
2012
  };
1700
2013
  }
1701
2014
  })
1702
2015
  );
1703
- registerTool(
1704
- "download-template",
1705
- "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.",
1706
- {
1707
- frame: z23.enum(["react", "nextjs"]).describe("Framework to use for the template (support React and Next.js)"),
1708
- projectName: z23.string().optional().describe('Name for the project directory (optional, defaults to "insforge-react")')
1709
- },
1710
- withUsageTracking("download-template", async ({ frame, projectName }) => {
1711
- try {
1712
- const response = await fetch2(`${API_BASE_URL}/api/auth/tokens/anon`, {
1713
- method: "POST",
1714
- headers: {
1715
- "x-api-key": getApiKey(),
1716
- "Content-Type": "application/json"
1717
- }
1718
- });
1719
- const result = await handleApiResponse(response);
1720
- const anonKey = result.accessToken;
1721
- if (!anonKey) {
1722
- throw new Error("Failed to retrieve anon key from backend");
1723
- }
1724
- const tempDir = tmpdir();
1725
- const targetDir = projectName || `insforge-${frame}`;
1726
- const templatePath = `${tempDir}/${targetDir}`;
1727
- console.error(`[download-template] Target path: ${templatePath}`);
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 }) => {
1728
2025
  try {
1729
- const stats = await fs.stat(templatePath);
1730
- if (stats.isDirectory()) {
1731
- console.error(`[download-template] Removing existing template at ${templatePath}`);
1732
- await fs.rm(templatePath, { recursive: true, force: true });
1733
- }
1734
- } catch {
1735
- }
1736
- const command = `npx create-insforge-app ${targetDir} --frame ${frame} --base-url ${API_BASE_URL} --anon-key ${anonKey} --skip-install`;
1737
- const { stdout, stderr } = await execAsync(command, {
1738
- maxBuffer: 10 * 1024 * 1024,
1739
- // 10MB buffer
1740
- cwd: tempDir
1741
- });
1742
- const output = stdout || stderr || "";
1743
- if (output.toLowerCase().includes("error") && !output.includes("successfully")) {
1744
- throw new Error(`Failed to download template: ${output}`);
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");
2036
+ }
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
+ };
1745
2059
  }
1746
- return await addBackgroundContext({
1747
- content: [
1748
- {
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 }) => {
2071
+ try {
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");
2082
+ }
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: [{
1749
2100
  type: "text",
1750
- text: `\u2705 React template downloaded successfully
2101
+ text: `\u2705 ${frameName} template downloaded successfully
1751
2102
 
1752
2103
  \u{1F4C1} Template Location: ${templatePath}
1753
2104
 
@@ -1759,43 +2110,38 @@ You MUST copy ALL files (INCLUDING HIDDEN FILES like .env, .gitignore, etc.) fro
1759
2110
  Copy all files from: ${templatePath}
1760
2111
  To: Your current project directory
1761
2112
  `
1762
- }
1763
- ]
1764
- });
1765
- } catch (error) {
1766
- const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1767
- return {
1768
- content: [
1769
- {
1770
- type: "text",
1771
- text: `Error downloading template: ${errMsg}`
1772
- }
1773
- ],
1774
- isError: true
1775
- };
1776
- }
1777
- })
1778
- );
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
+ }
1779
2125
  registerTool(
1780
2126
  "bulk-upsert",
1781
2127
  "Bulk insert or update data from CSV or JSON file. Supports upsert operations with a unique key.",
1782
2128
  {
1783
- 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)"),
1784
2130
  ...bulkUpsertRequestSchema.shape,
1785
- 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")
1786
2132
  },
1787
2133
  withUsageTracking("bulk-upsert", async ({ apiKey, table, filePath, upsertKey }) => {
1788
2134
  try {
1789
2135
  const actualApiKey = getApiKey(apiKey);
1790
2136
  const fileBuffer = await fs.readFile(filePath);
1791
- const fileName = filePath.split("/").pop() || "data.csv";
2137
+ const fileName = basename(filePath) || "data.csv";
1792
2138
  const formData = new FormData();
1793
2139
  formData.append("file", fileBuffer, fileName);
1794
2140
  formData.append("table", table);
1795
2141
  if (upsertKey) {
1796
2142
  formData.append("upsertKey", upsertKey);
1797
2143
  }
1798
- 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`, {
1799
2145
  method: "POST",
1800
2146
  headers: {
1801
2147
  "x-api-key": actualApiKey,
@@ -1803,47 +2149,50 @@ To: Your current project directory
1803
2149
  },
1804
2150
  body: formData
1805
2151
  });
1806
- const result = await handleApiResponse(response);
1807
- 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";
1808
2157
  return await addBackgroundContext({
1809
- content: [
1810
- {
1811
- type: "text",
1812
- text: formatSuccessMessage("Bulk upsert completed", {
1813
- message,
1814
- table: result.table,
1815
- rowsAffected: result.rowsAffected,
1816
- totalRecords: result.totalRecords,
1817
- errors: result.errors
1818
- })
1819
- }
1820
- ]
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
+ }]
1821
2168
  });
1822
2169
  } catch (error) {
1823
2170
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1824
2171
  return {
1825
- content: [
1826
- {
1827
- type: "text",
1828
- text: `Error performing bulk upsert: ${errMsg}`
1829
- }
1830
- ],
2172
+ content: [{ type: "text", text: `Error performing bulk upsert: ${errMsg}` }],
1831
2173
  isError: true
1832
2174
  };
1833
2175
  }
1834
2176
  })
1835
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;
1836
2185
  registerTool(
1837
2186
  "create-bucket",
1838
2187
  "Create new storage bucket",
1839
2188
  {
1840
- 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)"),
1841
2190
  ...createBucketRequestSchema.shape
1842
2191
  },
1843
2192
  withUsageTracking("create-bucket", async ({ apiKey, bucketName, isPublic }) => {
1844
2193
  try {
1845
2194
  const actualApiKey = getApiKey(apiKey);
1846
- const response = await fetch2(`${API_BASE_URL}/api/storage/buckets`, {
2195
+ const response = await fetch4(`${API_BASE_URL}/api/storage/buckets`, {
1847
2196
  method: "POST",
1848
2197
  headers: {
1849
2198
  "x-api-key": actualApiKey,
@@ -1853,22 +2202,12 @@ To: Your current project directory
1853
2202
  });
1854
2203
  const result = await handleApiResponse(response);
1855
2204
  return await addBackgroundContext({
1856
- content: [
1857
- {
1858
- type: "text",
1859
- text: formatSuccessMessage("Bucket created", result)
1860
- }
1861
- ]
2205
+ content: [{ type: "text", text: formatSuccessMessage("Bucket created", result) }]
1862
2206
  });
1863
2207
  } catch (error) {
1864
2208
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1865
2209
  return {
1866
- content: [
1867
- {
1868
- type: "text",
1869
- text: `Error creating bucket: ${errMsg}`
1870
- }
1871
- ],
2210
+ content: [{ type: "text", text: `Error creating bucket: ${errMsg}` }],
1872
2211
  isError: true
1873
2212
  };
1874
2213
  }
@@ -1877,33 +2216,23 @@ To: Your current project directory
1877
2216
  registerTool(
1878
2217
  "list-buckets",
1879
2218
  "Lists all storage buckets",
1880
- {},
1881
- 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 }) => {
1882
2223
  try {
1883
- const response = await fetch2(`${API_BASE_URL}/api/storage/buckets`, {
2224
+ const response = await fetch4(`${API_BASE_URL}/api/storage/buckets`, {
1884
2225
  method: "GET",
1885
- headers: {
1886
- "x-api-key": getApiKey()
1887
- }
2226
+ headers: { "x-api-key": getApiKey(apiKey) }
1888
2227
  });
1889
2228
  const result = await handleApiResponse(response);
1890
2229
  return await addBackgroundContext({
1891
- content: [
1892
- {
1893
- type: "text",
1894
- text: formatSuccessMessage("Buckets retrieved", result)
1895
- }
1896
- ]
2230
+ content: [{ type: "text", text: formatSuccessMessage("Buckets retrieved", result) }]
1897
2231
  });
1898
2232
  } catch (error) {
1899
2233
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1900
2234
  return {
1901
- content: [
1902
- {
1903
- type: "text",
1904
- text: `Error listing buckets: ${errMsg}`
1905
- }
1906
- ],
2235
+ content: [{ type: "text", text: `Error listing buckets: ${errMsg}` }],
1907
2236
  isError: true
1908
2237
  };
1909
2238
  }
@@ -1913,432 +2242,736 @@ To: Your current project directory
1913
2242
  "delete-bucket",
1914
2243
  "Deletes a storage bucket",
1915
2244
  {
1916
- apiKey: z23.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
1917
- 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
1918
2248
  },
1919
2249
  withUsageTracking("delete-bucket", async ({ apiKey, bucketName }) => {
1920
2250
  try {
1921
2251
  const actualApiKey = getApiKey(apiKey);
1922
- const response = await fetch2(`${API_BASE_URL}/api/storage/buckets/${bucketName}`, {
2252
+ const response = await fetch4(`${API_BASE_URL}/api/storage/buckets/${encodeURIComponent(bucketName)}`, {
1923
2253
  method: "DELETE",
1924
- headers: {
1925
- "x-api-key": actualApiKey
1926
- }
2254
+ headers: { "x-api-key": actualApiKey }
1927
2255
  });
1928
2256
  const result = await handleApiResponse(response);
1929
2257
  return await addBackgroundContext({
1930
- content: [
1931
- {
1932
- type: "text",
1933
- text: formatSuccessMessage("Bucket deleted", result)
1934
- }
1935
- ]
2258
+ content: [{ type: "text", text: formatSuccessMessage("Bucket deleted", result) }]
1936
2259
  });
1937
2260
  } catch (error) {
1938
2261
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1939
2262
  return {
1940
- content: [
1941
- {
1942
- type: "text",
1943
- text: `Error deleting bucket: ${errMsg}`
1944
- }
1945
- ],
2263
+ content: [{ type: "text", text: `Error deleting bucket: ${errMsg}` }],
1946
2264
  isError: true
1947
2265
  };
1948
2266
  }
1949
2267
  })
1950
2268
  );
1951
- registerTool(
1952
- "create-function",
1953
- "Create a new edge function that runs in Deno runtime. The code must be written to a file first for version control",
1954
- {
1955
- ...uploadFunctionRequestSchema.omit({ code: true }).shape,
1956
- codeFile: z23.string().describe(
1957
- "Path to JavaScript file containing the function code. Must export: module.exports = async function(request) { return new Response(...) }"
1958
- )
1959
- },
1960
- withUsageTracking("create-function", async (args) => {
1961
- try {
1962
- 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) => {
1963
2290
  try {
1964
- code = await fs.readFile(args.codeFile, "utf-8");
1965
- } catch (fileError) {
1966
- throw new Error(
1967
- `Failed to read code file '${args.codeFile}': ${fileError instanceof Error ? fileError.message : "Unknown error"}`
1968
- );
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
+ };
1969
2315
  }
1970
- const response = await fetch2(`${API_BASE_URL}/api/functions`, {
1971
- method: "POST",
1972
- headers: {
1973
- "Content-Type": "application/json",
1974
- "x-api-key": getApiKey()
1975
- },
1976
- body: JSON.stringify({
1977
- slug: args.slug,
1978
- name: args.name,
1979
- code,
1980
- description: args.description,
1981
- status: args.status
1982
- })
1983
- });
1984
- const result = await handleApiResponse(response);
1985
- return await addBackgroundContext({
1986
- content: [
1987
- {
1988
- type: "text",
1989
- text: formatSuccessMessage(
1990
- `Edge function '${args.slug}' created successfully from ${args.codeFile}`,
1991
- result
1992
- )
1993
- }
1994
- ]
1995
- });
1996
- } catch (error) {
1997
- const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1998
- return {
1999
- content: [
2000
- {
2001
- type: "text",
2002
- text: `Error creating function: ${errMsg}`
2003
- }
2004
- ],
2005
- isError: true
2006
- };
2007
- }
2008
- })
2009
- );
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
+ }
2010
2369
  registerTool(
2011
2370
  "get-function",
2012
2371
  "Get details of a specific edge function including its code",
2013
2372
  {
2014
- 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")
2015
2375
  },
2376
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2016
2377
  withUsageTracking("get-function", async (args) => {
2017
2378
  try {
2018
- const response = await fetch2(`${API_BASE_URL}/api/functions/${args.slug}`, {
2379
+ const response = await fetch5(`${API_BASE_URL}/api/functions/${encodeURIComponent(args.slug)}`, {
2019
2380
  method: "GET",
2020
- headers: {
2021
- "x-api-key": getApiKey()
2022
- }
2381
+ headers: { "x-api-key": getApiKey(args.apiKey) }
2023
2382
  });
2024
2383
  const result = await handleApiResponse(response);
2025
2384
  return await addBackgroundContext({
2026
- content: [
2027
- {
2028
- type: "text",
2029
- text: formatSuccessMessage(`Edge function '${args.slug}' details`, result)
2030
- }
2031
- ]
2385
+ content: [{ type: "text", text: formatSuccessMessage(`Edge function '${args.slug}' details`, result) }]
2032
2386
  });
2033
2387
  } catch (error) {
2034
2388
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2035
2389
  return {
2036
- content: [
2037
- {
2038
- type: "text",
2039
- text: `Error getting function: ${errMsg}`
2040
- }
2041
- ],
2390
+ content: [{ type: "text", text: `Error getting function: ${errMsg}` }],
2042
2391
  isError: true
2043
2392
  };
2044
2393
  }
2045
2394
  })
2046
2395
  );
2047
- registerTool(
2048
- "update-function",
2049
- "Update an existing edge function code or metadata",
2050
- {
2051
- slug: z23.string().describe("The slug identifier of the function to update"),
2052
- ...updateFunctionRequestSchema.omit({ code: true }).shape,
2053
- codeFile: z23.string().optional().describe(
2054
- "Path to JavaScript file containing the new function code. Must export: module.exports = async function(request) { return new Response(...) }"
2055
- )
2056
- },
2057
- withUsageTracking("update-function", async (args) => {
2058
- try {
2059
- const updateData = {};
2060
- if (args.name) {
2061
- 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
+ };
2062
2434
  }
2063
- if (args.codeFile) {
2064
- try {
2065
- updateData.code = await fs.readFile(args.codeFile, "utf-8");
2066
- } catch (fileError) {
2067
- throw new Error(
2068
- `Failed to read code file '${args.codeFile}': ${fileError instanceof Error ? fileError.message : "Unknown error"}`
2069
- );
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
+ }
2070
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
+ };
2071
2485
  }
2072
- if (args.description !== void 0) {
2073
- updateData.description = args.description;
2074
- }
2075
- if (args.status) {
2076
- updateData.status = args.status;
2077
- }
2078
- const response = await fetch2(`${API_BASE_URL}/api/functions/${args.slug}`, {
2079
- method: "PUT",
2080
- headers: {
2081
- "Content-Type": "application/json",
2082
- "x-api-key": getApiKey()
2083
- },
2084
- body: JSON.stringify(updateData)
2085
- });
2086
- const result = await handleApiResponse(response);
2087
- const fileInfo = args.codeFile ? ` from ${args.codeFile}` : "";
2088
- return await addBackgroundContext({
2089
- content: [
2090
- {
2091
- type: "text",
2092
- text: formatSuccessMessage(
2093
- `Edge function '${args.slug}' updated successfully${fileInfo}`,
2094
- result
2095
- )
2096
- }
2097
- ]
2098
- });
2099
- } catch (error) {
2100
- const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2101
- return {
2102
- content: [
2103
- {
2104
- type: "text",
2105
- text: `Error updating function: ${errMsg}`
2106
- }
2107
- ],
2108
- isError: true
2109
- };
2110
- }
2111
- })
2112
- );
2486
+ })
2487
+ );
2488
+ }
2113
2489
  registerTool(
2114
2490
  "delete-function",
2115
2491
  "Delete an edge function permanently",
2116
2492
  {
2117
- 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")
2118
2495
  },
2496
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2119
2497
  withUsageTracking("delete-function", async (args) => {
2120
2498
  try {
2121
- const response = await fetch2(`${API_BASE_URL}/api/functions/${args.slug}`, {
2499
+ const response = await fetch5(`${API_BASE_URL}/api/functions/${encodeURIComponent(args.slug)}`, {
2122
2500
  method: "DELETE",
2123
- headers: {
2124
- "x-api-key": getApiKey()
2125
- }
2501
+ headers: { "x-api-key": getApiKey(args.apiKey) }
2126
2502
  });
2127
2503
  const result = await handleApiResponse(response);
2128
2504
  return await addBackgroundContext({
2129
- content: [
2130
- {
2131
- type: "text",
2132
- text: formatSuccessMessage(`Edge function '${args.slug}' deleted successfully`, result)
2133
- }
2134
- ]
2505
+ content: [{ type: "text", text: formatSuccessMessage(`Edge function '${args.slug}' deleted successfully`, result) }]
2135
2506
  });
2136
2507
  } catch (error) {
2137
2508
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2138
2509
  return {
2139
- content: [
2140
- {
2141
- type: "text",
2142
- text: `Error deleting function: ${errMsg}`
2143
- }
2144
- ],
2510
+ content: [{ type: "text", text: `Error deleting function: ${errMsg}` }],
2145
2511
  isError: true
2146
2512
  };
2147
2513
  }
2148
2514
  })
2149
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;
2150
2538
  registerTool(
2151
2539
  "get-container-logs",
2152
2540
  "Get latest logs from a specific container/service. Use this to help debug problems with your app.",
2153
2541
  {
2154
- apiKey: z23.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
2155
- source: z23.enum(["insforge.logs", "postgREST.logs", "postgres.logs", "function.logs"]).describe("Log source to retrieve"),
2156
- 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)")
2157
2545
  },
2158
2546
  withUsageTracking("get-container-logs", async ({ apiKey, source, limit }) => {
2159
2547
  try {
2160
2548
  const actualApiKey = getApiKey(apiKey);
2161
2549
  const queryParams = new URLSearchParams();
2162
2550
  if (limit) queryParams.append("limit", limit.toString());
2163
- let response = await fetch2(`${API_BASE_URL}/api/logs/${source}?${queryParams}`, {
2551
+ let response = await fetch6(`${API_BASE_URL}/api/logs/${source}?${queryParams}`, {
2164
2552
  method: "GET",
2165
- headers: {
2166
- "x-api-key": actualApiKey
2167
- }
2553
+ headers: { "x-api-key": actualApiKey }
2168
2554
  });
2169
2555
  if (response.status === 404) {
2170
- response = await fetch2(`${API_BASE_URL}/api/logs/analytics/${source}?${queryParams}`, {
2556
+ response = await fetch6(`${API_BASE_URL}/api/logs/analytics/${source}?${queryParams}`, {
2171
2557
  method: "GET",
2172
- headers: {
2173
- "x-api-key": actualApiKey
2174
- }
2558
+ headers: { "x-api-key": actualApiKey }
2175
2559
  });
2176
2560
  }
2177
2561
  const result = await handleApiResponse(response);
2178
2562
  return await addBackgroundContext({
2179
- content: [
2180
- {
2181
- type: "text",
2182
- text: formatSuccessMessage(`Latest logs from ${source}`, result)
2183
- }
2184
- ]
2563
+ content: [{ type: "text", text: formatSuccessMessage(`Latest logs from ${source}`, result) }]
2185
2564
  });
2186
2565
  } catch (error) {
2187
2566
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2188
2567
  return {
2189
- content: [
2190
- {
2191
- type: "text",
2192
- text: `Error retrieving container logs: ${errMsg}`
2193
- }
2194
- ],
2568
+ content: [{ type: "text", text: `Error retrieving container logs: ${errMsg}` }],
2195
2569
  isError: true
2196
2570
  };
2197
2571
  }
2198
2572
  })
2199
2573
  );
2200
- registerTool(
2201
- "create-deployment",
2202
- "Deploy source code from a directory. This tool zips files, uploads to cloud storage, and triggers deployment with optional environment variables and project settings.",
2203
- {
2204
- 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 "."'),
2205
- ...startDeploymentRequestSchema.shape
2206
- },
2207
- withUsageTracking("create-deployment", async ({ sourceDirectory, projectSettings, envVars, meta }) => {
2208
- try {
2209
- const isAbsolutePath = sourceDirectory.startsWith("/") || /^[a-zA-Z]:[/\\]/.test(sourceDirectory);
2210
- if (!isAbsolutePath) {
2211
- return {
2212
- content: [
2213
- {
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 }) => {
2582
+ try {
2583
+ const isAbsolutePath = sourceDirectory.startsWith("/") || /^[a-zA-Z]:[/\\]/.test(sourceDirectory);
2584
+ if (!isAbsolutePath) {
2585
+ return {
2586
+ content: [{
2214
2587
  type: "text",
2215
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).`
2216
- }
2217
- ],
2589
+ }],
2590
+ isError: true
2591
+ };
2592
+ }
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"
2598
+ }
2599
+ });
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}
2610
+
2611
+ Please execute the following commands locally, then call the \`start-deployment\` tool:
2612
+
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}` }],
2218
2639
  isError: true
2219
2640
  };
2220
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 }) => {
2221
2652
  try {
2222
- const stats = await fs.stat(sourceDirectory);
2223
- if (!stats.isDirectory()) {
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`, {
2658
+ method: "POST",
2659
+ headers: {
2660
+ "x-api-key": getApiKey(),
2661
+ "Content-Type": "application/json"
2662
+ },
2663
+ body: JSON.stringify(startBody)
2664
+ });
2665
+ const startResult = await handleApiResponse(startResponse);
2666
+ return await addBackgroundContext({
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
+ }]
2671
+ });
2672
+ } catch (error) {
2673
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2674
+ return {
2675
+ content: [{ type: "text", text: `Error starting deployment: ${errMsg}` }],
2676
+ isError: true
2677
+ };
2678
+ }
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 }) => {
2690
+ try {
2691
+ const isAbsolutePath = sourceDirectory.startsWith("/") || /^[a-zA-Z]:[/\\]/.test(sourceDirectory);
2692
+ if (!isAbsolutePath) {
2224
2693
  return {
2225
- content: [
2226
- {
2227
- type: "text",
2228
- text: `Error: "${sourceDirectory}" is not a directory. Please provide a path to a directory containing the source code.`
2229
- }
2230
- ],
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
+ }],
2231
2698
  isError: true
2232
2699
  };
2233
2700
  }
2234
- } catch (statError) {
2235
- return {
2236
- content: [
2237
- {
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: [{
2238
2715
  type: "text",
2239
2716
  text: `Error: Directory "${sourceDirectory}" does not exist or is not accessible. Please verify the path is correct.`
2240
- }
2241
- ],
2242
- isError: true
2243
- };
2244
- }
2245
- const resolvedSourceDir = sourceDirectory;
2246
- const createResponse = await fetch2(`${API_BASE_URL}/api/deployments`, {
2247
- method: "POST",
2248
- headers: {
2249
- "x-api-key": getApiKey(),
2250
- "Content-Type": "application/json"
2717
+ }],
2718
+ isError: true
2719
+ };
2251
2720
  }
2252
- });
2253
- const createResult = await handleApiResponse(createResponse);
2254
- const { id: deploymentId, uploadUrl, uploadFields } = createResult;
2255
- const zipBuffer = await new Promise((resolve, reject) => {
2256
- const archive = archiver("zip", { zlib: { level: 9 } });
2257
- const chunks = [];
2258
- archive.on("data", (chunk) => chunks.push(chunk));
2259
- archive.on("end", () => resolve(Buffer.concat(chunks)));
2260
- archive.on("error", (err) => reject(err));
2261
- const excludePatterns = [
2262
- "node_modules",
2263
- ".git",
2264
- ".next",
2265
- ".env",
2266
- ".env.local",
2267
- "dist",
2268
- "build",
2269
- ".DS_Store"
2270
- ];
2271
- archive.directory(resolvedSourceDir, false, (entry) => {
2272
- const normalizedName = entry.name.replace(/\\/g, "/");
2273
- for (const pattern of excludePatterns) {
2274
- if (normalizedName.startsWith(pattern + "/") || normalizedName === pattern || normalizedName.endsWith("/" + pattern) || normalizedName.includes("/" + pattern + "/")) {
2275
- return false;
2276
- }
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"
2277
2727
  }
2278
- if (normalizedName.endsWith(".log")) {
2279
- return false;
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}`);
2280
2774
  }
2281
- return entry;
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`, {
2783
+ method: "POST",
2784
+ headers: {
2785
+ "x-api-key": getApiKey(),
2786
+ "Content-Type": "application/json"
2787
+ },
2788
+ body: JSON.stringify(startBody)
2282
2789
  });
2283
- archive.finalize();
2284
- });
2285
- const uploadFormData = new FormData();
2286
- for (const [key, value] of Object.entries(uploadFields)) {
2287
- uploadFormData.append(key, value);
2288
- }
2289
- uploadFormData.append("file", zipBuffer, {
2290
- filename: "deployment.zip",
2291
- contentType: "application/zip"
2292
- });
2293
- const uploadResponse = await fetch2(uploadUrl, {
2294
- method: "POST",
2295
- body: uploadFormData,
2296
- headers: uploadFormData.getHeaders()
2297
- });
2298
- if (!uploadResponse.ok) {
2299
- const uploadError = await uploadResponse.text();
2300
- throw new Error(`Failed to upload zip file: ${uploadError}`);
2301
- }
2302
- const startBody = {};
2303
- if (projectSettings) startBody.projectSettings = projectSettings;
2304
- if (envVars) startBody.envVars = envVars;
2305
- if (meta) startBody.meta = meta;
2306
- const startResponse = await fetch2(`${API_BASE_URL}/api/deployments/${deploymentId}/start`, {
2307
- method: "POST",
2308
- headers: {
2309
- "x-api-key": getApiKey(),
2310
- "Content-Type": "application/json"
2311
- },
2312
- body: JSON.stringify(startBody)
2313
- });
2314
- const startResult = await handleApiResponse(startResponse);
2315
- return await addBackgroundContext({
2316
- content: [
2317
- {
2790
+ const startResult = await handleApiResponse(startResponse);
2791
+ return await addBackgroundContext({
2792
+ content: [{
2318
2793
  type: "text",
2319
2794
  text: formatSuccessMessage("Deployment started", startResult) + "\n\nNote: You can check deployment status by querying the system.deployments table."
2320
- }
2321
- ]
2322
- });
2795
+ }]
2796
+ });
2797
+ } catch (error) {
2798
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2799
+ return {
2800
+ content: [{ type: "text", text: `Error creating deployment: ${errMsg}` }],
2801
+ isError: true
2802
+ };
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;
2323
2917
  } catch (error) {
2324
- const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
2325
- return {
2326
- content: [
2327
- {
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({
2328
2942
  type: "text",
2329
- text: `Error creating deployment: ${errMsg}`
2330
- }
2331
- ],
2332
- isError: true
2333
- };
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);
2334
2953
  }
2335
- })
2336
- );
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);
2337
2970
  return {
2338
2971
  apiKey: GLOBAL_API_KEY,
2339
2972
  apiBaseUrl: API_BASE_URL,
2340
- toolCount,
2341
- backendVersion
2973
+ backendVersion,
2974
+ toolCount
2342
2975
  };
2343
2976
  }
2344
2977