nina_planner
nina_planner is an MCP server for inspecting a N.I.N.A. observatory and managing observation plans and imaging sequences.
Inspect the active NINA profile, site/optics/filters, and live equipment status, including mount, camera, focuser, guider, filter wheel, dome, rotator, weather, safety, and stow capabilities.
Read recent NINA events, application logs, and imaging metadata for lights, darks, biases, and flats for diagnostics and quality assessment.
Validate and write observation-plan JSON files covering target coordinates, exposures, calibration frames, cooling, autofocus, guiding, batching, and constraints.
Check per-plan acquisition progress and remaining frames, optionally excluding light frames that fail HFR, star-count, or guiding-RMS thresholds.
Load light/dark/bias/dawn-flat/dusk-flat sequences in remaining or full mode; start/resume, stop, and inspect the loaded sequence state.
Enter a safety standby sequence or execute teardown/stow while NINA sequence-level safety guardrails remain active.
The README also documents higher-level planning/auditing, sky calculations, profile switching, dashboard screenshots, live frame stats, and NINA lifecycle controls, where supported.
Click on "Deploy 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., "@nina_plannerwrite a plan for M31, load lights, and start the sequence"
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.
π nina_planner β An Observatory Operator Agent

nina_planner is an MCP tool server for N.I.N.A. (Nighttime Imaging 'N' Astronomy). The separate events nina-notify plugin is OpenCode-specific. The focus is on building, executing, and monitoring multi-target sequence runs from observation plans.
π Quick Start
π Prepare N.I.N.A. β install the Advanced API, Sequencer+, and Session Metadata plugins, and put
$$TARGETNAME$$in the profile'sFilePattern(see Notes) β without it, light frames can't be attributed to a plan.π¦ Install requirements β opencode v2 (the
nina-notifyplugin is not compatible with Claude Code or other coding agents), Python 3.11+, and uv. Dependencies are installed byuvon first run.π§© Pick a config β replace
.opencode/opencode.jsonwith the opencode.json variant that matches where opencode runs:win_opencode.json(Windows),wsl_opencode.json(WSL), orsmb_opencode.json(e.g. a Mac reaching a N.I.N.A. VM).π Point at N.I.N.A. β set
NINA_ENDPOINT(default127.0.0.1:1888) in the config'senvironment. If the server isn't on the N.I.N.A. machine, also setNINA_DRIVE_MOUNTso image metadata can be read, assmb_opencode.jsondoes (see Environment Variables).βΆοΈ Start opencode in the project root; it launches the MCP server for you.
π Plan a night β list targets in
PLAN.mdand let the agent write plans, load a sequence, and start it.
Related MCP server: NASA-MCP
π§° Available Tools
Each table gives a one-line summary; details for the larger tools are in Tool Details.
π‘ Equipment & Telemetry
Tool | Purpose |
| Live connection, state, measurements, and capabilities of all equipment, plus weather and safety-monitor status. β οΈ Not read-only β it auto-connects devices (see details). |
| Active NINA profile and static configuration: site, optics, camera geometry, filters, plate solvers, image-save path. Read-only. |
| Summaries (id, name, description, last-used time) of every NINA profile, with the active one marked |
| Switch the active NINA profile by GUID from |
| Timestamped NINA events from the last |
| NINA application log entries from the last |
| Capture the N.I.N.A. dashboard as a PNG; returns the file path and the image. Only side effect: switches NINA to the Imaging tab. |
πΊοΈ Planning & Progress
Tool | Purpose |
| List every observation plan file in the project directory with its |
| Validate and write an observation plan JSON file for later use by |
| Expand a base plan into a multi-pointing mosaic plan from a Telescopius CSV. Creates the file only. |
| Per-frame-type acquired/remaining counts for every pointing of a plan. Use before |
| Whole-project capture audit in one call: reconcile plan expectations with CSV attribution (plus the one filesystem fact the CSV cannot hold β |
| Per-exposure quality, guiding, pointing, and weather metrics for one pointing of a plan. Read-only. |
| Live per-frame statistics for the current (most recent) frame, straight from the |
π Sky Calculations
Powered by astropy; no NINA connection is needed for resolve_target.
Tool | Purpose |
| Resolve a deep-sky object name (e.g. |
| Altitude/azimuth of each target at the active site, sorted highest first, for ranking candidates. Each target is |
| Next sunrise/sunset plus all four twilight boundaries (civil β6Β°, nautical β12Β°, astronomical β18Β°) and the astronomical-darkness window in effect or next to open, with |
| Ranks Messier and bright NGC objects to observe from the active site right now, returning |
π¬ Sequence Management
Tool | Purpose |
| Load a sequence for the whole night with safety guardrails. π€ Never starts it. An empty list is the stow. See details. |
| Start the currently loaded sequence immediately. Use after |
| Stop the running sequence and wait until NINA reports it stopped. β οΈ Does not stow the scope. |
| Per-step view of the loaded sequence: plan attribution, status, exposure progress, horizon altitude, errors. |
βοΈ NINA Lifecycle
Tool | Purpose |
| Lightweight liveness probe (no device probes); never raises on a down NINA. |
| π Launch NINA into the interactive desktop session. Raises if NINA is already running or the host has no Windows interop. |
| π Terminate NINA.exe immediately via |
π Tool Details
get_site_equipment_status
Returns the current connection, operating state, measurements, and capabilities of active observatory equipment (mount, camera, focuser, guider, filter wheel, rotator, dome, switches), including weather and safety-monitor status (whether the enclosure is open and it is safe to unpark) and mount capabilities (can_park, can_find_home) that determine how the scope can be stowed. Use for live operational and safety checks.
β οΈ Side effect: unlike the other telemetry tools, this call is not read-only. It connects any device that is present but disconnected. If any device is mid-connect the call errors; retry after a few seconds.
get_site_profile
image_save_path is the raw Windows path from the profile; local_image_save_path is the derived path usable on this host (the drive letter is mapped through NINA_DRIVE_MOUNT, default /mnt/<drive>, on Linux/WSL/macOS; unchanged on Windows) and is the one to use for local file access. Use get_site_equipment_status for live equipment state.
list_site_profiles / switch_site_profile
list_site_profiles only exposes summaries; only the active profile exposes full site/optics/filter detail, so use get_site_profile to inspect it. switch_site_profile passes the id straight through to NINA; an unknown id or refusal surfaces NINA's own error. Switching disconnects and reconnects equipment and changes site, filters, image-save path, and file pattern. Confirm with get_site_profile afterward.
get_events / get_logs
Use get_events to reconstruct activity; use get_site_equipment_status and get_sequence_state for current status. Use get_logs to diagnose sequence failures, equipment communication problems, and unexpected behavior.
screenshot_dashboard
Returns two content blocks: the absolute file path as text, then the image itself (e.g. /var/folders/.../T/nina_screenshots/dashboard_20261005T120000123456.png). The image is written to the system temp dir under nina_screenshots/, one uniquely named file per call, nothing overwritten. Clients that render MCP image blocks show the capture inline; otherwise open the returned path with the read tool. The tool is unstructured (no outputSchema) because it carries mixed text/image content. Switches NINA to the Imaging tab first so the capture shows the main imaging workspace; that tab switch is the only side effect.
get_imaging_metadata
Returns per-exposure imaging metadata for one pointing of an observation plan, parsed from ImageMetaData.csv and WeatherData.csv in the N.I.N.A. imaging directory. image_type is light (default), dark, bias, or flat (case-insensitive).
Attribution: lights are attributed via the underscore-delimited plan-id token in the recorded file path (
_{base_plan_id}_for a single-pointing plan,_{base_plan_id}-{pointing_index}_per pane for a mosaic; no target-name fallback). Dark/bias frames are matched plan-wide by image type and exposure; flat frames by image type and filter (exposure is not checked for flats). All calibration frames count only when shot withincalibration_window_daysof the session start (the earliest attributed light), on either side of it.pointing_index: range-checked (1..pointing count) for every image type and labels the response. For lights it selects the pointing; for calibration types it only selects which pointing's light session anchors the window.Scope: reports the frames that are attributable, not what the plan still needs β use
get_plan_progressfor that.Output: a wide table with one row per exposure: identity fields (
file_path,exposure_number,exposure_start,duration,filter_name) plus metrics as columns namespaced by group (quality.hfr,guiding.rms,background.adu_mean,pointing.airmass,weather.temperature, ...). Weather samples are joined by exposure number and UTC exposure start. The result also carriesplan_id,pointing_index, andtarget.Timestamps: come from the CSV's
ExposureStartUTCcolumn and are displayed in the host's local time with its UTC offset (e.g.2026-09-21T21:16:25.123-05:00); calibration-window arithmetic runs on UTC instants.Summary: columns constant across the returned frames are listed once under
summary.constants; columns with no real values (NaN/-1, empty,n/a, or the0sentinel N.I.N.A. writes for unmeasured metrics) are listed undersummary.unpopulated.
get_frame_stats
Returns N.I.N.A.'s live per-frame statistics for the current (most recently captured) frame, read from the GET /v2/api/image-history API β the same source the nina_notify quality watchdog polls, exposed on demand so a QUALITY-WATCHDOG wake can be verified against the exact numbers that fired it.
Full record:
hfr,hfr_st_dev,stars, background ADU (mean,median,st_dev,min,max), guiding RMS text (rms_text),temperature,gain,offset,filter,exposure_time,target_name,filename, anddate(the capture timestamp). A genuine0(e.g.stars: 0, a star-count failure) is preserved as0β unlike the CSV path,0here is a real reading, not a "not measured" sentinel.Single frame only: despite its name,
image-historyserves only the current frame β one record, no paging, no count parameter. For a multi-frame trend useget_imaging_metadata, which reads the on-diskImageMetaData.csvfor the whole session. This tool is the live view; that one is the historical view.image_type: optionally passed through as the API'simageTypefilter to assert the current frame's type (e.g."light","dark"). When the current frame isn't of that type the tool reportscount: 0with a note rather than erroring.Output:
{count, image_type, frame, note}βcountis1when a frame is present (else0, withframe: nullandnoteexplaining why). This tool is read-only.
write_plan_file
Validates and writes an observation plan to a JSON file. The plan defines one or more target pointings (RA/Dec/PA, optional label), acquisition intent, light and calibration frames, batching, cooling, autofocus, guiding, and observing constraints. See The Observation Plan.
write_mosaic_plan
Expands a base observation plan into a multi-pointing mosaic plan from a Telescopius-formatted mosaic CSV (see Mosaic CSV format). The base plan supplies every setting except the pointings, which are replaced wholesale by one pointing per CSV row. Writes <target>_<intent>_mosaic_<timestamp>.json in the project directory unless output_path is given.
get_plan_progress
Returns per-frame-type acquisition progress for every pointing of an observation-plan JSON file: for each exposure group, total_count from the plan, acquired_count attributed to that plan/pointing from the imaging metadata (ImageMetaData.csv), remaining_count, and deficit_count (the raw total β acquired, floored at zero, never adjusted for the window).
Attribution: lights are attributed via
_{base_plan_id}_(single-pointing) or_{base_plan_id}-{pointing_index}_(mosaic pane). Flats are matched by image type and filter, darks/bias by image type and exposure, and all count only when shot withincalibration_window_daysof the earliest attributed light, on either side of it (inclusive; a 1-day window spansanchor β 1d β¦ anchor + 1d).Calibration window: calibration groups also carry
window_open(bool) andwindow_end(earliest light + calibration_window_days, displayed in the host's local time with its UTC offset). While the current time is inside the windowremaining_countistotal β acquired; once outside, no frame shot now could be credited, soremaining_countis0(withwindow_open: false).acquired_countstays truthful, and lights carry neither field. Raisecalibration_window_daysor write a new plan to reopen the window.remaining_count == 0is not "complete": the window rule above zeroes a calibration group's remaining even when frames are missing, so completion is decided fromacquired_countvstotal_countand window state instead β a closed window under a deficit is reported asexpired_with_deficit, not complete. Per pane,statusiscomplete/incomplete/expired_with_deficit; the top level carriesexpired_with_deficit(bool), andcompleteis true only when every pane is genuinely done. The expired case is separated fromincompletebecause its fix differs: no sequence run can close an expired calibration deficit β raisecalibration_window_daysor write a new plan β andwarningsays so.Quality thresholds: pass
max_hfr,min_detected_stars, and/ormax_guiding_rms_arcsecto exclude light frames that fail them (frames missing the relevant field are excluded when a threshold is set).Scope: one plan per call β for a whole-project sweep (every plan, disk files included, in one call), use
audit_plans.Output:
complete(all panes genuinely done β decided from acquired counts and window state, never fromremaining_count == 0alone) plus apointingslist with per-pane progress, per-panecomplete, and per-panestatus, so panes can be acquired sequentially.Overshoot β read this before trusting
complete: light items also carrysurplus_count(frames attributed beyondtotal_count), and the top level carriesovershot(bool) andwarning(string or null).acquired_countis cumulative across every run of the plan whiletotal_countis a per-plan constant, so amode="full"re-shoot asks N.I.N.A. fortotal_countmore frames and the older ones keep the plan complete no matter what that run delivered β a re-shoot that lost 103 of its 120 frames still reportedacquired_count: 137, remaining_count: 0, complete: true. Whenovershotis true, treatcompleteas "these frames exist" and not as "the last run succeeded": confirm what the latest run actually landed withaudit_plansplus the per-date frame folders on the imaging drive.surplus_countcannot tell a re-shoot apart from the same target shot on separate nights under one plan id, so it is a prompt to check, not a verdict. Lights only β calibration is pool-credited across plans and routinely overshoots.
list_plans
list_plans(project_dir?)
Returns {"plans": [...], "errors": [...]} β one entry per plan JSON in the project directory (default: the server's working directory) with file, absolute path, plan_id, target, intent, pointing_count, and written (mtime, ISO-8601 local with offset). Unparseable JSON files are reported per file under errors instead of failing the call, and a relative project_dir resolves against the project directory. Use it to enumerate plans without globbing; audit_plans shares the same discovery.
audit_plans
audit_plans(plans?, project_dir?, types?, include_orphans=true)
Answers "are all my targets' captures saved to disk, and what is left to do?" for every plan in one call, replacing per-plan get_plan_progress sweeps (15 plans = 15 calls otherwise). plans selects a subset (paths or names, resolved against project_dir, default = every *.json in the project directory); types subsets the output rows to any of light / flat / dark / bias (case-insensitive).
Two truth sources, reconciled per plan Γ frame type:
expected(the plan'stotal_count, summed over its exposure groups β per pointing for lights, plan-level for calibration) andattributed/remaining(exactly whatget_plan_progressreports, quirks included). The filesystem is consulted for exactly one factImageMetaData.csvcannot carry: frames N.I.N.A. rejected via itsBAD_filename prefix β2026-10-22_04-46-37_L_...fits.fzβBAD_2026-10-22_04-46-37_L_...fits.fzβ whose CSV row still names the pre-rename file, so the reject never reachesattributed; the light row reports them asbad. Calibration rows carryown_rows: how many of that type's recorded frames sit under this plan's own token (_{base_plan_id}_, or_{base_plan_id}-{n}_per mosaic pane) rather than the shared pool.Top-level output:
all_saved(true only when no row needs work, no plan file failed to load, and no plan is unshot),totals(expected / attributed across everything, plus anever_shotcount),left_to_do,blocked,never_shot,rows,orphans,errors, androot(the imaging directory actually scanned).left_to_doholds only what running a sequence can fix βMISSING_FILESand row-levelNEVER_SHOTrows, withmissing=remainingand anactionhint per type, plus oneNEVER_SHOTentry per unshot pointing of a wholly unshot plan withmissing= that pointing's whole light ask.blockedisEXPIRED: the calibration window closed under a deficit, so only raisingcalibration_window_daysor writing a new plan helps.never_shotβ the left join's blind spot, closed: discovery is driven byImageMetaData.csv, so a plan that was written but never imaged has no row to join on and used to vanish from the audit entirely. The cost was real: an operator readingleft_to_doalone saw two 1-frame deficits while six whole unshot plans (240 frames) also waited β found only by diffinglist_plansby hand. Only light attributions count as a reference: calibration is pool-credited and lands under the shared base token, so a plan whose id appears solely on flat/dark/bias rows has never had a light acquired and is reported here, not joined into the audit by its own darks. Every loadable plan whose id no recorded light frame references is now reported here (plan_id,file,target,intent,pointing_count,light_expected,written) and inleft_to_dowithreason: "NEVER_SHOT". Matched on plan id, not file: a rewritten/archived twin of an already-shot plan is never flagged (its frames exist under the shared id), and two unshot copies of one plan report once, from the shallowest file. Unshot plans still get norowsβ expected vs attributed means nothing for frames never attempted β and they makeall_savedfalse, so delete or move out any plan file you never intend to shoot (unrelated JSON is already ignored: it never loads as a plan). Explicitplans=never populates it: naming a file is the request to audit it, so an unshot plan named there audits as an ordinary row with statusNEVER_SHOT.statusper row (first match wins):EXPIREDβ calibration window closed with an uncredited deficit; surfaces it despiteremainingbeing forced to0by the window rule β³NEVER_SHOTβ a light row with zero attributed frames: no run ever delivered a frame for this pointing, so the whole ask is untried work. Identified from the attribution count itself, never fromremaining(which is justexpected β attributedand cannot tell untried work from a partial deficit) πMISSING_FILESβremaining > 0and the frames genuinely aren't there: shoot more π«SUSPECT_JUNKβ degenerate light/dark rows (duration under half the plan's exposure or0.01s, e.g.0.00003sSmart-Exposure junk) riding alongside an otherwise-complete set. Bias and flats are exempt (0s is correct for bias; SkyFlat adapts). When junk instead leaves a deficit, the status isMISSING_FILESwith the junk count as the cause ποΈPOOL_CREDITEDβ calibration satisfied plan-wide from another target's directory while this plan's ownown_rowsfall short β informational, not a gap π·οΈOVERSHOTβ lights attributed above expected: the target has been acquired more than once, either by amode="full"re-run or on separate nights under one plan id. Harmless in itself βΉοΈ, but it is the case wherecompletecannot describe the latest run β a re-shoot that lost frames still reads complete because the older ones satisfy the plan β so confirm what that run landed via the per-date folder counts π§OKβ
β οΈ Why the columns can disagree: calibration is matched plan-wide (flats by type+filter, darks by type+exposure, bias by type, all within the window around the earliest attributed light), so one bias set credits every plan and
attributedcan legitimately exceed that plan's ownown_rowsβ which is precisely whatPOOL_CREDITEDreports. Lights also require the exposure duration to match (Β±0.05s), so degenerate frames never count. βΉοΈ Abad-rejected frame never reachesattributed, so it shows up as aremainingdeficit explained by the light row'sbadcount. There is no disk-vs-CSV cross-check any more: a frame the Session Metadata plugin never recorded appears in neither source, so it surfaces as an ordinaryMISSING_FILESdeficit. That is rare and safe to re-shoot, which is all this audit ever advised.π¨ Failure semantics: a missing or unmounted imaging root raises (naming the site profile path and
NINA_DRIVE_MOUNT) rather than reporting empty counts β "no frames" and "no mount" must never look alike. A broken plan file lands inerrorsand the rest of the sweep still runs; a project directory with no plans returns awarningwithroot: null.orphanslists landing token directories whose plan file no longer exists (from deleted plans) β the dual ofnever_shot, which is plan files whose frames do not exist. Every.fits/.fits.fzunder an orphan is counted regardless of what its parent folder is called, so an unexpected folder name can never make real frames read as absent.
load_sequence
load_sequence(request=[{action, plan, variant?, pointing_index?, mode="remaining", max_hfr?, min_detected_stars?, max_guiding_rms_arcsec?}, ...])
Loads a sequence with safety guardrails β π€ it never starts it; call start_sequence() to begin. β οΈ Fails if a sequence is already running β call stop_sequence() first.
request is a list of request objects composing one N.I.N.A. sequence for the whole night: a single Start area, one Target area holding each step's containers in list order, and one End area that always stows β one bounded 3-minute "On Safe" wait, then park (or home) the mount and warm the camera. π
ΏοΈ An empty list (the default) is therefore the stow; there is no stow/park/warm/unpark action, and load_sequence() with no arguments is the teardown.
Field | Description |
|
|
| The observation-plan JSON path. Required by every action; omitting it raises an error naming the entry. |
| 1-based, default 1. Selects the pointing; applies to lights only. Calibration is plan-level, so a calibration request must omit it (passing one is rejected). |
| Scoped to the action; a bad pairing fails naming the entry. |
|
|
| Exclude failing light frames from the acquired count. |
Behavior:
Lights embed
_{base_plan_id}_(single-pointing plan, shared with its calibration) or_{base_plan_id}-{pointing_index}_(per mosaic pane) in the target name for attribution, which requires$$TARGETNAME$$in the profile'sFilePattern. The token is underscore-delimited on purpose: N.I.N.A. rewrites[and]to_when it substitutes$$TARGETNAME$$into the path, so the id is written exactly as it will be recorded (the older bracketed and parenthesised spellings still attribute as legacy).Steps run in order as the sequence reaches them; a step with its own waits (lights, flats) waits, or is skipped once its window has passed, without blocking the steps behind it.
Entries already complete are skipped and reported; if every entry is complete nothing is sent to N.I.N.A. A calibration group whose window expired under a deficit reports the expired shortfall rather than "complete" (and still loads nothing β no frame shot now could be credited).
A
full-mode load of a light group that is already complete appends a β οΈ warning naming the frame count, because nothing downstream can verify the outcome afterwards βget_plan_progressreportscomplete: truewhether the re-shoot delivered every frame or a handful, since its acquired counts are cumulative across runs. Calibration is deliberately exempt: it is pool-credited across plans, so adark/bias/flatgroup reads "already complete" on nearly every plan and warning there would fire on every routine calibration load. A metadata read failure degrades to no warning rather than a failed load βfullis the mode chosen precisely when remaining counts are untrusted.Profile, equipment, and metadata are fetched once, one POST carries the whole sequence, and a validation error names the entry by 0-based position (
request[1]: ...).
stop_sequence
For an urgent halt or a stuck sequence. β οΈ The scope is left where it is. To stow afterward, load_sequence() (empty list) and start it. Avoid interrupting an in-progress stow unless safety requires it, since stopping mid-slew can leave the scope in an unsafe position.
get_nina_status
Cheaper than get_site_equipment_status. Returns running (true iff GET /time answered β a missing local NINA.exe does not mean NINA is down when it runs remotely), lifecycle_control_available (this host has Windows interop, so start_nina/stop_nina can work), probe_error (N.I.N.A.'s failure text, or None when running), and a one-line summary.
start_nina / stop_nina
stop_nina determines liveness from the REST API: it is a no-op when NINA is already down and raises when NINA is up but remote (no local Windows interop), since it cannot be terminated from here. start_nina raises if NINA is already running (call stop_nina() first) or if this host has no Windows interop to launch a remote NINA.
π The Observation Plan
A plan is a JSON document that describes one complete imaging session. It encodes the target, exposure settings for three of the four frame types (light, dark, bias β flats only list a filter and a count, since N.I.N.A. picks their exposure automatically), and equipment configuration (cooler, autofocus, guiding, constraints).
π§± Plan structure
An example json plan:
plans/m15_globular-cluster_20261003T112008.json
{
"target": "M15",
"intent": "Globular cluster",
"plan_id": "plan-a745ffa2357a",
"description": "Seestar S30 starter plan for M15. Use the available broadband L filter and short exposures to help preserve the bright cluster core; 90 ten-second light frames for 15 minutes total.",
"pointings": [
{
"label": "M15 center",
"ra_hours": 21.49954,
"dec_deg": 12.167,
"position_angle_deg": null,
"row": null,
"column": null
}
],
"batch_size": 10,
"calibration_window_days": 7,
"cooler": {
"on": false,
"setpoint_celsius": -10.0
},
"constraints": {
"min_altitude": 30.0,
"horizon_offset_degrees": 2.0
},
"autofocus": {
"reference_filter_name": "L",
"hfr_increase_sample_size": 3,
"hfr_increase_threshold_percent": 15.0,
"every_n_exposures": 10,
"threshold_celsius": 1.0
},
"guiding": {
"dither_every_n_exposures": 2,
"check_drift_every_n_exposures": 6,
"max_drift_arcmin": 1.5
},
"light_frames": [
{
"filter_name": "L",
"exposure_time_seconds": 10.0,
"total_count": 90
}
],
"flat_frames": [
{
"filter_name": "L",
"total_count": 20
}
],
"dark_frames": [
{
"exposure_time_seconds": 10.0,
"total_count": 20
}
],
"bias_frames": [
{
"total_count": 20
}
]
}π§Ύ Fields
Field | Required | Description |
| no | Catalog designation, e.g. "M31", "NGC 7000" |
| no | 2-3 words describing the goal |
| no | Stable identifier stamped on write. Used for frame attribution; when absent, a deterministic hash of plan content is used. Must be empty or ASCII letters, digits, |
| no | Explanation of the observation plan, including rationale, exposure goals, equipment, or sky constraints. |
| yes | One or more pointing objects. Each requires |
| no | Exposures per batch; defaults to |
| no | Defaults to |
| no | Cooler settings: |
| no |
|
| no |
|
| no |
|
| yes | Nonempty list of light exposure specs, each with |
| yes | Nonempty list of flat exposure specs, each with |
| yes | Nonempty list of dark exposure specs, each with |
| yes | Nonempty list of bias exposure specs, each with |
π Mosaic CSV format
For write_mosaic_plan, use a Telescopius-formatted CSV with these nine columns in this order:
Pane, RA, DEC, Position Angle (East), Pane width (arcmins), Pane height (arcmins), Overlap, Row, Column
Pane 1, 0hr 56' 01", 45ΒΊ 51' 18", 0.00, 309.00, 205.80, 10%, 1, 1
Pane 2, 0hr 29' 28", 45ΒΊ 51' 18", 0.00, 309.00, 205.80, 10%, 1, 2
Pane 3, 0hr 55' 22", 42ΒΊ 46' 13", 0.00, 309.00, 205.80, 10%, 2, 1
Pane 4, 0hr 30' 07", 42ΒΊ 46' 13", 0.00, 309.00, 205.80, 10%, 2, 2
Pane 5, 0hr 54' 47", 39ΒΊ 41' 07", 0.00, 309.00, 205.80, 10%, 3, 1
Pane 6, 0hr 30' 42", 39ΒΊ 41' 07", 0.00, 309.00, 205.80, 10%, 3, 2
Pane 7, 0hr 54' 15", 36ΒΊ 36' 00", 0.00, 309.00, 205.80, 10%, 4, 1
Pane 8, 0hr 31' 14", 36ΒΊ 36' 00", 0.00, 309.00, 205.80, 10%, 4, 2Each row becomes a Pointing: RA/DEC are parsed server-side (sexagesimal like 0hr 56' 01" / 45ΒΊ 51' 18" or decimal), Pane becomes the label, and Row/Column are stored as row/column metadata (N.I.N.A. does not use them β they are kept for ordering, labeling, and later mosaic assembly). The base plan's plan_id is cleared so the mosaic gets its own content-derived id; each pane is attributed independently via _{plan_id}-{pointing_index}_.
π« No rotator: every pane's
position_angle_degmust be0. Any nonzero PA fails loudly, since PA can't be enforced without a rotator.Acquiring panes: query
get_plan_progress(file_path)to see which panes still need frames, then load the first incomplete pane withload_sequence(request=[{"plan": file_path, "action": "light", "pointing_index": N, "mode": "remaining"}])andstart_sequence(). Repeat pane by pane across the night; when the top-levelcompleteis true, the mosaic is done β and ifexpired_with_deficitis true instead, the lights may well be fine and only the calibration window needs raising or a new plan (seeget_plan_progress).
π Notes
π Required N.I.N.A. plugins: the MCP server requires the following three plugins to be installed in N.I.N.A.: Advanced API, Sequencer+, and Session Metadata.
π Image file pattern: for light frames only, the N.I.N.A. profile's
ImageFileSettings.FilePatternmust put$$TARGETNAME$$in a path segment so frames can be attributed to a plan (e.g.$$TARGETNAME$$\$$IMAGETYPE$$\...). Attribution accepts the token in any segment β directory or filename β but put it in a directory, and let that directory be a direct child of the image-save path (the usual<target> _<plan-id>_landing folder):audit_plansclaims landing directories by the names of the top-level directories under the image-save path, so a filename-only token (or a token nested one level deeper) attributes fine on the CSV side yet still lands the folder inorphans, flagged as belonging to no plan. Its frames are counted correctly under that entry β the disk and CSV views no longer disagree about how many there are β but the folder will keep reading as unclaimed until the pattern puts the token in that directory. Calibration frames (dark, bias, and flat) do not require this token; they are matched by frame type and exposure settings within the calibration window (darks by exposure time, flats by filter alone).β Quality thresholds:
max_hfr,min_detected_stars, andmax_guiding_rms_arcsec(all optional) exclude light frames that fail the thresholds from the acquired count. Frames missing the quality fields are excluded whenever a threshold is set (fail-closed). Thresholds are applied only to light frames β calibration frames have no star quality.π Manually failing a frame: delete (or move using thumbs down icon) the image file on disk β e.g. a bad
.fits/.tif. The metadata row stays in the CSV (the plugin only appends), but it is omitted when the metadata is read, so progress no longer counts that frame as acquired and the nextmode="remaining"load re-acquires it.πΎ The plan file is persistent. You can inspect, edit, and reuse it across sessions. Changing the file will, however, alter its derived
plan_id, which is calculated whenplan_idis missing. A hand-writtenplan_idmust satisfy the same charset rule (empty, or ASCII letters, digits,.and-) β anything else,_included, is refused when the plan is loaded.π§© opencode.json variants: the repo contains example opencode.json variants:
win_opencode.jsonfor Windows,wsl_opencode.jsonfor WSL, andsmb_opencode.jsonfor, say, a Mac accessing a N.I.N.A. VM with its image file directory mapped back over SMB.
π nina-notify β OpenCode v2 Autonomous Plugin
nina-notify is an opencode v2 plugin (not compatible with Claude Code or other coding agents). Registered in .opencode/opencode.json, it runs as a background server inside opencode and does two things:
Websocket event monitoring β Connects to the N.I.N.A. event socket (
ws://<host:port>/v2/socket), subscribes to all events, and wakes the active agent only when a decision is actually required.Only three classes of event ever wake the agent, because N.I.N.A. already runs routine work itself β autofocus, dithering and drift correction are its own internal triggers, so reporting them to the agent buys nothing:
Milestones β sequence lifecycle (
RUN-STARTING/RUN-FINISHED/SEQUENCE-STARTING/SEQUENCE-FINISHED) and per-target transitions (TS-NEWTARGETSTART): the observation queue needs evaluating β transit checks, altitude limits, next target dispatch.Faults β failures (
ERROR/FAIL/EXCEPTION/ABORT/INTERRUPT/CRITICAL/TIMEOUT, which catchesCAMERA-DOWNLOAD-TIMEOUT) and link loss (OFFLINE/DISCONNECT/LOST/EMERGENCY): the agent diagnoses the root cause and selects a fallback. The tokens are matched as substrings, soPRESTOW-DELAYis caught bySTOW.Safety β safety state (
SAFE/UNSAFE/SAFETY/WEATHER/DOME/ROOF) and mount/stow (MOUNT*/STOW*/PRESTOW-*): stow or stand down.
Two demotions came out of that same 16h sample of real events (476 events β 45 matched the regex β but only 11 of the 24 coalesced prompts actually carried a milestone, fault or safety change):
MOUNT-CENTERnever wakes β it is the slew/settle centering the mount performs itself on every acquisition, and it alone produced 8 wake events β and device link events wake only while a sequence is running. N.I.N.A. cycles every device at session start and teardown, which produced the other 29 of 45 wake events, and a link change is only operationally interesting while something is imaging. That gate is seeded fromGET /v2/api/sequence/jsonat startup (using the same any-node-RUNNINGrule as the MCP server, so the two never disagree) and maintained fromRUN-*/SEQUENCE-*events; a seed failure leaves it wake-worthy, the safe default. Safety state changes (SAFETY-CHANGED) match neither rule and always wake. Replaying the same 476 events through the new rules gives 7 prompts instead of 24 β every one a genuine milestone, fault or safety change.Wakes are coalesced over a 250ms window, so a burst of distinct fault events costs the agent one prompt instead of one per event; safety still lands essentially instantly, since an agent wake already takes seconds.
Routine telemetry is never pushed. Everything else β per-frame
IMAGE-PREPARED/IMAGE-SAVE,GUIDER-*,ACQUISITION-*,EXPOSING-FINISHED, and any unrecognized event β is counted and dropped. Nothing is buffered to disk either: N.I.N.A. already keeps its own event history server-side (GET /v2/api/event-history), which the MCP server exposes asget_events(since=β¦). So every wake prompt carries the instructionget_events(since=<seconds since your last wake>)plus how many routine events were waiting, and the agent reads exactly the window it needs, on demand. Per-frame quality detail (HFR, star counts, ADU) is likewise already on disk inImageMetaData.csv/get_imaging_metadata.Quality watchdog. Because routine telemetry no longer wakes anyone, a slowly degrading night (climbing HFR, collapsing star count) would otherwise stay invisible until the next milestone. While
lightframes are being acquired the plugin pollsGET /v2/api/image-historyon eachIMAGE-SAVEand wakes the agent as an operational fault β aQUALITY-WATCHDOG: β¦event β when aframe-quality-diagnosticsstanding trigger holds for 3 consecutive frames βHFR > 3β³or under10detected stars. The consecutive-frame rule is deliberate: the single-frameGUIDER-STARTsettles and post-recenter dips that skill documents can never wake anyone, and it fires once per degradation, re-arming only after the metric recovers. Calibration is exempt twice over: a known dark/bias/flat step (fromACQUISITION-STARTING: [{kind}] β¦) is skipped without even polling, and the record's ownImageTypeis a per-frame hard stop β darks, biases and flats have no stars at all, so without that check every calibration frame would read as a star-count failure. ThatImageTypecheck is also what keeps the watchdog working when the plugin attaches after a step'sACQUISITION-STARTING.qualityWatchdog: falseturns the whole check off.image-historyis used in preference toβ¦/equipment/camera/capture/statistics: that one is a transient "last capture" buffer wiped by any camera reconnect, profile switch or sequence load (it answered400 No image availablefor a whole idle afternoon, while the previous night's frame was still served fromimage-history). Freshness is proven, not assumed β a sample is accepted only when itsDateis newer than the last one, cross-checked against theIMAGE-SAVEevent to 0.95 ms, so two saves reading one identical record can never count a single frame twice. A record with no provableDate, or nothing recorded yet, costs exactly one deferred re-check (1.5s later) and then leaves the streaks untouched: a missing sample may only delay a detection, never invent one, and a source that never advances cannot escalate into a retry loop (two attempts per frame, spaced at least 5s apart).Net effect: ~10β30 prompts per night instead of ~1,100 β one per milestone, fault, or sustained quality problem.
Interval check β every
intervalCheckMinutes(default 45, previously-1/disabled) it prompts the agent to query observatory status (get_sequence_state,get_site_equipment_status) and decide what to do next, even when no N.I.N.A. events are firing. With routine telemetry no longer prompting, this is the only periodic driver between milestones, so it deliberately fires even when the event socket is down β a silently dead event stream is exactly when a status check matters most.
The plugin auto-reconnects on websocket disconnection with a 5-second retry. This 5s delay is connection recovery only β it fires solely on onclose and waits before re-establishing the socket; it never delays event delivery. Immediate events flush after a 250ms coalescing window and routine telemetry never flushes at all. On server dispose, it cleans up all timers and the socket.
π« PLAN.md β the living wishlist
A markdown file at the project root, edited by the user at any time (before or during the night). It is intentionally lightweight β entries can be as simple as a target name:
# Tonight - 2026-10-08
- M31 "before the moon comes up"
- Veil Nebula plan=veil-nebula_..._20260913.json prio 1
- M33 "if high targets done"
Rules:
- min_altitude: 30
- horizon_offset_degrees: 2
- tie_break: priority, then altitudeπ Project directory
Plan JSON files, PLAN.md, and PROGRESS.md live in the project directory. Relative paths (e.g. a plan= entry in PLAN.md, or mosaic_csv) resolve against it, and the project directory is simply the server process's working directory: opencode starts local MCP servers in the workspace, and the launch command uses uv run --project rather than uv --directory β --directory chdirs into .opencode/local/mcps/nina_planner, which would make relative paths resolve inside .opencode/ instead of the project.
π€ AGENTS.md β Agent Instructions
Tells the agent it runs an observatory.
# Observatory Controller
You are the automated observatory controller. The observatory runs NINA via the nina-planner MCP server.
## Always-on rules:
- `is_safe=false` (enclosure closed) or weather violations mean: scope stowed, if not by the NINA sequence then by you.
- Append a timestamped entry to `PROGRESS.md` after each action; never edit `PLAN.md` (the user's living wishlist).
- You are woken only by milestones, faults, safety changes, the interval check, and sustained quality problems. Routine telemetry (per-frame image saves, guider, autofocus) is **not** pushed to you β every wake prompt names a `get_events(since=β¦)` window covering the quiet gap, so read it before deciding. `QUALITY-WATCHDOG: β¦` in a wake means frame quality degraded and held. Device connect/disconnect events wake **only while a sequence is running**; if you hear nothing about a device, that is by design, not a missed event.
## Detailed procedures live in `.agents/skills/`:
- `safety-interrupt-recovery` β on any new event or interval check: NINA liveness, equipment/safety checks, stowing.
- `night-loop` β on interval check or `RUN-FINISHED`: pick and start the next target, or end the night.
- `nina-lifecycle` β start/stop/status of the NINA application.
## Emoji Communication Guidelines
- You will use emojis liberally to communicate status updates, milestones, and emotional context as things happen.π PROGRESS.md β Shared Progress File
PROGRESS.md is a shared observatory progress file in the project directory. The active session reads it on start to restore context before acting, and appends a timestamped entry after each significant action, recording what was done, the observed equipment and safety state, and any decisions. This keeps history shared across sessions.
π opencode.json β Opencode v2 Sample Configuration
{
"$schema": "https://opencode.ai/config.json",
"instructions": [ ".opencode/rules/*.md" ],
"plugins": [
{
"package": "./local/plugins/nina_notify",
"options": {
"pluginLogs": "C:/Windows/Temp/nina-notify-debug.log"
}
}
],
"mcp": {
"servers": {
"nina-planner": {
"type": "local",
"environment": {
"NINA_PLANNER_LOG": "C:/Windows/Temp/nina-planner-debug.log"
},
"command": [
"uv", "run", "--project", ".opencode/local/mcps/nina_planner", "-m", "nina_planner"
]
}
}
}
}Registers the opencode plugin and the nina_planner MCP server so both run together. The MCP server provides the tools (load_sequence, get_site_equipment_status, etc.) that the plugin-prompted agent calls.
ποΈ Plugin Options
π nina-notify (opencode v2 plugin)
Configured in the plugin's options configuration. (see opencode.json).
Option | Type | Default | Description |
| string |
| N.I.N.A. host and port (no scheme) for the websocket event stream, e.g. |
| number |
| Minutes between autonomous status-check prompts. Accepts a float; converted to milliseconds (Γ60,000) only when greater than 0; with 0 or less no timer is scheduled. Fires regardless of the event-socket state, so a dead event stream still gets attention. |
| boolean |
| Wake the agent as an operational fault when a |
| string | (none) | File path appended on each plugin event (websocket open/message/close/error, every injected prompt, watchdog checks, interval trigger failures). Set to a writable path (e.g. |
π§ Environment Variables
π₯οΈ nina_planner (MCP server)
Variable | Default | Description |
|
| N.I.N.A. host and port for the REST API ( |
|
| Local mount root for the drive letter in the profile's |
| (none) | File path where the server appends each NINA request/response payload for debugging. Logging is disabled when unset. A Windows-style path (e.g. |
|
| Altitude in degrees for flat panel calibration frames |
|
| Azimuth in degrees pointing west for dawn flats |
|
| Azimuth in degrees pointing east for dusk flats |
|
| Windows path to |
π§ Setting up WSL
Make sure mirroring is setup in WSL to allow you to connect to NINA directly through localhost (otherwise you have to use your machines' IP address). Do this by editing wslconfig and setting networking mode to mirrored.
$ vi $HOME/.wslconfig
[wsl2]
networkingMode=mirroredIn addition, to get the system tools to work to stop and restart NINA should it stop, make sudo passwordless with:
$ echo "$USER ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/90-userπͺ Setting up Windows
I installed the following packages to start:
winget install git.git # bash
winget install Oven-sh.Bun # bun
winget install jqlang.jq # jq
winget install BurntSushi.ripgrep.MSVC # rg
winget install astral-sh.uv # uvNext I installed opencode v2 with:
$ bun install -g --trust @opencode/cliThen to run opencode I ran in windows:
$ export OPENCODE_SERVER_PASSWORD=pass
$ opencode serve --port 4096
$ opencode --server http://127.0.0.1:4096Available Tools
14 toolsget_eventsA
Returns recent timestamped NINA observatory events and their event-specific details from the last since seconds. Use it to reconstruct sequence, equipment, safety, imaging, and error activity. This tool is read-only; use get_site_equipment_status and sequence_get_state for current status.
| Name | Required | Description | Default |
|---|---|---|---|
| since | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It explicitly states 'This tool is read-only', which is a critical behavioral trait. It also implies a time-window behavior via 'since' seconds. However, it does not disclose details like ordering, pagination, or limits on returned events, which would be useful for a tool that fetches historical data. Given the read-only nature, this is a minor gap, so a 4 is appropriate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with zero redundancy. It front-loads the primary purpose and then gives usage guidance. Every sentence earns its place, and it is easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read-only getter with one parameter and no output schema, the description covers the essential information: what it returns, when to use it, and that it is safe. It could mention whether events are sorted or if there are any maximum limits, but these are not critical for a tool of this simplicity. The mention of alternatives is a strong plus.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has one parameter `since` with 0% description coverage. The description fully explains its meaning: 'from the last `since` seconds'. This adds clear semantic meaning beyond the type and default, making it easy for an agent to know what value to supply.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Returns recent timestamped NINA observatory events and their event-specific details'. It also clarifies the scope (time window) and differentiates from siblings by naming them ('use get_site_equipment_status and sequence_get_state for current status'). This leaves no ambiguity about what the tool does and how it differs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly tells when to use the tool ('reconstruct sequence, equipment, safety, imaging, and error activity') and when not to, with named alternatives for current status. The guidance is actionable and covers both inclusion and exclusion conditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_imaging_metadataA
Returns imaging metadata parsed from the ImageMetaData.csv files in the frame folders (LIGHT, DARK, BIAS, FLAT, etc.) of the mounted N.I.N.A imaging directory. Each row is tagged with Date, FrameType, and Source. When an AcquisitionDetails.csv is present in the same session directory, its fields (e.g. TargetName, FocalLength) are injected into each image row unless the image row already has a value for that field. Defaults to lights frames for star-quality checks; pass another image type (light, dark, bias, flat β case-insensitive). Optionally filter to a single YYYY-MM-DD date.
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | ||
| image_type | No | light |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It discloses the merging behavior (injecting AcquisitionDetails.csv fields unless already present), the default image type, and case-insensitivity. It does not explicitly state read-only safety, but the verb 'Returns' implies it, and it adds valuable behavioral context about the data transformation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a multi-sentence paragraph but every sentence carries useful information: source, tagging, injection rule, defaults, and filter. It is front-loaded with the main purpose and does not waste words, though it could be slightly more compact without losing substance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read tool with a documented output schema, the description covers the essential behaviors: source location, row tagging, field injection, default frame type, and date filter. It does not mention error cases or what happens if the directory is missing, but those are minor given the output schema and the tool's read-only nature.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It explains both parameters: image_type with allowed examples and case-insensitivity, and date with a specific YYYY-MM-DD format. This adds meaning well beyond the bare schema and provides concrete guidance for values and format.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'Returns' and the resource 'imaging metadata parsed from the ImageMetaData.csv files', and specifies the source directory and file type. It also distinguishes itself from siblings (which are about site status, sequences, logs) by being the only tool focused on imaging metadata extraction.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context: it defaults to lights frames for star-quality checks and allows passing other image types, and optionally filtering by date. However, it does not explicitly name alternative tools or state when not to use it, so it lacks explicit exclusions but provides enough situational context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_logsA
Returns recent NINA application log entries, including informational messages, warnings, and errors with source and timestamp details from the last since seconds. Use it to diagnose sequence failures, equipment communication problems, and unexpected behavior. This tool is read-only and takes no action.
| Name | Required | Description | Default |
|---|---|---|---|
| since | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It explicitly states the tool is read-only and takes no action, which is important behavioral disclosure. It also describes the content of the returned entries, covering the key behavioral traits an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three crisp sentences: what it returns, when to use it, and its safety profile. It is front-loaded with the core behavior and every sentence adds value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read-only tool with one optional parameter and no output schema, the description adequately covers return content, use cases, and non-mutating behavior. It could specify output limits or ordering, but nothing critical is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, but the description compensates by explaining that logs come from 'the last `since` seconds', giving clear meaning to the only parameter. It does not state the default value, but that is already present in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns recent NINA application log entries with source and timestamp details, which is a specific verb and resource. It does not explicitly differentiate from get_events, but the resource is specific enough to avoid most ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description names concrete diagnostic use cases: sequence failures, equipment communication problems, and unexpected behavior. It gives clear context for when to use the tool, though it does not mention alternatives or exclusion conditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
_get_site_equipment_statusD
| Name | Required | Description | Default |
|---|---|---|---|
| profile | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| dome | No | |
| mount | No | |
| camera | No | |
| guider | No | |
| focuser | No | |
| rotator | No | |
| weather | No | |
| filter_wheel | No | |
| safety_monitor | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Tool has no description.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Tool has no description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Tool has no description.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Tool has no description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Tool has no description.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Tool has no description.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_site_equipment_statusA
Returns the current connection, operating state, measurements, and capabilities of active observatory equipment, including weather and safety-monitor status (whether the enclosure is open and it is safe to unpark), and mount capabilities (can_park, can_find_home) that determine how the scope can be stowed. Use it for live operational and safety checks. This tool is read-only and takes no action.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| dome | No | |
| mount | No | |
| camera | No | |
| guider | No | |
| focuser | No | |
| rotator | No | |
| weather | No | |
| filter_wheel | No | |
| safety_monitor | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses that it is read-only and takes no action, which is a key behavioral trait. Since no annotations are provided, the description carries the full burden, and it adequately covers the safety and side-effect profile.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences with no redundancy. It front-loads the primary function, then adds usage and behavioral notes, each earning its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has an output schema, so return values are defined there. The description explains the purpose, usage, and behavioral constraints, and mentions specific capabilities like can_park and can_find_home, making it complete for an agent to decide when to call it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the schema already covers everything. The description adds no parameter-specific information, but with 0 parameters, the baseline of 4 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns current connection, operating state, measurements, and capabilities of observatory equipment, with specific examples like weather, safety-monitor status, and mount capabilities. This distinguishes it from siblings like get_site_profile and get_events by focusing on live status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states 'Use it for live operational and safety checks,' providing clear context for when to invoke it. However, it does not mention alternatives or when not to use it, so it lacks explicit exclusion guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_site_profileA
Returns the active NINA observatory profile and static configuration, including site location, optics, camera geometry, filters, plate solvers, and image-save path. Use get_site_equipment for live equipment state. This tool is read-only and takes no action.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| site | Yes | |
| optics | Yes | |
| filters | Yes | |
| equipment | Yes | |
| profile_id | Yes | |
| description | No | |
| file_pattern | No | |
| plate_solver | No | |
| profile_name | Yes | |
| image_save_path | Yes | |
| blind_plate_solver | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description explicitly states the tool is read-only and takes no action, which is valuable behavioral disclosure. Since no annotations are provided, the description carries the full burden, and it does so by clarifying the non-mutating nature and that it returns static configuration rather than live state.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with no wasted words. The first sentence front-loads the tool's purpose and content, and the second sentence provides the sibling distinction and safety profile. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is complete for a zero-parameter read-only tool. It names the sibling for live state, lists the content returned, and states the read-only nature. The output schema exists, so return values need not be described in prose. Minor gap: it doesn't mention whether the profile is cached or freshly fetched, but this is not essential for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the schema is trivially complete. The description adds context about what the returned profile contains, which is more useful than parameter documentation in this case. Baseline 4 for zero-parameter tools is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns the active NINA observatory profile and static configuration, listing specific content areas (site location, optics, camera geometry, filters, plate solvers, image-save path). It also distinguishes itself from the sibling get_site_equipment_status by noting that tool is for live equipment state.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly names the alternative get_site_equipment for live equipment state, providing a clear when-to-use distinction. It doesn't exhaustively enumerate all alternatives or exclusions, but the key sibling differentiation is present.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
observation_plan_get_progressA
Returns per-frame-type acquisition progress for an observation-plan JSON file: for each exposure group, the total_count from the plan, the acquired_count attributed to this plan from the imaging metadata (ImageMetaData.csv + AcquisitionDetails.csv), and the remaining_count. Lights are attributed via the plan_id embedded in the sequence target name (falling back to the target name for legacy frames); flats/darks/bias are matched by image type, filter, and exposure. Pass max_hfr and/or min_detected_stars to exclude light frames that fail quality thresholds (frames missing the quality fields are excluded when a threshold is set). Use this before sequence_load_plan to decide what still needs acquiring.
| Name | Required | Description | Default |
|---|---|---|---|
| max_hfr | No | ||
| file_path | Yes | ||
| min_detected_stars | No | ||
| max_guiding_rms_arcsec | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses the attribution logic (plan_id embedded in target name, fallback to target name, matching by image type/filter/exposure) and the exclusion behavior for quality thresholds. It implies a read-only operation but does not explicitly state so, nor does it mention potential edge cases like missing files. Still, it is substantially transparent about its behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is reasonably concise given the complexity, with each sentence adding essential detail. It is front-loaded with the core purpose, then explains attribution, parameters, and usage in a logical order. It avoids redundancy but is slightly long due to necessary technical detail.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has an output schema, so return values need not be described. The description covers the input parameters, the logic, and the usage context. It lacks explicit error handling or assumptions about file existence, but for an agent it provides enough to decide when to call and what to expect. It is complete for typical use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate. It explains file_path as the observation-plan JSON file and explains max_hfr and min_detected_stars as quality thresholds. However, it does not mention max_guiding_rms_arcsec at all, leaving that parameter semantically unexplained. This partial coverage warrants a score of 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Returns') and resource ('per-frame-type acquisition progress for an observation-plan JSON file') with detailed metrics (total_count, acquired_count, remaining_count). It clearly distinguishes itself from siblings by focusing on progress computation and explicitly routes to sequence_load_plan as the next step.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states 'Use this before sequence_load_plan to decide what still needs acquiring,' giving a clear when-to-use directive. It also explains when to pass quality thresholds (max_hfr/min_detected_stars) and how they affect results, covering the main use case without ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
observation_plan_write_fileA
Validates and writes an observation plan to a JSON file for later use by sequence_load_plan. The plan defines target coordinates, acquisition intent, light and calibration frames, batching, cooling, autofocus, guiding, and observing constraints. This tool only creates the plan file; it does not load or start a sequence.
| Name | Required | Description | Default |
|---|---|---|---|
| plan | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds useful context beyond the schema by stating the tool validates the plan, writes it to a file, and does not load/start a sequence. It also mentions the plan_id is 'stamped on write' indirectly through schema, but the description itself doesn't describe side effects of overwriting an existing file, permission requirements, or return behavior. With no annotations provided, it could still disclose more.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Compact and front-loaded: first sentence covers the action, resource, and key limitation; second sentence reinforces scope. No filler, every clause carries meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex nested plan object with no schema-level parameter descriptions, the description gives a high-level overview of what the plan should include, plus a critical caveat about not loading the plan. Given there is an output schema, return values are covered. Minor gaps: no mention of file path, overwrite behavior, or validation failure outcomes, but the core agent task is well supported.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. The description summarizes the plan's contents (target coordinates, acquisition intent, light/calibration frames, batching, cooling, autofocus, guiding, constraints), but it doesn't explain how the single parameter (plan) relates to those fields or that it must be a complete ObservationPlan object. The schema's own field descriptions are sparse, so the description helps but doesn't fully compensate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the tool's action ('Validates and writes'), the resource ('observation plan to a JSON file'), and its downstream purpose ('for later use by sequence_load_plan'). It explicitly distinguishes this tool from sequence_load_plan and sequence_start, making its scope obvious even among siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage β this is the tool to use when you want to create a plan file but not execute it β and explicitly states it does not load or start a sequence. However, it doesn't explicitly say 'use this when you need to persist a plan before loading it' nor does it contrast when to use observation_plan_get_progress or other siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sequence_enter_safety_standbyA
Loads a non-acquisition standby sequence that keeps NINA sequence-level safety and stow guardrails active while the observatory is idle. After loading, call sequence_start. Stop it before loading an acquisition or teardown sequence. Use whenever equipment is deployed and no other sequence is running.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the behavioral disclosure burden. It does well by explaining that this tool only loads a sequence, does not start it, keeps safety guardrails active, and must be stopped before loading other sequences. It does not mention side effects of neglecting the stop step, but the core behavior is transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences carry all essential information: what the tool does, what to do next, and when to stop/avoid it. Every sentence earns its place, and the most important context is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given zero parameters)Skip? Given zero parameters, an output schema, and clear sibling context, the description covers the complete call workflow: load, start, stop before replacing, and the operational condition for using it. Nothing critical is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and schema coverage is 100%, so the schema already exhaustively documents the inputs. Per the rubric, 0 parameters earns a baseline of 4; the description adds no unnecessary parameter detail and needs none.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb ('Loads'), a specific resource ('non-acquisition standby sequence'), and the purpose ('keeps NINA sequence-level safety and stow guardrails active while the observatory is idle'). This clearly distinguishes it from sibling tools that load or execute acquisition or teardown sequences.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit operational guidance: call sequence_start after loading, stop it before loading an acquisition or teardown sequence, and use it whenever equipment is deployed with no other sequence running. This fully orients an agent on when and how to invoke the tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sequence_execute_teardownA
Loads and starts the non-acquisition teardown sequence, safely stowing the telescope (park or home, per mount capability) while NINA's sequence-level safety guardrails remain active. Use for end-of-observation close-down β when you are done observing and want to shut down the scope β or before leaving the observatory unattended. Allow it to complete without interruption. This is separate from sequence_stop, which halts the current sequence but does not stow the scope.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses the stowing behavior, the park/home variation, the retained safety guardrails, and that interruption should be avoided. It does not mention failure modes or prerequisites such as mount connectivity, but the core operational behavior is well covered, which is strong for a zero-parameter tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Each sentence adds distinct value: action, usage, caution, and sibling distinction. There is no redundant content; the description is tightly structured and easy to parse, with the main verb and resource front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter tool with an output schema, this description covers purpose, usage, and operational cautions. It does not need to describe return values since an output schema exists, and nothing necessary for correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and the schema already documents this with 100% coverage, so baseline 4 applies. The description adds no parameter-specific meaning, but none is needed since there is nothing to explain.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action ('Loads and starts the non-acquisition teardown sequence') and the resource (telescope stow), and explicitly distinguishes from sequence_stop. This clearly separates it from all sibling tools without needing to inspect schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly tells when to use: 'when you are done observing and want to shut down the scope' or 'before leaving the observatory unattended,' and clarifies that sequence_stop does not stow. It also gives the operational instruction to 'Allow it to complete without interruption,' leaving no ambiguity about when to invoke it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sequence_get_stateA
Returns the loaded sequence structure and the current status of its containers, instructions, conditions, and triggers. Use it to determine whether a sequence is loaded, running, completed, failed, or waiting. This tool is read-only and takes no action.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description itself discloses that the tool is read-only and takes no action. This is important behavioral context for an agent deciding whether to invoke it. It also conveys what kind of information will be returned.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three short sentences with no filler. It front-loads the core purpose, then adds usage context, then the critical read-only behavior. Every sentence contributes value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter read-only inspection tool, the description covers what it returns, what it can be used to determine, and that it has no side effects. It does not mention error cases or prerequisites, but the tool is simple enough that this is not a major gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and the schema is empty, so there is nothing for the description to add about parameter meaning. The baseline of 4 applies because no parameter documentation burden exists.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Returns') and names the exact resource: the loaded sequence structure and the status of its containers, instructions, conditions, and triggers. This clearly distinguishes the tool from mutation siblings like sequence_start, sequence_stop, and sequence_load_plan.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly tells the agent when to use this tool: to determine whether a sequence is loaded, running, completed, failed, or waiting. It does not name exclusions or alternative tools, but the intended context is clear enough.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sequence_load_planA
Loads an acquisition sequence with safety guardrails from an observation-plan JSON file. Select the frame type: light, dark, bias, dawn_flat, or dusk_flat. In the default remaining mode the sequence only acquires frames still needed (total minus frames already attributed to this plan in the imaging metadata); if nothing remains it reports the plan as complete and loads nothing. max_hfr and/or min_detected_stars exclude light frames failing quality thresholds from the acquired count. Pass mode="full" to acquire the entire plan again. This tool only loads the sequence; call sequence_start afterward.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | remaining | |
| max_hfr | No | ||
| file_path | Yes | ||
| frame_type | No | light | |
| min_detected_stars | No | ||
| max_guiding_rms_arcsec | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral disclosure burden, and it does so well: it states side-effect limitations ('loads nothing' when complete), mode-dependent behavior, and the effect of quality thresholds. It doesn't fully describe every edge case or the behavior of max_guiding_rms_arcsec, but it is substantially transparent for an unannotated tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is six sentences of dense, non-redundant information. It front-loads the core purpose and guardrails, then explains modes and quality filters, and ends with the essential follow-up instruction. Every sentence adds value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers purpose, modes, frame types, quality filters, and the load-versus-start distinction, and an output schema exists to cover return values. It misses explicit meaning for `max_guiding_rms_arcsec` and does not mention prerequisites or error conditions, but overall it is largely complete for a 6-parameter tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It explains `mode` (remaining/full), enumerates all `frame_type` choices, and clarifies `max_hfr` and `min_detected_stars` effects. However, it omits any meaning for `max_guiding_rms_arcsec`, which is a small but notable gap given the 0% schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific action and resource: 'Loads an acquisition sequence with safety guardrails from an observation-plan JSON file.' It also differentiates itself from the sibling sequence_start by explicitly stating 'This tool only loads the sequence; call sequence_start afterward,' so an agent can separate loading from starting.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit usage context: the default `remaining` mode acquires only needed frames, `mode="full"` reacquires the entire plan, and quality thresholds affect light-frame counting. It also provides a clear boundary and follow-up instruction: 'This tool only loads the sequence; call sequence_start afterward,' which routes the agent to the correct next step.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sequence_startA
Starts or resumes the currently loaded sequence, activating its acquisition or safety workflow. Call sequence_get_state afterward to verify that it is running.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the behavioral disclosure burden. It discloses that the tool activates an acquisition or safety workflow and that the sequence may not be immediately confirmed as running, since the agent is told to call sequence_get_state afterward. It stops short of describing error behavior or idempotency, but for a zero-parameter command tool this is meaningful disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with no filler. The primary action and its effect are front-loaded, and the follow-up verification instruction is a single useful addition. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter lifecycle tool with an output schema available, the description is complete: it states the action, the resource, the effect, and the immediate next step. The 'currently loaded sequence' phrasing implies the load prerequisite, and no additional context is needed to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and the schema coverage is 100%, so there are no parameter semantics to clarify. The baseline for a no-parameter tool is 4, and the description appropriately focuses on behavior rather than arguments.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Starts or resumes') and a clear resource ('currently loaded sequence'), and says what it activates ('acquisition or safety workflow'). It also names the sibling sequence_get_state, which helps differentiate this tool from state-checking and lifecycle tools like sequence_stop and sequence_load_plan.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear procedural context: use this to start/resume a loaded sequence, then verify with sequence_get_state. It does not explicitly list exclusions or when to prefer a sibling, but the loaded-sequence qualifier implies a prerequisite and the named follow-up tool gives enough orientation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sequence_stopA
Stops the currently running NINA sequence immediately, leaving the telescope where it currently is β it does not stow the scope. Use for an urgent halt, to interrupt a stuck/looping sequence, or when the running sequence isn't what you wanted. If you then want to park/home the telescope, run sequence_execute_teardown separately. Note that sequence_load_plan and the teardown/standby entry tools already stop any running sequence before loading, so an explicit stop is only needed when you want to halt without loading anything new. Avoid stopping an in-progress teardown during the stow maneuver unless safety requires it, since interrupting mid-slew can leave the scope in an unsafe position. Call sequence_get_state afterward to confirm the sequence has stopped.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It clearly conveys side effects: immediate stop, telescope left in place, no stowing, and the potential unsafe position from interrupting a mid-slew teardown. It also advises confirmation via sequence_get_state. This fully covers the relevant behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than a simple one-liner, but every sentence earns its place: primary behavior first, then use cases, exclusions, a safety warning, and a verification step. The structure is logical and front-loaded with the most critical information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, safety-relevant control tool, the description covers all key operational context: what happens, when to call it, when not to call it, what to do instead, and how to verify the outcome. An output schema exists, so the description need not detail return values. Nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters and the schema is empty, so there is no parameter semantics to document. The 0-parameter baseline of 4 applies; the description appropriately does not invent unnecessary parameter details.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Stops the currently running NINA sequence immediately.' It clarifies the scope by explicitly noting the scope is not stowed and by distinguishing itself from sequence_execute_teardown, making sibling differentiation clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use scenarios (urgent halt, stuck/looping sequence, unwanted sequence) and when not to use it (before the teardown/standby entry tools, during an in-progress teardown unless safety requires). It also names the alternative sequence_execute_teardown for parking/homing and the follow-up sequence_get_state. This is exemplary routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
14 tool updates
v0.1.0- First observed
_get_site_equipment_status - First observed
get_events - First observed
get_imaging_metadata - First observed
get_logs - First observed
get_site_equipment_status - First observed
get_site_profile - First observed
observation_plan_get_progress - First observed
observation_plan_write_file - First observed
sequence_enter_safety_standby - First observed
sequence_execute_teardown - First observed
sequence_get_state - First observed
sequence_load_plan - First observed
sequence_start - First observed
sequence_stop
TDQS
Scored across 14 tools
Most tools are clearly separated by domain: status, profile, events, logs, imaging metadata, plan progress, and sequence control are all distinct. The duplicate `_get_site_equipment_status` tool with no description creates real ambiguity, but it is the only significant overlap.
Tool names follow predictable snake_case patterns such as `get_*`, `sequence_*`, and `observation_plan_*`. The leading-underscore duplicate `_get_site_equipment_status` breaks the convention and should be removed, but the rest of the naming is consistent.
Fourteen tools is within a reasonable range for an observatory planning and operations server, and each major area has dedicated coverage. The duplicate tool slightly inflates the count, but the real scope is well-sized.
The set covers the full observation workflow: diagnostics, plan creation, progress tracking, sequence loading/starting/stopping, teardown, and safety standby. Minor gaps like plan-file inspection/editing or pause/resume commands are workaroundable, but no severe dead ends exist.
Maintenance
Related MCP Connectors
Nifty's MCP server β exposes tasks, projects, messages, and files as tools for AI agents.
- ZapierOAuthcom.zapier
Hosted MCP server connecting AI assistants to 9,000+ apps and 40,000+ actions via Zapier.
- UnifAPIOAuthcom.unifapi
Hosted MCP server for live public-data APIs and Skills for AI agents.
- FensoryOAuthcom.fensory
Trading MCP server for AI agents, with live market data, account reads and controlled execution.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceA protocol server enabling AI agents to control astrophotography equipment through the N.I.N.A. (Nighttime Imaging 'N' Astronomy) software, allowing for natural language command processing of cameras, mounts, focusers, and other astronomy equipment.13MIT
- AlicenseBqualityCmaintenanceAn MCP server that enables LLMs to query data from various NASA APIs, allowing access to astronomical data, space weather information, Earth imagery, and exoplanet information directly from compatible AI clients.216MIT
- AlicenseBqualityDmaintenanceAn MCP server for controlling astronomy equipment, supporting Dark Dragons Astronomy devices and any ASCOM Alpaca-compatible equipment.4310 npmISC
- FlicenseNot gradedqualityDmaintenanceMCP server that wraps three NASA public APIs (Astronomy Picture of the Day, Mars rover photos, and near-Earth objects) as tools for AI assistants.-