17 Commits

Author SHA1 Message Date
47beacc9e1 macOS: resolve device-model libs (validated on real Apple Silicon)
Hardware testing on a native Apple Silicon Mac (LTspice XVII 17.2.4) surfaced
that model/subcircuit-bearing circuits failed with 'Unable to find definition
of model' / 'Could not open library file': native Mac LTspice in batch mode
does NOT auto-include the standard device libs or search the lib dir the way
Wine does (Wine netlists the .asc itself on Linux, so this never showed there).

Two fixes:
- netlister.py: emit .lib standard.{bjt,mos,dio,jft} for each semiconductor
  family present, since Mac LTspice won't auto-include them in batch mode.
- runner.py: _resolve_lib_paths() rewrites bare-name .lib/.include refs to
  absolute paths under the Mac lib root before running -b (Mac LTspice can't
  find them by name). darwin-only; Linux path untouched.
- test_netlister.py: skip the Wine-oracle comparison class when wine is absent
  (it's a Linux-only oracle).

Validated on hardware: full suite 534 passed / 8 skipped on native macOS
(all 10 integration circuits simulate, incl. op-amp + BJT); Linux unchanged
(524 unit + 18 integration). Also answered Kevin's open questions on hardware:
#2 -- 'LTspice -b <netlist>' DOES run over bare SSH (Background session), so
mcltspice is fully headless on Mac since it always netlists itself; #3 --
examples are not pre-extracted on Mac (examples_exist reports false).
2026-07-03 15:46:26 -06:00
37caaf0310 Add native macOS support (no Wine) per Kevin's spec
Platform-branches the Wine-specific execution so Linux behaves identically
while macOS drives LTspice.app natively. Implemented from muel9560's measured
spec + validated where possible against a Wine -netlist oracle on Linux.

- config.py: get_launch() abstraction (argv prefix + env + path translator);
  IS_DARWIN, LTSPICE_BIN (env-overridable), LTSPICE_LIB_ROOT under
  ~/Library/Application Support/LTspice on macOS. Linux resolves to the
  historical Wine paths unchanged.
- runner.py: darwin branch uses the native binary, POSIX paths (no Z: map),
  plain -b (never -Run), and completion by polling for a stable .raw (the Mac
  GUI app's exit code is unreliable). Linux keeps communicate()+wait_for.
- netlister.py (new): asc_to_netlist() converts a schematic to a netlist on
  macOS, since the Mac CLI can't netlist a .asc. Union-find net extraction;
  all 15 templates netlist, topology matches the Wine -netlist oracle.
- library_tools.py: check_installation reports platform/binary/lib-root/
  version/gui-session on macOS.
- packaging/docs: drop 'Linux only'; document LTSPICE_BIN + GUI-session need.

Adversarial review fixes: restore kill+reap of the Wine subprocess on Linux
timeout (was orphaning the process -- a real regression); alias duplicate net
labels instead of aborting (sallen-key); synthesize NC_xx nodes for floating
pins (op-amps) to match the oracle; use asyncio.get_running_loop().

Linux unaffected: 476 unit + 10 integration (real Wine) still pass; +48 unit
+8 integration for the platform/netlister paths. macOS runtime (LTspice.app
invocation, GUI session, poll timing) needs validation on Kevin's hardware.
2026-07-03 12:35:18 -06:00
9c252606f7 Fix SameFileError in batch sweeps; guard self-copy in runner
run_parameter_sweep/run_monte_carlo write each variant .cir into a work_dir,
then call run_netlist with that same work_dir -- which tried to shutil.copy2
the file onto itself (SameFileError), so every batch run failed before
simulating. Add _safe_copy() that skips when src and dst resolve to the same
path, used at all four staging-copy sites in run_simulation/run_netlist.

Caught by headless claude-cli testing of the batch tools (no batch test
existed). Adds test_runner.py (_safe_copy unit tests) and a TestBatchSweeps
integration class.
2026-06-21 22:24:24 -06:00
ca6e15e751 Extract library_tools + resources; fix 3 dead resources
ltspice://symbols, ltspice://examples, ltspice://status raised TypeError on
read -- they called list_symbols/list_examples/check_installation, which are
@mcp.tool() FunctionTool objects (not callable). Split those three into plain
*_impl functions + thin tool wrappers, and point the resources at the impls.
The other library tools move verbatim.

Adds test_resources.py to read every resource (regression guard). Library tool
outputs verified byte-identical old-vs-new. server.py: 2168 -> 1876 lines.
2026-06-21 20:50:27 -06:00
3a7e6887aa Decompose server.py: shared _app.mcp + spicebook_tools module
Introduce the multi-file FastMCP layout. The mcp instance moves to _app.py
(imports nothing from tool modules, keeping the registration graph acyclic),
and the self-contained SpiceBook domain (5 tools + status resource + publish
prompt + 2 helpers) moves to spicebook_tools.py. server.py imports it for the
registration side effect.

Tool/resource/prompt counts unchanged (42/6/8); names identical. Repointed
test_spicebook's _read_netlist import. server.py: 2472 -> 2168 lines.
2026-06-21 19:54:23 -06:00
90cd07d9bc Test output_format and waveform_query without a simulator
17 tests: round_sig/compact_list rounding, downsample_indices stride vs
peak-preserving (a narrow spike must survive), and extract_waveform/
analyze_signal across AC/transient/x-range/error/all-analysis paths.
2026-06-20 22:25:54 -06:00
35b12e61c4 Test build_waveform_svg without a simulator or filesystem
11 tests over auto plot-type detection (bode/spectrum/time), missing-signal
and multi-signal-overlay errors, dimension/height defaults, downsampling, and
x-range clipping -- all with hand-built RawFiles, no .raw read or SVG write.
2026-06-20 21:06:35 -06:00
181d1cb925 Test tuning.py pure logic without a simulator
15 tests over build_effective_params, extract_metrics (AC + transient via
hand-built RawFiles), evaluate_targets (all comparison operators +
unmeasurable), and make_suggestions. This logic previously required a full
LTspice run to exercise.
2026-06-20 01:43:44 -06:00
cf1502cee9 Test templates registries and resolve_template precedence
Covers the netlist-first precedence contract (names in both registries),
asc-only/netlist-only resolution, unknown -> None, and the deduped union
in all_template_names -- the lookup logic tune_circuit depends on.
2026-06-20 00:45:47 -06:00
7c4f09cbb3 Test RawFile.resolve_index -- exact match, case-insensitivity, no partial
Locks in the exact-match semantics that distinguish resolve_index from
get_variable, so the lookup used by tune_circuit and plot_waveform is now
covered without needing the live LTspice path.
2026-06-19 22:54:05 -06:00
71dfdc8d94 Add SpiceBook integration -- publish circuits as interactive notebooks
Bridge mcltspice to SpiceBook (spicebook.warehack.ing) with 5 new MCP
tools (publish, list, get, delete, simulate), a status resource, and a
guided publish prompt. Includes LTspice-to-ngspice netlist conversion
(strips .backanno, Rser=, Windows paths) with structured warnings.

Path traversal protection on file reads (extension allowlist + resolve),
notebook ID validation, narrowed exception handling, and size limits.
35 unit tests + 4 live integration tests.
2026-02-15 18:05:49 -07:00
b0db898ab4 Fix plot_waveform data indexing bug, add multi-signal overlay and adaptive axis labels
Row-major data from raw_parser was indexed as column-major, producing
garbled plots for digital waveforms. Also adds signals parameter for
overlaying multiple time-domain traces with legend, and adaptive X-axis
labels (ns/µs/ms/s) based on time span.
2026-02-14 20:16:10 -07:00
8f0b9ad46a Add axis control, point limits, and dimension params to plot_waveform
Expose x_min/x_max/y_min/y_max, max_points, width/height, and title
override through the MCP tool. Data is clipped to X range before
stride-based downsampling for better zoomed resolution. All params
default to None/current behavior for backward compatibility.
2026-02-14 18:04:40 -07:00
cf8394fa6f Rename mcp-ltspice -> mcltspice, remove stdout banner
Rename package from mcp-ltspice/mcp_ltspice to mcltspice throughout:
source directory, imports, pyproject.toml, tests, and README.

Remove startup banner prints from main() since FastMCP handles
its own banner and stdout is the MCP JSON-RPC transport.

Point repo URL at git.supported.systems/MCP/mcltspice.
2026-02-12 22:53:16 -07:00
b16c20c2ca Fix CE amp coupling cap routing and Colpitts test variable selection
CE amplifier schematic: the input coupling cap CC_in was placed
horizontally (R90) at y=336 — the same y as the RB1-to-base bias
wire. Both cap pins sat on the wire, shorting the cap and allowing
Vin's 0V DC to override the bias divider, putting Q1 in cutoff.

Fix: move CC_in to vertical orientation (R0) above the base wire.
Now pinA=(400,256) and pinB=(400,320) are off the y=336 bias path.
The cap properly blocks DC while passing the 1kHz input signal.
Result: V(out) swings 2.2Vpp (gain ≈ 110) instead of stuck at Vcc.

Colpitts oscillator test: the schematic was actually working (V(out)
pp=2.05V) but the test's fallback variable selection picked V(n001)
(the Vcc rail, constant 12V) instead of V(out). Fix: look for V(out)
first since the schematic labels the collector with "out".

Integration tests: 4/4 pass, unit tests: 360/360 pass.
2026-02-11 06:01:30 -07:00
9b418a06c5 Add SVG plotting, circuit tuning, 5 new templates, fix prompts
- SVG waveform plots (svg_plot.py): pure-SVG timeseries, Bode, spectrum
  generation with plot_waveform MCP tool — no matplotlib dependency
- Circuit tuning tool (tune_circuit): single-shot simulate → measure →
  compare targets → suggest adjustments workflow for iterative design
- 5 new circuit templates: Sallen-Key lowpass, boost converter,
  instrumentation amplifier, current mirror, transimpedance amplifier
  (both netlist and .asc schematic generators, 15 total templates)
- Fix all 6 prompts to return list[Message] per FastMCP 2.x spec
- Add ltspice://templates and ltspice://template/{name} resources
- Add troubleshoot_simulation prompt
- Integration tests for RC lowpass and non-inverting amp (2/4 pass;
  CE amp and Colpitts oscillator have pre-existing schematic bugs)
- 360 unit tests passing, ruff clean
2026-02-11 05:13:50 -07:00
cfcd0ae221 Initial commit 2026-02-10 23:35:53 -07:00