midnight-mcp 0.1.32 → 0.1.34

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.
package/README.md CHANGED
@@ -93,7 +93,7 @@ Or manually update your config:
93
93
  | Category | Tools | Description |
94
94
  | ----------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
95
95
  | **Search** | `search-compact`, `search-typescript`, `search-docs` | Semantic search across Midnight codebase |
96
- | **Analysis** | `analyze-contract`, `explain-circuit`, `extract-contract-structure` | Static analysis and pattern detection |
96
+ | **Analysis** | `analyze-contract`, `explain-circuit`, `extract-contract-structure` | Static analysis with 15+ checks (P0-P2 severity) |
97
97
  | **Repository** | `get-file`, `list-examples`, `get-latest-updates` | Access files and examples |
98
98
  | **Versioning** | `get-version-info`, `check-breaking-changes`, `get-migration-guide`, `get-file-at-version`, `compare-syntax`, `get-latest-syntax` | Version tracking and migration |
99
99
  | **AI Generation** | `generate-contract`, `review-contract`, `document-contract` | AI-powered code generation _(requires sampling)_ |
@@ -119,13 +119,26 @@ All tools are prefixed with `midnight-` (e.g., `midnight-search-compact`).
119
119
 
120
120
  Quick references available offline:
121
121
 
122
- - Compact syntax guide
122
+ - Compact syntax guide (v0.16-0.18)
123
123
  - SDK API reference
124
124
  - OpenZeppelin contracts
125
125
  - Tokenomics overview
126
126
  - Wallet integration
127
127
  - Common errors & solutions
128
128
 
129
+ ### Static Analysis
130
+
131
+ `extract-contract-structure` catches common mistakes before compilation:
132
+
133
+ | Check | Severity | Description |
134
+ | ------------------------- | -------- | ------------------------------------------------------- |
135
+ | `deprecated_ledger_block` | P0 | Catches `ledger { }` → use `export ledger field: Type;` |
136
+ | `invalid_void_type` | P0 | Catches `Void` → use `[]` (empty tuple) |
137
+ | `invalid_pragma_format` | P0 | Catches old pragma → use `>= 0.16 && <= 0.18` |
138
+ | `unexported_enum` | P1 | Enums need `export` for TypeScript access |
139
+ | `module_level_const` | P0 | Use `pure circuit` instead |
140
+ | + 10 more checks | P1-P2 | Overflow, division, assertions, etc. |
141
+
129
142
  ### 5 Prompts
130
143
 
