Device Tree — 18 Operations for AI Agents
Device trees tell embedded Linux what hardware exists — SoCs, buses, peripherals, pin muxing. act101 navigates nodes and properties structurally, so agents trace a peripheral's wiring through includes and overlays.
This page is the canonical reference an AI coding agent uses to refactor, query, and analyze Device Tree code through the act MCP server. 18 operations available: 0 refactor, 18 query, 0 analysis. Each operation is callable from Claude Code, Cursor, Codex, OpenCode, or any MCP-compatible agent host. Click any operation for a stable anchor link suitable for citation.
Worked Device Tree examples
act101 reads a Device Tree file's document-level constructs as declarations: #include//include/ lines as import, and each node that sits directly under the document as class, named for its own /-prefixed or labeled name. symbols covers node and property declarations at any depth, so it also reaches every node and property nested inside a top-level node's body, which skeleton cannot see since its node rules only match a node that is a direct child of the document. The unit of structure this grammar exposes to skeleton is the top-level fragment; the unit symbols exposes is the individual node or property, however deeply it is nested. Each example below is the verbatim output of the command shown, run against the file shown. Query outputs are pretty-printed with the timing block omitted.
Read the top-level includes and root node as a skeleton
soc.dts pulls in a GPIO binding header and a base .dtsi before declaring its own root node, which nests a uart0 UART and an i2c0 I2C bus with an eeprom child on that bus.
$ act query skeleton soc.dts
Before
/dts-v1/;
#include <dt-bindings/gpio/gpio.h>
/include/ "soc-base.dtsi"
/ {
compatible = "acme,soc-board";
uart0: serial@40011000 {
compatible = "acme,soc-uart";
reg = <0x40011000 0x400>;
interrupts = <5 2>;
status = "okay";
};
i2c0: i2c@40012000 {
compatible = "acme,soc-i2c";
reg = <0x40012000 0x400>;
clock-frequency = <400000>;
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>;
};
};
};
Output
{
"type": "Skeleton",
"declarations": [
{
"kind": "import",
"name": "<dt-bindings/gpio/gpio.h>",
"range": {
"start": {
"file": "soc.dts",
"line": 2,
"column": 1,
"byte_offset": 10
},
"end": {
"file": "soc.dts",
"line": 3,
"column": 1,
"byte_offset": 45
}
},
"name_range": {
"start": {
"file": "soc.dts",
"line": 2,
"column": 10,
"byte_offset": 19
},
"end": {
"file": "soc.dts",
"line": 2,
"column": 35,
"byte_offset": 44
}
}
},
{
"kind": "import",
"name": "\"soc-base.dtsi\"",
"range": {
"start": {
"file": "soc.dts",
"line": 3,
"column": 1,
"byte_offset": 45
},
"end": {
"file": "soc.dts",
"line": 3,
"column": 26,
"byte_offset": 70
}
},
"name_range": {
"start": {
"file": "soc.dts",
"line": 3,
"column": 11,
"byte_offset": 55
},
"end": {
"file": "soc.dts",
"line": 3,
"column": 26,
"byte_offset": 70
}
}
},
{
"kind": "class",
"name": "/",
"range": {
"start": {
"file": "soc.dts",
"line": 5,
"column": 1,
"byte_offset": 72
},
"end": {
"file": "soc.dts",
"line": 25,
"column": 3,
"byte_offset": 426
}
},
"name_range": {
"start": {
"file": "soc.dts",
"line": 5,
"column": 1,
"byte_offset": 72
},
"end": {
"file": "soc.dts",
"line": 5,
"column": 2,
"byte_offset": 73
}
}
}
]
}
The skeleton reports exactly three declarations: the #include and /include/ lines as import, and the root node as class named / — uart0, i2c0, and eeprom are all nested inside that root node's body, so none of them appear.
List every node and property as symbols, however deeply nested
uart0's reg and interrupts properties give its register base address and interrupt line; eeprom's reg gives its bus address instead.
$ act query symbols soc.dts
Before
/dts-v1/;
#include <dt-bindings/gpio/gpio.h>
/include/ "soc-base.dtsi"
/ {
compatible = "acme,soc-board";
uart0: serial@40011000 {
compatible = "acme,soc-uart";
reg = <0x40011000 0x400>;
interrupts = <5 2>;
status = "okay";
};
i2c0: i2c@40012000 {
compatible = "acme,soc-i2c";
reg = <0x40012000 0x400>;
clock-frequency = <400000>;
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>;
};
};
};
Output
{
"type": "Symbols",
"symbols": [
{
"name": "/",
"kind": "class",
"range": {
"start": {
"file": "soc.dts",
"line": 5,
"column": 1,
"byte_offset": 72
},
"end": {
"file": "soc.dts",
"line": 5,
"column": 2,
"byte_offset": 73
}
},
"visibility": "unknown"
},
{
"name": "compatible",
"kind": "field",
"range": {
"start": {
"file": "soc.dts",
"line": 6,
"column": 2,
"byte_offset": 77
},
"end": {
"file": "soc.dts",
"line": 6,
"column": 12,
"byte_offset": 87
}
},
"visibility": "unknown"
},
{
"name": "uart0",
"kind": "class",
"range": {
"start": {
"file": "soc.dts",
"line": 8,
"column": 2,
"byte_offset": 110
},
"end": {
"file": "soc.dts",
"line": 8,
"column": 7,
"byte_offset": 115
}
},
"visibility": "unknown"
},
{
"name": "serial",
"kind": "class",
"range": {
"start": {
"file": "soc.dts",
"line": 8,
"column": 9,
"byte_offset": 117
},
"end": {
"file": "soc.dts",
"line": 8,
"column": 15,
"byte_offset": 123
}
},
"visibility": "unknown"
},
{
"name": "compatible",
"kind": "field",
"range": {
"start": {
"file": "soc.dts",
"line": 9,
"column": 3,
"byte_offset": 137
},
"end": {
"file": "soc.dts",
"line": 9,
"column": 13,
"byte_offset": 147
}
},
"visibility": "unknown"
},
{
"name": "reg",
"kind": "field",
"range": {
"start": {
"file": "soc.dts",
"line": 10,
"column": 3,
"byte_offset": 169
},
"end": {
"file": "soc.dts",
"line": 10,
"column": 6,
"byte_offset": 172
}
},
"visibility": "unknown"
},
{
"name": "interrupts",
"kind": "field",
"range": {
"start": {
"file": "soc.dts",
"line": 11,
"column": 3,
"byte_offset": 197
},
"end": {
"file": "soc.dts",
"line": 11,
"column": 13,
"byte_offset": 207
}
},
"visibility": "unknown"
},
{
"name": "status",
"kind": "field",
"range": {
"start": {
"file": "soc.dts",
"line": 12,
"column": 3,
"byte_offset": 219
},
"end": {
"file": "soc.dts",
"line": 12,
"column": 9,
"byte_offset": 225
}
},
"visibility": "unknown"
},
{
"name": "i2c0",
"kind": "class",
"range": {
"start": {
"file": "soc.dts",
"line": 15,
"column": 2,
"byte_offset": 242
},
"end": {
"file": "soc.dts",
"line": 15,
"column": 6,
"byte_offset": 246
}
},
"visibility": "unknown"
},
{
"name": "i2c",
"kind": "class",
"range": {
"start": {
"file": "soc.dts",
"line": 15,
"column": 8,
"byte_offset": 248
},
"end": {
"file": "soc.dts",
"line": 15,
"column": 11,
"byte_offset": 251
}
},
"visibility": "unknown"
},
{
"name": "compatible",
"kind": "field",
"range": {
"start": {
"file": "soc.dts",
"line": 16,
"column": 3,
"byte_offset": 265
},
"end": {
"file": "soc.dts",
"line": 16,
"column": 13,
"byte_offset": 275
}
},
"visibility": "unknown"
},
{
"name": "reg",
"kind": "field",
"range": {
"start": {
"file": "soc.dts",
"line": 17,
"column": 3,
"byte_offset": 296
},
"end": {
"file": "soc.dts",
"line": 17,
"column": 6,
"byte_offset": 299
}
},
"visibility": "unknown"
},
{
"name": "clock-frequency",
"kind": "field",
"range": {
"start": {
"file": "soc.dts",
"line": 18,
"column": 3,
"byte_offset": 324
},
"end": {
"file": "soc.dts",
"line": 18,
"column": 18,
"byte_offset": 339
}
},
"visibility": "unknown"
},
{
"name": "eeprom",
"kind": "class",
"range": {
"start": {
"file": "soc.dts",
"line": 20,
"column": 3,
"byte_offset": 355
},
"end": {
"file": "soc.dts",
"line": 20,
"column": 9,
"byte_offset": 361
}
},
"visibility": "unknown"
},
{
"name": "compatible",
"kind": "field",
"range": {
"start": {
"file": "soc.dts",
"line": 21,
"column": 4,
"byte_offset": 370
},
"end": {
"file": "soc.dts",
"line": 21,
"column": 14,
"byte_offset": 380
}
},
"visibility": "unknown"
},
{
"name": "reg",
"kind": "field",
"range": {
"start": {
"file": "soc.dts",
"line": 22,
"column": 4,
"byte_offset": 401
},
"end": {
"file": "soc.dts",
"line": 22,
"column": 7,
"byte_offset": 404
}
},
"visibility": "unknown"
}
]
}
symbols reports sixteen entries: the root node, uart0/serial and i2c0/i2c as two class entries each (once for the label, once for the name), the nested eeprom node, and every compatible/reg/interrupts/status/clock-frequency property as a field — none of which skeleton reported.
Query
18 query tools, the same on every supported language. Descriptions live in the shared reference: /docs/query-tools.
callers control_flow data_flow definition diagnostics effect_closure effect_summary fix_auto get_type graph import_organize interface mutations references repo_outline skeleton symbols symbols_batch