Skip to content

feat: database version handling#42

Merged
QuantumExplorer merged 9 commits into
v0.2-devfrom
feat/database-version-handling
Nov 11, 2024
Merged

feat: database version handling#42
QuantumExplorer merged 9 commits into
v0.2-devfrom
feat/database-version-handling

Conversation

@pauldelucia
Copy link
Copy Markdown
Member

@pauldelucia pauldelucia commented Nov 8, 2024

Summary by CodeRabbit

  • New Features

    • Enhanced database initialization process with improved handling for first-time setups and versioning.
    • Added functionality to update app settings when switching networks.
  • Bug Fixes

    • Improved error handling and consistency for database operations.
  • Documentation

    • Updated constants for minimum and default database versions to reflect the latest changes.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 8, 2024

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The changes in this pull request involve significant modifications to the AppState struct and the Database implementation. The AppState struct's new method now initializes the Database with a reference to the database file path. Additionally, the update method has been updated to enhance the handling of network switching actions. In the Database implementation, the initialize method has been altered to manage database file paths, including checks for first-time setups and versioning. New helper methods and constants have been introduced to improve robustness and performance.

Changes

File Change Summary
src/app.rs - Updated new method in AppState to pass a reference for db_file_path.
- Modified update method in AppState to include a call to update_settings for AppAction::SwitchNetwork.
src/database/initialization.rs - Changed initialize method to accept db_file_path parameter.
- Added methods: is_first_time_setup, is_outdated, backup_and_recreate_db, set_default_version.
- Updated constants: MIN_SUPPORTED_DB_VERSION (to 1), DEFAULT_DB_VERSION (to 1), and added DEFAULT_NETWORK ("dash").
- Reformatted SQL commands and added new indexes for performance.

Sequence Diagram(s)

sequenceDiagram
    participant AppState
    participant Database
    participant Context

    AppState->>Database: new(db_file_path)
    Database->>Database: initialize(db_file_path)
    alt First time setup
        Database-->>Database: Create tables, set default version
    else Outdated database
        Database-->>Database: Backup and recreate database
    end
    AppState->>Context: update(AppAction::SwitchNetwork)
    Context->>AppState: update_settings(RootScreenType::RootScreenNetworkChooser)
Loading

🐰 "In the code, we hop and play,
With databases bright and gay.
New paths we set, and actions flow,
To make our app a joy to show!
So let’s rejoice, with a cheer and a dance,
For every change, we take a chance!" 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@pauldelucia pauldelucia changed the title Feat/database version handling feat: database version handling Nov 8, 2024
@pauldelucia pauldelucia marked this pull request as ready for review November 8, 2024 14:15
@pauldelucia pauldelucia changed the base branch from v0.2-dev to master November 11, 2024 07:09
@pauldelucia pauldelucia changed the base branch from master to v0.2-dev November 11, 2024 07:10
@pauldelucia
Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 11, 2024

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (5)
src/database/initialization.rs (5)

31-50: Optimize is_first_time_setup method logic

The method checks for the existence of the settings table and whether it has entries. However, you can simplify the logic by attempting to select from the settings table and handling exceptions if the table doesn't exist.

Apply this refactor to simplify the method:

-fn is_first_time_setup(&self) -> rusqlite::Result<bool> {
-    let conn = self.conn.lock().unwrap();
-
-    let table_exists: bool = conn.query_row(
-        "SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type='table' AND name='settings')",
-        [],
-        |row| row.get(0),
-    )?;
-
-    if !table_exists {
-        Ok(true)
-    } else {
-        let count: i64 =
-            conn.query_row("SELECT COUNT(*) FROM settings", [], |row| row.get(0))?;
-        Ok(count == 0)
-    }
-}
+fn is_first_time_setup(&self) -> rusqlite::Result<bool> {
+    let conn = self.conn.lock().unwrap();
+    let result = conn.query_row(
+        "SELECT COUNT(*) FROM settings",
+        [],
+        |row| row.get::<_, i64>(0),
+    );
+    match result {
+        Ok(count) => Ok(count == 0),
+        Err(rusqlite::Error::SqliteFailure(_, _)) => Ok(true), // Table doesn't exist
+        Err(e) => Err(e),
+    }
+}

