@insforge/mcp 1.1.7-dev.8 → 1.2.0

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.
@@ -4,6 +4,9 @@
4
4
  import { z as z14 } from "zod";
5
5
  import fetch2 from "node-fetch";
6
6
  import { promises as fs } from "fs";
7
+ import { exec } from "child_process";
8
+ import { promisify } from "util";
9
+ import { tmpdir } from "os";
7
10
 
8
11
  // src/shared/response-handler.ts
9
12
  async function handleApiResponse(response) {
@@ -735,6 +738,7 @@ var functionUpdateRequestSchema = z13.object({
735
738
 
736
739
  // src/shared/tools.ts
737
740
  import FormData from "form-data";
741
+ var execAsync = promisify(exec);
738
742
  var TOOL_VERSION_REQUIREMENTS = {
739
743
  "upsert-schedule": "1.1.1",
740
744
  // 'get-schedules': '1.1.1',
@@ -774,8 +778,10 @@ function registerInsforgeTools(server, config = {}) {
774
778
  }
775
779
  }
776
780
  function compareVersions(v1, v2) {
777
- const parts1 = v1.split(".").map(Number);
778
- const parts2 = v2.split(".").map(Number);
781
+ const clean1 = v1.replace(/^v/, "").split("-")[0];
782
+ const clean2 = v2.replace(/^v/, "").split("-")[0];
783
+ const parts1 = clean1.split(".").map(Number);
784
+ const parts2 = clean2.split(".").map(Number);
779
785
  for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
780
786
  const part1 = parts1[i] || 0;
781
787
  const part2 = parts2[i] || 0;
@@ -834,10 +840,14 @@ function registerInsforgeTools(server, config = {}) {
834
840
  "Content-Type": "application/json"
835
841
  }
836
842
  });
843
+ if (response.status === 404) {
844
+ throw new Error("Documentation not found");
845
+ }
837
846
  const result = await handleApiResponse(response);
838
847
  if (result && typeof result === "object" && "content" in result) {
839
848
  let content = result.content;
840
849
  content = content.replace(/http:\/\/localhost:7130/g, API_BASE_URL);
850
+ content = content.replace(/https:\/\/your-app\.region\.insforge\.app/g, API_BASE_URL);
841
851
  return content;
842
852
  }
843
853
  throw new Error("Invalid response format from documentation endpoint");
@@ -846,27 +856,65 @@ function registerInsforgeTools(server, config = {}) {
846
856
  throw new Error(`Unable to retrieve ${docType} documentation: ${errMsg}`);
847
857
  }
848
858
  };
859
+ const fetchInsforgeInstructionsContext = async () => {
860
+ try {
861
+ return await fetchDocumentation("instructions");
862
+ } catch (error) {
863
+ console.error("Failed to fetch insforge-instructions.md:", error);
864
+ return null;
865
+ }
866
+ };
867
+ const addBackgroundContext = async (response) => {
868
+ try {
869
+ const currentVersion = await getBackendVersion();
870
+ const isLegacyVersion = compareVersions(currentVersion, "1.1.7") < 0;
871
+ if (isLegacyVersion) {
872
+ const context = await fetchInsforgeInstructionsContext();
873
+ if (context && response.content && Array.isArray(response.content)) {
874
+ response.content.push({
875
+ type: "text",
876
+ text: `
877
+
878
+ ---
879
+ \u{1F527} INSFORGE DEVELOPMENT RULES (Auto-loaded):
880
+ ${context}`
881
+ });
882
+ }
883
+ }
884
+ } catch {
885
+ console.warn("Could not determine backend version, skipping background context");
886
+ }
887
+ return response;
888
+ };
849
889
  server.tool(
850
890
  "fetch-docs",
851
891
  'Fetch Insforge documentation. Use "instructions" for essential backend setup (MANDATORY FIRST), or select specific SDK docs for database, auth, storage, functions, or AI integration.',
852
892
  {
853
- docType: z14.enum(["instructions", "db-sdk", "storage-sdk", "functions-sdk", "ai-integration-sdk", "auth-components-nextjs", "auth-components-react"]).describe(
854
- 'Documentation type: "instructions" (essential backend setup - use FIRST), "db-sdk" (database operations), "storage-sdk" (file storage), "functions-sdk" (edge functions), "ai-integration-sdk" (AI features), "auth-components-nextjs" (authentication components for Next.js applications), "auth-components-react" (authentication components for React applications),'
893
+ docType: z14.enum(["instructions", "db-sdk", "storage-sdk", "functions-sdk", "ai-integration-sdk", "auth-components-react"]).describe(
894
+ 'Documentation type: "instructions" (essential backend setup - use FIRST), "db-sdk" (database operations), "storage-sdk" (file storage), "functions-sdk" (edge functions), "ai-integration-sdk" (AI features), "auth-components-react" (authentication components for React+Vite applications).'
855
895
  )
856
896
  },
857
897
  withUsageTracking("fetch-docs", async ({ docType }) => {
858
898
  try {
859
899
  const content = await fetchDocumentation(docType);
860
- return {
900
+ return await addBackgroundContext({
861
901
  content: [
862
902
  {
863
903
  type: "text",
864
904
  text: content
865
905
  }
866
906
  ]
867
- };
907
+ });
868
908
  } catch (error) {
869
909
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
910
+ if (errMsg.includes("404") || errMsg.toLowerCase().includes("not found")) {
911
+ return {
912
+ content: [{
913
+ type: "text",
914
+ text: `Documentation for "${docType}" is not available. This is likely because your backend version is too old and doesn't support this documentation endpoint yet. This won't affect the functionality of the tools - they will still work correctly.`
915
+ }]
916
+ };
917
+ }
870
918
  return {
871
919
  content: [{ type: "text", text: `Error fetching ${docType} documentation: ${errMsg}` }]
872
920
  };
@@ -890,14 +938,14 @@ function registerInsforgeTools(server, config = {}) {
890
938
  }
891
939
  });
892
940
  const result = await handleApiResponse(response);
893
- return {
941
+ return await addBackgroundContext({
894
942
  content: [
895
943
  {
896
944
  type: "text",
897
945
  text: formatSuccessMessage("Anonymous token generated", result)
898
946
  }
899
947
  ]
900
- };
948
+ });
901
949
  } catch (error) {
902
950
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
903
951
  return {
@@ -929,14 +977,14 @@ function registerInsforgeTools(server, config = {}) {
929
977
  }
930
978
  });
931
979
  const result = await handleApiResponse(response);
932
- return {
980
+ return await addBackgroundContext({
933
981
  content: [
934
982
  {
935
983
  type: "text",
936
984
  text: formatSuccessMessage("Schema retrieved", result)
937
985
  }
938
986
  ]
939
- };
987
+ });
940
988
  } catch (error) {
941
989
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
942
990
  return {
@@ -967,7 +1015,7 @@ function registerInsforgeTools(server, config = {}) {
967
1015
  }
968
1016
  });
969
1017
  const metadata = await handleApiResponse(response);
970
- return {
1018
+ return await addBackgroundContext({
971
1019
  content: [
972
1020
  {
973
1021
  type: "text",
@@ -976,7 +1024,7 @@ function registerInsforgeTools(server, config = {}) {
976
1024
  ${JSON.stringify(metadata, null, 2)}`
977
1025
  }
978
1026
  ]
979
- };
1027
+ });
980
1028
  } catch (error) {
981
1029
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
982
1030
  return {
@@ -1014,14 +1062,14 @@ ${JSON.stringify(metadata, null, 2)}`
1014
1062
  body: JSON.stringify(requestBody)
1015
1063
  });
1016
1064
  const result = await handleApiResponse(response);
1017
- return {
1065
+ return await addBackgroundContext({
1018
1066
  content: [
1019
1067
  {
1020
1068
  type: "text",
1021
1069
  text: formatSuccessMessage("SQL query executed", result)
1022
1070
  }
1023
1071
  ]
1024
- };
1072
+ });
1025
1073
  } catch (error) {
1026
1074
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1027
1075
  return {
@@ -1036,6 +1084,82 @@ ${JSON.stringify(metadata, null, 2)}`
1036
1084
  }
1037
1085
  })
1038
1086
  );
1087
+ server.tool(
1088
+ "download-template",
1089
+ "CRITICAL: MANDATORY FIRST STEP for all new InsForge projects. Download pre-configured starter template (React) to a temporary directory. After download, you MUST copy files to current directory using the provided command.",
1090
+ {
1091
+ frame: z14.enum(["react"]).describe("Framework to use for the template (currently only React is supported)"),
1092
+ projectName: z14.string().optional().describe('Name for the project directory (optional, defaults to "insforge-react")')
1093
+ },
1094
+ withUsageTracking("download-template", async ({ frame, projectName }) => {
1095
+ try {
1096
+ const response = await fetch2(`${API_BASE_URL}/api/auth/tokens/anon`, {
1097
+ method: "POST",
1098
+ headers: {
1099
+ "x-api-key": getApiKey(),
1100
+ "Content-Type": "application/json"
1101
+ }
1102
+ });
1103
+ const result = await handleApiResponse(response);
1104
+ const anonKey = result.accessToken;
1105
+ if (!anonKey) {
1106
+ throw new Error("Failed to retrieve anon key from backend");
1107
+ }
1108
+ const tempDir = tmpdir();
1109
+ const targetDir = projectName || `insforge-${frame}`;
1110
+ const templatePath = `${tempDir}/${targetDir}`;
1111
+ console.error(`[download-template] Target path: ${templatePath}`);
1112
+ try {
1113
+ const stats = await fs.stat(templatePath);
1114
+ if (stats.isDirectory()) {
1115
+ console.error(`[download-template] Removing existing template at ${templatePath}`);
1116
+ await fs.rm(templatePath, { recursive: true, force: true });
1117
+ }
1118
+ } catch {
1119
+ }
1120
+ const command = `npx create-insforge-app ${targetDir} --frame ${frame} --base-url ${API_BASE_URL} --anon-key ${anonKey} --skip-install`;
1121
+ const { stdout, stderr } = await execAsync(command, {
1122
+ maxBuffer: 10 * 1024 * 1024,
1123
+ // 10MB buffer
1124
+ cwd: tempDir
1125
+ });
1126
+ const output = stdout || stderr || "";
1127
+ if (output.toLowerCase().includes("error") && !output.includes("successfully")) {
1128
+ throw new Error(`Failed to download template: ${output}`);
1129
+ }
1130
+ return await addBackgroundContext({
1131
+ content: [
1132
+ {
1133
+ type: "text",
1134
+ text: `\u2705 React template downloaded successfully
1135
+
1136
+ \u{1F4C1} Template Location: ${templatePath}
1137
+
1138
+ \u26A0\uFE0F IMPORTANT: The template is in a temporary directory and NOT in your current working directory.
1139
+
1140
+ \u{1F534} CRITICAL NEXT STEP REQUIRED:
1141
+ You MUST copy ALL files (INCLUDING HIDDEN FILES like .env, .gitignore, etc.) from the temporary directory to your current project directory.
1142
+
1143
+ Copy all files from: ${templatePath}
1144
+ To: Your current project directory
1145
+ `
1146
+ }
1147
+ ]
1148
+ });
1149
+ } catch (error) {
1150
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1151
+ return {
1152
+ content: [
1153
+ {
1154
+ type: "text",
1155
+ text: `Error downloading template: ${errMsg}`
1156
+ }
1157
+ ],
1158
+ isError: true
1159
+ };
1160
+ }
1161
+ })
1162
+ );
1039
1163
  server.tool(
1040
1164
  "bulk-upsert",
1041
1165
  "Bulk insert or update data from CSV or JSON file. Supports upsert operations with a unique key.",
@@ -1065,7 +1189,7 @@ ${JSON.stringify(metadata, null, 2)}`
1065
1189
  });
1066
1190
  const result = await handleApiResponse(response);
1067
1191
  const message = result.success ? `Successfully processed ${result.rowsAffected} of ${result.totalRecords} records into table "${result.table}"` : result.message || "Bulk upsert operation completed";
1068
- return {
1192
+ return await addBackgroundContext({
1069
1193
  content: [
1070
1194
  {
1071
1195
  type: "text",
@@ -1078,7 +1202,7 @@ ${JSON.stringify(metadata, null, 2)}`
1078
1202
  })
1079
1203
  }
1080
1204
  ]
1081
- };
1205
+ });
1082
1206
  } catch (error) {
1083
1207
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1084
1208
  return {
@@ -1112,14 +1236,14 @@ ${JSON.stringify(metadata, null, 2)}`
1112
1236
  body: JSON.stringify({ bucketName, isPublic })
1113
1237
  });
1114
1238
  const result = await handleApiResponse(response);
1115
- return {
1239
+ return await addBackgroundContext({
1116
1240
  content: [
1117
1241
  {
1118
1242
  type: "text",
1119
1243
  text: formatSuccessMessage("Bucket created", result)
1120
1244
  }
1121
1245
  ]
1122
- };
1246
+ });
1123
1247
  } catch (error) {
1124
1248
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1125
1249
  return {
@@ -1147,14 +1271,14 @@ ${JSON.stringify(metadata, null, 2)}`
1147
1271
  }
1148
1272
  });
1149
1273
  const result = await handleApiResponse(response);
1150
- return {
1274
+ return await addBackgroundContext({
1151
1275
  content: [
1152
1276
  {
1153
1277
  type: "text",
1154
1278
  text: formatSuccessMessage("Buckets retrieved", result)
1155
1279
  }
1156
1280
  ]
1157
- };
1281
+ });
1158
1282
  } catch (error) {
1159
1283
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1160
1284
  return {
@@ -1186,14 +1310,14 @@ ${JSON.stringify(metadata, null, 2)}`
1186
1310
  }
1187
1311
  });
1188
1312
  const result = await handleApiResponse(response);
1189
- return {
1313
+ return await addBackgroundContext({
1190
1314
  content: [
1191
1315
  {
1192
1316
  type: "text",
1193
1317
  text: formatSuccessMessage("Bucket deleted", result)
1194
1318
  }
1195
1319
  ]
1196
- };
1320
+ });
1197
1321
  } catch (error) {
1198
1322
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1199
1323
  return {
@@ -1242,7 +1366,7 @@ ${JSON.stringify(metadata, null, 2)}`
1242
1366
  })
1243
1367
  });
1244
1368
  const result = await handleApiResponse(response);
1245
- return {
1369
+ return await addBackgroundContext({
1246
1370
  content: [
1247
1371
  {
1248
1372
  type: "text",
@@ -1252,7 +1376,7 @@ ${JSON.stringify(metadata, null, 2)}`
1252
1376
  )
1253
1377
  }
1254
1378
  ]
1255
- };
1379
+ });
1256
1380
  } catch (error) {
1257
1381
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1258
1382
  return {
@@ -1282,14 +1406,14 @@ ${JSON.stringify(metadata, null, 2)}`
1282
1406
  }
1283
1407
  });
1284
1408
  const result = await handleApiResponse(response);
1285
- return {
1409
+ return await addBackgroundContext({
1286
1410
  content: [
1287
1411
  {
1288
1412
  type: "text",
1289
1413
  text: formatSuccessMessage(`Edge function '${args.slug}' details`, result)
1290
1414
  }
1291
1415
  ]
1292
- };
1416
+ });
1293
1417
  } catch (error) {
1294
1418
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1295
1419
  return {
@@ -1345,7 +1469,7 @@ ${JSON.stringify(metadata, null, 2)}`
1345
1469
  });
