Add a `monitor_device` MCP tool that lets an AI agent run a scripted,
non-interactive `esp-idf-monitor` session against a flashed device and
get back a short status plus a log file path, instead of raw serial
output inline.
Under the hood:
- The agent supplies a plain-text command body (expect/send/sleep/reset/
exit/comments). `assemble_monitor_script_from_agent_commands()` frames
it into a script the monitor's non-interactive command mode can
consume via stdin: it appends `exit` if the agent didn't already end
with one, and rewrites every bare `expect <regex>` into `expect
--timeout <timeout_sec> <regex>` via `_monitor_normalize_expect_line()`
(an already-bounded `expect --timeout ...` line is left untouched so
the monitor itself reports a bad value). A leading `reset` is not
prepended - the monitor already resets the chip when it opens the
port - and any `reset` the agent wrote is left in place.
`_monitor_parse_sleep_duration()` extracts each `sleep <n>` duration.
The effective timeout is the sum of every bounded expect duration
plus every sleep duration. Scripts whose sum exceeds
`MONITOR_MAX_SCRIPT_SEC` are rejected. If the script has neither
expect nor sleep (for example only `send`), `timeout_sec` is used so
the process still has a kill bound.
- `monitor_device()` runs `python -m esp_idf_monitor` via
`subprocess.run(..., input=script, timeout=2 * effective_timeout)`.
`no_reset` is forwarded as `--no-reset` so the connection reset can be
skipped; an explicit `-p` is forwarded when a port is given. Extra
arguments match `idf.py monitor` where a build exists: baud (`baud`
tool arg, else `monitor_baud` from `project_description.json`),
toolchain prefix, `--target`/`--revision`, coredump/panic decode, and
ELF files with the app ELF first. The 2x hard timeout is a safety net
independent of the script's own `expect --timeout`/`exit` logic; on
`TimeoutExpired` the process is killed but any output already captured
is preserved and logged. `decode_stream()` normalizes that captured
output, which can be `bytes` on the timeout path even though the
process otherwise runs in text mode.
- The monitor's exit code drives the reported status via
`_monitor_status()`, using `EXIT_EXPECT_TIMEOUT` and
`EXIT_SCRIPT_ERROR` from `esp_idf_monitor.base.constants`: 0 is
success, 110 means an `expect` pattern never showed up before its
`--timeout` elapsed, 2 means the monitor rejected the script (bad
syntax/timeout/regex), anything else is reported generically.
- Serial output and the monitor's own messages share one pipe
(`stderr=STDOUT`) so decoded panic backtraces stay next to the lines
that triggered them. `_save_monitor_output()` writes the full merge to
`<tempdir>/esp_idf_mcp_log/action_monitor/monitor_<timestamp>.log` and
reports a dedicated `Log file:` line. On non-zero exit or process
kill, a short tail of that same merge is also returned inline so the
agent has some failure context without a second file read. If the log
file can't be written, it falls back to inlining a truncated tail.
Closes https://github.com/espressif/esp-idf/issues/18757
Closes https://github.com/espressif/esp-idf/pull/18385
Co-authored-by: Cursor <cursoragent@cursor.com>
The DRAM and RTC_FAST execute cases race the CPU exception against the PMS or
cache interrupt on ESP32-S3 too, so the panic reason is not deterministic.
__generate_gdbinit wrote into a single shared directory and derived the
application ELF from a global property, so a project building multiple
executables had their gdbinit files overwrite each other. Take the ELF
path and output directory as arguments, and derive them per executable in
idf_build_generate_metadata.
change(build): drop BUILD_COMPONENTS support and migrate test_apps to not use it for buildv2
Closes IDF-15859
See merge request espressif/esp-idf!51695
Auto-detect used to pick the first Espressif device, even when it did
not match IDF_TARGET. With several boards attached, flash/monitor could
talk to the wrong chip.
Pass the project target into esptool so unmatched ports are skipped.
Resolve the port after ensure_build_directory() so the target is known.
For monitor on an unconfigured project, probe the connected chip and
pass it directly to idf_monitor without configuring the project.
pytest.ini enables log_cli, so logging the full ninja stdout after a
failed first-time build is one multi-MB ERROR. That hung shard 3/6 for
hours after the hints test. Log the last 80 lines; keep the full output
on the exception and at DEBUG.
git worktree add materializes submodules as gitlink files. rmtree() cannot
remove those, and exists()+iterdir() on a source gitlink raises and falls
back to shutil.copytree, which leaves mbedtls/include as a file. Unlink
dest gitlinks and only copy populated source directories.
sdkconfig.ci.memprot_esp32s3 was built but listed in no CONFIGS_MEMPROT_*, so
the S3 PMS panic path was never exercised.
expect_gme() takes an optional core, for panics with no attributable core.
Reading the BUILD_COMPONENTS build property aborts the build with a bare
message stating that the property is unsupported, without naming a
replacement. Add a hint that points at the $<TARGET_EXISTS:idf::component>
generator expression and at the documentation section describing the
migration, so the guidance lives with the other build hints instead of
being spelled out in the build system sources.
The compatibility shim populated BUILD_COMPONENTS from the library
interface's linked-components list so that consumers reading it kept
working. Those consumers now query the library interface directly, so
drop the shim population and reject reads of the property
unconditionally.
The BUILD_COMPONENTS build property only exists when building through the
Build system v1 compatibility shim. Query the executable library
interface's linked-components list instead, so the app does not depend on
compatibility-only properties.
The BUILD_COMPONENTS build property only exists when building through the
Build system v1 compatibility shim. Query the executable library
interface's linked-components list instead, so the app does not depend on
compatibility-only properties.
The BUILD_COMPONENTS build property only exists when building through the
Build system v1 compatibility shim. Query the executable library
interface's linked-components list instead, so the app does not depend on
compatibility-only properties.
The bootloader programs only unlocked PMA entries (no PMP), so the layout is
not a bootloader<->application ABI; the application resets all entries, programs
them in ascending order from IRAM, and locks everything. Verified on ESP32-H4 v0.1.