The server exposes 25 tools. This page is generated from the server itself: names, parameters, defaults and descriptions are read from the tool definitions, so they match the code that ships. Required parameters are marked with an asterisk. Tools marked task can run as an MCP Task for clients that support it, resolving when the job ends.
Failures a caller can act on come back as a result with "status": "failed" and a reason, not as an error. Only an unknown job_id or a malformed argument raises.
Run
Watch
Results
Figures
Staging
Housekeeping
submit_case
Submit an OpenFOAM case directory as a job on this server's host.
figures (default on): sample the wall patches (p, wallShearStress) and cutting planes (U, p, vorticity) as VTP every 20 iterations over the run's last 200, then render the figure set in a headless ParaView container after the solve: A skin-friction separation map with friction lines, A2 wall flow angle, A3 the underside, B pressure coefficient, C wake-plane streamwise vorticity, D centreline velocity, E a three-quarter view with the wake; job_figures lists them and job://<job_id>/figures/<file> serves the PNGs. Pass a dict to override patches, planes ([{name, point, normal}]), window, interval, views, wake_plane, side_plane, magnitude or cofr; False disables sampling and rendering.
Give either case_path (a directory this server can read) or case_name (a case staged under the server's cases directory with put_case_file, see list_cases). Jobs on a host run one at a time, and a queued job also waits while an OpenFOAM container this server did not start is running there (server_info lists them as foreign_containers), so there is no need to wait outside the server.
The case is validated (pre-flight checks), copied into a job directory, given the server's function objects (solverInfo, and runTimeControl when stop="solver"), and queued. Nothing runs inside this call.
Failures the case is responsible for come back as {"status": "failed", "reason": "preflight", "checks": [{"level", "code", "message", "path"}, ...]}; branch on the codes (case_path_missing, case_unreadable, rank_budget, unknown_step, location_in_mesh_on_face, stl_missing, end_time_mismatch, ...). The job_id is <case directory name>-<8 hex>; case_json_name is the name inside case.json (equal to the directory name for generator-written cases, null when the case has no case.json). job_dir is the path as this server sees it and job_host_dir the same directory on the docker host, for rsync or ssh.
queue_position counts jobs queued ahead on this host, so 0 means next to run (possibly behind a running job, which job_status names as blocked_by).
Returns at once with {"status": "queued", "job_id": ...} unless the client opted into MCP Tasks, in which case the task resolves when the job reaches a terminal state and its status message carries a one-line progress summary. Cancelling the task only stops waiting; use cancel_job to kill the job. A task not polled for 15 minutes expires, the job does not: job_status always answers.
steps: names from surfaceFeatureExtract, blockMesh, decomposePar, snappyHexMesh, checkMesh, and the solver as "solve" or by its binary name, which is whatever controlDict's application says (simpleFoam, pimpleFoam, rhoSimpleFoam, ...). Default is the full external-aero chain (nproc>1) or every step but decomposePar (nproc==1). stop: "none" runs to endTime, "watcher" stops when the force coefficients are steady over a window (see stop_policy for the bands), "solver" injects OpenFOAM's runTimeControl instead. expected_cells enables the mesh-leak alarm after meshing. resume runs only the solver on an existing mesh.
| Parameter | Type | Default |
|---|
case_path | str | null | None |
case_name | str | null | None |
steps | list[str] | null | None |
nproc | int | null | None |
image | str | null | None |
stop | "none" | "watcher" | "solver" | none |
stop_policy | object | null | None |
expected_cells | int | null | None |
label | str | null | None |
resume | bool | false |
move | bool | false |
figures | bool | object | true |
submit_sweep
Mesh a case once and run several freestream conditions on that one mesh, as a single job. points is a list of {"alpha": deg, "beta": deg (default 0), "label"?: str, "stop"?: none|watcher|solver} in the order to run; each point starts from the previous point's converged fields, so later points settle in a fraction of the iterations, and every point shares the identical mesh, which is what makes differences between points (derivatives, keel variants at several angles) free of mesh-repeatability noise.
Per point the server rewrites the freestream velocity on the decomposed fields (OpenFOAM's changeDictionary on the latest time; the case's 0/U must have a freestreamVelocity patch), points the forceCoeffs lift and drag directions at the new angles (body axes Y aft, Z up, right-handed so +X is port; wind = (sin b, cos a cos b, sin a cos b), lift = (0, -sin a, cos a)), sets endTime = start + iters_per_point (default: the case's endTime), and makes the solver write fields at the point's end. The stop policy applies per point; a point's own "stop" overrides the job's, so oscillating angles can run to their full length inside a sweep.
Results: postProcessing gets one time directory per point; job_status lists points with their windowed coefficient means; job_coeffs(point=k) reads one point; and points/<k>/ in the job directory holds a case.json (alpha, beta) plus that point's postProcessing, so a per-case report script reads it as a case.
More points on an existing mesh: from_job names a finished (or cancelled) sweep or run on this server; its decomposed mesh is reused and no meshing runs. start_time picks the time level whose fields the first new point starts from (a sweep point's end from job_status, e.g. the alpha 8 point's), default the latest; only that level is copied, so the new job is small and starts in seconds. The new points are numbered from 0 in the new job.
| Parameter | Type | Default |
|---|
points * | list[object] | required |
case_path | str | null | None |
case_name | str | null | None |
nproc | int | null | None |
image | str | null | None |
stop | "none" | "watcher" | "solver" | watcher |
stop_policy | object | null | None |
iters_per_point | int | null | None |
expected_cells | int | null | None |
label | str | null | None |
figures | bool | object | true |
from_job | str | null | None |
start_time | float | null | None |
cancel_job
Stop a queued or running job: the container is killed and the job is marked cancelled. Logs and any postProcessing output written so far are kept. Waits a few seconds for the terminal state; "status": "cancelling" means the worker has not confirmed yet (poll job_status).
| Parameter | Type | Default |
|---|
job_id * | str | required |
set_end_time
Change endTime in the running job's controlDict (OpenFOAM re-reads it each iteration because runTimeModifiable is on). Use it to cut a run short by hand or to extend it. Refused on finished jobs.
| Parameter | Type | Default |
|---|
job_id * | str | required |
end_time * | int | required |
job_status
Full snapshot of one job: status, current step, solver iteration and ETA, residuals, force coefficients and their drift, error details, last event.
| Parameter | Type | Default |
|---|
job_id * | str | required |
wait_for
Wait until the job changes (until="change"), moves to another step ("step"), satisfies its convergence rule ("converged") or reaches a terminal state ("done"), or until timeout_s elapses (capped by the server, about 85 s). Always returns the current snapshot plus "reason": done | timeout | <until>. Call it again to keep waiting; a single call never outlives a client's tool timeout.
detail="summary" returns only reason, waited_s, seq, status and the summary line, which keeps a long poll loop cheap; the full snapshot is one job_status away.
Pass since_seq (the "seq" of the last snapshot you saw) so a transition that happened before this call returns at once instead of being missed; without it, only changes after the call begins are seen. A job that is already finished, failed or cancelled always answers reason "done" whatever until asked.
| Parameter | Type | Default |
|---|
job_id * | str | required |
until | "change" | "step" | "converged" | "done" | change |
timeout_s | int | 60 |
since_seq | int | null | None |
detail | "full" | "summary" | full |
list_jobs
Recent jobs, newest first, one compact row each. Filter by status (queued|running|finished|failed|cancelled) or label.
| Parameter | Type | Default |
|---|
status | str | null | None |
label | str | null | None |
limit | int | 20 |
job_log
The last lines of a step's log (default: the current or last step). Step names match the job's steps, e.g. snappyHexMesh or simpleFoam.
| Parameter | Type | Default |
|---|
job_id * | str | required |
step | str | null | None |
tail | int | 80 |
server_info
This server's version, host, executor, image, rank budget, jobs root, queue backend and job counts.
job_coeffs
Statistics of a function object's output over the trailing window of a job: mean, std, min, max, spread and spread_rel per column, for the rows whose Time lies within the last last_n iterations. Works while the job runs and after. spread_rel is null when |mean| < 1e-3 (a coefficient near zero has no meaningful relative spread; use the absolute spread). n_rows counts rows, not iterations: the function object writes every sample_interval iterations. point is 0-based.
function_object is a directory under postProcessing (coeffs, forces_wing, solverInfo, ...); file picks the .dat when the object writes several (force.dat, moment.dat). Time directories from restarts are merged. rows=True appends the windowed rows themselves (Time first), capped at 2000. For a sweep job, point=k restricts everything to that point's iterations.
| Parameter | Type | Default |
|---|
job_id * | str | required |
last_n | int | 400 |
function_object | str | coeffs |
file | str | null | None |
rows | bool | false |
point | int | null | None |
job_forces
Per-patch-group force and moment table from the forces_<group> function objects, averaged over the trailing window: total, pressure and viscous force vectors in body axes (Y aft, Z up, +X port; newtons, rhoInf applied by OpenFOAM), the moment about CofR, and each group's drag, lift and side force projected on the dragDir, liftDir and sideDir that the forceCoeffs function object recorded in its own header (so they follow alpha and beta, per point in a sweep), with each group's share of the total drag. Moments likewise: pitch_moment, roll_moment and yaw_moment (N m about CofR) are the moment vector projected on the header's pitchAxis, rollAxis and yawAxis, so pitch is nose-up positive with the same sign as CmPitch (the raw body-X component is the opposite sign), and cm_pitch etc. divide by q * Aref * lRef. Also check: the summed drag against Cd * q * Aref and the summed pitch moment against CmPitch * q * Aref * lRef from the coefficient table, which must agree within a few percent or something is wrong with the axes.
| Parameter | Type | Default |
|---|
job_id * | str | required |
last_n | int | 400 |
point | int | null | None |
job_files
List a job's files under case/<subdir> (postProcessing by default, or "" for the case root, "system", a time directory, ...): relative path and size. Read them as resources: job://<job_id>/status, job://<job_id>/events, job://<job_id>/log/<step>, job://<job_id>/postProcessing/<fo>/<file> (latest time directory); the static resource jobs://index lists jobs and these patterns.
| Parameter | Type | Default |
|---|
job_id * | str | required |
subdir | str | postProcessing |
job_archive
Pack selected files of a job's case into <job_dir>/archives/<name>.tar.gz and return its path (server and docker-host views), size and file count. Members are stored as <job_id>/case/<path> (and <job_id>/points/... when included), so extracting into an existing case directory needs --strip-components=2. include and exclude are glob patterns on case-relative paths; a pattern naming a directory takes everything under it. Default include: postProcessing/coeffs, postProcessing/forces_*, postProcessing/solverInfo, log.*, case.json, system; add "postProcessing/wallSurfaces" for the surface snapshots (hundreds of MB) or "points/*" is not needed (points/ views are outside the case; use include=["../points"] to add them). Fetch it with get_job_file(job_id, "archives/<name>.tar.gz") in chunks, rsync from job_host_dir, or on an HTTP server GET /jobs/<job_id>/files/archives/<name>.tar.gz.
| Parameter | Type | Default |
|---|
job_id * | str | required |
include | list[str] | null | None |
exclude | list[str] | null | None |
name | str | null | None |
get_job_file
Read a slice of any file in a job directory (relative to <job_dir>, e.g. "case/log.simpleFoam", "archives/x.tar.gz", "points/1/case.json") as base64: the reverse of put_case_file. Loop over offset until eof is true; length is capped at 6 MB per call. For text under 4 MB the job:// resources are simpler.
| Parameter | Type | Default |
|---|
job_id * | str | required |
rel_path * | str | required |
offset | int | 0 |
length | int | 4194304 |
The figures rendered for a job: one set per run or sweep point with view letters (A separation map, A2 wall flow angle, A3 underside, B Cp, C wake vorticity, D centreline velocity, E three-quarter view), file paths relative to the job directory, thumbnails, colour ranges, the averaging window and anything skipped; plus the sweep strip. Read a PNG through the resource job://<job_id>/figures/<set>/<file> (e.g. figures/p1/A.png), by get_job_file, or GET /jobs/<job_id>/files/figures/... on an HTTP server.
| Parameter | Type | Default |
|---|
job_id * | str | required |
Re-render a job's figure set (or one sweep point's) from the sampled VTP output already on disk, in the headless ParaView container. Returns when the render finishes (typically 10 to 60 s); the job need not be running. views restricts to a subset of A, A2, B, C, D.
The window average blurs an unsteady case (stop="none", or a coefficient with a large std over the window). frames adds single-snapshot surface figures beside the average, same colour range: "extremes" renders the snapshots where frame_field (default CmRoll) is lowest and highest in the window, which shows an alternating wing drop in both states; "first_last" the first and last snapshot; "snapshots" every one (20 iterations apart, so ten or so per window). Frame files are <view>_<tag>.png in the set, listed in job_figures with frame, time and note. Jobs submitted without figures fall back to the case's own postProcessing/wallSurfaces output for A, A2 and B.
| Parameter | Type | Default |
|---|
job_id * | str | required |
point | int | null | None |
views | list[str] | null | None |
frames | "snapshots" | "first_last" | "extremes" | null | None |
frame_field | str | CmRoll |
render_strip
Compose the sweep strip (view A of every point side by side) again.
| Parameter | Type | Default |
|---|
job_id * | str | required |
list_cases
Cases staged on this server's host, ready for submit_case(case_name=...). Reports the cases directory as the server and as the docker host see it.
put_case_file
Write one file into a staged case on this server's host: system/controlDict, 0/U, constant/triSurface/wing.stl, ... Text files go as-is; binary files (STLs) as base64, in chunks of up to 6 MB decoded with append=True after the first. Creates the case directory on first use. Followed by submit_case(case_name=...).
| Parameter | Type | Default |
|---|
case_name * | str | required |
rel_path * | str | required |
content * | str | required |
encoding | "text" | "base64" | text |
append | bool | false |
put_case_files
Write several text files into a staged case in one call: {rel_path: content}. For dictionaries and 0/ fields (edit-and-resubmit in one call); STLs and other binaries go through put_case_file with base64 chunks.
| Parameter | Type | Default |
|---|
case_name * | str | required |
files * | object | required |
get_case_file
Read a text file from a staged case (dictionaries, not STLs).
| Parameter | Type | Default |
|---|
case_name * | str | required |
rel_path * | str | required |
remove_case
Delete a staged case directory (not a job; jobs keep their own copy).
| Parameter | Type | Default |
|---|
case_name * | str | required |
prune_job
Free disk from a finished, failed or cancelled job without losing what a report or a restart needs: removes the intermediate field time directories (in the case root and every processor*), keeping 0, each sweep point's end level (what submit_sweep(from_job=, start_time=) starts from) and, for a plain run, the latest level. postProcessing (coefficients, forces) and figures stay. With samples=True the VTP sampling output (mcopenfoamSurfaces, mcopenfoamPlanes, wallSurfaces) goes too: figures already rendered survive, but render_figures can no longer re-render. Refuses on a job that is not terminal.
| Parameter | Type | Default |
|---|
job_id * | str | required |
samples | bool | false |
delete_job
Remove a terminal job entirely: its case copy, logs, results, figures, archives and events. Refuses on a queued or running job (cancel_job first). Returns the space freed.
| Parameter | Type | Default |
|---|
job_id * | str | required |
similar_failures
Earlier failures whose error text is most like this job's (or like text): each match has the job_id, the failing event, a score in [0, 1] and the text that matched. Answers "have we seen this before, and what was it?" across cases. Needs the server's embeddings (server_info.instrumentation says "+ embeddings"); a job's own failure is excluded from its matches.
| Parameter | Type | Default |
|---|
job_id | str | null | None |
text | str | null | None |
k | int | 5 |
min_score | float | null | None |