Compare commits

...
75 Commits
Author SHA1 Message Date
DevidandGitHub 2a6cd8015a Merge pull request #16 from masdevid/dependabot/github_actions/github-actions-af0bba1cf7
chore(deps): bump the github-actions group with 5 updates
2026-10-14 10:01:31 +07:00
DevidandGitHub 4216d704cd Merge pull request #18 from masdevid/dependabot/cargo/minor-and-patch-335c3d52a5
chore(deps): bump the minor-and-patch group across 1 directory with 4 updates
2026-10-14 10:01:09 +07:00
dependabot[bot]andGitHub 9ddabff95a chore(deps): bump the minor-and-patch group across 1 directory with 4 updates
Bumps the minor-and-patch group with 4 updates in the / directory: [tokio](https://github.com/tokio-rs/tokio), [clap](https://github.com/clap-rs/clap), [uuid](https://github.com/uuid-rs/uuid) and [encoding_rs](https://github.com/hsivonen/encoding_rs).


Updates `tokio` from 1.53.1 to 1.53.2
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.53.1...tokio-1.53.2)

Updates `clap` from 4.6.6 to 4.6.7
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.6.6...clap_complete-v4.6.7)

Updates `uuid` from 1.26.1 to 1.27.0
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/v1.26.1...v1.27.0)

Updates `encoding_rs` from 0.8.41 to 0.8.42
- [Commits](https://github.com/hsivonen/encoding_rs/compare/v0.8.41...v0.8.42)

---
updated-dependencies:
- dependency-name: tokio
  dependency-version: 1.53.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
- dependency-name: clap
  dependency-version: 4.6.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
- dependency-name: uuid
  dependency-version: 1.27.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: encoding_rs
  dependency-version: 0.8.42
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-07 12:14:45 +00:00
dependabot[bot]andGitHub 7300124445 chore(deps): bump the github-actions group with 5 updates
Bumps the github-actions group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [github/codeql-action/init](https://github.com/github/codeql-action) | `4.38.0` | `4.38.1` |
| [github/codeql-action/autobuild](https://github.com/github/codeql-action) | `4.38.0` | `4.38.1` |
| [github/codeql-action/analyze](https://github.com/github/codeql-action) | `4.38.0` | `4.38.1` |
| [dtolnay/rust-toolchain](https://github.com/dtolnay/rust-toolchain) | `6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772` | `02cb101ec7c40f2c49e1d9714d64511d8e1b74de` |
| [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) | `4.38.0` | `4.38.1` |


Updates `github/codeql-action/init` from 4.38.0 to 4.38.1
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd)

Updates `github/codeql-action/autobuild` from 4.38.0 to 4.38.1
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd)

Updates `github/codeql-action/analyze` from 4.38.0 to 4.38.1
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd)

Updates `dtolnay/rust-toolchain` from 6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 to 02cb101ec7c40f2c49e1d9714d64511d8e1b74de
- [Release notes](https://github.com/dtolnay/rust-toolchain/releases)
- [Commits](https://github.com/dtolnay/rust-toolchain/compare/6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772...02cb101ec7c40f2c49e1d9714d64511d8e1b74de)

Updates `github/codeql-action/upload-sarif` from 4.38.0 to 4.38.1
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.38.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.38.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.38.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: dtolnay/rust-toolchain
  dependency-version: 02cb101ec7c40f2c49e1d9714d64511d8e1b74de
  dependency-type: direct:production
  dependency-group: github-actions
- dependency-name: github/codeql-action/upload-sarif
  dependency-version: 4.38.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-23 12:19:42 +00:00
DevidandGitHub 62987f035c Merge pull request #13 from masdevid/dependabot/cargo/minor-and-patch-78de0ddba4
chore(deps): bump the minor-and-patch group with 2 updates
2026-09-18 04:17:37 +07:00
DevidandGitHub 5420713bbb Merge branch 'master' into dependabot/cargo/minor-and-patch-78de0ddba4 2026-09-18 04:16:21 +07:00
DevidandGitHub 5eab5feccb Merge pull request #14 from masdevid/dependabot/github_actions/github-actions-6b15e67bac
chore(deps): bump the github-actions group with 4 updates
2026-09-18 04:15:43 +07:00
dependabot[bot]andGitHub fdbaeb68a5 chore(deps): bump the github-actions group with 4 updates
Bumps the github-actions group with 4 updates: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/autobuild](https://github.com/github/codeql-action), [github/codeql-action/analyze](https://github.com/github/codeql-action) and [github/codeql-action/upload-sarif](https://github.com/github/codeql-action).


Updates `github/codeql-action/init` from 4.37.9 to 4.38.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/cdf488f595d80d6e07e03d4674febd5ab45fa938...b96794f015dfd88f77b49b1c93e0fa7110f94c63)

Updates `github/codeql-action/autobuild` from 4.37.9 to 4.38.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/cdf488f595d80d6e07e03d4674febd5ab45fa938...b96794f015dfd88f77b49b1c93e0fa7110f94c63)

Updates `github/codeql-action/analyze` from 4.37.9 to 4.38.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/cdf488f595d80d6e07e03d4674febd5ab45fa938...b96794f015dfd88f77b49b1c93e0fa7110f94c63)

Updates `github/codeql-action/upload-sarif` from 4.37.9 to 4.38.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/cdf488f595d80d6e07e03d4674febd5ab45fa938...b96794f015dfd88f77b49b1c93e0fa7110f94c63)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: github/codeql-action/upload-sarif
  dependency-version: 4.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-16 12:18:41 +00:00
dependabot[bot]andGitHub be41b43703 chore(deps): bump the minor-and-patch group with 2 updates
Bumps the minor-and-patch group with 2 updates: [uuid](https://github.com/uuid-rs/uuid) and [encoding_rs](https://github.com/hsivonen/encoding_rs).


Updates `uuid` from 1.26.0 to 1.26.1
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/v1.26.0...v1.26.1)

