agentic-hil
Agentic HIL is an MCP server that gives a coding agent gated, auditable control of real embedded hardware — flashing, debugging, serial and CAN I/O, bench runs, and YAML test plans — always behind the operator's permissions.
Discover the bench: check the debugger backend (
debugger_info), list attached probes (debugger_probes_list), and confirm a board answers without reset or flash (probe_target).Flash firmware: upload images to get an
artifact_id(artifact_upload), then write them withflash_firmware, optionally resetting and capturing boot output through a COM port;reset_targetrestarts the board in run, halt, or init mode.Debug the target: open a GDB session on an ELF (
debug_start_session), inspect or stop it (debug_get_session_status,debug_stop_session), set/list/clear breakpoints, resume and halt the core (debug_continue,debug_halt,debug_get_stop_reason).Inspect firmware symbols: resolve an allowed symbol's address (
debug_symbol_info), read its value from memory (debug_symbol_value), or dump it to Intel HEX (debug_dump_symbol_ihex).Work over serial: list configured ports (
com_ports_list), then open a session, write text or hex, read with literaluntilmatching, and close it (com_session_start,com_write,com_read,com_session_stop).Work over CAN: list buses (
can_buses_list), open/close sessions (including shared buses viaparticipant), send frames including CAN FD (can_send), and read frames or wait for specific ids (can_read).Hold the bench for a sequence: declare devices for a multi-step run (
bench_run_start), check it (bench_run_status), and release them (bench_run_stop).Run and follow test plans: execute a YAML plan on hardware (
test_reactor_run), poll it (test_reactor_status), and stop it (test_reactor_stop).Diagnose results: fetch the last structured report (
get_last_report) or explain the last failure with causes and evidence paths (classify_last_error).Handle safety state: inspect leases and incidents (
hardware_lease_status) and clear a quarantine, with the operator's verbatim statement when physical verification is required (hardware_recover).Manage configuration: generate a config from attached hardware (
project_config_create), inspect writable and locked keys (project_config_describe), change keys — narrowing only, never widening permissions (project_config_set), adopt probe/port identities (project_config_adopt_hardware), and reload the bench description (project_config_reload_description).Maintain the installation: upgrade to the latest release with the original package manager (
server_upgrade).
Provides tools for flashing and debugging STM32 microcontrollers via STM32CubeProgrammer, enabling hardware-in-the-loop firmware development.
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., "@agentic-hil flash the current build to the dev board and run the regression tests"
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.
Agentic HIL
Connect your AI to reality.
Let your agent develop, test, and improve firmware autonomously. In the real world. Agentic HIL makes this possible as a hard gate: it enforces strict safety boundaries and verifies the results on real hardware.
https://github.com/user-attachments/assets/8d39ba93-beeb-484e-b9e9-d9ce79538523
Watch the agent set up a fresh firmware project, write the firmware, and make the board say Hello World after installation and one restart. The recording shows real hardware feedback through the gate. The agent saves a test plan and runs it with both a correct and a deliberately wrong expectation. You get the plan, a report, and evidence that the test can pass and fail. The run ends with the lease released, the safe state confirmed, and nothing quarantined.
Get started
Install Agentic HIL for your user with one command. No admin rights required.
Linux / macOS (any shell):
curl -LsSf https://agentic-hil.github.io/install.sh | shWindows, in PowerShell:
irm https://agentic-hil.github.io/install.ps1 | iexThe installer registers the skill and MCP server for every supported agent CLI it finds on your PATH. It keeps the installation outside your repositories. Restart your agent once, then ask your first hardware question. Your agent sets up the project's bench through MCP.
If no claude, codex, or opencode CLI is found, the installer prints the next step. Install your agent CLI, then run agentic-hil agent-install --agent <claude-code|codex|opencode> to register it.
The installer asks the package manager where its user bin directory is and adds that directory to your shell startup files or Windows user Path when needed. Open a new shell to use the command. Pass --no-path (-NoPath in PowerShell) to manage that change yourself; the installer prints the line to add.
A fresh installation gets agentic-hil[can] from PyPI. Use --version <x.y.z> to pin a release. Rerunning the installer repairs an existing installation: editable checkouts reporting a .devN version are kept, and uv tools installed from a path, URL, or git reference are refreshed from that recorded source. To inspect and verify the installer before running it, follow the checksummed route in installation: download the script and its checksum from the same release, check the file, then run that file.
For a guided first run, try the STM32 starter. It has the firmware, test plans, and a planted defect ready for a three-step walkthrough on a Nucleo-F446RE.
Claude Code can also install the skill from the plugin marketplace:
/plugin marketplace add agentic-hil/agentic-hil, then /plugin install agentic-hil@agentic-hil.
The install command above still provides and registers the MCP server, with a verified absolute executable path outside your repository.
See installation for cmd.exe, registering one agent, using your own package manager, setting up an attached bench, optional components, and upgrades. The same installer command can repair an installation when agentic-hil upgrade fails. Troubleshooting covers startup problems.
In a clone of this repository, try a first check without a board:
agentic-hil check-plan examples/nucleo-f446re_demo/testconfig.yamlIt answers All 1 test plan(s) load through the reactor's loader. and exits 0, without loading a bench configuration or touching hardware.
Related MCP server: Chiplab
Let your agent close the loop
You set the goal. Your agent writes and builds the firmware. Agentic HIL puts it on the board, exercises the device, and checks its responses against the test criteria. When a test fails, the agent gets concrete results it can use to diagnose the problem, change the code, and try again.
Agentic HIL supplies the hardware execution and feedback that make this development loop possible. Much of the flashing, testing, and checking you would do at the bench becomes part of the agent's workflow. You review the code alongside a record of what ran, what passed, and what failed, and focus your attention on requirements, design decisions, and test coverage.
Agentic Hardware-in-the-Loop (Agentic HIL) connects coding agents to the bench through MCP tools and CI suites through a pytest plugin. Setup is available for Claude Code, Codex, and OpenCode.
Each hardware action returns a structured result with its outcome, diagnostics, and report and log paths. Your agent gets feedback it can act on, and you can trace the result back to the recorded run.
Bring your bench into the loop
Agentic HIL connects to your probe through its backend: ST-Link through OpenOCD or the STM32CubeProgrammer CLI, and CMSIS-DAP probes through pyOCD. It also flashes and resets ESP32 targets through esptool and their USB-UART bridge, without opening a debug session. Hardware support follows the probe and backend.
Send commands and read responses over serial ports and CAN buses using PCAN, SocketCAN, or a custom bridge. Several runs can share a CAN bus through the broker.
Agentic HIL runs on Linux, macOS, and Windows with Python 3.10 or newer, with CI coverage across all three platforms. The Nucleo-F446RE example takes you through a complete run on the board used to demonstrate the loop. See installation for backend and platform details.
Put the firmware to the test
Write down what the device should do in a reviewable YAML test plan. The test reactor validates the plan before touching hardware, then flashes, resets, and exercises the device in the order you specify. Checks compare actual responses with exact values, patterns, or numeric ranges. A check passes when the device meets that expectation.
Plans name logical devices, and the bench configuration connects them to real hardware. Use the same plan at your desk and in CI. A failed step stops the run; sessions are cleaned up, and configured recovery actions and their outcomes are recorded with the test results. Write your first hardware test.
Set boundaries that hold
You decide which devices and actions your agent may use. Agentic HIL rejects undeclared devices before opening a driver. It enforces permissions, validates requests, applies timeouts, and records what happened. The CLI, MCP tools, pytest plugin, and test reactor all pass through the same gate.
The authoritative configuration lives outside the workspace, beyond the agent's own file tools. Review the permissions it grants. Use agentic-hil revoke <key> or agentic-hil grant <key> from your own shell to change individual grants. Over MCP, an agent can only narrow its authority. Flashing is refused while either allow_raw_debugger_commands or allow_mass_erase is enabled.
Device leases protect runs from interference across the machine, and a SHA-256 audit chain makes the hardware record tamper-evident. A failed run keeps its failed verdict while configured recovery works to restore the bench and records the outcome. A broken audit trail holds the bench until operator recovery, because another hardware test cannot rebuild missing evidence.
Read the safety model for the overview and the security design for the full reasoning.
Try it on a real board
Start with the Nucleo-F446RE demo. Connect the board, build the firmware, and set up Agentic HIL from the example's directory:
cd examples/nucleo-f446re_demo
cmake --preset Debug && cmake --build --preset Debug # → build/Debug/nucleo-f446re_demo.elf
agentic-hil setup --agent claude-code # or: codex / opencode
agentic-hil doctordoctor checks the configuration against the attached bench and reports missing tools or hardware before you flash. If you connected the board after setup, agentic-hil adopt-hardware fills in the probe identity, backend executable, and COM device left unset. Use --dry-run to see the proposed changes first. On a host with OpenOCD and no STM32CubeProgrammer, the host's USB inventory can identify an attached ST-Link. Use --probe-id <serial> to select one when several probes are attached.
Run the example's hardware test plan:
agentic-hil test-reactor --test-config testconfig.yamlThe plan flashes the firmware, opens the serial port, resets the board, and checks the boot output for Hello World. A missing or wrong boot banner fails the test and returns the device output for diagnosis. The run closes the sessions it opened, including when a step fails.
Your agent can drive the same steps through MCP tools. Run pytest tests/ from the example directory to put the boot check in a regression suite. The example guide covers all three paths, and hardware testing shows how to write plans for your own device.
Go deeper
Your next step | Read |
Install, upgrade, or add CAN, pyOCD, or esptool support | |
Define the bench and its permissions | |
Drive hardware through MCP | |
Connect a specific MCP host | |
Write hardware tests with YAML or pytest | |
Understand how safety boundaries and recovery work | |
Find platform and response-time commitments | |
Diagnose a failed run | |
Set up your agent for this project |
Use agentic-hil for the package installation, CLI command, repository URL, and MCP server name. Python imports, pytest plugin names, fixtures, and Python examples use agentic_hil.
Development
To work on Agentic HIL itself:
python -m pip install -e '.[dev]'
ruff check src tests evals tools
pytest
python -m build
twine check dist/*Releases publish to PyPI through GitHub trusted publishing in .github/workflows/workflow.yml. See CONTRIBUTING.md to contribute.
Security
Policy bypasses are treated as vulnerabilities; see SECURITY.md.
Support
Linux, macOS, and Windows have equal support. Hardware support follows the probe and backend: ST-Link through OpenOCD or STM32CubeProgrammer, CMSIS-DAP through pyOCD, and ESP32 USB-UART bridges through esptool.
Issues receive a response within 24 hours on workdays, and security reports within seven days. See the support policy for the full scope and commitments.
Ask questions in Discussions Q&A, share a run in Show and tell, or submit your first run report, whether it passed or failed.
License
Apache-2.0. See LICENSE.
Available Tools
44 toolsartifact_uploadAIdempotent
Store a firmware image in artifacts.upload_directory and return its artifact_id for flash_firmware or debug_start_session; no board needed. Give image_path, or filename with data_base64, else invalid_argument. Needs allow_upload, else permission_denied. The id is the sha256 of the bytes plus the lowercased extension: same bytes and extension, same id. Unsafe destination: unsafe_configured_path.
Inputs: filename, image_path, data_base64.
bench_run_startA
Declare a run holding the devices a sequence like flash, reset, read needs until bench_run_stop or server exit, no timeout; without one the board is free between calls. All or nothing: a refused start holds none. Other devices fail undeclared_device; a second start, run_already_active; a standing incident, resource_quarantined. A single call, or flash_firmware with capture, needs no run.
Inputs: devices (required), label, wait_s.
bench_run_statusARead-only
Report whether this server has a run open (run_active), what it declared (declared_devices, run_label) and which devices it holds (held_devices); read it when unsure whether you still hold the bench. In memory only: what another process holds shows in hardware_lease_status.
Inputs: no named parameters.
bench_run_stopAIdempotent
End the bench_run_start run and release its devices (released_devices). With no run open it answers run_was_active: false. A COM, CAN or debug session still open keeps its own device: open_leases, still_held_devices. With an incident open it runs the recovery recovery.auto_recover allows (by default reset into halt, then a probe re-read), reported in recovery.
Inputs: no named parameters.
can_buses_listARead-only
List every can_buses entry in buses, keyed by bus_id, the name can_session_start, can_send and can_read take. Each shows session_active, listen_only with its listen_only_enforcement, fd, max_frame_data_bytes and max_buffer_frames; a bus with shares adds active_participants. Needs no session and opens no adapter. Use this instead of ip link or candump.
Inputs: no named parameters.
can_readARead-only
Read the CAN frames your can_session_start session received into frames, counted by frames_read. Use it instead of candump; to wait for a reply, pass until_id rather than polling. Without a session: session_not_active. Returned frames are consumed; an empty read is still ok. Adapter faults: can_read_failed; after can_adapter_invalid_response, can_session_stop and start again.
Inputs: bus_id (required), until_id, max_frames, participant, +1 more.
can_sendADestructive
Send one CAN frame (CAN FD on an fd: true bus) on the can_session_start session, else session_not_active. Needs the bus allow_write, else permission_denied; a listen_only bus refuses with can_listen_only_mode. A repeat sends again. ok: the adapter accepted it, not that a node ACKed it. can_send_failed sets cleanup_required: it may have gone out. Use this instead of cansend.
Inputs: bus_id (required), frame_id (required), rtr, data_hex, +2 more.
can_session_startADestructive
Open a session on a configured CAN bus for can_send and can_read, until can_session_stop or server exit. Returns session; a repeat call answers already_active: true. Fails can_bus_not_configured, device_busy or resource_busy (bus held), can_adapter_timeout (bridge timeout_s, default 10 s), and on a listen_only bus can_listen_only_unsupported or can_listen_only_unconfirmed.
Inputs: bus_id (required), participant, clear_rx_queue.
can_session_stopAIdempotent
Close the can_session_start session on bus_id when done with the bus; its lease is released. Its adapter closes unless other participants share the bus. A declared run still holds the bus. can_send and can_read then answer session_not_active. With no session open, was_active is false. If the close fails or a bridge is silent for 1 s, it returns can_adapter_close_failed and keeps the session.
Inputs: bus_id (required), participant.
classify_last_errorARead-only
Explain the last hardware failure: error_type names it, likely_causes says what to check, report_path and log_path hold the evidence. Unlike get_last_report, it keeps that failure through later successes. source_tool is the failed tool, or reset_target if a flash's recovery reset failed too. Some refusals, such as allow_flash off, record nothing. Needs no board; no failure yet: report_not_found.
Inputs: no named parameters.
com_ports_listARead-only
List com_ports entries in ports by port_id, which com_session_start takes (not a device path), with session_active. Opens no port. available_com_ports holds host serial ports, or permission_denied unless a configured port may be read, serial_backend_not_available without pyserial, com_port_discovery_failed on an OS error; the call stays ok. Use this instead of picocom or /dev/tty*.
Inputs: no named parameters.
com_readARead-only
Read what the board sent on a COM port session from com_session_start (else session_not_active), instead of screen, minicom or picocom. With until, one call waits for a pattern instead of polling. Each byte is returned once, as text and hex. If until is not seen in time, the answer is still ok, with until_matched false and the bytes so far.
Inputs: port_id (required), until, max_bytes, wait_timeout_s.
com_session_startADestructive
Open the serial port a com_ports entry names and hold it, with its machine-wide lock, until com_session_stop; a reader buffers what the board sends for com_read. DTR and RTS are driven as configured on open, which can reset a board. device_busy, resource_busy or com_port_busy: another server, process or program has the port. permission_denied: neither reading nor writing is allowed.
Inputs: port_id (required), clear_buffer.
com_session_stopAIdempotent
Close the com_session_start session on port_id: stop its reader, close the port, release its lock. Unread bytes are dropped; the session log at log_path keeps them. Without a session it returns ok with was_active false. If the reader does not end within max(1 s, timeout_s + 0.5 s) of the close, it returns com_port_close_failed and keeps the session registered; call again to retry.
Inputs: port_id (required).
com_writeADestructive
Send bytes over the session com_session_start opened on port_id (else session_not_active); needs the port's allow_write (else permission_denied). At most max_write_bytes encoded bytes, 4096 by default. bytes_written counts the bytes sent; a short write returns serial_write_incomplete. Example: com_session_start port_id "uart0", com_write text "hello\r\n", com_read until "\n", com_session_stop.
Inputs: port_id (required), hex, text.
debug_clear_breakpointsAIdempotent
Removes every breakpoint GDB holds in the debug_start_session session (else session_not_active), then checks none is left. It neither resumes nor halts the core. It answers cleared (the count) and backend_reconciled true; a second call clears 0. Unconfirmed: ok false, breakpoint_reconciliation_failed; a retry is safe. OpenOCD only, exactly one debugger configured; else not_supported.
Inputs: no named parameters.
debug_continueA
Resumes the core in the debug_start_session session until it stops. Needs allow_debug_execution, else permission_denied. Returns stop_reason, stop (frame): breakpoint_hit is ok; unexpected_breakpoint and target_exception are not. An exception or debugger_error stop is not resumed. No session: session_not_active. OpenOCD only, exactly one debugger configured; else not_supported.
Inputs: timeout_s.
debug_dump_symbol_ihexADestructive
Read an allowed symbol from target memory and write it as Intel HEX to output_path; returns address, size_bytes and output. Value inline: debug_symbol_value. OpenOCD needs debug_start_session (else session_not_active); pyOCD and STM32CubeProgrammer, the ELF last flashed via flash_firmware (else symbol_source_not_available). Over debug.max_dump_size_bytes: permission_denied.
Inputs: symbol (required), output_path (required).
debugger_infoARead-only
Check the debugger backend is installed: runs only --version, at most 10 s; contacts no probe or board. Replaces openocd. Returns backend, executable, version, config_status. Next: debugger_probes_list (probe ids), probe_target (board). Failures: debugger_not_found (missing), timeout; not_supported unless exactly one debugger is configured; permission_denied without allow_probe (config version 1).
Inputs: no named parameters.
debugger_probes_listARead-only
List the probe ids of the in-circuit debuggers or programmers on this host; connects to no board. Replaces st-info. Returns probes, each with a probe_id to set as the debugger entry's probe_id. pyOCD and STM32CubeProgrammer ask their CLI; OpenOCD reads the USB serial inventory, complete false (adapters it cannot read there: not_supported). Failure: probe_discovery_failed.
Inputs: no named parameters.
debug_get_session_statusARead-only
Reports if the debug_start_session session is open and its status: halted, running, error or cleanup_required. Records new stops, but never halts or resumes the core; debug_halt halts, debug_get_stop_reason reads the last stop. Abnormal stop: ok true, target_ok false, target_error_type. No session: ok, active false, status stopped. OpenOCD only, exactly one debugger configured; else not_supported.
Inputs: no named parameters.
debug_get_stop_reasonARead-only
Why the core last stopped: stop_reason (breakpoint_hit, halted, exception), stop (frame). Records a newly arrived stop; never halts or resumes the core. Abnormal stop: ok true, target_ok false, target_error_type. None yet: stop_reason_not_available. Without debug_start_session: session_not_active. Session: debug_get_session_status. OpenOCD only, exactly one debugger configured; else not_supported.
Inputs: no named parameters.
debug_haltA
Halts the core; the session stays open. It needs debug_start_session (else session_not_active) on OpenOCD with exactly one debugger configured, else not_supported. debug_continue resumes. Returns stop_reason halted or a stopped core's recorded stop. If an acknowledged interrupt's stop times out, ok and halt_confirmed are false, target_state unknown, bench quarantined; a confirmed halt can lift it.
Inputs: timeout_s.
debug_list_breakpointsARead-only
Lists the breakpoints debug_set_breakpoint added in the debug_start_session session, from the session's own record without asking GDB: breakpoints (id, backend_id, location). Never halts or resumes the core. No session: ok, active false, breakpoints empty. debug_clear_breakpoints removes them and checks GDB. OpenOCD only, exactly one debugger configured; else not_supported.
Inputs: no named parameters.
debug_set_breakpointA
Adds a breakpoint in the debug_start_session session (else session_not_active) without resuming the core. Each call adds one and returns its breakpoint id; debug_list_breakpoints lists them. A location without a grant answers permission_denied. An unconfirmed insert answers cleanup_required; call debug_clear_breakpoints. OpenOCD only, exactly one debugger configured; else not_supported.
Inputs: location (required).
debug_start_sessionADestructive
Opens a GDB session on an ELF for debug_continue and debug_halt. OpenOCD backend only; others answer not_supported. Needs GDB (debug.gdb_executable or PATH), the debugger entry's scripts and allow_raw_debugger_commands off. Returns the core halted: session (status, stop_reason), log_path. A second start: session_already_active. debug_get_session_status inspects it; debug_stop_session ends it.
Inputs: mode, timeout_s, image_path, artifact_id.
debug_stop_sessionAIdempotent
Ends the debug_start_session session; debug_halt keeps it open. Confirms the core halted, disables resume on detach, ends GDB and the debug server, drops breakpoints, frees flash_firmware and reset_target. OpenOCD backend only; others answer not_supported. Success: safe_state_confirmed true. No session: ok, active false. Unproven: ok false, cleanup_required, hardware_state unknown, quarantined.
Inputs: timeout_s.
debug_symbol_infoARead-only
Find an allowed symbol's hex address, size_bytes and resolved_from in the ELF. Reads no target memory; for bytes use debug_symbol_value, for Intel HEX debug_dump_symbol_ihex. OpenOCD needs debug_start_session (else session_not_active). pyOCD and STM32CubeProgrammer use the ELF this server last flashed via flash_firmware (else symbol_source_not_available; symbol_source_changed if changed since).
Inputs: symbol (required).
debug_symbol_valueARead-only
Reads a symbol from target memory, like gdb print. debug_symbol_info gives its address, debug_dump_symbol_ihex a file. Returns hex, plus value_unsigned and value_signed at 1, 2, 4 or 8 bytes. OpenOCD needs debug_start_session (else session_not_active); pyOCD and STM32CubeProgrammer, the ELF flash_firmware last wrote (else symbol_source_not_available). Over max_dump_size_bytes: permission_denied.
Inputs: symbol (required).
flash_firmwareADestructive
Write firmware to the board via the in-circuit debugger or programmer, not st-flash; every call writes it again. Needs allow_flash, else permission_denied. resource_busy: debug_stop_session first; device_busy: held elsewhere. The debugger entry's timeout_s (seconds, 60 unless set) caps each debugger command, else timeout. No bench_run_start needed; capture with reset_after_flash adds boot output.
Inputs: capture, image_path, artifact_id, reset_after_flash.
get_last_reportARead-only
Return the newest stored hardware report, pass or fail, under report. For the last failure diagnosed, even after successes, use classify_last_error. Needs no board; reading changes nothing. After a failure it can be the recovery's reset_target or probe_target (report.tool). ok stays true for a failed report: judge report.ok, report.audit_ok, report.cleanup_required. None yet: report_not_found.
Inputs: no named parameters.
hardware_lease_statusARead-only
Read who holds this bench, from any process (bench_held, held_devices), and any open incident: blocked, cleanup_reasons, auto_recoverable, quarantine_guidance, next_step. Only incident_stands: true needs hardware_recover; otherwise the next hardware call settles it or stands it down. standing_incidents are other projects' incidents, not yours to clear. Drives no device.
Inputs: no named parameters.
hardware_recoverAIdempotent
Clear the standing quarantine that blocks hardware calls. Call it when hardware_lease_status shows incident_stands, rather than deleting state files. It needs allow_recover and touches no hardware. A no-contact reason clears with no argument. Any other reason answers recovery_requires_physical_check: ask the operator, pass their answer verbatim as operator_statement, never invent one.
Inputs: operator_statement, accept_config_change.
probe_targetAIdempotent
Connect the in-circuit debugger or programmer to the board and confirm it answers (target_detected true), with no reset and no flash; reset_target resets. Replaces openocd. Refused with resource_busy while a debug session is open. Failures: adapter_not_found (no probe), target_not_detected (board silent), debugger_not_found (backend missing), timeout (one command over debugger.timeout_s).
Inputs: no named parameters.
project_config_adopt_hardwareAIdempotent
Fill the configuration keys that still hold placeholders from the attached probe. Call it once a board is attached, so nobody retypes its serial. It may also rename a COM device to its stable name and correct a stale identity_source. It writes only when apply is true, never flashes or erases. A read that leaves the board state unknown may get it reset into halt by recovery (resource_quarantined).
Inputs: apply, probe_id, com_port_id, debugger_id.
project_config_createADestructive
Generate this workspace's configuration from attached hardware instead of writing one by hand: every permission true except allow_raw_debugger_commands and allow_mass_erase, which it writes false. Never ask for those two to be turned on. Regenerating is the operator's call, not a way to narrow or re-open.
Inputs: no named parameters.
project_config_describeARead-only
List the keys project_config_set may change: writable_keys with current_value and value_schema, locked_keys naming in unlocked_by the permission that opens each. Read it before project_config_set instead of guessing; it needs no permission. writes_blocked_by_open_run is true while this server holds a run or session. Bad YAML answers config_invalid, an unreadable file config_unreadable.
Inputs: no named parameters.
project_config_reload_descriptionARead-only
Re-read target, debuggers, com_ports and can_buses from this workspace's configuration into the running server, never permissions or debug. Call it after project_config_set, project_config_adopt_hardware or a hand edit, rather than a restart. New devices get every permission false; changed grants need a restart (restart_required_for). description_changes lists what moved.
Inputs: no named parameters.
project_config_setADestructive
Write values you choose to named keys of the configuration file. Call it after project_config_describe lists the keys, rather than editing the configuration file. Device keys need allow_config_description_write, permission keys allow_config_permissions_write, else permission_denied. Permissions only narrow: closing allow_config_permissions_write freezes them all at once (permissions_frozen).
Inputs: changes (required).
reset_targetA
Reset the board through the in-circuit debugger or programmer: the firmware restarts, flash is kept. Replaces st-util. Needs allow_reset (else permission_denied). Refused with resource_busy while a debug session is open: debug_stop_session first. To check the board without a reset use probe_target. Failures: reset_failed (reset not confirmed), timeout (one command over debugger.timeout_s).
Inputs: mode.
server_upgradeAIdempotent
Upgrade this installation to the newest release with the uv, pipx or pip that installed it. Use it when the operator asks; it takes no arguments and needs allow_upgrade. Each manager run may take 600 s. running_version stays old until this server restarts. On Windows, ask the operator to run agentic-hil upgrade (upgrade_cli_only_on_host). On failure the operator runs any reinstall_command.
Inputs: no named parameters.
test_reactor_runADestructive
Run a test plan file (agentic-hil://reference/test-plan) on the board. For calls made one by one, use bench_run_start, whose open run refuses this with run_already_active. permission_denied and device_busy refuse at once, before any step. A failing step gets the recovery recovery.auto_recover allows, reported in recovery. With detach, poll test_reactor_status; test_reactor_stop ends it.
Inputs: detach, test_config_path.
test_reactor_statusARead-only
Follow a detached test_reactor_run by its handle; for a bench_run_start run, use bench_run_status. state goes starting, running, then finished or stopped, or worker_gone if its process died; stop_requested_at dates a test_reactor_stop. Once ended, run_ok is the verdict and canonical_report_path its own report; report_path is a mirror the next run overwrites. Fails run_state_invalid.
Inputs: run.
test_reactor_stopAIdempotent
Ask a test plan run to stop by writing a request it reads between steps. It finishes the step it is in (a delay or device wait ends early), closes its devices and writes its report; one still starting ends before any step. Answers at once stop_requested: true, also on a repeat; an ended run answers stop_requested: false. Fails run_state_invalid, or run_worker_gone (its process died).
Inputs: run (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 44 tools
The set has several superficially similar status/report tools (debug_get_session_status, debug_get_stop_reason, get_last_report, classify_last_error, bench_run_status, test_reactor_status, hardware_lease_status), but descriptions cross-reference each other explicitly and assign distinct purposes, so an agent can tell them apart. The run-vs-run distinction (bench_run_start vs test_reactor_run) is also carefully delineated. Only minor boundary friction remains among the many 'status' tools.
Names are uniformly snake_case with a clear verb_noun pattern and domain prefixes (debug_*, com_*, can_*, bench_run_*, project_config_*). A few names lack a domain prefix (get_last_report, classify_last_error, artifact_upload, server_upgrade) and project_config_reload_description is an awkward phrasing, but overall the convention is predictable.
44 tools is well beyond the 25+ heavy threshold. The domain is genuinely broad (debug, flash, serial, CAN, config, recovery, bench orchestration), but the surface is bloated with many fine-grained session/status/breakpoint tools that could be consolidated. This is a lot of surface for an agent to navigate reliably.
Coverage is remarkably thorough: each subsystem has full lifecycle tools (session start/stop, breakpoint set/list/clear, read/write), plus flash, reset, symbol inspection, bench-run orchestration, config describe/set/adopt/create, error classification, and quarantine recovery. No obvious dead ends for the stated hardware-in-the-loop purpose.
Maintenance
Related MCP Connectors
MCP server for building and testing AI agents with multi-model experimentation and insights.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Related MCP Servers
- AlicenseAqualityDmaintenanceStateful MCP server for driving debug probes (J-Link) to flash, debug, and inspect embedded targets. Enables AI agents to perform flash, memory, breakpoint, and ELF/SVD-aware operations conversationally.4149 PyPI10MIT
- AlicenseNot gradedqualityBmaintenanceMCP server for simulating firmware on virtual microcontroller instances, allowing AI agents to upload, run, and read UART output from supported boards such as STM32 and Nordic.18MIT
- AlicenseCqualityCmaintenanceMCP server for AI-assisted MCU and embedded firmware debugging. It connects to real hardware via debug probes, inspects CPU/memory/peripherals, manages Keil builds, and provides structured evidence for fault diagnosis.1936 PyPI9MIT
- AlicenseNot gradedqualityDmaintenanceAn MCP server that lets AI coding agents drive the full STM32 development loop—code generation, build, flash, debug, serial monitoring, and fault diagnosis—end to end via CubeIDE, CubeMX, CubeProgrammer, OpenOCD, and GDB.1MIT