53-63: Handle default version more gracefully in is_outdated

In is_outdated, if there's no version set, it defaults to 0. This could lead to unnecessary database backups if the version is not found. Consider explicitly checking for the absence of a version and handling it appropriately.


118-130: Consolidate table creation statements

You're calling self.execute multiple times for creating individual tables. Consider consolidating the creation of all tables within a single transaction to improve performance and ensure atomicity.

Apply this change to wrap table creation in a transaction:

+let conn = self.conn.lock().unwrap();
+let tx = conn.transaction()?;
 // Create the settings table
 self.execute(
     "CREATE TABLE IF NOT EXISTS settings (
         id INTEGER PRIMARY KEY CHECK (id = 1),
         network TEXT NOT NULL,
         start_root_screen INTEGER NOT NULL,
         database_version INTEGER NOT NULL
     )",
     [],
 )?;
 
 // ... other table creation statements ...

+tx.commit()?;

7-9: Use consistent naming for constants

The constant MIN_SUPPORTED_DB_VERSION is set to 1, which is the same as DEFAULT_DB_VERSION. If both are intended to always be the same, consider adding a comment to clarify or reuse one constant to avoid confusion.


85-87: Handle potential errors when renaming the database file

When renaming data.db to the backup file, any failure will currently return a generic ToSqlConversionFailure error. Consider providing a more descriptive error or handling specific cases like permission issues.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 9b5685e and 019e6ef.

📒 Files selected for processing (2)
  • src/app.rs (2 hunks)
  • src/database/initialization.rs (3 hunks)
🔇 Additional comments (3)
src/app.rs (1)

117-118: ⚠️ Potential issue

Improve database initialization error handling and clarity.

The current implementation has several concerns:

  1. Using unwrap() for database operations could lead to runtime panics
  2. Database is initialized twice with the same path

Consider applying this improvement:

-        let db = Arc::new(Database::new(&db_file_path).unwrap());
-        db.initialize(&db_file_path).unwrap();
+        let db = Arc::new(Database::new(&db_file_path)
+            .map_err(|e| format!("Failed to create database: {}", e))?);
+        db.initialize(&db_file_path)
+            .map_err(|e| format!("Failed to initialize database: {}", e))?;

Let's verify if these are indeed separate operations:

src/database/initialization.rs (2)

104-110: Encrypt sensitive data when inserting default settings

When inserting default settings, ensure that any sensitive information is properly encrypted or secured, especially if network or other fields could contain confidential data.

[security]


Line range hint 66-116: Ensure thread safety when recreating database connection in backup_and_recreate_db

When you replace the self.conn with a new connection after recreating the database, ensure that this operation is thread-safe. Locking the mutex and replacing the connection is correct, but consider the implications if other threads are accessing the database simultaneously.

To verify thread safety, check for other usages of self.conn that might be affected during the replacement:

✅ Verification successful

Based on the search results, I can now generate the final response:

Thread safety is properly maintained in database operations

All usages of self.conn across the codebase consistently follow the same pattern of acquiring the mutex lock before performing database operations. The backup_and_recreate_db method follows this pattern correctly:

  • It creates a new connection outside the lock
  • Only acquires the lock when replacing the connection
  • Releases the lock immediately after the replacement

This implementation ensures thread safety as:

  • Other threads will block when trying to acquire the lock during connection replacement
  • The lock is held for the minimal duration needed
  • All other database operations in the codebase properly acquire the lock before accessing the connection
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for all usages of self.conn in the codebase to identify potential threading issues.

# Expected: Review all points where self.conn is used to ensure they handle the mutex correctly.

rg -A 2 "self\.conn" src/

Length of output: 7833

