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>
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.
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.
idf.py mcp-server printed startup, shutdown, and error messages on
stdout. On the stdio MCP transport, stdout is the JSON-RPC channel, so
those non-JSON lines can confuse or break strict clients. Route them to
stderr like the other diagnostics in mcp_ext.py.
Co-authored-by: Cursor <cursoragent@cursor.com>
idf.py mcp-server tool handlers were spawning idf.py without redirecting
stdin, so the child inherited the long-lived MCP JSON-RPC transport and
could hang indefinitely on tools/call. Pass stdin=subprocess.DEVNULL on
every spawn of idf.py.
Closes https://github.com/espressif/esp-idf/issues/18961
FastMCP was renamed to MCPServer in mcp 2.0, which made idf.py mcp-server
report a false "MCP dependencies not available" error. Prefer MCPServer
and keep a FastMCP fallback for mcp 1.x.
Closes https://github.com/espressif/esp-idf/issues/18904
Co-authored-by: Cursor <cursoragent@cursor.com>
This file predates the adoption of ruff-format and was never reformatted,
because pre-commit only runs on the files touched by a change. GitHub PR
#18871 bumps the copyright year here, which pulls the file into the hook's
scope and makes check_pre_commit fail on a pre-existing formatting
violation unrelated to that change.
Signed-off-by: Frantisek Hrbata <frantisek.hrbata@espressif.com>
Add a global -j/--jobs option to idf.py that sets the number of parallel
build jobs passed to the underlying build tool. Previously parallelism was
only configurable via the IDF_PY_BUILD_JOBS environment variable, and only
for the Ninja generator.
The option applies to both Ninja and Make and defaults to IDF_PY_BUILD_JOBS
when not given, so the environment variable keeps working as before.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Allow specifying extra compiler flags (warning flags, preprocessor
definitions, etc.) via a new 'compile_options' entry in the
idf-build-file frontmatter. The options are applied to the source
file via target_compile_options in the generated main component
CMakeLists, with CMake quoting so that flags like -DMSG="hello world"
reach the compiler exactly as written.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a new idf.py extension that allows building standalone C files
without requiring full project boilerplate. Source files can include
optional YAML frontmatter in block comments to specify sdkconfig
options, component dependencies, and target configuration.
When the frontmatter configuration changes, the stale sdkconfig is
removed so the new defaults are applied, and a target change also
clears the build directory since IDF_TARGET is pinned in the CMake
cache.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Migrate idf.py and its actions to the shared esp-pylib library:
- add esp-pylib (and esp-pylib[ide]) to the core/ide requirements
- base FatalError on esp_pylib.errors.FatalError, keeping the idf.py ctx cleanup
- replace the local raw-ANSI helpers (red_print/yellow_print/print_warning/
color_print) with esp_pylib.logger.log (warn/err/note/hint) across the
idf.py actions
- install esp_pylib exception reporting at the idf.py entry point and silence
the logger during shell completion
- update affected tests for the new prefixes/line wrapping
- add normalize_output() helper and unify terminal env in conftest to handle
Rich line-wrapping in CI assertions
Co-authored-by: Cursor <cursoragent@cursor.com>
The esp-idf-configdep utility works as a compiler wrapper. It lets the
compiler compile the app and checks whether -MF option was used to
generate dependency files. If yes, it checks for sdkconfig.h dependency
in those files.
Target file depending on sdkconfig.h is then scanned for all CONFIG_*
occurrences and the dependency file is altered so that the given target
file is marked as "dirty" only if the config options it actually uses
are changed.
This ensures that after a configuration update, only affected files are
rebuilt, reducing incremental build times.
The compiler launcher mechanism is changed from RULE_LAUNCH_COMPILE
(Makefile-only) to CMAKE_*_COMPILER_LAUNCHER (generator-agnostic),
enabling launcher chaining (configdep -> ccache -> compiler).
Made-with: Cursor
Add USB DFU and OTG console SOC capability flags for esp32s2, esp32s3, and esp32p4.
Use these caps in Kconfig, documentation conditionals, and idf.py DFU actions so USB support
is derived from SOC capabilities instead of hardcoded target names.
Calling idf.py size on linux target project would cause an
error. This was a problem for IDEs or build script which
often call this as the last step in the build process,
as it could mark the whole "build" as failed.