Migrate the MCP server extension to the shared esp-pylib library:
- replace raw stderr prints with esp_pylib.logger.log (note/err)
- route informational logs to stderr via set_info_stream so stdout
stays reserved for the MCP JSON-RPC transport
- enumerate connected devices via esp_pylib.serial_ports.get_port_names
- stub the new imports in the mcp_ext unit tests
Co-authored-by: Cursor <cursoragent@cursor.com>
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>
change: the light-sleep test so an internal-RAM helper task enters light sleep while the PSRAM-stacked Unity task is blocked.Verifies that light sleep succeeds and that the PSRAM-stacked task resumes correctly afterward.Removes the unreliable sleep-callback counter check.
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.
A ULP program is built against ulp_riscv, lp_core or ulp_fsm, each stating its
own sources, dependencies and memory layout. Sources shared with the driver
stay in components/ulp.
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.