Contributing¶
Thanks for your interest in mcp-ltspice-qucs. This document covers
the basics of getting a development environment running and submitting
changes.
Before you contribute¶
By submitting a pull request to this repository, you agree to the terms of CLA.md. A short contributor license agreement that lets the Maintainer change how this Project is licensed in future without needing to track down every past contributor individually. You keep your own copyright; you're just granting the Maintainer the same relicensing rights over your contribution that they already have over the rest of the codebase.
No signature or bot step is required today: opening the PR is the agreement. Read CLA.md before you submit if you want the full terms.
Quick setup¶
git clone https://github.com/RFingAdam/mcp-ltspice-qucs
cd mcp-ltspice-qucs
uv sync
uv run pytest -q
On a machine with no simulators installed you should see 533 tests pass and 11 skip, and no failures. Every skip is gated on a missing binary (LTspice / ngspice / Qucs-S / Xyce) or is a tool with no required argument to make invalid; install a simulator (see the Installation guide) to exercise the simulator-driven paths.
Before opening a PR, run the same three commands CI runs. If these are clean, CI will be too:
Layout¶
The repo is a uv workspace with four packages under packages/:
rf-mcp-common: shared envelope, Touchstone I/O, E-series snap, loggingmcp-ltspice: LTspice + ngspice runner, filter synthesis, spec evalmcp-rf-analysis: skrf wrappers, regulatory band DBs, coex matrixmcp-qucs-s: Qucs-S native S-param + harmonic balance + microstrip
See ARCHITECTURE for the inter-server contract.
Running tests¶
uv run pytest # all packages
uv run pytest packages/mcp-ltspice/ # one package
uv run pytest -k "synthesis" # by keyword
uv run pytest -m ngspice # only simulator-integration
uv run pytest --cov # with coverage
Simulator-gated tests¶
Tests that need a real simulator carry a marker and skip cleanly when
the binary is absent: ngspice, ltspice, qucs, xyce. Availability
is decided by the package's own discovery function (e.g.
mcp_ltspice.runner.find_ltspice), so anything the runner can find:
$LTSPICE_PATH, $WINEPREFIX, the standard Windows / macOS / Wine
install paths: also un-skips the tests.
If -m ltspice skips when you believe LTspice is installed, run:
A None there means the runner cannot see it either. That is the bug
to report, not the skip. Setting $LTSPICE_PATH to a file that does not
exist logs a warning rather than failing silently.
To pin which simulator is used regardless of what is installed:
Linting & typing¶
Both run via the uv-managed dev tools:
uv run ruff format # auto-format
uv run ruff check --fix # autofix what's safe
uv run mypy packages/ # strict types
Pre-commit hooks (configured in .pre-commit-config.yaml, enable with
uv run pre-commit install) run ruff format + ruff check + mypy on
every commit.
Adding a new tool to a server¶
- Implement the underlying logic in a new module under
packages/<server>/src/<pkg>/. Pure functions. Don't touch FastMCP yet. - Write tests under
packages/<server>/tests/that exercise it without any simulator dependency where possible. - Wire it into
server.pywith@mcp.tool(description=...). Wrap the call inTimer()+ok(...)/error(...)so the response envelope stays consistent. - Run
uv run pytest packages/<server>and confirm green. - Add an entry to the per-server
README.mdtool catalogue. - Update
CHANGELOG.mdunder[Unreleased].
Adding a new MCP server¶
Three steps:
- Scaffold
packages/<new>/src/<module>/with the same layout as one of the existing servers. Add the module totool.hatch.build.targets.wheel.packages(and itssrcto the sdistonly-include), its command toproject.scripts, and any new dependencies toproject.dependenciesin the rootpyproject.toml. - Import the envelope and Touchstone helpers from
rf_mcp_common. Don't reimplement either. - Use Touchstone files for any inter-server data exchange. The contract is documented in ARCHITECTURE.
Testing conventions¶
- One module of source ↔ one test file, named
test_<module>.py. - Tests should not write to the repo working tree; use the
tmp_pathpytest fixture for any file output. - Fixtures that build sample networks live in the package's
conftest.py, not at the repo root, so packages stay decoupled. - Mark simulator-requiring tests with
@pytest.mark.ngspiceor@pytest.mark.ltspiceso they skip cleanly when the simulator is absent.
Commit messages¶
Conventional Commits style: <type>(<scope>): <subject>. Common types:
feat, fix, docs, test, refactor, chore, ci, perf.
Examples:
feat(rf-analysis): add 5G NR FR2 band lookupfix(ltspice): handle resonance singularity in shunt LC trapdocs: clarify Touchstone interop contract
Releases¶
We follow Keep a Changelog +
Semver. The repository publishes one PyPI
package, mcp-ltspice-qucs, with one version:
- Bump
versionin the rootpyproject.tomland runuv lock - Move entries from
[Unreleased]to a new dated section inCHANGELOG.md - Merge to
main, then push av<version>tag on that commit
The Release workflow (.github/workflows/release.yml) builds one
sdist and one wheel, checks them, and publishes to PyPI through
trusted publishing from the pypi environment.
Code of conduct¶
We follow the Contributor Covenant.
See CODE_OF_CONDUCT.md.