Comment thread src/app.rs
Comment on lines +474 to +479
AppAction::SwitchNetwork(network) => {
self.change_network(network);
self.current_app_context()
.update_settings(RootScreenType::RootScreenNetworkChooser)
.ok();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

🛠️ Refactor suggestion

Error handling improvement is valid but needs adjustment

The review suggestion is partially correct. While error handling can be improved, the implementation should be adjusted based on the codebase context:

            AppAction::SwitchNetwork(network) => {
                self.change_network(network);
-                self.current_app_context()
-                    .update_settings(RootScreenType::RootScreenNetworkChooser)
-                    .ok();
+                if let Err(e) = self.current_app_context()
+                    .update_settings(RootScreenType::RootScreenNetworkChooser) {
+                    self.visible_screen_mut().display_message(
+                        "Failed to save network settings",
+                        MessageType::Error
+                    );
+                }
            }
  • The error message should be simplified as the detailed error might expose internal implementation details
  • The display_message method exists through the ScreenLike trait
  • The suggested error handling pattern aligns with the existing MessageType enum that includes Error variant
🔗 Analysis chain

Enhance network switching error handling and user feedback.

The current implementation silently ignores potential errors during settings update with .ok(). This could lead to a confusing user experience if the settings update fails.

Consider this improvement:

             AppAction::SwitchNetwork(network) => {
                 self.change_network(network);
-                self.current_app_context()
-                    .update_settings(RootScreenType::RootScreenNetworkChooser)
-                    .ok();
+                if let Err(e) = self.current_app_context()
+                    .update_settings(RootScreenType::RootScreenNetworkChooser) {
+                    self.visible_screen_mut()
+                        .display_message(&format!("Failed to update network settings: {}", e), MessageType::Error);
+                }
             }

Let's verify the network switching implementation:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if change_network handles all network types
ast-grep --pattern 'fn change_network($params) {
  $$$
}'

# Verify if update_settings is properly implemented
ast-grep --pattern 'fn update_settings($params) {
  $$$
}'

Length of output: 272


Script:

#!/bin/bash
# Search for change_network method implementation
rg "fn change_network" -A 10

# Search for update_settings method implementation
rg "fn update_settings" -A 10

# Search for any error handling patterns in network switching
rg "SwitchNetwork" -A 10

Length of output: 3666


Script:

#!/bin/bash
# Check if there's any error handling or user feedback mechanism in the visible_screen_mut method
rg "fn visible_screen_mut" -A 10

# Check if there's any error type definition for update_settings
rg "insert_or_update_settings" -A 10

# Look for MessageType enum definition
rg "enum MessageType" -A 10

Length of output: 2193

Comment on lines +12 to +28
pub fn initialize(&self, db_file_path: &Path) -> rusqlite::Result<()> {
// Check if this is the first time setup by looking for entries in the settings table.
if self.is_first_time_setup()? {
self.create_tables()?;
self.set_default_version()?;
} else {
// Perform version check and back up and recreate the database if outdated.
if self.is_outdated()? {
self.backup_and_recreate_db(db_file_path)?;
self.create_tables()?;
self.set_default_version()?;
println!("Database reinitialized with default settings.");
}
}

Ok(())
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Ensure proper error handling in initialize method

The initialize method handles different scenarios for database setup and versioning. However, consider handling potential errors more robustly, especially when calling create_tables() and set_default_version() methods. If these methods fail, it might leave the database in an inconsistent state. You could implement a transaction to ensure atomicity of database initialization operations.

Comment on lines +211 to +226
"CREATE TABLE IF NOT EXISTS identity (
id BLOB PRIMARY KEY,
data BLOB,
is_in_creation INTEGER NOT NULL DEFAULT 0,
is_local INTEGER NOT NULL,
alias TEXT,
info TEXT,
wallet BLOB,
wallet_index INTEGER,
identity_type TEXT,
network TEXT NOT NULL,
CHECK ((wallet IS NOT NULL AND wallet_index IS NOT NULL) OR (wallet IS NULL AND wallet_index IS NULL)),
FOREIGN KEY (wallet) REFERENCES wallet(seed_hash) ON DELETE CASCADE
)",
[],
)?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Define foreign key constraints explicitly

In the identity table, you're referencing the wallet table via wallet(seed_hash). Ensure that the foreign key constraints are correctly enforced and consider adding ON UPDATE CASCADE or ON DELETE CASCADE as appropriate.

