[3.5.5] - 2026-04-19
MCP Agent Parity
Sprint 1 of the MCP vs CLI parity plan. The AeroFTP MCP server gains three new tools so Claude Code, Claude Desktop, Cursor, and Windsurf clients no longer need to fall back to Bash for sync-class operations, and every long-running transfer now streams real-time notifications/progress. Warm-call latency drops ~14x vs CLI cold-start thanks to pool reuse (13-14 ms via MCP vs ~194 ms via CLI on a local SFTP target).
This release also unbreaks the main branch CI by committing the seed_test_profiles helper that v3.5.4 referenced in Cargo.toml but forgot to stage.
Added (MCP parity Sprint 1, 2026-04-19)
- MCP progress notifications:
aeroftp_upload_file,aeroftp_download_fileandaeroftp_sync_treenow emitnotifications/progresswhen the caller supplies aprogressTokenvia_meta.progressToken. Throttled to ~10 Hz with a guaranteed final flush. Newmcp/notifier.rsmodule. - MCP
aeroftp_sync_treetool: synchronise a local tree with a remote tree. Supports direction (upload/download/both), dry_run, delete_orphans (upload/download), conflict modes (larger/newer/skip), glob excludes, max_depth. - MCP
aeroftp_check_treetool: compare a local tree with a remote tree and reportmatch/differ/missing_local/missing_remotegroups. Supports one_way, SHA-256 checksums, glob excludes. - MCP
aeroftp_close_connectiontool: explicitly close a pooled connection by server name or id. Useful to free resources or force a fresh handshake. - MCP pool introspection:
aeroftp://connectionsresource now returns full pool metadata per connection (profile_id,name,protocol,state,idle_secs,connected_at,requests_served) plusmax_pool_sizeandidle_timeout_secson the envelope. - Shared
sync_coremodule (src-tauri/src/sync_core/): newscan_local_tree,scan_remote_tree,compare_trees, andsync_tree_corehelpers.cmd_checkandcmd_reconcilein the CLI now delegate to this core, so the CLI and MCP front-ends share a single scan/compare implementation (15 unit tests inscan.rs+compare.rs+sync.rs). - CLI benchmark vs MCP: on warm calls MCP pool reuse delivers a measured ~14x speedup over CLI cold-start on Docker SFTP (
ls /in 13-14 ms via MCP vs ~194 ms via CLI). Seedocs/dev/test-workspace/runs/2026-04-19-mcp-parity-sprint-1/REPORT.md.
Net MCP parity coverage moves from ~40% of CLI capabilities to ~70%; the killer feature (progress notifications) is now live.
Fixed
- CI main branch unbroken: commit
763b3253ships theseed_test_profilessource file that v3.5.4 referenced insrc-tauri/Cargo.tomlbut forgot to stage. Every Build AeroFTP / Strada C Prototype / Delta Sync Integration run on main from 2026-04-18 to 2026-04-19 failed during cargo target resolution withcan't find bin 'seed_test_profiles'. The file is a local test helper that seeds six Docker-harness profiles (FTP, FTPS, SFTP ed25519/RSA, WebDAV, MinIO) into the encrypted vault for reproducible live-battery runs. All passwords are test literals; zero production credentials. Run withcargo run --bin seed_test_profilesafter opening the vault.
Added (Strada C scaffolding — experimental, dormant)
- Native rsync delta transport (scaffolding only): built-in Rust implementation of rsync protocol 31 wired into the
DeltaTransporttrait. Compiled only when theproto_native_rsyncCargo feature is enabled (off by default in shipped builds). The runtime toggle lives at Settings > Advanced Sync > "Enable native rsync for SFTP delta sync" and renders only on Unix feature-on builds. Not yet routed by the sync executor:provider_transfer_executorstill callsprovider.download/uploaddirectly; wiringdelta_sync_rsync::try_delta_transferinto the executor is a follow-up sinergia. Enabling the toggle persists the opt-in but does not change transfer behaviour today. - Native SSH host-key pinning: when the native leg becomes reachable, it pins the SHA-256 fingerprint captured by the parent SFTP handshake (
SshHandler::check_server_key). Sessions without a captured fingerprint refuse to open the native path (secure default, closes the MITM window thatAcceptAnyleft on a fresh TCP socket). - Fallback policy:
RsyncError::HardRejection+DeltaSyncResult::hard_errorsurface security or invariant failures to the UI with no silent classic fallback. Pre-rename atomic-write failures and environmental errors fall back transparently to classic; rename-stage or host-key failures surface as hard errors.
In progress — Delta Sync Fase 1 (SFTP via rsync-over-SSH)
First concrete step toward the v3.6.0 "SFTP delta flagship" release. The long-standing phantom deltaSyncEnabled toggle in SyncPanel.tsx (a UI switch with no backend wiring) is being replaced with a real implementation: on SFTP connections with a local rsync binary and a rsync-capable server, transfers fall through to a managed rsync -e ssh invocation that delivers real bandwidth savings measured against the network, not simulated.
This entry tracks infrastructure already landed on main. Nothing here is user-visible yet; the sync loop wiring and the UI honest-gate ship together in a later commit.
Added (foundations, not yet wired)
rsync_output.rs: locale-tolerant parser for rsync 3.x stdout (Progress,FileStart,Summary,Warning,Errorevents). 15 unit tests covering real fixtures and edge cases (clamped percentages, partial summary lines, banner-line filtering)ssh_exec.rs: generic exec primitive over a shared russhHandlewith multiplexed stdout/stderr mpsc streams, oneshot exit code, and RAII cleanup of the reader task. Used for remote probing today; reusable for future AI agent tools and port-forwarding use casesrsync_over_ssh.rs(Unix): orchestrator for the localrsyncsubprocess with SSH transport. Handles remote probing viassh_exec, local binary probing,ssh -e ...argument assembly with shell-escaped key paths, subprocess spawn with stdout parsing, and typed errors for every fallback path. 9 unit testsdelta_sync_rsync.rs(Unix): thin adapter the sync loop will call instead ofprovider.download()/provider.upload(). 5-minute per-session capability cache, typedDeltaSyncResult { used_delta, stats, fallback_reason }so the loop can always report why delta wasn't used. 5 unit testsdelta_transport.rs(Unix):DeltaTransporttrait (object-safe, async viaasync_trait) that abstracts the transport mechanism behind delta sync. Today's only implementation,RsyncBinaryTransport, wraps the subprocess path; the future native-rsync protocol (strada C) plugs in as a second implementation with zero churn in the adapter or the sync loopproviders::sftp::SharedSshHandle+handle_shared()getter: exposes the authenticated russh handle forssh_execreuse without reopening a second SSH connectionSftpProvider::delta_transport()factory +try_delta_transfer()helper: single choke point where a connected SFTP session becomes aBox<dyn DeltaTransport>, and a public entry point the executor will call from one line. Uses the existingas_any_mut()trait escape hatch — zero new methods onStorageProvider
Added (test infrastructure)
- Docker fixture
tests/fixtures/sftp-rsync/: alpine + openssh + rsync on127.0.0.1:2222. Single-command bring-up (setup.sh && docker compose up -d --build). Honest security posture —StrictModes yespreserved via an entrypoint script that normalizes ownership and mode on the bind-mounted authorized_keys tests/integration_delta_sync.rs:#[ignore]-gated live checks that verify the fixture has rsync and prove delta savings on a redundant upload (65 bytes sent for a 2 MiB identical file → 99.997% bandwidth reduction, validated against the local fixture).github/workflows/delta-sync-integration.yml: path-scoped GitHub Actions job that brings up the fixture, runs the unit tests for every new module, then the live integration test. Only triggers when delta-sync files change, so unrelated PRs don't pay the cost. Pinned to the same action SHAs used bybuild.yml
Fixed
- Locale-dependent rsync parsing:
rsync_output.rsused to requireLC_NUMERIC=C LC_ALL=Con the child process so it only saw en_US-formatted numbers ("1,048,576" / "1.00"); any non-POSIX locale would silently produce wrong counters. Replaced with a newnumber_parsingmodule whoseparse_u64_loose/parse_f64_loosedecide per input which of.or,is decimal vs thousands, using deterministic rules valid for every rsync locale we have observed (POSIX, en_US, it_IT, de_DE, fr_FR). Regexes widened from[\d,]+to[\d.,]+. The LC_* override was removed, so rsync inherits the caller's locale and any future translated messages stay in the user's language. Integration test validated live underLANG=it_IT.UTF-8andLC_ALL=Cwith identical result (65 bytes transferred on a redundant 2 MiB file)
Behaviour
- No user-visible change yet. The phantom toggle still does nothing; that fix lands together with the honest UI gate (tooltip "rsync not detected on server" when the probe fails)
- Foundations carry module-level
#![allow(dead_code)]attributes pointing at the wiring task; they are removed when the sync loop starts callingtransfer_with_delta
Forward-compatibility with the native rsync (strada C)
The public API of rsync_over_ssh intentionally exposes no subprocess types (Child, Command, stdio pipes) — only RsyncCapability, RsyncConfig, RsyncStats, RsyncError, rsync_download(), rsync_upload(). When the Rust-native rsync protocol implementation (multi-month Strada C effort) is ready, the internal transport flips from tokio::process::Command to an in-process Rust state machine without any change to callers. delta_sync_rsync and sync.rs stay untouched.
Downloads:
- Windows:
.msiinstaller,.exe, or.zipportable (no installation required) - macOS:
.dmgdisk image - Linux:
.deb,.rpm,.snap, or.AppImage