@insforge/mcp 1.1.7-dev.9 → 1.2.1

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,6 +840,9 @@ 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;
@@ -847,27 +856,65 @@ function registerInsforgeTools(server, config = {}) {
847
856
  throw new Error(`Unable to retrieve ${docType} documentation: ${errMsg}`);
848
857
  }
849
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
+ };
850
889
  server.tool(
851
890
  "fetch-docs",
852
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.',
853
892
  {
854
- docType: z14.enum(["instructions", "db-sdk", "storage-sdk", "functions-sdk", "ai-integration-sdk", "auth-components-nextjs", "auth-components-react"]).describe(
855
- '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).'
856
895
  )
857
896
  },
858
897
  withUsageTracking("fetch-docs", async ({ docType }) => {
859
898
  try {
860
899
  const content = await fetchDocumentation(docType);
861
- return {
900
+ return await addBackgroundContext({
862
901
  content: [
863
902
  {
864
903
  type: "text",
865
904
  text: content
866
905
  }
867
906
  ]
868
- };
907
+ });
869
908
  } catch (error) {
870
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
+ }
871
918
  return {
872
919
  content: [{ type: "text", text: `Error fetching ${docType} documentation: ${errMsg}` }]
873
920
  };
@@ -891,14 +938,14 @@ function registerInsforgeTools(server, config = {}) {
891
938
  }
892
939
  });
893
940
  const result = await handleApiResponse(response);
894
- return {
941
+ return await addBackgroundContext({
895
942
  content: [
896
943
  {
897
944
  type: "text",
898
945
  text: formatSuccessMessage("Anonymous token generated", result)
899
946
  }
900
947
  ]
901
- };
948
+ });
902
949
  } catch (error) {
903
950
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
904
951
  return {
@@ -930,14 +977,14 @@ function registerInsforgeTools(server, config = {}) {
930
977
  }
931
978
  });
932
979
  const result = await handleApiResponse(response);
933
- return {
980
+ return await addBackgroundContext({
934
981
  content: [
935
982
  {
936
983
  type: "text",
937
984
  text: formatSuccessMessage("Schema retrieved", result)
938
985
  }
939
986
  ]
940
- };
987
+ });
941
988
  } catch (error) {
942
989
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
943
990
  return {
@@ -968,7 +1015,7 @@ function registerInsforgeTools(server, config = {}) {
968
1015
  }
969
1016
  });
970
1017
  const metadata = await handleApiResponse(response);
