Logs & Artifacts
Five tools deal with everything a build produces — log lines, output files, the firmware manifest, structured error messages, and the size report.
| Tool | What it does |
|---|---|
logs.tail | Get the last N log lines for a build. |
list_artifacts (alias artifacts.list) | List the files a build produced and group them by type. |
artifacts.manifest | Read the official manifest.json from a finished build. |
parse_build_errors | Turn raw compiler errors into something readable. |
parse_size_report | Turn idf.py size output into a flash/RAM breakdown. |
logs.tail
Returns the most recent N lines from a build’s log.
Input:
{
"task_id": "0abf...e2",
"lines": 200
}
| Field | Required | Notes |
|---|---|---|
task_id | Yes | The id returned by build. |
lines | No | How many trailing lines to return. Default 100. |
since_seq | No | Only return lines after this sequence number (from a previous call). |
Returns:
{
"task_id": "0abf...e2",
"lines": [
{ "seq": 4198, "ts": 1712340060, "stream": "stdout", "text": "[1234/1500] CC main.o" },
{ "seq": 4199, "ts": 1712340061, "stream": "stderr", "text": "warning: ..." }
],
"next_seq": 4200,
"more": false
}
more: true means the build is still producing log lines and you should
ask again.
Tip: For long-running builds, use the build://log/{task_id}
resource instead — it pushes new lines as they happen, instead of you
asking over and over.
list_artifacts / artifacts.list
Lists the files a build produced, grouped by type.
Input:
{
"task_id": "0abf...e2",
"target": "esp32s3"
}
You can pass a task_id (preferred — looks at the exact build that
ran) or just a target (looks at the project’s current build/
folder).
Returns:
{
"build_dir": "/work/build",
"artifacts": {
"firmware": [
{ "path": "build/my-app.bin", "size": 1048576, "sha256": "..." }
],
"elf": [
{ "path": "build/my-app.elf", "size": 4823104 }
],
"bootloader": [
{ "path": "build/bootloader/bootloader.bin", "size": 24576 }
],
"partitions": [
{ "path": "build/partition_table/partition-table.bin", "size": 3072 }
],
"maps": [
{ "path": "build/my-app.map", "size": 8421376 }
],
"other": []
}
}
The grouping knows about ESP-IDF’s standard output layout (firmware,
bootloader, partition table, ELF, maps) and groups files accordingly.
Anything it doesn’t recognize lands in other.
CLI: espctl artifacts
Local — scans build/<target>/ for files matching the artifact
classifier (.bin, .elf, .map, bootloader, partition table,
sdkconfig, etc.) and emits an ArtifactManifest. Does not consult
the build server.
espctl artifacts [--target <chip>]
Inputs
| Flag | Default | Notes |
|---|---|---|
--target | default_target from .espctl.toml | Chip — falls back to the project default. |
Output
Human mode lists each classified file:
Artifacts in /path/to/build/esp32s3:
Bin bootloader/bootloader.bin (24576 bytes)
Bin esp32s3.bin (1048576 bytes)
Elf esp32s3.elf (4823104 bytes)
Map esp32s3.map (8421376 bytes)
JSON (--json): the full ArtifactManifest with artifacts[] (each
entry has artifact_type, path, size_bytes).
Failure modes
build/<target>/doesn’t exist → exit 1.- Target invalid → exit 2.
Examples
# Use default_target from .espctl.toml
espctl artifacts
# Explicit target
espctl artifacts --target esp32s3
# JSON shape ready for a script
espctl --json artifacts --target esp32s3
Related
list_artifacts/artifacts.list— MCP equivalent that can scope bytask_id.artifacts.manifest— the official manifest written by the build server (different shape).
artifacts.manifest
Reads the official manifest.json from a finished build. The manifest
is the official record of what the build produced and how to flash it.
Input:
{ "task_id": "0abf...e2" }
Returns: The contents of manifest.json. The exact shape depends on
the recipe, but always includes:
{
"task_id": "0abf...e2",
"target": "esp32s3",
"idf_version": "v5.3.1",
"profile": "release",
"git_commit": "abc123",
"built_at": 1712340060,
"artifacts": [
{ "name": "firmware", "path": "build/my-app.bin", "size": 1048576, "sha256": "..." },
{ "name": "bootloader", "path": "build/bootloader/bootloader.bin", "offset": "0x0" },
{ "name": "partition-table", "path": "build/partition_table/partition-table.bin", "offset": "0x8000" },
{ "name": "app", "path": "build/my-app.bin", "offset": "0x10000" }
],
"flash_size": "4MB",
"flash_freq": "80m",
"flash_mode": "dio"
}
This is the right tool to call when your assistant needs to know “which file goes to which flash address”.
Security note: Your compiled firmware may contain embedded secrets (Wi-Fi credentials, API keys). Treat
.binfiles as sensitive and don’t share them publicly.
parse_build_errors
Takes a raw compiler/linker error log (or a task_id) and returns
structured, de-duplicated error messages.
Input:
{ "task_id": "0abf...e2" }
…or:
{ "log_text": "main.c:42:5: error: ..." }
Returns:
{
"errors": [
{
"file": "main/app_main.c",
"line": 42,
"column": 5,
"severity": "error",
"message": "implicit declaration of function 'foo'",
"context": [
" 40 | void app_main(void) {",
" 41 | printf(\"hello\\n\");",
" 42 | foo();",
" ^"
]
}
],
"warnings": [...],
"summary": "1 error, 0 warnings"
}
Knows about GCC, Clang, CMake, and ESP-IDF’s own error formats. Useful when your assistant wants to show you “here’s the line to fix” instead of dumping 500 lines of log.
parse_size_report
Parses the output of idf.py size and returns a flash/RAM breakdown by
section.
Input:
{ "task_id": "0abf...e2" }
Returns:
{
"target": "esp32s3",
"total_flash": { "used": 1048576, "free": 3145728, "total": 4194304 },
"total_ram": { "used": 131072, "free": 393216, "total": 524288 },
"sections": [
{ "name": ".text", "size": 524288, "memory": "flash" },
{ "name": ".rodata", "size": 262144, "memory": "flash" },
{ "name": ".data", "size": 16384, "memory": "ram" },
{ "name": ".bss", "size": 114688, "memory": "ram" }
]
}
Combine with parse_build_errors for a complete post-build summary your
assistant can present in one go.
CLI: espctl clean
Removes per-target build artifacts. With --full, removes the entire
build/, sdkconfig, and managed_components/. Operates locally
only — does not touch the build server.
espctl clean [--full] [target]
Modes
- Incremental —
espctl clean <target>deletes the files thatespctl_core::clean_planlists forbuild/<target>/.... - Full —
espctl clean --fulldeletes the wholebuild/,sdkconfig, andmanaged_components/(fullclean_plan). The positionaltargetis ignored when--fullis set.
Flag matrix
| Argument | Default | Notes |
|---|---|---|
target (positional) | — | Required unless --full. Chip name. |
--full | false | Switch to full clean. |
Output
Human mode:
Removed:
/path/to/build/esp32s3/CMakeCache.txt
/path/to/build/esp32s3/CMakeFiles
...
…or Nothing to clean. if nothing matched.
JSON (--json): { "removed": ["/path/...", ...] }.
Failure modes
| Condition | Exit | Message |
|---|---|---|
neither target nor --full given | 2 | target required for clean (use --full for full clean) |
| invalid target | 2 | invalid target: <name> |
Pre-flight
Before a destructive cleanup (especially in CI), get a preview with
the MCP get_clean_plan tool — it tells
you exactly what would be removed, without removing anything.
Examples
# Incremental — only build/esp32s3/...
espctl clean esp32s3
# Full wipe — build/, sdkconfig, managed_components/
espctl clean --full
# JSON for scripting
espctl --json clean esp32s3