SpiceMCP
Provides tools for running and optimizing LTspice circuit simulations, including parameter sweeps, metric extraction from .meas directives, and schematic generation using LTspice's symbol library.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@SpiceMCPOptimize my amplifier circuit for max gain and bandwidth"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
SpiceMCP
LTspice MCP server where the server owns the optimization state, not the LLM.
The model contributes topology and strategy. Candidate identity, simulation history, dedup, best-so-far, sensitivities and rollback all live in SQLite and are re-derived on every call, so a long optimization can't drift into remembering a circuit that never existed.
Install
pip install -e ".[dev]"
pytest -q LTspice is auto-detected (AppData\Local\Programs\ADI\LTspice\LTspice.exe,
Program Files\LTC\LTspiceXVII\XVIIx64.exe, …). Override with LTSPICE_EXE.
Register with your MCP client:
{
"mcpServers": {
"spicemcp": {
"command": "python",
"args": ["-m", "spicemcp.server"],
"env": { "SPICEMCP_PROJECT": "C:/path/to/your/circuit/project" }
}
}
}State lands in $SPICEMCP_PROJECT/.ltspice-mcp/:
state.db authoritative state
candidates/ cand_XXXX.cir, byte-exact netlists for rollback
simulations/ LTspice working dirs and logs
cache/ reports/Related MCP server: ltspice-mcp
Metrics come from .meas
There is no .raw waveform parser. Every metric you want to optimize is a .meas
directive in the netlist, and the server reads the values back from LTspice's .log.
The metric definitions then live with the circuit, versioned alongside it.
.ac dec 100 1 10Meg
.meas AC gain_db MAX mag(V(out)) ; mag(), NOT db() -- see below
.meas AC bandwidth WHEN mag(V(out))=0.707 FALL=1
.meas TRAN power AVG (-I(V1)*V(vcc))Never wrap an AC .meas in db(). LTspice already reports AC measurement
magnitudes in dB, so db() converts twice — and it never errors, it just returns a
smaller plausible number. Verified on 26.0.2: a gain of 100 measures as 40dB via
mag() but 32.04dB (= 20·log10(40)) via db(). The server lints for this and
returns a warning alongside the metrics.
AC results are complex, so the phase is available too, as <name>_deg:
gdb: MAX(mag(V(out)))=(40.0dB,-159.417738334°) -> gdb = 40.0, gdb_deg = -159.42One more trap, because two .meas forms print the same shape with opposite meanings:
bw: mag(V(out))=0.7071 AT 159158.003411 -> 159158 (WHEN: the crossing)
p050: V(out) =0.140915020014 at 6.666666667e-05 -> 0.1409 (FIND AT: the value)In a WHEN measure the number after = is the trigger level you wrote and AT
carries the result; in FIND ... AT it is the reverse. LTspice separates them only by
case — uppercase AT for a point it found, lowercase at for one you specified — so
the parser is case-sensitive here. Getting it backwards returns the sample time as the
measurement, which plots as a perfectly plausible straight line.
Usage
start_optimization once, then let run_optimization do the iterating:
start_optimization(
run_id="amp_001",
netlist_template=open("amp.cir").read(), # tunables written as {R1}, {C1}
param_space={"R1": {"min": 1e3, "max": 100e3},
"C1": {"values": [1e-9, 4.7e-9, 1e-8]}},
objectives=[{"metric": "gain_db", "direction": "max"},
{"metric": "bandwidth", "direction": "max"},
{"metric": "power", "direction": "min"}],
constraints={"bandwidth": ">100000", "power": "<0.005"},
seed=42)
run_optimization(run_id="amp_001", iterations=40) # 40 sims, ONE compact answerA two-sided bound needs the list form, since a dict can't hold two entries for one
metric: constraints=[{"metric": "fc", "op": ">", "value": 9500}, {"metric": "fc", "op": "<", "value": 10500}].
run_optimization returns a summary, not a transcript: best candidate, constraint
status, the strongest measured sensitivity, the most promising unexplored region,
step_frac_final (how far the step had to shrink), and at_param_space_bound — which
parameters sit on a min/max/list edge. That last one matters: a bounded search
always reports an optimum, and if the winner is pinned to the fence you drew, the
answer is "widen param_space", not "converged".
The full history stays queryable but never arrives unasked.
Schematics from LTspice's own parts
spicemcp.asc writes a real .asc using LTspice's symbol library, so a candidate opens
as a schematic you can probe and edit — not as a netlist in a text window. Pin offsets
are read from the actual .asy, which means res, cap, OpAmps/opamp, nmos, npn
and everything else in lib/sym work with no per-part table.
Wire by pin name; nothing takes a pin coordinate:
from spicemcp import asc
sh = asc.Sheet()
V1 = sh.part("voltage", "V1", (0, 80), value="AC 1")
R1 = sh.part("res", "R1", (80, 112), "R270", value=1849.60938)
U1 = sh.part("OpAmps/opamp", "U1", (480, 176), "M180",
SpiceLine="Aol=1Meg", SpiceLine2="GBW=1G")
sh.net(V1["+"], R1["A"])
sh.route(R1["B"], U1["noninvin"], "VH") # L-shaped, no intermediate points
sh.gnd(V1["-"]); sh.flag(U1["out"], "out")
sh.directive(".lib opamp.sub", ".ac dec 400 100 1Meg")
assert "XU1 out b out opamp" in asc.check(sh.write("f.asc"))check() netlists the drawing back through LTspice and returns its element lines. Use
it — it is the only way to know the picture is the circuit you meant. A wrong
orientation or an unwired pin produces a schematic that opens and simulates happily,
and comparing against the candidate netlist is what catches it. Directives and notes
auto-stack above and below the drawn content, so text placement isn't a coordinate
either.
Use the library part, not an equivalent model: E1 out 0 in out 1e6 is a VCVS, and
OpAmps/opamp is a single-pole amplifier with Aol and GBW you can dial. GBW is a
real knob, not a formality — on a 10 kHz Sallen-Key, going from an effectively ideal
GBW=1G to the block's own GBW=10Meg default moves the corner 1.55 Hz and the step
overshoot from 5.76 % to 5.81 %.
Tools
lifecycle | evaluation | state queries |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
| ||
| ||
| ||
| ||
|
Guarantees worth knowing
A worse iteration cannot demote the incumbent.
best_candidateis aMAX(score)query, andscoreis a pure function of a candidate's metrics (per-objectiverefscales fixed at run creation). Nothing to overwrite, so nothing can be overwritten.Modification never mutates. A changed parameter set is a new
cand_NNNNwith aparent_id; the tree reconstructs exactly how any candidate was produced.Duplicates aren't re-simulated. Lookup by
sha256(design) + sha256(sim_config). Same circuit under different conditions is a different experiment, not a duplicate.rollback_to_candidaterestores stored bytes, never a reconstruction.Sensitivities are measured.
finite_differencemeans it came from candidate pairs differing in that parameter alone;ols_marginalis a weaker correlational fit, and the method is reported so you can tell them apart.Every parameter gets screened once. A parameter nobody has varied has no measured sensitivity, so no amount of exploiting can pick it — the search would fixate on whichever knob the seed happened to move. Unmeasured therefore outranks unexploited, and a decade-spaced
valueslist steps to the adjacent entry rather than by a fraction of its span (±15% of1e-8snaps back to1e-8, which would freeze every E-series part you list).The search is scale-free, cycles coordinates, and refines its step. Three ways a bounded local search reports a confident near-miss instead of an answer, all three found by running a real filter to completion:
ranking parameters by raw
d(metric)/d(param)compares farads against ohms, so the capacitor always wins — leverage is scaled by each parameter's range instead;greedy coordinate descent never revisits a lower-ranked coordinate while a higher-ranked one still has untried values, and a continuous one always does, so coordinates take turns;
a step fixed at a fraction of each range can bracket an optimum but never enter a tolerance window narrower than one step, so it halves on stagnation.
Failures are remembered and avoided, classified as
convergence_failure,structural_error,constraint_violation,numerical_instability, …
Deliberate simplifications
skipped | add when |
| a metric can't be expressed as |
Bayesian optimization / GP surrogate | a single sim is slow enough that ~30 wasted evals beats a surrogate's complexity |
step halving on stagnation, not a line search or trust region | a sim is slow enough that the evaluations spent bracketing cost more than the bookkeeping |
one parameter moves per iteration (coordinate descent, not a full compass poll) | the metric surface has strong parameter interactions the per-coordinate view misses |
component-graph IR (topology edits are new templates) | a tool needs to rewrite topology programmatically |
incremental sensitivity updates (O(n²) per iteration) | runs exceed a few hundred candidates |
| you want it as a search directive rather than a coverage hint (the actual search already uses measured sensitivity) |
| a topology arrives that nobody wants to lay out by hand — then write a placer, not more templates |
The hallucination-loop test
tests/test_hallucination_loop.py drives the sequence that makes memory-based agents
panic — improve, improve, best, worse, worse, improve — and asserts the incumbent
survives it, that it also legitimately updates when something is genuinely better, that
duplicates aren't re-simulated, that sensitivities match closed-form derivatives, and
that a simulated process restart loses nothing.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityCmaintenanceA thin MCP server that wraps spicelib for circuit simulation. Exposes tools for running AC, transient, DC op, and parameter sweep analyses, enabling behavioral model fitting through iterative simulation and measurement comparison.47GPL 3.0
- FlicenseCqualityDmaintenanceMCP server for automating LTspice on macOS, enabling simulation, schematic generation, data extraction, verification, and rendering via natural language or agents.7115
- Alicense-qualityBmaintenanceThis MCP server enables agents to control LTspice on macOS for running simulations, generating schematics, extracting data, and automating verification workflows.MIT
- AlicenseAqualityBmaintenanceAn MCP server that connects LLM assistants to real circuit simulation: LTspice and ngspice, plus direct editing of LTspice .asc schematics. Simulation results come back as structured numbers so the assistant can design, verify, and iterate on circuits.4826GPL 3.0
Related MCP Connectors
MCP server providing access to the Scorecard API to evaluate and optimize LLM systems.
MCP server exposing the Backtest360 engine API as tools for AI agents.
MCP server for generating rough-draft project plans from natural-language prompts.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/oniondas/SpiceMCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server