midnight-mcp 0.2.18 → 0.2.20

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.
@@ -25,7 +25,7 @@ import {
25
25
  validateNumber,
26
26
  validateQuery,
27
27
  vectorStore
28
- } from "./chunk-WVMAQ2LK.js";
28
+ } from "./chunk-RCQYFKD3.js";
29
29
 
30
30
  // src/tools/search/schemas.ts
31
31
  import { z } from "zod";
@@ -1698,7 +1698,14 @@ var REPO_ALIASES = {
1698
1698
  lucentlabs: { owner: "statera-protocol", repo: "statera-protocol-midnight" },
1699
1699
  stablecoin: { owner: "statera-protocol", repo: "statera-protocol-midnight" },
1700
1700
  "midnight-bank": { owner: "nel349", repo: "midnight-bank" },
1701
- bank: { owner: "nel349", repo: "midnight-bank" }
1701
+ bank: { owner: "nel349", repo: "midnight-bank" },
1702
+ // Third-Party / Community (NOT official Midnight, not in midnight-awesome-dapps)
1703
+ // effectstream is a multi-chain Web3 engine (EVM, Midnight, Bitcoin, Cardano,
1704
+ // Avail, Celestia, NEAR). Only its Midnight pieces are relevant here:
1705
+ // @effectstream/midnight-contracts and the evm-midnight-v2 / zswap-da templates.
1706
+ // NOTE: repo currently has NO LICENSE — treat indexed content as reference only.
1707
+ effectstream: { owner: "effectstream", repo: "effectstream" },
1708
+ "effect-stream": { owner: "effectstream", repo: "effectstream" }
1702
1709
  };