971
- return {
1018
+ return await addBackgroundContext({
972
1019
  content: [
973
1020
  {
974
1021
  type: "text",
@@ -977,7 +1024,7 @@ function registerInsforgeTools(server, config = {}) {
977
1024
  ${JSON.stringify(metadata, null, 2)}`
978
1025
  }
979
1026
  ]
980
- };
1027
+ });
981
1028
  } catch (error) {
982
1029
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
983
1030
  return {
@@ -1015,14 +1062,14 @@ ${JSON.stringify(metadata, null, 2)}`
1015
1062
  body: JSON.stringify(requestBody)
1016
1063
  });
1017
1064
  const result = await handleApiResponse(response);
1018
- return {
1065
+ return await addBackgroundContext({
1019
1066
  content: [
1020
1067
  {
1021
1068
  type: "text",
1022
1069
  text: formatSuccessMessage("SQL query executed", result)
1023
1070
  }
1024
1071
  ]
1025
- };
1072
+ });
1026
1073
  } catch (error) {
1027
1074
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1028
1075
  return {
@@ -1037,6 +1084,82 @@ ${JSON.stringify(metadata, null, 2)}`
1037
1084
  }
1038
1085
  })
1039
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
+ );
1040
1163
  server.tool(
1041
1164
  "bulk-upsert",
1042
1165
  "Bulk insert or update data from CSV or JSON file. Supports upsert operations with a unique key.",
@@ -1066,7 +1189,7 @@ ${JSON.stringify(metadata, null, 2)}`
1066
1189
  });
1067
1190
  const result = await handleApiResponse(response);
1068
1191
  const message = result.success ? `Successfully processed ${result.rowsAffected} of ${result.totalRecords} records into table "${result.table}"` : result.message || "Bulk upsert operation completed";
1069
- return {
1192
+ return await addBackgroundContext({
1070
1193
  content: [
1071
1194
  {
1072
1195
  type: "text",
@@ -1079,7 +1202,7 @@ ${JSON.stringify(metadata, null, 2)}`
1079
1202
  })
1080
1203
  }
1081
1204
  ]
1082
- };
1205
+ });
1083
1206
  } catch (error) {
1084
1207
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1085
1208
  return {
@@ -1113,14 +1236,14 @@ ${JSON.stringify(metadata, null, 2)}`
1113
1236
  body: JSON.stringify({ bucketName, isPublic })
1114
1237
  });
1115
1238
  const result = await handleApiResponse(response);
1116
- return {
1239
+ return await addBackgroundContext({
1117
1240
  content: [
1118
1241
  {
1119
1242
  type: "text",
1120
1243
  text: formatSuccessMessage("Bucket created", result)
1121
1244
  }
1122
1245
  ]
1123
- };
1246
+ });
1124
1247
  } catch (error) {
1125
1248
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1126
1249
  return {
@@ -1148,14 +1271,14 @@ ${JSON.stringify(metadata, null, 2)}`
1148
1271
  }
1149
1272
  });
1150
1273
  const result = await handleApiResponse(response);
1151
- return {
1274
+ return await addBackgroundContext({
1152
1275
  content: [
1153
1276
  {
1154
1277
  type: "text",
1155
1278
  text: formatSuccessMessage("Buckets retrieved", result)
1156
1279
  }
1157
1280
  ]
1158
- };
1281
+ });
1159
1282
  } catch (error) {
1160
1283
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1161
1284
  return {
@@ -1187,14 +1310,14 @@ ${JSON.stringify(metadata, null, 2)}`
1187
1310
  }
1188
1311
  });
1189
1312
  const result = await handleApiResponse(response);
1190
- return {
1313
+ return await addBackgroundContext({
1191
1314
  content: [
1192
1315
  {
1193
1316
  type: "text",
1194
1317
  text: formatSuccessMessage("Bucket deleted", result)
1195
1318
  }
1196
1319
  ]
1197
- };
1320
+ });
1198
1321
  } catch (error) {
1199
1322
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1200
1323
  return {
@@ -1243,7 +1366,7 @@ ${JSON.stringify(metadata, null, 2)}`
1243
1366
  })
1244
1367
  });
1245
1368
  const result = await handleApiResponse(response);
1246
- return {
1369
+ return await addBackgroundContext({
1247
1370
  content: [
1248
1371
  {
1249
1372
  type: "text",
@@ -1253,7 +1376,7 @@ ${JSON.stringify(metadata, null, 2)}`
1253
1376
  )
1254
1377
  }
1255
1378
  ]
1256
- };
1379
+ });
1257
1380
  } catch (error) {
1258
1381
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1259
1382
  return {
@@ -1283,14 +1406,14 @@ ${JSON.stringify(metadata, null, 2)}`
1283
1406
  }
1284
1407
  });
1285
1408
  const result = await handleApiResponse(response);
1286
- return {
1409
+ return await addBackgroundContext({
1287
1410
  content: [
1288
1411
  {
1289
1412
  type: "text",
1290
1413
  text: formatSuccessMessage(`Edge function '${args.slug}' details`, result)
1291
1414
  }
1292
1415
  ]
1293
- };
1416
+ });
1294
1417
  } catch (error) {
1295
1418
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1296
1419
  return {
@@ -1346,7 +1469,7 @@ ${JSON.stringify(metadata, null, 2)}`
1346
1469
  });
1347
1470
  const result = await handleApiResponse(response);
1348
1471
  const fileInfo = args.codeFile ? ` from ${args.codeFile}` : "";
1349
- return {
1472
+ return await addBackgroundContext({
1350
1473
  content: [
1351
1474
  {
1352
1475
  type: "text",
@@ -1356,7 +1479,7 @@ ${JSON.stringify(metadata, null, 2)}`
1356
1479
  )
1357
1480
  }
1358
1481
  ]
1359
- };
1482
+ });
1360
1483
  } catch (error) {
1361
1484
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1362
1485
  return {
@@ -1386,14 +1509,14 @@ ${JSON.stringify(metadata, null, 2)}`
1386
1509
  }
1387
1510
  });
1388
1511
  const result = await handleApiResponse(response);
1389
- return {
1512
+ return await addBackgroundContext({
1390
1513
  content: [
1391
1514
  {
1392
1515
  type: "text",
1393
1516
  text: formatSuccessMessage(`Edge function '${args.slug}' deleted successfully`, result)
1394
1517
  }
1395
1518
  ]
1396
- };
1519
+ });
1397
1520
  } catch (error) {
1398
1521
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1399
1522
  return {
@@ -1436,14 +1559,14 @@ ${JSON.stringify(metadata, null, 2)}`
1436
1559
  });
1437
1560
  }
1438
1561
  const result = await handleApiResponse(response);
1439
- return {
1562
+ return await addBackgroundContext({
1440
1563
  content: [
1441
1564
  {
1442
1565
  type: "text",
1443
1566
  text: formatSuccessMessage(`Latest logs from ${source}`, result)
1444
1567
  }
1445
1568
  ]
1446
- };
1569
+ });
1447
1570
  } catch (error) {
1448
1571
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1449
1572
  return {
@@ -1458,114 +1581,10 @@ ${JSON.stringify(metadata, null, 2)}`
1458
1581
  }
1459
1582
  })
1460
1583
  );
