midnight-mcp 0.1.18 → 0.1.19

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.
@@ -549,6 +549,7 @@ export declare function extractContractStructure(input: ExtractContractStructure
549
549
  structure?: undefined;
550
550
  exports?: undefined;
551
551
  stats?: undefined;
552
+ potentialIssues?: undefined;
552
553
  summary?: undefined;
553
554
  } | {
554
555
  success: boolean;
@@ -561,6 +562,7 @@ export declare function extractContractStructure(input: ExtractContractStructure
561
562
  structure?: undefined;
562
563
  exports?: undefined;
563
564
  stats?: undefined;
565
+ potentialIssues?: undefined;
564
566
  summary?: undefined;
565
567
  } | {
566
568
  success: boolean;
@@ -620,6 +622,13 @@ export declare function extractContractStructure(input: ExtractContractStructure
620
622
  exportedWitnesses: number;
621
623
  exportedLedger: number;
622
624
  };
625
+ potentialIssues: {
626
+ type: string;
627
+ line?: number;
628
+ message: string;
629
+ suggestion: string;
630
+ severity: "error" | "warning";
631
+ }[] | undefined;
623
632
  summary: string;
624
633
  message: string;
625
634
  error?: undefined;
@@ -1156,6 +1156,144 @@ export async function extractContractStructure(input) {
1156
1156
  witnesses: witnesses.filter((w) => w.isExport).map((w) => w.name),
1157
1157
  ledger: ledgerItems.filter((l) => l.isExport).map((l) => l.name),
1158
1158
  };
1159
+ // ============================================================================
1160
+ // PRE-COMPILATION ISSUE DETECTION
1161
+ // Catch common mistakes before hitting the compiler
1162
+ // ============================================================================
1163
+ const potentialIssues = [];
1164
+ // Known CompactStandardLibrary exports that shouldn't be redefined
1165
+ const stdlibExports = [
1166
+ "burnAddress",
1167
+ "ownPublicKey",
1168
+ "contractAddress",
1169
+ "default",
1170
+ "disclose",
1171
+ "assert",
1172
+ "pad",
1173
+ "unpad",
1174
+ "Counter",
1175
+ "Map",
1176
+ "Set",
1177
+ "MerkleTree",
1178
+ "Opaque",
1179
+ "Vector",
1180
+ ];
1181
+ // 1. Detect module-level const (not supported in Compact)
1182
+ const constPattern = /^const\s+(\w+)\s*:/gm;
1183
+ let constMatch;
1184
+ while ((constMatch = constPattern.exec(code)) !== null) {
1185
+ // Check if this const is inside a circuit block by looking for preceding circuit/constructor
1186
+ const beforeConst = code.substring(0, constMatch.index);
1187
+ const lastCircuitStart = Math.max(beforeConst.lastIndexOf("circuit "), beforeConst.lastIndexOf("constructor {"));
1188
+ const lastCloseBrace = beforeConst.lastIndexOf("}");
1189
+ // If no circuit before, or the last } is after the last circuit start, it's module-level
1190
+ if (lastCircuitStart === -1 || lastCloseBrace > lastCircuitStart) {
1191
+ const lineNum = lineByIndex[constMatch.index] || 1;
1192
+ potentialIssues.push({
1193
+ type: "module_level_const",
1194
+ line: lineNum,
1195
+ message: `Module-level 'const ${constMatch[1]}' is not supported in Compact`,
1196
+ suggestion: `Use 'pure circuit ${constMatch[1]}(): <type> { return <value>; }' instead`,
1197
+ severity: "error",
1198
+ });
1199
+ }
1200
+ }
1201
+ // 2. Detect standard library name collisions
1202
+ const hasStdlibImport = imports.includes("CompactStandardLibrary") ||
1203
+ code.includes('include "std"');
1204
+ if (hasStdlibImport) {
1205
+ // Check circuits for name collisions
1206
+ for (const circuit of circuits) {
1207
+ if (stdlibExports.includes(circuit.name)) {
1208
+ potentialIssues.push({
1209
+ type: "stdlib_name_collision",
1210
+ line: circuit.line,
1211
+ message: `Circuit '${circuit.name}' conflicts with CompactStandardLibrary.${circuit.name}()`,
1212
+ suggestion: `Rename to avoid ambiguity, or remove to use the standard library version`,
1213
+ severity: "error",
1214
+ });
1215
+ }
1216
+ }
1217
+ }
1218
+ // 3. Detect sealed + export conflicts
1219
+ const sealedFields = [];
1220
+ const sealedPattern = /sealed\s+ledger\s+(\w+)\s*:/g;
1221
+ let sealedMatch;
1222
+ while ((sealedMatch = sealedPattern.exec(code)) !== null) {
1223
+ const lineNum = lineByIndex[sealedMatch.index] || 1;
1224
+ sealedFields.push({ name: sealedMatch[1], line: lineNum });
1225
+ }
1226
+ if (sealedFields.length > 0) {
1227
+ // Check if any exported circuit writes to sealed fields
1228
+ for (const circuit of circuits) {
1229
+ if (circuit.isExport) {
1230
+ // Find the circuit body and check for assignments to sealed fields
1231
+ const circuitBodyMatch = code.match(new RegExp(`(?:export\\s+)?circuit\\s+${circuit.name}\\s*\\([^)]*\\)\\s*:[^{]*\\{([\\s\\S]*?)\\n\\}`, "m"));
1232
+ if (circuitBodyMatch) {
1233
+ const body = circuitBodyMatch[1];
1234
+ for (const field of sealedFields) {
1235
+ // Check for assignment patterns: fieldName = or fieldName.method(
1236
+ if (new RegExp(`\\b${field.name}\\s*=`).test(body) ||
1237
+ new RegExp(`\\b${field.name}\\s*\\.\\s*\\w+\\s*\\(`).test(body)) {
1238
+ potentialIssues.push({
1239
+ type: "sealed_export_conflict",
1240
+ line: circuit.line,
1241
+ message: `Exported circuit '${circuit.name}' modifies sealed field '${field.name}'`,
1242
+ suggestion: `Move sealed field initialization to a 'constructor { }' block instead`,
1243
+ severity: "error",
1244
+ });
1245
+ }
1246
+ }
1247
+ }
1248
+ }
1249
+ }
1250
+ }
1251
+ // 4. Detect missing constructor when sealed fields exist but no constructor
1252
+ if (sealedFields.length > 0) {
1253
+ const hasConstructor = /constructor\s*\{/.test(code);
1254
+ if (!hasConstructor) {
1255
+ // Check if there's an initialize-like circuit trying to set sealed fields
1256
+ const initCircuit = circuits.find((c) => c.name.toLowerCase().includes("init") ||
1257
+ c.name.toLowerCase() === "setup");
1258
+ if (initCircuit && initCircuit.isExport) {
1259
+ potentialIssues.push({
1260
+ type: "missing_constructor",
1261
+ line: initCircuit.line,
1262
+ message: `Contract has sealed fields but uses '${initCircuit.name}' instead of constructor`,
1263
+ suggestion: `Sealed fields must be initialized in 'constructor { }', not in exported circuits`,
1264
+ severity: "warning",
1265
+ });
1266
+ }
1267
+ }
1268
+ }
1269
+ // 5. Detect potential type mismatches with stdlib functions
1270
+ if (hasStdlibImport) {
1271
+ // Check for burnAddress() used where ZswapCoinPublicKey is expected
1272
+ // burnAddress() returns Either<ZswapCoinPublicKey, ContractAddress>
1273
+ const burnAddressUsages = code.matchAll(/burnAddress\s*\(\s*\)/g);
1274
+ for (const usage of burnAddressUsages) {
1275
+ // Check if it's being passed to a function or assigned
1276
+ const afterUsage = code.substring(usage.index + usage[0].length, usage.index + usage[0].length + 50);
1277
+ const beforeUsage = code.substring(Math.max(0, usage.index - 100), usage.index);
1278
+ // If used in a context expecting ZswapCoinPublicKey (not .left or .right access)
1279
+ if (!afterUsage.startsWith(".left") &&
1280
+ !afterUsage.startsWith(".right") &&
1281
+ !afterUsage.startsWith(".is_left")) {
1282
+ // Check if it's in a function call or assignment that likely expects ZswapCoinPublicKey
1283
+ if (/\(\s*$/.test(beforeUsage) || /,\s*$/.test(beforeUsage)) {
1284
+ const lineNum = lineByIndex[usage.index] || 1;
1285
+ potentialIssues.push({
1286
+ type: "stdlib_type_mismatch",
1287
+ line: lineNum,
1288
+ message: `burnAddress() returns Either<ZswapCoinPublicKey, ContractAddress>, not ZswapCoinPublicKey`,
1289
+ suggestion: `Use burnAddress().left for ZswapCoinPublicKey, or define 'pure circuit zeroKey(): ZswapCoinPublicKey { return default<ZswapCoinPublicKey>; }'`,
1290
+ severity: "warning",
1291
+ });
1292
+ break; // Only warn once
1293
+ }
1294
+ }
1295
+ }
1296
+ }
1159
1297
  const summary = [];
1160
1298
  if (circuits.length > 0) {
1161
1299
  summary.push(`${circuits.length} circuit(s)`);
@@ -1201,8 +1339,11 @@ export async function extractContractStructure(input) {
1201
1339
  exportedWitnesses: exports.witnesses.length,
1202
1340
  exportedLedger: exports.ledger.length,
1203
1341
  },
1342
+ potentialIssues: potentialIssues.length > 0 ? potentialIssues : undefined,
1204
1343
  summary: summary.length > 0 ? summary.join(", ") : "Empty contract",
1205
- message: `📋 Contract contains: ${summary.join(", ") || "no definitions found"}`,
1344
+ message: potentialIssues.length > 0
1345
+ ? `⚠️ Found ${potentialIssues.length} potential issue(s). Contract contains: ${summary.join(", ") || "no definitions found"}`
1346
+ : `📋 Contract contains: ${summary.join(", ") || "no definitions found"}`,
1206
1347
  };
1207
1348
  }
1208
1349
  //# sourceMappingURL=validation.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "midnight-mcp",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "Model Context Protocol Server for Midnight Blockchain Development",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",