Configure Agentic HIL in MCP Hosts¶
Agentic Hardware-in-the-Loop (Agentic HIL) exposes the same local MCP stdio server and the same tool semantics in every host. Only the host configuration syntax changes.
Every block below was verified against the linked host documentation on 2026-10-03. Moving that date is a release chore, described under "The Host Documentation Check" in Release Strategy, because a date nobody moves invites more trust than it can carry.
Common Setup¶
Install the server once and initialize the firmware project from its repository root:
agentic-hil setup --agent claude-code # or: codex / opencode
setup installs the skill, registers the host with a verified absolute
executable path, and creates the external policy. Install it first if the
command is missing: AI_AGENT_QUICKSTART.md has the
complete install chain. The per-host blocks below are the reference for every
host, the three setup registers itself included.
setup composes two commands; either runs on its own:
agentic-hil agent-install --agent claude-code # skill + user-level registration; once per user and agent
agentic-hil init --agent claude-code # this project's policy + doctor; from the project root
agent-install writes only under the invoking user's home: user-wide, meaning
per user and per machine, not shared with another OS user on the host. It needs
no project and reads no configuration, so a second repository for this user
needs only init, and a configuration location this profile refuses cannot take
the registration down.
Every host configuration below represents this launch contract:
Server name: agentic-hil
Transport: stdio
Command: <verified absolute path to persistent agentic-hil>
Arguments: mcp-stdio
Working directory: firmware project root
Environment: optional AGENTIC_HIL_CONFIG absolute-path override
The official MCP Registry identity is io.github.agentic-hil/agentic-hil. That registry identity, the local server key agentic-hil, and the executable agentic-hil refer to the same server, but they are not interchangeable fields.
The MCP host needs a persistent, trusted agentic-hil executable and must store its reviewed absolute path, never a bare command resolved afresh from PATH. agentic-hil setup performs the path, ownership, link, and persistence checks for supported hosts. A stable user-owned pipx/uv-tool link is allowed only when its parent chain and fully resolved target are also owner- and mode-safe and outside the workspace, temporary directories, and caches; the stored command remains the absolute user-bin link path. Mode-safe means writable by nobody but the owner, and group write counts as another writer only when the group is not the owner's own user-private group (its name is the account's, its gid is the account's primary gid, and it has no other member), so the group-writable console script a default Debian or Ubuntu umask 0002 produces needs no chmod before it can be registered, while world write, a foreign group, and ownership by a third account are refused with the failing condition and the path element named. For a manual host, locate the user-level tool directory with uv tool dir --bin, review the resulting console script and target, and substitute its absolute path in the examples below. Do not save an unversioned uvx invocation as a long-lived server command: it can resolve a different package version later and its cache is not an installation boundary.
server.json publishes "runtimeHint": "uvx" to the MCP Registry, and that is not a contradiction: the hint tells a registry client how to fetch and try the package, which is a trial run, not the persistent registration this page governs. After trying it that way, run agentic-hil setup --agent <agent> so the stored command is a verified executable. agentic-hil doctor prints the exact command registration will accept, or says why there is none yet.
Generated by agent-install (recommended)¶
For these three agents you do not need to hand-write the blocks below. agentic-hil agent-install --agent <agent>, which agentic-hil setup --agent <agent> runs first, registers the server in that agent's user-level config, outside the firmware repo so the untrusted repo cannot control how the agent launches tools (the same trust boundary as the authoritative config), with no baked-in cwd and with the verified absolute persistent executable path:
- Claude Code →
~/.claude.jsonuser scope (secure direct merge) - Codex →
~/.codex/config.toml([mcp_servers.agentic-hil]) - opencode →
~/.config/opencode/opencode.json(mcp.agentic-hil)
The host launches the verified executable with mcp-stdio from the firmware project root; the server discovers the project's config from that working directory. The per-host blocks below are the reference and the path for other hosts. A deliberate project-scoped .mcp.json from agentic-hil mcp-config --output .mcp.json also contains the machine-specific absolute executable and must remain uncommitted.
Authoritative Project Config¶
Run agentic-hil init from the firmware project root. It creates one authoritative config outside the repository with every permission granted except the two flashing is interlocked against, binds its mandatory workspace_root to that absolute project path, and verifies the result with doctor. Rerunning it keeps a config that is already there; only --force rewrites one. The generated path is under ${XDG_CONFIG_HOME:-~/.config}/agentic-hil/projects/<name>-<digest>/config.yaml on POSIX or %APPDATA%/agentic-hil/projects/<name>-<digest>/config.yaml on Windows, and under ~/.agentic-hil/projects/<name>-<digest>/config.yaml or %USERPROFILE%\.agentic-hil\projects\<name>-<digest>\config.yaml when that first root cannot be written, which is decided by attempting the write rather than by looking for the directory. A config already sitting under the second root is the one every later run reads, and the one --force rewrites, so it stays where it was generated.
init reads the attached bench while it does that, and it needs one debug toolchain on the host, not a particular one: with STM32CubeProgrammer installed it enumerates the ST-Link through that CLI, and otherwise out of this host's USB serial inventory, driving it with the openocd on PATH. On the OpenOCD-only path the one ST-Link that inventory shows is bound, so init writes a type: openocd entry naming the probe, the toolchain and the port. That inventory is not an authoritative count (it cannot see a VCP-less ST-LINK/V2), so the entry it writes also carries discovered_by: usb_serial_inventory, probe_inventory: incomplete and the sentence saying so, and the report repeats it in a step of its own. Its summary and next steps say which binaries were looked for, where each resolved and which ST-Link serials the host is showing, so a run that wrote placeholders says why. Where the workspace ships an agentic-hil.config.example.yaml, a placeholder still carries that profile's target and port names, with the ports left unbound until agentic-hil adopt-hardware fills the devices in. Where a probe that publishes no virtual COM port is attached beside the visible one, agentic-hil adopt-hardware --probe-id <serial> names the intended board instead.
The operator reviews that file and takes away the permissions this bench should not have: every one of them starts granted except allow_raw_debugger_commands and the irreversible allow_mass_erase, which start false because flash_firmware is refused on a probe while either is true. Telling the agent which ones to take away works as well as editing: over MCP project_config_set writes false into a permission and no other value. The MCP server discovers the file from the configured firmware-project working directory. If a different external location is needed, set its absolute path as AGENTIC_HIL_CONFIG in the host's user-level, managed, or parent-process environment; do not add a machine-specific value to .vscode/mcp.json, .codex/config.toml, .mcp.json, opencode.json, or another repository-controlled file. For unattended hardware benches, use a host/user-level registration that the agent cannot edit.
%APPDATA% and %LOCALAPPDATA% are the discovered defaults, and nothing about their ACLs is inspected: the ownership walk that once did was removed in 0.8.0, because it could only defend against a different account on the same machine and never against your own processes. What is still refused with unsafe_configured_path is a path that is not the object it names: a symlinked component, a file where a directory belongs. A redirected profile directory needs no override at all: %USERPROFILE%\.agentic-hil is the second root agentic-hil init walks to by itself, and a config generated there stays the one this project loads. What the override is for is a location neither root reaches (a roaming share, a volume chosen to keep this bench's files off the system disk), and there it is the supported answer rather than a workaround:
$env:AGENTIC_HIL_CONFIG = "D:\bench-configs\agentic-hil\projects\<name>-<digest>\config.yaml"
The POSIX spelling of the same binding:
export AGENTIC_HIL_CONFIG="/srv/bench-configs/agentic-hil/projects/<name>-<digest>/config.yaml"
Set state_root in that file to a directory under the same root. Set AGENTIC_HIL_CONFIG in the host's user-level or managed environment, never in a repository-controlled file. Where each file lives: agentic-hil://reference/platform-paths.
Canonical Tool Names¶
Agentic HIL returns one canonical name for each tool in MCP tools/list, such as probe_target, flash_firmware, and com_read. Hosts may display a qualified form such as agentic-hil_probe_target or mcp__agentic-hil__probe_target. That prefix is a host-side namespace, not a second Agentic HIL API.
Documentation, prompts, tests, and cross-host workflows should use the canonical wire name probe_target. Do not add aliases or change behavior by host. Use a qualified rendering only where a host's own permission or tool-selection syntax requires it.
Tool Annotations¶
Each entry in tools/list carries the MCP annotations object (title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint) as defined in schema revision 2025-06-18, the protocol version this server advertises. Hosts that use those fields to decide which calls need confirmation get an answer per tool instead of one blanket judgement by name.
What they say:
| Class | Tools |
|---|---|
changes nothing (readOnlyHint: true) |
debugger_info, debugger_probes_list, debug_get_session_status, debug_list_breakpoints, debug_get_stop_reason, debug_symbol_info, debug_symbol_value, get_last_report, classify_last_error, com_ports_list, com_read, can_buses_list, can_read, bench_run_status, hardware_lease_status, project_config_describe, project_config_reload_description, test_reactor_status |
changes something reversible (destructiveHint: false) |
probe_target, artifact_upload, reset_target, debug_stop_session, debug_set_breakpoint, debug_clear_breakpoints, debug_continue, debug_halt, com_session_stop, can_session_stop, bench_run_start, bench_run_stop, test_reactor_stop, hardware_recover, project_config_adopt_hardware, server_upgrade |
may destroy what it replaces (destructiveHint: true) |
flash_firmware, debug_start_session, debug_dump_symbol_ihex, com_session_start, com_write, can_session_start, can_send, test_reactor_run, project_config_create, project_config_set |
Three of them are worth reading before configuring a host's permission rules. probe_target reads and is still not read-only, because an SWD attach halts the core. project_config_reload_description is read-only: it re-reads the authoritative file, writes nothing, and touches no hardware. A host that blocks it sends the operator back to reconnecting the server, which is what it exists to avoid. server_upgrade replaces the installed package and is still not destructive: it erases nothing this server holds, leaves the bench and the configuration untouched, and the release it replaces is still on the index. It is idempotent because it can only lift to the newest one, so a second call finds nothing to do.
openWorldHint is false on every tool but server_upgrade: a bench is the closed, named set of devices the authoritative configuration declares, plus that configuration and the workspace artifact store. server_upgrade is true because what it installs comes off a package index over the network, an entity outside this machine, which is the open world exactly as the schema means it.
These are hints. Agentic HIL decides nothing by them (what a call may do is decided by the permissions in the authoritative configuration and by the machine-wide device locks), and a host is free to prompt for anything regardless.
VS Code and GitHub Copilot¶
Put this server in the operator-controlled VS Code user-profile MCP configuration. One registration serves every project, and none of it lives in a repository:
{
"servers": {
"agentic-hil": {
"type": "stdio",
"command": "/absolute/path/to/persistent/agentic-hil",
"args": [
"mcp-stdio"
]
}
}
}
Run MCP: List Servers and start agentic-hil. VS Code starts a user-profile server in the home directory, where there is no project, so the server asks VS Code which folder it has open (the MCP roots/list request) and serves that folder's configuration; with several folders open, it serves the one that has a configuration. Leave cwd out: VS Code resolves no ${workspaceFolder} in the user profile and does not start the server with it. VS Code's own mcp.json uses servers, not mcpServers; the mcpServers shape it also reads belongs to the portable .mcp.json and ~/.copilot/mcp-config.json, a different file.
Sources: VS Code MCP configuration reference and GitHub Copilot MCP setup.
JetBrains AI Assistant in CLion¶
Open Settings | Tools | AI Assistant | Model Context Protocol (MCP), add a stdio server, and paste:
{
"mcpServers": {
"agentic-hil": {
"command": "/absolute/path/to/persistent/agentic-hil",
"args": [
"mcp-stdio"
]
}
}
}
Set Working directory to the firmware repository root and then enable the server. JetBrains documents this as an IDE configuration flow, not as a portable repository file. An operator-set AGENTIC_HIL_CONFIG remains available as an optional override.
If CLion uses the GitHub Copilot plugin instead of JetBrains AI Assistant, open Copilot Chat, select Configure your MCP server, and use the plugin's mcp.json shape:
{
"servers": {
"agentic-hil": {
"command": "/absolute/path/to/persistent/agentic-hil",
"args": [
"mcp-stdio"
]
}
}
}
The AI Assistant and Copilot plugin use different JSON containers. Their server and tool semantics remain identical.
Sources: JetBrains AI Assistant MCP setup and GitHub Copilot MCP setup for JetBrains IDEs. JetBrains renamed its own page to mcp.html; the address this file used before still redirects there.
OpenAI Codex¶
Put this table in the operator-controlled ~/.codex/config.toml and replace cwd with the absolute firmware repository root:
[mcp_servers.agentic-hil]
command = "/absolute/path/to/persistent/agentic-hil"
args = ["mcp-stdio"]
cwd = "/absolute/path/to/firmware-project"
enabled = true
The configured cwd enables automatic config discovery. Start Codex with AGENTIC_HIL_CONFIG inherited only when an absolute-path override is required. Project-scoped configuration is unnecessary for this registration.
Sources: Codex MCP setup and Codex configuration reference. Both moved off developers.openai.com, which answers them with a permanent redirect.
Claude Code¶
Use this server shape in an operator-controlled Claude Code user registration:
{
"mcpServers": {
"agentic-hil": {
"type": "stdio",
"command": "/absolute/path/to/persistent/agentic-hil",
"args": [
"mcp-stdio"
]
}
}
}
The equivalent CLI command, run from the firmware repository root, is:
claude mcp add --transport stdio --scope user agentic-hil -- "/absolute/path/to/persistent/agentic-hil" mcp-stdio
agentic-hil mcp-config --output .mcp.json writes this host family's machine-local mcpServers discovery format with the verified absolute executable: the command and args above, and no type, because Claude Code reads an entry without one as a stdio server and only a url entry has to declare its transport. The user-level entry agent-install writes does state "type": "stdio", because an operator reads that file. Neither includes AGENTIC_HIL_CONFIG, and neither generates VS Code, Codex, or OpenCode configuration. Keep this machine-specific project file uncommitted; prefer setup --agent claude for the secure user-scoped registration and add an operator-set override only when needed.
Source: Claude Code MCP documentation.
OpenCode¶
Add this server to the operator-controlled ~/.config/opencode/opencode.json and replace cwd with the absolute firmware repository root:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"agentic-hil": {
"type": "local",
"command": [
"/absolute/path/to/persistent/agentic-hil",
"mcp-stdio"
],
"cwd": "/absolute/path/to/firmware-project",
"enabled": true
}
}
}
OpenCode uses mcp, type: "local", and one command array. For an operator-controlled registration, use ~/.config/opencode/opencode.json; its cwd enables automatic config discovery. If an override is needed, inherit AGENTIC_HIL_CONFIG from the parent environment rather than placing its machine-specific value in project configuration. Managed configuration is preferable on unattended benches because project configuration has higher precedence than normal global configuration.
Sources: OpenCode MCP servers and OpenCode configuration locations.
Generic MCP Host¶
MCP standardizes the stdio transport, not a universal host configuration file. In the host's MCP settings, map the common launch contract to its local fields:
Name: agentic-hil
Transport: stdio
Executable: <verified absolute path to persistent agentic-hil>
Arguments: mcp-stdio
Working directory: <absolute firmware repository root>
Environment: optional AGENTIC_HIL_CONFIG=<absolute external config path>
Do not assume that mcpServers, servers, mcp, env, cwd, or enabled are portable keys. A Claude-compatible .mcp.json is a host convention, not part of the MCP specification.
Source: MCP stdio transport specification.
Verify the Connection¶
- Confirm the host reports
agentic-hilas connected. - Inspect the host's tool list or MCP
tools/listresult. - Confirm canonical tools such as
debugger_info,probe_target, andflash_firmwareare present. - Run
debugger_infobefore a hardware action when setup is unclear. - Treat
permission_deniedas authoritative. Ask the operator to review the authoritative config; do not bypass it.
See Troubleshooting for startup, configuration, PATH, and debugger errors.