Guide for contributors working on ascend-tools.
- Rust stable toolchain (1.85+, edition 2024)
- Python 3.11+ (for PyO3 bindings and linting)
- Node.js 24+ (for napi-rs bindings and JS tests)
- uv (Python package manager)
Clone and run the setup script:
git clone https://github.com/ascend-io/ascend-tools.git
cd ascend-tools
bin/setupbin/setup installs Rust (via rustup) and uv if missing, verifies all tools are on PATH, and installs JS dependencies. If it errors, follow the printed instructions (usually just sourcing a shell env) and re-run.
bin/build # build everything (Rust + Python + JS)
bin/build-rs # Rust workspace only (cargo build --workspace)
bin/build-py # Python wheel (maturin develop)
bin/build-js # JS native module (napi build)bin/build-rs accepts extra args (e.g., bin/build-rs --release).
bin/check # full CI suite: version check + lint + test (Rust, Python, JS)
bin/test # Rust tests only (cargo test --workspace)bin/check is what CI runs. Always run it before committing.
| Step | Command |
|---|---|
bin/check-version |
Verify version is consistent across all Cargo.toml, pyproject.toml, package.json |
bin/check-rs |
cargo fmt --all --check, cargo clippy --workspace -- -D warnings, cargo test --workspace |
bin/check-py |
ruff check ., ruff format --check ., ty check |
bin/check-js |
npm run build, npm test (ava) |
bin/format # auto-format everything
bin/format-rs # cargo fmt --all
bin/format-py # ruff format .bin/install # install Rust binary + Python package locally
bin/install-rs # cargo install --path crates/ascend-tools-cli
bin/install-py # uv tool install .Six Rust crates, two language bridges:
ascend-tools-core → ascend-tools-mcp → ascend-tools-cli
→ ascend-tools-tui →
→ ascend-tools-py (PyO3, cdylib)
→ ascend-tools-js (napi-rs, cdylib)
The four workspace crates (core, mcp, tui, cli) share the root Cargo.toml workspace. The two binding crates (py, js) are standalone cdylibs with their own Cargo.lock (required by maturin/napi-rs build tooling).
| Crate | Published to | Description |
|---|---|---|
ascend-tools-core |
crates.io | SDK: typed HTTP client, auth, models, SSE |
ascend-tools-mcp |
crates.io | MCP server (rmcp) |
ascend-tools-tui |
crates.io | Interactive TUI (ratatui) |
ascend-tools-cli |
crates.io | CLI binary (clap) |
ascend-tools-py |
PyPI | PyO3 bindings (maturin) |
ascend-tools-js |
npm | napi-rs bindings (@napi-rs/cli) |
All six crates and both language packages share one version. To bump:
bin/bump-version # minor bump (default)
bin/bump-version --patch # patch bump
bin/bump-version --major # major bumpThis updates all Cargo.toml files (including inter-crate dependency versions), pyproject.toml, package.json, and regenerates all lock files.
Releases are triggered by git tags. bin/release handles the full flow:
bin/releasePre-flight checks:
- Working tree is clean
- HEAD matches
origin/main bin/checkpasses- Tag doesn't exist on GitHub
- Version doesn't exist on PyPI, npm, or crates.io
On tag push, four GitHub Actions workflows run in parallel:
release.yml: builds standalone binaries, creates GitHub Releaserelease-python.yml: builds wheels (4 platforms), publishes to PyPI (trusted publisher)release-javascript.yml: builds native modules (4 platforms), publishes to npm (trusted publisher)release-rust.yml: publishes 4 crates to crates.io (trusted publisher)
All registry publishing uses OIDC trusted publishers — no long-lived tokens.
Integration tests run via GitHub Actions (see .github/workflows/integration.yml). They require ASCEND_SERVICE_ACCOUNT_ID, ASCEND_SERVICE_ACCOUNT_KEY, and ASCEND_INSTANCE_API_URL environment variables pointing to a test instance.
- Add the clap subcommand in
crates/ascend-tools-cli/src/ - Wire it into
cli.rs - Update
skill-cli.mdto keep the skill template in sync - Run
bin/check
- Add the method to
AscendClientincrates/ascend-tools-core/src/client.rs - Add the tool to
AscendMcpServerincrates/ascend-tools-mcp/src/server.rs - Add parameter structs to
crates/ascend-tools-mcp/src/params.rs - Update
skill-mcp.md - Run
bin/check
- Commits use Conventional Commits (
feat:,fix:,refactor:, etc.) - Core library avoids panics in runtime paths (no
unwrap/expectoutside tests) - HTTP client is sync (
ureq); async adapters live at boundaries (MCP usesspawn_blocking, JS uses napiAsyncTask) - PyO3 uses
pythonizefor direct Rust-to-Python dict conversion (no JSON intermediary) - napi-rs uses
serde-jsonfeature for direct Rust-to-JS object conversion - MCP parameters use
schemarsfor automatic JSON Schema generation - CLI prints tables by default, JSON with
-o json