Comment on lines +291 to +299
fn set_default_version(&self) -> rusqlite::Result<()> {
self.execute(
"INSERT INTO settings (id, network, start_root_screen, database_version)
VALUES (1, ?, 0, ?)
ON CONFLICT(id) DO UPDATE SET database_version = excluded.database_version",
params![DEFAULT_NETWORK, DEFAULT_DB_VERSION],
)?;
Ok(())
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Prevent potential conflicts in set_default_version

The INSERT ... ON CONFLICT statement updates the database_version, but it also attempts to insert default values for other columns, which might not be desired if those columns have been modified. Consider updating only the database_version to avoid overwriting other settings.

Apply this fix to update only the database_version:

 self.execute(
     "INSERT INTO settings (id, database_version)
      VALUES (1, ?)
      ON CONFLICT(id) DO UPDATE SET database_version = excluded.database_version",
     params![DEFAULT_DB_VERSION],
 )?;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn set_default_version(&self) -> rusqlite::Result<()> {
self.execute(
"INSERT INTO settings (id, network, start_root_screen, database_version)
VALUES (1, ?, 0, ?)
ON CONFLICT(id) DO UPDATE SET database_version = excluded.database_version",
params![DEFAULT_NETWORK, DEFAULT_DB_VERSION],
)?;
Ok(())
}
fn set_default_version(&self) -> rusqlite::Result<()> {
self.execute(
"INSERT INTO settings (id, database_version)
VALUES (1, ?)
ON CONFLICT(id) DO UPDATE SET database_version = excluded.database_version",
params![DEFAULT_DB_VERSION],
)?;
Ok(())
}

@QuantumExplorer QuantumExplorer merged commit 732a91c into v0.2-dev Nov 11, 2024
@QuantumExplorer QuantumExplorer deleted the feat/database-version-handling branch November 11, 2024 11:07
@coderabbitai coderabbitai Bot mentioned this pull request Nov 26, 2024
This was referenced Dec 12, 2024
lklimek added a commit that referenced this pull request Mar 25, 2026
Bot issues fixed:
- #35: WalletNotFound stores only the identifier, not full error sentence
- #36: MCP log differentiates unset vs invalid MCP_API_KEY
- #37: Release workflow splits GUI and CLI builds (GUI without --features cli,
  CLI with --features headless --bin det-cli)
- #38: SendCoreFunds validates empty network string before require_network
- #39: Remove unnecessary ensure_spv_synced() from QueryWithdrawals
- #40: Remove input_schema() override from NetworkTool

Human issues addressed:
- #41: Tool table includes det-cli command column
- #42: CLI section added to MCP.md
- #43: Headless mode performance characteristics documented
- #45: Authentication section with setup, behavior, and security guidance
- #46: ListWalletsTool moved from network.rs to wallet.rs
- #47: TODO added for withdrawal pagination (requires backend changes)
- #48: TODO added for structured JSON output (requires new backend variant)
- #49: Verified correct BackendTask dispatch for withdrawals

Issue #44 (CLAUDE.md duplication): no actual duplication found — the MCP
section in CLAUDE.md covers architecture guidance while docs/MCP.md covers
usage. Both serve distinct purposes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
lklimek added a commit that referenced this pull request Mar 25, 2026
* feat: add MCP HTTP server for programmatic access

Embed a streamable-HTTP MCP server (rmcp + axum) that exposes backend
tasks as tools for AI agents. Feature-gated behind `mcp` feature flag
and opt-in via MCP_API_KEY environment variable.

Tools: list_wallets, generate_receive_address, list_core_wallets.
Auth: Bearer token with constant-time comparison (subtle crate).
Supports network switching -- MCP context follows GUI network changes
via ArcSwap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: support dual MCP transport (stdio + HTTP) with lazy init

Refactor MCP module to support two feature-gated transports:
- mcp-stdio: standalone binary with lazy AppContext initialization
- mcp-http: embedded HTTP server sharing GUI app's context

New binary: dash-evo-tool-mcp (requires mcp-stdio feature).
Neither transport is enabled by default.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add det-cli MCP client with tool caching and completion