1346
1470
  const result = await handleApiResponse(response);
1347
1471
  const fileInfo = args.codeFile ? ` from ${args.codeFile}` : "";
1348
- return {
1472
+ return await addBackgroundContext({
1349
1473
  content: [
1350
1474
  {
1351
1475
  type: "text",
@@ -1355,7 +1479,7 @@ ${JSON.stringify(metadata, null, 2)}`
1355
1479
  )
1356
1480
  }
1357
1481
  ]
1358
- };
1482
+ });
1359
1483
  } catch (error) {
1360
1484
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1361
1485
  return {
@@ -1385,14 +1509,14 @@ ${JSON.stringify(metadata, null, 2)}`
1385
1509
  }
1386
1510
  });
1387
1511
  const result = await handleApiResponse(response);
1388
- return {
1512
+ return await addBackgroundContext({
1389
1513
  content: [
1390
1514
  {
1391
1515
  type: "text",
1392
1516
  text: formatSuccessMessage(`Edge function '${args.slug}' deleted successfully`, result)
1393
1517
  }
1394
1518
  ]
1395
- };
1519
+ });
1396
1520
  } catch (error) {
1397
1521
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1398
1522
  return {
@@ -1435,14 +1559,14 @@ ${JSON.stringify(metadata, null, 2)}`
1435
1559
  });
1436
1560
  }
1437
1561
  const result = await handleApiResponse(response);
1438
- return {
1562
+ return await addBackgroundContext({
1439
1563
  content: [
1440
1564
  {
1441
1565
  type: "text",
1442
1566
  text: formatSuccessMessage(`Latest logs from ${source}`, result)
1443
1567
  }
1444
1568
  ]
1445
- };
1569
+ });
1446
1570
  } catch (error) {
1447
1571
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1448
1572
  return {
@@ -1457,114 +1581,10 @@ ${JSON.stringify(metadata, null, 2)}`
1457
1581
  }
1458
1582
  })
