@insforge/mcp 1.1.7-dev.2 → 1.1.7-dev.21

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,8 @@
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";
7
9
 
8
10
  // src/shared/response-handler.ts
9
11
  async function handleApiResponse(response) {
@@ -161,8 +163,8 @@ var deleteTableResponse = z2.object({
161
163
  });
162
164
  var rawSQLRequestSchema = z2.object({
163
165
  query: z2.string().min(1, "Query is required"),
164
- params: z2.array(z2.any()).optional()
165
- // z.any() generates valid JSON Schema with items: {}
166
+ params: z2.array(z2.unknown()).optional()
167
+ // z.unknown() generates JSON Schema with items: {}
166
168
  });
167
169
  var rawSQLResponseSchema = z2.object({
168
170
  rows: z2.array(z2.record(z2.string(), z2.unknown())),
@@ -735,10 +737,75 @@ var functionUpdateRequestSchema = z13.object({
735
737
 
736
738
  // src/shared/tools.ts
737
739
  import FormData from "form-data";
740
+ var execAsync = promisify(exec);
741
+ var TOOL_VERSION_REQUIREMENTS = {
742
+ "upsert-schedule": "1.1.1",
743
+ // 'get-schedules': '1.1.1',
744
+ // 'get-schedule-logs': '1.1.1',
745
+ "delete-schedule": "1.1.1"
746
+ };
738
747
  function registerInsforgeTools(server, config = {}) {
739
748
  const GLOBAL_API_KEY = config.apiKey || process.env.API_KEY || "";
740
749
  const API_BASE_URL = config.apiBaseUrl || process.env.API_BASE_URL || "http://localhost:7130";
741
750
  const usageTracker = new UsageTracker(API_BASE_URL, GLOBAL_API_KEY);
751
+ let versionCache = null;
752
+ const VERSION_CACHE_TTL = 5 * 60 * 1e3;
753
+ async function getBackendVersion() {
754
+ const now = Date.now();
755
+ if (versionCache && now - versionCache.timestamp < VERSION_CACHE_TTL) {
756
+ return versionCache.version;
757
+ }
758
+ try {
759
+ const response = await fetch2(`${API_BASE_URL}/api/health`, {
760
+ method: "GET",
761
+ headers: {
762
+ "Content-Type": "application/json"
763
+ }
764
+ });
765
+ if (!response.ok) {
766
+ throw new Error(`Health check failed with status ${response.status}`);
767
+ }
768
+ const health = await response.json();
769
+ versionCache = {
770
+ version: health.version,
771
+ timestamp: now
772
+ };
773
+ return health.version;
774
+ } catch (error) {
775
+ const errMsg = error instanceof Error ? error.message : "Unknown error";
776
+ throw new Error(`Failed to fetch backend version: ${errMsg}`);
777
+ }
778
+ }
779
+ function compareVersions(v1, v2) {
780
+ const parts1 = v1.split(".").map(Number);
781
+ const parts2 = v2.split(".").map(Number);
782
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
783
+ const part1 = parts1[i] || 0;
784
+ const part2 = parts2[i] || 0;
785
+ if (part1 > part2) return 1;
786
+ if (part1 < part2) return -1;
787
+ }
788
+ return 0;
789
+ }
790
+ async function checkToolVersion(toolName) {
791
+ const requiredVersion = TOOL_VERSION_REQUIREMENTS[toolName];
792
+ if (!requiredVersion) {
793
+ return;
794
+ }
795
+ try {
796
+ const currentVersion = await getBackendVersion();
797
+ if (compareVersions(currentVersion, requiredVersion) < 0) {
798
+ throw new Error(
799
+ `Tool '${toolName}' requires backend version ${requiredVersion} or higher, but current version is ${currentVersion}. Please upgrade your Insforge backend server.`
800
+ );
801
+ }
802
+ } catch (error) {
803
+ if (error instanceof Error && error.message.includes("requires backend version")) {
804
+ throw error;
805
+ }
806
+ console.warn(`Warning: Could not verify backend version for tool '${toolName}': ${error instanceof Error ? error.message : "Unknown error"}`);
807
+ }
808
+ }
742
809
  async function trackToolUsage(toolName, success = true) {
743
810
  if (GLOBAL_API_KEY) {
744
811
  await usageTracker.trackUsage(toolName, success);
@@ -774,6 +841,7 @@ function registerInsforgeTools(server, config = {}) {
774
841
  if (result && typeof result === "object" && "content" in result) {
775
842
  let content = result.content;
776
843
  content = content.replace(/http:\/\/localhost:7130/g, API_BASE_URL);
844
+ content = content.replace(/https:\/\/your-app\.region\.insforge\.app/g, API_BASE_URL);
777
845
  return content;
778
846
  }
779
847
  throw new Error("Invalid response format from documentation endpoint");
@@ -783,12 +851,16 @@ function registerInsforgeTools(server, config = {}) {
783
851
  }
784
852
  };
785
853
  server.tool(
786
- "get-instructions",
787
- "Instruction Essential backend setup tool. <critical>MANDATORY: You MUST use this tool FIRST before attempting any backend operations. Contains required API endpoints, authentication details, and setup instructions.</critical>",
788
- {},
789
- withUsageTracking("get-instructions", async () => {
854
+ "fetch-docs",
855
+ 'Fetch Insforge documentation. Use "instructions" for essential backend setup (MANDATORY FIRST), or select specific SDK docs for database, auth, storage, functions, or AI integration.',
856
+ {
857
+ docType: z14.enum(["instructions", "db-sdk", "storage-sdk", "functions-sdk", "ai-integration-sdk", "auth-components-react", "auth-components-react-router"]).describe(
858
+ '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), "auth-components-react-router" (authentication components for React + React Router applications),'
859
+ )
860
+ },
861
+ withUsageTracking("fetch-docs", async ({ docType }) => {
790
862
  try {
791
- const content = await fetchDocumentation("instructions");
863
+ const content = await fetchDocumentation(docType);
792
864
  return {
793
865
  content: [
794
866
  {
@@ -797,10 +869,62 @@ function registerInsforgeTools(server, config = {}) {
797
869
  }
798
870
  ]
799
871
  };
872
+ } catch (error) {
873
+ if (error instanceof Error && error.message.includes("404")) {
874
+ try {
875
+ const instructionsContent = await fetchDocumentation("instructions");
876
+ return {
877
+ content: [{ type: "text", text: instructionsContent }]
878
+ };
879
+ } catch (fallbackError) {
880
+ const fallbackErrMsg = fallbackError instanceof Error ? fallbackError.message : "Unknown error occurred";
881
+ return {
882
+ content: [{ type: "text", text: `Error fetching documentation: ${fallbackErrMsg}` }]
883
+ };
884
+ }
885
+ }
886
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
887
+ return {
888
+ content: [{ type: "text", text: `Error fetching ${docType} documentation: ${errMsg}` }]
889
+ };
890
+ }
891
+ })
892
+ );
893
+ server.tool(
894
+ "get-anon-key",
895
+ "Generate an anonymous JWT token that never expires. Requires admin API key. Use this for client-side applications that need public access.",
896
+ {
897
+ apiKey: z14.string().optional().describe("API key for authentication (optional if provided via --api_key)")
898
+ },
899
+ withUsageTracking("get-anon-key", async ({ apiKey }) => {
900
+ try {
901
+ const actualApiKey = getApiKey(apiKey);
902
+ const response = await fetch2(`${API_BASE_URL}/api/auth/tokens/anon`, {
903
+ method: "POST",
904
+ headers: {
905
+ "x-api-key": actualApiKey,
906
+ "Content-Type": "application/json"
907
+ }
908
+ });
909
+ const result = await handleApiResponse(response);
910
+ return {
911
+ content: [
912
+ {
913
+ type: "text",
914
+ text: formatSuccessMessage("Anonymous token generated", result)
915
+ }
916
+ ]
917
+ };
800
918
  } catch (error) {
801
919
  const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
802
920
  return {
803
- content: [{ type: "text", text: `Error: ${errMsg}` }]
921
+ content: [
922
+ {
923
+ type: "text",
924
+ text: `Error generating anonymous token: ${errMsg}`
925
+ }
926
+ ],
927
+ isError: true
804
928
  };
805
929
  }
806
930
  })
@@ -929,9 +1053,78 @@ ${JSON.stringify(metadata, null, 2)}`
929
1053
  }
930
1054
  })
931
1055
  );
1056
+ server.tool(
1057
+ "download-template",
1058
+ "CRITICAL: MANDATORY FIRST STEP for all new InsForge projects. Download pre-configured starter template (React/Next.js/React Router). ALWAYS use this tool BEFORE any other setup when starting a new project.",
1059
+ {
1060
+ frame: z14.enum(["react"]).describe("Framework to use for the template (currently only React is supported)"),
1061
+ projectName: z14.string().optional().describe('Name for the project directory (optional, defaults to "insforge-react")'),
1062
+ workingDirectory: z14.string().optional().describe("Working directory path where project should be created")
1063
+ },
1064
+ withUsageTracking("download-template", async ({ frame, projectName, workingDirectory }) => {
1065
+ try {
1066
+ const response = await fetch2(`${API_BASE_URL}/api/auth/tokens/anon`, {
1067
+ method: "POST",
1068
+ headers: {
1069
+ "x-api-key": getApiKey(),
1070
+ "Content-Type": "application/json"
1071
+ }
1072
+ });
1073
+ const result = await handleApiResponse(response);
1074
+ const anonKey = result.accessToken;
1075
+ if (!anonKey) {
1076
+ throw new Error("Failed to retrieve anon key from backend");
1077
+ }
1078
+ const targetDir = projectName || `insforge-${frame}`;
1079
+ const cwd = workingDirectory || process.cwd();
1080
+ const command = `npx create-insforge-app ${targetDir} --frame ${frame} --base-url ${API_BASE_URL} --anon-key ${anonKey}`;
1081
+ const { stdout, stderr } = await execAsync(command, {
1082
+ maxBuffer: 10 * 1024 * 1024,
1083
+ // 10MB buffer
1084
+ cwd
1085
+ });
1086
+ const output = stdout || stderr || "";
1087
+ if (output.toLowerCase().includes("error") && !output.includes("successfully")) {
1088
+ throw new Error(`Failed to download template: ${output}`);
1089
+ }
1090
+ const fullPath = `${cwd}/${targetDir}`;
1091
+ return {
1092
+ content: [
1093
+ {
1094
+ type: "text",
1095
+ text: formatSuccessMessage(
1096
+ `React template created successfully`,
1097
+ {
1098
+ projectName: targetDir,
1099
+ location: fullPath,
1100
+ baseUrl: API_BASE_URL,
1101
+ nextSteps: [
1102
+ `cd ${targetDir}`,
1103
+ `npm install`,
1104
+ `npm run dev`
1105
+ ]
1106
+ }
1107
+ )
1108
+ }
1109
+ ]
1110
+ };
1111
+ } catch (error) {
1112
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1113
+ return {
1114
+ content: [
1115
+ {
1116
+ type: "text",
1117
+ text: `Error downloading template: ${errMsg}`
1118
+ }
1119
+ ],
1120
+ isError: true
1121
+ };
1122
+ }
1123
+ })
1124
+ );
932
1125
  server.tool(
933
1126
  "bulk-upsert",
934
- "Bulk insert or update data from CSV or JSON file. Supports upsert operations with a unique key.",
1127
+ "Bulk insert or updallet data from CSV or JSON file. Supports upsert operations with a unique key.",
935
1128
  {
936
1129
  apiKey: z14.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
937
1130
  ...bulkUpsertRequestSchema.shape,
@@ -1313,12 +1506,20 @@ ${JSON.stringify(metadata, null, 2)}`
1313
1506
  const actualApiKey = getApiKey(apiKey);
1314
1507
  const queryParams = new URLSearchParams();
1315
1508
  if (limit) queryParams.append("limit", limit.toString());
1316
- const response = await fetch2(`${API_BASE_URL}/api/logs/analytics/${source}?${queryParams}`, {
1509
+ let response = await fetch2(`${API_BASE_URL}/api/logs/${source}?${queryParams}`, {
1317
1510
  method: "GET",
1318
1511
  headers: {
1319
1512
  "x-api-key": actualApiKey
1320
1513
  }
1321
1514
  });
1515
+ if (response.status === 404) {
1516
+ response = await fetch2(`${API_BASE_URL}/api/logs/analytics/${source}?${queryParams}`, {
1517
+ method: "GET",
1518
+ headers: {
1519
+ "x-api-key": actualApiKey
1520
+ }
1521
+ });
1522
+ }
1322
1523
  const result = await handleApiResponse(response);
1323
1524
  return {
1324
1525
  content: [
@@ -1342,10 +1543,114 @@ ${JSON.stringify(metadata, null, 2)}`
1342
1543
  }
1343
1544
  })
1344
1545
  );
1546
+ server.tool(
1547
+ "upsert-schedule",
1548
+ "Create or update a cron job schedule. If id is provided, updates existing schedule; otherwise creates a new one.",
1549
+ {
1550
+ apiKey: z14.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
1551
+ id: z14.string().uuid().optional().describe("The UUID of the schedule to update. If omitted, a new schedule will be created."),
1552
+ name: z14.string().min(3).describe("Schedule name (at least 3 characters)"),
1553
+ cronSchedule: z14.string().describe('Cron schedule format (5 or 6 parts, e.g., "0 */2 * * *" for every 2 hours)'),
1554
+ functionUrl: z14.string().url().describe("The URL to call when the schedule triggers"),
1555
+ httpMethod: z14.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).optional().default("POST").describe("HTTP method to use"),
1556
+ headers: z14.record(z14.string()).optional().describe('HTTP headers. Values starting with "secret:" will be resolved from secrets store.'),
1557
+ body: z14.record(z14.unknown()).optional().describe("JSON body to send with the request")
1558
+ },
1559
+ withUsageTracking("upsert-schedule", async ({ apiKey, id, name, cronSchedule, functionUrl, httpMethod, headers, body }) => {
1560
+ try {
1561
+ await checkToolVersion("upsert-schedule");
1562
+ const actualApiKey = getApiKey(apiKey);
1563
+ const requestBody = {
1564
+ name,
1565
+ cronSchedule,
1566
+ functionUrl,
1567
+ httpMethod: httpMethod || "POST"
1568
+ };
1569
+ if (id) {
1570
+ requestBody.id = id;
1571
+ }
1572
+ if (headers) {
1573
+ requestBody.headers = headers;
1574
+ }
1575
+ if (body) {
1576
+ requestBody.body = body;
1577
+ }
1578
+ const response = await fetch2(`${API_BASE_URL}/api/schedules`, {
1579
+ method: "POST",
1580
+ headers: {
1581
+ "x-api-key": actualApiKey,
1582
+ "Content-Type": "application/json"
1583
+ },
1584
+ body: JSON.stringify(requestBody)
1585
+ });
1586
+ const result = await handleApiResponse(response);
1587
+ const action = id ? "updated" : "created";
1588
+ return {
1589
+ content: [
1590
+ {
1591
+ type: "text",
1592
+ text: formatSuccessMessage(`Schedule '${name}' ${action} successfully`, result)
1593
+ }
1594
+ ]
1595
+ };
1596
+ } catch (error) {
1597
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1598
+ return {
1599
+ content: [
1600
+ {
1601
+ type: "text",
1602
+ text: `Error upserting schedule: ${errMsg}`
1603
+ }
1604
+ ],
1605
+ isError: true
1606
+ };
1607
+ }
1608
+ })
1609
+ );
1610
+ server.tool(
1611
+ "delete-schedule",
1612
+ "Delete a cron job schedule permanently",
1613
+ {
1614
+ apiKey: z14.string().optional().describe("API key for authentication (optional if provided via --api_key)"),
1615
+ scheduleId: z14.string().uuid().describe("The UUID of the schedule to delete")
1616
+ },
1617
+ withUsageTracking("delete-schedule", async ({ apiKey, scheduleId }) => {
1618
+ try {
1619
+ await checkToolVersion("delete-schedule");
1620
+ const actualApiKey = getApiKey(apiKey);
1621
+ const response = await fetch2(`${API_BASE_URL}/api/schedules/${scheduleId}`, {
1622
+ method: "DELETE",
1623
+ headers: {
1624
+ "x-api-key": actualApiKey
1625
+ }
1626
+ });
1627
+ const result = await handleApiResponse(response);
1628
+ return {
1629
+ content: [
1630
+ {
1631
+ type: "text",
1632
+ text: formatSuccessMessage(`Schedule ${scheduleId} deleted successfully`, result)
1633
+ }
1634
+ ]
1635
+ };
1636
+ } catch (error) {
1637
+ const errMsg = error instanceof Error ? error.message : "Unknown error occurred";
1638
+ return {
1639
+ content: [
1640
+ {
1641
+ type: "text",
1642
+ text: `Error deleting schedule: ${errMsg}`
1643
+ }
1644
+ ],
1645
+ isError: true
1646
+ };
1647
+ }
1648
+ })
1649
+ );
1345
1650
  return {
1346
1651
  apiKey: GLOBAL_API_KEY,
1347
1652
  apiBaseUrl: API_BASE_URL,
1348
- toolCount: 14
1653
+ toolCount: 17
1349
1654
  };
1350
1655
  }
1351
1656
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  registerInsforgeTools
4
- } from "./chunk-74IVXADC.js";
4
+ } from "./chunk-V3UJJA4Z.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-74IVXADC.js";
4
+ } from "./chunk-V3UJJA4Z.js";
5
5
 
6
6
  // src/stdio/index.ts
7
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@insforge/mcp",
3
- "version": "1.1.7-dev.2",
3
+ "version": "1.1.7-dev.21",
4
4
  "description": "MCP (Model Context Protocol) server for Insforge backend-as-a-service",
5
+ "mcpName": "io.github.InsForge/insforge-mcp",
5
6
  "type": "module",
6
7
  "main": "dist/index.js",
7
8
  "bin": {
@@ -31,10 +32,11 @@
31
32
  },
32
33
  "files": [
33
34
  "dist",
34
- "mcp.json"
35
+ "mcp.json",
36
+ "server.json"
35
37
  ],
36
38
  "dependencies": {
37
- "@insforge/shared-schemas": "^1.1.6",
39
+ "@insforge/shared-schemas": "^1.1.7",
38
40
  "@modelcontextprotocol/sdk": "^1.15.1",
39
41
  "@types/express": "^5.0.3",
40
42
  "commander": "^14.0.0",
package/server.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
3
+ "name": "io.github.InsForge/insforge-mcp",
4
+ "title": "Insforge",
5
+ "description": "MCP server for Insforge BaaS - database, auth, storage, edge functions, and container logs",
6
+ "version": "1.1.5",
7
+ "packages": [
8
+ {
9
+ "registryType": "npm",
10
+ "identifier": "@insforge/mcp",
11
+ "version": "1.1.5",
12
+ "transport": {
13
+ "type": "stdio"
14
+ }
15
+ }
16
+ ]
17
+ }