midnight-mcp 0.2.17 → 0.2.19

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