131
144
  - `create-contract` — Generate new contracts
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Compact Language Version Configuration
3
+ *
4
+ * MAINTAINER: Update these values when Compact language syntax changes!
5
+ * See docs/SYNTAX_MAINTENANCE.md for the full update checklist.
6
+ */
7
+ /**
8
+ * Supported Compact language version range
9
+ * Update when new compiler versions are released
10
+ */
11
+ export declare const COMPACT_VERSION: {
12
+ /** Minimum supported version */
13
+ min: string;
14
+ /** Maximum supported version */
15
+ max: string;
16
+ /** When this config was last updated */
17
+ lastUpdated: string;
18
+ /** Source of truth for syntax patterns */
19
+ referenceSource: string;
20
+ };
21
+ /**
22
+ * Current pragma format that should be used in contracts
23
+ */
24
+ export declare const RECOMMENDED_PRAGMA: string;
25
+ /**
26
+ * Known deprecated patterns (add new ones here when Compact evolves)
27
+ */
28
+ export declare const DEPRECATED_PATTERNS: {
29
+ /** Deprecated in: 0.16 */
30
+ ledgerBlock: {
31
+ pattern: RegExp;
32
+ since: string;
33
+ replacement: string;
34
+ description: string;
35
+ };
36
+ /** Deprecated in: 0.15 */
37
+ cellWrapper: {
38
+ pattern: RegExp;
39
+ since: string;
40
+ replacement: string;
41
+ description: string;
42
+ };
43
+ /** Never existed */
44
+ voidType: {
45
+ pattern: RegExp;
46
+ since: string;
47
+ replacement: string;
48
+ description: string;
49
+ };
50
+ };
51
+ /**
52
+ * Reference contracts known to compile successfully
53
+ * Use these to verify syntax is still correct
54
+ */
55
+ export declare const REFERENCE_CONTRACTS: {
56
+ name: string;
57
+ repo: string;
58
+ description: string;
59
+ }[];
60
+ /**
61
+ * Get the version info as a string for display
62
+ */
63
+ export declare function getVersionInfo(): string;
64
+ /**
65
+ * Check if a version is within supported range
66
+ */
67
+ export declare function isVersionSupported(version: string): boolean;
68
+ //# sourceMappingURL=compact-version.d.ts.map
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Compact Language Version Configuration
3
+ *
4
+ * MAINTAINER: Update these values when Compact language syntax changes!
5
+ * See docs/SYNTAX_MAINTENANCE.md for the full update checklist.
6
+ */
7
+ /**
8
+ * Supported Compact language version range
9
+ * Update when new compiler versions are released
10
+ */
11
+ export const COMPACT_VERSION = {
12
+ /** Minimum supported version */
13
+ min: "0.16",
14
+ /** Maximum supported version */
15
+ max: "0.18",
16
+ /** When this config was last updated */
17
+ lastUpdated: "2025-01-26",
18
+ /** Source of truth for syntax patterns */
19
+ referenceSource: "https://github.com/piotr-iohk/template-contract",
20
+ };
21
+ /**
22
+ * Current pragma format that should be used in contracts
23
+ */
24
+ export const RECOMMENDED_PRAGMA = `pragma language_version >= ${COMPACT_VERSION.min} && <= ${COMPACT_VERSION.max};`;
25
+ /**
26
+ * Known deprecated patterns (add new ones here when Compact evolves)
27
+ */
28
+ export const DEPRECATED_PATTERNS = {
29
+ /** Deprecated in: 0.16 */
30
+ ledgerBlock: {
31
+ pattern: /ledger\s*\{/,
32
+ since: "0.16",
33
+ replacement: "export ledger fieldName: Type;",
34
+ description: "Block-style ledger declarations",
35
+ },
36
+ /** Deprecated in: 0.15 */
37
+ cellWrapper: {
38
+ pattern: /Cell\s*<\s*\w+\s*>/,
39
+ since: "0.15",
40
+ replacement: "Type (without Cell wrapper)",
41
+ description: "Cell<T> type wrapper",
42
+ },
43
+ /** Never existed */
44
+ voidType: {
45
+ pattern: /:\s*Void\b/,
46
+ since: "always",
47
+ replacement: "[] (empty tuple)",
48
+ description: "Void return type",
49
+ },
50
+ };
51
+ /**
52
+ * Reference contracts known to compile successfully
53
+ * Use these to verify syntax is still correct
54
+ */
55
+ export const REFERENCE_CONTRACTS = [
56
+ {
57
+ name: "template-contract",
58
+ repo: "piotr-iohk/template-contract",
59
+ description: "Official Midnight template contract",
60
+ },
61
+ {
62
+ name: "tokenomics-project",
63
+ repo: "piotr-iohk/tokenomics-project",
64
+ description: "Token implementation example",
65
+ },
66
+ {
67
+ name: "zswap-example",
68
+ repo: "piotr-iohk/zswap-example",
69
+ description: "Privacy-preserving swap example",
70
+ },
71
+ {
72
+ name: "reentrancy-example",
73
+ repo: "piotr-iohk/reentrancy-example",
74
+ description: "Cross-contract call patterns",
75
+ },
76
+ ];
77
+ /**
78
+ * Get the version info as a string for display
79
+ */
80
+ export function getVersionInfo() {
81
+ return `Compact ${COMPACT_VERSION.min}-${COMPACT_VERSION.max} (updated ${COMPACT_VERSION.lastUpdated})`;
82
+ }
83
+ /**
84
+ * Check if a version is within supported range
85
+ */
86
+ export function isVersionSupported(version) {
87
+ const [major, minor] = version.split(".").map(Number);
88
+ const [minMajor, minMinor] = COMPACT_VERSION.min.split(".").map(Number);
89
+ const [maxMajor, maxMinor] = COMPACT_VERSION.max.split(".").map(Number);
90
+ const versionNum = major * 100 + minor;
91
+ const minNum = minMajor * 100 + minMinor;
92
+ const maxNum = maxMajor * 100 + maxMinor;
93
+ return versionNum >= minNum && versionNum <= maxNum;
94
+ }
95
+ //# sourceMappingURL=compact-version.js.map
@@ -128,6 +128,35 @@ function generateCreateContractPrompt(args) {
128
128
  **Privacy Level:** ${privacyLevel}
129
129
  **Complexity:** ${complexity}
130
130
 
131
+ ## ⚠️ MANDATORY WORKFLOW - Follow these steps IN ORDER:
132
+
133
+ ### Step 1: Get Current Syntax
134
+ Call \`midnight-get-latest-syntax\` FIRST to get:
135
+ - The \`quickStartTemplate\` (use as your base)
136
+ - The \`commonMistakes\` array (avoid these errors)
137
+ - Current pragma format: \`pragma language_version >= 0.16 && <= 0.18;\`
138
+
139
+ ### Step 2: Generate Contract
140
+ Based on syntax reference, generate the contract using:
141
+ - Individual ledger declarations: \`export ledger field: Type;\` (NOT \`ledger { }\` blocks)
142
+ - Empty tuple return: \`circuit fn(): []\` (NOT \`Void\`)
143
+ - Export enums: \`export enum State { ... }\`
144
+ - Wrap witness conditionals: \`if (disclose(witness == value))\`
145
+
146
+ ### Step 3: Validate Before Returning
147
+ Call \`midnight-extract-contract-structure\` with your generated code to check for:
148
+ - deprecated_ledger_block
149
+ - invalid_void_type
150
+ - invalid_pragma_format
151
+ - unexported_enum
152
+ - deprecated_cell_wrapper
153
+
154
+ If ANY errors are found, fix them before returning the code to the user.
155
+
156
+ ---
157
+
158
+ ## Contract Requirements
159
+
131
160
  Please help me design and implement this contract. Consider:
132
161
 
133
162
  1. **State Design**
@@ -179,33 +208,57 @@ ${contractCode}
179
208
 
180
209
  **Focus Areas:** ${focusAreas}
181
210
 
211
+ ## ⚠️ MANDATORY WORKFLOW:
212
+
213
+ ### Step 1: Validate Syntax
214
+ Call \`midnight-extract-contract-structure\` with the contract code to check for:
215
+ - deprecated_ledger_block (should use \`export ledger field: Type;\`)
216
+ - invalid_void_type (should use \`[]\` not \`Void\`)
217
+ - invalid_pragma_format (should use \`>= 0.16 && <= 0.18\`)
218
+ - unexported_enum (enums need \`export\`)
219
+ - deprecated_cell_wrapper
220
+
221
+ Report ALL static analysis findings first.
222
+
223
+ ### Step 2: Get Latest Syntax Reference
224
+ If syntax errors are found, call \`midnight-get-latest-syntax\` to get:
225
+ - The \`commonMistakes\` array showing correct patterns
226
+ - Current syntax reference
227
+
228
+ ---
229
+
182
230
  Please analyze:
183
231
 
184
- 1. **Security Analysis**
232
+ 1. **Static Analysis Results** (from midnight-extract-contract-structure)
233
+ - Syntax errors found
234
+ - Deprecated patterns detected
235
+ - Required fixes
236
+
237
+ 2. **Security Analysis**
185
238
  - Input validation
186
239
  - Access control
187
240
  - State manipulation vulnerabilities
188
241
  - Assertion coverage
189
242
 
190
- 2. **Privacy Assessment**
243
+ 3. **Privacy Assessment**
191
244
  - Proper use of @private state
192
245
  - Information leakage risks
193
246
  - Correct use of disclose() and commit()
194
247
  - Privacy guarantees provided
195
248
 
196
- 3. **Best Practices**
249
+ 4. **Best Practices**
197
250
  - Code organization
198
251
  - Naming conventions
199
252
  - Documentation
200
253
  - Error messages
201
254
 
202
- 4. **Performance**
255
+ 5. **Performance**
203
256
  - Circuit complexity
204
257
  - State access patterns
205
258
  - Optimization opportunities
206
259
 
207
- 5. **Recommendations**
208
- - Critical issues to fix
260
+ 6. **Recommendations**
261
+ - Critical issues to fix (start with P0 syntax errors)
209
262
  - Improvements to consider
210
263
  - Alternative approaches
211
264
 
@@ -319,25 +372,46 @@ ${contractCode}
319
372
 
320
373
  **Error/Issue:** ${errorMessage}
321
374
 
375
+ ## ⚠️ MANDATORY WORKFLOW:
376
+
377
+ ### Step 1: Run Static Analysis
378
+ Call \`midnight-extract-contract-structure\` FIRST to check for common syntax errors:
379
+ - deprecated_ledger_block → should use \`export ledger field: Type;\`
380
+ - invalid_void_type → should use \`[]\` not \`Void\`
381
+ - invalid_pragma_format → should use \`>= 0.16 && <= 0.18\`
382
+ - unexported_enum → enums need \`export\` keyword
383
+
384
+ ### Step 2: Get Correct Syntax
385
+ If syntax errors found, call \`midnight-get-latest-syntax\` to get:
386
+ - The \`commonMistakes\` array with correct patterns
387
+ - Current \`quickStartTemplate\` for reference
388
+
389
+ ---
390
+
322
391
  Please help me debug by:
323
392
 
324
- 1. **Identifying the Problem**
393
+ 1. **Static Analysis Results**
394
+ - Run midnight-extract-contract-structure
395
+ - List all P0 syntax errors found
396
+ - Show the correct syntax for each error
397
+
398
+ 2. **Identifying the Problem**
325
399
  - What's causing the error?
326
400
  - Which line(s) are problematic?
327
401
 
328
- 2. **Explaining Why**
402
+ 3. **Explaining Why**
329
403
  - Root cause analysis
330
404
  - How Compact/ZK constraints work
331
405
 
332
- 3. **Providing a Fix**
333
- - Corrected code
406
+ 4. **Providing a Fix**
407
+ - Corrected code (validated against static analysis)
334
408
  - Explanation of changes
335
409
 
336
- 4. **Preventing Future Issues**
410
+ 5. **Preventing Future Issues**
337
411
  - Related pitfalls to watch for
338
412
  - Testing strategies
339
413
 
340
- 5. **Additional Improvements**
414
+ 6. **Additional Improvements**
341
415
  - Code quality suggestions
342
416
  - Best practices`,
343
417
  },
@@ -11,262 +11,388 @@
11
11
  * use the search_docs tool which queries the Vector DB.
12
12
  */
13
13
  export const EMBEDDED_DOCS = {
14
- "midnight://docs/compact-reference": `# Compact Language Quick Reference
14
+ "midnight://docs/compact-reference": `# Compact Language Syntax Reference (v0.16 - v0.18)
15
15
 
16
- A curated syntax reference for Compact - Midnight's smart contract language.
16
+ > **CRITICAL**: This reference is derived from **actual compiling contracts** in the Midnight ecosystem.
17
+ > Always verify syntax against this reference before generating contracts.
17
18
 
18
- > **Version Note**: This reference is for Compact 0.16+ (current). Some older examples may use deprecated syntax like \`Cell<T>\` wrappers - see "Common Pitfalls" section below.
19
+ ## Quick Start Template
19
20
 
20
- ## Basic Structure
21
+ Use this as a starting point - it compiles successfully:
21
22
 
22
23
  \`\`\`compact
23
- pragma language_version >= 0.14.0;
24
+ pragma language_version >= 0.16 && <= 0.18;
24
25
 
25
- include "std";
26
+ import CompactStandardLibrary;
27
+
28
+ // Ledger state (individual declarations, NOT a block)
29
+ export ledger counter: Counter;
30
+ export ledger owner: Bytes<32>;
31
+
32
+ // Witness for private/off-chain data
33
+ witness local_secret_key(): Bytes<32>;
34
+
35
+ // Circuit (returns [] not Void)
36
+ export circuit increment(): [] {
37
+ counter.increment(1);
38
+ }
39
+ \`\`\`
40
+
41
+ ---
42
+
43
+ ## 1. Pragma (Version Declaration)
44
+
45
+ **CORRECT** - use bounded range without patch version:
46
+ \`\`\`compact
47
+ pragma language_version >= 0.16 && <= 0.18;
48
+ \`\`\`
49
+
50
+ **WRONG** - these will cause parse errors:
51
+ \`\`\`compact
52
+ pragma language_version >= 0.14.0; // ❌ patch version not needed
53
+ pragma language_version >= 0.16.0 < 0.19.0; // ❌ wrong operator format
54
+ \`\`\`
55
+
56
+ ---
57
+
58
+ ## 2. Imports
59
+
60
+ Always import the standard library:
61
+ \`\`\`compact
62
+ import CompactStandardLibrary;
63
+ \`\`\`
64
+
65
+ For multi-file contracts, use \`include\`:
66
+ \`\`\`compact
67
+ include "types";
68
+ include "ledger";
69
+ include "circuits";
70
+ \`\`\`
71
+
72
+ ---
73
+
74
+ ## 3. Ledger Declarations
75
+
76
+ **CORRECT** - individual declarations with \`export ledger\`:
77
+ \`\`\`compact
78
+ export ledger counter: Counter;
79
+ export ledger owner: Bytes<32>;
80
+ export ledger balances: Map<Bytes<32>, Uint<64>>;
81
+
82
+ // Private state (off-chain only)
83
+ ledger secretValue: Field; // no export = private
84
+ \`\`\`
26
85
 
86
+ **WRONG** - block syntax is deprecated:
87
+ \`\`\`compact
88
+ // ❌ This causes parse error: found "{" looking for an identifier
27
89
  ledger {
28
- // Public state (on-chain, visible to everyone)
29
90
  counter: Counter;
30
-
31
- // Private state (off-chain, only owner can see)
32
- @private
33
- secretValue: Field;
91
+ owner: Bytes<32>;
34
92
  }
93
+ \`\`\`
35
94
 
36
- // Circuit - generates ZK proof
37
- export circuit increment(amount: Field): Void {
38
- assert(amount > 0);
39
- ledger.counter.increment(amount);
40
- }
95
+ ### Ledger Modifiers
41
96
 
42
- // Witness - off-chain computation
43
- witness getSecret(): Field {
44
- return ledger.secretValue;
45
- }
97
+ \`\`\`compact
98
+ export ledger publicData: Field; // Public, readable by anyone
99
+ export sealed ledger immutableData: Field; // Set once in constructor, cannot change
100
+ ledger privateData: Field; // Private, not exported
46
101
  \`\`\`
47
102
 
48
- ## Data Types
103
+ ---
104
+
105
+ ## 4. Data Types
49
106
 
50
107
  ### Primitive Types
51
- - \`Field\` - Finite field element (basic numeric type)
52
- - \`Boolean\` - True or false
53
- - \`Bytes<N>\` - Fixed-size byte array
54
- - \`Uint<N>\` - Unsigned integer (N = 8, 16, 32, 64, 128, 256)
108
+ | Type | Description | Example |
109
+ |------|-------------|---------|
110
+ | \`Field\` | Finite field element (basic numeric) | \`amount: Field\` |
111
+ | \`Boolean\` | True or false | \`isActive: Boolean\` |
112
+ | \`Bytes<N>\` | Fixed-size byte array | \`hash: Bytes<32>\` |
113
+ | \`Uint<N>\` | Unsigned integer (N = 8, 16, 32, 64, 128, 256) | \`balance: Uint<64>\` |
114
+ | \`Uint<MIN..MAX>\` | Bounded unsigned integer | \`score: Uint<0..100>\` |
55
115
 
56
116
  ### Collection Types
57
- - \`Counter\` - Incrementable/decrementable counter
58
- - \`Map<K, V>\` - Key-value mapping
59
- - \`Set<T>\` - Collection of unique values
60
- - \`Opaque<T>\` - Type-safe wrapper for arbitrary data
61
-
62
- ## Circuits
63
-
64
- Circuits are functions that generate zero-knowledge proofs:
117
+ | Type | Description | Example |
118
+ |------|-------------|---------|
119
+ | \`Counter\` | Incrementable/decrementable | \`count: Counter\` |
120
+ | \`Map<K, V>\` | Key-value mapping | \`Map<Bytes<32>, Uint<64>>\` |
121
+ | \`Set<T>\` | Unique value collection | \`Set<Bytes<32>>\` |
122
+ | \`Vector<N, T>\` | Fixed-size array | \`Vector<3, Field>\` |
123
+ | \`List<T>\` | Dynamic list | \`List<Bytes<32>>\` |
124
+ | \`Maybe<T>\` | Optional value | \`Maybe<Bytes<32>>\` |
125
+ | \`Either<L, R>\` | Union type | \`Either<Field, Bytes<32>>\` |
126
+ | \`Opaque<"type">\` | External type from TypeScript | \`Opaque<"string">\` |
127
+
128
+ ### Custom Types
129
+
130
+ **Enums** - must use \`export\` to access from TypeScript:
131
+ \`\`\`compact
132
+ export enum GameState { waiting, playing, finished }
133
+ export enum Choice { rock, paper, scissors }
134
+ \`\`\`
65
135
 
136
+ **Structs**:
66
137
  \`\`\`compact
67
- export circuit transfer(to: Address, amount: Field): Void {
68
- // Assertions create ZK constraints
69
- assert(amount > 0);
70
- assert(ledger.balance.value() >= amount);
71
-
72
- // State modifications
73
- ledger.balance.decrement(amount);
138
+ export struct PlayerConfig {
139
+ name: Opaque<"string">,
140
+ score: Uint<32>,
141
+ isActive: Boolean,
74
142
  }
75
143
  \`\`\`
76
144
 
77
- ### Key Points:
78
- - \`export\` makes circuit callable from outside
79
- - Must be deterministic (same inputs = same outputs)
80
- - Cannot access external data directly (use witnesses)
81
- - Assertions become ZK constraints
145
+ ---
146
+
147
+ ## 5. Circuits
82
148
 
83
- ## Witnesses
149
+ Circuits are on-chain functions that generate ZK proofs.
84
150
 
85
- Witnesses provide off-chain data to circuits:
151
+ **CRITICAL**: Return type is \`[]\` (empty tuple), NOT \`Void\`:
86
152
 
87
153
  \`\`\`compact
88
- witness getCurrentPrice(): Field {
89
- // This runs off-chain
90
- return fetchPrice();
154
+ // CORRECT - returns []
155
+ export circuit increment(): [] {
156
+ counter.increment(1);
157
+ }
158
+
159
+ // CORRECT - with parameters
160
+ export circuit transfer(to: Bytes<32>, amount: Uint<64>): [] {
161
+ assert(amount > 0, "Amount must be positive");
162
+ // ... logic
163
+ }
164
+
165
+ // CORRECT - with return value
166
+ export circuit getBalance(addr: Bytes<32>): Uint<64> {
167
+ return balances.lookup(addr);
91
168
  }
92
169
 
93
- export circuit buyAtMarketPrice(maxPrice: Field): Void {
94
- const price = getCurrentPrice();
95
- assert(price <= maxPrice);
96
- // ... execute purchase
170
+ // WRONG - Void does not exist
171
+ export circuit broken(): Void { // Parse error
172
+ counter.increment(1);
97
173
  }
98
174
  \`\`\`
99
175
 
100
- ### Key Points:
101
- - Run locally, not on-chain
102
- - Can access external APIs, databases
103
- - Cannot modify ledger state directly
104
- - Results are private unless disclosed
176
+ ### Circuit Modifiers
105
177
 
106
- ## State Management
178
+ \`\`\`compact
179
+ export circuit publicFn(): [] // Callable externally
180
+ circuit internalFn(): [] // Internal only, not exported
181
+ export pure circuit hash(x: Field): Bytes<32> // No state access
182
+ \`\`\`
183
+
184
+ ---
185
+
186
+ ## 6. Witnesses
187
+
188
+ Witnesses provide off-chain/private data to circuits. They run locally, not on-chain.
107
189
 
108
- ### Public State
109
190
  \`\`\`compact
110
- ledger {
111
- publicCounter: Counter;
112
- publicMap: Map<Field, Field>;
113
- }
191
+ // Basic witness - returns private data
192
+ witness local_secret_key(): Bytes<32>;
193
+
194
+ // Witness with parameters
195
+ witness get_merkle_path(leaf: Bytes<32>): MerkleTreePath<10, Bytes<32>>;
196
+
197
+ // Witness that returns nothing (side effect only)
198
+ witness store_locally(data: Field): [];
199
+
200
+ // Witness returning optional
201
+ witness find_user(id: Bytes<32>): Maybe<UserData>;
114
202
  \`\`\`
115
203
 
116
- ### Private State
204
+ ---
205
+
206
+ ## 7. Constructor
207
+
208
+ Optional - initializes sealed ledger fields at deploy time:
209
+
117
210
  \`\`\`compact
118
- ledger {
119
- @private
120
- secretBalance: Field;
121
-
122
- @private
123
- hiddenVotes: Map<Address, Field>;
211
+ export sealed ledger owner: Bytes<32>;
212
+ export sealed ledger nonce: Bytes<32>;
213
+
214
+ constructor(initNonce: Bytes<32>) {
215
+ owner = disclose(public_key(local_secret_key()));
216
+ nonce = disclose(initNonce);
124
217
  }
125
218
  \`\`\`
126
219
 
127
- ## Common Patterns
220
+ ---
128
221
 
129
- ### Access Control
222
+ ## 8. Common Patterns
223
+
224
+ ### Authentication Pattern
130
225
  \`\`\`compact
131
- ledger {
132
- owner: Opaque<"address">;
133
- }
226
+ witness local_secret_key(): Bytes<32>;
134
227
 
135
- witness getCaller(): Opaque<"address"> {
136
- return context.caller;
228
+ circuit get_public_key(sk: Bytes<32>): Bytes<32> {
229
+ return persistentHash<Vector<2, Bytes<32>>>([pad(32, "myapp:pk:"), sk]);
137
230
  }
138
231
 
139
- export circuit ownerOnly(): Void {
140
- assert(getCaller() == ledger.owner, "Not owner");
232
+ export circuit authenticated_action(): [] {
233
+ const sk = local_secret_key();
234
+ const caller = get_public_key(sk);
235
+ assert(disclose(caller == owner), "Not authorized");
236
+ // ... action
141
237
  }
142
238
  \`\`\`
143
239
 
144
- ### Disclosure
240
+ ### Commit-Reveal Pattern
145
241
  \`\`\`compact
146
- export circuit revealSecret(): Field {
147
- const secret = getPrivateData();
148
- return disclose(secret); // Makes private data public
242
+ export ledger commitment: Bytes<32>;
243
+ export ledger revealed: Boolean;
244
+
245
+ witness get_stored_value(): Field;
246
+ witness store_value(v: Field): [];
247
+
248
+ export circuit commit(value: Field): [] {
249
+ const sk = local_secret_key();
250
+ store_value(value);
251
+ commitment = disclose(persistentHash<Vector<2, Bytes<32>>>([
252
+ value as Bytes<32>,
253
+ sk
254
+ ]));
255
+ }
256
+
257
+ export circuit reveal(): Field {
258
+ const sk = local_secret_key();
259
+ const value = get_stored_value();
260
+ const expected = persistentHash<Vector<2, Bytes<32>>>([
261
+ value as Bytes<32>,
262
+ sk
263
+ ]);
264
+ assert(disclose(expected == commitment), "Mismatch");
265
+ revealed = true;
266
+ return disclose(value);
149
267
  }
150
268
  \`\`\`
151
269
 
152
- ### Disclosure in Conditionals (IMPORTANT)
153
- When using witness values in if/else conditions, you MUST explicitly disclose the comparison result:
270
+ ### Disclosure in Conditionals
271
+ When branching on witness values, wrap comparisons in \`disclose()\`:
154
272
 
155
273
  \`\`\`compact
156
- // ❌ WRONG - compiler error: "potential witness-value disclosure must be declared"
157
- witness getSecret(): Field { return 42; }
158
- export circuit checkValue(guess: Field): Boolean {
159
- const secret = getSecret();
160
- if (guess == secret) { // ERROR: implicit disclosure
274
+ // CORRECT
275
+ export circuit check(guess: Field): Boolean {
276
+ const secret = get_secret(); // witness
277
+ if (disclose(guess == secret)) {
161
278
  return true;
162
279
  }
163
280
  return false;
164
281
  }
165
282
 
166
- // CORRECT - wrap comparison in disclose()
167
- export circuit checkValue(guess: Field): Boolean {
168
- const secret = getSecret();
169
- if (disclose(guess == secret)) { // Explicitly acknowledge disclosure
283
+ // WRONG - will not compile
284
+ export circuit check_broken(guess: Field): Boolean {
285
+ const secret = get_secret();
286
+ if (guess == secret) { // implicit disclosure error
170
287
  return true;
171
288
  }
172
289
  return false;
173
290
  }
174
-
175
- // ✅ Multiple comparisons
176
- export circuit giveHint(guess: Field): Hint {
177
- const secret = getSecret();
178
- if (disclose(guess == secret)) {
179
- return Hint.correct;
180
- } else if (disclose(guess > secret)) {
181
- return Hint.too_high;
182
- } else {
183
- return Hint.too_low;
184
- }
185
- }
186
291
  \`\`\`
187
292
 
188
- **Why?** When you branch on a comparison involving private (witness) values, the boolean result becomes observable on-chain. Compact requires explicit acknowledgment via \`disclose()\` to prevent accidental privacy leaks.
293
+ ---
189
294
 
190
- ### Assertions
295
+ ## 9. Common Operations
296
+
297
+ ### Counter Operations
191
298
  \`\`\`compact
192
- assert(condition); // Basic assertion
193
- assert(condition, "Error message"); // With message
299
+ counter.increment(1);
300
+ counter.decrement(1);
301
+ const val = counter.value(); // Note: returns as Field
194
302
  \`\`\`
195
303
 
196
- ## Common Pitfalls & Solutions
197
-
198
- ### 1. Cell<T> Wrapper (Deprecated)
304
+ ### Map Operations
199
305
  \`\`\`compact
200
- // ❌ OLD SYNTAX (pre-0.15) - causes "unbound identifier Cell"
201
- export ledger myValue: Cell<Field>;
202
- myValue.write(42);
203
- const x = myValue.read();
204
-
205
- // ✅ CURRENT SYNTAX (0.16+) - direct declaration
206
- export ledger myValue: Field;
207
- myValue = 42;
208
- const x = myValue;
306
+ balances.insert(address, 100);
307
+ const balance = balances.lookup(address);
308
+ const exists = balances.member(address);
209
309
  \`\`\`
210
310
 
211
- ### 2. Opaque String Assignment
311
+ ### Set Operations
212
312
  \`\`\`compact
213
- // ❌ WRONG - cannot assign string literals to Opaque
214
- export ledger message: Opaque<"string">;
215
- export circuit setMessage(): Void {
216
- message = "hello"; // ERROR!
217
- }
313
+ members.insert(address);
314
+ const isMember = members.member(address);
315
+ \`\`\`
218
316
 
219
- // CORRECT - use enum for fixed values
220
- export enum Status { pending, approved, rejected }
221
- export ledger status: Status;
222
- export circuit setStatus(): Void {
223
- status = Status.approved; // Works!
224
- }
317
+ ### Maybe Operations
318
+ \`\`\`compact
319
+ const opt: Maybe<Field> = some<Field>(42);
320
+ const empty: Maybe<Field> = none<Field>();
225
321
 
226
- // Or receive Opaque from parameter/witness
227
- export circuit setMessage(msg: Opaque<"string">): Void {
228
- message = msg; // msg comes from TypeScript
322
+ if (opt.is_some) {
323
+ const val = opt.value;
229
324
  }
230
325
  \`\`\`
231
326
 
232
- ### 3. Counter vs Field
327
+ ### Type Casting
233
328
  \`\`\`compact
234
- // Counter has special methods
235
- export ledger count: Counter;
236
- count.increment(1);
237
- count.decrement(1);
238
- const val = count.value();
239
-
240
- // Field uses direct assignment
241
- export ledger amount: Field;
242
- amount = amount + 1;
243
- const val = amount;
329
+ const f: Field = counter.value(); // Counter to Field
330
+ const bytes: Bytes<32> = f as Bytes<32>; // Field to Bytes
331
+ const num: Uint<64> = f as Uint<64>; // Field to Uint
244
332
  \`\`\`
245
333
 
246
- ### 4. Map Initialization
334
+ ### Hashing
247
335
  \`\`\`compact
248
- // Maps don't need initialization
249
- export ledger balances: Map<Bytes<32>, Field>;
336
+ // Persistent hash (same input = same output across calls)
337
+ const hash = persistentHash<Vector<2, Bytes<32>>>([data1, data2]);
250
338
 
251
- // Access with .member()
252
- const balance = balances.member(address);
253
- balances.insert(address, 100);
339
+ // Transient hash (includes randomness)
340
+ const commit = transientCommit<Field>(value, randomness);
254
341
  \`\`\`
255
342
 
256
- ### 5. Boolean Returns with Witnesses
343
+ ---
344
+
345
+ ## 10. Assertions
346
+
257
347
  \`\`\`compact
258
- // WRONG - returns private boolean without disclosure
259
- export circuit isOwner(): Boolean {
260
- const caller = getCaller(); // witness
261
- return caller == owner; // ERROR: undisclosed
262
- }
348
+ assert(condition, "Error message");
349
+ assert(amount > 0, "Amount must be positive");
350
+ assert(disclose(caller == owner), "Not authorized");
351
+ \`\`\`
263
352
 
264
- // ✅ CORRECT - disclose the result
265
- export circuit isOwner(): Boolean {
266
- const caller = getCaller();
267
- return disclose(caller == owner);
268
- }
353
+ ---
354
+
355
+ ## 11. Common Mistakes to Avoid
356
+
357
+ | Mistake | Correct |
358
+ |---------|---------|
359
+ | \`ledger { field: Type; }\` | \`export ledger field: Type;\` |
360
+ | \`circuit fn(): Void\` | \`circuit fn(): []\` |
361
+ | \`pragma >= 0.16.0\` | \`pragma >= 0.16 && <= 0.18\` |
362
+ | \`enum State { ... }\` | \`export enum State { ... }\` |
363
+ | \`if (witness_val == x)\` | \`if (disclose(witness_val == x))\` |
364
+ | \`Cell<Field>\` | \`Field\` (Cell is deprecated) |
365
+ | \`myValue.read()\` / \`.write()\` | Direct assignment: \`myValue = x\` |
366
+
367
+ ---
368
+
369
+ ## 12. Exports for TypeScript
370
+
371
+ To use types/values in TypeScript, they must be exported:
372
+
373
+ \`\`\`compact
374
+ // These are accessible from TypeScript
375
+ export enum GameState { waiting, playing }
376
+ export struct Config { value: Field }
377
+ export ledger counter: Counter;
378
+ export circuit play(): []
379
+
380
+ // Standard library re-exports (if needed in TS)
381
+ export { Maybe, Either, CoinInfo };
269
382
  \`\`\`
383
+
384
+ ---
385
+
386
+ ## Reference Contracts
387
+
388
+ These contracts compile successfully and demonstrate correct patterns:
389
+
390
+ 1. **Counter** (beginner): \`midnightntwrk/example-counter\`
391
+ 2. **Bulletin Board** (intermediate): \`midnightntwrk/example-bboard\`
392
+ 3. **Naval Battle Game** (advanced): \`ErickRomeroDev/naval-battle-game_v2\`
393
+ 4. **Sea Battle** (advanced): \`bricktowers/midnight-seabattle\`
394
+
395
+ When in doubt, reference these repos for working syntax.
270
396
  `,
271
397
  "midnight://docs/sdk-api": `# Midnight TypeScript SDK Quick Reference
272
398
 
package/dist/server.d.ts CHANGED
@@ -8,6 +8,10 @@ export declare function getUpdateWarning(): string | null;
8
8
  * Clear all subscriptions (useful for server restart/testing)
9
9
  */
10
10
  export declare function clearSubscriptions(): void;
11
+ /**
12
+ * Mark server as connected (safe to send notifications)
13
+ */
14
+ export declare function setServerConnected(connected: boolean): void;
11
15
  /**
12
16
  * Send a log message to the MCP client
13
17
  * This allows clients to see server logs for debugging
package/dist/server.js CHANGED
@@ -8,7 +8,7 @@ import { allResources, getDocumentation, getCode, getSchema, } from "./resources
8
8
  import { promptDefinitions, generatePrompt } from "./prompts/index.js";
9
9
  import { registerSamplingCallback } from "./services/index.js";
10
10
  // Server information - version should match package.json
11
- const CURRENT_VERSION = "0.1.32";
11
+ const CURRENT_VERSION = "0.1.33";
12
12
  const SERVER_INFO = {
13
13
  name: "midnight-mcp",
14
14
  version: CURRENT_VERSION,
@@ -101,12 +101,21 @@ const resourceTemplates = [
101
101
  let mcpLogLevel = "info";
102
102
  // Server instance for sending notifications
103
103
  let serverInstance = null;
104
+ // Track if server is connected (can send notifications)
105
+ let isConnected = false;
106
+ /**
107
+ * Mark server as connected (safe to send notifications)
108
+ */
109
+ export function setServerConnected(connected) {
110
+ isConnected = connected;
111
+ }
104
112
  /**
105
113
  * Send a log message to the MCP client
106
114
  * This allows clients to see server logs for debugging
107
115
  */
108
116
  export function sendLogToClient(level, loggerName, data) {
109
- if (!serverInstance)
117
+ // Only send if server exists AND is connected
118
+ if (!serverInstance || !isConnected)
110
119
  return;
111
120
  // Map levels to numeric values for comparison
112
121
  const levelValues = {
@@ -667,6 +676,8 @@ export async function startServer() {
667
676
  const server = await initializeServer();
668
677
  const transport = new StdioServerTransport();
669
678
  await server.connect(transport);
679
+ // Now safe to send notifications
680
+ setServerConnected(true);
670
681
  logger.info("Midnight MCP Server running on stdio");
671
682
  }
672
683
  //# sourceMappingURL=server.js.map
@@ -158,13 +158,29 @@ export declare function compareSyntax(input: CompareSyntaxInput): Promise<{
158
158
  * This is the source of truth for writing valid, compilable contracts
159
159
  */
160
160
  export declare function getLatestSyntax(input: GetLatestSyntaxInput): Promise<{
161
+ quickStartTemplate: string;
162
+ commonMistakes: {
163
+ wrong: string;
164
+ correct: string;
165
+ error: string;
166
+ }[];
161
167
  syntaxReference: string;
162
168
  sections: string[];
163
- pitfalls: string[];
169
+ referenceContracts: {
170
+ name: string;
171
+ repo: string;
172
+ description: string;
173
+ }[];
164
174
  note: string;
165
175
  versionWarning?: string | undefined;
166
176
  repository: string;
167
177
  version: string;
178
+ versionConfig: {
179
+ min: string;
180
+ max: string;
181
+ lastUpdated: string;
182
+ maintenanceGuide: string;
183
+ };
168
184
  warning?: undefined;
169
185
  syntaxFiles?: undefined;
170
186
  examplePaths?: undefined;
@@ -8,6 +8,7 @@ import { logger, DEFAULT_REPOSITORIES, SelfCorrectionHints, } from "../../utils/
8
8
  import { sendProgressNotification } from "../../server.js";
9
9
  import { REPO_ALIASES, EXAMPLES } from "./constants.js";
10
10
  import { EMBEDDED_DOCS } from "../../resources/content/docs-content.js";
11
+ import { COMPACT_VERSION, RECOMMENDED_PRAGMA, REFERENCE_CONTRACTS, } from "../../config/compact-version.js";
11
12
  // Re-export validation handlers from validation.ts
12
13
  export { extractContractStructure } from "./validation.js";
13
14
  /**
@@ -310,7 +311,7 @@ export async function getLatestSyntax(input) {
310
311
  if (repoName === "compact" || repoName === "midnight-compact") {
311
312
  const compactReference = EMBEDDED_DOCS["midnight://docs/compact-reference"];
312
313
  // Check if there's a newer release we might not have documented
313
- const EMBEDDED_DOCS_VERSION = "0.16"; // Version our docs are based on
314
+ // Version config is centralized in src/config/compact-version.ts
314
315
  let versionWarning;
315
316
  try {
316
317
  const versionInfo = await releaseTracker.getVersionInfo("midnightntwrk", "compact");
@@ -322,12 +323,9 @@ export async function getLatestSyntax(input) {
322
323
  .split(".")
323
324
  .slice(0, 2)
324
325
  .join(".");
325
- const embeddedMajorMinor = EMBEDDED_DOCS_VERSION.split(".")
326
- .slice(0, 2)
327
- .join(".");
328
- if (latestVersion !== embeddedMajorMinor &&
329
- parseFloat(latestVersion) > parseFloat(embeddedMajorMinor)) {
330
- versionWarning = `⚠️ Compact ${latestTag} is available. This reference is based on ${EMBEDDED_DOCS_VERSION}. Some syntax may have changed - check release notes for breaking changes.`;
326
+ if (latestVersion !== COMPACT_VERSION.max &&
327
+ parseFloat(latestVersion) > parseFloat(COMPACT_VERSION.max)) {
328
+ versionWarning = `⚠️ Compact ${latestTag} is available. This reference is based on ${COMPACT_VERSION.max}. Some syntax may have changed - check release notes for breaking changes. See docs/SYNTAX_MAINTENANCE.md for update instructions.`;
331
329
  }
332
330
  }
333
331
  }
@@ -337,27 +335,83 @@ export async function getLatestSyntax(input) {
337
335
  if (compactReference) {
338
336
  return {
339
337
  repository: "midnightntwrk/compact",
340
- version: "0.16+ (current)",
338
+ version: `${COMPACT_VERSION.min}-${COMPACT_VERSION.max} (current)`,
339
+ versionConfig: {
340
+ min: COMPACT_VERSION.min,
341
+ max: COMPACT_VERSION.max,
342
+ lastUpdated: COMPACT_VERSION.lastUpdated,
343
+ maintenanceGuide: "See docs/SYNTAX_MAINTENANCE.md for update instructions",
344
+ },
341
345
  ...(versionWarning && { versionWarning }),
346
+ // Quick start template - ALWAYS compiles
347
+ quickStartTemplate: `${RECOMMENDED_PRAGMA}
348
+
349
+ import CompactStandardLibrary;
350
+
351
+ export ledger counter: Counter;
352
+ export ledger owner: Bytes<32>;
353
+
354
+ witness local_secret_key(): Bytes<32>;
355
+
356
+ export circuit increment(): [] {
357
+ counter.increment(1);
358
+ }`,
359
+ // Common mistakes that cause compilation failures
360
+ commonMistakes: [
361
+ {
362
+ wrong: "ledger { field: Type; }",
363
+ correct: "export ledger field: Type;",
364
+ error: 'parse error: found "{" looking for an identifier',
365
+ },
366
+ {
367
+ wrong: "circuit fn(): Void",
368
+ correct: "circuit fn(): []",
369
+ error: 'parse error: found "{" looking for ";"',
370
+ },
371
+ {
372
+ wrong: "pragma language_version >= 0.14.0;",
373
+ correct: RECOMMENDED_PRAGMA,
374
+ error: "version mismatch or parse error",
375
+ },
376
+ {
377
+ wrong: "enum State { a, b }",
378
+ correct: "export enum State { a, b }",
379
+ error: "enum not accessible from TypeScript",
380
+ },
381
+ {
382
+ wrong: "if (witness_val == x)",
383
+ correct: "if (disclose(witness_val == x))",
384
+ error: "implicit disclosure error",
385
+ },
386
+ {
387
+ wrong: "Cell<Field>",
388
+ correct: "Field",
389
+ error: "unbound identifier Cell (deprecated)",
390
+ },
391
+ ],
342
392
  syntaxReference: compactReference,
343
393
  sections: [
344
- "Basic Structure",
394
+ "Quick Start Template",
395
+ "Pragma (Version Declaration)",
396
+ "Imports",
397
+ "Ledger Declarations",
345
398
  "Data Types",
346
399
  "Circuits",
347
400
  "Witnesses",
348
- "State Management",
401
+ "Constructor",
349
402
  "Common Patterns",
350
- "Disclosure in Conditionals (IMPORTANT)",
351
- "Common Pitfalls & Solutions",
352
- ],
353
- pitfalls: [
354
- "Cell<T> wrapper deprecated in 0.15+ - use direct type",
355
- 'Cannot assign string literals to Opaque<"string"> - use enum or parameters',
356
- "Must disclose() comparisons used in if/else conditions",
357
- "Counter uses .increment()/.value(), Field uses direct assignment",
358
- "Boolean returns from witnesses need disclose()",
403
+ "Common Operations",
404
+ "Assertions",
405
+ "Common Mistakes to Avoid",
406
+ "Exports for TypeScript",
407
+ "Reference Contracts",
359
408
  ],
360
- note: "This is the curated syntax reference for Compact 0.16+. Includes common pitfalls and correct patterns.",
409
+ referenceContracts: REFERENCE_CONTRACTS.map((rc) => ({
410
+ name: rc.name,
411
+ repo: rc.repo,
412
+ description: rc.description,
413
+ })),
414
+ note: `CRITICAL: Use quickStartTemplate as your base. Check commonMistakes before submitting code. This reference is for Compact ${COMPACT_VERSION.min}-${COMPACT_VERSION.max} (last updated: ${COMPACT_VERSION.lastUpdated}).`,
361
415
  };
362
416
  }
363
417
  }
@@ -359,10 +359,12 @@ export const repositoryTools = [
359
359
  {
360
360
  name: "midnight-extract-contract-structure",
361
361
  description: "Extract and analyze Compact contract structure (circuits, witnesses, ledger). " +
362
- "Detects common issues: module-level const, stdlib name collisions, if-expression in assignments, Void return type, " +
363
- "Counter.value access, division operator, missing disclose() calls. " +
364
- "Use for understanding contract structure and catching common syntax mistakes. " +
365
- "Note: This is static analysis - it catches common patterns but cannot verify semantic correctness.",
362
+ "CRITICAL CHECKS: deprecated 'ledger { }' block syntax, 'Void' return type (should be []), " +
363
+ "old pragma format, unexported enums, deprecated Cell<T> wrapper. " +
364
+ "Also detects: module-level const, stdlib name collisions, division operator, " +
365
+ "Counter.value access, missing disclose() calls, potential overflow. " +
366
+ "Use BEFORE generating contracts to catch syntax errors. " +
367
+ "Note: Static analysis only - catches common patterns but not semantic errors.",
366
368
  inputSchema: {
367
369
  type: "object",
368
370
  properties: {
@@ -440,6 +440,77 @@ export async function extractContractStructure(input) {
440
440
  "Opaque",
441
441
  "Vector",
442
442
  ];
443
+ // ========== CRITICAL SYNTAX CHECKS (P0 - causes immediate compilation failure) ==========
444
+ // P0-1. Detect deprecated ledger block syntax
445
+ const ledgerBlockPattern = /ledger\s*\{/g;
446
+ let ledgerBlockMatch;
447
+ while ((ledgerBlockMatch = ledgerBlockPattern.exec(code)) !== null) {
448
+ const lineNum = lineByIndex[ledgerBlockMatch.index] || 1;
449
+ potentialIssues.push({
450
+ type: "deprecated_ledger_block",
451
+ line: lineNum,
452
+ message: `Deprecated ledger block syntax 'ledger { }' - causes parse error`,
453
+ suggestion: `Use individual declarations: 'export ledger fieldName: Type;'`,
454
+ severity: "error",
455
+ });
456
+ }
457
+ // P0-2. Detect Void return type (doesn't exist in Compact)
458
+ const voidReturnPattern = /circuit\s+\w+\s*\([^)]*\)\s*:\s*Void\b/g;
459
+ let voidMatch;
460
+ while ((voidMatch = voidReturnPattern.exec(code)) !== null) {
461
+ const lineNum = lineByIndex[voidMatch.index] || 1;
462
+ potentialIssues.push({
463
+ type: "invalid_void_type",
464
+ line: lineNum,
465
+ message: `Invalid return type 'Void' - Void does not exist in Compact`,
466
+ suggestion: `Use '[]' (empty tuple) for circuits that return nothing: 'circuit fn(): []'`,
467
+ severity: "error",
468
+ });
469
+ }
470
+ // P0-3. Detect old pragma format with patch version
471
+ const oldPragmaPattern = /pragma\s+language_version\s*>=?\s*\d+\.\d+\.\d+/g;
472
+ let oldPragmaMatch;
473
+ while ((oldPragmaMatch = oldPragmaPattern.exec(code)) !== null) {
474
+ const lineNum = lineByIndex[oldPragmaMatch.index] || 1;
475
+ potentialIssues.push({
476
+ type: "invalid_pragma_format",
477
+ line: lineNum,
478
+ message: `Pragma includes patch version which may cause parse errors`,
479
+ suggestion: `Use bounded range format: 'pragma language_version >= 0.16 && <= 0.18;'`,
480
+ severity: "error",
481
+ });
482
+ }
483
+ // P0-4. Detect missing export on enums (won't be accessible from TypeScript)
484
+ const unexportedEnumPattern = /(?<!export\s+)enum\s+(\w+)\s*\{/g;
485
+ let unexportedEnumMatch;
486
+ while ((unexportedEnumMatch = unexportedEnumPattern.exec(code)) !== null) {
487
+ // Double check it's not preceded by export
488
+ const before = code.substring(Math.max(0, unexportedEnumMatch.index - 10), unexportedEnumMatch.index);
489
+ if (!before.includes("export")) {
490
+ const lineNum = lineByIndex[unexportedEnumMatch.index] || 1;
491
+ potentialIssues.push({
492
+ type: "unexported_enum",
493
+ line: lineNum,
494
+ message: `Enum '${unexportedEnumMatch[1]}' is not exported - won't be accessible from TypeScript`,
495
+ suggestion: `Add 'export' keyword: 'export enum ${unexportedEnumMatch[1]} { ... }'`,
496
+ severity: "warning",
497
+ });
498
+ }
499
+ }
500
+ // P0-5. Detect Cell<T> wrapper (deprecated since 0.15)
501
+ const cellPattern = /Cell\s*<\s*\w+\s*>/g;
502
+ let cellMatch;
503
+ while ((cellMatch = cellPattern.exec(code)) !== null) {
504
+ const lineNum = lineByIndex[cellMatch.index] || 1;
505
+ potentialIssues.push({
506
+ type: "deprecated_cell_wrapper",
507
+ line: lineNum,
508
+ message: `'Cell<T>' wrapper is deprecated since Compact 0.15`,
509
+ suggestion: `Use the type directly: 'Field' instead of 'Cell<Field>'`,
510
+ severity: "error",
511
+ });
512
+ }
513
+ // ========== EXISTING CHECKS ==========
443
514
  // 1. Detect module-level const (not supported in Compact)