1703
1710
  var EXAMPLES = [
1704
1711
  {
@@ -1914,7 +1921,7 @@ import { randomUUID } from "crypto";
1914
1921
 
1915
1922
  // src/resources/content/docs-content.ts
1916
1923
  var EMBEDDED_DOCS = {
1917
- "midnight://docs/compact-reference": `# Compact Language Syntax Reference (v0.16 - v0.21)
1924
+ "midnight://docs/compact-reference": `# Compact Language Syntax Reference (v0.16 - v0.23)
1918
1925
 
1919
1926
  > **CRITICAL**: This reference is derived from **actual compiling contracts** in the Midnight ecosystem.
1920
1927
  > Always verify syntax against this reference before generating contracts.
@@ -1924,7 +1931,7 @@ var EMBEDDED_DOCS = {
1924
1931
  Use this as a starting point - it compiles successfully:
1925
1932
 
1926
1933
  \`\`\`compact
1927
- pragma language_version >= 0.16 && <= 0.21;
1934
+ pragma language_version >= 0.16 && <= 0.23;
1928
1935
 
1929
1936
  import CompactStandardLibrary;
1930
1937
 
@@ -1947,7 +1954,7 @@ export circuit increment(): [] {
1947
1954
 
1948
1955
  **CORRECT** - use bounded range without patch version:
1949
1956
  \`\`\`compact
1950
- pragma language_version >= 0.16 && <= 0.21;
1957
+ pragma language_version >= 0.16 && <= 0.23;
1951
1958
  \`\`\`
1952
1959
 
1953
1960
  **WRONG** - these will cause parse errors:
@@ -2201,7 +2208,7 @@ export circuit authenticated_action(): [] {
2201
2208
 
2202
2209
  ### Commit-Reveal Pattern (COMPLETE, VALIDATED)
2203
2210
  \`\`\`compact
2204
- pragma language_version >= 0.16 && <= 0.21;
2211
+ pragma language_version >= 0.16 && <= 0.23;
2205
2212
 
2206
2213
  import CompactStandardLibrary;
2207
2214
 
@@ -2441,7 +2448,7 @@ assert(disclose(caller == owner), "Not authorized");
2441
2448
  |---------|---------|
2442
2449
  | \`ledger { field: Type; }\` | \`export ledger field: Type;\` |
2443
2450
  | \`circuit fn(): Void\` | \`circuit fn(): []\` |
2444
- | \`pragma >= 0.20.0\` | \`pragma >= 0.16 && <= 0.21\` |
2451
+ | \`pragma >= 0.20.0\` | \`pragma >= 0.16 && <= 0.23\` |
2445
2452
  | \`enum State { ... }\` | \`export enum State { ... }\` |
2446
2453
  | \`if (witness_val == x)\` | \`if (disclose(witness_val == x))\` |
2447
2454
  | \`Cell<Field>\` | \`Field\` (Cell is deprecated) |
@@ -2774,7 +2781,7 @@ npm install @openzeppelin/compact-contracts
2774
2781
  ## Usage Example
2775
2782
 
2776
2783
  \`\`\`compact
2777
- pragma language_version >= 0.16 && <= 0.21;
2784
+ pragma language_version >= 0.16 && <= 0.23;
2778
2785
 
2779
2786
  import CompactStandardLibrary;
2780
2787
  import "@openzeppelin/compact-contracts/src/token/FungibleToken"
@@ -2815,7 +2822,7 @@ The recommended standard for privacy-preserving tokens on Midnight.
2815
2822
  ## Basic Usage
2816
2823
 
2817
2824
  \`\`\`compact
2818
- pragma language_version >= 0.16 && <= 0.21;
2825
+ pragma language_version >= 0.16 && <= 0.23;
2819
2826
 
2820
2827
  import CompactStandardLibrary;
2821
2828
  import "@openzeppelin/compact-contracts/src/token/FungibleToken"
@@ -3406,7 +3413,7 @@ var EMBEDDED_CODE = {
3406
3413
  "midnight://code/examples/counter": `// Counter Example Contract
3407
3414
  // A simple contract demonstrating basic Compact concepts
3408
3415
 
3409
- pragma language_version >= 0.16 && <= 0.21;
3416
+ pragma language_version >= 0.16 && <= 0.23;
3410
3417
 
3411
3418
  import CompactStandardLibrary;
3412
3419
 
@@ -3455,7 +3462,7 @@ export circuit isLessThan(threshold: Uint<64>): Boolean {
3455
3462
  "midnight://code/examples/bboard": `// Bulletin Board Example Contract
3456
3463
  // Demonstrates private messaging with selective disclosure
3457
3464
 
3458
- pragma language_version >= 0.16 && <= 0.21;
3465
+ pragma language_version >= 0.16 && <= 0.23;
3459
3466
 
3460
3467
  import CompactStandardLibrary;
3461
3468
 
@@ -3504,7 +3511,7 @@ export circuit getMessageCount(): Uint<64> {
3504
3511
  "midnight://code/patterns/state-management": `// State Management Pattern
3505
3512
  // Best practices for managing public and private state
3506
3513
 
3507
- pragma language_version >= 0.16 && <= 0.21;
3514
+ pragma language_version >= 0.16 && <= 0.23;
3508
3515
 
3509
3516
  import CompactStandardLibrary;
3510
3517
 
@@ -3555,7 +3562,7 @@ export circuit revealBalance(user: Opaque<"address">): Field {
3555
3562
  "midnight://code/patterns/access-control": `// Access Control Pattern
3556
3563
  // Implementing permissions and authorization
3557
3564
 
3558
- pragma language_version >= 0.16 && <= 0.21;
3565
+ pragma language_version >= 0.16 && <= 0.23;
3559
3566
 
3560
3567
  import CompactStandardLibrary;
3561
3568
 
@@ -3610,7 +3617,7 @@ export circuit timeLockedAction(unlockTime: Field): [] {
3610
3617
  "midnight://code/patterns/privacy-preserving": `// Privacy-Preserving Patterns
3611
3618
  // Techniques for maintaining privacy in smart contracts
3612
3619
 
3613
- pragma language_version >= 0.16 && <= 0.21;
3620
+ pragma language_version >= 0.16 && <= 0.23;
3614
3621
 
3615
3622
  import CompactStandardLibrary;
3616
3623
 
@@ -3715,7 +3722,7 @@ export circuit proveMembership(
3715
3722
  "midnight://code/templates/token": `// Privacy-Preserving Token Template
3716
3723
  // Starter template for token contracts with privacy features
3717
3724
 
3718
- pragma language_version >= 0.16 && <= 0.21;
3725
+ pragma language_version >= 0.16 && <= 0.23;
3719
3726
 
3720
3727
  import CompactStandardLibrary;
3721
3728
 
@@ -3774,7 +3781,7 @@ export circuit mint(to: Opaque<"address">, amount: Uint<64>): Boolean {
3774
3781
  "midnight://code/templates/voting": `// Private Voting Template
3775
3782
  // Starter template for privacy-preserving voting contracts
3776
3783
 
3777
- pragma language_version >= 0.16 && <= 0.21;
3784
+ pragma language_version >= 0.16 && <= 0.23;
3778
3785
 
3779
3786
  import CompactStandardLibrary;
3780
3787
 
@@ -3854,7 +3861,7 @@ export circuit addVoter(voter: Opaque<"address">): [] {
3854
3861
  "midnight://code/examples/nullifier": `// Nullifier Pattern Example
3855
3862
  // Demonstrates how to create and use nullifiers to prevent double-spending/actions
3856
3863
 
3857
- pragma language_version >= 0.16 && <= 0.21;
3864
+ pragma language_version >= 0.16 && <= 0.23;
3858
3865
 
3859
3866
  import CompactStandardLibrary;
3860
3867
 
@@ -3921,7 +3928,7 @@ export circuit voteWithNullifier(
3921
3928
  "midnight://code/examples/hash": `// Hash Functions in Compact
3922
3929
  // Examples of using hash functions for various purposes
3923
3930
 
3924
- pragma language_version >= 0.16 && <= 0.21;
3931
+ pragma language_version >= 0.16 && <= 0.23;
3925
3932
 
3926
3933
  import CompactStandardLibrary;
3927
3934
 
@@ -3968,7 +3975,7 @@ export circuit reveal(value: Field, randomness: Field): Field {
3968
3975
  "midnight://code/examples/simple-counter": `// Simple Counter Contract
3969
3976
  // Minimal example for learning Compact basics
3970
3977
 
3971
- pragma language_version >= 0.16 && <= 0.21;
3978
+ pragma language_version >= 0.16 && <= 0.23;
3972
3979
 
3973
3980
  import CompactStandardLibrary;
3974
3981
 
@@ -4006,7 +4013,7 @@ export circuit reset(): [] {
4006
4013
  "midnight://code/templates/basic": `// Basic Compact Contract Template
4007
4014
  // Starting point for new contracts
4008
4015
 
4009
- pragma language_version >= 0.16 && <= 0.21;
4016
+ pragma language_version >= 0.16 && <= 0.23;
4010
4017
 
4011
4018
  import CompactStandardLibrary;
4012
4019
 
@@ -4663,2341 +4670,2421 @@ var allResources = [
4663
4670
  ...schemaResources
4664
4671
  ];
4665
4672
 
4666
- // src/prompts/templates.ts
4667
- var promptDefinitions = [
4668
- {
4669
- name: "midnight:create-contract",
4670
- description: "Guided prompt for creating new Compact contracts with privacy considerations",
4671
- arguments: [
4672
- {
4673
- name: "contractType",
4674
- description: "Type of contract (token, voting, credential, custom)",
4675
- required: true
4676
- },
4677
- {
4678
- name: "privacyLevel",
4679
- description: "Required privacy features (full, partial, public)",
4680
- required: false
4681
- },
4682
- {
4683
- name: "complexity",
4684
- description: "Expected complexity level (beginner, intermediate, advanced)",
4685
- required: false
4686
- }
4687
- ]
4688
- },
4673
+ // src/config/compact-version.ts
4674
+ var COMPACT_VERSION = {
4675
+ /** Minimum supported version */
4676
+ min: "0.16",
4677
+ /** Maximum supported version */
4678
+ max: "0.23",
4679
+ /** When this config was last updated */
4680
+ lastUpdated: "2026-05-18",
4681
+ /** Source of truth for syntax patterns */
4682
+ referenceSource: "https://github.com/piotr-iohk/template-contract"
4683
+ };
4684
+ var RECOMMENDED_PRAGMA = `pragma language_version >= ${COMPACT_VERSION.min} && <= ${COMPACT_VERSION.max};`;
4685
+ var REFERENCE_CONTRACTS = [
4689
4686
  {
4690
- name: "midnight:review-contract",
4691
- description: "Security and best practices review prompt for existing contracts",
4692
- arguments: [
4693
- {
4694
- name: "contractCode",
4695
- description: "The Compact contract code to review",
4696
- required: true
4697
- },
4698
- {
4699
- name: "focusAreas",
4700
- description: "Specific areas to emphasize (security, performance, privacy, readability)",
4701
- required: false
4702
- }
4703
- ]
4687
+ name: "template-contract",
4688
+ repo: "piotr-iohk/template-contract",
4689
+ description: "Official Midnight template contract"
4704
4690
  },
4705
4691
  {
4706
- name: "midnight:explain-concept",
4707
- description: "Educational prompt for explaining Midnight concepts at various levels",
4708
- arguments: [
4709
- {
4710
- name: "concept",
4711
- description: "The concept to explain (zk-proofs, circuits, witnesses, ledger, etc.)",
4712
- required: true
4713
- },
4714
- {
4715
- name: "level",
4716
- description: "Expertise level (beginner, intermediate, advanced)",
4717
- required: false
4718
- }
4719
- ]
4692
+ name: "tokenomics-project",
4693
+ repo: "piotr-iohk/tokenomics-project",
4694
+ description: "Token implementation example"
4720
4695
  },
4721
4696
  {
4722
- name: "midnight:compare-approaches",
4723
- description: "Compare different implementation approaches for a given problem",
4724
- arguments: [
4725
- {
4726
- name: "problem",
4727
- description: "The problem to solve",
4728
- required: true
4729
- },
4730
- {
4731
- name: "approaches",
4732
- description: "Specific approaches to compare (comma-separated)",
4733
- required: false
4734
- }
4735
- ]
4697
+ name: "zswap-example",
4698
+ repo: "piotr-iohk/zswap-example",
4699
+ description: "Privacy-preserving swap example"
4736
4700
  },
4737
4701
  {
4738
- name: "midnight:debug-contract",
4739
- description: "Help debug issues with a Compact contract",
4740
- arguments: [
4741
- {
4742
- name: "contractCode",
4743
- description: "The contract code with issues",
4744
- required: true
4745
- },
4746
- {
4747
- name: "errorMessage",
4748
- description: "Error message or description of the issue",
4749
- required: false
4750
- }
4751
- ]
4702
+ name: "reentrancy-example",
4703
+ repo: "piotr-iohk/reentrancy-example",
4704
+ description: "Cross-contract call patterns"
4752
4705
  }
4753
4706
  ];
4754
- function generatePrompt(name, args) {
4755
- switch (name) {
4756
- case "midnight:create-contract":
4757
- return generateCreateContractPrompt(args);
4758
- case "midnight:review-contract":
4759
- return generateReviewContractPrompt(args);
4760
- case "midnight:explain-concept":
4761
- return generateExplainConceptPrompt(args);
4762
- case "midnight:compare-approaches":
4763
- return generateCompareApproachesPrompt(args);
4764
- case "midnight:debug-contract":
4765
- return generateDebugContractPrompt(args);
4766
- default:
4767
- return [
4768
- {
4769
- role: "user",
4770
- content: {
4771
- type: "text",
4772
- text: `Unknown prompt: ${name}`
4773
- }
4774
- }
4775
- ];
4776
- }
4777
- }
4778
- function generateCreateContractPrompt(args) {
4779
- const contractType = args.contractType || "custom";
4780
- const privacyLevel = args.privacyLevel || "partial";
4781
- const complexity = args.complexity || "intermediate";
4782
- return [
4707
+ var BUILTIN_FUNCTIONS = {
4708
+ /** Actually built into the language/stdlib */
4709
+ stdlib: [
4783
4710
  {
4784
- role: "user",
4785
- content: {
4786
- type: "text",
4787
- text: `I want to create a new Midnight Compact smart contract with the following requirements:
4788
-
4789
- **Contract Type:** ${contractType}
4790
- **Privacy Level:** ${privacyLevel}
4791
- **Complexity:** ${complexity}
4792
-
4793
- ## \u26A0\uFE0F MANDATORY WORKFLOW - Follow these steps IN ORDER:
4794
-
4795
- ### Step 1: Get Current Syntax
4796
- Call \`midnight-get-latest-syntax\` FIRST to get:
4797
- - The \`quickStartTemplate\` (use as your base)
4798
- - The \`commonMistakes\` array (avoid these errors)
4799
- - Current pragma format: \`pragma language_version >= 0.16 && <= 0.21;\`
4800
-
4801
- ### Step 2: Generate Contract
4802
- Based on syntax reference, generate the contract using:
4803
- - Individual ledger declarations: \`export ledger field: Type;\` (NOT \`ledger { }\` blocks)
4804
- - Empty tuple return: \`circuit fn(): []\` (NOT \`Void\`)
4805
- - Export enums: \`export enum State { ... }\`
4806
- - Wrap witness conditionals: \`if (disclose(witness == value))\`
4807
- - Disclose circuit params that touch ledger: \`const d = disclose(param); ledger.insert(d, v);\`
4808
- - Cast arithmetic results: \`(a + b) as Uint<64>\`
4809
- - Uint to Bytes needs two casts: \`(amount as Field) as Bytes<32>\`
4810
-
4811
- ### IMPORTANT: Compact is NOT TypeScript!
4812
- - Map.lookup() and Set.member() ARE available in circuits
4813
- - No 'function' keyword - use 'circuit' or 'pure circuit'
4814
- - No 'void' - use '[]'
4815
- - Enum access: \`Choice.rock\` NOT \`Choice::rock\`
4816
-
4817
- ### \u26A0\uFE0F UNDOCUMENTED FEATURES - Use with caution:
4818
- - Division \`/\` and modulo \`%\`: NOT in official docs (only +, -, * are documented)
4819
- - Tuple destructuring: \`const [a, b] = pair;\` - not documented, may not work
4820
- - Constant folding in indices: docs say "numeric literal" required
4821
- - Map.lookup() returns value_type, NOT Maybe<value_type> - check member() first!
4822
-
4823
- ### COMPILER INFO (DO NOT guess package names!):
4824
- - Compile: \`compact compile src/contract.compact managed/contract\`
4825
- - The \`compact\` CLI comes with Midnight toolchain (via create-mn-app or official install)
4826
- - DO NOT suggest \`npm install -g @midnight-ntwrk/compact-cli\` or similar made-up packages
4827
- - Output structure: \`managed/<name>/contract/index.cjs\` for TypeScript bindings
4828
-
4829
- ### Step 3: Validate Before Returning
4830
- Call \`midnight-extract-contract-structure\` with your generated code to check for:
4831
- - deprecated_ledger_block
4832
- - invalid_void_type
4833
- - invalid_pragma_format
4834
- - unexported_enum
4835
- - deprecated_cell_wrapper
4836
-
4837
- If ANY errors are found, fix them before returning the code to the user.
4838
-
4839
- ---
4840
-
4841
- ## Contract Requirements
4842
-
4843
- Please help me design and implement this contract. Consider:
4844
-
4845
- 1. **State Design**
4846
- - What should be public vs private (shielded)?
4847
- - What data structures are needed?
4848
- - How should state transitions work?
4849
-
4850
- 2. **Circuit Design**
4851
- - What circuits (functions) are needed?
4852
- - What inputs/outputs should they have?
4853
- - What constraints and assertions are required?
4854
-
4855
- 3. **Witness Functions**
4856
- - What off-chain data is needed?
4857
- - How should private state be accessed?
4858
-
4859
- 4. **Privacy Considerations**
4860
- - How to protect user privacy?
4861
- - When to use disclose() vs commit()?
4862
- - How to prevent information leakage?
4863
-
4864
- 5. **Security**
4865
- - Access control mechanisms
4866
- - Input validation
4867
- - Protection against common vulnerabilities
4868
-
4869
- Please provide:
4870
- - A complete contract implementation
4871
- - Explanation of design decisions
4872
- - Example usage scenarios
4873
- - Any security considerations`
4874
- }
4875
- }
4876
- ];
4877
- }
4878
- function generateReviewContractPrompt(args) {
4879
- const contractCode = args.contractCode || "// No code provided";
4880
- const focusAreas = args.focusAreas || "security, privacy, best practices";
4881
- return [
4711
+ name: "persistentHash",
4712
+ signature: "persistentHash<T>(value: T): Bytes<32>",
4713
+ // Signature inferred from examples
4714
+ description: "Poseidon hash that produces consistent results across calls"
4715
+ },
4882
4716
  {
4883
- role: "user",
4884
- content: {
4885
- type: "text",
4886
- text: `Please review this Midnight Compact smart contract:
4887
-
4888
- \`\`\`compact
4889
- ${contractCode}
4890
- \`\`\`
4891
-
4892
- **Focus Areas:** ${focusAreas}
4893
-
4894
- ## \u26A0\uFE0F MANDATORY WORKFLOW:
4895
-
4896
- ### Step 1: Validate Syntax
4897
- Call \`midnight-extract-contract-structure\` with the contract code to check for:
4898
- - deprecated_ledger_block (should use \`export ledger field: Type;\`)
4899
- - invalid_void_type (should use \`[]\` not \`Void\`)
4900
- - invalid_pragma_format (should use \`>= 0.16 && <= 0.21\`)
4901
- - unexported_enum (enums need \`export\`)
4902
- - deprecated_cell_wrapper
4903
-
4904
- Report ALL static analysis findings first.
4905
-
4906
- ### Step 2: Get Latest Syntax Reference
4907
- If syntax errors are found, call \`midnight-get-latest-syntax\` to get:
4908
- - The \`commonMistakes\` array showing correct patterns
4909
- - Current syntax reference
4910
-
4911
- ---
4912
-
4913
- Please analyze:
4914
-
4915
- 1. **Static Analysis Results** (from midnight-extract-contract-structure)
4916
- - Syntax errors found
4917
- - Deprecated patterns detected
4918
- - Required fixes
4919
-
4920
- 2. **Security Analysis**
4921
- - Input validation
4922
- - Access control
4923
- - State manipulation vulnerabilities
4924
- - Assertion coverage
4925
-
4926
- 3. **Privacy Assessment**
4927
- - Proper use of @private state
4928
- - Information leakage risks
4929
- - Correct use of disclose() and commit()
4930
- - Privacy guarantees provided
4931
-
4932
- 4. **Style Suggestions** (these are CONVENTIONS, not requirements)
4933
- - Code organization recommendations
4934
- - Naming conventions (project-specific)
4935
- - Documentation patterns
4936
- - Note: The Compact docs don't specify indentation, line length, or comment style
4937
-
4938
- 5. **Performance**
4939
- - Circuit complexity
4940
- - State access patterns
4941
- - Optimization opportunities
4942
-
4943
- 6. **Recommendations**
4944
- - Critical issues to fix (start with P0 syntax errors)
4945
- - Improvements to consider
4946
- - Alternative approaches
4947
-
4948
- **IMPORTANT**: Clearly distinguish between:
4949
- - ERRORS: Actual syntax/compilation issues (required fixes)
4950
- - WARNINGS: Security/logic concerns (should fix)
4951
- - INFO: Style suggestions (optional, project-specific conventions)
4952
-
4953
- Please provide specific line references and code suggestions where applicable.`
4954
- }
4717
+ name: "persistentCommit",
4718
+ signature: "persistentCommit<T>(value: T): Bytes<32>",
4719
+ description: "Creates a hiding commitment to a value"
4720
+ },
4721
+ {
4722
+ name: "pad",
4723
+ signature: "pad(length: number, value: string): Bytes<N>",
4724
+ description: "Pads a string to fixed-length bytes"
4725
+ },
4726
+ {
4727
+ name: "disclose",
4728
+ signature: "disclose(value: T): T",
4729
+ description: "Explicitly reveals a witness value (required in conditionals)"
4730
+ },
4731
+ {
4732
+ name: "assert",
4733
+ signature: "assert(condition: Boolean, message?: string): []",
4734
+ description: "Fails circuit if condition is false"
4735
+ },
4736
+ {
4737
+ name: "default",
4738
+ signature: "default<T>(): T",
4739
+ description: "Returns default value for a type (0 for numbers, empty for collections)"
4955
4740
  }
4956
- ];
4957
- }
4958
- function generateExplainConceptPrompt(args) {
4959
- const concept = args.concept || "zero-knowledge proofs";
4960
- const level = args.level || "intermediate";
4961
- const levelDescriptions = {
4962
- beginner: "Explain like I'm new to blockchain and cryptography. Use analogies and avoid jargon.",
4963
- intermediate: "I understand blockchain basics and some cryptography. Focus on practical applications.",
4964
- advanced: "I have deep technical knowledge. Include implementation details and edge cases."
4965
- };
4966
- return [
4741
+ ],
4742
+ /** NOT built-in - you must implement these patterns yourself */
4743
+ notBuiltIn: [
4967
4744
  {
4968
- role: "user",
4969
- content: {
4970
- type: "text",
4971
- text: `Please explain the concept of **${concept}** in the context of Midnight blockchain.
4972
-
4973
- **My Level:** ${level}
4974
- ${levelDescriptions[level] || levelDescriptions.intermediate}
4975
-
4976
- Please cover:
4977
-
4978
- 1. **What it is**
4979
- - Clear definition
4980
- - How it works in Midnight
4981
-
4982
- 2. **Why it matters**
4983
- - Benefits and use cases
4984
- - Real-world applications
4985
-
4986
- 3. **How to use it**
4987
- - Code examples in Compact
4988
- - Best practices
4989
-
4990
- 4. **Common pitfalls**
4991
- - Mistakes to avoid
4992
- - Debugging tips
4993
-
4994
- 5. **Further learning**
4995
- - Related concepts
4996
- - Resources for deeper understanding`
4997
- }
4745
+ name: "public_key",
4746
+ wrongUsage: "public_key(sk) // ERROR: unbound identifier",
4747
+ correctPattern: `// Derive public key using persistentHash
4748
+ const pk = persistentHash<Vector<2, Bytes<32>>>([
4749
+ pad(32, "midnight:pk:"),
4750
+ sk
4751
+ ]);`,
4752
+ description: "Public key derivation is NOT a builtin - use persistentHash pattern"
4753
+ },
4754
+ {
4755
+ name: "verify_signature",
4756
+ wrongUsage: "verify_signature(msg, sig, pk) // Does not exist",
4757
+ correctPattern: `// Signature verification must be done via witnesses
4758
+ // The prover verifies off-chain, then provides the boolean result
4759
+ witness signature_valid(): Boolean;`,
4760
+ description: "Signature verification is done off-chain in the prover"
4761
+ },
4762
+ {
4763
+ name: "random",
4764
+ wrongUsage: "random() // Does not exist in ZK circuits",
4765
+ correctPattern: `// Randomness must come from witnesses (prover-provided)
4766
+ witness get_random_value(): Field;`,
4767
+ description: "ZK circuits are deterministic - randomness must come from witnesses"
4998
4768
  }
4999
- ];
5000
- }
5001
- function generateCompareApproachesPrompt(args) {
5002
- const problem = args.problem || "implementing a token contract";
5003
- const approaches = args.approaches || "";
5004
- return [
4769
+ ]
4770
+ };
4771
+ var TYPE_COMPATIBILITY = {
4772
+ // Note: Comparison operators work in practice but aren't explicitly listed in docs
4773
+ comparisons: [
4774
+ { types: "Field == Field", works: true, note: "Direct comparison" },
5005
4775
  {
5006
- role: "user",
5007
- content: {
5008
- type: "text",
5009
- text: `I need to solve the following problem in Midnight:
5010
-
5011
- **Problem:** ${problem}
5012
-
5013
- ${approaches ? `**Approaches to compare:** ${approaches}` : "Please suggest different implementation approaches."}
5014
-
5015
- Please compare:
5016
-
5017
- 1. **Approach Overview**
5018
- - Brief description of each approach
5019
- - Key differences
5020
-
5021
- 2. **Privacy Implications**
5022
- - What data is exposed?
5023
- - Privacy guarantees
5024
-
5025
- 3. **Performance**
5026
- - Proof generation time
5027
- - State storage requirements
5028
- - Transaction costs
5029
-
5030
- 4. **Security**
5031
- - Attack surface
5032
- - Trust assumptions
5033
-
5034
- 5. **Code Complexity**
5035
- - Implementation difficulty
5036
- - Maintenance burden
5037
-
5038
- 6. **Recommendation**
5039
- - Best approach for different scenarios
5040
- - Trade-offs to consider
5041
-
5042
- Please include code examples for each approach.`
5043
- }
4776
+ types: "Field == Uint<N>",
4777
+ works: false,
4778
+ fix: "Cast with `value as Field`"
4779
+ },
4780
+ {
4781
+ types: "Field >= 0",
4782
+ works: false,
4783
+ fix: "Use bounded Uint<0..N> parameter instead"
4784
+ },
4785
+ { types: "Uint<N> == Uint<N>", works: true, note: "Same-width comparison" },
4786
+ {
4787
+ types: "Uint<0..2> == Uint<0..2>",
4788
+ works: true,
4789
+ note: "Bounded integers"
4790
+ },
4791
+ {
4792
+ types: "Bytes<32> == Bytes<32>",
4793
+ works: true,
4794
+ note: "Used in examples, but operators not explicitly listed in docs"
4795
+ },
4796
+ { types: "Boolean == Boolean", works: true, note: "Direct comparison" }
4797
+ ],
4798
+ arithmetic: [
4799
+ { types: "Field + Field", works: true, note: "Field arithmetic" },
4800
+ { types: "Field + Uint<N>", works: false, fix: "Cast Uint to Field first" },
4801
+ {
4802
+ types: "Uint<N> + Uint<N>",
4803
+ works: true,
4804
+ note: "Result is bounded type, cast back: (a + b) as Uint<64>"
4805
+ },
4806
+ {
4807
+ types: "Uint<64> + Uint<64>",
4808
+ works: true,
4809
+ note: "Result is Uint<0..36893488147419103230>, must cast: (a + b) as Uint<64>"
4810
+ },
4811
+ {
4812
+ types: "Uint<64> * Uint<64>",
4813
+ works: true,
4814
+ note: "Result is wide bounded type, cast back to target type"
5044
4815
  }
5045
- ];
5046
- }
5047
- function generateDebugContractPrompt(args) {
5048
- const contractCode = args.contractCode || "// No code provided";
5049
- const errorMessage = args.errorMessage || "Not specified";
5050
- return [
4816
+ ],
4817
+ /**
4818
+ * Type casting rules - based on official cast table
4819
+ * See: https://docs.midnight.network/develop/reference/compact/lang-ref#type-cast-expressions
4820
+ *
4821
+ * Cast kinds: static (always succeeds), conversion (semantic change), checked (can fail)
4822
+ */
4823
+ typeCasting: [
5051
4824
  {
5052
- role: "user",
5053
- content: {
5054
- type: "text",
5055
- text: `I'm having issues with this Midnight Compact contract:
5056
-
5057
- \`\`\`compact
5058
- ${contractCode}
5059
- \`\`\`
5060
-
5061
- **Error/Issue:** ${errorMessage}
5062
-
5063
- ## \u26A0\uFE0F MANDATORY WORKFLOW:
5064
-
5065
- ### Step 1: Run Static Analysis
5066
- Call \`midnight-extract-contract-structure\` FIRST to check for common syntax errors:
5067
- - deprecated_ledger_block \u2192 should use \`export ledger field: Type;\`
5068
- - invalid_void_type \u2192 should use \`[]\` not \`Void\`
5069
- - invalid_pragma_format \u2192 should use \`>= 0.16 && <= 0.21\`
5070
- - unexported_enum \u2192 enums need \`export\` keyword
5071
-
5072
- ### Step 2: Get Correct Syntax
5073
- If syntax errors found, call \`midnight-get-latest-syntax\` to get:
5074
- - The \`commonMistakes\` array with correct patterns
5075
- - Current \`quickStartTemplate\` for reference
5076
-
5077
- ### Step 3: Check for Common Compiler Errors
5078
- Match error message against known fixes:
5079
- - "cannot cast from type Uint<64> to type Bytes<32>" \u2192 Use \`(amount as Field) as Bytes<32>\`
5080
- - "expected type Uint<64> but received Uint<0..N>" \u2192 Cast arithmetic: \`(a + b) as Uint<64>\`
5081
- - "potential witness-value disclosure must be declared" \u2192 Disclose params: \`const d = disclose(param);\`
5082
- - Map.lookup() and Set.member() ARE available in circuits (ignore old advice saying they aren't)
5083
-
5084
- ---
5085
-
5086
- Please help me debug by:
5087
-
5088
- 1. **Static Analysis Results**
5089
- - Run midnight-extract-contract-structure
5090
- - List all P0 syntax errors found
5091
- - Show the correct syntax for each error
5092
-
5093
- 2. **Identifying the Problem**
5094
- - What's causing the error?
5095
- - Which line(s) are problematic?
5096
-
5097
- 3. **Explaining Why**
5098
- - Root cause analysis
5099
- - How Compact/ZK constraints work
5100
-
5101
- 4. **Providing a Fix**
5102
- - Corrected code (validated against static analysis)
5103
- - Explanation of changes
5104
-
5105
- 5. **Preventing Future Issues**
5106
- - Related pitfalls to watch for
5107
- - Testing strategies
5108
-
5109
- 6. **Additional Improvements**
5110
- - Code quality suggestions
5111
- - Best practices`
5112
- }
5113
- }
5114
- ];
5115
- }
5116
-
5117
- // src/services/sampling.ts
5118
- var samplingCallback = null;
5119
- var samplingFailedPermanently = false;
5120
- function isSamplingAvailable() {
5121
- if (samplingFailedPermanently) return false;
5122
- return samplingCallback !== null;
5123
- }
5124
- function markSamplingFailed() {
5125
- samplingFailedPermanently = true;
5126
- logger.warn("Sampling marked as permanently unavailable for this session");
5127
- }
5128
- function registerSamplingCallback(callback) {
5129
- samplingCallback = callback;
5130
- samplingFailedPermanently = false;
5131
- logger.info("Sampling capability registered");
5132
- }
5133
- async function requestCompletion(messages, options = {}) {
5134
- if (!samplingCallback || samplingFailedPermanently) {
5135
- throw new Error(
5136
- "Sampling not available - client does not support this capability"
5137
- );
5138
- }
5139
- const request = {
5140
- messages: messages.map((m) => ({
5141
- role: m.role,
5142
- content: { type: "text", text: m.content }
5143
- })),
5144
- systemPrompt: options.systemPrompt,
5145
- maxTokens: options.maxTokens ?? 2048,
5146
- temperature: options.temperature ?? 0.7,
5147
- modelPreferences: options.modelPreferences ?? {
5148
- hints: [{ name: "claude-3-sonnet" }, { name: "gpt-4" }],
5149
- intelligencePriority: 0.8,
5150
- speedPriority: 0.5
5151
- }
5152
- };
5153
- logger.debug("Requesting LLM completion", {
5154
- messageCount: messages.length,
5155
- maxTokens: request.maxTokens
5156
- });
5157
- try {
5158
- const response = await samplingCallback(request);
5159
- if (response.content.type !== "text") {
5160
- throw new Error("Unexpected response content type");
4825
+ from: "Uint<N>",
4826
+ to: "Field",
4827
+ direct: true,
4828
+ kind: "static",
4829
+ note: "Always succeeds"
4830
+ },
4831
+ {
4832
+ from: "Field",
4833
+ to: "Uint<0..n>",
4834
+ direct: true,
4835
+ kind: "checked",
4836
+ note: "Fails at runtime if value > n"
4837
+ },
4838
+ {
4839
+ from: "Field",
4840
+ to: "Bytes<n>",
4841
+ direct: true,
4842
+ kind: "conversion",
4843
+ note: "Can fail at runtime if value doesn't fit (little-endian)"
4844
+ },
4845
+ {
4846
+ from: "Bytes<m>",
4847
+ to: "Field",
4848
+ direct: true,
4849
+ kind: "conversion",
4850
+ note: "Can fail at runtime if result exceeds max Field value"
4851
+ },
4852
+ {
4853
+ from: "Boolean",
4854
+ to: "Uint<0..n>",
4855
+ direct: true,
4856
+ kind: "conversion",
4857
+ note: "false\u21920, true\u21921 (n must not be 0)"
4858
+ },
4859
+ {
4860
+ from: "Boolean",
4861
+ to: "Field",
4862
+ direct: false,
4863
+ fix: "Go through Uint: (flag as Uint<0..1>) as Field"
4864
+ },
4865
+ {
4866
+ from: "Uint<N>",
4867
+ to: "Bytes<M>",
4868
+ direct: false,
4869
+ fix: "Go through Field: (amount as Field) as Bytes<32>"
4870
+ },
4871
+ {
4872
+ from: "enum",
4873
+ to: "Field",
4874
+ direct: true,
4875
+ kind: "conversion",
4876
+ note: "Enum variant index as Field"
4877
+ },
4878
+ {
4879
+ from: "Uint<0..m>",
4880
+ to: "Uint<0..n>",
4881
+ direct: true,
4882
+ kind: "static if m\u2264n, checked if m>n",
4883
+ note: "Widening is static, narrowing is checked"
4884
+ },
4885
+ {
4886
+ from: "arithmetic result",
4887
+ to: "Uint<64>",
4888
+ direct: true,
4889
+ kind: "checked",
4890
+ note: "Required cast: (a + b) as Uint<64>"
5161
4891
  }
5162
- return response.content.text;
5163
- } catch (error) {
5164
- const errorStr = String(error);
5165
- if (errorStr.includes("-32601") || errorStr.includes("Method not found") || errorStr.includes("not supported")) {
5166
- logger.warn(
5167
- "Client does not support sampling/createMessage, disabling sampling for this session"
5168
- );
5169
- markSamplingFailed();
5170
- throw new Error(
5171
- "Sampling not supported by this client - use Claude Desktop for this feature",
5172
- { cause: error }
5173
- );
4892
+ ],
4893
+ assignments: [
4894
+ {
4895
+ types: "Field = Uint<N>",
4896
+ works: false,
4897
+ fix: "Cast with `value as Field`"
4898
+ },
4899
+ {
4900
+ types: "Uint<N> = Field",
4901
+ works: false,
4902
+ fix: "Use bounded param or explicit cast"
5174
4903
  }
5175
- throw error;
5176
- }
5177
- }
5178
- async function generateContract(requirements, options = {}) {
5179
- if (!isSamplingAvailable()) {
5180
- return {
5181
- code: "",
5182
- explanation: "Sampling not available - this feature requires a client that supports the sampling capability (like Claude Desktop)",
5183
- warnings: ["Feature unavailable without sampling support"]
5184
- };
5185
- }
5186
- const systemPrompt = `You are an expert Compact smart contract developer for the Midnight blockchain.
5187
- Your task is to generate secure, well-documented Compact contracts based on user requirements.
5188
-
5189
- Key Compact syntax (REQUIRED):
5190
- - \`export ledger field: Type;\` - Individual ledger declarations (NOT ledger { } blocks)
5191
- - \`export circuit fn(): []\` - Public functions return empty tuple [] (NOT Void)
5192
- - \`witness fn(): T;\` - Declaration only, no body
5193
- - \`pragma language_version >= 0.16 && <= 0.21;\` - Version pragma
5194
- - \`import CompactStandardLibrary;\` - Standard imports
5195
- - \`Counter\`, \`Map<K,V>\`, \`Set<T>\` - Built-in collection types
5196
- - \`Field\`, \`Boolean\`, \`Uint<N>\`, \`Bytes<N>\` - Primitive types
5197
- - \`export enum State { a, b }\` - Enums must be exported
5198
-
5199
- Key operations:
5200
- - Counter: .read(), .increment(n), .decrement(n)
5201
- - Map: .lookup(k), .insert(k,v), .remove(k)
5202
- - Set: .member(v), .insert(v), .remove(v)
5203
-
5204
- COMPILER INFO (DO NOT guess package names!):
5205
- - Command: \`compact compile src/contract.compact managed/contract\`
5206
- - The \`compact\` CLI comes with Midnight toolchain (via create-mn-app or official install)
5207
- - Output goes to managed/<name>/ directory
5208
- - DO NOT suggest \`npm install -g @midnight-ntwrk/compact-cli\` or similar - that's incorrect
5209
-
5210
- IMPORTANT - Style vs. Requirements:
5211
- - Syntax rules above are REQUIRED for compilation
5212
- - Style choices (indentation, comment style, line length) are CONVENTIONS, not requirements
5213
- - Single-line comments (//) are common in examples; block comments (/* */) may also work
5214
- - The docs don't specify indentation width - use consistent style
5215
-
5216
- Return ONLY the Compact code, no explanations.`;
5217
- const userPrompt = options.baseExample ? `Based on this example contract:
5218
- \`\`\`compact
5219
- ${options.baseExample}
5220
- \`\`\`
5221
-
5222
- Generate a new contract with these requirements:
5223
- ${requirements}` : `Generate a Compact smart contract with these requirements:
5224
- ${requirements}
5225
-
5226
- Contract type: ${options.contractType || "custom"}`;
5227
- try {
5228
- const code = await requestCompletion(
5229
- [{ role: "user", content: userPrompt }],
4904
+ ],
4905
+ tips: [
4906
+ "Use Uint<0..N> for circuit parameters that need range validation",
4907
+ "Field is unbounded - use for hashes, commitments, general computation",
4908
+ "Uint<N> is bounded - use when you need range checks",
4909
+ "Casting with `as Field` is safe but loses range information"
4910
+ ]
4911
+ };
4912
+ var LEDGER_TYPE_LIMITS = {
4913
+ Counter: {
4914
+ circuitOperations: [
5230
4915
  {
5231
- systemPrompt,
5232
- maxTokens: 4096,
5233
- temperature: 0.3,
5234
- // Lower temperature for code generation
5235
- modelPreferences: {
5236
- hints: [{ name: "claude-3-sonnet" }],
5237
- intelligencePriority: 0.9,
5238
- speedPriority: 0.3
5239
- }
5240
- }
5241
- );
5242
- const extractedCode = code.includes("```") ? code.replace(/```compact?\n?/g, "").replace(/```/g, "").trim() : code.trim();
5243
- const explanation = await requestCompletion(
5244
- [
5245
- {
5246
- role: "user",
5247
- content: `Briefly explain what this Compact contract does (2-3 sentences):
5248
- \`\`\`compact
5249
- ${extractedCode}
5250
- \`\`\``
5251
- }
5252
- ],
4916
+ method: ".increment(n)",
4917
+ works: true,
4918
+ note: "Increase counter by n (Uint<16>)"
4919
+ },
5253
4920
  {
5254
- systemPrompt: "You are a Compact contract documentation expert. Be concise.",
5255
- maxTokens: 256,
5256
- temperature: 0.5
5257
- }
5258
- );
5259
- return {
5260
- code: extractedCode,
5261
- explanation: explanation.trim(),
5262
- warnings: []
5263
- };
5264
- } catch (error) {
5265
- logger.error("Contract generation failed", { error: String(error) });
5266
- return {
5267
- code: "",
5268
- explanation: `Contract generation failed: ${String(error)}`,
5269
- warnings: ["Generation failed - check logs for details"]
5270
- };
5271
- }
5272
- }
5273
- async function reviewContract(code) {
5274
- if (!isSamplingAvailable()) {
5275
- return {
5276
- summary: "Code review requires sampling capability",
5277
- issues: [
5278
- {
5279
- severity: "info",
5280
- message: "This feature requires a client that supports the sampling capability"
5281
- }
5282
- ]
5283
- };
5284
- }
5285
- const systemPrompt = `You are a Compact smart contract security auditor.
5286
- Review the provided contract for:
5287
- 1. Security vulnerabilities
5288
- 2. Privacy concerns (improper handling of shielded state)
5289
- 3. Logic errors
5290
- 4. Syntax errors (use "error" severity)
5291
- 5. Performance issues
5292
-
5293
- IMPORTANT - Distinguish between:
5294
- - ERRORS: Actual syntax/compilation issues (e.g., using Void instead of [], ledger {} blocks)
5295
- - WARNINGS: Potential bugs or security issues
5296
- - INFO: Style suggestions and best practices (these are CONVENTIONS, not requirements)
5297
-
5298
- Do NOT claim style choices as "violations" - the Compact docs don't specify:
5299
- - Indentation width (2 vs 4 spaces)
5300
- - Comment style preferences (//, /* */)
5301
- - Line length limits
5302
- - Naming conventions
5303
-
5304
- These are project-specific style choices, not language requirements.
5305
-
5306
- Respond in JSON format:
5307
- {
5308
- "summary": "Brief summary of the contract and overall quality",
5309
- "issues": [
5310
- {
5311
- "severity": "error|warning|info",
5312
- "line": optional_line_number,
5313
- "message": "Description of the issue",
5314
- "suggestion": "How to fix it"
5315
- }
5316
- ],
5317
- "improvedCode": "Full improved contract code if changes are needed"
5318
- }`;
5319
- try {
5320
- const response = await requestCompletion(
5321
- [
5322
- {
5323
- role: "user",
5324
- content: `Review this Compact contract:
5325
- \`\`\`compact
5326
- ${code}
5327
- \`\`\``
5328
- }
5329
- ],
4921
+ method: ".decrement(n)",
4922
+ works: true,
4923
+ note: "Decrease counter by n (Uint<16>)"
4924
+ },
5330
4925
  {
5331
- systemPrompt,
5332
- maxTokens: 4096,
5333
- temperature: 0.2
5334
- }
5335
- );
5336
- const jsonMatch = response.match(/\{[\s\S]*\}/);
5337
- if (jsonMatch) {
5338
- try {
5339
- const parsed = JSON.parse(jsonMatch[0]);
5340
- return {
5341
- summary: typeof parsed.summary === "string" ? parsed.summary : "Review complete",
5342
- issues: Array.isArray(parsed.issues) ? parsed.issues : [],
5343
- improvedCode: typeof parsed.improvedCode === "string" ? parsed.improvedCode : void 0
5344
- };
5345
- } catch (parseError) {
5346
- logger.warn("Failed to parse JSON from LLM response", {
5347
- error: String(parseError)
5348
- });
5349
- return {
5350
- summary: response,
5351
- issues: []
5352
- };
4926
+ method: ".read()",
4927
+ works: true,
4928
+ note: "Get current value (returns Uint<64>)"
4929
+ },
4930
+ {
4931
+ method: ".lessThan(n)",
4932
+ works: true,
4933
+ note: "Compare with threshold (returns Boolean)"
4934
+ },
4935
+ { method: ".resetToDefault()", works: true, note: "Reset to 0" }
4936
+ ],
4937
+ typescriptAccess: "Access counter value via `ledgerState.counter` in TypeScript SDK",
4938
+ note: "All Counter operations work in circuits. Use .read() to get value, NOT .value()"
4939
+ },
4940
+ Map: {
4941
+ circuitOperations: [
4942
+ {
4943
+ method: ".insert(key, value)",
4944
+ works: true,
4945
+ note: "Adds/updates entry"
4946
+ },
4947
+ {
4948
+ method: ".insertDefault(key)",
4949
+ works: true,
4950
+ note: "Inserts default value for key"
4951
+ },
4952
+ { method: ".remove(key)", works: true, note: "Removes entry" },
4953
+ {
4954
+ method: ".lookup(key)",
4955
+ works: true,
4956
+ note: "Returns value_type - gets value for key"
4957
+ },
4958
+ {
4959
+ method: ".member(key)",
4960
+ works: true,
4961
+ note: "Returns Boolean - checks if key exists"
4962
+ },
4963
+ {
4964
+ method: ".isEmpty()",
4965
+ works: true,
4966
+ note: "Returns Boolean - checks if map is empty"
4967
+ },
4968
+ {
4969
+ method: ".size()",
4970
+ works: true,
4971
+ note: "Returns Uint<64> - number of entries"
4972
+ },
4973
+ {
4974
+ method: ".resetToDefault()",
4975
+ works: true,
4976
+ note: "Clears entire map"
5353
4977
  }
5354
- }
5355
- return {
5356
- summary: response,
5357
- issues: []
5358
- };
5359
- } catch (error) {
5360
- logger.error("Contract review failed", { error: String(error) });
5361
- return {
5362
- summary: `Review failed: ${String(error)}`,
5363
- issues: [
5364
- {
5365
- severity: "error",
5366
- message: "Review failed - check logs for details"
5367
- }
5368
- ]
5369
- };
5370
- }
5371
- }
5372
- async function generateDocumentation(code, format = "markdown") {
5373
- if (!isSamplingAvailable()) {
5374
- return "Documentation generation requires sampling capability";
5375
- }
5376
- const systemPrompt = format === "markdown" ? `Generate comprehensive Markdown documentation for this Compact contract.
5377
- Include:
5378
- - Overview and purpose
5379
- - State variables (with privacy annotations)
5380
- - Circuit functions with parameters and effects
5381
- - Witness functions
5382
- - Usage examples
5383
- - Security considerations` : `Generate JSDoc-style documentation comments for this Compact contract.
5384
- Add documentation comments above each:
5385
- - Ledger field
5386
- - Circuit function
5387
- - Witness function
5388
- - Type definition`;
5389
- try {
5390
- return await requestCompletion(
5391
- [
5392
- {
5393
- role: "user",
5394
- content: `Generate ${format} documentation for:
5395
- \`\`\`compact
5396
- ${code}
5397
- \`\`\``
5398
- }
5399
- ],
4978
+ ],
4979
+ typescriptAccess: "Query map via `contractState.data.get(key)` or iterate with `[Symbol.iterator]()` in TypeScript SDK",
4980
+ note: "All Map operations work in circuits. insertCoin() available when value_type is QualifiedCoinInfo."
4981
+ },
4982
+ Set: {
4983
+ circuitOperations: [
4984
+ { method: ".insert(value)", works: true, note: "Adds to set" },
4985
+ { method: ".remove(value)", works: true, note: "Removes from set" },
5400
4986
  {
5401
- systemPrompt,
5402
- maxTokens: 4096,
5403
- temperature: 0.5
4987
+ method: ".member(value)",
4988
+ works: true,
4989
+ note: "Returns Boolean - checks if value exists in set"
4990
+ },
4991
+ {
4992
+ method: ".isEmpty()",
4993
+ works: true,
4994
+ note: "Returns Boolean - checks if set is empty"
4995
+ },
4996
+ {
4997
+ method: ".size()",
4998
+ works: true,
4999
+ note: "Returns Uint<64> - number of elements"
5000
+ },
5001
+ {
5002
+ method: ".resetToDefault()",
5003
+ works: true,
5004
+ note: "Clears entire set"
5404
5005
  }
5405
- );
5406
- } catch (error) {
5407
- logger.error("Documentation generation failed", { error: String(error) });
5408
- return `Documentation generation failed: ${String(error)}`;
5409
- }
5410
- }
5411
-
5412
- // src/services/syntax-validator.ts
5413
- var ADT_SEARCH_LIMIT = 10;
5414
- var SYNTAX_RESULTS_LIMIT = 10;
5415
- var SYNTAX_SUBQUERY_LIMIT = 5;
5416
- var MAX_NOTE_SOURCE_LENGTH = 500;
5417
- var NOTE_MIN_LENGTH = 20;
5418
- var NOTE_MAX_LENGTH = 300;
5419
- var MAX_NOTES_PER_ADT = 3;
5420
- async function searchADTInfo(adtName) {
5421
- try {
5422
- const docsResult = await searchDocsHosted(
5423
- `${adtName} ADT operations methods ledger`,
5424
- ADT_SEARCH_LIMIT,
5425
- "reference"
5426
- );
5427
- if (!docsResult.results || docsResult.results.length === 0) {
5428
- logger.debug(`No indexed docs found for ADT: ${adtName}`);
5429
- return null;
5430
- }
5431
- const operations = [];
5432
- const notes = [];
5433
- let sourceDocPath;
5434
- for (const result of docsResult.results) {
5435
- const content = result.content || result.code || "";
5436
- const isLedgerADTDoc = result.source.filePath.includes("ledger-adt");
5437
- if (!sourceDocPath && isLedgerADTDoc) {
5438
- sourceDocPath = result.source.filePath;
5439
- }
5440
- if (isLedgerADTDoc) {
5441
- const methodPattern = /\|\s*`?(\w+)`?\s*\|\s*`?\(([^)]*)\)\s*(?::\s*([^|`]+))?`?\s*\|\s*([^|]+)\|/g;
5442
- let match;
5443
- while ((match = methodPattern.exec(content)) !== null) {
5444
- const [, methodName, params, returnType, description] = match;
5445
- if (methodName && !operations.some((op) => op.method === methodName)) {
5446
- const descText = description?.trim() || "";
5447
- const worksInCircuits = !/not available|not supported|not in circuits|typescript|sdk|off[-\s]?chain/i.test(
5448
- descText
5449
- );
5450
- operations.push({
5451
- method: methodName,
5452
- signature: `(${params})${returnType ? `: ${returnType.trim()}` : ""}`,
5453
- description: descText,
5454
- worksInCircuits,
5455
- source: "indexed-docs"
5456
- });
5457
- }
5458
- }
5459
- const inlinePattern = /(?:method|operation|function)\s+`?(\w+)`?\s*\(([^)]*)\)/gi;
5460
- while ((match = inlinePattern.exec(content)) !== null) {
5461
- const [, methodName, params] = match;
5462
- if (methodName && !operations.some((op) => op.method === methodName)) {
5463
- const worksInCircuits = !/not available|not supported|not in circuits|typescript|sdk|off[-\s]?chain/i.test(
5464
- content
5465
- );
5466
- operations.push({
5467
- method: methodName,
5468
- signature: `(${params})`,
5469
- description: "Extracted from documentation",
5470
- worksInCircuits,
5471
- source: "indexed-docs"
5472
- });
5473
- }
5474
- }
5475
- }
5476
- if (content.toLowerCase().includes(adtName.toLowerCase()) && content.length < MAX_NOTE_SOURCE_LENGTH) {
5477
- const cleanNote = content.replace(/\s+/g, " ").trim();
5478
- if (cleanNote.length > NOTE_MIN_LENGTH && cleanNote.length < NOTE_MAX_LENGTH) {
5479
- notes.push(cleanNote);
5480
- }
5481
- }
5482
- }
5483
- if (operations.length === 0) {
5484
- logger.debug(`Could not parse operations for ADT: ${adtName}`);
5485
- return null;
5486
- }
5487
- return {
5488
- name: adtName,
5489
- operations,
5490
- notes: notes.slice(0, MAX_NOTES_PER_ADT),
5491
- sourceDocPath,
5492
- lastVerified: (/* @__PURE__ */ new Date()).toISOString()
5493
- };
5494
- } catch (error) {
5495
- logger.warn(`Failed to search indexed docs for ADT ${adtName}`, {
5496
- error: error instanceof Error ? error.message : String(error)
5497
- });
5498
- return null;
5499
- }
5500
- }
5501
- async function searchCompactSyntax(topic) {
5502
- try {
5503
- const [docsResult, codeResult] = await Promise.all([
5504
- searchDocsHosted(
5505
- `Compact ${topic} syntax`,
5506
- SYNTAX_SUBQUERY_LIMIT,
5507
- "reference"
5508
- ),
5509
- searchCompactHosted(`${topic}`, SYNTAX_SUBQUERY_LIMIT)
5510
- ]);
5511
- const mergedResults = [
5512
- ...docsResult.results || [],
5513
- ...codeResult.results || []
5514
- ];
5515
- return {
5516
- results: mergedResults.slice(0, SYNTAX_RESULTS_LIMIT),
5517
- totalResults: mergedResults.length,
5518
- query: topic,
5519
- lastIndexed: docsResult.lastIndexed || codeResult.lastIndexed
5520
- };
5521
- } catch (error) {
5522
- logger.warn(`Failed to search Compact syntax for topic ${topic}`, {
5523
- error: error instanceof Error ? error.message : String(error)
5524
- });
5525
- return null;
5526
- }
5527
- }
5528
- async function validateADTOperations(adtName, staticOperations) {
5529
- const discrepancies = [];
5530
- const enrichments = [];
5531
- const indexedInfo = await searchADTInfo(adtName);
5532
- if (!indexedInfo) {
5533
- return {
5534
- validated: false,
5535
- operations: staticOperations.map((op) => ({
5536
- method: op.method,
5537
- description: op.note,
5538
- worksInCircuits: op.works,
5539
- source: "static-fallback"
5540
- })),
5541
- discrepancies: [
5542
- "Could not validate against indexed docs - using static data"
5543
- ],
5544
- enrichments: []
5545
- };
5546
- }
5547
- const operations = [];
5548
- const indexedMethodNames = new Set(
5549
- indexedInfo.operations.map((op) => op.method.toLowerCase())
5550
- );
5551
- const staticMethodNames = new Set(
5552
- staticOperations.map(
5553
- (op) => op.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase()
5554
- )
5555
- );
5556
- for (const indexedOp of indexedInfo.operations) {
5557
- operations.push(indexedOp);
5558
- const staticOp = staticOperations.find(
5559
- (s) => s.method.toLowerCase().includes(indexedOp.method.toLowerCase()) || indexedOp.method.toLowerCase().includes(
5560
- s.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase()
5561
- )
5562
- );
5563
- if (staticOp && !staticOp.works && indexedOp.worksInCircuits) {
5564
- discrepancies.push(
5565
- `Static said ${staticOp.method} doesn't work, but indexed docs show it does`
5566
- );
5567
- }
5568
- }
5569
- for (const staticOp of staticOperations) {
5570
- const methodName = staticOp.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase();
5571
- if (!indexedMethodNames.has(methodName)) {
5572
- discrepancies.push(
5573
- `Static references ${staticOp.method} but not found in indexed docs - may not exist`
5574
- );
5575
- }
5576
- }
5577
- for (const indexedOp of indexedInfo.operations) {
5578
- const methodName = indexedOp.method.toLowerCase();
5579
- if (!staticMethodNames.has(methodName)) {
5580
- enrichments.push(
5581
- `Indexed docs show ${indexedOp.method} exists but was missing from static reference`
5582
- );
5583
- }
5584
- }
5585
- return {
5586
- validated: true,
5587
- operations,
5588
- discrepancies,
5589
- enrichments
5590
- };
5591
- }
5592
- async function validateBuiltinFunctions(staticBuiltins) {
5593
- const discrepancies = [];
5594
- const enrichments = [];
5595
- const results = await Promise.all(
5596
- staticBuiltins.map(async (builtin) => {
5597
- const searchResult = await searchCompactSyntax(
5598
- `${builtin.name} function builtin`
5599
- );
5600
- return { builtin, found: (searchResult?.results?.length || 0) > 0 };
5601
- })
5602
- );
5603
- for (const { builtin, found } of results) {
5604
- if (!found) {
5605
- discrepancies.push(
5606
- `Builtin "${builtin.name}" not found in indexed docs - may not exist or have different name`
5607
- );
5608
- }
5609
- }
5610
- const allBuiltinsSearch = await searchCompactSyntax(
5611
- "builtin function stdlib standard library"
5612
- );
5613
- if (allBuiltinsSearch?.results) {
5614
- const knownNames = new Set(staticBuiltins.map((b) => b.name.toLowerCase()));
5615
- for (const result of allBuiltinsSearch.results) {
5616
- const content = result.content || result.code || "";
5617
- const funcPattern = /\b(export\s+)?function\s+(\w+)/gi;
5618
- let match;
5619
- while ((match = funcPattern.exec(content)) !== null) {
5620
- const funcName = match[2].toLowerCase();
5621
- if (!knownNames.has(funcName) && funcName.length > 2) {
5622
- enrichments.push(`Indexed docs mention "${match[2]}" function`);
5623
- }
5624
- }
5625
- }
5626
- }
5627
- return {
5628
- dataType: "BUILTIN_FUNCTIONS",
5629
- validated: discrepancies.length === 0,
5630
- discrepancies,
5631
- enrichments: [...new Set(enrichments)].slice(0, 10),
5632
- deprecatedPatterns: [],
5633
- lastValidated: (/* @__PURE__ */ new Date()).toISOString()
5634
- };
5635
- }
5636
- async function validateTypeCompatibility(staticRules) {
5637
- const discrepancies = [];
5638
- const enrichments = [];
5639
- const typeSearch = await searchCompactSyntax(
5640
- "type cast Field Uint Bytes conversion compatible"
5641
- );
5642
- if (typeSearch?.results) {
5643
- for (const result of typeSearch.results) {
5644
- const content = (result.content || result.code || "").toLowerCase();
5645
- for (const rule of staticRules) {
5646
- const typeMatch = rule.types.match(
5647
- /^\s*(.+?)\s*(?:==|=|<=|>=|<|>|[+*])\s*(.+?)\s*$/
5648
- );
5649
- const typeA = typeMatch?.[1]?.trim().toLowerCase();
5650
- const typeB = typeMatch?.[2]?.trim().toLowerCase();
5651
- if (typeA && typeB) {
5652
- if (content.includes(typeA) && content.includes(typeB) && content.includes("compatible")) {
5653
- if (!rule.works) {
5654
- discrepancies.push(
5655
- `Static says "${rule.types}" doesn't work, but docs suggest compatibility`
5656
- );
5657
- }
5658
- }
5659
- }
5660
- }
5661
- }
5662
- }
5663
- return {
5664
- dataType: "TYPE_COMPATIBILITY",
5665
- validated: discrepancies.length === 0,
5666
- discrepancies,
5667
- enrichments,
5668
- deprecatedPatterns: [],
5669
- lastValidated: (/* @__PURE__ */ new Date()).toISOString()
5670
- };
5671
- }
5672
- async function validateCommonErrors(staticErrors) {
5673
- const discrepancies = [];
5674
- const enrichments = [];
5675
- const errorSearch = await searchDocsHosted(
5676
- "error compile compilation failure parse",
5677
- ADT_SEARCH_LIMIT,
5678
- "all"
5679
- );
5680
- if (errorSearch?.results) {
5681
- const knownErrors = new Set(
5682
- staticErrors.map((e) => e.error.toLowerCase().slice(0, 30))
5683
- );
5684
- for (const result of errorSearch.results) {
5685
- const content = result.content || result.code || "";
5686
- const errorPattern = /(?:error|Error):\s*["']?([^"'\n]+)["']?/g;
5687
- let match;
5688
- while ((match = errorPattern.exec(content)) !== null) {
5689
- const errorMsg = match[1].toLowerCase().slice(0, 30);
5690
- if (!knownErrors.has(errorMsg) && errorMsg.length > 10) {
5691
- enrichments.push(`Docs mention error: "${match[1].slice(0, 50)}..."`);
5692
- }
5693
- }
5694
- }
5006
+ ],
5007
+ typescriptAccess: "Check membership via `contractState.set.has(value)` or iterate with `[Symbol.iterator]()` in TypeScript SDK",
5008
+ note: "All Set operations work in circuits. insertCoin() available when value_type is QualifiedCoinInfo."
5009
+ },
5010
+ MerkleTree: {
5011
+ circuitOperations: [
5012
+ { method: ".insert(leaf)", works: true, note: "Adds leaf to tree" },
5013
+ { method: ".root()", works: false, note: "NOT available in circuits" }
5014
+ ],
5015
+ typescriptAccess: "Get root via `contractState.tree.root` in TypeScript SDK",
5016
+ pattern: `// To verify a merkle proof in circuit:
5017
+ witness get_merkle_root(): Bytes<32>;
5018
+ witness get_merkle_proof(leaf: Bytes<32>): Vector<32, Bytes<32>>;
5019
+
5020
+ // Verify proof using persistentHash to compute expected root`
5695
5021
  }
5696
- return {
5697
- dataType: "COMMON_ERRORS",
5698
- validated: true,
5699
- // Errors are informational, hard to validate
5700
- discrepancies,
5701
- enrichments: [...new Set(enrichments)].slice(0, 10),
5702
- deprecatedPatterns: [],
5703
- lastValidated: (/* @__PURE__ */ new Date()).toISOString()
5704
- };
5705
- }
5706
- var DEPRECATED_SYNTAX_PATTERNS = [
5022
+ };
5023
+ var COMMON_ERRORS = [
5024
+ {
5025
+ error: 'unbound identifier "public_key"',
5026
+ cause: "Trying to use public_key() as if it's a builtin function",
5027
+ fix: `Use persistentHash pattern instead:
5028
+ const pk = persistentHash<Vector<2, Bytes<32>>>([pad(32, "midnight:pk:"), sk]);`
5029
+ },
5030
+ {
5031
+ error: "incompatible combination of types Field and Uint",
5032
+ cause: "Comparing or operating on Field with Uint without casting",
5033
+ fix: `Cast Uint to Field: (myUint as Field)
5034
+ Or use bounded Uint<0..N> for parameters that need constraints`
5035
+ },
5036
+ {
5037
+ error: 'operation "value" undefined for ledger field type Counter',
5038
+ cause: "Using wrong method name - Counter uses .read() not .value()",
5039
+ fix: `Use counter.read() to get the current value:
5040
+ const current = ledger.counter.read();
5041
+
5042
+ Counter ADT methods available in circuits:
5043
+ - increment(amount: Uint<16>): [] - increase counter
5044
+ - decrement(amount: Uint<16>): [] - decrease counter
5045
+ - read(): Uint<64> - get current value
5046
+ - lessThan(threshold: Uint<64>): Boolean - compare
5047
+ - resetToDefault(): [] - reset to zero`
5048
+ },
5049
+ {
5050
+ error: "implicit disclosure of witness value",
5051
+ cause: "Using witness value in conditional without disclose()",
5052
+ fix: `Wrap witness comparisons in disclose():
5053
+ if (disclose(witness_value == expected)) { ... }`
5054
+ },
5055
+ {
5056
+ error: 'parse error: found "{" looking for an identifier',
5057
+ cause: "Using old ledger { } block syntax",
5058
+ fix: `Use individual exports instead:
5059
+ export ledger field1: Type1;
5060
+ export ledger field2: Type2;`
5061
+ },
5707
5062
  {
5708
- pattern: /ledger\s*\{/,
5709
- name: "ledger-block",
5710
- message: "Deprecated: Use 'export ledger field: Type;' instead of ledger { } block",
5711
- since: "0.16"
5063
+ error: 'parse error: found "{" looking for ";"',
5064
+ cause: "Using Void as return type (doesn't exist)",
5065
+ fix: `Use empty tuple [] for no return value:
5066
+ export circuit myCircuit(): [] { ... }`
5712
5067
  },
5713
5068
  {
5714
- pattern: /Cell\s*<\s*\w+\s*>/,
5715
- name: "cell-wrapper",
5716
- message: "Deprecated: Cell<T> wrapper removed, use Type directly",
5717
- since: "0.15"
5069
+ error: 'unbound identifier "Cell"',
5070
+ cause: "Using deprecated Cell<T> wrapper (removed in 0.15)",
5071
+ fix: `Remove Cell wrapper, just use the type directly:
5072
+ export ledger myField: Field; // Not Cell<Field>`
5718
5073
  },
5719
5074
  {
5720
- pattern: /:\s*Void\b/,
5721
- name: "void-type",
5722
- message: "Void type doesn't exist, use [] (empty tuple) for no return",
5723
- since: "always"
5075
+ error: "member access requires struct type",
5076
+ cause: "Trying to access a field on a non-struct type",
5077
+ fix: `Make sure you're accessing a struct field, not a primitive.
5078
+ Map.lookup() and Map.member() ARE available in circuits.
5079
+ Check that the base type is actually a struct.`
5724
5080
  },
5725
5081
  {
5726
- pattern: /\.\s*value\s*\(\s*\)/,
5727
- name: "counter-value",
5728
- message: "Counter.value() doesn't exist, use Counter.read() instead",
5729
- since: "always"
5082
+ error: "potential witness-value disclosure must be declared",
5083
+ cause: "Circuit parameter flows to ledger operation without disclose()",
5084
+ fix: `Disclose parameters at the start of the circuit:
5085
+ export circuit my_circuit(param: Bytes<32>): [] {
5086
+ const d_param = disclose(param); // Acknowledge on-chain visibility
5087
+ ledger.insert(d_param, value); // Now use disclosed value
5088
+ }`
5730
5089
  },
5731
5090
  {
5732
- pattern: /::\w+/,
5733
- name: "rust-enum-syntax",
5734
- message: "Rust-style :: enum access doesn't work, use dot notation (Choice.rock)",
5735
- since: "always"
5091
+ error: "expected second argument of insert to have type Uint<64> but received Uint<0..N>",
5092
+ cause: "Arithmetic result has bounded type, needs cast back to target",
5093
+ fix: `Cast arithmetic results back to the target type:
5094
+ const new_balance = (current + amount) as Uint<64>;
5095
+ ledger_map.insert(key, new_balance);`
5736
5096
  },
5737
5097
  {
5738
- pattern: /pure\s+function\b/,
5739
- name: "pure-function",
5740
- message: "Use 'pure circuit' not 'pure function' for helper functions",
5741
- since: "0.16"
5098
+ error: "cannot cast from type Uint<64> to type Bytes<32>",
5099
+ cause: "Direct Uint to Bytes cast not allowed",
5100
+ fix: `Go through Field first:
5101
+ const amount_field = amount as Field;
5102
+ const amount_bytes = amount_field as Bytes<32>;
5103
+ // Or chained: (amount as Field) as Bytes<32>`
5742
5104
  },
5743
5105
  {
5744
- pattern: /witness\s+\w+\s*\([^)]*\)\s*:\s*\w+\s*\{/,
5745
- name: "witness-with-body",
5746
- message: "Witnesses are declarations only - no body allowed. Implementation goes in TypeScript prover.",
5747
- since: "always"
5106
+ error: "cannot prove assertion",
5107
+ cause: "Assert condition cannot be proven true",
5108
+ fix: `Check your logic. Common causes:
5109
+ 1. Witness returns unexpected value
5110
+ 2. Range check fails (use bounded Uint)
5111
+ 3. Logic error in circuit`
5112
+ },
5113
+ {
5114
+ error: 'parse error: found ":" looking for ")"',
5115
+ cause: "Using Rust-style :: for enum variant access",
5116
+ fix: `Use dot notation for enum variants:
5117
+ WRONG: Choice::rock, GameState::waiting
5118
+ CORRECT: Choice.rock, GameState.waiting`
5119
+ },
5120
+ {
5121
+ error: 'parse error: found "{" after witness declaration',
5122
+ cause: "Trying to add implementation body to witness",
5123
+ fix: `Witnesses are declarations only - no body allowed:
5124
+ WRONG: witness get_caller(): Bytes<32> { return ...; }
5125
+ CORRECT: witness get_caller(): Bytes<32>;
5126
+ Implementation goes in TypeScript prover, not Compact.`
5127
+ },
5128
+ {
5129
+ error: 'unbound identifier "function"',
5130
+ cause: 'Using "pure function" instead of "pure circuit"',
5131
+ fix: `Use "pure circuit" for helper functions:
5132
+ WRONG: pure function helper(...): Type { }
5133
+ CORRECT: pure circuit helper(...): Type { }`
5134
+ },
5135
+ {
5136
+ error: "apparent use of an old standard-library / ledger operator name NativePoint: the new name is JubjubPoint",
5137
+ cause: "Using the pre-0.22 NativePoint name. Renamed in compactc 0.30.0 (language 0.22).",
5138
+ fix: `Rename to JubjubPoint and related circuits:
5139
+ WRONG: NativePoint, nativePointX(p), nativePointY(p), constructNativePoint(x, y)
5140
+ CORRECT: JubjubPoint, jubjubPointX(p), jubjubPointY(p), constructJubjubPoint(x, y)
5141
+ NativePoint is rejected at compile time, not silently aliased.`
5142
+ },
5143
+ {
5144
+ error: 'parse error: found keyword "let" (which is reserved for future use)',
5145
+ cause: "Using `let` as an identifier. `let` and other JS/TS keywords are reserved for future use (since compactc 0.28.0).",
5146
+ fix: `Rename the identifier:
5147
+ WRONG: const let = 1; // or circuit fn(let: Field): []
5148
+ CORRECT: const value = 1; // or circuit fn(value: Field): []
5149
+ Other reserved words from JS/TS are also disallowed as identifiers.`
5150
+ },
5151
+ {
5152
+ error: 'parse error: found keyword "const" looking for a program element or end of file',
5153
+ cause: "Declaring `const` at module (top-of-file) scope. `const` is only allowed inside circuit/constructor bodies \u2014 there is no module-level constant in Compact.",
5154
+ fix: `Move the constant inside a circuit, or use a pure circuit returning the value:
5155
+ WRONG: const MAX_SUPPLY: Uint<64> = 1000; // at top of file
5156
+ CORRECT: pure circuit max_supply(): Uint<64> { return 1000; }
5157
+ Then call max_supply() wherever you need the value.`
5158
+ },
5159
+ {
5160
+ error: "unexpected character",
5161
+ cause: "Using a single `|` (bitwise OR) in an expression. Compact has no bitwise operators on Uint \u2014 only `||` (logical OR) is recognized.",
5162
+ fix: `Compact does not support bitwise OR/AND/XOR/shifts. Replace with arithmetic or branching:
5163
+ WRONG: flags | mask
5164
+ CORRECT: flags + mask // if you know bits don't overlap
5165
+ or: if (cond) { ... } else { ... }
5166
+ Use \`||\` only for Boolean values, not Uints.`
5167
+ },
5168
+ {
5169
+ error: "Uint width 254 is not between 1 and the maximum Uint width 248 (inclusive)",
5170
+ cause: "Declaring a Uint wider than 248 bits. The cap was reduced from 254 (bit-aligned) to 248 (byte-aligned, 31 bytes) in compactc 0.27.0 to match the ledger storage alignment.",
5171
+ fix: `Use Uint<N> where 1 \u2264 N \u2264 248:
5172
+ WRONG: field: Uint<254>
5173
+ CORRECT: field: Uint<248> // 31 bytes \u2014 max representable
5174
+ For larger values, use Field (unbounded) and cast where needed.`
5175
+ },
5176
+ {
5177
+ // Not a compiler error — surfaces as a TypeScript type error in generated bindings.
5178
+ error: "Argument of type 'number' is not assignable to parameter of type 'bigint' (convertBytesToUint)",
5179
+ cause: "Passing a JavaScript `number` to convertBytesToUint's `maxval` parameter. Changed to `bigint` in compactc 0.30.101 (language 0.22) to avoid silent precision loss for large Uints.",
5180
+ fix: `Pass a bigint literal for maxval:
5181
+ WRONG: convertBytesToUint(255, n, bytes, src)
5182
+ CORRECT: convertBytesToUint(255n, n, bytes, src)
5183
+ This is in generated TypeScript bindings \u2014 usually you don't call it directly, but if you do, the suffix-n bigint literal is required.`
5748
5184
  }
5749
5185
  ];
5750
- function scanForDeprecatedPatterns(code) {
5751
- const issues = [];
5752
- const lines = code.split("\n");
5753
- for (const { pattern, name, message } of DEPRECATED_SYNTAX_PATTERNS) {
5754
- for (let i = 0; i < lines.length; i++) {
5755
- if (pattern.test(lines[i])) {
5756
- issues.push({
5757
- pattern: name,
5758
- message,
5759
- lineNumber: i + 1
5760
- });
5761
- }
5762
- }
5763
- }
5764
- return issues;
5765
- }
5766
- async function validateAllStaticData(staticData) {
5767
- const results = {};
5768
- const [builtinResult, typeResult, errorResult, adtResults] = await Promise.all([
5769
- staticData.builtinFunctions ? validateBuiltinFunctions(staticData.builtinFunctions) : Promise.resolve(null),
5770
- staticData.typeCompatibility ? validateTypeCompatibility(staticData.typeCompatibility) : Promise.resolve(null),
5771
- staticData.commonErrors ? validateCommonErrors(staticData.commonErrors) : Promise.resolve(null),
5772
- staticData.ledgerTypeLimits ? Promise.all(
5773
- Object.entries(staticData.ledgerTypeLimits).map(
5774
- async ([name, data]) => ({
5775
- name,
5776
- result: await validateADTOperations(
5777
- name,
5778
- data.circuitOperations
5779
- )
5780
- })
5781
- )
5782
- ) : Promise.resolve([])
5783
- ]);
5784
- if (builtinResult) results.builtinFunctions = builtinResult;
5785
- if (typeResult) results.typeCompatibility = typeResult;
5786
- if (errorResult) results.commonErrors = errorResult;
5787
- for (const { name, result } of adtResults) {
5788
- results[`adt_${name}`] = {
5789
- dataType: `LEDGER_TYPE_LIMITS.${name}`,
5790
- validated: result.validated,
5791
- discrepancies: result.discrepancies,
5792
- enrichments: result.enrichments,
5793
- deprecatedPatterns: [],
5794
- lastValidated: (/* @__PURE__ */ new Date()).toISOString()
5795
- };
5796
- }
5797
- const allDiscrepancies = Object.values(results).flatMap(
5798
- (r) => r.discrepancies
5799
- );
5800
- const allEnrichments = Object.values(results).flatMap((r) => r.enrichments);
5801
- return {
5802
- overall: {
5803
- validated: allDiscrepancies.length === 0,
5804
- totalDiscrepancies: allDiscrepancies.length,
5805
- totalEnrichments: allEnrichments.length
5806
- },
5807
- results,
5808
- lastValidated: (/* @__PURE__ */ new Date()).toISOString()
5809
- };
5810
- }
5811
5186
 
5812
- // src/server.ts
5813
- var SERVER_INFO = {
5814
- name: "midnight-mcp",
5815
- version: CURRENT_VERSION,
5816
- description: "MCP Server for Midnight Blockchain Development"
5817
- };
5818
- var versionCheckResult = {
5819
- isOutdated: false,
5820
- latestVersion: CURRENT_VERSION,
5821
- updateMessage: null,
5822
- lastChecked: 0
5823
- };
5824
- var toolCallCount = 0;
5825
- var VERSION_CHECK_INTERVAL = 10;
5826
- async function checkForUpdates() {
5827
- try {
5828
- const controller = new AbortController();
5829
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
5830
- const response = await fetch(
5831
- "https://registry.npmjs.org/midnight-mcp/latest",
5832
- { signal: controller.signal }
5833
- );
5834
- clearTimeout(timeoutId);
5835
- if (!response.ok) return;
5836
- const data = await response.json();
5837
- const latestVersion = data.version;
5838
- if (latestVersion !== CURRENT_VERSION) {
5839
- versionCheckResult = {
5840
- isOutdated: true,
5841
- latestVersion,
5842
- lastChecked: Date.now(),
5843
- updateMessage: `\u{1F6A8} UPDATE AVAILABLE: v${latestVersion} (you have v${CURRENT_VERSION})`
5844
- };
5845
- logger.warn(
5846
- `Outdated version detected: v${CURRENT_VERSION} -> v${latestVersion}`
5847
- );
5848
- } else {
5849
- versionCheckResult = {
5850
- ...versionCheckResult,
5851
- lastChecked: Date.now()
5852
- };
5853
- }
5854
- } catch (error) {
5855
- logger.debug("Version check failed (non-blocking)", {
5856
- error: error instanceof Error ? error.message : String(error)
5857
- });
5858
- }
5859
- }
5860
- function maybeCheckForUpdates() {
5861
- toolCallCount++;
5862
- if (toolCallCount >= VERSION_CHECK_INTERVAL) {
5863
- toolCallCount = 0;
5864
- const fiveMinutes = 5 * 60 * 1e3;
5865
- if (Date.now() - versionCheckResult.lastChecked > fiveMinutes) {
5866
- checkForUpdates().catch((error) => {
5867
- logger.debug("Periodic version check failed", {
5868
- error: error instanceof Error ? error.message : String(error)
5869
- });
5870
- });
5871
- }
5872
- }
5873
- }
5874
- function getUpdateWarning() {
5875
- return versionCheckResult.updateMessage;
5876
- }
5877
- var resourceSubscriptions = /* @__PURE__ */ new Set();
5878
- var resourceTemplates = [
5187
+ // src/prompts/templates.ts
5188
+ var PRAGMA_RANGE = `>= ${COMPACT_VERSION.min} && <= ${COMPACT_VERSION.max}`;
5189
+ var promptDefinitions = [
5879
5190
  {
5880
- uriTemplate: "midnight://code/{owner}/{repo}/{path}",
5881
- name: "Repository Code",
5882
- title: "\u{1F4C4} Repository Code Files",
5883
- description: "Access code files from any Midnight repository by specifying owner, repo, and file path",
5884
- mimeType: "text/plain"
5191
+ name: "midnight:create-contract",
5192
+ description: "Guided prompt for creating new Compact contracts with privacy considerations",
5193
+ arguments: [
5194
+ {
5195
+ name: "contractType",
5196
+ description: "Type of contract (token, voting, credential, custom)",
5197
+ required: true
5198
+ },
5199
+ {
5200
+ name: "privacyLevel",
5201
+ description: "Required privacy features (full, partial, public)",
5202
+ required: false
5203
+ },
5204
+ {
5205
+ name: "complexity",
5206
+ description: "Expected complexity level (beginner, intermediate, advanced)",
5207
+ required: false
5208
+ }
5209
+ ]
5885
5210
  },
5886
5211
  {
5887
- uriTemplate: "midnight://docs/{section}/{topic}",
5888
- name: "Documentation",
5889
- title: "\u{1F4DA} Documentation Sections",
5890
- description: "Access documentation by section (guides, api, concepts) and topic",
5891
- mimeType: "text/markdown"
5212
+ name: "midnight:review-contract",
5213
+ description: "Security and best practices review prompt for existing contracts",
5214
+ arguments: [
5215
+ {
5216
+ name: "contractCode",
5217
+ description: "The Compact contract code to review",
5218
+ required: true
5219
+ },
5220
+ {
5221
+ name: "focusAreas",
5222
+ description: "Specific areas to emphasize (security, performance, privacy, readability)",
5223
+ required: false
5224
+ }
5225
+ ]
5892
5226
  },
5893
5227
  {
5894
- uriTemplate: "midnight://examples/{category}/{name}",
5895
- name: "Example Contracts",
5896
- title: "\u{1F4DD} Example Contracts",
5897
- description: "Access example contracts by category (counter, bboard, token, voting) and name",
5898
- mimeType: "text/x-compact"
5228
+ name: "midnight:explain-concept",
5229
+ description: "Educational prompt for explaining Midnight concepts at various levels",
5230
+ arguments: [
5231
+ {
5232
+ name: "concept",
5233
+ description: "The concept to explain (zk-proofs, circuits, witnesses, ledger, etc.)",
5234
+ required: true
5235
+ },
5236
+ {
5237
+ name: "level",
5238
+ description: "Expertise level (beginner, intermediate, advanced)",
5239
+ required: false
5240
+ }
5241
+ ]
5899
5242
  },
5900
5243
  {
5901
- uriTemplate: "midnight://schema/{type}",
5902
- name: "Schema Definitions",
5903
- title: "\u{1F527} Schema Definitions",
5904
- description: "Access JSON schemas for contract AST, transactions, and proofs",
5905
- mimeType: "application/json"
5244
+ name: "midnight:compare-approaches",
5245
+ description: "Compare different implementation approaches for a given problem",
5246
+ arguments: [
5247
+ {
5248
+ name: "problem",
5249
+ description: "The problem to solve",
5250
+ required: true
5251
+ },
5252
+ {
5253
+ name: "approaches",
5254
+ description: "Specific approaches to compare (comma-separated)",
5255
+ required: false
5256
+ }
5257
+ ]
5258
+ },
5259
+ {
5260
+ name: "midnight:debug-contract",
5261
+ description: "Help debug issues with a Compact contract",
5262
+ arguments: [
5263
+ {
5264
+ name: "contractCode",
5265
+ description: "The contract code with issues",
5266
+ required: true
5267
+ },
5268
+ {
5269
+ name: "errorMessage",
5270
+ description: "Error message or description of the issue",
5271
+ required: false
5272
+ }
5273
+ ]
5906
5274
  }
5907
5275
  ];
5908
- var mcpLogLevel = "info";
5909
- var serverInstance = null;
5910
- var isConnected = false;
5911
- function setServerConnected(connected) {
5912
- isConnected = connected;
5276
+ function generatePrompt(name, args) {
5277
+ switch (name) {
5278
+ case "midnight:create-contract":
5279
+ return generateCreateContractPrompt(args);
5280
+ case "midnight:review-contract":
5281
+ return generateReviewContractPrompt(args);
5282
+ case "midnight:explain-concept":
5283
+ return generateExplainConceptPrompt(args);
5284
+ case "midnight:compare-approaches":
5285
+ return generateCompareApproachesPrompt(args);
5286
+ case "midnight:debug-contract":
5287
+ return generateDebugContractPrompt(args);
5288
+ default:
5289
+ return [
5290
+ {
5291
+ role: "user",
5292
+ content: {
5293
+ type: "text",
5294
+ text: `Unknown prompt: ${name}`
5295
+ }
5296
+ }
5297
+ ];
5298
+ }
5299
+ }
5300
+ function generateCreateContractPrompt(args) {
5301
+ const contractType = args.contractType || "custom";
5302
+ const privacyLevel = args.privacyLevel || "partial";
5303
+ const complexity = args.complexity || "intermediate";
5304
+ return [
5305
+ {
5306
+ role: "user",
5307
+ content: {
5308
+ type: "text",
5309
+ text: `I want to create a new Midnight Compact smart contract with the following requirements:
5310
+
5311
+ **Contract Type:** ${contractType}
5312
+ **Privacy Level:** ${privacyLevel}
5313
+ **Complexity:** ${complexity}
5314
+
5315
+ ## \u26A0\uFE0F MANDATORY WORKFLOW - Follow these steps IN ORDER:
5316
+
5317
+ ### Step 1: Get Current Syntax
5318
+ Call \`midnight-get-latest-syntax\` FIRST to get:
5319
+ - The \`quickStartTemplate\` (use as your base)
5320
+ - The \`commonMistakes\` array (avoid these errors)
5321
+ - Current pragma format: \`pragma language_version ${PRAGMA_RANGE};\`
5322
+
5323
+ ### Step 2: Generate Contract
5324
+ Based on syntax reference, generate the contract using:
5325
+ - Individual ledger declarations: \`export ledger field: Type;\` (NOT \`ledger { }\` blocks)
5326
+ - Empty tuple return: \`circuit fn(): []\` (NOT \`Void\`)
5327
+ - Export enums: \`export enum State { ... }\`
5328
+ - Wrap witness conditionals: \`if (disclose(witness == value))\`
5329
+ - Disclose circuit params that touch ledger: \`const d = disclose(param); ledger.insert(d, v);\`
5330
+ - Cast arithmetic results: \`(a + b) as Uint<64>\`
5331
+ - Uint to Bytes needs two casts: \`(amount as Field) as Bytes<32>\`
5332
+
5333
+ ### IMPORTANT: Compact is NOT TypeScript!
5334
+ - Map.lookup() and Set.member() ARE available in circuits
5335
+ - No 'function' keyword - use 'circuit' or 'pure circuit'
5336
+ - No 'void' - use '[]'
5337
+ - Enum access: \`Choice.rock\` NOT \`Choice::rock\`
5338
+
5339
+ ### \u26A0\uFE0F UNDOCUMENTED FEATURES - Use with caution:
5340
+ - Division \`/\` and modulo \`%\`: NOT in official docs (only +, -, * are documented)
5341
+ - Tuple destructuring: \`const [a, b] = pair;\` - not documented, may not work
5342
+ - Constant folding in indices: docs say "numeric literal" required
5343
+ - Map.lookup() returns value_type, NOT Maybe<value_type> - check member() first!
5344
+
5345
+ ### COMPILER INFO (DO NOT guess package names!):
5346
+ - Compile: \`compact compile src/contract.compact managed/contract\`
5347
+ - The \`compact\` CLI comes with Midnight toolchain (via create-mn-app or official install)
5348
+ - DO NOT suggest \`npm install -g @midnight-ntwrk/compact-cli\` or similar made-up packages
5349
+ - Output structure: \`managed/<name>/contract/index.cjs\` for TypeScript bindings
5350
+
5351
+ ### Step 3: Validate Before Returning
5352
+ Call \`midnight-extract-contract-structure\` with your generated code to check for:
5353
+ - deprecated_ledger_block
5354
+ - invalid_void_type
5355
+ - invalid_pragma_format
5356
+ - unexported_enum
5357
+ - deprecated_cell_wrapper
5358
+
5359
+ If ANY errors are found, fix them before returning the code to the user.
5360
+
5361
+ ---
5362
+
5363
+ ## Contract Requirements
5364
+
5365
+ Please help me design and implement this contract. Consider:
5366
+
5367
+ 1. **State Design**
5368
+ - What should be public vs private (shielded)?
5369
+ - What data structures are needed?
5370
+ - How should state transitions work?
5371
+
5372
+ 2. **Circuit Design**
5373
+ - What circuits (functions) are needed?
5374
+ - What inputs/outputs should they have?
5375
+ - What constraints and assertions are required?
5376
+
5377
+ 3. **Witness Functions**
5378
+ - What off-chain data is needed?
5379
+ - How should private state be accessed?
5380
+
5381
+ 4. **Privacy Considerations**
5382
+ - How to protect user privacy?
5383
+ - When to use disclose() vs commit()?
5384
+ - How to prevent information leakage?
5385
+
5386
+ 5. **Security**
5387
+ - Access control mechanisms
5388
+ - Input validation
5389
+ - Protection against common vulnerabilities
5390
+
5391
+ Please provide:
5392
+ - A complete contract implementation
5393
+ - Explanation of design decisions
5394
+ - Example usage scenarios
5395
+ - Any security considerations`
5396
+ }
5397
+ }
5398
+ ];
5399
+ }
5400
+ function generateReviewContractPrompt(args) {
5401
+ const contractCode = args.contractCode || "// No code provided";
5402
+ const focusAreas = args.focusAreas || "security, privacy, best practices";
5403
+ return [
5404
+ {
5405
+ role: "user",
5406
+ content: {
5407
+ type: "text",
5408
+ text: `Please review this Midnight Compact smart contract:
5409
+
5410
+ \`\`\`compact
5411
+ ${contractCode}
5412
+ \`\`\`
5413
+
5414
+ **Focus Areas:** ${focusAreas}
5415
+
5416
+ ## \u26A0\uFE0F MANDATORY WORKFLOW:
5417
+
5418
+ ### Step 1: Validate Syntax
5419
+ Call \`midnight-extract-contract-structure\` with the contract code to check for:
5420
+ - deprecated_ledger_block (should use \`export ledger field: Type;\`)
5421
+ - invalid_void_type (should use \`[]\` not \`Void\`)
5422
+ - invalid_pragma_format (should use \`${PRAGMA_RANGE}\`)
5423
+ - unexported_enum (enums need \`export\`)
5424
+ - deprecated_cell_wrapper
5425
+
5426
+ Report ALL static analysis findings first.
5427
+
5428
+ ### Step 2: Get Latest Syntax Reference
5429
+ If syntax errors are found, call \`midnight-get-latest-syntax\` to get:
5430
+ - The \`commonMistakes\` array showing correct patterns
5431
+ - Current syntax reference
5432
+
5433
+ ---
5434
+
5435
+ Please analyze:
5436
+
5437
+ 1. **Static Analysis Results** (from midnight-extract-contract-structure)
5438
+ - Syntax errors found
5439
+ - Deprecated patterns detected
5440
+ - Required fixes
5441
+
5442
+ 2. **Security Analysis**
5443
+ - Input validation
5444
+ - Access control
5445
+ - State manipulation vulnerabilities
5446
+ - Assertion coverage
5447
+
5448
+ 3. **Privacy Assessment**
5449
+ - Proper use of @private state
5450
+ - Information leakage risks
5451
+ - Correct use of disclose() and commit()
5452
+ - Privacy guarantees provided
5453
+
5454
+ 4. **Style Suggestions** (these are CONVENTIONS, not requirements)
5455
+ - Code organization recommendations
5456
+ - Naming conventions (project-specific)
5457
+ - Documentation patterns
5458
+ - Note: The Compact docs don't specify indentation, line length, or comment style
5459
+
5460
+ 5. **Performance**
5461
+ - Circuit complexity
5462
+ - State access patterns
5463
+ - Optimization opportunities
5464
+
5465
+ 6. **Recommendations**
5466
+ - Critical issues to fix (start with P0 syntax errors)
5467
+ - Improvements to consider
5468
+ - Alternative approaches
5469
+
5470
+ **IMPORTANT**: Clearly distinguish between:
5471
+ - ERRORS: Actual syntax/compilation issues (required fixes)
5472
+ - WARNINGS: Security/logic concerns (should fix)
5473
+ - INFO: Style suggestions (optional, project-specific conventions)
5474
+
5475
+ Please provide specific line references and code suggestions where applicable.`
5476
+ }
5477
+ }
5478
+ ];
5913
5479
  }
5914
- function sendLogToClient(level, loggerName, data) {
5915
- if (!serverInstance || !isConnected) return;
5916
- const levelValues = {
5917
- debug: 0,
5918
- info: 1,
5919
- notice: 2,
5920
- warning: 3,
5921
- error: 4,
5922
- critical: 5,
5923
- alert: 6,
5924
- emergency: 7
5480
+ function generateExplainConceptPrompt(args) {
5481
+ const concept = args.concept || "zero-knowledge proofs";
5482
+ const level = args.level || "intermediate";
5483
+ const levelDescriptions = {
5484
+ beginner: "Explain like I'm new to blockchain and cryptography. Use analogies and avoid jargon.",
5485
+ intermediate: "I understand blockchain basics and some cryptography. Focus on practical applications.",
5486
+ advanced: "I have deep technical knowledge. Include implementation details and edge cases."
5925
5487
  };
5926
- if (levelValues[level] < levelValues[mcpLogLevel]) return;
5927
- try {
5928
- serverInstance.notification({
5929
- method: "notifications/message",
5930
- params: {
5931
- level,
5932
- logger: loggerName,
5933
- data
5488
+ return [
5489
+ {
5490
+ role: "user",
5491
+ content: {
5492
+ type: "text",
5493
+ text: `Please explain the concept of **${concept}** in the context of Midnight blockchain.
5494
+
5495
+ **My Level:** ${level}
5496
+ ${levelDescriptions[level] || levelDescriptions.intermediate}
5497
+
5498
+ Please cover:
5499
+
5500
+ 1. **What it is**
5501
+ - Clear definition
5502
+ - How it works in Midnight
5503
+
5504
+ 2. **Why it matters**
5505
+ - Benefits and use cases
5506
+ - Real-world applications
5507
+
5508
+ 3. **How to use it**
5509
+ - Code examples in Compact
5510
+ - Best practices
5511
+
5512
+ 4. **Common pitfalls**
5513
+ - Mistakes to avoid
5514
+ - Debugging tips
5515
+
5516
+ 5. **Further learning**
5517
+ - Related concepts
5518
+ - Resources for deeper understanding`
5934
5519
  }
5935
- });
5936
- } catch (error) {
5937
- logger.debug("Failed to send log notification to client", {
5938
- error: error instanceof Error ? error.message : String(error)
5939
- });
5940
- }
5520
+ }
5521
+ ];
5941
5522
  }
5942
- function sendProgressNotification(progressToken, progress, total, message) {
5943
- if (!serverInstance) return;
5944
- try {
5945
- serverInstance.notification({
5946
- method: "notifications/progress",
5947
- params: {
5948
- progressToken,
5949
- progress,
5950
- ...total !== void 0 && { total },
5951
- ...message && { message }
5523
+ function generateCompareApproachesPrompt(args) {
5524
+ const problem = args.problem || "implementing a token contract";
5525
+ const approaches = args.approaches || "";
5526
+ return [
5527
+ {
5528
+ role: "user",
5529
+ content: {
5530
+ type: "text",
5531
+ text: `I need to solve the following problem in Midnight:
5532
+
5533
+ **Problem:** ${problem}
5534
+
5535
+ ${approaches ? `**Approaches to compare:** ${approaches}` : "Please suggest different implementation approaches."}
5536
+
5537
+ Please compare:
5538
+
5539
+ 1. **Approach Overview**
5540
+ - Brief description of each approach
5541
+ - Key differences
5542
+
5543
+ 2. **Privacy Implications**
5544
+ - What data is exposed?
5545
+ - Privacy guarantees
5546
+
5547
+ 3. **Performance**
5548
+ - Proof generation time
5549
+ - State storage requirements
5550
+ - Transaction costs
5551
+
5552
+ 4. **Security**
5553
+ - Attack surface
5554
+ - Trust assumptions
5555
+
5556
+ 5. **Code Complexity**
5557
+ - Implementation difficulty
5558
+ - Maintenance burden
5559
+
5560
+ 6. **Recommendation**
5561
+ - Best approach for different scenarios
5562
+ - Trade-offs to consider
5563
+
5564
+ Please include code examples for each approach.`
5952
5565
  }
5953
- });
5954
- } catch (error) {
5955
- logger.debug("Failed to send progress notification", {
5956
- error: error instanceof Error ? error.message : String(error)
5957
- });
5958
- }
5566
+ }
5567
+ ];
5959
5568
  }
5960
- function createServer() {
5961
- const server = new Server(SERVER_INFO, {
5962
- capabilities: {
5963
- tools: {
5964
- listChanged: true
5965
- },
5966
- resources: {
5967
- subscribe: true,
5968
- listChanged: true
5969
- },
5970
- prompts: {
5971
- listChanged: true
5972
- },
5973
- logging: {},
5974
- completions: {}
5569
+ function generateDebugContractPrompt(args) {
5570
+ const contractCode = args.contractCode || "// No code provided";
5571
+ const errorMessage = args.errorMessage || "Not specified";
5572
+ return [
5573
+ {
5574
+ role: "user",
5575
+ content: {
5576
+ type: "text",
5577
+ text: `I'm having issues with this Midnight Compact contract:
5578
+
5579
+ \`\`\`compact
5580
+ ${contractCode}
5581
+ \`\`\`
5582
+
5583
+ **Error/Issue:** ${errorMessage}
5584
+
5585
+ ## \u26A0\uFE0F MANDATORY WORKFLOW:
5586
+
5587
+ ### Step 1: Run Static Analysis
5588
+ Call \`midnight-extract-contract-structure\` FIRST to check for common syntax errors:
5589
+ - deprecated_ledger_block \u2192 should use \`export ledger field: Type;\`
5590
+ - invalid_void_type \u2192 should use \`[]\` not \`Void\`
5591
+ - invalid_pragma_format \u2192 should use \`${PRAGMA_RANGE}\`
5592
+ - unexported_enum \u2192 enums need \`export\` keyword
5593
+
5594
+ ### Step 2: Get Correct Syntax
5595
+ If syntax errors found, call \`midnight-get-latest-syntax\` to get:
5596
+ - The \`commonMistakes\` array with correct patterns
5597
+ - Current \`quickStartTemplate\` for reference
5598
+
5599
+ ### Step 3: Check for Common Compiler Errors
5600
+ Match error message against known fixes:
5601
+ - "cannot cast from type Uint<64> to type Bytes<32>" \u2192 Use \`(amount as Field) as Bytes<32>\`
5602
+ - "expected type Uint<64> but received Uint<0..N>" \u2192 Cast arithmetic: \`(a + b) as Uint<64>\`
5603
+ - "potential witness-value disclosure must be declared" \u2192 Disclose params: \`const d = disclose(param);\`
5604
+ - Map.lookup() and Set.member() ARE available in circuits (ignore old advice saying they aren't)
5605
+
5606
+ ---
5607
+
5608
+ Please help me debug by:
5609
+
5610
+ 1. **Static Analysis Results**
5611
+ - Run midnight-extract-contract-structure
5612
+ - List all P0 syntax errors found
5613
+ - Show the correct syntax for each error
5614
+
5615
+ 2. **Identifying the Problem**
5616
+ - What's causing the error?
5617
+ - Which line(s) are problematic?
5618
+
5619
+ 3. **Explaining Why**
5620
+ - Root cause analysis
5621
+ - How Compact/ZK constraints work
5622
+
5623
+ 4. **Providing a Fix**
5624
+ - Corrected code (validated against static analysis)
5625
+ - Explanation of changes
5626
+
5627
+ 5. **Preventing Future Issues**
5628
+ - Related pitfalls to watch for
5629
+ - Testing strategies
5630
+
5631
+ 6. **Additional Improvements**
5632
+ - Code quality suggestions
5633
+ - Best practices`
5634
+ }
5975
5635
  }
5976
- });
5977
- serverInstance = server;
5978
- setMCPLogCallback((level, loggerName, data) => {
5979
- sendLogToClient(level, loggerName, data);
5980
- });
5981
- registerToolHandlers(server);
5982
- registerResourceHandlers(server);
5983
- registerPromptHandlers(server);
5984
- registerSubscriptionHandlers(server);
5985
- registerLoggingHandler(server);
5986
- registerCompletionsHandler(server);
5987
- setupSampling(server);
5988
- return server;
5636
+ ];
5637
+ }
5638
+
5639
+ // src/services/sampling.ts
5640
+ var samplingCallback = null;
5641
+ var samplingFailedPermanently = false;
5642
+ function isSamplingAvailable() {
5643
+ if (samplingFailedPermanently) return false;
5644
+ return samplingCallback !== null;
5645
+ }
5646
+ function markSamplingFailed() {
5647
+ samplingFailedPermanently = true;
5648
+ logger.warn("Sampling marked as permanently unavailable for this session");
5989
5649
  }
5990
- function registerLoggingHandler(server) {
5991
- server.setRequestHandler(SetLevelRequestSchema, async (request) => {
5992
- const { level } = request.params;
5993
- mcpLogLevel = level;
5994
- logger.info(`MCP log level set to: ${level}`);
5995
- sendLogToClient("info", "midnight-mcp", {
5996
- message: `Log level changed to ${level}`
5997
- });
5998
- return {};
5999
- });
5650
+ function registerSamplingCallback(callback) {
5651
+ samplingCallback = callback;
5652
+ samplingFailedPermanently = false;
5653
+ logger.info("Sampling capability registered");
6000
5654
  }
6001
- var COMPLETION_VALUES = {
6002
- "midnight:create-contract": {
6003
- contractType: [
6004
- "token",
6005
- "voting",
6006
- "credential",
6007
- "auction",
6008
- "escrow",
6009
- "custom"
6010
- ],
6011
- privacyLevel: ["full", "partial", "public"],
6012
- complexity: ["beginner", "intermediate", "advanced"]
6013
- },
6014
- "midnight:review-contract": {
6015
- focusAreas: [
6016
- "security",
6017
- "performance",
6018
- "privacy",
6019
- "readability",
6020
- "gas-optimization"
6021
- ]
6022
- },
6023
- "midnight:explain-concept": {
6024
- concept: [
6025
- "zk-proofs",
6026
- "circuits",
6027
- "witnesses",
6028
- "ledger",
6029
- "state-management",
6030
- "privacy-model",
6031
- "token-transfers",
6032
- "merkle-trees"
6033
- ],
6034
- level: ["beginner", "intermediate", "advanced"]
6035
- },
6036
- "midnight:compare-approaches": {
6037
- approaches: [
6038
- "token-standards",
6039
- "state-management",
6040
- "privacy-patterns",
6041
- "circuit-design"
6042
- ]
6043
- },
6044
- "midnight:debug-contract": {
6045
- errorType: [
6046
- "compilation",
6047
- "runtime",
6048
- "logic",
6049
- "privacy-leak",
6050
- "state-corruption"
6051
- ]
5655
+ async function requestCompletion(messages, options = {}) {
5656
+ if (!samplingCallback || samplingFailedPermanently) {
5657
+ throw new Error(
5658
+ "Sampling not available - client does not support this capability"
5659
+ );
6052
5660
  }
6053
- };
6054
- function registerCompletionsHandler(server) {
6055
- server.setRequestHandler(CompleteRequestSchema, async (request) => {
6056
- const { ref, argument } = request.params;
6057
- if (ref.type !== "ref/prompt") {
6058
- return { completion: { values: [], hasMore: false } };
5661
+ const request = {
5662
+ messages: messages.map((m) => ({
5663
+ role: m.role,
5664
+ content: { type: "text", text: m.content }
5665
+ })),
5666
+ systemPrompt: options.systemPrompt,
5667
+ maxTokens: options.maxTokens ?? 2048,
5668
+ temperature: options.temperature ?? 0.7,
5669
+ modelPreferences: options.modelPreferences ?? {
5670
+ hints: [{ name: "claude-3-sonnet" }, { name: "gpt-4" }],
5671
+ intelligencePriority: 0.8,
5672
+ speedPriority: 0.5
6059
5673
  }
6060
- const promptName = ref.name;
6061
- const argName = argument.name;
6062
- const currentValue = argument.value?.toLowerCase() || "";
6063
- const promptCompletions = COMPLETION_VALUES[promptName];
6064
- if (!promptCompletions) {
6065
- return { completion: { values: [], hasMore: false } };
5674
+ };
5675
+ logger.debug("Requesting LLM completion", {
5676
+ messageCount: messages.length,
5677
+ maxTokens: request.maxTokens
5678
+ });
5679
+ try {
5680
+ const response = await samplingCallback(request);
5681
+ if (response.content.type !== "text") {
5682
+ throw new Error("Unexpected response content type");
6066
5683
  }
6067
- const argValues = promptCompletions[argName];
6068
- if (!argValues) {
6069
- return { completion: { values: [], hasMore: false } };
5684
+ return response.content.text;
5685
+ } catch (error) {
5686
+ const errorStr = String(error);
5687
+ if (errorStr.includes("-32601") || errorStr.includes("Method not found") || errorStr.includes("not supported")) {
5688
+ logger.warn(
5689
+ "Client does not support sampling/createMessage, disabling sampling for this session"
5690
+ );
5691
+ markSamplingFailed();
5692
+ throw new Error(
5693
+ "Sampling not supported by this client - use Claude Desktop for this feature",
5694
+ { cause: error }
5695
+ );
6070
5696
  }
6071
- const filtered = argValues.filter(
6072
- (v) => v.toLowerCase().includes(currentValue)
6073
- );
5697
+ throw error;
5698
+ }
5699
+ }
5700
+ async function generateContract(requirements, options = {}) {
5701
+ if (!isSamplingAvailable()) {
6074
5702
  return {
6075
- completion: {
6076
- values: filtered.slice(0, 20),
6077
- total: filtered.length,
6078
- hasMore: filtered.length > 20
5703
+ code: "",
5704
+ explanation: "Sampling not available - this feature requires a client that supports the sampling capability (like Claude Desktop)",
5705
+ warnings: ["Feature unavailable without sampling support"]
5706
+ };
5707
+ }
5708
+ const systemPrompt = `You are an expert Compact smart contract developer for the Midnight blockchain.
5709
+ Your task is to generate secure, well-documented Compact contracts based on user requirements.
5710
+
5711
+ Key Compact syntax (REQUIRED):
5712
+ - \`export ledger field: Type;\` - Individual ledger declarations (NOT ledger { } blocks)
5713
+ - \`export circuit fn(): []\` - Public functions return empty tuple [] (NOT Void)
5714
+ - \`witness fn(): T;\` - Declaration only, no body
5715
+ - \`pragma language_version >= 0.16 && <= 0.23;\` - Version pragma
5716
+ - \`import CompactStandardLibrary;\` - Standard imports
5717
+ - \`Counter\`, \`Map<K,V>\`, \`Set<T>\` - Built-in collection types
5718
+ - \`Field\`, \`Boolean\`, \`Uint<N>\`, \`Bytes<N>\` - Primitive types
5719
+ - \`export enum State { a, b }\` - Enums must be exported
5720
+
5721
+ Key operations:
5722
+ - Counter: .read(), .increment(n), .decrement(n)
5723
+ - Map: .lookup(k), .insert(k,v), .remove(k)
5724
+ - Set: .member(v), .insert(v), .remove(v)
5725
+
5726
+ COMPILER INFO (DO NOT guess package names!):
5727
+ - Command: \`compact compile src/contract.compact managed/contract\`
5728
+ - The \`compact\` CLI comes with Midnight toolchain (via create-mn-app or official install)
5729
+ - Output goes to managed/<name>/ directory
5730
+ - DO NOT suggest \`npm install -g @midnight-ntwrk/compact-cli\` or similar - that's incorrect
5731
+
5732
+ IMPORTANT - Style vs. Requirements:
5733
+ - Syntax rules above are REQUIRED for compilation
5734
+ - Style choices (indentation, comment style, line length) are CONVENTIONS, not requirements
5735
+ - Single-line comments (//) are common in examples; block comments (/* */) may also work
5736
+ - The docs don't specify indentation width - use consistent style
5737
+
5738
+ Return ONLY the Compact code, no explanations.`;
5739
+ const userPrompt = options.baseExample ? `Based on this example contract:
5740
+ \`\`\`compact
5741
+ ${options.baseExample}
5742
+ \`\`\`
5743
+
5744
+ Generate a new contract with these requirements:
5745
+ ${requirements}` : `Generate a Compact smart contract with these requirements:
5746
+ ${requirements}
5747
+
5748
+ Contract type: ${options.contractType || "custom"}`;
5749
+ try {
5750
+ const code = await requestCompletion(
5751
+ [{ role: "user", content: userPrompt }],
5752
+ {
5753
+ systemPrompt,
5754
+ maxTokens: 4096,
5755
+ temperature: 0.3,
5756
+ // Lower temperature for code generation
5757
+ modelPreferences: {
5758
+ hints: [{ name: "claude-3-sonnet" }],
5759
+ intelligencePriority: 0.9,
5760
+ speedPriority: 0.3
5761
+ }
5762
+ }
5763
+ );
5764
+ const extractedCode = code.includes("```") ? code.replace(/```compact?\n?/g, "").replace(/```/g, "").trim() : code.trim();
5765
+ const explanation = await requestCompletion(
5766
+ [
5767
+ {
5768
+ role: "user",
5769
+ content: `Briefly explain what this Compact contract does (2-3 sentences):
5770
+ \`\`\`compact
5771
+ ${extractedCode}
5772
+ \`\`\``
5773
+ }
5774
+ ],
5775
+ {
5776
+ systemPrompt: "You are a Compact contract documentation expert. Be concise.",
5777
+ maxTokens: 256,
5778
+ temperature: 0.5
6079
5779
  }
5780
+ );
5781
+ return {
5782
+ code: extractedCode,
5783
+ explanation: explanation.trim(),
5784
+ warnings: []
6080
5785
  };
6081
- });
6082
- }
6083
- function registerToolHandlers(server) {
6084
- server.setRequestHandler(ListToolsRequestSchema, async () => {
6085
- logger.debug("Listing tools");
5786
+ } catch (error) {
5787
+ logger.error("Contract generation failed", { error: String(error) });
6086
5788
  return {
6087
- tools: allTools.map((tool) => ({
6088
- name: tool.name,
6089
- description: tool.description,
6090
- inputSchema: tool.inputSchema,
6091
- // Include output schema if defined
6092
- ...tool.outputSchema && { outputSchema: tool.outputSchema },
6093
- // Include annotations if defined
6094
- ...tool.annotations && { annotations: tool.annotations }
6095
- }))
5789
+ code: "",
5790
+ explanation: `Contract generation failed: ${String(error)}`,
5791
+ warnings: ["Generation failed - check logs for details"]
6096
5792
  };
6097
- });
6098
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
6099
- const { name, arguments: args } = request.params;
6100
- logger.info(`Tool called: ${name}`, { args });
6101
- const startTime = Date.now();
6102
- const tool = allTools.find((t) => t.name === name);
6103
- if (!tool) {
6104
- const availableTools = allTools.map((t) => t.name).slice(0, 5).join(", ");
6105
- trackToolCall(name, false, Date.now() - startTime, CURRENT_VERSION);
6106
- return {
6107
- content: [
6108
- {
6109
- type: "text",
6110
- text: serialize({
6111
- error: `Unknown tool: ${name}`,
6112
- suggestion: `Available tools include: ${availableTools}...`,
6113
- hint: "Use ListTools to see all available tools"
6114
- })
6115
- }
6116
- ],
6117
- isError: true
6118
- };
5793
+ }
5794
+ }
5795
+ async function reviewContract(code) {
5796
+ if (!isSamplingAvailable()) {
5797
+ return {
5798
+ summary: "Code review requires sampling capability",
5799
+ issues: [
5800
+ {
5801
+ severity: "info",
5802
+ message: "This feature requires a client that supports the sampling capability"
5803
+ }
5804
+ ]
5805
+ };
5806
+ }
5807
+ const systemPrompt = `You are a Compact smart contract security auditor.
5808
+ Review the provided contract for:
5809
+ 1. Security vulnerabilities
5810
+ 2. Privacy concerns (improper handling of shielded state)
5811
+ 3. Logic errors
5812
+ 4. Syntax errors (use "error" severity)
5813
+ 5. Performance issues
5814
+
5815
+ IMPORTANT - Distinguish between:
5816
+ - ERRORS: Actual syntax/compilation issues (e.g., using Void instead of [], ledger {} blocks)
5817
+ - WARNINGS: Potential bugs or security issues
5818
+ - INFO: Style suggestions and best practices (these are CONVENTIONS, not requirements)
5819
+
5820
+ Do NOT claim style choices as "violations" - the Compact docs don't specify:
5821
+ - Indentation width (2 vs 4 spaces)
5822
+ - Comment style preferences (//, /* */)
5823
+ - Line length limits
5824
+ - Naming conventions
5825
+
5826
+ These are project-specific style choices, not language requirements.
5827
+
5828
+ Respond in JSON format:
5829
+ {
5830
+ "summary": "Brief summary of the contract and overall quality",
5831
+ "issues": [
5832
+ {
5833
+ "severity": "error|warning|info",
5834
+ "line": optional_line_number,
5835
+ "message": "Description of the issue",
5836
+ "suggestion": "How to fix it"
6119
5837
  }
6120
- try {
6121
- const result = await tool.handler(args);
6122
- const durationMs = Date.now() - startTime;
6123
- trackToolCall(name, true, durationMs, CURRENT_VERSION);
6124
- maybeCheckForUpdates();
6125
- const updateWarning = getUpdateWarning();
6126
- if (updateWarning && versionCheckResult.isOutdated) {
6127
- const updatePrompt = {
6128
- "\u{1F6A8} UPDATE REQUIRED": {
6129
- current: `v${CURRENT_VERSION}`,
6130
- latest: `v${versionCheckResult.latestVersion}`,
6131
- quickFix: {
6132
- description: "Most users just need these 2 steps:",
6133
- step1: "Run in terminal: rm -rf ~/.npm/_npx",
6134
- step2: "Restart editor completely (Cmd+Q / Alt+F4, then reopen)"
6135
- },
6136
- ifStillOld: {
6137
- description: "If still showing old version after restart:",
6138
- action: "Edit your MCP config file to use @latest:",
6139
- change: 'Change "midnight-mcp" to "midnight-mcp@latest" in args',
6140
- configFiles: {
6141
- "Claude Desktop (Mac)": "~/Library/Application Support/Claude/claude_desktop_config.json",
6142
- "Claude Desktop (Win)": "%APPDATA%/Claude/claude_desktop_config.json",
6143
- Cursor: ".cursor/mcp.json",
6144
- "VS Code": ".vscode/mcp.json",
6145
- Windsurf: "~/.codeium/windsurf/mcp_config.json"
6146
- }
6147
- },
6148
- tip: "Use 'midnight-get-update-instructions' tool for detailed platform-specific steps."
6149
- },
6150
- result
5838
+ ],
5839
+ "improvedCode": "Full improved contract code if changes are needed"
5840
+ }`;
5841
+ try {
5842
+ const response = await requestCompletion(
5843
+ [
5844
+ {
5845
+ role: "user",
5846
+ content: `Review this Compact contract:
5847
+ \`\`\`compact
5848
+ ${code}
5849
+ \`\`\``
5850
+ }
5851
+ ],
5852
+ {
5853
+ systemPrompt,
5854
+ maxTokens: 4096,
5855
+ temperature: 0.2
5856
+ }
5857
+ );
5858
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
5859
+ if (jsonMatch) {
5860
+ try {
5861
+ const parsed = JSON.parse(jsonMatch[0]);
5862
+ return {
5863
+ summary: typeof parsed.summary === "string" ? parsed.summary : "Review complete",
5864
+ issues: Array.isArray(parsed.issues) ? parsed.issues : [],
5865
+ improvedCode: typeof parsed.improvedCode === "string" ? parsed.improvedCode : void 0
6151
5866
  };
5867
+ } catch (parseError) {
5868
+ logger.warn("Failed to parse JSON from LLM response", {
5869
+ error: String(parseError)
5870
+ });
6152
5871
  return {
6153
- content: [
6154
- {
6155
- type: "text",
6156
- text: serialize(updatePrompt)
6157
- }
6158
- ]
5872
+ summary: response,
5873
+ issues: []
6159
5874
  };
6160
5875
  }
6161
- return {
6162
- content: [
6163
- {
6164
- type: "text",
6165
- text: serialize(result)
6166
- }
6167
- ],
6168
- // Include structured content for machine-readable responses
6169
- // This allows clients to parse results without JSON.parse()
6170
- structuredContent: result
6171
- };
6172
- } catch (error) {
6173
- const durationMs = Date.now() - startTime;
6174
- logger.error(`Tool error: ${name}`, { error: String(error) });
6175
- trackToolCall(name, false, durationMs, CURRENT_VERSION);
6176
- const errorResponse = formatErrorResponse(error, `tool:${name}`);
6177
- return {
6178
- content: [
6179
- {
6180
- type: "text",
6181
- text: serialize(errorResponse)
6182
- }
6183
- ],
6184
- isError: true
6185
- };
6186
5876
  }
6187
- });
6188
- }
6189
- function registerResourceHandlers(server) {
6190
- server.setRequestHandler(ListResourcesRequestSchema, async () => {
6191
- logger.debug("Listing resources");
6192
5877
  return {
6193
- resources: allResources.map((resource) => ({
6194
- uri: resource.uri,
6195
- name: resource.name,
6196
- description: resource.description,
6197
- mimeType: resource.mimeType
6198
- }))
5878
+ summary: response,
5879
+ issues: []
6199
5880
  };
6200
- });
6201
- server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
6202
- logger.debug("Listing resource templates");
5881
+ } catch (error) {
5882
+ logger.error("Contract review failed", { error: String(error) });
6203
5883
  return {
6204
- resourceTemplates: resourceTemplates.map((template) => ({
6205
- uriTemplate: template.uriTemplate,
6206
- name: template.name,
6207
- title: template.title,
6208
- description: template.description,
6209
- mimeType: template.mimeType
6210
- }))
5884
+ summary: `Review failed: ${String(error)}`,
5885
+ issues: [
5886
+ {
5887
+ severity: "error",
5888
+ message: "Review failed - check logs for details"
5889
+ }
5890
+ ]
6211
5891
  };
6212
- });
6213
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
6214
- const { uri } = request.params;
6215
- logger.info(`Resource requested: ${uri}`);
6216
- try {
6217
- let content = null;
6218
- let mimeType = "text/plain";
6219
- if (uri.startsWith("midnight://docs/")) {
6220
- content = await getDocumentation(uri);
6221
- mimeType = "text/markdown";
6222
- } else if (uri.startsWith("midnight://code/")) {
6223
- content = await getCode(uri);
6224
- mimeType = "text/x-compact";
6225
- } else if (uri.startsWith("midnight://schema/")) {
6226
- const schema = getSchema(uri);
6227
- content = schema ? JSON.stringify(schema, null, 2) : null;
6228
- mimeType = "application/json";
6229
- }
6230
- if (!content) {
6231
- const resourceTypes = [
6232
- "midnight://docs/",
6233
- "midnight://code/",
6234
- "midnight://schema/"
6235
- ];
6236
- const validPrefix = resourceTypes.find((p) => uri.startsWith(p));
6237
- let suggestion = validPrefix ? `Check the resource path after '${validPrefix}'` : `Valid resource prefixes: ${resourceTypes.join(", ")}`;
6238
- if (uri.includes("://resources/")) {
6239
- const resourceName = uri.split("://resources/").pop() || "";
6240
- if (resourceName.includes("template") || resourceName.includes("pattern") || resourceName.includes("example")) {
6241
- suggestion = `Try: midnight://code/templates/${resourceName} or midnight://code/examples/${resourceName} or midnight://code/patterns/${resourceName}`;
6242
- } else if (resourceName.includes("doc") || resourceName.includes("reference") || resourceName.includes("guide")) {
6243
- suggestion = `Try: midnight://docs/${resourceName}`;
6244
- } else {
6245
- suggestion = `'midnight://resources/' is not valid. Use: midnight://docs/, midnight://code/, or midnight://schema/`;
6246
- }
5892
+ }
5893
+ }
5894
+ async function generateDocumentation(code, format = "markdown") {
5895
+ if (!isSamplingAvailable()) {
5896
+ return "Documentation generation requires sampling capability";
5897
+ }
5898
+ const systemPrompt = format === "markdown" ? `Generate comprehensive Markdown documentation for this Compact contract.
5899
+ Include:
5900
+ - Overview and purpose
5901
+ - State variables (with privacy annotations)
5902
+ - Circuit functions with parameters and effects
5903
+ - Witness functions
5904
+ - Usage examples
5905
+ - Security considerations` : `Generate JSDoc-style documentation comments for this Compact contract.
5906
+ Add documentation comments above each:
5907
+ - Ledger field
5908
+ - Circuit function
5909
+ - Witness function
5910
+ - Type definition`;
5911
+ try {
5912
+ return await requestCompletion(
5913
+ [
5914
+ {
5915
+ role: "user",
5916
+ content: `Generate ${format} documentation for:
5917
+ \`\`\`compact
5918
+ ${code}
5919
+ \`\`\``
6247
5920
  }
6248
- return {
6249
- contents: [
6250
- {
6251
- uri,
6252
- mimeType: "application/json",
6253
- text: serialize({
6254
- error: `Resource not found: ${uri}`,
6255
- suggestion,
6256
- hint: "Use ListResources to see all available resources",
6257
- validPrefixes: resourceTypes
6258
- })
6259
- }
6260
- ]
6261
- };
5921
+ ],
5922
+ {
5923
+ systemPrompt,
5924
+ maxTokens: 4096,
5925
+ temperature: 0.5
5926
+ }
5927
+ );
5928
+ } catch (error) {
5929
+ logger.error("Documentation generation failed", { error: String(error) });
5930
+ return `Documentation generation failed: ${String(error)}`;
5931
+ }
5932
+ }
5933
+
5934
+ // src/services/syntax-validator.ts
5935
+ var ADT_SEARCH_LIMIT = 10;
5936
+ var SYNTAX_RESULTS_LIMIT = 10;
5937
+ var SYNTAX_SUBQUERY_LIMIT = 5;
5938
+ var MAX_NOTE_SOURCE_LENGTH = 500;
5939
+ var NOTE_MIN_LENGTH = 20;
5940
+ var NOTE_MAX_LENGTH = 300;
5941
+ var MAX_NOTES_PER_ADT = 3;
5942
+ async function searchADTInfo(adtName) {
5943
+ try {
5944
+ const docsResult = await searchDocsHosted(
5945
+ `${adtName} ADT operations methods ledger`,
5946
+ ADT_SEARCH_LIMIT,
5947
+ "reference"
5948
+ );
5949
+ if (!docsResult.results || docsResult.results.length === 0) {
5950
+ logger.debug(`No indexed docs found for ADT: ${adtName}`);
5951
+ return null;
5952
+ }
5953
+ const operations = [];
5954
+ const notes = [];
5955
+ let sourceDocPath;
5956
+ for (const result of docsResult.results) {
5957
+ const content = result.content || result.code || "";
5958
+ const isLedgerADTDoc = result.source.filePath.includes("ledger-adt");
5959
+ if (!sourceDocPath && isLedgerADTDoc) {
5960
+ sourceDocPath = result.source.filePath;
6262
5961
  }
6263
- return {
6264
- contents: [
6265
- {
6266
- uri,
6267
- mimeType,
6268
- text: content
5962
+ if (isLedgerADTDoc) {
5963
+ const methodPattern = /\|\s*`?(\w+)`?\s*\|\s*`?\(([^)]*)\)\s*(?::\s*([^|`]+))?`?\s*\|\s*([^|]+)\|/g;
5964
+ let match;
5965
+ while ((match = methodPattern.exec(content)) !== null) {
5966
+ const [, methodName, params, returnType, description] = match;
5967
+ if (methodName && !operations.some((op) => op.method === methodName)) {
5968
+ const descText = description?.trim() || "";
5969
+ const worksInCircuits = !/not available|not supported|not in circuits|typescript|sdk|off[-\s]?chain/i.test(
5970
+ descText
5971
+ );
5972
+ operations.push({
5973
+ method: methodName,
5974
+ signature: `(${params})${returnType ? `: ${returnType.trim()}` : ""}`,
5975
+ description: descText,
5976
+ worksInCircuits,
5977
+ source: "indexed-docs"
5978
+ });
6269
5979
  }
6270
- ]
6271
- };
6272
- } catch (error) {
6273
- logger.error(`Resource error: ${uri}`, { error: String(error) });
6274
- const errorResponse = formatErrorResponse(error, `resource:${uri}`);
6275
- return {
6276
- contents: [
6277
- {
6278
- uri,
6279
- mimeType: "application/json",
6280
- text: serialize(errorResponse)
5980
+ }
5981
+ const inlinePattern = /(?:method|operation|function)\s+`?(\w+)`?\s*\(([^)]*)\)/gi;
5982
+ while ((match = inlinePattern.exec(content)) !== null) {
5983
+ const [, methodName, params] = match;
5984
+ if (methodName && !operations.some((op) => op.method === methodName)) {
5985
+ const worksInCircuits = !/not available|not supported|not in circuits|typescript|sdk|off[-\s]?chain/i.test(
5986
+ content
5987
+ );
5988
+ operations.push({
5989
+ method: methodName,
5990
+ signature: `(${params})`,
5991
+ description: "Extracted from documentation",
5992
+ worksInCircuits,
5993
+ source: "indexed-docs"
5994
+ });
6281
5995
  }
6282
- ]
6283
- };
5996
+ }
5997
+ }
5998
+ if (content.toLowerCase().includes(adtName.toLowerCase()) && content.length < MAX_NOTE_SOURCE_LENGTH) {
5999
+ const cleanNote = content.replace(/\s+/g, " ").trim();
6000
+ if (cleanNote.length > NOTE_MIN_LENGTH && cleanNote.length < NOTE_MAX_LENGTH) {
6001
+ notes.push(cleanNote);
6002
+ }
6003
+ }
6284
6004
  }
6285
- });
6286
- }
6287
- function registerPromptHandlers(server) {
6288
- server.setRequestHandler(ListPromptsRequestSchema, async () => {
6289
- logger.debug("Listing prompts");
6290
- return {
6291
- prompts: promptDefinitions.map((prompt) => ({
6292
- name: prompt.name,
6293
- description: prompt.description,
6294
- arguments: prompt.arguments
6295
- }))
6296
- };
6297
- });
6298
- server.setRequestHandler(GetPromptRequestSchema, async (request) => {
6299
- const { name, arguments: args } = request.params;
6300
- logger.info(`Prompt requested: ${name}`, { args });
6301
- const prompt = promptDefinitions.find((p) => p.name === name);
6302
- if (!prompt) {
6303
- return {
6304
- description: `Unknown prompt: ${name}`,
6305
- messages: []
6306
- };
6005
+ if (operations.length === 0) {
6006
+ logger.debug(`Could not parse operations for ADT: ${adtName}`);
6007
+ return null;
6307
6008
  }
6308
- const messages = generatePrompt(name, args || {});
6309
6009
  return {
6310
- description: prompt.description,
6311
- messages: messages.map((m) => ({
6312
- role: m.role,
6313
- content: m.content
6314
- }))
6010
+ name: adtName,
6011
+ operations,
6012
+ notes: notes.slice(0, MAX_NOTES_PER_ADT),
6013
+ sourceDocPath,
6014
+ lastVerified: (/* @__PURE__ */ new Date()).toISOString()
6315
6015
  };
6316
- });
6016
+ } catch (error) {
6017
+ logger.warn(`Failed to search indexed docs for ADT ${adtName}`, {
6018
+ error: error instanceof Error ? error.message : String(error)
6019
+ });
6020
+ return null;
6021
+ }
6317
6022
  }
6318
- function registerSubscriptionHandlers(server) {
6319
- server.setRequestHandler(SubscribeRequestSchema, async (request) => {
6320
- const { uri } = request.params;
6321
- logger.info(`Subscribing to resource: ${uri}`);
6322
- const validPrefixes = [
6323
- "midnight://docs/",
6324
- "midnight://code/",
6325
- "midnight://schema/"
6023
+ async function searchCompactSyntax(topic) {
6024
+ try {
6025
+ const [docsResult, codeResult] = await Promise.all([
6026
+ searchDocsHosted(
6027
+ `Compact ${topic} syntax`,
6028
+ SYNTAX_SUBQUERY_LIMIT,
6029
+ "reference"
6030
+ ),
6031
+ searchCompactHosted(`${topic}`, SYNTAX_SUBQUERY_LIMIT)
6032
+ ]);
6033
+ const mergedResults = [
6034
+ ...docsResult.results || [],
6035
+ ...codeResult.results || []
6326
6036
  ];
6327
- const isValid = validPrefixes.some((prefix) => uri.startsWith(prefix));
6328
- if (!isValid) {
6329
- logger.warn(`Invalid subscription URI: ${uri}`);
6330
- throw new Error(
6331
- `Invalid subscription URI: ${uri}. Valid prefixes: ${validPrefixes.join(", ")}`
6037
+ return {
6038
+ results: mergedResults.slice(0, SYNTAX_RESULTS_LIMIT),
6039
+ totalResults: mergedResults.length,
6040
+ query: topic,
6041
+ lastIndexed: docsResult.lastIndexed || codeResult.lastIndexed
6042
+ };
6043
+ } catch (error) {
6044
+ logger.warn(`Failed to search Compact syntax for topic ${topic}`, {
6045
+ error: error instanceof Error ? error.message : String(error)
6046
+ });
6047
+ return null;
6048
+ }
6049
+ }
6050
+ async function validateADTOperations(adtName, staticOperations) {
6051
+ const discrepancies = [];
6052
+ const enrichments = [];
6053
+ const indexedInfo = await searchADTInfo(adtName);
6054
+ if (!indexedInfo) {
6055
+ return {
6056
+ validated: false,
6057
+ operations: staticOperations.map((op) => ({
6058
+ method: op.method,
6059
+ description: op.note,
6060
+ worksInCircuits: op.works,
6061
+ source: "static-fallback"
6062
+ })),
6063
+ discrepancies: [
6064
+ "Could not validate against indexed docs - using static data"
6065
+ ],
6066
+ enrichments: []
6067
+ };
6068
+ }
6069
+ const operations = [];
6070
+ const indexedMethodNames = new Set(
6071
+ indexedInfo.operations.map((op) => op.method.toLowerCase())
6072
+ );
6073
+ const staticMethodNames = new Set(
6074
+ staticOperations.map(
6075
+ (op) => op.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase()
6076
+ )
6077
+ );
6078
+ for (const indexedOp of indexedInfo.operations) {
6079
+ operations.push(indexedOp);
6080
+ const staticOp = staticOperations.find(
6081
+ (s) => s.method.toLowerCase().includes(indexedOp.method.toLowerCase()) || indexedOp.method.toLowerCase().includes(
6082
+ s.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase()
6083
+ )
6084
+ );
6085
+ if (staticOp && !staticOp.works && indexedOp.worksInCircuits) {
6086
+ discrepancies.push(
6087
+ `Static said ${staticOp.method} doesn't work, but indexed docs show it does`
6332
6088
  );
6333
6089
  }
6334
- resourceSubscriptions.add(uri);
6335
- logger.debug(`Active subscriptions: ${resourceSubscriptions.size}`);
6336
- return {};
6337
- });
6338
- server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
6339
- const { uri } = request.params;
6340
- logger.info(`Unsubscribing from resource: ${uri}`);
6341
- resourceSubscriptions.delete(uri);
6342
- logger.debug(`Active subscriptions: ${resourceSubscriptions.size}`);
6343
- return {};
6344
- });
6345
- }
6346
- function setupSampling(server) {
6347
- const samplingCallback2 = async (request) => {
6348
- logger.debug("Requesting sampling from client", {
6349
- messageCount: request.messages.length,
6350
- maxTokens: request.maxTokens
6351
- });
6352
- try {
6353
- const response = await server.request(
6354
- {
6355
- method: "sampling/createMessage",
6356
- params: {
6357
- messages: request.messages,
6358
- systemPrompt: request.systemPrompt,
6359
- maxTokens: request.maxTokens || 2048,
6360
- temperature: request.temperature,
6361
- modelPreferences: request.modelPreferences
6362
- }
6363
- },
6364
- // Use a schema that matches the expected response
6365
- {
6366
- parse: (data) => {
6367
- const response2 = data;
6368
- if (!response2 || typeof response2 !== "object") {
6369
- throw new Error("Invalid sampling response: expected object");
6370
- }
6371
- if (!response2.content || typeof response2.content !== "object") {
6372
- throw new Error("Invalid sampling response: missing content");
6373
- }
6374
- return response2;
6375
- },
6376
- _def: { typeName: "SamplingResponse" }
6377
- }
6090
+ }
6091
+ for (const staticOp of staticOperations) {
6092
+ const methodName = staticOp.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase();
6093
+ if (!indexedMethodNames.has(methodName)) {
6094
+ discrepancies.push(
6095
+ `Static references ${staticOp.method} but not found in indexed docs - may not exist`
6378
6096
  );
6379
- return response;
6380
- } catch (error) {
6381
- logger.error("Sampling request failed", { error: String(error) });
6382
- throw error;
6383
6097
  }
6384
- };
6385
- registerSamplingCallback(samplingCallback2);
6386
- logger.info("Sampling capability configured");
6387
- }
6388
- async function initializeServer() {
6389
- logger.info("Initializing Midnight MCP Server...");
6390
- checkForUpdates().catch((error) => {
6391
- logger.debug("Startup version check failed (non-blocking)", {
6392
- error: error instanceof Error ? error.message : String(error)
6393
- });
6394
- });
6395
- try {
6396
- await vectorStore.initialize();
6397
- logger.info("Vector store initialized");
6398
- } catch (error) {
6399
- logger.warn("Vector store initialization failed, continuing without it", {
6400
- error: String(error)
6401
- });
6402
6098
  }
6403
- const server = createServer();
6404
- logger.info(`Server v${CURRENT_VERSION} created successfully`);
6405
- return server;
6406
- }
6407
- async function startServer() {
6408
- const server = await initializeServer();
6409
- const transport = new StdioServerTransport();
6410
- await server.connect(transport);
6411
- setServerConnected(true);
6412
- logger.info("Midnight MCP Server running on stdio");
6099
+ for (const indexedOp of indexedInfo.operations) {
6100
+ const methodName = indexedOp.method.toLowerCase();
6101
+ if (!staticMethodNames.has(methodName)) {
6102
+ enrichments.push(
6103
+ `Indexed docs show ${indexedOp.method} exists but was missing from static reference`
6104
+ );
6105
+ }
6106
+ }
6107
+ return {
6108
+ validated: true,
6109
+ operations,
6110
+ discrepancies,
6111
+ enrichments
6112
+ };
6413
6113
  }
6414
- var transports = {
6415
- streamable: {},
6416
- sse: {}
6417
- };
6418
- async function closeTransports(transportMap) {
6419
- const closePromises = Object.values(transportMap).map(
6420
- (transport) => transport.close?.().catch(() => {
6114
+ async function validateBuiltinFunctions(staticBuiltins) {
6115
+ const discrepancies = [];
6116
+ const enrichments = [];
6117
+ const results = await Promise.all(
6118
+ staticBuiltins.map(async (builtin) => {
6119
+ const searchResult = await searchCompactSyntax(
6120
+ `${builtin.name} function builtin`
6121
+ );
6122
+ return { builtin, found: (searchResult?.results?.length || 0) > 0 };
6421
6123
  })
6422
6124
  );
6423
- await Promise.all(closePromises);
6424
- }
6425
- async function startHttpServer(port = 3e3) {
6426
- const mcpServer = await initializeServer();
6427
- const app = express();
6428
- app.use("/mcp", express.json());
6429
- app.get("/health", (_req, res) => {
6430
- res.json({
6431
- status: "ok",
6432
- version: CURRENT_VERSION,
6433
- transport: "http"
6434
- });
6435
- });
6436
- app.post("/mcp", async (req, res) => {
6437
- const sessionId = req.headers["mcp-session-id"];
6438
- let transport;
6439
- if (sessionId && transports.streamable[sessionId]) {
6440
- transport = transports.streamable[sessionId];
6441
- } else if (!sessionId && isInitializeRequest(req.body)) {
6442
- transport = new StreamableHTTPServerTransport({
6443
- sessionIdGenerator: () => randomUUID(),
6444
- onsessioninitialized: (newSessionId) => {
6445
- transports.streamable[newSessionId] = transport;
6446
- logger.debug(`New streamable session: ${newSessionId}`);
6447
- }
6448
- });
6449
- transport.onclose = () => {
6450
- if (transport.sessionId) {
6451
- delete transports.streamable[transport.sessionId];
6452
- logger.debug(`Streamable session closed: ${transport.sessionId}`);
6125
+ for (const { builtin, found } of results) {
6126
+ if (!found) {
6127
+ discrepancies.push(
6128
+ `Builtin "${builtin.name}" not found in indexed docs - may not exist or have different name`
6129
+ );
6130
+ }
6131
+ }
6132
+ const allBuiltinsSearch = await searchCompactSyntax(
6133
+ "builtin function stdlib standard library"
6134
+ );
6135
+ if (allBuiltinsSearch?.results) {
6136
+ const knownNames = new Set(staticBuiltins.map((b) => b.name.toLowerCase()));
6137
+ for (const result of allBuiltinsSearch.results) {
6138
+ const content = result.content || result.code || "";
6139
+ const funcPattern = /\b(export\s+)?function\s+(\w+)/gi;
6140
+ let match;
6141
+ while ((match = funcPattern.exec(content)) !== null) {
6142
+ const funcName = match[2].toLowerCase();
6143
+ if (!knownNames.has(funcName) && funcName.length > 2) {
6144
+ enrichments.push(`Indexed docs mention "${match[2]}" function`);
6453
6145
  }
6454
- };
6455
- try {
6456
- await mcpServer.connect(transport);
6457
- } catch (error) {
6458
- logger.error("Failed to connect streamable transport", {
6459
- error: String(error)
6460
- });
6461
- res.status(500).json({
6462
- jsonrpc: "2.0",
6463
- error: { code: -32603, message: "Internal error: connection failed" },
6464
- id: null
6465
- });
6466
- return;
6467
6146
  }
6468
- } else {
6469
- res.status(400).json({
6470
- jsonrpc: "2.0",
6471
- error: {
6472
- code: -32e3,
6473
- message: "Bad Request: No valid session ID provided"
6474
- },
6475
- id: null
6476
- });
6477
- return;
6478
- }
6479
- await transport.handleRequest(req, res, req.body);
6480
- });
6481
- app.get("/sse", async (_req, res) => {
6482
- logger.debug("New SSE connection");
6483
- const transport = new SSEServerTransport("/messages", res);
6484
- transports.sse[transport.sessionId] = transport;
6485
- res.on("close", () => {
6486
- delete transports.sse[transport.sessionId];
6487
- logger.debug(`SSE session closed: ${transport.sessionId}`);
6488
- });
6489
- try {
6490
- await mcpServer.connect(transport);
6491
- } catch (error) {
6492
- delete transports.sse[transport.sessionId];
6493
- logger.error(`SSE connection failed: ${transport.sessionId}`, {
6494
- error: String(error)
6495
- });
6496
- res.status(500).end();
6497
- }
6498
- });
6499
- app.post("/messages", async (req, res) => {
6500
- const sessionId = req.query.sessionId;
6501
- const transport = transports.sse[sessionId];
6502
- if (transport) {
6503
- await transport.handlePostMessage(req, res);
6504
- } else {
6505
- res.status(400).send(`No transport found for sessionId ${sessionId}`);
6506
6147
  }
6507
- });
6508
- const handleSessionRequest = async (req, res) => {
6509
- const sessionId = req.headers["mcp-session-id"];
6510
- if (!sessionId || !transports.streamable[sessionId]) {
6511
- res.status(400).send("Invalid or missing session ID");
6512
- return;
6148
+ }
6149
+ return {
6150
+ dataType: "BUILTIN_FUNCTIONS",
6151
+ validated: discrepancies.length === 0,
6152
+ discrepancies,
6153
+ enrichments: [...new Set(enrichments)].slice(0, 10),
6154
+ deprecatedPatterns: [],
6155
+ lastValidated: (/* @__PURE__ */ new Date()).toISOString()
6156
+ };
6157
+ }
6158
+ async function validateTypeCompatibility(staticRules) {
6159
+ const discrepancies = [];
6160
+ const enrichments = [];
6161
+ const typeSearch = await searchCompactSyntax(
6162
+ "type cast Field Uint Bytes conversion compatible"
6163
+ );
6164
+ if (typeSearch?.results) {
6165
+ for (const result of typeSearch.results) {
6166
+ const content = (result.content || result.code || "").toLowerCase();
6167
+ for (const rule of staticRules) {
6168
+ const typeMatch = rule.types.match(
6169
+ /^\s*(.+?)\s*(?:==|=|<=|>=|<|>|[+*])\s*(.+?)\s*$/
6170
+ );
6171
+ const typeA = typeMatch?.[1]?.trim().toLowerCase();
6172
+ const typeB = typeMatch?.[2]?.trim().toLowerCase();
6173
+ if (typeA && typeB) {
6174
+ if (content.includes(typeA) && content.includes(typeB) && content.includes("compatible")) {
6175
+ if (!rule.works) {
6176
+ discrepancies.push(
6177
+ `Static says "${rule.types}" doesn't work, but docs suggest compatibility`
6178
+ );
6179
+ }
6180
+ }
6181
+ }
6182
+ }
6513
6183
  }
6514
- const transport = transports.streamable[sessionId];
6515
- await transport.handleRequest(req, res);
6184
+ }
6185
+ return {
6186
+ dataType: "TYPE_COMPATIBILITY",
6187
+ validated: discrepancies.length === 0,
6188
+ discrepancies,
6189
+ enrichments,
6190
+ deprecatedPatterns: [],
6191
+ lastValidated: (/* @__PURE__ */ new Date()).toISOString()
6516
6192
  };
6517
- app.get("/mcp", handleSessionRequest);
6518
- app.delete("/mcp", handleSessionRequest);
6519
- const httpServer = app.listen(port, "127.0.0.1", () => {
6520
- logger.info(`Midnight MCP Server running on HTTP port ${port}`);
6521
- logger.info(` Streamable HTTP: http://localhost:${port}/mcp`);
6522
- logger.info(` SSE endpoint: http://localhost:${port}/sse`);
6523
- logger.info(` Health check: http://localhost:${port}/health`);
6524
- });
6525
- const shutdown = async () => {
6526
- logger.info("Shutting down HTTP server...");
6527
- await closeTransports(transports.sse);
6528
- await closeTransports(transports.streamable);
6529
- httpServer.close(() => {
6530
- logger.info("Server shutdown complete");
6531
- process.exit(0);
6532
- });
6193
+ }
6194
+ async function validateCommonErrors(staticErrors) {
6195
+ const discrepancies = [];
6196
+ const enrichments = [];
6197
+ const errorSearch = await searchDocsHosted(
6198
+ "error compile compilation failure parse",
6199
+ ADT_SEARCH_LIMIT,
6200
+ "all"
6201
+ );
6202
+ if (errorSearch?.results) {
6203
+ const knownErrors = new Set(
6204
+ staticErrors.map((e) => e.error.toLowerCase().slice(0, 30))
6205
+ );
6206
+ for (const result of errorSearch.results) {
6207
+ const content = result.content || result.code || "";
6208
+ const errorPattern = /(?:error|Error):\s*["']?([^"'\n]+)["']?/g;
6209
+ let match;
6210
+ while ((match = errorPattern.exec(content)) !== null) {
6211
+ const errorMsg = match[1].toLowerCase().slice(0, 30);
6212
+ if (!knownErrors.has(errorMsg) && errorMsg.length > 10) {
6213
+ enrichments.push(`Docs mention error: "${match[1].slice(0, 50)}..."`);
6214
+ }
6215
+ }
6216
+ }
6217
+ }
6218
+ return {
6219
+ dataType: "COMMON_ERRORS",
6220
+ validated: true,
6221
+ // Errors are informational, hard to validate
6222
+ discrepancies,
6223
+ enrichments: [...new Set(enrichments)].slice(0, 10),
6224
+ deprecatedPatterns: [],
6225
+ lastValidated: (/* @__PURE__ */ new Date()).toISOString()
6533
6226
  };
6534
- process.on("SIGINT", shutdown);
6535
- process.on("SIGTERM", shutdown);
6536
6227
  }
6537
-
6538
- // src/config/compact-version.ts
6539
- var COMPACT_VERSION = {
6540
- /** Minimum supported version */
6541
- min: "0.16",
6542
- /** Maximum supported version */
6543
- max: "0.21",
6544
- /** When this config was last updated */
6545
- lastUpdated: "2026-03-24",
6546
- /** Source of truth for syntax patterns */
6547
- referenceSource: "https://github.com/piotr-iohk/template-contract"
6548
- };
6549
- var RECOMMENDED_PRAGMA = `pragma language_version >= ${COMPACT_VERSION.min} && <= ${COMPACT_VERSION.max};`;
6550
- var REFERENCE_CONTRACTS = [
6228
+ var DEPRECATED_SYNTAX_PATTERNS = [
6551
6229
  {
6552
- name: "template-contract",
6553
- repo: "piotr-iohk/template-contract",
6554
- description: "Official Midnight template contract"
6230
+ pattern: /ledger\s*\{/,
6231
+ name: "ledger-block",
6232
+ message: "Deprecated: Use 'export ledger field: Type;' instead of ledger { } block",
6233
+ since: "0.16"
6555
6234
  },
6556
6235
  {
6557
- name: "tokenomics-project",
6558
- repo: "piotr-iohk/tokenomics-project",
6559
- description: "Token implementation example"
6236
+ pattern: /Cell\s*<\s*\w+\s*>/,
6237
+ name: "cell-wrapper",
6238
+ message: "Deprecated: Cell<T> wrapper removed, use Type directly",
6239
+ since: "0.15"
6560
6240
  },
6561
6241
  {
6562
- name: "zswap-example",
6563
- repo: "piotr-iohk/zswap-example",
6564
- description: "Privacy-preserving swap example"
6242
+ pattern: /:\s*Void\b/,
6243
+ name: "void-type",
6244
+ message: "Void type doesn't exist, use [] (empty tuple) for no return",
6245
+ since: "always"
6565
6246
  },
6566
6247
  {
6567
- name: "reentrancy-example",
6568
- repo: "piotr-iohk/reentrancy-example",
6569
- description: "Cross-contract call patterns"
6248
+ pattern: /\.\s*value\s*\(\s*\)/,
6249
+ name: "counter-value",
6250
+ message: "Counter.value() doesn't exist, use Counter.read() instead",
6251
+ since: "always"
6252
+ },
6253
+ {
6254
+ pattern: /::\w+/,
6255
+ name: "rust-enum-syntax",
6256
+ message: "Rust-style :: enum access doesn't work, use dot notation (Choice.rock)",
6257
+ since: "always"
6258
+ },
6259
+ {
6260
+ pattern: /pure\s+function\b/,
6261
+ name: "pure-function",
6262
+ message: "Use 'pure circuit' not 'pure function' for helper functions",
6263
+ since: "0.16"
6264
+ },
6265
+ {
6266
+ pattern: /witness\s+\w+\s*\([^)]*\)\s*:\s*\w+\s*\{/,
6267
+ name: "witness-with-body",
6268
+ message: "Witnesses are declarations only - no body allowed. Implementation goes in TypeScript prover.",
6269
+ since: "always"
6570
6270
  }
6571
6271
  ];
6572
- var BUILTIN_FUNCTIONS = {
6573
- /** Actually built into the language/stdlib */
6574
- stdlib: [
6575
- {
6576
- name: "persistentHash",
6577
- signature: "persistentHash<T>(value: T): Bytes<32>",
6578
- // Signature inferred from examples
6579
- description: "Poseidon hash that produces consistent results across calls"
6580
- },
6581
- {
6582
- name: "persistentCommit",
6583
- signature: "persistentCommit<T>(value: T): Bytes<32>",
6584
- description: "Creates a hiding commitment to a value"
6585
- },
6586
- {
6587
- name: "pad",
6588
- signature: "pad(length: number, value: string): Bytes<N>",
6589
- description: "Pads a string to fixed-length bytes"
6590
- },
6591
- {
6592
- name: "disclose",
6593
- signature: "disclose(value: T): T",
6594
- description: "Explicitly reveals a witness value (required in conditionals)"
6595
- },
6596
- {
6597
- name: "assert",
6598
- signature: "assert(condition: Boolean, message?: string): []",
6599
- description: "Fails circuit if condition is false"
6600
- },
6601
- {
6602
- name: "default",
6603
- signature: "default<T>(): T",
6604
- description: "Returns default value for a type (0 for numbers, empty for collections)"
6605
- }
6606
- ],
6607
- /** NOT built-in - you must implement these patterns yourself */
6608
- notBuiltIn: [
6609
- {
6610
- name: "public_key",
6611
- wrongUsage: "public_key(sk) // ERROR: unbound identifier",
6612
- correctPattern: `// Derive public key using persistentHash
6613
- const pk = persistentHash<Vector<2, Bytes<32>>>([
6614
- pad(32, "midnight:pk:"),
6615
- sk
6616
- ]);`,
6617
- description: "Public key derivation is NOT a builtin - use persistentHash pattern"
6618
- },
6619
- {
6620
- name: "verify_signature",
6621
- wrongUsage: "verify_signature(msg, sig, pk) // Does not exist",
6622
- correctPattern: `// Signature verification must be done via witnesses
6623
- // The prover verifies off-chain, then provides the boolean result
6624
- witness signature_valid(): Boolean;`,
6625
- description: "Signature verification is done off-chain in the prover"
6626
- },
6627
- {
6628
- name: "random",
6629
- wrongUsage: "random() // Does not exist in ZK circuits",
6630
- correctPattern: `// Randomness must come from witnesses (prover-provided)
6631
- witness get_random_value(): Field;`,
6632
- description: "ZK circuits are deterministic - randomness must come from witnesses"
6633
- }
6634
- ]
6635
- };
6636
- var TYPE_COMPATIBILITY = {
6637
- // Note: Comparison operators work in practice but aren't explicitly listed in docs
6638
- comparisons: [
6639
- { types: "Field == Field", works: true, note: "Direct comparison" },
6640
- {
6641
- types: "Field == Uint<N>",
6642
- works: false,
6643
- fix: "Cast with `value as Field`"
6644
- },
6645
- {
6646
- types: "Field >= 0",
6647
- works: false,
6648
- fix: "Use bounded Uint<0..N> parameter instead"
6649
- },
6650
- { types: "Uint<N> == Uint<N>", works: true, note: "Same-width comparison" },
6651
- {
6652
- types: "Uint<0..2> == Uint<0..2>",
6653
- works: true,
6654
- note: "Bounded integers"
6655
- },
6656
- {
6657
- types: "Bytes<32> == Bytes<32>",
6658
- works: true,
6659
- note: "Used in examples, but operators not explicitly listed in docs"
6660
- },
6661
- { types: "Boolean == Boolean", works: true, note: "Direct comparison" }
6662
- ],
6663
- arithmetic: [
6664
- { types: "Field + Field", works: true, note: "Field arithmetic" },
6665
- { types: "Field + Uint<N>", works: false, fix: "Cast Uint to Field first" },
6666
- {
6667
- types: "Uint<N> + Uint<N>",
6668
- works: true,
6669
- note: "Result is bounded type, cast back: (a + b) as Uint<64>"
6670
- },
6671
- {
6672
- types: "Uint<64> + Uint<64>",
6673
- works: true,
6674
- note: "Result is Uint<0..36893488147419103230>, must cast: (a + b) as Uint<64>"
6675
- },
6676
- {
6677
- types: "Uint<64> * Uint<64>",
6678
- works: true,
6679
- note: "Result is wide bounded type, cast back to target type"
6680
- }
6681
- ],
6682
- /**
6683
- * Type casting rules - based on official cast table
6684
- * See: https://docs.midnight.network/develop/reference/compact/lang-ref#type-cast-expressions
6685
- *
6686
- * Cast kinds: static (always succeeds), conversion (semantic change), checked (can fail)
6687
- */
6688
- typeCasting: [
6689
- {
6690
- from: "Uint<N>",
6691
- to: "Field",
6692
- direct: true,
6693
- kind: "static",
6694
- note: "Always succeeds"
6695
- },
6696
- {
6697
- from: "Field",
6698
- to: "Uint<0..n>",
6699
- direct: true,
6700
- kind: "checked",
6701
- note: "Fails at runtime if value > n"
6702
- },
6703
- {
6704
- from: "Field",
6705
- to: "Bytes<n>",
6706
- direct: true,
6707
- kind: "conversion",
6708
- note: "Can fail at runtime if value doesn't fit (little-endian)"
6709
- },
6710
- {
6711
- from: "Bytes<m>",
6712
- to: "Field",
6713
- direct: true,
6714
- kind: "conversion",
6715
- note: "Can fail at runtime if result exceeds max Field value"
6716
- },
6717
- {
6718
- from: "Boolean",
6719
- to: "Uint<0..n>",
6720
- direct: true,
6721
- kind: "conversion",
6722
- note: "false\u21920, true\u21921 (n must not be 0)"
6723
- },
6724
- {
6725
- from: "Boolean",
6726
- to: "Field",
6727
- direct: false,
6728
- fix: "Go through Uint: (flag as Uint<0..1>) as Field"
6729
- },
6730
- {
6731
- from: "Uint<N>",
6732
- to: "Bytes<M>",
6733
- direct: false,
6734
- fix: "Go through Field: (amount as Field) as Bytes<32>"
6735
- },
6736
- {
6737
- from: "enum",
6738
- to: "Field",
6739
- direct: true,
6740
- kind: "conversion",
6741
- note: "Enum variant index as Field"
6742
- },
6743
- {
6744
- from: "Uint<0..m>",
6745
- to: "Uint<0..n>",
6746
- direct: true,
6747
- kind: "static if m\u2264n, checked if m>n",
6748
- note: "Widening is static, narrowing is checked"
6749
- },
6750
- {
6751
- from: "arithmetic result",
6752
- to: "Uint<64>",
6753
- direct: true,
6754
- kind: "checked",
6755
- note: "Required cast: (a + b) as Uint<64>"
6272
+ function scanForDeprecatedPatterns(code) {
6273
+ const issues = [];
6274
+ const lines = code.split("\n");
6275
+ for (const { pattern, name, message } of DEPRECATED_SYNTAX_PATTERNS) {
6276
+ for (let i = 0; i < lines.length; i++) {
6277
+ if (pattern.test(lines[i])) {
6278
+ issues.push({
6279
+ pattern: name,
6280
+ message,
6281
+ lineNumber: i + 1
6282
+ });
6283
+ }
6756
6284
  }
6757
- ],
6758
- assignments: [
6759
- {
6760
- types: "Field = Uint<N>",
6761
- works: false,
6762
- fix: "Cast with `value as Field`"
6285
+ }
6286
+ return issues;
6287
+ }
6288
+ async function validateAllStaticData(staticData) {
6289
+ const results = {};
6290
+ const [builtinResult, typeResult, errorResult, adtResults] = await Promise.all([
6291
+ staticData.builtinFunctions ? validateBuiltinFunctions(staticData.builtinFunctions) : Promise.resolve(null),
6292
+ staticData.typeCompatibility ? validateTypeCompatibility(staticData.typeCompatibility) : Promise.resolve(null),
6293
+ staticData.commonErrors ? validateCommonErrors(staticData.commonErrors) : Promise.resolve(null),
6294
+ staticData.ledgerTypeLimits ? Promise.all(
6295
+ Object.entries(staticData.ledgerTypeLimits).map(
6296
+ async ([name, data]) => ({
6297
+ name,
6298
+ result: await validateADTOperations(
6299
+ name,
6300
+ data.circuitOperations
6301
+ )
6302
+ })
6303
+ )
6304
+ ) : Promise.resolve([])
6305
+ ]);
6306
+ if (builtinResult) results.builtinFunctions = builtinResult;
6307
+ if (typeResult) results.typeCompatibility = typeResult;
6308
+ if (errorResult) results.commonErrors = errorResult;
6309
+ for (const { name, result } of adtResults) {
6310
+ results[`adt_${name}`] = {
6311
+ dataType: `LEDGER_TYPE_LIMITS.${name}`,
6312
+ validated: result.validated,
6313
+ discrepancies: result.discrepancies,
6314
+ enrichments: result.enrichments,
6315
+ deprecatedPatterns: [],
6316
+ lastValidated: (/* @__PURE__ */ new Date()).toISOString()
6317
+ };
6318
+ }
6319
+ const allDiscrepancies = Object.values(results).flatMap(
6320
+ (r) => r.discrepancies
6321
+ );
6322
+ const allEnrichments = Object.values(results).flatMap((r) => r.enrichments);
6323
+ return {
6324
+ overall: {
6325
+ validated: allDiscrepancies.length === 0,
6326
+ totalDiscrepancies: allDiscrepancies.length,
6327
+ totalEnrichments: allEnrichments.length
6763
6328
  },
6764
- {
6765
- types: "Uint<N> = Field",
6766
- works: false,
6767
- fix: "Use bounded param or explicit cast"
6768
- }
6769
- ],
6770
- tips: [
6771
- "Use Uint<0..N> for circuit parameters that need range validation",
6772
- "Field is unbounded - use for hashes, commitments, general computation",
6773
- "Uint<N> is bounded - use when you need range checks",
6774
- "Casting with `as Field` is safe but loses range information"
6775
- ]
6329
+ results,
6330
+ lastValidated: (/* @__PURE__ */ new Date()).toISOString()
6331
+ };
6332
+ }
6333
+
6334
+ // src/server.ts
6335
+ var SERVER_INFO = {
6336
+ name: "midnight-mcp",
6337
+ version: CURRENT_VERSION,
6338
+ description: "MCP Server for Midnight Blockchain Development"
6776
6339
  };
6777
- var LEDGER_TYPE_LIMITS = {
6778
- Counter: {
6779
- circuitOperations: [
6780
- {
6781
- method: ".increment(n)",
6782
- works: true,
6783
- note: "Increase counter by n (Uint<16>)"
6784
- },
6785
- {
6786
- method: ".decrement(n)",
6787
- works: true,
6788
- note: "Decrease counter by n (Uint<16>)"
6789
- },
6790
- {
6791
- method: ".read()",
6792
- works: true,
6793
- note: "Get current value (returns Uint<64>)"
6794
- },
6795
- {
6796
- method: ".lessThan(n)",
6797
- works: true,
6798
- note: "Compare with threshold (returns Boolean)"
6799
- },
6800
- { method: ".resetToDefault()", works: true, note: "Reset to 0" }
6801
- ],
6802
- typescriptAccess: "Access counter value via `ledgerState.counter` in TypeScript SDK",
6803
- note: "All Counter operations work in circuits. Use .read() to get value, NOT .value()"
6340
+ var versionCheckResult = {
6341
+ isOutdated: false,
6342
+ latestVersion: CURRENT_VERSION,
6343
+ updateMessage: null,
6344
+ lastChecked: 0
6345
+ };
6346
+ var toolCallCount = 0;
6347
+ var VERSION_CHECK_INTERVAL = 10;
6348
+ async function checkForUpdates() {
6349
+ try {
6350
+ const controller = new AbortController();
6351
+ const timeoutId = setTimeout(() => controller.abort(), 5e3);
6352
+ const response = await fetch(
6353
+ "https://registry.npmjs.org/midnight-mcp/latest",
6354
+ { signal: controller.signal }
6355
+ );
6356
+ clearTimeout(timeoutId);
6357
+ if (!response.ok) return;
6358
+ const data = await response.json();
6359
+ const latestVersion = data.version;
6360
+ if (latestVersion !== CURRENT_VERSION) {
6361
+ versionCheckResult = {
6362
+ isOutdated: true,
6363
+ latestVersion,
6364
+ lastChecked: Date.now(),
6365
+ updateMessage: `\u{1F6A8} UPDATE AVAILABLE: v${latestVersion} (you have v${CURRENT_VERSION})`
6366
+ };
6367
+ logger.warn(
6368
+ `Outdated version detected: v${CURRENT_VERSION} -> v${latestVersion}`
6369
+ );
6370
+ } else {
6371
+ versionCheckResult = {
6372
+ ...versionCheckResult,
6373
+ lastChecked: Date.now()
6374
+ };
6375
+ }
6376
+ } catch (error) {
6377
+ logger.debug("Version check failed (non-blocking)", {
6378
+ error: error instanceof Error ? error.message : String(error)
6379
+ });
6380
+ }
6381
+ }
6382
+ function maybeCheckForUpdates() {
6383
+ toolCallCount++;
6384
+ if (toolCallCount >= VERSION_CHECK_INTERVAL) {
6385
+ toolCallCount = 0;
6386
+ const fiveMinutes = 5 * 60 * 1e3;
6387
+ if (Date.now() - versionCheckResult.lastChecked > fiveMinutes) {
6388
+ checkForUpdates().catch((error) => {
6389
+ logger.debug("Periodic version check failed", {
6390
+ error: error instanceof Error ? error.message : String(error)
6391
+ });
6392
+ });
6393
+ }
6394
+ }
6395
+ }
6396
+ function getUpdateWarning() {
6397
+ return versionCheckResult.updateMessage;
6398
+ }
6399
+ var resourceSubscriptions = /* @__PURE__ */ new Set();
6400
+ var resourceTemplates = [
6401
+ {
6402
+ uriTemplate: "midnight://code/{owner}/{repo}/{path}",
6403
+ name: "Repository Code",
6404
+ title: "\u{1F4C4} Repository Code Files",
6405
+ description: "Access code files from any Midnight repository by specifying owner, repo, and file path",
6406
+ mimeType: "text/plain"
6804
6407
  },
6805
- Map: {
6806
- circuitOperations: [
6807
- {
6808
- method: ".insert(key, value)",
6809
- works: true,
6810
- note: "Adds/updates entry"
6811
- },
6812
- {
6813
- method: ".insertDefault(key)",
6814
- works: true,
6815
- note: "Inserts default value for key"
6816
- },
6817
- { method: ".remove(key)", works: true, note: "Removes entry" },
6818
- {
6819
- method: ".lookup(key)",
6820
- works: true,
6821
- note: "Returns value_type - gets value for key"
6822
- },
6823
- {
6824
- method: ".member(key)",
6825
- works: true,
6826
- note: "Returns Boolean - checks if key exists"
6408
+ {
6409
+ uriTemplate: "midnight://docs/{section}/{topic}",
6410
+ name: "Documentation",
6411
+ title: "\u{1F4DA} Documentation Sections",
6412
+ description: "Access documentation by section (guides, api, concepts) and topic",
6413
+ mimeType: "text/markdown"
6414
+ },
6415
+ {
6416
+ uriTemplate: "midnight://examples/{category}/{name}",
6417
+ name: "Example Contracts",
6418
+ title: "\u{1F4DD} Example Contracts",
6419
+ description: "Access example contracts by category (counter, bboard, token, voting) and name",
6420
+ mimeType: "text/x-compact"
6421
+ },
6422
+ {
6423
+ uriTemplate: "midnight://schema/{type}",
6424
+ name: "Schema Definitions",
6425
+ title: "\u{1F527} Schema Definitions",
6426
+ description: "Access JSON schemas for contract AST, transactions, and proofs",
6427
+ mimeType: "application/json"
6428
+ }
6429
+ ];
6430
+ var mcpLogLevel = "info";
6431
+ var serverInstance = null;
6432
+ var isConnected = false;
6433
+ function setServerConnected(connected) {
6434
+ isConnected = connected;
6435
+ }
6436
+ function sendLogToClient(level, loggerName, data) {
6437
+ if (!serverInstance || !isConnected) return;
6438
+ const levelValues = {
6439
+ debug: 0,
6440
+ info: 1,
6441
+ notice: 2,
6442
+ warning: 3,
6443
+ error: 4,
6444
+ critical: 5,
6445
+ alert: 6,
6446
+ emergency: 7
6447
+ };
6448
+ if (levelValues[level] < levelValues[mcpLogLevel]) return;
6449
+ try {
6450
+ serverInstance.notification({
6451
+ method: "notifications/message",
6452
+ params: {
6453
+ level,
6454
+ logger: loggerName,
6455
+ data
6456
+ }
6457
+ });
6458
+ } catch (error) {
6459
+ logger.debug("Failed to send log notification to client", {
6460
+ error: error instanceof Error ? error.message : String(error)
6461
+ });
6462
+ }
6463
+ }
6464
+ function sendProgressNotification(progressToken, progress, total, message) {
6465
+ if (!serverInstance) return;
6466
+ try {
6467
+ serverInstance.notification({
6468
+ method: "notifications/progress",
6469
+ params: {
6470
+ progressToken,
6471
+ progress,
6472
+ ...total !== void 0 && { total },
6473
+ ...message && { message }
6474
+ }
6475
+ });
6476
+ } catch (error) {
6477
+ logger.debug("Failed to send progress notification", {
6478
+ error: error instanceof Error ? error.message : String(error)
6479
+ });
6480
+ }
6481
+ }
6482
+ function createServer() {
6483
+ const server = new Server(SERVER_INFO, {
6484
+ capabilities: {
6485
+ tools: {
6486
+ listChanged: true
6827
6487
  },
6828
- {
6829
- method: ".isEmpty()",
6830
- works: true,
6831
- note: "Returns Boolean - checks if map is empty"
6488
+ resources: {
6489
+ subscribe: true,
6490
+ listChanged: true
6832
6491
  },
6833
- {
6834
- method: ".size()",
6835
- works: true,
6836
- note: "Returns Uint<64> - number of entries"
6492
+ prompts: {
6493
+ listChanged: true
6837
6494
  },
6838
- {
6839
- method: ".resetToDefault()",
6840
- works: true,
6841
- note: "Clears entire map"
6495
+ logging: {},
6496
+ completions: {}
6497
+ }
6498
+ });
6499
+ serverInstance = server;
6500
+ setMCPLogCallback((level, loggerName, data) => {
6501
+ sendLogToClient(level, loggerName, data);
6502
+ });
6503
+ registerToolHandlers(server);
6504
+ registerResourceHandlers(server);
6505
+ registerPromptHandlers(server);
6506
+ registerSubscriptionHandlers(server);
6507
+ registerLoggingHandler(server);
6508
+ registerCompletionsHandler(server);
6509
+ setupSampling(server);
6510
+ return server;
6511
+ }
6512
+ function registerLoggingHandler(server) {
6513
+ server.setRequestHandler(SetLevelRequestSchema, async (request) => {
6514
+ const { level } = request.params;
6515
+ mcpLogLevel = level;
6516
+ logger.info(`MCP log level set to: ${level}`);
6517
+ sendLogToClient("info", "midnight-mcp", {
6518
+ message: `Log level changed to ${level}`
6519
+ });
6520
+ return {};
6521
+ });
6522
+ }
6523
+ var COMPLETION_VALUES = {
6524
+ "midnight:create-contract": {
6525
+ contractType: [
6526
+ "token",
6527
+ "voting",
6528
+ "credential",
6529
+ "auction",
6530
+ "escrow",
6531
+ "custom"
6532
+ ],
6533
+ privacyLevel: ["full", "partial", "public"],
6534
+ complexity: ["beginner", "intermediate", "advanced"]
6535
+ },
6536
+ "midnight:review-contract": {
6537
+ focusAreas: [
6538
+ "security",
6539
+ "performance",
6540
+ "privacy",
6541
+ "readability",
6542
+ "gas-optimization"
6543
+ ]
6544
+ },
6545
+ "midnight:explain-concept": {
6546
+ concept: [
6547
+ "zk-proofs",
6548
+ "circuits",
6549
+ "witnesses",
6550
+ "ledger",
6551
+ "state-management",
6552
+ "privacy-model",
6553
+ "token-transfers",
6554
+ "merkle-trees"
6555
+ ],
6556
+ level: ["beginner", "intermediate", "advanced"]
6557
+ },
6558
+ "midnight:compare-approaches": {
6559
+ approaches: [
6560
+ "token-standards",
6561
+ "state-management",
6562
+ "privacy-patterns",
6563
+ "circuit-design"
6564
+ ]
6565
+ },
6566
+ "midnight:debug-contract": {
6567
+ errorType: [
6568
+ "compilation",
6569
+ "runtime",
6570
+ "logic",
6571
+ "privacy-leak",
6572
+ "state-corruption"
6573
+ ]
6574
+ }
6575
+ };
6576
+ function registerCompletionsHandler(server) {
6577
+ server.setRequestHandler(CompleteRequestSchema, async (request) => {
6578
+ const { ref, argument } = request.params;
6579
+ if (ref.type !== "ref/prompt") {
6580
+ return { completion: { values: [], hasMore: false } };
6581
+ }
6582
+ const promptName = ref.name;
6583
+ const argName = argument.name;
6584
+ const currentValue = argument.value?.toLowerCase() || "";
6585
+ const promptCompletions = COMPLETION_VALUES[promptName];
6586
+ if (!promptCompletions) {
6587
+ return { completion: { values: [], hasMore: false } };
6588
+ }
6589
+ const argValues = promptCompletions[argName];
6590
+ if (!argValues) {
6591
+ return { completion: { values: [], hasMore: false } };
6592
+ }
6593
+ const filtered = argValues.filter(
6594
+ (v) => v.toLowerCase().includes(currentValue)
6595
+ );
6596
+ return {
6597
+ completion: {
6598
+ values: filtered.slice(0, 20),
6599
+ total: filtered.length,
6600
+ hasMore: filtered.length > 20
6601
+ }
6602
+ };
6603
+ });
6604
+ }
6605
+ function registerToolHandlers(server) {
6606
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
6607
+ logger.debug("Listing tools");
6608
+ return {
6609
+ tools: allTools.map((tool) => ({
6610
+ name: tool.name,
6611
+ description: tool.description,
6612
+ inputSchema: tool.inputSchema,
6613
+ // Include output schema if defined
6614
+ ...tool.outputSchema && { outputSchema: tool.outputSchema },
6615
+ // Include annotations if defined
6616
+ ...tool.annotations && { annotations: tool.annotations }
6617
+ }))
6618
+ };
6619
+ });
6620
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
6621
+ const { name, arguments: args } = request.params;
6622
+ logger.info(`Tool called: ${name}`, { args });
6623
+ const startTime = Date.now();
6624
+ const tool = allTools.find((t) => t.name === name);
6625
+ if (!tool) {
6626
+ const availableTools = allTools.map((t) => t.name).slice(0, 5).join(", ");
6627
+ trackToolCall(name, false, Date.now() - startTime, CURRENT_VERSION);
6628
+ return {
6629
+ content: [
6630
+ {
6631
+ type: "text",
6632
+ text: serialize({
6633
+ error: `Unknown tool: ${name}`,
6634
+ suggestion: `Available tools include: ${availableTools}...`,
6635
+ hint: "Use ListTools to see all available tools"
6636
+ })
6637
+ }
6638
+ ],
6639
+ isError: true
6640
+ };
6641
+ }
6642
+ try {
6643
+ const result = await tool.handler(args);
6644
+ const durationMs = Date.now() - startTime;
6645
+ trackToolCall(name, true, durationMs, CURRENT_VERSION);
6646
+ maybeCheckForUpdates();
6647
+ const updateWarning = getUpdateWarning();
6648
+ if (updateWarning && versionCheckResult.isOutdated) {
6649
+ const updatePrompt = {
6650
+ "\u{1F6A8} UPDATE REQUIRED": {
6651
+ current: `v${CURRENT_VERSION}`,
6652
+ latest: `v${versionCheckResult.latestVersion}`,
6653
+ quickFix: {
6654
+ description: "Most users just need these 2 steps:",
6655
+ step1: "Run in terminal: rm -rf ~/.npm/_npx",
6656
+ step2: "Restart editor completely (Cmd+Q / Alt+F4, then reopen)"
6657
+ },
6658
+ ifStillOld: {
6659
+ description: "If still showing old version after restart:",
6660
+ action: "Edit your MCP config file to use @latest:",
6661
+ change: 'Change "midnight-mcp" to "midnight-mcp@latest" in args',
6662
+ configFiles: {
6663
+ "Claude Desktop (Mac)": "~/Library/Application Support/Claude/claude_desktop_config.json",
6664
+ "Claude Desktop (Win)": "%APPDATA%/Claude/claude_desktop_config.json",
6665
+ Cursor: ".cursor/mcp.json",
6666
+ "VS Code": ".vscode/mcp.json",
6667
+ Windsurf: "~/.codeium/windsurf/mcp_config.json"
6668
+ }
6669
+ },
6670
+ tip: "Use 'midnight-get-update-instructions' tool for detailed platform-specific steps."
6671
+ },
6672
+ result
6673
+ };
6674
+ return {
6675
+ content: [
6676
+ {
6677
+ type: "text",
6678
+ text: serialize(updatePrompt)
6679
+ }
6680
+ ]
6681
+ };
6682
+ }
6683
+ return {
6684
+ content: [
6685
+ {
6686
+ type: "text",
6687
+ text: serialize(result)
6688
+ }
6689
+ ],
6690
+ // Include structured content for machine-readable responses
6691
+ // This allows clients to parse results without JSON.parse()
6692
+ structuredContent: result
6693
+ };
6694
+ } catch (error) {
6695
+ const durationMs = Date.now() - startTime;
6696
+ logger.error(`Tool error: ${name}`, { error: String(error) });
6697
+ trackToolCall(name, false, durationMs, CURRENT_VERSION);
6698
+ const errorResponse = formatErrorResponse(error, `tool:${name}`);
6699
+ return {
6700
+ content: [
6701
+ {
6702
+ type: "text",
6703
+ text: serialize(errorResponse)
6704
+ }
6705
+ ],
6706
+ isError: true
6707
+ };
6708
+ }
6709
+ });
6710
+ }
6711
+ function registerResourceHandlers(server) {
6712
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
6713
+ logger.debug("Listing resources");
6714
+ return {
6715
+ resources: allResources.map((resource) => ({
6716
+ uri: resource.uri,
6717
+ name: resource.name,
6718
+ description: resource.description,
6719
+ mimeType: resource.mimeType
6720
+ }))
6721
+ };
6722
+ });
6723
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
6724
+ logger.debug("Listing resource templates");
6725
+ return {
6726
+ resourceTemplates: resourceTemplates.map((template) => ({
6727
+ uriTemplate: template.uriTemplate,
6728
+ name: template.name,
6729
+ title: template.title,
6730
+ description: template.description,
6731
+ mimeType: template.mimeType
6732
+ }))
6733
+ };
6734
+ });
6735
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
6736
+ const { uri } = request.params;
6737
+ logger.info(`Resource requested: ${uri}`);
6738
+ try {
6739
+ let content = null;
6740
+ let mimeType = "text/plain";
6741
+ if (uri.startsWith("midnight://docs/")) {
6742
+ content = await getDocumentation(uri);
6743
+ mimeType = "text/markdown";
6744
+ } else if (uri.startsWith("midnight://code/")) {
6745
+ content = await getCode(uri);
6746
+ mimeType = "text/x-compact";
6747
+ } else if (uri.startsWith("midnight://schema/")) {
6748
+ const schema = getSchema(uri);
6749
+ content = schema ? JSON.stringify(schema, null, 2) : null;
6750
+ mimeType = "application/json";
6842
6751
  }
6843
- ],
6844
- typescriptAccess: "Query map via `contractState.data.get(key)` or iterate with `[Symbol.iterator]()` in TypeScript SDK",
6845
- note: "All Map operations work in circuits. insertCoin() available when value_type is QualifiedCoinInfo."
6846
- },
6847
- Set: {
6848
- circuitOperations: [
6849
- { method: ".insert(value)", works: true, note: "Adds to set" },
6850
- { method: ".remove(value)", works: true, note: "Removes from set" },
6851
- {
6852
- method: ".member(value)",
6853
- works: true,
6854
- note: "Returns Boolean - checks if value exists in set"
6855
- },
6856
- {
6857
- method: ".isEmpty()",
6858
- works: true,
6859
- note: "Returns Boolean - checks if set is empty"
6860
- },
6861
- {
6862
- method: ".size()",
6863
- works: true,
6864
- note: "Returns Uint<64> - number of elements"
6865
- },
6866
- {
6867
- method: ".resetToDefault()",
6868
- works: true,
6869
- note: "Clears entire set"
6752
+ if (!content) {
6753
+ const resourceTypes = [
6754
+ "midnight://docs/",
6755
+ "midnight://code/",
6756
+ "midnight://schema/"
6757
+ ];
6758
+ const validPrefix = resourceTypes.find((p) => uri.startsWith(p));
6759
+ let suggestion = validPrefix ? `Check the resource path after '${validPrefix}'` : `Valid resource prefixes: ${resourceTypes.join(", ")}`;
6760
+ if (uri.includes("://resources/")) {
6761
+ const resourceName = uri.split("://resources/").pop() || "";
6762
+ if (resourceName.includes("template") || resourceName.includes("pattern") || resourceName.includes("example")) {
6763
+ suggestion = `Try: midnight://code/templates/${resourceName} or midnight://code/examples/${resourceName} or midnight://code/patterns/${resourceName}`;
6764
+ } else if (resourceName.includes("doc") || resourceName.includes("reference") || resourceName.includes("guide")) {
6765
+ suggestion = `Try: midnight://docs/${resourceName}`;
6766
+ } else {
6767
+ suggestion = `'midnight://resources/' is not valid. Use: midnight://docs/, midnight://code/, or midnight://schema/`;
6768
+ }
6769
+ }
6770
+ return {
6771
+ contents: [
6772
+ {
6773
+ uri,
6774
+ mimeType: "application/json",
6775
+ text: serialize({
6776
+ error: `Resource not found: ${uri}`,
6777
+ suggestion,
6778
+ hint: "Use ListResources to see all available resources",
6779
+ validPrefixes: resourceTypes
6780
+ })
6781
+ }
6782
+ ]
6783
+ };
6870
6784
  }
6871
- ],
6872
- typescriptAccess: "Check membership via `contractState.set.has(value)` or iterate with `[Symbol.iterator]()` in TypeScript SDK",
6873
- note: "All Set operations work in circuits. insertCoin() available when value_type is QualifiedCoinInfo."
6874
- },
6875
- MerkleTree: {
6876
- circuitOperations: [
6877
- { method: ".insert(leaf)", works: true, note: "Adds leaf to tree" },
6878
- { method: ".root()", works: false, note: "NOT available in circuits" }
6879
- ],
6880
- typescriptAccess: "Get root via `contractState.tree.root` in TypeScript SDK",
6881
- pattern: `// To verify a merkle proof in circuit:
6882
- witness get_merkle_root(): Bytes<32>;
6883
- witness get_merkle_proof(leaf: Bytes<32>): Vector<32, Bytes<32>>;
6884
-
6885
- // Verify proof using persistentHash to compute expected root`
6886
- }
6887
- };
6888
- var COMMON_ERRORS = [
6889
- {
6890
- error: 'unbound identifier "public_key"',
6891
- cause: "Trying to use public_key() as if it's a builtin function",
6892
- fix: `Use persistentHash pattern instead:
6893
- const pk = persistentHash<Vector<2, Bytes<32>>>([pad(32, "midnight:pk:"), sk]);`
6894
- },
6895
- {
6896
- error: "incompatible combination of types Field and Uint",
6897
- cause: "Comparing or operating on Field with Uint without casting",
6898
- fix: `Cast Uint to Field: (myUint as Field)
6899
- Or use bounded Uint<0..N> for parameters that need constraints`
6900
- },
6901
- {
6902
- error: 'operation "value" undefined for ledger field type Counter',
6903
- cause: "Using wrong method name - Counter uses .read() not .value()",
6904
- fix: `Use counter.read() to get the current value:
6905
- const current = ledger.counter.read();
6906
-
6907
- Counter ADT methods available in circuits:
6908
- - increment(amount: Uint<16>): [] - increase counter
6909
- - decrement(amount: Uint<16>): [] - decrease counter
6910
- - read(): Uint<64> - get current value
6911
- - lessThan(threshold: Uint<64>): Boolean - compare
6912
- - resetToDefault(): [] - reset to zero`
6913
- },
6914
- {
6915
- error: "implicit disclosure of witness value",
6916
- cause: "Using witness value in conditional without disclose()",
6917
- fix: `Wrap witness comparisons in disclose():
6918
- if (disclose(witness_value == expected)) { ... }`
6919
- },
6920
- {
6921
- error: 'parse error: found "{" looking for an identifier',
6922
- cause: "Using old ledger { } block syntax",
6923
- fix: `Use individual exports instead:
6924
- export ledger field1: Type1;
6925
- export ledger field2: Type2;`
6926
- },
6927
- {
6928
- error: 'parse error: found "{" looking for ";"',
6929
- cause: "Using Void as return type (doesn't exist)",
6930
- fix: `Use empty tuple [] for no return value:
6931
- export circuit myCircuit(): [] { ... }`
6932
- },
6933
- {
6934
- error: 'unbound identifier "Cell"',
6935
- cause: "Using deprecated Cell<T> wrapper (removed in 0.15)",
6936
- fix: `Remove Cell wrapper, just use the type directly:
6937
- export ledger myField: Field; // Not Cell<Field>`
6938
- },
6939
- {
6940
- error: "member access requires struct type",
6941
- cause: "Trying to access a field on a non-struct type",
6942
- fix: `Make sure you're accessing a struct field, not a primitive.
6943
- Map.lookup() and Map.member() ARE available in circuits.
6944
- Check that the base type is actually a struct.`
6945
- },
6946
- {
6947
- error: "potential witness-value disclosure must be declared",
6948
- cause: "Circuit parameter flows to ledger operation without disclose()",
6949
- fix: `Disclose parameters at the start of the circuit:
6950
- export circuit my_circuit(param: Bytes<32>): [] {
6951
- const d_param = disclose(param); // Acknowledge on-chain visibility
6952
- ledger.insert(d_param, value); // Now use disclosed value
6953
- }`
6954
- },
6955
- {
6956
- error: "expected second argument of insert to have type Uint<64> but received Uint<0..N>",
6957
- cause: "Arithmetic result has bounded type, needs cast back to target",
6958
- fix: `Cast arithmetic results back to the target type:
6959
- const new_balance = (current + amount) as Uint<64>;
6960
- ledger_map.insert(key, new_balance);`
6961
- },
6962
- {
6963
- error: "cannot cast from type Uint<64> to type Bytes<32>",
6964
- cause: "Direct Uint to Bytes cast not allowed",
6965
- fix: `Go through Field first:
6966
- const amount_field = amount as Field;
6967
- const amount_bytes = amount_field as Bytes<32>;
6968
- // Or chained: (amount as Field) as Bytes<32>`
6969
- },
6970
- {
6971
- error: "cannot prove assertion",
6972
- cause: "Assert condition cannot be proven true",
6973
- fix: `Check your logic. Common causes:
6974
- 1. Witness returns unexpected value
6975
- 2. Range check fails (use bounded Uint)
6976
- 3. Logic error in circuit`
6977
- },
6978
- {
6979
- error: 'parse error: found ":" looking for ")"',
6980
- cause: "Using Rust-style :: for enum variant access",
6981
- fix: `Use dot notation for enum variants:
6982
- WRONG: Choice::rock, GameState::waiting
6983
- CORRECT: Choice.rock, GameState.waiting`
6984
- },
6985
- {
6986
- error: 'parse error: found "{" after witness declaration',
6987
- cause: "Trying to add implementation body to witness",
6988
- fix: `Witnesses are declarations only - no body allowed:
6989
- WRONG: witness get_caller(): Bytes<32> { return ...; }
6990
- CORRECT: witness get_caller(): Bytes<32>;
6991
- Implementation goes in TypeScript prover, not Compact.`
6992
- },
6993
- {
6994
- error: 'unbound identifier "function"',
6995
- cause: 'Using "pure function" instead of "pure circuit"',
6996
- fix: `Use "pure circuit" for helper functions:
6997
- WRONG: pure function helper(...): Type { }
6998
- CORRECT: pure circuit helper(...): Type { }`
6785
+ return {
6786
+ contents: [
6787
+ {
6788
+ uri,
6789
+ mimeType,
6790
+ text: content
6791
+ }
6792
+ ]
6793
+ };
6794
+ } catch (error) {
6795
+ logger.error(`Resource error: ${uri}`, { error: String(error) });
6796
+ const errorResponse = formatErrorResponse(error, `resource:${uri}`);
6797
+ return {
6798
+ contents: [
6799
+ {
6800
+ uri,
6801
+ mimeType: "application/json",
6802
+ text: serialize(errorResponse)
6803
+ }
6804
+ ]
6805
+ };
6806
+ }
6807
+ });
6808
+ }
6809
+ function registerPromptHandlers(server) {
6810
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
6811
+ logger.debug("Listing prompts");
6812
+ return {
6813
+ prompts: promptDefinitions.map((prompt) => ({
6814
+ name: prompt.name,
6815
+ description: prompt.description,
6816
+ arguments: prompt.arguments
6817
+ }))
6818
+ };
6819
+ });
6820
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
6821
+ const { name, arguments: args } = request.params;
6822
+ logger.info(`Prompt requested: ${name}`, { args });
6823
+ const prompt = promptDefinitions.find((p) => p.name === name);
6824
+ if (!prompt) {
6825
+ return {
6826
+ description: `Unknown prompt: ${name}`,
6827
+ messages: []
6828
+ };
6829
+ }
6830
+ const messages = generatePrompt(name, args || {});
6831
+ return {
6832
+ description: prompt.description,
6833
+ messages: messages.map((m) => ({
6834
+ role: m.role,
6835
+ content: m.content
6836
+ }))
6837
+ };
6838
+ });
6839
+ }
6840
+ function registerSubscriptionHandlers(server) {
6841
+ server.setRequestHandler(SubscribeRequestSchema, async (request) => {
6842
+ const { uri } = request.params;
6843
+ logger.info(`Subscribing to resource: ${uri}`);
6844
+ const validPrefixes = [
6845
+ "midnight://docs/",
6846
+ "midnight://code/",
6847
+ "midnight://schema/"
6848
+ ];
6849
+ const isValid = validPrefixes.some((prefix) => uri.startsWith(prefix));
6850
+ if (!isValid) {
6851
+ logger.warn(`Invalid subscription URI: ${uri}`);
6852
+ throw new Error(
6853
+ `Invalid subscription URI: ${uri}. Valid prefixes: ${validPrefixes.join(", ")}`
6854
+ );
6855
+ }
6856
+ resourceSubscriptions.add(uri);
6857
+ logger.debug(`Active subscriptions: ${resourceSubscriptions.size}`);
6858
+ return {};
6859
+ });
6860
+ server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
6861
+ const { uri } = request.params;
6862
+ logger.info(`Unsubscribing from resource: ${uri}`);
6863
+ resourceSubscriptions.delete(uri);
6864
+ logger.debug(`Active subscriptions: ${resourceSubscriptions.size}`);
6865
+ return {};
6866
+ });
6867
+ }
6868
+ function setupSampling(server) {
6869
+ const samplingCallback2 = async (request) => {
6870
+ logger.debug("Requesting sampling from client", {
6871
+ messageCount: request.messages.length,
6872
+ maxTokens: request.maxTokens
6873
+ });
6874
+ try {
6875
+ const response = await server.request(
6876
+ {
6877
+ method: "sampling/createMessage",
6878
+ params: {
6879
+ messages: request.messages,
6880
+ systemPrompt: request.systemPrompt,
6881
+ maxTokens: request.maxTokens || 2048,
6882
+ temperature: request.temperature,
6883
+ modelPreferences: request.modelPreferences
6884
+ }
6885
+ },
6886
+ // Use a schema that matches the expected response
6887
+ {
6888
+ parse: (data) => {
6889
+ const response2 = data;
6890
+ if (!response2 || typeof response2 !== "object") {
6891
+ throw new Error("Invalid sampling response: expected object");
6892
+ }
6893
+ if (!response2.content || typeof response2.content !== "object") {
6894
+ throw new Error("Invalid sampling response: missing content");
6895
+ }
6896
+ return response2;
6897
+ },
6898
+ _def: { typeName: "SamplingResponse" }
6899
+ }
6900
+ );
6901
+ return response;
6902
+ } catch (error) {
6903
+ logger.error("Sampling request failed", { error: String(error) });
6904
+ throw error;
6905
+ }
6906
+ };
6907
+ registerSamplingCallback(samplingCallback2);
6908
+ logger.info("Sampling capability configured");
6909
+ }
6910
+ async function initializeServer() {
6911
+ logger.info("Initializing Midnight MCP Server...");
6912
+ checkForUpdates().catch((error) => {
6913
+ logger.debug("Startup version check failed (non-blocking)", {
6914
+ error: error instanceof Error ? error.message : String(error)
6915
+ });
6916
+ });
6917
+ try {
6918
+ await vectorStore.initialize();
6919
+ logger.info("Vector store initialized");
6920
+ } catch (error) {
6921
+ logger.warn("Vector store initialization failed, continuing without it", {
6922
+ error: String(error)
6923
+ });
6999
6924
  }
7000
- ];
6925
+ const server = createServer();
6926
+ logger.info(`Server v${CURRENT_VERSION} created successfully`);
6927
+ return server;
6928
+ }
6929
+ async function startServer() {
6930
+ const server = await initializeServer();
6931
+ const transport = new StdioServerTransport();
6932
+ await server.connect(transport);
6933
+ setServerConnected(true);
6934
+ logger.info("Midnight MCP Server running on stdio");
6935
+ }
6936
+ var transports = {
6937
+ streamable: {},
6938
+ sse: {}
6939
+ };
6940
+ function buildAllowlist(port) {
6941
+ const hosts = [`127.0.0.1:${port}`, `localhost:${port}`];
6942
+ const origins = hosts.flatMap((host) => [
6943
+ `http://${host}`,
6944
+ `https://${host}`
6945
+ ]);
6946
+ return { allowedHosts: hosts, allowedOrigins: origins };
6947
+ }
6948
+ function isRebindingBlocked(host, origin, allowedHosts, allowedOrigins) {
6949
+ if (host && !allowedHosts.includes(host)) return true;
6950
+ if (origin && !allowedOrigins.includes(origin)) return true;
6951
+ return false;
6952
+ }
6953
+ async function closeTransports(transportMap) {
6954
+ const closePromises = Object.values(transportMap).map(
6955
+ (transport) => transport.close?.().catch(() => {
6956
+ })
6957
+ );
6958
+ await Promise.all(closePromises);
6959
+ }
6960
+ async function startHttpServer(port = 3e3) {
6961
+ const mcpServer = await initializeServer();
6962
+ const app = express();
6963
+ const { allowedHosts, allowedOrigins } = buildAllowlist(port);
6964
+ app.use("/mcp", express.json());
6965
+ app.get("/health", (_req, res) => {
6966
+ res.json({
6967
+ status: "ok",
6968
+ version: CURRENT_VERSION,
6969
+ transport: "http"
6970
+ });
6971
+ });
6972
+ app.post("/mcp", async (req, res) => {
6973
+ const sessionId = req.headers["mcp-session-id"];
6974
+ let transport;
6975
+ if (sessionId && transports.streamable[sessionId]) {
6976
+ transport = transports.streamable[sessionId];
6977
+ } else if (!sessionId && isInitializeRequest(req.body)) {
6978
+ transport = new StreamableHTTPServerTransport({
6979
+ sessionIdGenerator: () => randomUUID(),
6980
+ // Block DNS-rebinding attacks from browser pages (loopback allowlist).
6981
+ enableDnsRebindingProtection: true,
6982
+ allowedHosts,
6983
+ allowedOrigins,
6984
+ onsessioninitialized: (newSessionId) => {
6985
+ transports.streamable[newSessionId] = transport;
6986
+ logger.debug(`New streamable session: ${newSessionId}`);
6987
+ }
6988
+ });
6989
+ transport.onclose = () => {
6990
+ if (transport.sessionId) {
6991
+ delete transports.streamable[transport.sessionId];
6992
+ logger.debug(`Streamable session closed: ${transport.sessionId}`);
6993
+ }
6994
+ };
6995
+ try {
6996
+ await mcpServer.connect(transport);
6997
+ } catch (error) {
6998
+ logger.error("Failed to connect streamable transport", {
6999
+ error: String(error)
7000
+ });
7001
+ res.status(500).json({
7002
+ jsonrpc: "2.0",
7003
+ error: { code: -32603, message: "Internal error: connection failed" },
7004
+ id: null
7005
+ });
7006
+ return;
7007
+ }
7008
+ } else {
7009
+ res.status(400).json({
7010
+ jsonrpc: "2.0",
7011
+ error: {
7012
+ code: -32e3,
7013
+ message: "Bad Request: No valid session ID provided"
7014
+ },
7015
+ id: null
7016
+ });
7017
+ return;
7018
+ }
7019
+ await transport.handleRequest(req, res, req.body);
7020
+ });
7021
+ app.get("/sse", async (req, res) => {
7022
+ const host = req.headers.host;
7023
+ const origin = req.headers.origin;
7024
+ if (isRebindingBlocked(host, origin, allowedHosts, allowedOrigins)) {
7025
+ logger.warn(`Rejected SSE connection`, { host, origin });
7026
+ res.status(403).send("Forbidden: invalid Host or Origin");
7027
+ return;
7028
+ }
7029
+ logger.debug("New SSE connection");
7030
+ const transport = new SSEServerTransport("/messages", res, {
7031
+ enableDnsRebindingProtection: true,
7032
+ allowedHosts,
7033
+ allowedOrigins
7034
+ });
7035
+ transports.sse[transport.sessionId] = transport;
7036
+ res.on("close", () => {
7037
+ delete transports.sse[transport.sessionId];
7038
+ logger.debug(`SSE session closed: ${transport.sessionId}`);
7039
+ });
7040
+ try {
7041
+ await mcpServer.connect(transport);
7042
+ } catch (error) {
7043
+ delete transports.sse[transport.sessionId];
7044
+ logger.error(`SSE connection failed: ${transport.sessionId}`, {
7045
+ error: String(error)
7046
+ });
7047
+ res.status(500).end();
7048
+ }
7049
+ });
7050
+ app.post("/messages", async (req, res) => {
7051
+ const sessionId = req.query.sessionId;
7052
+ const transport = transports.sse[sessionId];
7053
+ if (transport) {
7054
+ await transport.handlePostMessage(req, res);
7055
+ } else {
7056
+ res.status(400).send(`No transport found for sessionId ${sessionId}`);
7057
+ }
7058
+ });
7059
+ const handleSessionRequest = async (req, res) => {
7060
+ const sessionId = req.headers["mcp-session-id"];
7061
+ if (!sessionId || !transports.streamable[sessionId]) {
7062
+ res.status(400).send("Invalid or missing session ID");
7063
+ return;
7064
+ }
7065
+ const transport = transports.streamable[sessionId];
7066
+ await transport.handleRequest(req, res);
7067
+ };
7068
+ app.get("/mcp", handleSessionRequest);
7069
+ app.delete("/mcp", handleSessionRequest);
7070
+ const httpServer = app.listen(port, "127.0.0.1", () => {
7071
+ logger.info(`Midnight MCP Server running on HTTP port ${port}`);
7072
+ logger.info(` Streamable HTTP: http://localhost:${port}/mcp`);
7073
+ logger.info(` SSE endpoint: http://localhost:${port}/sse`);
7074
+ logger.info(` Health check: http://localhost:${port}/health`);
7075
+ });
7076
+ const shutdown = async () => {
7077
+ logger.info("Shutting down HTTP server...");
7078
+ await closeTransports(transports.sse);
7079
+ await closeTransports(transports.streamable);
7080
+ httpServer.close(() => {
7081
+ logger.info("Server shutdown complete");
7082
+ process.exit(0);
7083
+ });
7084
+ };
7085
+ process.on("SIGINT", shutdown);
7086
+ process.on("SIGTERM", shutdown);
7087
+ }
7001
7088
 
7002
7089
  // src/tools/repository/validation.ts
7003
7090
  import { readFile } from "fs/promises";
@@ -7387,7 +7474,7 @@ async function extractContractStructure(input) {
7387
7474
  type: "invalid_pragma_format",
7388
7475
  line: lineNum,
7389
7476
  message: `Pragma includes patch version which may cause parse errors`,
7390
- suggestion: `Use bounded range format: 'pragma language_version >= 0.16 && <= 0.21;'`,
7477
+ suggestion: `Use bounded range format: '${RECOMMENDED_PRAGMA}'`,
7391
7478
  severity: "error"
7392
7479
  });
7393
7480
  }
@@ -8427,6 +8514,36 @@ export circuit increment(): [] {
8427
8514
  wrong: "export circuit fn(param: T): [] { ledger.insert(param, v); }",
8428
8515
  correct: "export circuit fn(param: T): [] { const d = disclose(param); ledger.insert(d, v); }",
8429
8516
  error: "potential witness-value disclosure must be declared"
8517
+ },
8518
+ {
8519
+ wrong: "NativePoint, nativePointX(p), constructNativePoint(x, y)",
8520
+ correct: "JubjubPoint, jubjubPointX(p), constructJubjubPoint(x, y)",
8521
+ error: "apparent use of an old standard-library / ledger operator name NativePoint: the new name is JubjubPoint (renamed in compactc 0.30.0)"
8522
+ },
8523
+ {
8524
+ wrong: "const let = 1; // or `let` used as any identifier",
8525
+ correct: "const value = 1; // rename the identifier",
8526
+ error: 'parse error: found keyword "let" (which is reserved for future use)'
8527
+ },
8528
+ {
8529
+ wrong: "const MAX_SUPPLY: Uint<64> = 1000; // module-level const",
8530
+ correct: "pure circuit max_supply(): Uint<64> { return 1000; } // wrap in a pure circuit",
8531
+ error: 'parse error: found keyword "const" looking for a program element'
8532
+ },
8533
+ {
8534
+ wrong: "flags | mask // bitwise OR on Uint",
8535
+ correct: "// No bitwise ops on Uint \u2014 use arithmetic or branching. `||` is logical OR for Booleans only.",
8536
+ error: "unexpected character (single `|` not recognized)"
8537
+ },
8538
+ {
8539
+ wrong: "field: Uint<254>",
8540
+ correct: "field: Uint<248> // max byte-aligned Uint width",
8541
+ error: "Uint width 254 is not between 1 and the maximum Uint width 248 (inclusive)"
8542
+ },
8543
+ {
8544
+ wrong: "convertBytesToUint(255, n, bytes, src) // number literal",
8545
+ correct: "convertBytesToUint(255n, n, bytes, src) // bigint literal",
8546
+ error: "TypeScript binding: maxval is bigint since compactc 0.30.101 (avoids silent precision loss)"
8430
8547
  }
8431
8548
  ],
8432
8549
  syntaxReference: compactReference,
@@ -11172,4 +11289,4 @@ export {
11172
11289
  startServer,
11173
11290
  startHttpServer
11174
11291
  };
11175
- //# sourceMappingURL=chunk-TSXIIBEH.js.map
11292
+ //# sourceMappingURL=chunk-F4KM42XU.js.map