1459
1583
  );
1460
- server.tool(
1461
- "upsert-schedule",
1462
- "Create or update a cron job schedule. If id is provided, updates existing schedule; otherwise creates a new one.",
1463
- {
1464
- apiKey: z14.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
1465
- id: z14.string().uuid().optional().describe("The UUID of the schedule to update. If omitted, a new schedule will be created."),
1466
- name: z14.string().min(3).describe("Schedule name (at least 3 characters)"),
1467
- cronSchedule: z14.string().describe('Cron schedule format (5 or 6 parts, e.g., "0 */2 * * *" for every 2 hours)'),
1468
- functionUrl: z14.string().url().describe("The URL to call when the schedule triggers"),
1469
- httpMethod: z14.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).optional().default("POST").describe("HTTP method to use"),
1470
- headers: z14.record(z14.string()).optional().describe('HTTP headers. Values starting with "secret:" will be resolved from secrets store.'),
1471
- body: z14.record(z14.unknown()).optional().describe("JSON body to send with the request")
1472
- },
1473
- withUsageTracking("upsert-schedule", async ({ apiKey, id, name, cronSchedule, functionUrl, httpMethod, headers, body }) => {
1474
- try {
1475
- await checkToolVersion("upsert-schedule");
1476
- const actualApiKey = getApiKey(apiKey);
1477
- const requestBody = {
1478
- name,
1479
- cronSchedule,
1480
- functionUrl,
1481
- httpMethod: httpMethod || "POST"
1482
- };
1483
- if (id) {
1484
- requestBody.id = id;
1485
- }
1486
- if (headers) {
1487
- requestBody.headers = headers;
1488
- }
1489
- if (body) {
1490
- requestBody.body = body;
1491
- }
1492
- const response = await fetch2(`${API_BASE_URL}/api/schedules`, {
1493
- method: "POST",
1494
- headers: {
1495
- "x-api-key": actualApiKey,
1496
- "Content-Type": "application/json"
1497
- },
1498
- body: JSON.stringify(requestBody)
1499
- });
1500
- const result = await handleApiResponse(response);
1501
- const action = id ? "updated" : "created";
1502
- return await addBackgroundContext({
1503
- content: [
1504
- {
1505
- type: "text",
1506
- text: formatSuccessMessage(`Schedule '${name}' ${action} successfully`, result)
1507
- }
1508
- ]
1509
- });
1510
- } catch (error) {
1511
- const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1512
- return await addBackgroundContext({
1513
- content: [
1514
- {
1515
- type: "text",
1516
- text: `Error upserting schedule: ${errMsg}`
1517
- }
1518
- ],
1519
- isError: true
1520
- });
1521
- }
1522
- })
1523
- );
1524
- server.tool(
1525
- "delete-schedule",
1526
- "Delete a cron job schedule permanently",
1527
- {
1528
- apiKey: z14.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
1529
- scheduleId: z14.string().uuid().describe("The UUID of the schedule to delete")
1530
- },
1531
- withUsageTracking("delete-schedule", async ({ apiKey, scheduleId }) => {
1532
- try {
1533
- await checkToolVersion("delete-schedule");
1534
- const actualApiKey = getApiKey(apiKey);
1535
- const response = await fetch2(`${API_BASE_URL}/api/schedules/${scheduleId}`, {
1536
- method: "DELETE",
1537
- headers: {
1538
- "x-api-key": actualApiKey
1539
- }
1540
- });
1541
- const result = await handleApiResponse(response);
1542
- return await addBackgroundContext({
1543
- content: [
1544
- {
1545
- type: "text",
1546
- text: formatSuccessMessage(`Schedule ${scheduleId} deleted successfully`, result)
1547
- }
1548
- ]
1549
- });
1550
- } catch (error) {
1551
- const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1552
- return await addBackgroundContext({
1553
- content: [
1554
- {
1555
- type: "text",
1556
- text: `Error deleting schedule: ${errMsg}`
1557
- }
1558
- ],
1559
- isError: true
1560
- });
1561
- }
1562
- })
1563
- );
1564
1584
  return {
1565
1585
  apiKey: GLOBAL_API_KEY,
1566
1586
  apiBaseUrl: API_BASE_URL,
1567
- toolCount: 16
1587
+ toolCount: 15
1568
1588
  };
1569
1589
  }
1570
1590
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  registerInsforgeTools
4
- } from "./chunk-YCORQXCY.js";
4
+ } from "./chunk-MRGODXOM.js";
5
5
 
6
6
  // src/http/server.ts
7
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  registerInsforgeTools
4
- } from "./chunk-YCORQXCY.js";
4
+ } from "./chunk-MRGODXOM.js";
5
5
 
6
6
  // src/stdio/index.ts
7
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@insforge/mcp",
3
- "version": "1.1.7-dev.8",
3
+ "version": "1.2.0",
4
4
  "description": "MCP (Model Context Protocol) server for Insforge backend-as-a-service",
5
5
  "mcpName": "io.github.InsForge/insforge-mcp",
6
6
  "type": "module",