Releases: Konjkov/casino-mcp
Release list
casino-mcp 0.6.0
Runs are one at a time now — a second is refused while a job this server started is going,
because two CASINO runs sharing the cores make Total CASINO CPU time, and the efficiency
computed from it, stop meaning what they say. And a restart no longer has to destroy the
answer of the run before it, which matters more now that rerunning in place is the ordinary
way to work.
Behind both, the corrections that go with them: casino_results for a job whose directory had
been rerun was answering with the next run's numbers, and three of the tools that act had no
output schema at all — which is how JobState.binary declared the wrong type for five
releases without anything noticing.
pip install casino-mcpOn PyPI: https://pypi.org/project/casino-mcp/0.6.0/
Added
casino_wait(job_id, timeout)(casino-mcp wait): block until a running calculation
ends, and answer with the state it ended in, pluswaitedandtimed_out. A chain is then
run, waited for and read back without a polling loop -- what a campaign wrote by hand was
while pgrep -x casino; do sleep 5; done, or a status call every few seconds, each one a
round trip through the model. The wait is bounded (600 s by default,wait_timeoutin
casino-mcp config) because a stdio server answers one call at a time, so a wait is the
whole control plane standing still; a caller that wants longer calls again. Waiting is not
stopping: nothing is signalled and the calculation is not touched.- Every tool that takes a job takes a calculation directory instead --
casino_status,
casino_wait,casino_results,casino_stop-- and it means the newest job that ran there.
A campaign holds directories: the scan's own loop variable is the directory it prepared, and
the job id of the run in it is a string the caller never saw, which is why reading results
back meant greppingoutrather than asking for them. The registry answers the question, so
nothing is written into the calculation directory to record it. casino_results(job_id, fields=[...])(casino-mcp results -f vmc.efficiency): answer
with just the paths asked for, as one flat{path: value}. A parsed run is 10-16 kB of JSON
and a scan over 38 directories wanting six numbers from each does not want 600 kB of it --
which is why the campaign that prompted this greppedoutinstead of calling the tool. A
path is written in the run's own keys, with four rules:vmc/opt/dmc_equil/
dmc_statsare the last phase of that kind,opt[3]andvmc[2]the cycle CASINO itself
numbered,phases[-1]andphases[0]a position, and anything else is a key --
keywords.DTVMC,cpu_time,result.energy,vmc.energy.error,vmc.blocks[0].time,
status. A path that lands on a measured value collapses to the number. A path that does not
exist is a mistake in the question and comes back inproblemsnaming what is there instead;
a path that exists but holds a number CASINO never printed comes back as null with its reason
-- the two are different things and the projection keeps them apart. The same selector is what
a scan tool will project its table with, rather than a second one beside it.casino_input(job_id)(casino-mcp input): the keywords and%blocks a calculation was
given, as data.resultsanswers what a run did and this answers what it was told to do, and
the two are not the same reading: thekeywordsin a result are CASINO's own echo, which
holds every default it applied -- 70 entries against the 23 a file typically sets -- and drops
what it does not print.random_seedis one of those, and it is the keyword the question
"can this number be reproduced" turns on. It reads a directory nothing has run in, which is
how a prepared calculation is checked before there is a job to name it by, and for a job that
casino_stop halted it also returnsbefore_halt: theinputthe run was started from, kept
in the registry becausehaltqmc -urewrites the file in place and the rewrite is lossy.casino_list_jobs(limit, workdir)filters to one directory: what a chain of runs did in one
place, and the way to see that a directory has been run twice.- Runs are one at a time, and
casino_run(allow_concurrent=true)is how they are not. A
second run is refused while a job this server started is going -- not because the machine is
busy, but because of what sharing it does to the numbers:Total CASINO CPU timecounts CPU
seconds across the MPI processes, and two jobs that landed on the same core measured 97.2 s
of CPU against 194.41 s of real time, withefficiencywrong by the same factor and nothing
in the output saying so. The reply on the override names what it runs beside, under
concurrent. A job running in the same directory is refused with no override at all --
one directory is one calculation -- and that check is asked before the.runqmc.lockone,
which would otherwise offerunlock=trueand turn the caller loose on a live calculation.
Only jobs this server started are known: apgrep casinoover the machine is deliberately
not done, because "something is computing" with no owner and no job id is a refusal the
caller has nothing to answer with. So readingcpu_timeagainstreal_timeafterwards is
not a backstop but the check that holds, and it is one
casino_results(fields=['cpu_time', 'real_time']). casino_run(restart=true, keep_previous=true)(casino-mcp run --keep-previous) moves
outto the first freeout.1,out.2, ... instead of deleting it; the rest of what
restartdeletes goes as before, and the reply says where it went, underkept. The
numbering ends up being the order the runs happened in. The archives are deliberately not in
DEBRIS, and are not namedout_part.N-- that name is runqmc's own, for the segments of a
--continue, and it is inDEBRIS. Refused withoutrestart: nothing is being deleted
then. What this is really for is thecasino_resultsfix below.
Changed
- The job index records
created_epochbesidecreated. A job id is only chronological to the
second, and two runs of one directory a second apart -- which is what a scan does -- cannot be
told apart by it. A record written before this falls back to its id, which is all it has. casino_resultssays what it returns. Its description named energies and per-block
numbers and nothing else, while the parser had been returningacceptance,
correlation_time,efficiency, the optimizeddtvmc,steps_per_processand the whole
statistical-efficiency section all along — so the way to those numbers was to grepoutfor
them. The docstring now lists what a phase holds, by kind, and a test holds it to the parser.
Fixed
casino_resultsfor a job whose directory has been rerun answered with the wrong run's
numbers. A job record held a directory, not a file, and the output was found in it by the
nameout-- which holds for exactly as long as one run has happened there. After a restart
in place, the earlier job answered with the physics of the run that came after it, under its
own id, with its own status and its own start time, and nothing in the reply saying so. Job
records now name the file,casino_statusreports it asout, andkeep_previousmoves the
record along with the file, so each job goes on answering with what it produced. Records
written before this read asout, which is what every one of them wrote.adviseno longer tells aruntype : vmcinput that CASINO will not read itsopt_dtvmc.
unusedplaced a keyword by its prefix, andopt_dtvmcis named like an optimisation
keyword and is a VMC one — the VMC phase's own time-step optimisation. Which phase a keyword
belongs to isSECTIONSnow; a keyword the table does not know is still placed by its prefix.- The trap that is real is the other way round, and
advisenow warns about it:dtvmcset
whileopt_dtvmcis not 0 means CASINO takes the value as a starting point and optimizes the
step away from it, so a scan overdtvmcmeasures one step in every directory. casino_status,casino_waitandcasino_list_jobsanswer again. All three return the
JobStatefamily, andJobState.binarydeclared astrwhilebinary_stamp()has answered
with a dict —path,exists,size,mtime— since the initial commit, so every one of
them failed at serialization withbinary: Input should be a valid string, after the
calculation had already run. The stamp now has a model of its own,BinaryStamp, which is
also the honest name for it: not which binary, but which build of it, the thing that tells
results from before and after a rebuild apart. Nothing had caught this in five releases
because the field was the one place the model named what the runtime returns and named it
wrongly —extra='allow'waves every unnamed key through untouched — whilecasino_run
declares a plain dict and hands the same stamp back correctly, andcasino_resultsnever
fills the field in. Guarded now by a unit test that puts a realbinary_stamp(), in both the
shapes it has, throughJobState; the tests that stub the runtime out call the tool as a
plain function and never reach the output model at all.- The three tools that act rather than read now have an output schema too.
casino_run,
casino_prepareandcasino_stopwere declared-> dict[str, Any], so what reached the
model was{"type": "object", "additionalProperties": true}: not one field name, not one
unit, and nothing a test could compare against what the runtime actually answers. That is the
holebinarylived in for five releases — a reply nothing describes is a reply nothing can
check. They returnStarted,PreparedandStoppednow, withBinaryStamp,
CorrelationData,GeminalPlan,Terminated...
casino-mcp 0.5.0
A geminal wave function can be written now, which is the third file at the start of a chain
that no CASINO utility writes — and the tools that read a calculation say what the numbers
they hand back are, units and all, instead of answering with an untyped object.
pip install casino-mcpOn PyPI: https://pypi.org/project/casino-mcp/0.5.0/
Added
- The
GEMINALblock of aparameters.casl, which is the third file at the start of a
chain that nothing else writes.psi_s : geminalreplaces the Slater determinant with a sum
of geminal determinants — the electrons are paired byPhi(r,r') = sum_mk g_mk phi_m(r) phi_k(r')rather than put in orbitals — and everycandgof it lives in that one block.
casino_prepare(source, dest, geminal=[])writes the Hartree-Fock geminal, which is the
Slater determinant exactly and the check the manual recommends making first;
geminal=['p:2', 'd:1']adds a correlating geminal over the first two p levels and the first
d level of the orbital file.geminal_settingsholds the seeds, the anchors, the mirror
geminal and the purity. - The channels are levels, not orbitals, and that is the whole difficulty: a correlating
geminal built out of one component of a degenerate level is not spherically symmetric, and
optimizing it breaks the symmetry of the state it is meant to describe. So each level is tied
together inConstraints, component by component and both ways round the diagonal. The levels
are read off the orbital coefficients ofgwfn.data— each MO classified by the (l, m-slot)
carrying its weight, after the solid-harmonic constants CASINO premultiplies into d
coefficients and (permolden2qmc.py) not into f and g ones are divided back out — and a
level whose orbitals are not one clean component each is demoted to a diagonal-only tie with
a warning rather than guessed at. - The unpaired columns of an open shell go into every geminal with a non-zero
c, not only
the first: an empty unpaired column makes the geminal matrix singular at every configuration,
which ischeck_umat's errstop, and they are written fixed becauseparse_umat_elrefuses an
optimizable one. The occupation itself comes fromneuandnedin theinput, the only
place that says which orbitals this calculation fills. - Unlike a Jastrow factor, a geminal cannot start from zeros — a pairing matrix with an empty
diagonal is singular — so the two leading correlating channels start atseedandseed2
(−0.05 and −0.02, the values the committed examples start their cycles from). casino-mcp prepare --geminal p:2,d:1 -g anchors=1 -g mirror=1, the same for a shell.tests/integration/test_geminal_casl.py: every generated file put to the sametestrun : T
CASINO, which parses the block, resolves the constraint groups, checks them for contradictions
and callscheck_umat. Its last test is the one no unit test can make — a VMC run over the
Hartree-Fock geminal against one overpsi_s : slater, which is the only check that the
orbital indices written mean what they are meant to.
Changed
casino_status,casino_resultsandcasino_list_jobsnow declare what comes back.
Their return annotation wasdict[str, Any], out of which the SDK builds an output schema
saying "an object" and nothing further — so a caller holdingacceptance : 50.1797had
nothing to tell it whether that is a per cent or a fraction,correlation_timesteps or
moves,efficiencyper second of CPU time or per move. The three return models now, each
field carrying its units and its meaning, and the schema travels with the tool listing. They
are pydantic models rather than TypedDicts because the SDK builds a TypedDict's model through
get_type_hintswithoutinclude_extras, which drops every description on the way. Every
field stays optional and every model allows extras: the runtime answers with the same plain
dicts, an unknown job still answers witherroralone, and a key that is not declared still
reaches the caller. On the wire a validated reply now carries the fields it has no answer for
as nulls instead of omitting them.casino_runandcasino_preparekeep the free-form
schema: what they answer is an account of what they did, which is not a fixed shape.input_file.check_filescounts a freshparameters.caslamong the files the caller is about
to write, as it already did a blankcorrelation.data: the file is prepared alongside the
inputit goes with, and refusing the input because it is not there yet would refuse the pair.
casino-mcp 0.4.0
The server can now read a calculation and write one, at both ends of a chain. Reading: a DMC
run takes hours, and until this release the only two moments it could be spoken about were
before it started and after it ended. Writing: the input for the next run, and — for the
first run, the one whose directory holds an orbital file and nothing else — the
correlation.data that no CASINO utility writes.
pip install casino-mcpOn PyPI: https://pypi.org/project/casino-mcp/0.4.0/
Added
casino_results(job_id)(casino-mcp results): the physics of one job as data —
phases, energies, error bars, variance, per-block numbers, every value carrying the file
and the line it was read from.resultpoints at the number that is the run's answer.- A running DMC calculation can be read, and this is the only way to read one. CASINO
writes the mixed estimators intooutonce, at the very end; until then the current
estimate lives indmc.status, which it rewrites after every statistics block and
deletes when the run finishes — copying the same text intooutat that moment, so
nothing is lost, but nothing is available either while it matters most.parse_outnow
reads that file when it is there (parse_dmc_status, one parser shared with theout
section, becausewrite_dmc_statusindmc.f90writes both), and pointsresultat it.
A run stopped bycasino_stopkeeps itsdmc.status, so the last estimate it reached
survives the stop. - While a DMC run is still equilibrating there is no DMC energy anywhere yet, and
result
now says so instead of answering with the VMC phase — whose energy is the trial wave
function's, not the calculation's. - The reblock dump is parsed: the summary rows and the block-length table, with the row
CASINO marked*** BEST ***— which is where the quoted error bar comes from, and whether
the rows above it have flattened out is the whole question of whether it means anything. casino_prepare(source, dest, runtype, overrides)(casino-mcp prepare): copy a
calculation into a new directory and write theinputthe next run needs.runtypefills
in what that runtype requires and the source does not set — switchingvmctovmc_dmcis
one keyword in the file and several more that CASINO then demands — while everything the
source already says survives. Only what a calculation is given is copied:input, the
orbital file,correlation.data,parameters.casl, the pseudopotentials andconfig.in;
neverout, the.histfiles orconfig.out.input_file, the module under it: ESDF reading, anapplythat edits rather than
regenerates (hand comments,%blocks and expert keywords all survive a rewrite), recipes
for nine runtypes, andcheckfor the combinations CASINO only rejects at run time — an
optimisation sample smaller than the DMC population or larger than the number of steps that
would write it,opt_backflowwithoutbackflow, the flooropt_dtvmcputs under
vmc_equil_nstep, a missing mandatory keyword. Nothing is written unless the result would
run; a refusal names the problems and creates no directory. What is legal but probably
unintended comes back aswarnings.- The reblock dump is not an always-present field:
vmc.f90prints it only inside the
derr > 0.1*errbranch, so a VMC phase has one exactly when its reblocking failed, while a
DMC phase always does.tools/refresh_examples.pyskips it in the field comparison for the
same reason it skipsefficiency— whether CASINO prints it is a property of the run, not of
the output format. tests/integration/test_recipes_check_only.py: every recipe put torunqmc --check-only,
which is the only oracle worth having for this — a recipe is right when CASINO says so, not
when our owncheckdoes. It earned its place immediately: bothdmc_*_nstepkeywords are
mandatory for any DMC runtype, including an equilibration-only one, anddtdmcand the
*_nblocks are not mandatory at all. The tables now come fromrunqmc's own.- A blank Jastrow factor and a blank backflow function, for the calculation at the start of
a chain: one that has an orbital file and nothing else, whereuse_jastrow : Tand
backflow : Tneed acorrelation.datathat does not exist yet. No CASINO utility writes one
— nothing inutils/does, and the manual's own instruction is to copy an example and delete
its parameter lines by hand — so
casino_prepare(source, dest, jastrow=['u', 'chi', 'f'], backflow=['eta', 'mu', 'phi'])
writes it, both blocks in the one file, each only if the input turns its keyword on. - The backflow half is the Jastrow half twice over — a
muset is achiset,phiisf
with two more flags — with two differences that are not cosmetic.etahas a cutoff per
spin-pair channel, andread_cutoff_etaerrstops if the first line is missing. And the
electron-nucleus cusp type of everymuandphiset is not a preference but a fact about
the atom, 1 for a bare nucleus and 0 behind a pseudopotential — which CASINO reads and
believes, checking it against nothing, so a wrong value there is not an errstop but a wrong
wave function. It is derived from the*_pp.datafiles rather than defaulted. - The one rule that could not be read off the CASINO source, and was found by putting orders to
atestrun : TCASINO one at a time: an all-electronphiset withN_eN = 1has no free
parameters once the cusp conditions are imposed, whateverN_eeis, while a pseudo-atom set
at the same order is fine. Refused before the file is written, with the fix named. - No
AE CUTOFFSblock is written: it is optional, andinit_pbackflowgives every
all-electron nucleus a set of its own with the length it would have defaulted to. correlation_data, the module under it: the geometry out of the orbital file's own header
(inputsays how many electrons there are and never how many nuclei), one chi and f set per
element with every atom labelled, and no parameter value anywhere — CASINO fillsalpha,
betaandgammawith zeros before it reads a line, and says "Not all coefficients supplied"
when there are none. Which atoms are pseudo-atoms is read out of the*_pp.datafiles, each
of which states its own atomic number, so the chi cusp is refused exactly where
read_chi_termwould errstop on it — on a pseudo-atom, and on a Slater-type basis.- Cutoffs are written as zero, which CASINO reads as use the default and answers with
default_L_u,default_L_chi,default_L_f. Writing a number instead would be
reimplementing a choice that depends on the geometry;warningssays which values CASINO will
take, andjastrow_settingssets one where the default is not wanted. casino-mcp prepare --jastrow u,chi,f --backflow eta,mu,phi -j n_u=4, the same for a shell.tests/integration/test_blank_correlation.py: every generated file put to a real CASINO with
testrun : T, which reads the input files, imposes the cusp, no-duplication and no-cusp
constraints, counts what is left free, checks that they hold, and stops — in a fraction of a
second.runqmc --check-onlyis no oracle for this one: it never openscorrelation.data.
Changed
- The tool surface is six, where it was four.
casino_run,casino_status,casino_stop,
casino_list_jobs, and nowcasino_resultsandcasino_prepare. - A DMC recipe sets
popstats : T. It is not CASINO's default, and it is what puts the
statistical-efficiency section intodmc.status— the difference between a running
calculation that can be read and one that cannot, for no cost. input_file.check_filestakeswriting, the files the caller is about to write. A blank
correlation.datais prepared alongside theinputit goes with, and refusing the input
because the file is not there yet would refuse the pair.
Fixed
casino_run(restart=true)now deletesdmc.statusalong with the rest of what an earlier
run left. Only an orderly end deletes it, so a killed DMC run leaves one behind, and a stale
one next to a freshoutwould have been read as the current estimate of a calculation that
no longer exists.
casino-mcp 0.3.0
A packaging release. The server can now be run from an image rather than an installation,
it says which version of itself is running, and it carries the licence that scientific
software is most often redistributed under. Nothing about how CASINO is driven has changed.
pip install casino-mcpOn PyPI: https://pypi.org/project/casino-mcp/0.3.0/
Added
- A
Dockerfile: apython:3.12-slimimage carrying nothing but the virtual environment,
running as an unprivileged user, withcasino-mcp serveas its default command. CASINO is
not in it and does not need to be — the server starts and answerstools/listwith no
installation present, which is what an introspecting registry asks of it. To run a real
calculation, mount the installation and pointCASINO_HOMEat the mount.
Changed
- The licence is now Apache-2.0, where it was MIT. Same permissions, plus an explicit
patent grant and the requirement to state modifications — the terms scientific-software
users are most often required to redistribute under.
Fixed
- The
initializehandshake reported an empty server version. It now carries the one from
casino_mcp.__version__, which the package metadata reads too — so the version is a single
string in a single file rather than a copy inpyproject.tomlto keep in step.
casino-mcp 0.2.0
Two things, one release. Stopping a calculation and continuing it now go through CASINO’s
own scripts — haltqmc and runqmc — instead of through signals and file moves of this
layer’s own devising. And the test data became a check on CASINO rather than a record of it:
every calculation the suite reads now lives in this repository, runs in minutes, and can be
re-run against a newly built CASINO to see whether the output format moved under the parser.
pip install casino-mcpOn PyPI: https://pypi.org/project/casino-mcp/0.2.0/
Added
casino_run(resume=true)(casino-mcp run --continue): carry an interrupted run on instead
of starting it again. Which of CASINO's two continuation routes that takes is read out of
outrather than chosen here: a run CASINO stopped onmax_cpu_time/max_real_timeleft
aCONTINUATION INFO:block and is continued byrunqmc --continue, and a run that was
interrupted is continued by a plainrunqmcover theinputthathaltqmc -urewrote —
--continueerrstops there, on continuation info that was never written. A run that reached
its own end is refused: there is nothing to continue. The reply says which route was taken.CASINO_HALTQMC, alongsideCASINO_RUNQMC: an explicit path tohaltqmc, otherwisePATH,
then$CASINO_HOME/bin_qmc/haltqmc.casino-mcp configreports it and says when it is
missing — a job can still be stopped without it, but its directory will not be tidied.examples/: eighteen real CASINO calculations committed with theoutfiles they produced,
and enough of their inputs to be re-run. They are a settings cover, not a sample — chosen out
of PyCasino's 526 so that every runtype, basis type, sampling method, optimiser and
wavefunction option appears at least once, at the smallest total size that achieves it,
together with a run that never printed an energy and one interrupted mid-optimisation.tests/test_examples.py: the tree parses, and it still covers every setting it was
assembled to cover. Runs without CASINO, so a calculation cannot be dropped fromexamples/
without the suite naming the setting that went with it.tools/refresh_examples.pyandtests/integration/test_examples_rerun.py: the tree is
re-run against the installed CASINO and compared with what was committed. The test asserts
only that no phase, keyword or numberparse_outreads has disappeared, which is what a
changed output format looks like from a parser's side. Moved values are reported, never
asserted: a new release may legitimately produce different numbers, andrandom_seeddoes
not pin an optimisation run anyway — it redistributes configurations across MPI processes and
lands somewhere slightly different each time. Efficiency is excluded from the comparison
entirely, being computed from a measured time that rounds to zero on a short block.
Changed
casino_stopno longer signals the whole process tree and clears the lock file itself. It
sends SIGTERM to that job'scasinoprocesses and to nothing else — the same signal
haltqmc -ksends, except that haltqmc's own kill is apkill -x casinoover every CASINO
process the account owns, which on a machine running several jobs would take the others down
too. The ranks are found by session id, becausempirunputs each of them in a process group
of its own andkillpgtherefore never reaches them.runqmcis left alive to finish its
epilogue — the per-node output concatenated intoout, its own lock file removed — and only
a job still running aftertimeouthas its process group signalled and then killed.- Everything a stop then does to the directory is
haltqmc -f -u:config.outtoconfig.in,
the lock and marker files, andinputrewritten for the work that is left. The reply carries
what it did underhalt. Since that rewrite is the one thingrestartcannot undo — CASINO
refusesnewrun : Fwithout theconfig.inrestarting deletes — theinputas it was is
copied into the job directory first, andrestart=trueon a directory whoseinputis set
up to continue is refused with that copy's location. - Breaking:
overwriteis replaced byrestart, which does whatoverwriteonly claimed
to.overwrite=truelifted the refusal to start in a directory that already held anout
and then deleted nothing, sorunqmc— which appends — produced anoutcontaining two
runs, and left the previous.histfiles and configs to be appended to as well.
restart=truedeletes them:out,out_part.N,.out_proc*,vmc.hist,dmc.histand
their numbered backups,config.in/config.outand their_fixed/_nofixedforms,
correlation.out.N,parameters.N.casl,saved_part_N/. Inputs are never touched — the
list is named rather than derived, because the same directory holds the wave function, the
pseudopotentials and acorrelation.datathat is usually hand-edited. The reply carries
removed, the names that went. Nothing is deleted until every check that could refuse the
run has passed. - The parser fixtures under
tests/data/were regenerated on CASINO v3.1.24 and now each keep
theinputthat produced them, so their asserted values can be reproduced rather than
trusted. The DMC fixture moved from krypton to beryllium — 4 electrons instead of 36 — and
from a single statistics block to 2 equilibration and 20 statistics blocks, which is the
shape a wandering DMC population actually shows up in and which nothing was asserting
before. The interrupted fixture was left alone; it was already v3.1.24, and a run that was
killed cannot be reproduced by running. - The integration suite reads
examples/and nothing else:--examples-dir,$CASINO_EXAMPLES
and the dependency on a PyCasino checkout are gone. Installing casino-mcp does not install
PyCasino, sopytest -m integrationnow needs only CASINO itself. - The example calculations were shortened and re-run in one pass, so the tree takes minutes
rather than days to reproduce. This costs statistics and no output format: the longest run
went from 2.5 hours to a couple of minutes with every phase, keyword and printed number
intact. Two examples are cut below the point where CASINO can reblock, which is the one
output shape the tree previously had no instance of.
casino-mcp 0.1.0
First packaged release. The control plane works and is tested; the tools that return physics
to the model are not shipped yet.
pip install casino-mcpOn PyPI: https://pypi.org/project/casino-mcp/0.1.0/
Added
- MCP server over stdio with four tools:
casino_run,casino_status,casino_stop,
casino_list_jobs. No generic shell tool, by design. - An on-disk job registry under
$XDG_STATE_HOME/casino-mcp, so a calculation outlives the
server that started it, and a launcher process per job, so the whole tree
(runqmc → mpirun → casino) can be signalled as one group and its exit code recorded. parse_out: a CASINOoutfile as structured phases, every number carrying the line it
was read from. No MCP, no dependencies. Checked againstenvmcover 526 example files.- Configuration through the environment, which is what
.mcp.jsoncan set per server
registration:CASINO_HOME,CASINO_ARCH,CASINO_RUNQMC,CASINO_MCP_STATE_DIR,
CASINO_MCP_FORBID. Every other default is a constant insettings.py. - A
casino-mcpcommand:serve,config,run,status,stop,jobs,parse. - Guardrails against destroying results: a directory that already holds an
out, one whose
outis tracked by git, one another runqmc has locked, and a$CASINO_MCP_FORBIDlist
that has no per-call override. - Provenance: every job record freezes the command and the path, size and mtime of the
casinobinary it ran. - Tests: 102 unit tests that need no CASINO (a fake
runqmcstands in for the real one), plus
an opt-in integration suite that drives real calculations over real stdio MCP.
Notes
parse_outderives exactly one number: the sample-variance error of a single-block run,
which CASINO does not print. It is taken from the one block asenvmcdoes, and labelled.- Nothing shells out to
envmc/endmcat runtime;endmcmisparses numbers under a non-C
locale.
Requires Python 3.11+ and a working CASINO installation. The package does not bundle, build
or replace CASINO — it drives runqmc.