1461
- server.tool(
1462
- "upsert-schedule",
1463
- "Create or update a cron job schedule. If id is provided, updates existing schedule; otherwise creates a new one.",
1464
- {
1465
- apiKey: z14.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
1466
- id: z14.string().uuid().optional().describe("The UUID of the schedule to update. If omitted, a new schedule will be created."),
1467
- name: z14.string().min(3).describe("Schedule name (at least 3 characters)"),
1468
- cronSchedule: z14.string().describe('Cron schedule format (5 or 6 parts, e.g., "0 */2 * * *" for every 2 hours)'),
1469
- functionUrl: z14.string().url().describe("The URL to call when the schedule triggers"),
1470
- httpMethod: z14.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).optional().default("POST").describe("HTTP method to use"),
1471
- headers: z14.record(z14.string()).optional().describe('HTTP headers. Values starting with "secret:" will be resolved from secrets store.'),
1472
- body: z14.record(z14.unknown()).optional().describe("JSON body to send with the request")
1473
- },
1474
- withUsageTracking("upsert-schedule", async ({ apiKey, id, name, cronSchedule, functionUrl, httpMethod, headers, body }) => {
1475
- try {
1476
- await checkToolVersion("upsert-schedule");
1477
- const actualApiKey = getApiKey(apiKey);
1478
- const requestBody = {
1479
- name,
1480
- cronSchedule,
1481
- functionUrl,
1482
- httpMethod: httpMethod || "POST"
1483
- };
1484
- if (id) {
1485
- requestBody.id = id;
1486
- }
1487
- if (headers) {
1488
- requestBody.headers = headers;
1489
- }
1490
- if (body) {
1491
- requestBody.body = body;
1492
- }
1493
- const response = await fetch2(`${API_BASE_URL}/api/schedules`, {
1494
- method: "POST",
1495
- headers: {
1496
- "x-api-key": actualApiKey,
1497
- "Content-Type": "application/json"
1498
- },
1499
- body: JSON.stringify(requestBody)
1500
- });
1501
- const result = await handleApiResponse(response);
1502
- const action = id ? "updated" : "created";
1503
- return await addBackgroundContext({
1504
- content: [
1505
- {
1506
- type: "text",
1507
- text: formatSuccessMessage(`Schedule '${name}' ${action} successfully`, result)
1508
- }
1509
- ]
1510
- });
1511
- } catch (error) {
1512
- const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1513
- return await addBackgroundContext({
1514
- content: [
1515
- {
1516
- type: "text",
1517
- text: `Error upserting schedule: ${errMsg}`
1518
- }
1519
- ],
1520
- isError: true
1521
- });
1522
- }
1523
- })
1524
- );
1525
- server.tool(
1526
- "delete-schedule",
1527
- "Delete a cron job schedule permanently",
1528
- {
1529
- apiKey: z14.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
1530
- scheduleId: z14.string().uuid().describe("The UUID of the schedule to delete")
1531
- },
1532
- withUsageTracking("delete-schedule", async ({ apiKey, scheduleId }) => {
1533
- try {
1534
- await checkToolVersion("delete-schedule");
1535
- const actualApiKey = getApiKey(apiKey);
1536
- const response = await fetch2(`${API_BASE_URL}/api/schedules/${scheduleId}`, {
1537
- method: "DELETE",
1538
- headers: {
1539
- "x-api-key": actualApiKey
1540
- }
1541
- });
1542
- const result = await handleApiResponse(response);
1543
- return await addBackgroundContext({
1544
- content: [
1545
- {
1546
- type: "text",
1547
- text: formatSuccessMessage(`Schedule ${scheduleId} deleted successfully`, result)
1548
- }
1549
- ]
1550
- });
1551
- } catch (error) {
1552
- const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1553
- return await addBackgroundContext({
1554
- content: [
1555
- {
1556
- type: "text",
1557
- text: `Error deleting schedule: ${errMsg}`
1558
- }
1559
- ],
1560
- isError: true
1561
- });
1562
- }
1563
- })
1564
- );
1565
1584
  return {
1566
1585
  apiKey: GLOBAL_API_KEY,
1567
1586
  apiBaseUrl: API_BASE_URL,
1568
- toolCount: 16
1587
+ toolCount: 15
1569
1588
  };
1570
1589
  }
1571
1590
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  registerInsforgeTools
4
- } from "./chunk-HEUOFCZP.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,23 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  registerInsforgeTools
4
- } from "./chunk-HEUOFCZP.js";
4
+ } from "./chunk-MRGODXOM.js";
5
5
 
6
6
  // src/stdio/index.ts
7
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
8
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
9
  import { program } from "commander";
10
10
  program.option("--api_key <value>", "API Key");
11
+ program.option("--api_base_url <value>", "API Base URL");
11
12
  program.parse(process.argv);
12
13
  var options = program.opts();
13
- var { api_key } = options;
14
+ var { api_key, api_base_url } = options;
14
15
  var server = new McpServer({
15
16
  name: "insforge-mcp",
16
17
  version: "1.0.0"
17
18
  });
18
19
  var toolsConfig = registerInsforgeTools(server, {
19
20
  apiKey: api_key,
20
- apiBaseUrl: process.env.API_BASE_URL
21
+ apiBaseUrl: api_base_url || process.env.API_BASE_URL
21
22
  });
22
23
  async function main() {
23
24
  const transport = new StdioServerTransport();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@insforge/mcp",
3
- "version": "1.1.7-dev.9",
3
+ "version": "1.2.1",
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",