Updates `encoding_rs` from 0.8.35 to 0.8.41
- [Commits](https://github.com/hsivonen/encoding_rs/compare/v0.8.35...v0.8.41)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
- dependency-name: encoding_rs
  dependency-version: 0.8.41
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-16 12:14:11 +00:00
DevidandGitHub 186b846936 Merge pull request #12 from masdevid/dependabot/cargo/dirs-7.0.0
chore(deps): bump dirs from 6.0.0 to 7.0.0
2026-09-10 03:06:34 +07:00
DevidandGitHub b9ad93f873 Merge branch 'master' into dependabot/cargo/dirs-7.0.0 2026-09-10 03:00:31 +07:00
DevidandGitHub 16f10805f3 Merge pull request #11 from masdevid/dependabot/cargo/minor-and-patch-8d2c85db1c
chore(deps): bump uuid from 1.25.0 to 1.26.0 in the minor-and-patch group
2026-09-10 03:00:14 +07:00
dependabot[bot]andGitHub 2ee8a72fe0 chore(deps): bump dirs from 6.0.0 to 7.0.0
Bumps dirs from 6.0.0 to 7.0.0.

---
updated-dependencies:
- dependency-name: dirs
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-09 12:14:45 +00:00
dependabot[bot]andGitHub 868752d39f chore(deps): bump uuid in the minor-and-patch group
Bumps the minor-and-patch group with 1 update: [uuid](https://github.com/uuid-rs/uuid).


Updates `uuid` from 1.25.0 to 1.26.0
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.25.0...v1.26.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 1.26.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-09 12:14:42 +00:00
DevidandGitHub cc464e85ad Merge pull request #10 from masdevid/dependabot/github_actions/github-actions-7c7a268a88
chore(deps): bump the github-actions group with 5 updates
2026-09-02 19:33:44 +07:00
DevidandGitHub f87735538b Merge pull request #9 from masdevid/dependabot/cargo/minor-and-patch-0d901d6848
chore(deps): bump uuid from 1.24.1 to 1.25.0 in the minor-and-patch group
2026-09-02 19:33:39 +07:00
dependabot[bot]andGitHub af7715621d chore(deps): bump the github-actions group with 5 updates
Bumps the github-actions group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [github/codeql-action/init](https://github.com/github/codeql-action) | `4.37.8` | `4.37.9` |
| [github/codeql-action/autobuild](https://github.com/github/codeql-action) | `4.37.8` | `4.37.9` |
| [github/codeql-action/analyze](https://github.com/github/codeql-action) | `4.37.8` | `4.37.9` |
| [softprops/action-gh-release](https://github.com/softprops/action-gh-release) | `3.0.2` | `3.0.3` |
| [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) | `4.37.8` | `4.37.9` |


Updates `github/codeql-action/init` from 4.37.8 to 4.37.9
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28...cdf488f595d80d6e07e03d4674febd5ab45fa938)

Updates `github/codeql-action/autobuild` from 4.37.8 to 4.37.9
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28...cdf488f595d80d6e07e03d4674febd5ab45fa938)

Updates `github/codeql-action/analyze` from 4.37.8 to 4.37.9
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28...cdf488f595d80d6e07e03d4674febd5ab45fa938)

Updates `softprops/action-gh-release` from 3.0.2 to 3.0.3
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/3d0d9888cb7fd7b750713d6e236d1fcb99157228...efb35369e0ad2afab669f228072c1b0d510eae64)

Updates `github/codeql-action/upload-sarif` from 4.37.8 to 4.37.9
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28...cdf488f595d80d6e07e03d4674febd5ab45fa938)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.37.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.37.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.37.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: softprops/action-gh-release
  dependency-version: 3.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: github/codeql-action/upload-sarif
  dependency-version: 4.37.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-02 12:19:43 +00:00
dependabot[bot]andGitHub 6647b189ec chore(deps): bump uuid in the minor-and-patch group
Bumps the minor-and-patch group with 1 update: [uuid](https://github.com/uuid-rs/uuid).


Updates `uuid` from 1.24.1 to 1.25.0
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/v1.24.1...1.25.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 1.25.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-26 12:15:19 +00:00
Devid HW 7eace36bb6 refactor: extract CII helper into standalone masdevid/cii-best-practices skill
scripts/cii_criteria.py and scripts/cii_justifications.json moved to the
new dedicated repo, generalized for any project:

  https://github.com/masdevid/cii-best-practices

Install as an agent skill with:
  npx skills add masdevid/cii-best-practices
2026-08-26 06:28:49 +07:00
Devid HW 302a0c5624 fix(ci): move GPG signing guard out of step if: — secrets context invalid there
Referencing secrets.* in a step-level if: expression makes GitHub reject
the whole workflow file at parse time: every push to any branch produced
a startup-failure run for release.yml with zero jobs ('Code Scanning
could not...' style runs named .github/workflows/release.yml). The tags
trigger was irrelevant because an unparseable workflow has no readable
triggers.

Fix: evaluate the both-secrets-present check in job-level env (where the
secrets context IS allowed) and test env.HAS_GPG_SECRETS in the step if:.
Behavior unchanged: sign when both secrets exist, ship unsigned otherwise.
2026-08-26 05:14:50 +07:00
Devid HW 608b07cead fix(ci): satisfy rustfmt and clippy after bin+lib split
The Rust workflow lint job failed on master:
- cargo fmt --check: imports in src/main.rs were misordered
  (mcp_server/models/pipeline lines out of alphabetical order,
  McpError/McpRequest/McpResponse tuple unsorted) from b843146.
- cargo clippy -D warnings: clippy::new_without_default now fires on
  DealAnalyzer, ReportExtractor, OptimizationParser, StageExecutor
  and McpServer because the lib target (src/lib.rs) makes their
  pub constructors part of the public API.

Fix: reorder main.rs imports per rustfmt; add Default impls
delegating to new() for the five types.
2026-08-26 05:02:44 +07:00
Devid HW 6db5460b99 docs: add required URLs to legacy CII justifications (report_archive, vulnerability_report_process, contribution_requirements) 2026-08-26 04:51:29 +07:00
Devid HW fcf7ac26dc fix: use correct OSPS form-field names in CII edit URLs 2026-08-26 04:26:46 +07:00
Devid HW 09b4fbd831 feat: support OSPS Baseline schema in CII criteria script 2026-08-26 03:40:16 +07:00
Devid HW 3cbb5b00a7 ci: enable manual workflow_dispatch for Scorecard rescan 2026-08-26 03:28:40 +07:00
Devid HW 65b0346628 feat: auto-open generated bestpractices.dev URL in browser when run interactively 2026-08-26 03:26:38 +07:00
Devid HW c65c4b5d1c docs: add SECURITY.md and GPG release signing for OpenSSF passing badge
Satisfies vulnerability_report_process/know_secure_design/know_common_errors via SECURITY.md; delivery_unsigned via GPG-signed releases (public key in docs/signing-key.asc); N/A justifications for the 4 N/A-able MUST criteria (crypto_password_storage, vulnerability_report_private, vulnerability_report_response, release_notes_vulns). Backs the OpenSSF Best Practices passing badge and closes the CIIBestPracticesID alert.
2026-08-26 03:17:38 +07:00
Devid HW b843146554 test: add cargo-fuzz fuzzing harness
adds lib target + cargo-fuzz fuzz targets exercising pure parsers; backs dynamic_analysis criteria and the Scorecard fuzzing check.
2026-08-26 02:58:06 +07:00
Devid HW d8b0b37919 docs: add CONTRIBUTING.md and CII best-practices criteria helper
The scripts/cii_criteria.py helper fetches bestpractices.dev criteria
and generates pre-filled edit URLs for each criterion, streamlining
badge maintenance. CONTRIBUTING.md backs the contribution and testing
criteria referenced by the CII best-practices submission.
2026-08-26 02:38:16 +07:00
Devid HW 38d897d52f ci: trigger scorecard rescan after adding strict-status ruleset 2026-08-25 15:43:29 +07:00
Devid HW e3b04f51b6 ci: rescan scorecard after strict branch-protection settled 2026-08-25 12:00:14 +07:00
Devid HW 66f728d545 ci: trigger scorecard rescan for tightened branch-protection 2026-08-25 11:56:33 +07:00
Devid HW 06a4f8b87d ci: trigger scorecard rescan for branch-protection update 2026-08-25 11:35:51 +07:00
Devid HW ac50ae7bae ci: declare explicit least-privilege permissions (fix Token-Permissions) 2026-08-25 10:44:44 +07:00
Devid HW dece88c514 ci: trigger OpenSSF Scorecard workflow 2026-08-25 10:39:16 +07:00
Devid HW 2951f372e3 ci: enable CodeQL + add code-scanning badge 2026-08-25 10:36:18 +07:00
Devid HW 3c6da1d1d2 ci(scorecard): auto-publish badge.json to badges branch 2026-08-25 10:36:05 +07:00
Devid HW 64cdc30df7 docs(readme): use static scorecard badge (shields ossf backend broken upstream) 2026-08-25 10:30:22 +07:00
Devid HW 5c4bb2a863 docs(readme): activate CII Best Practices badge (id 14227) 2026-08-25 10:23:27 +07:00
Devid HW 5c2b4c52a0 docs(readme): add CII Best Practices badge (placeholder until enrollment) 2026-08-25 10:09:40 +07:00
Devid HW e2cdd0684b ci: pin mcp-publisher download 2026-08-25 09:50:08 +07:00
Devid HW 8a56394f1c ci(release): support RELEASE_PAT for approval-free bot PRs (falls back to GITHUB_TOKEN) 2026-08-25 09:32:18 +07:00
Devid HW 845fce7d68 docs: add documentation index and README navigation 2026-08-25 09:25:30 +07:00
Devid HW 019ddf7c21 docs(readme): copyable install instructions for major LLM clients 2026-08-25 09:23:53 +07:00
Devid HW 599dcb14ba fix(release): attest final release artifacts instead of pre-repackage builds 2026-08-25 09:13:43 +07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
89d7ff5186 ci: update server.json SHA256 for v1.34.3 (#8)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-08-25 02:03:12 +00:00
Devid HW 3baf0e3271 release: v1.34.3
- Version bump 1.34.2 → 1.34.3
- server.json identifier URL updated (SHA256 set by CI after build)
- CHANGELOG.md updated
2026-08-25 08:19:10 +07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Devid
55f072da4d chore(deps): bump dirs from 5.0.1 to 6.0.0 (#6)
* chore(deps): bump dirs from 5.0.1 to 6.0.0

Bumps [dirs](https://github.com/soc/dirs-rs) from 5.0.1 to 6.0.0.
- [Commits](https://github.com/soc/dirs-rs/commits)

---
updated-dependencies:
- dependency-name: dirs
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(deps): regenerate Cargo.lock to resolve duplicate thiserror entry

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Devid <devid.wahid@gmail.com>
2026-08-25 07:59:40 +07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e97e5bbd63 chore(deps): bump base64 from 0.22.1 to 0.23.1 (#4)
Bumps [base64](https://github.com/marshallpierce/rust-base64) from 0.22.1 to 0.23.1.
- [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md)
- [Commits](https://github.com/marshallpierce/rust-base64/compare/v0.22.1...v0.23.1)

---
updated-dependencies:
- dependency-name: base64
  dependency-version: 0.23.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 07:53:00 +07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Devid
0e35f94705 chore(deps): bump rusqlite from 0.31.0 to 0.40.2 (#5)
* chore(deps): bump rusqlite from 0.31.0 to 0.40.2

Bumps [rusqlite](https://github.com/rusqlite/rusqlite) from 0.31.0 to 0.40.2.
- [Release notes](https://github.com/rusqlite/rusqlite/releases)
- [Changelog](https://github.com/rusqlite/rusqlite/blob/master/Changelog.md)
- [Commits](https://github.com/rusqlite/rusqlite/compare/v0.31.0...v0.40.2)

---
updated-dependencies:
- dependency-name: rusqlite
  dependency-version: 0.40.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(deps): adapt to rusqlite 0.40 FromSql changes

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Devid <devid.wahid@gmail.com>
2026-08-25 07:45:55 +07:00
Devid HW d7bbaddb12 ci(release): generate build provenance attestations for artifacts 2026-08-25 07:30:12 +07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ced1913c98 chore(deps): bump the minor-and-patch group across 1 directory with 7 updates (#3)
Bumps the minor-and-patch group with 7 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [tokio](https://github.com/tokio-rs/tokio) | `1.52.1` | `1.53.1` |
| [serde](https://github.com/serde-rs/serde) | `1.0.228` | `1.0.229` |
| [serde_json](https://github.com/serde-rs/json) | `1.0.149` | `1.0.151` |
| [clap](https://github.com/clap-rs/clap) | `4.6.1` | `4.6.6` |
| [uuid](https://github.com/uuid-rs/uuid) | `1.23.1` | `1.24.1` |
| [regex](https://github.com/rust-lang/regex) | `1.12.3` | `1.13.1` |
| [chrono](https://github.com/chronotope/chrono) | `0.4.44` | `0.4.45` |



Updates `tokio` from 1.52.1 to 1.53.1
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.52.1...tokio-1.53.1)

Updates `serde` from 1.0.228 to 1.0.229
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.228...v1.0.229)

Updates `serde_json` from 1.0.149 to 1.0.151
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.149...v1.0.151)

Updates `clap` from 4.6.1 to 4.6.6
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.6.1...clap_complete-v4.6.6)

Updates `uuid` from 1.23.1 to 1.24.1
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/v1.23.1...v1.24.1)

Updates `regex` from 1.12.3 to 1.13.1
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.12.3...1.13.1)

Updates `chrono` from 0.4.44 to 0.4.45
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.44...v0.4.45)

---
updated-dependencies:
- dependency-name: chrono
  dependency-version: 0.4.45
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
- dependency-name: clap
  dependency-version: 4.6.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
- dependency-name: regex
  dependency-version: 1.13.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: serde
  dependency-version: 1.0.229
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
- dependency-name: serde_json
  dependency-version: 1.0.151
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-and-patch
- dependency-name: tokio
  dependency-version: 1.53.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: uuid
  dependency-version: 1.24.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 07:27:22 +07:00
Devid HW 71e5819d38 fix(release): drop [skip ci] from server.json PR so required checks can run 2026-08-25 06:41:20 +07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
b41f0364c5 ci: update server.json SHA256 for v1.34.2 (#7)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-08-24 23:23:07 +00:00
Devid HW e03acb108b docs(readme): fix Scorecard badge, add CI, crates.io, and license badges 2026-08-25 06:06:06 +07:00
Devid HW 785c99c80d ci(scorecard): rename invalid 'publish' input to 'publish_results' so results are published 2026-08-25 06:06:05 +07:00
Devid HW 0135ffa3b4 release: v1.34.2
- Version bump 1.34.1 → 1.34.2
- server.json identifier URL updated (SHA256 set by CI after build)
- CHANGELOG.md updated
2026-08-25 05:48:16 +07:00
Devid HW 4dded9b083 ci(release): publish to crates.io via trusted publishing (OIDC) 2026-08-25 05:46:34 +07:00
Devid HW 46e6de2474 fix: resolve clippy lints surfaced by newer toolchain in CI gate 2026-08-25 02:25:45 +07:00
Devid HW a23dbee52f fix(release): update server.json via auto-merged PR to satisfy branch ruleset 2026-08-25 02:07:38 +07:00
Devid HW ffe55863ba chore(templates): link Discussions from issue chooser 2026-08-25 01:57:22 +07:00
Devid HW ed82452200 chore: add CODEOWNERS 2026-08-25 01:52:45 +07:00
Devid HW 941be795c1 chore(templates): add issue forms and PR template 2026-08-25 01:52:17 +07:00
Devid HW 6a54b2f54a ci: gate PRs on fmt and clippy, add run concurrency groups 2026-08-25 01:50:26 +07:00
Devid HW e17e6b82fa ci(security): add OpenSSF Scorecard analysis and badge 2026-08-25 01:45:40 +07:00
Devid HW a6618e6811 ci(hardening): least-privilege GITHUB_TOKEN permissions across workflows 2026-08-25 01:45:15 +07:00
Devid HW e6111c421f ci(security): add cargo-deny checks for advisories, licenses, bans, sources 2026-08-25 01:44:40 +07:00
Devid HW 5056be3ee9 ci(security): add dependency review on PRs 2026-08-25 01:43:30 +07:00
Devid HW 059141a3d5 docs(security): add security policy 2026-08-25 01:43:15 +07:00
Devid HW 53e9cd6ee6 ci: upgrade actions to latest majors and pin by commit SHA 2026-08-25 01:37:05 +07:00
Devid HW 11e7b27fe2 chore: add MIT LICENSE file matching Cargo.toml declaration 2026-08-25 01:35:05 +07:00
Devid HW f1e4538b6a fix(ci): activate Dependabot for cargo and github-actions ecosystems 2026-08-25 01:34:25 +07:00
github-actions[bot] 415fb90f72 ci: update server.json SHA256 for v1.34.1 [skip ci] 2026-08-24 18:19:29 +00:00
Devid HW ce172a077f chore: broaden gitignore coverage for macOS metadata 2026-08-25 01:11:24 +07:00
Devid HW 9aa9bb83b7 chore: untrack AGENTS.md from version control
Keep agent instructions local-only; file remains on disk.
2026-08-25 01:02:59 +07:00
41 changed files with 2573 additions and 614 deletions
+2
View File
@@ -0,0 +1,2 @@
# Default owners for everything
* @masdevid
+71
View File
@@ -0,0 +1,71 @@
name: Bug Report
description: Report a problem with mt5-quant (backtest, compile, analytics, MCP tools)
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to report a bug. Please keep answers concise — link gists for very long logs.
- type: textarea
id: description
attributes:
label: Description
description: A clear, one-paragraph summary of the bug.
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps to Reproduce
description: Include the MT5 command or MCP tool called, with its arguments (and EA/.set inputs if relevant).
placeholder: |
1. Call tool `run_backtest` with expert=..., symbol=XAUUSDc, from_date=...
2. Observe ...
validations:
required: true
- type: textarea
id: expected_vs_actual
attributes:
label: Expected vs Actual
description: What did you expect to happen, and what actually happened?
validations:
required: true
- type: dropdown
id: os
attributes:
label: OS
options:
- macOS (Apple Silicon)
- macOS (Intel)
- Linux
validations:
required: true
- type: dropdown
id: wine_setup
attributes:
label: Wine / CrossOver setup
options:
- CrossOver (macOS)
- MetaTrader 5.app bundled Wine (macOS)
- System Wine on Linux (Xvfb headless)
- Other / not sure
validations:
required: true
- type: input
id: mt5_build
attributes:
label: MT5 terminal build
description: e.g. 4885 (Help → About in MetaTrader 5)
- type: textarea
id: logs
attributes:
label: Relevant logs
description: Tester journal, MT5 logs, or MCP server output (wrap in code fences).
render: text
+6
View File
@@ -0,0 +1,6 @@
blank_issues_enabled: false
contact_links:
- name: Questions & Discussion
url: https://github.com/masdevid/mt5-quant/discussions
about: Ask questions and discuss ideas here instead of opening an issue
@@ -0,0 +1,30 @@
name: Feature Request
description: Suggest an idea or improvement for mt5-quant
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Thanks for suggesting a feature. Start with the problem — the solution can be discussed from there.
- type: textarea
id: problem
attributes:
label: Problem Statement
description: What are you trying to do, and what is missing or painful today? Name the MCP tool(s) involved, if any.
validations:
required: true
- type: textarea
id: proposed_solution
attributes:
label: Proposed Solution
description: What would you like to happen? Include rough tool names/arguments if it affects the MCP surface.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives Considered
description: Other approaches, workarounds you've tried, or existing tools that almost fit.
+18
View File
@@ -0,0 +1,18 @@
## What & Why
<!-- Summarize the change and why it is needed. Link related issues. -->
## How Tested
- [ ] `cargo test` passes
- [ ] `cargo clippy --all-targets -- -D warnings` passes clean
- [ ] `cargo fmt --all -- --check` passes
- [ ] Verified against MT5/Wine locally (only if compile/backtest behavior changed)
## MCP Tool Impact
- [ ] No tool schema changes (if schemas changed, list affected tools below)
- [ ] Existing tool outputs unchanged (no breaking changes)
- [ ] Any new tool wired into definitions, handler, and dispatch table
<!-- Affected MCP tools: -->
+17 -6
View File
@@ -1,11 +1,22 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# Dependabot version updates: weekly grouped updates for Cargo dependencies
# and GitHub Actions.
# Configuration reference:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "" # See documentation for possible values
directory: "/" # Location of package manifests
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "monthly"
interval: "weekly"
groups:
minor-and-patch:
update-types: ["minor", "patch"]
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
groups:
github-actions:
patterns: ["*"]
+35
View File
@@ -0,0 +1,35 @@
name: CodeQL
on:
push:
branches: [master]
pull_request:
branches: [master]
schedule:
- cron: '22 4 * * 1'
workflow_dispatch:
permissions:
contents: read
jobs:
analyze:
name: Analyze (Rust)
runs-on: ubuntu-latest
permissions:
security-events: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Initialize CodeQL
uses: github/codeql-action/init@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4
with:
languages: rust
- name: Autobuild
uses: github/codeql-action/autobuild@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4
+16
View File
@@ -0,0 +1,16 @@
name: Dependency Review
on: [pull_request]
permissions:
contents: read
pull-requests: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Dependency Review
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
+239 -77
View File
@@ -11,19 +11,28 @@ on:
required: true
type: string
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }}
jobs:
# ── Build binaries ───────────────────────────────────────────────────────────
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de # v1
with:
toolchain: stable
- name: Cache Cargo
uses: actions/cache@v4
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cargo/registry
@@ -46,7 +55,7 @@ jobs:
tar -czf mcp-mt5-quant-macos-arm64.tar.gz mcp-mt5-quant-macos
- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: macos-binary
path: dist/mcp-mt5-quant-macos-arm64.tar.gz
@@ -54,13 +63,15 @@ jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de # v1
with:
toolchain: stable
- name: Cache Cargo
uses: actions/cache@v4
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cargo/registry
@@ -83,7 +94,7 @@ jobs:
tar -czf mcp-mt5-quant-linux-x64.tar.gz mcp-mt5-quant-linux
- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: linux-binary
path: dist/mcp-mt5-quant-linux-x64.tar.gz
@@ -92,14 +103,19 @@ jobs:
cargo-publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # Required for crates.io Trusted Publishing (OIDC)
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de # v1
with:
toolchain: stable
- name: Cache Cargo
uses: actions/cache@v4
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cargo/registry
@@ -108,10 +124,14 @@ jobs:
key: cargo-publish-${{ hashFiles('Cargo.lock') }}
restore-keys: cargo-publish-
- name: Get crates.io authentication token
uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5
id: auth
- name: Publish to crates.io
run: cargo publish --no-verify --allow-dirty
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
# ── GitHub Release ───────────────────────────────────────────────────────────
@@ -123,29 +143,69 @@ jobs:
contents: write
env:
RELEASE_TAG: ${{ github.event.inputs.version || github.ref_name }}
# secrets context is NOT available in step-level if: expressions —
# referencing it there makes the whole workflow fail validation
# (startup-failure runs on every push). Route the check through a
# job-level env var instead: env IS readable in if:.
HAS_GPG_SECRETS: ${{ secrets.GPG_PRIVATE_KEY != '' && secrets.GPG_PASSPHRASE != '' }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Download macOS artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: macos-binary
path: dist
- name: Download Linux artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: linux-binary
path: dist
# ── GPG signing of release artifacts (delivery_unsigned) ────────────────
# Guarded: only runs when BOTH signing secrets are configured. When they
# are absent the release still ships (unsigned) instead of failing — so
# forks/contributors without the secrets are not blocked. When present,
# each binary gets a detached ASCII-armored signature and the public key
# (docs/signing-key.asc) is attached to the release for verification.
- name: Import GPG key and sign artifacts
if: env.HAS_GPG_SECRETS == 'true'
env:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
GPG_KEYID: EF1CA6A2E2D0CEFF61DC7F080256A0C36A6D5E72
run: |
echo "$GPG_PRIVATE_KEY" | gpg --batch --import
for f in dist/mcp-mt5-quant-macos-arm64.tar.gz \
dist/mcp-mt5-quant-linux-x64.tar.gz; do
gpg --batch --yes --pinentry-mode loopback \
--passphrase "$GPG_PASSPHRASE" \
--detach-sign --armor -u "$GPG_KEYID" "$f"
done
- name: Build release asset list
id: assets
run: |
{
echo "files<<EOF"
echo "dist/mcp-mt5-quant-macos-arm64.tar.gz"
echo "dist/mcp-mt5-quant-linux-x64.tar.gz"
echo "docs/signing-key.asc"
if [ -f dist/mcp-mt5-quant-macos-arm64.tar.gz.asc ] && \
[ -f dist/mcp-mt5-quant-linux-x64.tar.gz.asc ]; then
echo "dist/mcp-mt5-quant-macos-arm64.tar.gz.asc"
echo "dist/mcp-mt5-quant-linux-x64.tar.gz.asc"
fi
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with:
tag_name: ${{ env.RELEASE_TAG }}
name: ${{ env.RELEASE_TAG }}
files: |
dist/mcp-mt5-quant-macos-arm64.tar.gz
dist/mcp-mt5-quant-linux-x64.tar.gz
files: ${{ steps.assets.outputs.files }}
draft: false
prerelease: false
generate_release_notes: true
@@ -154,26 +214,36 @@ jobs:
# ── MCP Package ──────────────────────────────────────────────────────────────
# Builds the installable MCP package (binary + config + docs),
# computes its SHA256, and uploads to the release.
# computes its SHA256, and uploads to the release. Also attests the FINAL
# release bytes — the macOS tarball is re-packaged here (server.json added),
# so attesting in the build jobs would cover digests that never ship.
build-mcp-package:
needs: release
runs-on: macos-latest
permissions:
contents: write
contents: write # Attach the package to the GitHub Release
id-token: write # Required for build provenance attestation (OIDC)
attestations: write # Required to publish the attestation
outputs:
sha256: ${{ steps.compute-sha256.outputs.sha256 }}
env:
RELEASE_TAG: ${{ github.event.inputs.version || github.ref_name }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Download macOS binary
uses: actions/download-artifact@v4
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: macos-binary
path: dist
- name: Download Linux binary
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: linux-binary
path: dist
- name: Build MCP package
run: |
mkdir -p mcp-package/mcp-server/bin
@@ -185,6 +255,19 @@ jobs:
cp server.json mcp-package/
cd mcp-package && tar -czf ../mcp-mt5-quant-macos-arm64.tar.gz . && cd ..
# Attest the exact bytes attached to the Release: the re-packaged macOS
# tarball at the repo root and the pass-through Linux tarball from the
# artifact — so attested digests match the published assets.
- name: Generate build provenance attestation (macOS)
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: mcp-mt5-quant-macos-arm64.tar.gz
- name: Generate build provenance attestation (Linux)
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: dist/mcp-mt5-quant-linux-x64.tar.gz
- name: Compute SHA256
id: compute-sha256
run: |
@@ -193,7 +276,7 @@ jobs:
echo "MCP Package SHA256: $SHA256"
- name: Upload MCP package to release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with:
tag_name: ${{ env.RELEASE_TAG }}
files: mcp-mt5-quant-macos-arm64.tar.gz
@@ -201,22 +284,44 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload MCP package artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: mcp-package
path: mcp-mt5-quant-macos-arm64.tar.gz
# ── Update server.json SHA256 ────────────────────────────────────────────────
# Commits the computed SHA256 back to master so server.json always
# reflects the latest published package hash.
# Updates server.json on master via an auto-merged PR instead of a direct
# push: the protect-master ruleset requires build/lint/cargo-deny status
# checks, which github-actions[bot] cannot bypass on a direct push. The PR
# runs those checks legitimately, then auto-merges once green. This job
# blocks until the merge lands so mcp-publish reads the patched server.json.
#
# Bot identity (RELEASE_PAT): when the RELEASE_PAT secret exists (a
# fine-grained personal access token owned by masdevid with Contents+PR
# write), the branch push and the PR are attributed to masdevid instead of
# github-actions[bot]. GitHub only holds pull_request workflows for manual
# approval when the PR author is an OUTSIDE contributor — same-owner PRs
# run their checks immediately, making releases fully hands-off. Without
# the secret everything falls back to GITHUB_TOKEN: the release still
# completes, but the server.json PR's required status checks sit in
# "action_required" until manually approved once per release.
update-server-json:
needs: build-mcp-package
runs-on: ubuntu-latest
permissions:
contents: write
contents: write # push the ci/* release branch
pull-requests: write # create the PR and enable auto-merge
env:
# Single bot identity used for BOTH the git push and the gh pr calls —
# GitHub ties workflow-trigger approval to the PR-authoring identity,
# so push and PR must match. Resolves to RELEASE_PAT when configured,
# otherwise the default GITHUB_TOKEN, so nothing breaks before the
# secret is added. Declared here instead of interpolated inline so the
# token never lands on a command line; GitHub masks it in logs anyway.
RELEASE_PAT_OR_TOKEN: ${{ secrets.RELEASE_PAT || github.token }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: master
fetch-depth: 1
@@ -256,19 +361,78 @@ jobs:
print(f"Patched: version={version}, sha256={sha256[:16]}...")
PYEOF
- name: Commit server.json
- name: Commit server.json to release branch
id: commit
run: |
BRANCH="ci/server-json-sha-${VERSION}"
# Authenticate ALL pushes (stale-branch cleanup + branch upload)
# with the SAME identity that creates the PR below: GitHub ties
# workflow-trigger approval to the PR author, so a mismatch here
# would defeat the RELEASE_PAT hands-off path. Token comes from the
# job-level env (never interpolated inline, never echoed).
git remote set-url --push origin \
"https://x-access-token:${RELEASE_PAT_OR_TOKEN}@github.com/masdevid/mt5-quant.git"
# Drop any stale branch/PR left over from a previous attempt at
# this release so every run starts from a clean slate
git push origin --delete "$BRANCH" 2>/dev/null || true
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git checkout -b "$BRANCH"
git add server.json
if git diff --cached --quiet; then
echo "server.json unchanged — no commit needed"
echo "changed=false" >> "$GITHUB_OUTPUT"
else
git commit -m "ci: update server.json SHA256 for v${VERSION} [skip ci]"
git push origin master
echo "Committed server.json update"
# Never add a CI-skip marker to this commit message: under the
# protect-master ruleset the server.json PR carries required
# status checks, and a CI-skip marker suppresses
# pull_request-triggered workflows, so those checks never report
# and auto-merge hangs until timeout. Omitting it is safe —
# release.yml triggers only on tags (no loop risk) and
# GITHUB_TOKEN-attributed merges never retrigger workflows.
git commit -m "ci: update server.json SHA256 for v${VERSION}"
git push origin "$BRANCH"
echo "changed=true" >> "$GITHUB_OUTPUT"
echo "Committed server.json update to ${BRANCH}"
fi
- name: Create PR and enable auto-merge
if: steps.commit.outputs.changed == 'true'
run: |
BRANCH="ci/server-json-sha-${VERSION}"
# Same rule as above: no CI-skip marker in the title — GitHub
# honors markers on PR titles too, which would suppress the
# required checks.
GH_TOKEN="$RELEASE_PAT_OR_TOKEN" gh pr create \
--title "ci: update server.json SHA256 for v${VERSION}" \
--body "Automated MCP package hash update." \
--base master \
--head "$BRANCH"
GH_TOKEN="$RELEASE_PAT_OR_TOKEN" gh pr merge "$BRANCH" --auto --squash
# Block until required checks (build, lint, cargo-deny x4) finish
# and auto-merge completes — mcp-publish depends on this job and
# checks out master expecting the patched server.json
DEADLINE=$((SECONDS + 1800))
while (( SECONDS < DEADLINE )); do
STATE=$(GH_TOKEN="$RELEASE_PAT_OR_TOKEN" gh pr view "$BRANCH" --json state --jq .state)
case "$STATE" in
MERGED)
echo "server.json PR merged to master"
exit 0
;;
CLOSED)
echo "::error::server.json PR was closed without merging" >&2
exit 1
;;
esac
sleep 20
done
echo "::error::Timed out after 30m waiting for server.json PR auto-merge (required checks: build, lint, cargo-deny x4)" >&2
exit 1
# ── MCP Registry Publish ─────────────────────────────────────────────────────
mcp-publish:
@@ -278,65 +442,63 @@ jobs:
contents: read
id-token: write # Required for GitHub OIDC
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: master # Pull master so server.json has the correct SHA256
- name: Download MCP package
uses: actions/download-artifact@v4
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: mcp-package
path: .
- name: Install mcp-publisher
id: install-publisher
env:
# Hash-pinned download (OpenSSF Scorecard Pinned-Dependencies):
# PUB_SHA256 is the mcp-publisher_linux_amd64.tar.gz entry from
# registry_1.8.1_checksums.txt shipped in the same release.
# Bump both values together when upgrading.
PUB_VERSION: v1.8.1
PUB_SHA256: a06c9096dcb9727c13555b6be26c7effa707b01f06a4c561ba7a3635443cf2cc
run: |
# Fetch the download URL for linux_amd64 directly from the release assets API
# (asset name format changed in v1.6.0 — look it up rather than constructing it)
URL=$(curl -sf \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/modelcontextprotocol/registry/releases/latest" \
| python3 -c "
import sys, json
r = json.load(sys.stdin)
tag = r['tag_name']
assets = r.get('assets', [])
match = next((a['browser_download_url'] for a in assets
if 'linux' in a['name'] and 'amd64' in a['name']
and a['name'].endswith('.tar.gz')
and 'sbom' not in a['name']
and 'sigstore' not in a['name']), None)
if match:
print(match)
else:
print(f'https://github.com/modelcontextprotocol/registry/releases/download/{tag}/mcp-publisher_linux_amd64.tar.gz', file=sys.stderr)
sys.exit(1)
" 2>/dev/null)
URL="https://github.com/modelcontextprotocol/registry/releases/download/${PUB_VERSION}/mcp-publisher_linux_amd64.tar.gz"
if [[ -z "$URL" ]]; then
echo "Downloading: $URL"
if ! curl -fsSL -o "$RUNNER_TEMP/mcp-publisher.tar.gz" "$URL"; then
echo "installed=false" >> "$GITHUB_OUTPUT"
echo "Could not resolve mcp-publisher download URL"
else
echo "Downloading: $URL"
if curl -fsSL -o mcp-publisher.tar.gz "$URL"; then
tar -xzf mcp-publisher.tar.gz 2>/dev/null || true
BINARY=$(find . -maxdepth 3 -name "mcp-publisher" ! -path "./.git/*" | head -1)
if [[ -n "$BINARY" && -f "$BINARY" ]]; then
chmod +x "$BINARY"
sudo mv "$BINARY" /usr/local/bin/mcp-publisher
echo "installed=true" >> "$GITHUB_OUTPUT"
echo "Installed: $(mcp-publisher --version 2>/dev/null || echo 'ok')"
else
echo "installed=false" >> "$GITHUB_OUTPUT"
echo "Binary not found in archive"
fi
rm -f mcp-publisher.tar.gz
else
echo "installed=false" >> "$GITHUB_OUTPUT"
echo "Download failed: $URL"
fi
echo "Download failed: $URL"
exit 0
fi
# Never execute unverified bytes: hard-fail the job on any digest
# mismatch (upstream re-release or tampering)
ACTUAL=$(sha256sum "$RUNNER_TEMP/mcp-publisher.tar.gz" | awk '{print $1}')
if [[ "$ACTUAL" != "$PUB_SHA256" ]]; then
echo "::error::SHA256 mismatch for mcp-publisher ${PUB_VERSION}: expected ${PUB_SHA256}, got ${ACTUAL}"
exit 1
fi
echo "SHA256 verified: $ACTUAL"
EXTRACT_DIR="$(mktemp -d)"
if ! tar -xzf "$RUNNER_TEMP/mcp-publisher.tar.gz" -C "$EXTRACT_DIR"; then
rm -rf "$EXTRACT_DIR" "$RUNNER_TEMP/mcp-publisher.tar.gz"
echo "installed=false" >> "$GITHUB_OUTPUT"
echo "Extraction failed"
exit 0
fi
if [[ -f "$EXTRACT_DIR/mcp-publisher" ]]; then
chmod +x "$EXTRACT_DIR/mcp-publisher"
sudo mv "$EXTRACT_DIR/mcp-publisher" /usr/local/bin/mcp-publisher
echo "installed=true" >> "$GITHUB_OUTPUT"
echo "Installed: $(mcp-publisher --version 2>/dev/null || echo 'ok')"
else
echo "installed=false" >> "$GITHUB_OUTPUT"
echo "Binary not found in archive"
fi
rm -rf "$EXTRACT_DIR" "$RUNNER_TEMP/mcp-publisher.tar.gz"
- name: Validate server.json before publish
id: validate
run: |
+43 -1
View File
@@ -6,6 +6,13 @@ on:
pull_request:
branches: [ "master" ]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }}
env:
CARGO_TERM_COLOR: always
@@ -15,8 +22,43 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de # v1
with:
toolchain: stable
components: clippy, rustfmt
- name: Cache Cargo
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: linux-cargo-${{ hashFiles('Cargo.lock') }}
restore-keys: linux-cargo-
- name: Check formatting
run: cargo fmt --all -- --check
- name: Clippy
run: cargo clippy --all-targets -- -D warnings
cargo-deny:
runs-on: ubuntu-latest
strategy:
matrix:
checks: [advisories, bans, licenses, sources]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: embarkstudios/cargo-deny-action@3c6349835b2b7b196a839186cb8b78e02f7b5f25 # v2.1.1
with:
command: check ${{ matrix.checks }}
log-level: warn
+81
View File
@@ -0,0 +1,81 @@
name: Scorecard supply-chain security
on:
branch_protection_rule:
workflow_dispatch:
schedule:
- cron: '30 6 * * 3'
push:
branches: [master]
permissions:
contents: read
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
security-events: write
id-token: write
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
with:
results_file: results.sarif
results_format: sarif
publish_results: true
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: SARIF file
path: results.sarif
- uses: github/codeql-action/upload-sarif@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4
with:
sarif_file: results.sarif
- name: Update badge JSON
if: github.ref == 'refs/heads/master'
run: |
set -euo pipefail
python3 - <<'PY'
import json
import urllib.request
url = "https://api.scorecard.dev/projects/github.com/masdevid/mt5-quant"
with urllib.request.urlopen(url) as resp:
data = json.load(resp)
score = float(data["score"])
if score >= 9:
color = "brightgreen"
elif score >= 7:
color = "yellowgreen"
elif score >= 5:
color = "yellow"
else:
color = "red"
badge = {
"schemaVersion": 1,
"label": "OpenSSF Scorecard",
"message": f"{score:g}",
"color": color,
}
with open("badge.json", "w") as f:
json.dump(badge, f)
print("Wrote badge.json:", badge)
PY
git remote set-url origin "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git"
git checkout -B badges
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add -f badge.json
git commit -m "chore: update OpenSSF Scorecard badge" || echo "no changes to commit"
git push -f origin badges
+11 -1
View File
@@ -35,8 +35,18 @@ AGENTS.md
# Codegraph
.codegraph/
# macOS
# macOS metadata
.DS_Store
.AppleDouble
.LSOverride
._*
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Logs
*.log
-245
View File
@@ -1,245 +0,0 @@
# AGENTS.md — Developer instructions for MT5-Quant
## Project Overview
Open-source MCP server for MetaTrader 5 backtesting, optimization, and analytics — written entirely in Rust. Exposes 89 stdio MCP tools for compiling MQL5 EAs, running backtests via Wine, extracting deal-level data, organizing reports in SQLite, and performing 19 dimensions of analysis.
Target users: MQL developers on macOS (CrossOver or native MetaTrader5.app) and Linux (Wine/Xvfb).
## Repository Layout
```
MT5-Quant/
├── src/
│ ├── main.rs # CLI entry + stdio MCP transport
│ ├── mcp_server.rs # McpServer struct, tool dispatch, init/notification
│ ├── mt5.rs # Wine/MT5 executable launchers
│ ├── models/
│ │ ├── mod.rs
│ │ ├── config.rs # Config, CurrentAccount, wine detection, .set helpers
│ │ ├── deals.rs # Deal, PositionPair, DrawdownEvent
│ │ ├── metrics.rs # BacktestMetrics parsing (HTML + XML formats)
│ │ └── report.rs # ReportEntry, PipelineMetadata, BacktestJob, status
│ ├── analytics/
│ │ ├── mod.rs
│ │ ├── extract.rs # HTML/XML report → Deal[] + metrics
│ │ └── analyze.rs # DealAnalyzer: 19+ analysis methods
│ ├── compile/
│ │ ├── mod.rs
│ │ └── mql_compiler.rs # MetaEditor compiler via Wine
│ ├── pipeline/
│ │ ├── mod.rs
│ │ ├── backtest.rs # 5-stage pipeline: COMPILE→CLEAN→BACKTEST→EXTRACT→ANALYZE
│ │ └── stages.rs # PipelineStage enum
│ ├── optimization/
│ │ ├── mod.rs
│ │ ├── optimizer.rs # Background optimization launcher
│ │ └── parser.rs # Optimization result XML → best params
│ ├── storage/
│ │ ├── mod.rs
│ │ └── database.rs # ReportDb: reports + deals SQLite tables, queries
│ ├── utils/ # (new) shared utilities — growing directory
│ └── tools/
│ ├── mod.rs # MCP definitions aggregation
│ ├── definitions/ # Tool schemas (JSON Schema) — 11 modules, 90 tools
│ │ ├── mod.rs # exports: all_tools(), system_tools, etc.
│ │ ├── analytics.rs # 19 analysis tools + compare_baseline
│ │ ├── backtest.rs # 7 backtest tools
│ │ ├── baseline.rs # 1 baseline tool
│ │ ├── experts.rs # 9 EA/indicator/script tools
│ │ ├── optimization.rs # 4 optimization tools
│ │ ├── reports.rs # 20 report management tools
│ │ ├── setfiles.rs # 8 .set file tools
│ │ ├── system.rs # 6 system/health tools + version update
│ │ ├── update.rs # update tool definition
│ │ └── utility.rs # 10 pre-flight/compat tools
│ └── handlers/ # Tool dispatch functions — 11 modules
│ ├── mod.rs # ToolHandler struct, dispatch table (89+ cases)
│ ├── analysis.rs # analytics/analyze dispatch
│ ├── backtest.rs # pipeline/backtest dispatch
│ ├── experts.rs # wine + WalkDir helpers: scan_mql_dir, copy_mql_to_project
│ ├── optimization.rs # optimizer dispatch
│ ├── reports.rs # ReportDb + ReportEntry dispatch
│ ├── setfiles.rs # UTF-16LE .set read/write/patch helpers
│ ├── system.rs # healthcheck, version update
│ ├── utility.rs # pre-flight, diagnostics, init_project helpers
│ └── update.rs # update tool handler
├── scripts/
│ ├── setup.sh # Auto-detect Wine/MT5, write config (1271 lines)
│ ├── platform_detect.sh # Wine path + headless detection
│ ├── build-rust.sh # cargo build --release (25 lines)
│ ├── release.sh # Version bump + changelog + tag + push (238 lines)
│ └── optimize.sh # Legacy optimization driver (pending full Rust migration)
├── analytics/ # Legacy Python (reference only — do not modify)
├── config/
│ ├── mt5-quant.example.yaml # Template user copies to mt5-quant.yaml
│ └── mt5-quant.yaml # Generated config (gitignored)
├── tests/
│ ├── test_mcp.py # End-to-end MCP tests (stdio-driven)
│ ├── integration_test.sh # Shell-based integration tests
│ └── fixtures/ # Sample reports + CSVs for testing
├── server.json # MCP registry manifest (tracked)
├── Cargo.toml # version = 1.34.0
└── CHANGELOG.md
```
## Key Design Constraints
- **Headless/GUI**: `platform_detect.sh` auto-selects. macOS CrossOver = GUI (CrossOver abstracts display). Linux without `$DISPLAY` = Xvfb. Controlled by `display.mode` in config (`auto` | `gui` | `headless`).
- **Single MT5 instance**: Never run two backtests in parallel on the same Wine prefix.
- **Model 0 only for optimization**: Model 1 overfits martingale/grid EAs — intra-bar movement not simulated. Use `model=0` (every tick).
- **UTF-16LE .set files**: MT5 strips `||Y` flags from UTF-8. All `.set` writes use UTF-16LE + `chmod 444`. See `setfiles.rs` helpers.
- **OptMode reset**: After any optimization, `terminal.ini` gets `OptMode=-1`. Must reset to `0` before next backtest (handled in `backtest.rs` cleanup stage).
- **Report format detection**: MT5 Build 48+ writes SpreadsheetML XML (`.htm.xml`), not HTML. Both formats handled in `extract.rs` — always check for the XML variant first.
- **EA-agnostic**: Never hardcode magic numbers, strategy names, or assume specific EA behavior. All code must work with any MQL5 EA.
## Architecture
1. **CLI (`main.rs`)** — parses CLi args (`--stdio`, `--port`, `--test-launch`), initializes `McpServer`, runs stdio or TCP transport loop, handles JSON-RPC requests.
2. **McpServer (`mcp_server.rs`)** — wraps `ToolHandler` with `Arc<Mutex>`, handles `initialize`/`notifications`/`tool/error` method routing. Auto-verifies Wine/MT5 on first call.
3. **ToolHandler (`tools/handlers/mod.rs`)** — dispatch table mapping 89 tool names to handler functions. Each handler module is self-contained.
4. **Definitions (`tools/definitions/`)** — JSON Schema objects for tool input/output. Never executes logic.
5. **Analytics (`analytics/`)** — two core structs:
- `ReportExtractor` — reads HTML/XML report + tester log → `Vec<Deal>` + `BacktestMetrics`
- `DealAnalyzer` — takes `Vec<Deal>` → 19 analysis methods (monthly PnL, drawdown events, streaks, etc.)
6. **Pipeline (`pipeline/backtest.rs`)** — `BacktestPipeline` struct with 5 stages:
- `COMPILE` — `MqlCompiler::compile()` via MetaEditor
- `CLEAN` — clean cache, reset OptMode
- `BACKTEST` — launch MT5 tester via Wine, poll status
- `EXTRACT` — parse HTML/XML → deals + metrics
- `ANALYZE` — run all analytics, write to SQLite
7. **Storage (`storage/database.rs`)** — `ReportDb` with two tables:
- `reports` — metrics, set file paths, verdict, tags, notes
- `deals` — individual deal rows, keyed by `report_id`
- `CREATE TABLE IF NOT EXISTS` in `init()` — idempotent on startup
## Building
```bash
bash scripts/build-rust.sh
# or
cargo build --release
```
Binary: `target/release/mt5-quant`
## Testing
- **Unit/integration:** `cargo test`
- **End-to-end MCP:** `python3 tests/test_mcp.py` — drives the MCP server over stdio (full backtest + all analytics)
- **Analytics only:** No MT5/Wine needed; loads deals from SQLite DB via `db.get_deals(report_id)`
- **Symbol note:** Testing account uses `XAUUSDc` (broker suffix required), not `XAUUSD`
## Configuration
Users copy `config/mt5-quant.example.yaml` → `config/mt5-quant.yaml` (gitignored).
Run `bash scripts/setup.sh` to auto-detect Wine, MT5 paths, architecture, and write config.
Minimum config:
```yaml
wine_executable: "/path/to/wine64"
terminal_dir: "/path/to/MetaTrader 5"
```
Optional:
```yaml
defaults:
symbol: "XAUUSD.cent"
timeframe: "M5"
deposit: 10000
display:
mode: auto # auto | gui | headless
reports_dir: "./reports"
experts_dir: "~/.../MQL5/Experts"
```
## Developing New Tools
Each new tool follows a 4-file pattern:
1. **Schema** — Add tool input/output to `tools/definitions/{domain}.rs` via `Tool::new(name) {...}` with `Input::schema` and `Output::schema`.
2. **Export** — Add to `tools/definitions/mod.rs`:
- Include in `all_tools()` array
- Add to domain exports if creating new domain
3. **Handler** — Add function in `tools/handlers/{domain}.rs`:
```rust
pub async fn handle_tool_name(config: &Config, args: &Value) -> Result<Value> {
let required = required_str(args, "param_name")?;
ok_response(json! { "result": "..." })
}
```
4. **Dispatch** — Add case in `handlers/mod.rs` match arm under `ToolHandler::handle()`.
### Handler Helper Functions (use instead of inline logic)
| Helper | Purpose |
|--------|---------|
| `required_str(args, key)` | Extract required `&str` or return error |
| `ok_response(data)` / `err_response(msg)` | Wrap in MCP content envelope |
| `resolve_report(args)` | → `(deals, metrics, report_dir)` — `report_id > report_dir > latest` |
| `prepare_analysis(args)` | → `(deals, metrics, analyzer, report_dir)` — wraps resolve_report |
| `scan_mql_dir(dir, filter, type_label)` | WalkDir with optional filter for list/search |
| `copy_mql_to_project(config, args, fallback)` | Shared copy logic for indicator/script |
Extend helpers when adding new handlers.
## Commit Style
Conventional commits: `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `release:`
Keep commits focused — one logical change per commit.
## Release Process
```bash
bash scripts/release.sh <patch|minor|major|X.Y.Z> [--yes]
```
Script handles:
1. Bump `Cargo.toml` + `Cargo.lock` version
2. Update `server.json` (version + SHA256 placeholder)
3. Generate CHANGELOG entry from git log
4. `cargo check --quiet`
5. Commit `release: vX.Y.Z`
6. Tag `vX.Y.Z`
7. Push — CI (`release.yml`) triggered:
- Build macOS arm64 + Linux x64 binaries
- Publish to crates.io
- Create GitHub Release with artifacts
- Build MCP package, compute SHA256
- Update `server.json` SHA256, push
- Publish to MCP Registry
## Deal Storage
- Deals stored in `deals` SQLite table, keyed by `report_id`. **No `deals.csv` written automatically.**
- `db.insert_deals(report_id, &[Deal])` called in pipeline after extraction.
- `db.get_deals(report_id)` loads deals for analytics. `db.get_by_report_dir(path)` resolves by filesystem path.
- `export_deals_csv` tool writes CSV on demand.
## DB Schema Evolution
Add new tables/columns in `ReportDb::init()` using `CREATE TABLE IF NOT EXISTS` / `CREATE INDEX IF NOT EXISTS` — runs on every startup, is idempotent. No migration files needed.
## Wine / MT5 Notes
- Wine executable paths differ across platforms:
- macOS MT5.app: `/Applications/MetaTrader 5.app/Contents/SharedSupport/wine/bin/wine64`
- macOS CrossOver: `/Applications/CrossOver.app/Contents/SharedSupport/CrossOver/bin/wine64`
- Linux: `/usr/bin/wine64` (or first `wine64` on PATH)
- MT5 terminal_dir is auto-resolved from wine_executable path.
- Apple Silicon: append `arch -x86_64` prefix for x86 Wine binaries (auto-detected).
## Common Pitfalls
- **M1/Memory issues**: Wine on Apple Silicon runs x86 via Rosetta 2. Watch memory pressure.
- **Model mismatch**: Grid/martingale EAs need `model=0` (every tick). Model 1/2 will produce zero deals.
- **Set file encoding**: Always use UTF-16LE with `chmod 444`. UTF-8 strips `||Y` flags.
- **OptMode=-1**: Must clear after optimization. Handled in pipeline cleanup stage.
- **Report not found**: Check `<terminal_dir>/history/` for correct symbol name (broker suffix matters: `XAUUSDc` vs `XAUUSD`).
- **Journal-only backtest**: `ShutdownTerminal=1` doesn't exit MT5 on Wine. Inactivity watchdog (in `launch_backtest`) handles this with `inactivity_kill_secs` param.
+27
View File
@@ -1,5 +1,32 @@
# Changelog
## [1.34.3] — 2026-08-25
- chore(deps): bump dirs from 5.0.1 to 6.0.0 (#6)
- chore(deps): bump base64 from 0.22.1 to 0.23.1 (#4)
- chore(deps): bump rusqlite from 0.31.0 to 0.40.2 (#5)
- ci(release): generate build provenance attestations for artifacts
- chore(deps): bump the minor-and-patch group across 1 directory with 7 updates (#3)
- fix(release): drop [skip ci] from server.json PR so required checks can run
- docs(readme): fix Scorecard badge, add CI, crates.io, and license badges
- ci(scorecard): rename invalid 'publish' input to 'publish_results' so results are published
## [1.34.2] — 2026-08-25
- ci(release): publish to crates.io via trusted publishing (OIDC)
- fix: resolve clippy lints surfaced by newer toolchain in CI gate
- fix(release): update server.json via auto-merged PR to satisfy branch ruleset
- chore(templates): link Discussions from issue chooser
- chore(templates): add issue forms and PR template
- ci(security): add OpenSSF Scorecard analysis and badge
- ci(hardening): least-privilege GITHUB_TOKEN permissions across workflows
- ci(security): add cargo-deny checks for advisories, licenses, bans, sources
- ci(security): add dependency review on PRs
- docs(security): add security policy
- fix(ci): activate Dependabot for cargo and github-actions ecosystems
## [1.34.1] — 2026-08-25
- fix: remove PayPal funding link, bump anyhow for RUSTSEC-2026-0190, resolve all clippy warnings
+40
View File
@@ -0,0 +1,40 @@
# Contributing to MT5-Quant
Thanks for your interest in improving MT5-Quant! This document explains how to
contribute and what we expect from contributions.
## How to contribute
1. **Fork** the repository and create a topic branch from `master`.
2. Make your change, keeping it focused (one logical change per PR).
3. Open a **pull request against `master`**. A pull request template
(`.github/PULL_REQUEST_TEMPLATE.md`) is provided — please fill it in.
4. Ensure CI passes (build, `cargo test`, clippy, formatting, cargo-deny,
CodeQL). Maintainers review and merge once checks and review are satisfied.
## Development setup
- Install Rust (stable toolchain) and, for full backtests, Wine/MT5 as described
in `README.md` and `docs/QUICKSTART.md`.
- Build the release binary with either:
- `bash scripts/build-rust.sh`, or
- `cargo build --release`
- The binary is produced at `target/release/mt5-quant`.
## Testing requirement
- **Non-trivial changes must include tests.** New functionality should come with
unit or integration tests where practical.
- `cargo test` runs automatically in CI (`rust.yml`) on every push and pull
request, so a change that breaks tests cannot merge.
## Commit style
- Follow **Conventional Commits** (`feat:`, `fix:`, `docs:`, `refactor:`,
`test:`, `release:`), as documented in `AGENTS.md`.
## Issue tracker
- Bugs and enhancement requests are tracked on the project's
[GitHub Issues](https://github.com/masdevid/mt5-quant/issues), with
bug-report and feature-request templates under `.github/ISSUE_TEMPLATE/`.
Generated
+148 -186
View File
@@ -2,18 +2,6 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.4"
@@ -68,7 +56,7 @@ version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@@ -79,7 +67,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@@ -96,9 +84,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "base64"
version = "0.22.1"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
[[package]]
name = "bitflags"
@@ -136,9 +124,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chrono"
version = "0.4.44"
version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
dependencies = [
"iana-time-zone",
"js-sys",
@@ -150,9 +138,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.6.1"
version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
checksum = "aa8876b300ab35ba921adea3dfd70157a46249b33f95c9084ae5709785478946"
dependencies = [
"clap_builder",
"clap_derive",
@@ -160,9 +148,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.6.0"
version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
checksum = "ec0797fb7aeb1406c84efac526901f7ec3ead2124f946b494e72879d4b54704d"
dependencies = [
"anstream",
"anstyle",
@@ -172,14 +160,14 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.6.1"
version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
checksum = "f9c751b79415d4e559e3d1fcf128e09e720eb673a06d26cf6f392d37d75b66e0"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
"syn 3.0.4",
]
[[package]]
@@ -201,33 +189,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "dirs"
version = "5.0.1"
name = "core_detect"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48"
[[package]]
name = "dirs"
version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d57d423b3c82e89b9a24ca3091fee61f456a26edbd28d26c65906f4bc1dcd8f"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.4.1"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.48.0",
"windows-sys",
]
[[package]]
name = "encoding_rs"
version = "0.8.35"
version = "0.8.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
checksum = "8e985e0451871ad22fb8d2b6b076e2028a502a0d3950998c2c5c0a4f9b5d9679"
dependencies = [
"cfg-if",
"core_detect",
"multiversion_no_op",
"rustversion",
"scopeguard",
"simdutf8",
]
[[package]]
@@ -243,7 +242,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@@ -276,6 +275,12 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "getrandom"
version = "0.2.17"
@@ -300,22 +305,22 @@ dependencies = [
"wasip3",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash",
"foldhash 0.1.5",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"foldhash 0.2.0",
]
[[package]]
@@ -323,14 +328,17 @@ name = "hashbrown"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
dependencies = [
"foldhash 0.2.0",
]
[[package]]
name = "hashlink"
version = "0.9.1"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
checksum = "32069d97bb81e38fa67eab65e3393bf804bb85969f2bc06bf13f64aef5aba248"
dependencies = [
"hashbrown 0.14.5",
"hashbrown 0.17.0",
]
[[package]]
@@ -432,9 +440,9 @@ dependencies = [
[[package]]
name = "libsqlite3-sys"
version = "0.28.0"
version = "0.38.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f"
checksum = "f1d20bef17f513b9b3004532233187769cd072d790971f4e4da0e346eb6401e8"
dependencies = [
"cc",
"pkg-config",
@@ -476,12 +484,12 @@ checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
dependencies = [
"libc",
"wasi",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
name = "mt5-quant"
version = "1.34.1"
version = "1.34.3"
dependencies = [
"anyhow",
"base64",
@@ -503,13 +511,19 @@ dependencies = [
"walkdir",
]
[[package]]
name = "multiversion_no_op"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d"
[[package]]
name = "nu-ansi-term"
version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@@ -581,7 +595,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
"syn 2.0.117",
]
[[package]]
@@ -619,9 +633,9 @@ dependencies = [
[[package]]
name = "redox_users"
version = "0.4.6"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [
"getrandom 0.2.17",
"libredox",
@@ -630,9 +644,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.12.3"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
dependencies = [
"aho-corasick",
"memchr",
@@ -642,9 +656,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.14"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
dependencies = [
"aho-corasick",
"memchr",
@@ -653,9 +667,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.10"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]]
name = "roxmltree"
@@ -667,10 +681,20 @@ dependencies = [
]
[[package]]
name = "rusqlite"
version = "0.31.0"
name = "rsqlite-vfs"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae"
checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c"
dependencies = [
"hashbrown 0.16.1",
"thiserror",
]
[[package]]
name = "rusqlite"
version = "0.40.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23f2a97da3e3873c73cb2a2e71b35c40ff95e0b1eefa8d72d8499a6928c3b5b3"
dependencies = [
"bitflags",
"fallible-iterator",
@@ -678,6 +702,7 @@ dependencies = [
"hashlink",
"libsqlite3-sys",
"smallvec",
"sqlite-wasm-rs",
]
[[package]]
@@ -690,7 +715,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@@ -728,9 +753,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
[[package]]
name = "serde"
version = "1.0.228"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [
"serde_core",
"serde_derive",
@@ -738,29 +763,29 @@ dependencies = [
[[package]]
name = "serde_core"
version = "1.0.228"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 3.0.4",
]
[[package]]
name = "serde_json"
version = "1.0.149"
version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [
"itoa",
"memchr",
@@ -807,6 +832,12 @@ dependencies = [
"libc",
]
[[package]]
name = "simdutf8"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]]
name = "smallvec"
version = "1.15.1"
@@ -820,7 +851,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
name = "sqlite-wasm-rs"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc3efc0da82635d7e1ced0053bbbfa8c7ab9645d0bf36ceb4f7127bb85315d75"
dependencies = [
"cc",
"js-sys",
"rsqlite-vfs",
"wasm-bindgen",
]
[[package]]
@@ -840,6 +883,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.27.0"
@@ -850,27 +904,27 @@ dependencies = [
"getrandom 0.4.2",
"once_cell",
"rustix",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
name = "thiserror"
version = "1.0.69"
version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 3.0.4",
]
[[package]]
@@ -884,9 +938,9 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.52.1"
version = "1.53.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6"
checksum = "e95f91fcc7a621e8b030f6aa23c71fe9838ae2fb4d8118b75602a328f5144044"
dependencies = [
"bytes",
"libc",
@@ -896,7 +950,7 @@ dependencies = [
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@@ -907,7 +961,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
]
[[package]]
@@ -929,7 +983,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
]
[[package]]
@@ -993,9 +1047,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.23.1"
version = "1.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
checksum = "97277d36b9c3ace13e58fa6e753f8b0bbbf302a18dd193240d70f9e29681059a"
dependencies = [
"getrandom 0.4.2",
"js-sys",
@@ -1014,12 +1068,6 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "walkdir"
version = "2.5.0"
@@ -1086,7 +1134,7 @@ dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
"wasm-bindgen-shared",
]
@@ -1139,7 +1187,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@@ -1163,7 +1211,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
]
[[package]]
@@ -1174,7 +1222,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
]
[[package]]
@@ -1201,15 +1249,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
@@ -1219,63 +1258,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "wit-bindgen"
version = "0.51.0"
@@ -1312,7 +1294,7 @@ dependencies = [
"heck",
"indexmap",
"prettyplease",
"syn",
"syn 2.0.117",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
@@ -1328,7 +1310,7 @@ dependencies = [
"prettyplease",
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
"wit-bindgen-core",
"wit-bindgen-rust",
]
@@ -1370,26 +1352,6 @@ dependencies = [
"wasmparser",
]
[[package]]
name = "zerocopy"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zmij"
version = "1.0.21"
+4 -4
View File
@@ -1,6 +1,6 @@
[package]
name = "mt5-quant"
version = "1.34.1"
version = "1.34.3"
edition = "2021"
description = "MCP server for MT5 strategy development on macOS/Linux"
authors = ["masdevid <masdevid@example.com>"]
@@ -30,11 +30,11 @@ tracing = "0.1"
tracing-subscriber = "0.3"
uuid = { version = "1.0", features = ["v4"] }
regex = "1.0"
dirs = "5.0"
dirs = "7.0"
walkdir = "2.0"
chrono = { version = "0.4", features = ["serde"] }
encoding_rs = "0.8"
tempfile = "3.0"
roxmltree = "0.21.1"
rusqlite = { version = "0.31", features = ["bundled"] }
base64 = "0.22"
rusqlite = { version = "0.40", features = ["bundled"] }
base64 = "0.23"
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 masdevid
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+75 -6
View File
@@ -1,5 +1,7 @@
# MT5-Quant
[![OpenSSF Scorecard](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/masdevid/mt5-quant/badges/badge.json)](https://scorecard.dev/viewer/?uri=github.com/masdevid/mt5-quant) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/14227/badge)](https://bestpractices.coreinfrastructure.org/projects/14227) [![Rust CI](https://github.com/masdevid/mt5-quant/actions/workflows/rust.yml/badge.svg)](https://github.com/masdevid/mt5-quant/actions/workflows/rust.yml) [![crates.io](https://img.shields.io/crates/v/mt5-quant.svg)](https://crates.io/crates/mt5-quant) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![CodeQL](https://img.shields.io/badge/CodeQL-enabled-brightgreen)](https://github.com/masdevid/mt5-quant/security/code-scanning)
**MCP server for MT5 strategy development on macOS/Linux.** 89 tools to compile, backtest, analyze, optimize, debug crashes, and manage MQL5 Expert Advisors — no Windows required.
```
@@ -31,14 +33,68 @@ Claude: [compile → clean → backtest → analyze 1,847 deals]
### Install MT5-Quant
Ask your LLM coding platform to install and configure MT5-Quant:
#### Option A — Cargo (recommended, needs Rust toolchain)
> "Please install mt5-quant. It's a Rust-based MCP server for MT5 backtesting at https://github.com/masdevid/mt5-quant. Run its `scripts/setup.sh` to auto-detect Wine and MT5 paths, register the MCP server, and configure everything."
```bash
cargo install mt5-quant --locked
```
The LLM will:
1. Download the pre-built binary (or `cargo build --release`)
2. Run `scripts/setup.sh` to auto-detect Wine/MT5 and write config
3. Register the MCP server with your coding platform
#### Option B — Pre-built binaries (no toolchain)
```bash
# macOS Apple Silicon
curl -LO https://github.com/masdevid/mt5-quant/releases/latest/download/mcp-mt5-quant-macos-arm64.tar.gz
tar xzf mcp-mt5-quant-macos-arm64.tar.gz # → mcp-mt5-quant-macos/mt5-quant
# Linux x64
curl -LO https://github.com/masdevid/mt5-quant/releases/latest/download/mcp-mt5-quant-linux-x64.tar.gz
tar xzf mcp-mt5-quant-linux-x64.tar.gz # → mcp-mt5-quant-linux/mt5-quant
```
Move the binary somewhere on your `PATH` (e.g. `/usr/local/bin` or `~/.local/bin`) so clients can launch it as `mt5-quant`.
#### Option C — Register with your LLM client (copy-paste)
**Claude Code**
```bash
claude mcp add mt5-quant -- mt5-quant --stdio
```
**Claude Desktop** — `~/Library/Application Support/Claude/claude_desktop_config.json`
```json
{ "mcpServers": { "mt5-quant": { "command": "mt5-quant", "args": ["--stdio"] } } }
```
**Cursor** — `.cursor/mcp.json` (same JSON shape as Claude Desktop)
```json
{ "mcpServers": { "mt5-quant": { "command": "mt5-quant", "args": ["--stdio"] } } }
```
**opencode** — `.opencode/opencode.jsonc`
```jsonc
{ "mcp": { "mt5-quant": { "type": "local", "command": ["mt5-quant", "--stdio"], "enabled": true } } }
```
**Codex CLI** — `~/.codex/config.toml`
```toml
[mcp_servers.mt5-quant]
command = "mt5-quant"
args = ["--stdio"]
```
Then finish setup (auto-detects Wine + MT5 paths):
```bash
bash scripts/setup.sh
```
> **Tip:** you can also just ask your LLM coding agent to do all of the above:
> *"Please install mt5-quant from https://github.com/masdevid/mt5-quant — download or build it, run its `scripts/setup.sh` to auto-detect Wine and MT5 paths, and register it as an MCP server."*
### First Backtest
@@ -226,6 +282,19 @@ For crashes or unexplained failures during backtest/compile/optimization:
**[Full Troubleshooting Guide →](docs/TROUBLESHOOTING.md)**
## Documentation
| Doc | Description |
|-----|-------------|
| [QUICKSTART](docs/QUICKSTART.md) | Step-by-step install, setup, and first backtest |
| [MCP_TOOLS](docs/MCP_TOOLS.md) | Full input/output schemas for all 90 tools |
| [CONFIG](docs/CONFIG.md) | Configuration reference (`config/mt5-quant.yaml`) |
| [ARCHITECTURE](docs/ARCHITECTURE.md) | Design principles and internal module layout |
| [REMOTE_AGENTS](docs/REMOTE_AGENTS.md) | Distribute optimization across Linux agents via Wine |
| [TROUBLESHOOTING](docs/TROUBLESHOOTING.md) | Platform-specific fixes for Wine, MT5, and backtests |
Full index with reading order: [docs/INDEX.md](docs/INDEX.md)
---
## Acknowledgements
+154
View File
@@ -0,0 +1,154 @@
# Security Policy
This document describes how to report security issues in **mt5-quant**, and the
secure-development practices the project follows. It backs the OpenSSF Best
Practices criteria `vulnerability_report_process`, `know_secure_design`, and
`know_common_errors`, and the signed-release process described under
[Signing](#signing-of-release-artifacts) backs `delivery_unsigned`.
mt5-quant is a Rust MCP server that drives MetaTrader 5 (under Wine/CrossOver)
to compile, backtest, and analyze trading Expert Advisors. It runs **locally**;
it never exposes a network listener and never transmits user data off the
machine except for the opt-in version self-check (HTTPS to GitHub). The security
notes below focus on the real risks in that model: untrusted input from MT5
report files, the user-supplied Wine/MT5 configuration, and the build/publish
pipeline.
## Vulnerability reporting process
We encourage responsible disclosure. If you discover a vulnerability, please
report it through **one** of the following channels (in order of preference):
1. **GitHub Security Advisories (recommended).** Open a private security
advisory at <https://github.com/masdevid/mt5-quant/security/advisories/new>.
This keeps the details confidential while we prepare a fix.
2. **Public GitHub Issue.** If you prefer, open a normal issue at
<https://github.com/masdevid/mt5-quant/issues>. Public reports are accepted;
please avoid posting working exploits in the open issue body — link to a
private gist or email the maintainer instead.
### What to include
A useful report contains:
- A clear description of the vulnerability and its impact.
- The affected version(s) (`mt5-quant --version` or the `version` field in
`Cargo.toml`).
- Steps to reproduce, or a proof-of-concept input (e.g. a crafted MT5 report,
`.set` file, or tool argument).
- Any relevant logs, crashes, or sanitizer/fuzzer output.
### Expected handling timeline
- **Acknowledgement:** within 7 days of a complete report.
- **Triage & severity assessment:** within 14 days.
- **Fix or mitigation plan:** timeline depends on severity; critical issues are
addressed as soon as a patch is ready, and a coordinated disclosure date is
agreed with the reporter when a private advisory is used.
- **Disclosure:** once a fixed release is published, the advisory (if used) is
made public and the fix is credited to the reporter unless they request
anonymity.
There is no formal contractual SLA; reports are handled on a best-effort,
case-by-case basis through the public channels above.
## Secure development practices (`know_secure_design`)
mt5-quant follows these practices to keep the code trustworthy:
- **Input validation at every tool boundary.** All 89 MCP tool handlers in
`src/tools/handlers/` parse and validate their arguments at the entry point
(using helpers such as `required_str`, `resolve_report`, and
`prepare_analysis`). Untrusted data — tool arguments, the filesystem, and MT5
output — is never trusted inside core logic.
- **No hardcoded secrets.** The source tree contains no embedded credentials,
API tokens, or private keys. The only secrets are user-provided: a local
`config/mt5-quant.yaml` (gitignored) holding the Wine/MT5 paths, and an
optional release PAT used only in CI. Neither is persisted by the project
itself.
- **Safe parsing of untrusted MT5 reports.** Backtest/optimization reports come
from MetaTrader 5 as either HTML or SpreadsheetML XML (`.htm.xml`, Build 48+).
Parsers in `src/analytics/extract.rs` and `src/optimization/parser.rs` use
explicit, bounded parsing (no `eval`, no regex-backed HTML/script execution)
and fail fast on malformed input. These parsers are additionally exercised by
the cargo-fuzz harness in `fuzz/` (see `dynamic_analysis*` criteria).
- **Least privilege at runtime.** The architecture enforces **a single MT5
instance** and never runs two backtests in parallel on the same Wine prefix.
When no display is available the pipeline falls back to headless/Xvfb mode; the
only external process launched is the user-configured Wine/MT5 binary.
- **Dependency auditing in CI.** Every push and pull request runs
`cargo-deny` (license + advisory + source checks) and the GitHub
`dependency-review` action. Vulnerable or non-compliant dependencies block the
build via the `protect-master` ruleset's required status checks.
- **Fuzzing of parsers.** `fuzz/` contains libFuzzer targets that feed arbitrary
bytes into the report/metrics parsers with debug assertions and integer-
overflow traps enabled, catching panics and invariant violations.
- **Isolation of the Wine/MT5 sandbox.** MT5 runs under the user's Wine prefix,
which is isolated from the host shell environment. The server does not grant
MT5 any elevated privileges.
- **Static analysis.** Clippy runs with `-D warnings` (warnings fail the build)
and GitHub CodeQL (Rust) scans every push/PR.
## Common implementation errors we avoid (`know_common_errors`)
The codebase is deliberately written to avoid well-known Rust/systems pitfalls:
- **Integer overflow.** Rust's release builds disable overflow checks by
default, so arithmetic on deal volumes, prices, and cumulative PnL uses
`checked_*`/`saturating_*` operations or is validated at parse time. The
fuzz harness re-enables `debug-assertions` so overflow traps fire during
testing.
- **Path traversal in `.set` file handling.** `.set` files (UTF-16LE parameter
files for EAs) are read/written through helpers in
`src/tools/handlers/setfiles.rs`. Paths are resolved against the configured
tester profiles directory and normalized; user-supplied filenames are never
interpolated into shell commands and are confined to that directory.
- **UTF-16LE encoding pitfalls.** MetaTrader strips `||Y` flags from UTF-8 `.set`
files, so all writes use UTF-16LE with an explicit BOM and `chmod 444`. The
helpers never assume UTF-8, avoiding silent mojibake / flag loss.
- **Avoiding `unsafe`.** The crate uses no `unsafe` blocks in application logic;
all FFI with Wine/MT5 is performed via the `std::process` API and the system
`curl`/`gpg` binaries, not raw pointer manipulation.
- **Proper error propagation.** Errors are returned as typed `Result`s and
propagated with `?`; there are no swallowed errors or empty `catch`-and-ignore
paths. Invalid states halt fast with descriptive messages (Fail-Fast
philosophy) rather than being silently patched.
- **No injection via shell.** Wine/MT5 is launched with an argument vector (not
a constructed shell string), eliminating command-injection from config values.
## Signing of release artifacts
To satisfy `delivery_unsigned`, **release artifacts are GPG-signed**:
- Each GitHub Release publishes the macOS (`mcp-mt5-quant-macos-arm64.tar.gz`)
and Linux (`mcp-mt5-quant-linux-x64.tar.gz`) binaries **plus** their detached
ASCII-armored signatures (`*.tar.gz.asc`), and the project's public key at
**`docs/signing-key.asc`**.
- The signing key is an RSA-4096 key generated for the `mt5-quant releases`
identity. The public half is committed to the repository; the private half is
held only in CI secrets (`GPG_PRIVATE_KEY`, `GPG_PASSPHRASE`) and is never
committed.
- Signing is performed in CI (`release.yml`) with
`gpg --detach-sign --armor` and is **guarded** so the release still succeeds
if the signing secrets are not configured — it simply omits the signatures in
that case.
### Verifying a downloaded release
```bash
# 1. Import the project public key
gpg --import docs/signing-key.asc
# 2. Verify a release artifact
gpg --verify mcp-mt5-quant-linux-x64.tar.gz.asc \
mcp-mt5-quant-linux-x64.tar.gz
# A good signature from "mt5-quant releases <releases@mt5-quant.local>"
# confirms the artifact was produced by the project's release pipeline.
```
## Supported versions
Security fixes are applied to the latest released version on the `master`
branch. Older releases are not back-ported; users should upgrade to the current
release to receive fixes.
+27
View File
@@ -0,0 +1,27 @@
# cargo-deny configuration — https://embarkstud.io.github.io/cargo-deny/
[advisories]
version = 2
yanked = "deny"
[licenses]
version = 2
allow = [
"MIT",
"Apache-2.0",
"Apache-2.0 WITH LLVM-exception",
"BSD-2-Clause",
"BSD-3-Clause",
"ISC",
"Unicode-3.0",
"Zlib",
"CDLA-Permissive-2.0",
# via option-ext ← dirs-sys ← dirs
"MPL-2.0",
]
[bans]
multiple-versions = "warn"
[sources]
unknown-registry = "warn"
+23
View File
@@ -0,0 +1,23 @@
# Documentation Index
**Start here:** [QUICKSTART.md](QUICKSTART.md) — install MT5-Quant, run `scripts/setup.sh` to auto-detect Wine/MT5 paths, register it with your LLM client, and complete your first backtest.
| Doc | Description |
|-----|-------------|
| [QUICKSTART.md](QUICKSTART.md) | Step-by-step install, setup, and first backtest |
| [MCP_TOOLS.md](MCP_TOOLS.md) | Full input/output schemas for all 90 tools |
| [CONFIG.md](CONFIG.md) | Configuration reference (`config/mt5-quant.yaml`) |
| [ARCHITECTURE.md](ARCHITECTURE.md) | Design principles and internal module layout |
| [REMOTE_AGENTS.md](REMOTE_AGENTS.md) | Distribute optimization across Linux agents via Wine |
| [TROUBLESHOOTING.md](TROUBLESHOOTING.md) | Platform-specific fixes for Wine, MT5, and backtests |
## Suggested reading order
1. [QUICKSTART.md](QUICKSTART.md) — get a working installation and first report
2. [MCP_TOOLS.md](MCP_TOOLS.md) — browse what your LLM agent can call
3. [CONFIG.md](CONFIG.md) — tune symbols, timeframes, deposit defaults
4. [TROUBLESHOOTING.md](TROUBLESHOOTING.md) — when something fails
5. [ARCHITECTURE.md](ARCHITECTURE.md) — how the pipeline works under the hood
6. [REMOTE_AGENTS.md](REMOTE_AGENTS.md) — scale up optimization throughput
These files are also bundled in every release tarball and MCP package, so installed copies stay available offline.
+65
View File
@@ -0,0 +1,65 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGqN954BEAC8tSNRLfu4BgrTk/k83jWkB3aHBrgohPTGXemEGSTP6WZbeI+6
NSHw70lB9MlPx65AhzJoLVxIzI9SwBBtoQ+DzoK6QIYaJZkstbMZi5U8rxNXTpU7
4jyScoLb097ZYpxNhDK5hrxRxN0rg82jmSgC+PrVXuGohgkbHbVHjQJouLr3MuDZ
d+y1ET+WQ5kBSN/KIzKmnIxjaXFxSi/S90n3pLJADhtcJ0QKdOzKbNf+G2mdZrcG
XbnphbfgRRWhg+mq/om+SNVz9LEdrrueoI2vAphWvuKSXJxUEAb/hqvQnInbiBIq
E02K3A3Dypf80jGfB/93wGTx7thxlWzedczm57iZJ0DJu2OT1aXF0uK1hnG9jEUB
yrruXBuziSLNnQS8X3prbOAMCK3TWEV+l1+F6uK9Dp6vuvSFzmMfEJW+MRK7Yhl7
7Bi4TlEaWo72DOV7U3XvvcU6kxSdU/CnC79ibMhM8+u0rPa9089Mo0BSqyoP/GFX
5G2hKjB+gBX4brCT0KMGPal18aaZ9Ej6q3XiziLNY2K9AyXMrFtmwFcnpNQxuA1e
yewIQ7daV6MJM/DQc8haLr8DxisWEX57OxJywu8l7aldsrQQrxSOO24Or7AUG2lA
eaGNM14njRU4zJ6BNKnozv3JJTRT6TpqWv6ZSPgXh+BvhdC032uVOJdKCQARAQAB
tC1tdDUtcXVhbnQgcmVsZWFzZXMgPHJlbGVhc2VzQG10NS1xdWFudC5sb2NhbD6J
Am4EEwEIAFgWIQTvHKai4tDO/2HcfwgCVqDDam1ecgUCao33nhsUgAAAAAAEAA5t
YW51MiwyLjUrMS4xMiwwLDMDGy8EBQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheA
AAoJEAJWoMNqbV5y71IP+we/bZUebidePUvLLlHmL2QKWd4iOqlw4zeQDTuEwRTf
S3qDmiIDytEAJ6AyhQIC+Po4in9hywDOUZnCXgz+VPsWWjdeIyEGNPzmmUU8vcMr
R3VgeQni3b2qsx5RQnDGLz6y2aF6QdMK8TMlG8QVypsJ8N74A68+voEE2vli6nGJ
Jf1Hucn4t7cCWCDgpsSrL+dSM4DXZ0Uz1XByXUXvrIf8/XkFgk6l0V7V24uUdOUl
X5QVL/414wOC5UvzrsuZtE19gdP7BH5IHWAmBwx8CGOuKGiwEZydqnjrGykMCY1s
80pe6LWYOWsWAmu12lzNlFG/1JxL106YqnMukPn1CTMzWnrEAriRSKVuLN2KvUHd
+QNAbxdBbZCaAmEkoUoBnk/7OEgcmeri8F0PnElRIN7IzJBcqG0JZKx5VAudLveX
+vNU469Jh9isxCmev6rpI7QreCWUF/QeKgDGcCKp8acJQqfmACMgYGFkcS9OrCnL
qVGGj16IVEIYs4yRde9X4pYL4PTlv10QWpcTaoaMpOmsr2YkYORHE/QWya6+Pf4h
Dj1NZSof+RuaYjkIFryog8IPtVuRbg7T8UIT9K9wTKf1sRSFCcF1EAYwy0yv5Ch6
qCZUQtjhHf7zttsSiFtAfcF0ymnI8Vunv099tpTqKwB0u0ke5+yjNJqPOb7y8Nmm
uQINBGqN954BEADmekxbtTMLr0ayftAh3ZcmNCVSJrn3tSEkmyPCeI2yBxEg7J+H
ebu+jzleI1jOi4Us6l4mD+GFfXub09LldryugjFLcvI05/vgtlFErC9zEFv+1L5v
0RylV94PJXJFxxdDUUp4/cdERg+QT/h5jLZhoHKvUQ4PnoTOpEjTElLuH/1Zk480
MbHkB0fjm4DV1MzG1pYf8QaSkTCEhnXC7SvnlcerU1LuH33VWXlkXHhho3mho3qK
alf8yjG0PlH+ikvqEHvzOH5SCzUD2hI84/p7vtBvPoQvGdzBIhP+m6QsbeGbxt94
UYBUudM1c405cYPf1p1wl9Oz96MvH4IM4ucS6c3WK33ALwgdYqA7kbNyhSqk+o+a
T7zSroCHRhDaVxHH+Qlad8YRVoIGoX24650CbmuiJ7N0SHyny2AFk15EMzoHcI28
3joSfkUqg7io7/w/k7q0b9fbVkvMn+zQqpYeo7xPqXVDcBpfPXLQZzQgCUiweUbi
j8rxFEHYFGXxKm2NxZdX5t8ss14Ggv16MNM8/fwDfJ7Scxo5y1TFFSZgNs3dAcdd
zhBbxHRZTlxyan24O3xrF+aP6BsiXyHUQs/WPYewen0eEWXrfRTr/GgfIONGSk6S
IWKwbMLnPvkMATwFee8Pf75QO9keNB1nX4dEGeKLhEC1xLJvS8yaNmJorQARAQAB
iQSIBBgBCAA8FiEE7xymouLQzv9h3H8IAlagw2ptXnIFAmqN954bFIAAAAAABAAO
bWFudTIsMi41KzEuMTIsMCwzAhsuAkAJEAJWoMNqbV5ywXQgBBkBCAAdFiEETywc
/FBIkF+vVFdyEHpgkPDwwZsFAmqN954ACgkQEHpgkPDwwZvVwQ//cLwJyiAOfpce
NOBmsuvz4M5xSvxiMpW3N93of3FwGXtWxp0pdlljVxmx1iv+wwK5uYQsbBPgC48J
JvUr7W1V/6QcFNQbOevLA5GVcm533gGgeQ+hrptUPL0l3W610DnUO6yQEWRdGyKG
EM+uQZe20CKb/NyUiOTf9s9zO3rJT/7IYWTitpxvK1t/83FAOvAD6oVQNTwgHVGL
oKTC2i7MdQ2M9Hc64UGG2D28L/plLUwkLKLDtlcueTv+qSi7RZMjQhpBfhnoH9by
pOXLNJCFUhVIAIMB5WncjqpAbXneGgLPhDe8napFKB4Mxky1+jycFq+AgVA98/y9
y3hmOIrK+lnTan1rtUYh6O0Tf9DlDM58/Dr4Pbt7S949WGccEtArCX1B7SqcgvO7
TP5ENFHmaSTwheTtPnzgiwrICO+BXL9bYyb/oUj0U1zIiVQxDmgiz5M9CM8/5221
tPH8lFLAGtGvnpjlRzcKX7WL8+fD8V4fZCC4mzt7xMZdRZB5D5upE+7N/EdjNEM0
UBoBbpZ0muFWtp5f1m2Fu1zJpEDIimfKKl+YHFkODyxOXGeOrRLQB42ecFDOCg0X
ie5hqQ+PnASiaHMC48ZVTYy6T4+rcGRIcRg+FzvK0Lef8J1klQwN85rc71AIV7a1
ZWzn35vUqW1YTjBA4mKSGp050BFpNFYFUA/+MW1iqDGwvZC76KXlrA1q9OlTs41q
9oBW2BgpV7Xaj9AEYC87LTbXGDL5QfLisI7NAVkXKUkRRyhG6J2NiDyPKxnE9V2Y
eFzq6ENSj7MWSonatQmEyGXeMkbBLfddCssb0FU934ACCzccchB1mHA+CqimKiPe
mMMW3OPZ0u4lvTrFPktL/PkPHyV75xgQvr7JsKnjdT9h3VHsm4krhC3f2yi5+orb
nicS5NSGv1l4xNJbqI2nfMk02VY64WOMqduVo12KQeazPO0JBRVFXOMGrmiuluPU
EFDIbu8AZdU67mhIvIDuP9bkm53EOXT3xk0GZ9iiUhlnvbEW9Oewbw9Lircz3Wwp
VeQtp+fbaLrWosAmpeUIxhUP9RJmfHjEMJ+AGrLeAZ3UJQMoR2uUoN45/c+aiUPT
6tNH8BXhloyr6hIfCpkPzbTKpu2jhf1vDTyIigIt5wZoshCbVDy680+1keIPxEuM
HR1s1Sm5NkvX9VRxk7ZSjv9BPL2GVDmPez0+LlBI1hxHDrzhH8iCNka15GL8eNTR
JW3zeFWpgfbqm6XP90bKLRSZXybr1vz5k0znWXje4uIW8iuHiPBNi0pHRJktZ0Fo
z/wtRZUl9wzbQnYqmewAt7hzLc379KsYexOesJoDLgq8iCSyWZbUgC/euMZV9emL
GqF/jfDJVQqPMNY=
=sjIH
-----END PGP PUBLIC KEY BLOCK-----
+4
View File
@@ -0,0 +1,4 @@
target
corpus
artifacts
coverage
+1204
View File
File diff suppressed because it is too large Load Diff
+33
View File
@@ -0,0 +1,33 @@
[package]
name = "mt5-quant-fuzz"
version = "0.0.0"
publish = false
edition = "2021"
[package.metadata]
cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.4"
[dependencies.mt5-quant]
path = ".."
[[bin]]
name = "parse_metrics"
path = "fuzz_targets/parse_metrics.rs"
test = false
doc = false
bench = false
# Enable debug/overflow assertions during fuzzing. cargo-fuzz builds fuzz
# targets in release mode by default, so we explicitly turn debug-assertions
# back on for the release profile. This backs the
# `dynamic_analysis_enable_assertions` CII criterion: any debug_assert! or
# arithmetic-overflow check inside the crate is active while libFuzzer feeds
# arbitrary input, so crashes from invariant violations are caught as bugs.
[profile.release]
debug-assertions = true
[profile.dev]
debug-assertions = true
+36
View File
@@ -0,0 +1,36 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use mt5_quant::models::Metrics;
// Fuzz target for the pure, I/O-free MT5 backtest report metrics parser.
//
// `Metrics::from_html` takes an arbitrary `&str` and extracts the numeric
// summary metrics from an MT5 HTML/XML report. It is panic-free on bad input:
// every numeric parse uses `unwrap_or`, regex patterns are compile-time
// constants (never panicking), and the function returns `Option<Metrics>`
// (None when no metrics are found). We return early on non-UTF-8 input and on
// the None case, so the fuzzer only ever exercises the parser itself.
fuzz_target!(|data: &[u8]| {
let Ok(text) = std::str::from_utf8(data) else {
// Parser only accepts UTF-8 text; arbitrary byte sequences are
// out of scope and must not cause a crash.
return;
};
if let Some(metrics) = Metrics::from_html(text) {
// Force the computed fields to be materialized so any logic error
// inside the parser surfaces during fuzzing.
let _ = (
metrics.net_profit,
metrics.profit_factor,
metrics.max_dd_pct,
metrics.sharpe_ratio,
metrics.total_trades,
metrics.recovery_factor,
metrics.win_rate_pct,
metrics.gross_profit,
metrics.gross_loss,
);
}
});
+4 -4
View File
@@ -7,13 +7,13 @@
"url": "https://github.com/masdevid/mt5-quant",
"source": "github"
},
"version": "1.34.1",
"version": "1.34.3",
"packages": [
{
"registryType": "mcpb",
"version": "1.34.1",
"identifier": "https://github.com/masdevid/mt5-quant/releases/download/v1.34.1/mcp-mt5-quant-macos-arm64.tar.gz",
"fileSha256": "TBD_CI_WILL_UPDATE",
"version": "1.34.3",
"identifier": "https://github.com/masdevid/mt5-quant/releases/download/v1.34.3/mcp-mt5-quant-macos-arm64.tar.gz",
"fileSha256": "2f303cb3deb62e47742e5eb4d73a0e3edf200aadf7db9da39a0ef2151d064171",
"transport": {
"type": "stdio"
},
+7 -1
View File
@@ -7,6 +7,12 @@ use crate::models::metrics::Metrics;
pub struct DealAnalyzer;
impl Default for DealAnalyzer {
fn default() -> Self {
Self::new()
}
}
impl DealAnalyzer {
pub fn new() -> Self {
Self
@@ -412,7 +418,7 @@ impl DealAnalyzer {
}
}
events.sort_by(|a, b| a.0.cmp(&b.0));
events.sort_by_key(|event| event.0);
let mut count = 0;
let mut peak = 0;
+9 -1
View File
@@ -8,6 +8,12 @@ use crate::models::{Deal, Metrics};
pub struct ReportExtractor;
impl Default for ReportExtractor {
fn default() -> Self {
Self::new()
}
}
impl ReportExtractor {
pub fn new() -> Self {
Self
@@ -334,7 +340,9 @@ impl ReportExtractor {
if raw.starts_with(&[0xFF, 0xFE]) || raw.starts_with(&[0xFE, 0xFF]) {
// UTF-16 BOM
let text = String::from_utf16_lossy(
raw.chunks_exact(2)
raw.as_chunks::<2>()
.0
.iter()
.map(|c| u16::from_le_bytes([c[0], c[1]]))
.collect::<Vec<_>>()
.as_slice(),
+3 -1
View File
@@ -359,7 +359,9 @@ impl MqlCompiler {
};
if raw.starts_with(&[0xFF, 0xFE]) {
raw[2..]
.chunks_exact(2)
.as_chunks::<2>()
.0
.iter()
.map(|c| u16::from_le_bytes([c[0], c[1]]))
.filter_map(|c| char::from_u32(c as u32))
.collect()
+60
View File
@@ -0,0 +1,60 @@
pub mod analytics;
pub mod compile;
pub mod models;
pub mod optimization;
pub mod pipeline;
pub mod storage;
pub mod tools;
pub mod mcp_server;
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct McpRequest {
pub jsonrpc: String,
pub id: Option<serde_json::Value>,
pub method: String,
pub params: Option<serde_json::Value>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct McpResponse {
pub jsonrpc: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<McpError>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct McpError {
pub code: i32,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<serde_json::Value>,
}
#[derive(Debug, serde::Serialize)]
pub struct ServerInfo {
pub name: String,
pub version: String,
}
#[derive(Debug, serde::Serialize)]
pub struct InitializeResult {
pub protocol_version: String,
pub capabilities: ServerCapabilities,
pub server_info: ServerInfo,
}
#[derive(Debug, serde::Serialize)]
pub struct ServerCapabilities {
pub experimental: serde_json::Value,
pub tools: ToolCapabilities,
}
#[derive(Debug, serde::Serialize)]
pub struct ToolCapabilities {
pub list_changed: bool,
}
+9 -68
View File
@@ -1,24 +1,16 @@
mod analytics;
mod compile;
mod models;
mod optimization;
mod pipeline;
mod storage;
mod tools;
mod mcp_server;
use anyhow::Result;
use clap::Parser;
use serde_json::{json, Value};
use serde_json::json;
use std::io::{stdout, Write};
use std::time::Instant;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::TcpListener;
use tracing::{error, info};
use crate::models::Config;
use crate::pipeline::backtest::{BacktestParams, BacktestPipeline};
use mt5_quant::mcp_server::{McpServer, Notification};
use mt5_quant::models::Config;
use mt5_quant::pipeline::backtest::{BacktestParams, BacktestPipeline};
use mt5_quant::{McpError, McpRequest, McpResponse};
#[derive(Parser)]
#[command(name = "mt5-quant")]
@@ -45,57 +37,6 @@ struct Cli {
startup_delay: Option<u64>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct McpRequest {
jsonrpc: String,
id: Option<Value>,
method: String,
params: Option<Value>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct McpResponse {
jsonrpc: String,
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
result: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<McpError>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct McpError {
code: i32,
message: String,
#[serde(skip_serializing_if = "Option::is_none")]
data: Option<Value>,
}
#[derive(Debug, serde::Serialize)]
pub struct ServerInfo {
name: String,
version: String,
}
#[derive(Debug, serde::Serialize)]
pub struct InitializeResult {
protocol_version: String,
capabilities: ServerCapabilities,
server_info: ServerInfo,
}
#[derive(Debug, serde::Serialize)]
pub struct ServerCapabilities {
experimental: Value,
tools: ToolCapabilities,
}
#[derive(Debug, serde::Serialize)]
pub struct ToolCapabilities {
list_changed: bool,
}
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
@@ -178,9 +119,9 @@ async fn run_test_launch(ea: Option<String>, startup_delay: Option<u64>) -> Resu
async fn run_stdio_server() -> Result<()> {
info!("Starting MT5-Quant MCP server on stdio");
let server = std::sync::Arc::new(mcp_server::McpServer::new());
let server = std::sync::Arc::new(McpServer::new());
let (notification_tx, mut notification_rx) =
tokio::sync::mpsc::unbounded_channel::<mcp_server::Notification>();
tokio::sync::mpsc::unbounded_channel::<Notification>();
server.set_notification_sender(notification_tx).await;
// Spawn notification sender task
@@ -254,7 +195,7 @@ async fn run_tcp_server(port: u16) -> Result<()> {
info!("Starting MT5-Quant MCP server on TCP port {}", port);
let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).await?;
info!("Listening on 127.0.0.1:{}", port);
info!("Running TCP server on port {}", port);
loop {
let (socket, addr) = listener.accept().await?;
@@ -272,7 +213,7 @@ async fn handle_connection(socket: tokio::net::TcpStream) -> Result<()> {
let (reader, mut writer) = socket.into_split();
let mut reader = BufReader::new(reader);
let mut line = String::new();
let server = mcp_server::McpServer::new();
let server = McpServer::new();
loop {
line.clear();
+6
View File
@@ -31,6 +31,12 @@ pub struct McpServer {
notification_tx: Arc<Mutex<Option<mpsc::UnboundedSender<Notification>>>>,
}
impl Default for McpServer {
fn default() -> Self {
Self::new()
}
}
impl McpServer {
pub fn new() -> Self {
let config = ModelsConfig::load().unwrap_or_default();
+3 -1
View File
@@ -16,7 +16,9 @@ fn read_file_as_utf8(path: &Path) -> Result<String> {
if bytes.len() >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE {
// UTF-16LE with BOM - skip the 2-byte BOM and decode
let utf16_data: Vec<u16> = bytes[2..]
.chunks_exact(2)
.as_chunks::<2>()
.0
.iter()
.map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
.collect();
String::from_utf16(&utf16_data).map_err(|e| anyhow!("Failed to decode UTF-16LE: {}", e))
+15 -3
View File
@@ -17,6 +17,12 @@ pub struct OptimizationPass {
pub struct OptimizationParser;
impl Default for OptimizationParser {
fn default() -> Self {
Self::new()
}
}
impl OptimizationParser {
pub fn new() -> Self {
Self
@@ -106,14 +112,18 @@ impl OptimizationParser {
if raw[0] == 0xFF && raw[1] == 0xFE {
// UTF-16 LE with BOM
let u16_vec: Vec<u16> = raw[2..]
.chunks_exact(2)
.as_chunks::<2>()
.0
.iter()
.map(|c| u16::from_le_bytes([c[0], c[1]]))
.collect();
return Ok(String::from_utf16_lossy(&u16_vec));
} else if raw[0] == 0xFE && raw[1] == 0xFF {
// UTF-16 BE with BOM
let u16_vec: Vec<u16> = raw[2..]
.chunks_exact(2)
.as_chunks::<2>()
.0
.iter()
.map(|c| u16::from_be_bytes([c[0], c[1]]))
.collect();
return Ok(String::from_utf16_lossy(&u16_vec));
@@ -128,7 +138,9 @@ impl OptimizationParser {
// Try UTF-16 without BOM
if raw.len() % 2 == 0 {
let u16_vec: Vec<u16> = raw
.chunks_exact(2)
.as_chunks::<2>()
.0
.iter()
.map(|c| u16::from_le_bytes([c[0], c[1]]))
.collect();
let text = String::from_utf16_lossy(&u16_vec);
+12 -4
View File
@@ -22,7 +22,9 @@ fn read_set_file_as_utf8(path: &Path) -> Result<String> {
let bytes = fs::read(path)?;
if bytes.len() >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE {
let utf16_data: Vec<u16> = bytes[2..]
.chunks_exact(2)
.as_chunks::<2>()
.0
.iter()
.map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
.collect();
String::from_utf16(&utf16_data).map_err(|e| anyhow!("Failed to decode UTF-16LE: {}", e))
@@ -870,7 +872,9 @@ impl BacktestPipeline {
let text = if bytes.len() >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE {
// UTF-16 LE with BOM
let words: Vec<u16> = bytes[2..]
.chunks_exact(2)
.as_chunks::<2>()
.0
.iter()
.map(|c| u16::from_le_bytes([c[0], c[1]]))
.collect();
String::from_utf16_lossy(&words).to_string()
@@ -1288,7 +1292,9 @@ impl BacktestPipeline {
if content.starts_with(&[0xFF, 0xFE]) || content.starts_with(&[0xFE, 0xFF]) {
let text = String::from_utf16_lossy(
content
.chunks_exact(2)
.as_chunks::<2>()
.0
.iter()
.map(|c| u16::from_le_bytes([c[0], c[1]]))
.collect::<Vec<_>>()
.as_slice(),
@@ -1450,7 +1456,9 @@ impl BacktestPipeline {
let raw = fs::read(&terminal_ini).unwrap_or_default();
let text = if raw.starts_with(&[0xFF, 0xFE]) {
raw[2..]
.chunks_exact(2)
.as_chunks::<2>()
.0
.iter()
.map(|c| u16::from_le_bytes([c[0], c[1]]))
.collect::<Vec<_>>()
.iter()
+7
View File
@@ -40,6 +40,13 @@ impl Stage {
#[allow(dead_code)]
pub struct StageExecutor;
#[allow(dead_code)]
impl Default for StageExecutor {
fn default() -> Self {
Self::new()
}
}
#[allow(dead_code)]
impl StageExecutor {
pub fn new() -> Self {
+4 -3
View File
@@ -367,7 +367,8 @@ impl ReportDb {
keep_last: usize,
) -> Result<Vec<(String, String, Option<String>)>> {
let conn = self.connect()?;
let count: usize = conn.query_row("SELECT COUNT(*) FROM reports", [], |r| r.get(0))?;
let count: i64 = conn.query_row("SELECT COUNT(*) FROM reports", [], |r| r.get(0))?;
let count = count as usize;
if count <= keep_last {
return Ok(Vec::new());
@@ -404,8 +405,8 @@ impl ReportDb {
pub fn count(&self) -> Result<usize> {
let conn = self.connect()?;
let n: usize = conn.query_row("SELECT COUNT(*) FROM reports", [], |r| r.get(0))?;
Ok(n)
let n: i64 = conn.query_row("SELECT COUNT(*) FROM reports", [], |r| r.get(0))?;
Ok(n as usize)
}
/// Get the latest report by created_at (most recent first)
+3 -1
View File
@@ -8,7 +8,9 @@ fn read_file_as_utf8(path: &str) -> Result<String> {
let bytes = fs::read(path)?;
if bytes.len() >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE {
let utf16_data: Vec<u16> = bytes[2..]
.chunks_exact(2)
.as_chunks::<2>()
.0
.iter()
.map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
.collect();
String::from_utf16(&utf16_data)
+1 -1
View File
@@ -1207,7 +1207,7 @@ pub async fn handle_get_mt5_logs(config: &Config, args: &Value) -> Result<Value>
}
// Sort by modification time (newest first)
log_files.sort_by(|a, b| b.1.cmp(&a.1));
log_files.sort_by_key(|file| std::cmp::Reverse(file.1));
if let Some((latest_log, _)) = log_files.first() {
result["found"] = json!(true);