Dynamic CLI client that discovers tools from the MCP server at runtime.
Supports HTTP transport (default) and standalone mode (--standalone).
Caches tool schemas locally for shell completion.

New binary: det-cli (requires `cli` feature).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add MCP and CLI quick start guides

Add docs/MCP.md covering the HTTP and stdio transport modes, available tools,
environment variables, and security notes. Add docs/CLI.md covering det-cli
connection modes, subcommands, and shell completion setup. Update README.md
with a Programmatic Access section linking to both guides.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(cli): read MCP config from app .env file

- Load app's .env on startup so MCP_API_KEY/MCP_LISTEN are available
  without separate DET_CLI_BEARER setup
- Replace DET_CLI_BEARER with MCP_API_KEY (matches server config)
- Derive server URL from MCP_LISTEN env var; fall back to 127.0.0.1:9527
- Auto-select stdio mode when no API key is configured; HTTP when set
- Make subcommand optional: bare `det-cli` lists tools live
- Inject cached tool list into --help output via print_cached_tools_help()
- Update docs/CLI.md to reflect new config and connection behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(cli): implicit versioned cache, remove Cache subcommand

- Remove explicit `det-cli cache` subcommand; tool list is cached
  automatically after every `tools` invocation
- Add ToolCache struct with version field; cache includes server version
  from MCP peer info (falls back to PKG_VERSION)
- Cache staleness: --help only shows cached tools when version matches
  the binary; stale cache prompts user to run `det-cli` to refresh
- Add `#[command(version)]` so `det-cli --version` works
- Fix server info access: use `client.peer().peer_info()` (correct rmcp
  API) instead of non-existent `peer_server_info()`
- Update bash completion jq path to `.tools[].name` for new cache format
- Update docs/CLI.md to reflect automatic caching behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ci(release): build and package det-cli binary alongside dash-evo-tool

Add --features cli to all release build commands and include the det-cli
binary in release packages (zip for Linux/Windows, .app bundle for macOS).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: consolidate MCP features and embed stdio server in det-cli

Simplify from 3 features (mcp-stdio, mcp-http, cli) to 2:
- `mcp`: HTTP server embedded in GUI app
- `cli`: det-cli binary with in-process MCP and `serve` subcommand

Remove standalone `dash-evo-tool-mcp` binary — `det-cli serve` replaces
it as the MCP stdio server for Claude Desktop and AI agents.

Update docs (MCP.md, CLI.md, CONTRIBUTING.md) to reflect new layout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: expand feature flags documentation in CONTRIBUTING.md

Add binary output mapping, dependency details, CI usage table,
and combination examples for all cargo features.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: remove redundant build examples from feature flags section

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: remove dependencies section from feature flags

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: remove CI feature usage section

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(cli): auto-install bash completion on cache refresh

Write bash completion script to ~/.local/share/bash-completion/completions/det-cli
whenever the tool cache is rebuilt. bash-completion 2.x auto-loads from this
directory — no .bashrc editing or manual setup needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(cli): direct tool subcommands with custom help

- Remove `call` subcommand — tools are now first-class subcommands
  (e.g. `det-cli list-wallets` instead of `det-cli call list_wallets`)
- Use hyphens in command and parameter names (CLI convention)
- Custom help output shows all available tools as primary content,
  with management commands and options below
- Hyphen-to-underscore conversion for MCP tool/param names

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: update CLI.md for direct subcommand UX

Rewrite examples and usage to reflect hyphenated commands
(det-cli list-wallets), remove call subcommand references,
document auto-installed bash completion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(cli): unify help output for bare invocation and --help

Both `det-cli` (no args) and `det-cli --help` now display the same
format. Bare invocation queries the server for fresh tools first;
--help uses the disk cache for instant response.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(cli): replace MCP_NETWORK with --network flag and database default

Network selection now follows the same pattern as the GUI:
read from the database (last selected network), with --network
flag as override. Removes the MCP_NETWORK env var.

Also adds short flags: -n (network), -s (standalone), -a (addr),
-b (bearer).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(mcp): remove list_core_wallets tool — MCP is SPV-only