444
515
  const constPattern = /^const\s+(\w+)\s*:/gm;
445
516
  let constMatch;
@@ -693,19 +764,7 @@ export async function extractContractStructure(input) {
693
764
  severity: "error",
694
765
  });
695
766
  }
696
- // 12. Detect "Void" return type (should use [] empty tuple)
697
- const voidReturnPattern = /circuit\s+\w+\s*\([^)]*\)\s*:\s*Void\b/g;
698
- let voidMatch;
699
- while ((voidMatch = voidReturnPattern.exec(code)) !== null) {
700
- const lineNum = lineByIndex[voidMatch.index] || 1;
701
- potentialIssues.push({
702
- type: "invalid_void_type",
703
- line: lineNum,
704
- message: `'Void' is not a valid type in Compact`,
705
- suggestion: `Use empty tuple '[]' for circuits with no return value: 'circuit name(): []'`,
706
- severity: "error",
707
- });
708
- }
767
+ // 12. (Moved to P0-2 above - Void return type check)
709
768
  const summary = [];
710
769
  if (circuits.length > 0) {
711
770
  summary.push(`${circuits.length} circuit(s)`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "midnight-mcp",
3
- "version": "0.1.32",
3
+ "version": "0.1.34",
4
4
  "description": "Model Context Protocol Server for Midnight Blockchain Development",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",