feat(mcp): optional path argument on grab_widget#4270
Conversation
The grab_widget MCP tool always wrote its PNG to a generated name
under tempfile.gettempdir(), even though the underlying raw grab verb
already accepts a path. Screenshots are evidence the human operator
wants to find later (before/after grabs, bug-report captures), so the
assistant needed an extra shell step to copy each capture out of the
temp dir.
Add an optional 'path' property to the grab_widget input schema and
use it in place of the generated temp path when supplied. Everything
downstream is unchanged: the request dict, the resp.get("path", out)
resolution (the app-side reply stays authoritative if the app
normalizes the path), and the inline/too-large branches.
Test: two new field-mapping cases — caller-supplied path is emitted
verbatim, and the no-path default still lands under the temp dir.
Fixes aethersdr#4249
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Clean, tightly-scoped change — the optional path threads straight through to the raw grab verb's existing [path] argument, the request dict / resp.get("path", out) resolution / inline-vs-too-large branches are all untouched, and the existing except OSError around the read already covers an unwritable caller path. The schema note that the returned JSON's path stays authoritative is exactly right, and the two new field-mapping tests (verbatim path + temp-dir default) cover both branches. Ran python3 tools/test_aether_mcp.py locally on the branch: all checks pass.
Nothing blocking. One optional note below; take it or leave it.
Non-blocking notes
tools/aether_mcp.py— a caller-supplied~/...path is forwarded verbatim (noexpanduser), so a literal~would land relative to the cwd rather than$HOME. This matches the rawgrabverb's behavior and the authoritative-return-path note covers it, so it's fine as-is — just worth a mention if you want to be friendlier to hand-typed paths later.
Thanks for the thorough writeup and the live-verification transcript — nice work.
🤖 aethersdr-agent · cost: $4.4810 · model: claude-opus-4-8
jensenpat
left a comment
There was a problem hiding this comment.
Reviewed the exact head and its clean combined merge with current main. The optional path is schema-typed, forwarded unchanged to the existing grab verb, and the app-returned path remains authoritative; existing inline-size and read-error behavior is unchanged. Python compilation passed and all 36 MCP field-mapping/protocol checks passed. No blockers.
Adds the optional
pathproperty to thegrab_widgetMCP tool, wired through to theraw
grabverb's existing[path]argument, so callers can say where the PNG landsinstead of always getting
aether-mcp-grab-<epoch>.pngin the temp dir.Implements the shape proposed in #4249's triage comment:
pathproperty ongrab_widget(tools/aether_mcp.py),with a note that the returned JSON's
pathstays authoritative (the app maynormalize the requested path).
out = args.get("path") or <temp default>; the request dict, theresp.get("path", out)resolution, and the inline/too-large branches are unchanged.tools/test_aether_mcp.py: acaller-supplied path is emitted verbatim to the bridge, and the no-
pathdefaultstill lands under
tempfile.gettempdir().docs/automation-bridge.md's tool list.Verification (macOS, UI-only, no radio):
python3 tools/test_aether_mcp.py— all 36 checks pass (34 existing + 2 new).AETHER_AUTOMATION=1+AETHER_AUTOMATION_NO_AUTOCONNECT=1: the samegrab_widgetcall with apathwrote the PNG exactly there and returned it inline; without
pathit still landedas
aether-mcp-grab-<epoch>.pngin the temp dir. Returned JSONpathmatched theon-disk file in both cases — transcript below.
Verification transcript (paths sanitized: home →
~, macOS per-user temp →$TMPDIR)Fixes #4249
— authored by agent (Claude Code) on behalf of @skerker
🤖 Generated with Claude Code