Core wallet operations require a local Dash Core node, which conflicts
with the MCP server's SPV-only design. Remove the tool and all
references from docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(cli): show meaningful errors when AppContext init fails

- Add tracing subscriber (stderr, warn level) for non-serve CLI paths
  so tracing::error!() from AppContext::new() is visible to users
- Pre-validate network config exists before attempting AppContext init,
  giving a clear error like "no configuration found for network 'Dash'.
  Check your .env file has MAINNET_dapi_addresses set."
- Fall through message now says "check logs for details" instead of
  the opaque "failed to create AppContext"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(mcp): add network tool, remove --network CLI flag

Replace the --network CLI flag with a proper `network` MCP tool that
returns the active network and available configured networks as JSON.
This keeps det-cli as a thin client with no local logic — network
info comes from the server, same as all other operations.

Changes:
- Add `network` tool to DashMcpService (active + available networks)
- Remove --network flag from det-cli
- Remove network_override threading (ContextProvider::Lazy, new_lazy,
  init_app_context, start_stdio)
- Simplify init_app_context to use DB default only
- Add available_network_names() and network_display_name() helpers
- Make AppContext.network and data_dir pub (needed by MCP tools)
- Update docs/CLI.md and docs/MCP.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: silently skip unconfigured networks in Config loading

Missing network configs are normal — not every user configures all four
networks. Remove tracing::error! spam and the hard mainnet requirement.
Only fail when no networks are configured at all. The actual network
availability check happens in init_app_context where it matters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(cli): clean error messages and network tool fixes

- Handle rmcp ServiceError with typed matching instead of string parsing.
  McpError variant extracts message + code directly from ErrorData fields.
- Suppress rmcp service-level tracing warnings (duplicates handled errors)
- Revert network tool to use AppContext (correct for embedded mode —
  needs working config to be useful)
- Make AppContext.network and data_dir pub (needed by MCP tools)
- Add collect_available() helper for listing configured networks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(mcp): start SPV and wait for wallets in standalone mode

In CLI/stdio mode, init_app_context now starts the SPV client after
creating AppContext so wallet operations work. The init returns
immediately — tools like list-wallets and network are not blocked.

The generate-receive-address tool polls ConnectionStatus via
trigger_refresh() until SPV reaches Synced state (60s timeout),
then verifies the specific wallet is loaded into SPV's det_wallets
map before dispatching the backend task.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(cli): add per-tool --help support

Intercept --help/-h in tool subcommand args and print tool-specific
help from the disk cache instead of forwarding to the MCP server.
Uses cached tool metadata to show description, usage, and parameters
with required/optional markers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore(cli): bump SPV timeout to 600s, disable logs by default

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(mcp): add wallet_balances and send_core_funds tools

Add two new MCP tools:
- wallet_balances: read total/confirmed/unconfirmed balances in duffs
- send_core_funds: send DASH from a wallet to an address via CoreTask

Extract SPV wait loop from generate_receive_address into a reusable
wait_for_spv_sync() helper shared by both SPV-dependent tools.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(cli): use wrapper pattern for generate_completion bash output

The generate_completion() function was emitting a standalone _det_cli_tools
function with its own `complete -F` registration, overriding clap's built-in
completions. Now uses the same _det_cli_wrapper pattern as install_bash_completion()
— calls clap's _det-cli first, then adds dynamic tool/parameter names.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(mcp): wait for SPV sync before reading wallet balances

wallet_balances was returning stale data because it read in-memory state
without waiting for SPV to finish syncing. Now waits for green state like
the other wallet-dependent tools.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(cli): split det_cli into modules and add headless feature

Split the 546-line single-file binary into a multi-file directory:
main.rs, connect.rs, cache.rs, completion.rs, help.rs.

Add `headless` feature flag (= ["cli", "mcp"]) and `det-cli headless`
command that runs as a standalone HTTP MCP server daemon with bearer
auth, ctrl+c graceful shutdown, and eager AppContext initialization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(mcp): migrate tools from proc-macro to trait-based ToolBase/AsyncTool pattern

Replace #[tool_router]/#[tool] proc-macro approach with rmcp's
ToolBase + AsyncTool traits. Each tool is now a standalone struct
in its own domain file, enabling auto-generated schemas, typed
output with structuredContent, and per-tool annotations.

New files:
- error.rs: McpToolError enum with From<McpToolError> for ErrorData
- resolve.rs: wallet resolution and SPV sync helpers (extracted)
- tools/mod.rs: shared param types (WalletIdParams, EmptyParams, etc.)
- tools/network.rs: NetworkTool, ListWalletsTool
- tools/wallet.rs: GenerateReceiveAddress, WalletBalancesQuery,
  SendCoreFunds, FetchPlatformBalances (new)
- tools/meta.rs: DescribeTool

Also cleans up dispatch.rs (removes resolve_wallet/task_error_to_mcp)
and simplifies server.rs (manual ServerHandler impl).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(mcp): add query_withdrawals tool for Platform withdrawal queries

Wraps existing PlatformInfoTask backend (CurrentWithdrawalsInQueue,
RecentlyCompletedWithdrawals) as a new MCP tool. Accepts status
parameter: "queued" (default) or "completed".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(mcp): wait for SPV sync in query_withdrawals tool

Platform queries require proof verification which needs SPV to be
initialized. Without the sync wait, standalone CLI mode fails with
"SPV client not initialized" after exhausting retries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(mcp): override bare `true` in describe_tool outputSchema

schemars v1 derives bare `true` for serde_json::Value fields, which
Claude Code's MCP client rejects during tools/list schema validation.
Use `#[schemars(transform)]` to emit `{"type": "object"}` instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(mcp): add network verification guard to tools

Add an optional `network` parameter to wallet, platform, and listing
tools so MCP clients can assert the expected network before executing.
If the caller provides a network name that does not match the server's
active network, the request is rejected with an InvalidParams error.

Tools guarded: generate_receive_address, wallet_balances,
fetch_platform_address_balances, send_core_funds, query_withdrawals,
list_wallets.

Exempt (by design): network (discovery tool), describe_tool (meta).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: update MCP, CLI, and CONTRIBUTING for current tool set

- MCP.md: add all 8 tools to table (fetch_platform_address_balances,
  query_withdrawals, describe_tool were missing); add network verification
  section; add headless mode section; add Claude Code configuration example
- CLI.md: add examples for query-withdrawals, fetch-platform-address-balances,
  describe-tool, and network guard usage; document headless subcommand
- CONTRIBUTING.md: add headless feature to feature flags table

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(mcp): rename tools to domain_object_action convention

Adopt consistent naming: core_* for wallet/address operations,
platform_* for Platform queries, tool_* and network_* for meta/infra.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add MCP/CLI architecture decisions to CLAUDE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve merge artifacts after v1.0-dev merge

Network::Dash was renamed to Network::Mainnet in dashcore; update
two remaining references in src/mcp/server.rs that were missed during
the v1.0-dev merge.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Update src/mcp/server.rs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix(mcp): typed McpToolError, input validation, mandatory network on send

Replace String-based McpToolError with typed thiserror variants
(WalletNotFound, InvalidParam, NetworkMismatch, SpvSyncFailed,
TaskFailed, Internal) each mapped to a distinct MCP error code.

Add input validation at the MCP layer for SendCoreFunds: reject
zero amounts, validate Dash address prefix format, and require the
'network' parameter on destructive operations to prevent accidental
cross-network transfers.

Add INTENTIONAL(PROJ-003) annotation for tool_describe naming.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(mcp): auth hardening, API key minimum length, dispatch comment

- Enforce minimum 16-character MCP_API_KEY at server startup; refuse
  to start and log error if too short.
- Return JSON body {"error": "unauthorized"} on 401 instead of bare
  status code.
- Log failed auth attempts at warn level with client address (never
  log the attempted key).
- Add ConnectInfo<SocketAddr> extraction for auth logging.
- Document why the dispatch channel receiver is intentionally dropped
  in headless/CLI mode.
- Fix collapsible_if clippy lint in server.rs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: pub(crate) AppContext fields, deduplicate bash completion

- Change AppContext.data_dir and .network from pub to pub(crate) and
  add data_dir()/network() accessor methods for external consumers.
- Extract the bash completion wrapper script to a shared constant in
  completion.rs, eliminating duplication between generate and install.
- Document TOCTOU-safe pattern in DashMcpService::ctx() docstring
  (each tool calls ctx() exactly once and passes the Arc snapshot).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(config): log parse failures instead of silently swallowing them

Add tracing::debug! before .ok() when parsing per-network configs
from environment variables, so parse errors are visible in logs
instead of silently falling through to "no config found".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(mcp): add unit tests for MCP validation layer

15 tests covering:
- API key length validation (too short rejected, 16+ accepted, empty)
- Amount validation (zero rejected, positive accepted)
- Address format validation (empty, valid mainnet/testnet, invalid prefix)
- McpToolError Display output for all variants
- MCP error code distinctness for custom codes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: headless build note, feature flag deps, MCP user stories

- Add note in MCP.md that headless mode requires building from source.
- Fix CONTRIBUTING.md feature flag table: clarify headless depends on
  cli + mcp rather than claiming all features are independent.
- Add MCP-001 and MCP-002 user stories for CLI wallet management and
  AI agent MCP server access.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add guide for exposing backend tasks via MCP/CLI

Add docs/EXPOSING_BACKEND_TASKS.md — a concise reference with architecture
rules, step-by-step checklist, and don'ts for adding new MCP tools that
wrap BackendTask variants. Cross-reference from CLAUDE.md and CONTRIBUTING.md.

https://claude.ai/code/session_01D4TGENSgE6fFCrXn5iPMnD

* fix: address review findings from CodeRabbit and Copilot

- Fix jq query in bash completion: use `input_schema` (snake_case)
  instead of `inputSchema` (camelCase) to match rmcp serialization
- Validate hex seed hash exists in wallet map before accepting it,
  preventing misleading errors for non-existent wallets
- Version-check cache in print_tool_help() to reject stale entries
- Add headless subcommand to custom help output (cfg-gated)
- Return non-zero exit for unknown tool help lookups
- Add Accept: application/json header to MCP curl examples

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address 7 PR review findings for MCP server

- docs/MCP.md: mark `network` as required for `core_funds_send`
- docs/CLI.md: add `network=testnet` to send example, remove misleading
  "optional" example
- src/app.rs: initialize MCP HTTP server with context matching
  `chosen_network` instead of hardcoded mainnet
- src/mcp/tools/wallet.rs: make `SendFundsParams.network` a non-optional
  `String` to match runtime requirement
- docs/EXPOSING_BACKEND_TASKS.md: fix typo `InvalidParams` → `InvalidParam`
- CLAUDE.md: fix same `InvalidParams` → `InvalidParam` typo
- src/mcp/tests.rs: remove env var tests that race with parallel tests
  due to unsynchronized `set_var`/`remove_var`

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR #773 review comments across MCP server

Bot issues fixed:
- #35: WalletNotFound stores only the identifier, not full error sentence
- #36: MCP log differentiates unset vs invalid MCP_API_KEY
- #37: Release workflow splits GUI and CLI builds (GUI without --features cli,
  CLI with --features headless --bin det-cli)
- #38: SendCoreFunds validates empty network string before require_network
- #39: Remove unnecessary ensure_spv_synced() from QueryWithdrawals
- #40: Remove input_schema() override from NetworkTool

Human issues addressed:
- #41: Tool table includes det-cli command column
- #42: CLI section added to MCP.md
- #43: Headless mode performance characteristics documented
- #45: Authentication section with setup, behavior, and security guidance
- #46: ListWalletsTool moved from network.rs to wallet.rs
- #47: TODO added for withdrawal pagination (requires backend changes)
- #48: TODO added for structured JSON output (requires new backend variant)
- #49: Verified correct BackendTask dispatch for withdrawals

Issue #44 (CLAUDE.md duplication): no actual duplication found — the MCP
section in CLAUDE.md covers architecture guidance while docs/MCP.md covers
usage. Both serve distinct purposes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants