agy-ui-mcp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agy_ui_mcp-0.1.0/LICENSE +21 -0
- agy_ui_mcp-0.1.0/PKG-INFO +467 -0
- agy_ui_mcp-0.1.0/README.md +432 -0
- agy_ui_mcp-0.1.0/pyproject.toml +82 -0
- agy_ui_mcp-0.1.0/setup.cfg +4 -0
- agy_ui_mcp-0.1.0/src/agy_ui_mcp/__init__.py +25 -0
- agy_ui_mcp-0.1.0/src/agy_ui_mcp/__main__.py +24 -0
- agy_ui_mcp-0.1.0/src/agy_ui_mcp/agy_runner.py +368 -0
- agy_ui_mcp-0.1.0/src/agy_ui_mcp/policy_builder.py +271 -0
- agy_ui_mcp-0.1.0/src/agy_ui_mcp/scope.py +743 -0
- agy_ui_mcp-0.1.0/src/agy_ui_mcp/screenshot.py +1335 -0
- agy_ui_mcp-0.1.0/src/agy_ui_mcp/server.py +1547 -0
- agy_ui_mcp-0.1.0/src/agy_ui_mcp/vendor/axe.min.js +12 -0
- agy_ui_mcp-0.1.0/src/agy_ui_mcp/worktree.py +475 -0
- agy_ui_mcp-0.1.0/src/agy_ui_mcp.egg-info/PKG-INFO +467 -0
- agy_ui_mcp-0.1.0/src/agy_ui_mcp.egg-info/SOURCES.txt +35 -0
- agy_ui_mcp-0.1.0/src/agy_ui_mcp.egg-info/dependency_links.txt +1 -0
- agy_ui_mcp-0.1.0/src/agy_ui_mcp.egg-info/entry_points.txt +2 -0
- agy_ui_mcp-0.1.0/src/agy_ui_mcp.egg-info/requires.txt +8 -0
- agy_ui_mcp-0.1.0/src/agy_ui_mcp.egg-info/top_level.txt +1 -0
- agy_ui_mcp-0.1.0/tests/test_a11y.py +63 -0
- agy_ui_mcp-0.1.0/tests/test_android.py +387 -0
- agy_ui_mcp-0.1.0/tests/test_apply.py +143 -0
- agy_ui_mcp-0.1.0/tests/test_browser_launch.py +197 -0
- agy_ui_mcp-0.1.0/tests/test_diff_gate.py +138 -0
- agy_ui_mcp-0.1.0/tests/test_in_place_safety.py +181 -0
- agy_ui_mcp-0.1.0/tests/test_native_flow.py +584 -0
- agy_ui_mcp-0.1.0/tests/test_parse_match.py +137 -0
- agy_ui_mcp-0.1.0/tests/test_platform.py +163 -0
- agy_ui_mcp-0.1.0/tests/test_policy_builder.py +102 -0
- agy_ui_mcp-0.1.0/tests/test_prompt_targets.py +104 -0
- agy_ui_mcp-0.1.0/tests/test_scope.py +76 -0
- agy_ui_mcp-0.1.0/tests/test_simulator.py +375 -0
- agy_ui_mcp-0.1.0/tests/test_target_cases.py +171 -0
- agy_ui_mcp-0.1.0/tests/test_targets.py +234 -0
- agy_ui_mcp-0.1.0/tests/test_worktree_revert.py +122 -0
- agy_ui_mcp-0.1.0/tests/test_zero_config.py +457 -0
agy_ui_mcp-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 qdzsh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agy-ui-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server that delegates frontend/UI work to the agy CLI (Gemini), scoped via a diff-gate to never touch backend/API/business logic.
|
|
5
|
+
Author-email: qdzsh <github@qdzsh.dev>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/qdzsh/agy-ui-mcp
|
|
8
|
+
Project-URL: Repository, https://github.com/qdzsh/agy-ui-mcp
|
|
9
|
+
Project-URL: Issues, https://github.com/qdzsh/agy-ui-mcp/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/qdzsh/agy-ui-mcp#readme
|
|
11
|
+
Keywords: mcp,ui,frontend,gemini,agy,playwright
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: POSIX
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
23
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: mcp<2,>=1.0
|
|
28
|
+
Requires-Dist: pydantic<3,>=2
|
|
29
|
+
Requires-Dist: pyyaml<7,>=6
|
|
30
|
+
Requires-Dist: pathspec<1,>=0.12
|
|
31
|
+
Requires-Dist: playwright<2,>=1.40
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# agy-ui-mcp
|
|
37
|
+
|
|
38
|
+
An MCP (Model Context Protocol) server that delegates **frontend / UI** work to
|
|
39
|
+
Google Antigravity's **`agy` CLI** (Gemini) - while guaranteeing the agent
|
|
40
|
+
**never touches backend, API, or business logic**. It is designed to be shared
|
|
41
|
+
by **Claude Code** and **Codex** as a dedicated "FE/UI worker".
|
|
42
|
+
|
|
43
|
+
The server exposes two tools:
|
|
44
|
+
|
|
45
|
+
- **`ui_implement`** - an iterative vision loop: screenshot the running app,
|
|
46
|
+
prompt `agy` to edit CSS/components toward the target design, diff-gate the
|
|
47
|
+
result to revert anything out of scope, re-screenshot, and repeat until it
|
|
48
|
+
converges (or hits `max_iters`). Edits are applied to your working tree.
|
|
49
|
+
- **`ui_review`** - serve the app, screenshot it across every target
|
|
50
|
+
(route × device × theme × state), optionally run accessibility checks, and
|
|
51
|
+
have `agy` critique it **read-only** (any edit `agy` makes is reverted).
|
|
52
|
+
|
|
53
|
+
## What it can drive
|
|
54
|
+
|
|
55
|
+
| Surface | Platform values | How it's captured |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| Web apps | `web` (default) | Playwright (Chromium) over the dev-server URL |
|
|
58
|
+
| Mobile **web-targets** | `expo-web`, `ionic`, `flutter-web` | Playwright (same as web) |
|
|
59
|
+
| Native **iOS** | `ios-sim` | `flutter run` on the iOS Simulator + `xcrun simctl` screenshots |
|
|
60
|
+
| Native **Android** | `android-emu` | `flutter run` on an Android emulator + `adb` screenshots |
|
|
61
|
+
|
|
62
|
+
Across these it supports responsive viewports, device emulation, dark mode /
|
|
63
|
+
`prefers-color-scheme`, `forced-colors` (high contrast), print media, RTL,
|
|
64
|
+
component states (via `pre_steps`), seeded `localStorage`, per-target design
|
|
65
|
+
references, and match-score convergence when design references are provided.
|
|
66
|
+
|
|
67
|
+
**Accessibility:** for web targets, `ui_review` injects the vendored
|
|
68
|
+
[axe-core](https://github.com/dequelabs/axe-core) into the page and returns
|
|
69
|
+
structured WCAG violations (per target), which also ground `agy`'s critique.
|
|
70
|
+
|
|
71
|
+
## Use case: realign a drifted frontend
|
|
72
|
+
|
|
73
|
+
The case this server is built for: you (or Claude Code / Codex) shipped a
|
|
74
|
+
**full-stack project** - backend and frontend both done - but the **FE drifted
|
|
75
|
+
from, or doesn't match, the original design** (screen mockups, or design tokens
|
|
76
|
+
with an HTML/CSS demo). You want to **redo the FE to match the design without
|
|
77
|
+
risking the working backend**. That is exactly what the diff-gate guarantees:
|
|
78
|
+
`agy` realigns the UI, and anything outside your FE `allow` scope (API, server,
|
|
79
|
+
business logic) is reverted automatically.
|
|
80
|
+
|
|
81
|
+
**What it's strong at vs. where it needs help** - this is an *iterative
|
|
82
|
+
refinement loop*, not a from-scratch FE generator:
|
|
83
|
+
|
|
84
|
+
| Your FE today | Fit |
|
|
85
|
+
|---|---|
|
|
86
|
+
| Structure is right, **styling/layout/colors/spacing/responsive** is off | **Great** - its core job; realistically ~80-90% then human polish |
|
|
87
|
+
| **Partly** wrong (a few components / screens drifted) | Good - run it **screen by screen** with the matching `design_ref` |
|
|
88
|
+
| **Structurally** wrong (wrong component tree, missing screens, wrong layout) | **Partial** - it nudges existing code toward the design within scope; it does **not** rebuild markup from scratch. Have Claude Code/Codex scaffold the correct structure first, then use this server to drive pixel fidelity |
|
|
89
|
+
|
|
90
|
+
Fidelity is highest when you provide an **HTML/CSS demo or design tokens** (exact
|
|
91
|
+
colors/spacing/fonts) rather than an image alone (values are inferred from
|
|
92
|
+
pixels). Note the convergence score is `agy`'s **own visual self-assessment** -
|
|
93
|
+
always eyeball the returned `shots_before`/`shots_after` and `diff` to sign off.
|
|
94
|
+
|
|
95
|
+
**Workflow**
|
|
96
|
+
|
|
97
|
+
1. **Commit** your current state (a dirty tree is fine - it's snapshotted and
|
|
98
|
+
preserved; the only requirement is a git repo with ≥1 commit).
|
|
99
|
+
2. Drop a `.agy-ui-scope` that **allows only FE files** and **denies the
|
|
100
|
+
backend**, declares how to serve the app, and lists **one target per screen**
|
|
101
|
+
with that screen's `design_ref` (see below).
|
|
102
|
+
3. Run **`ui_implement`** per screen with its design ref; review
|
|
103
|
+
`shots_before`/`shots_after` + `diff`, then iterate (`max_iters`).
|
|
104
|
+
4. Run **`ui_review`** (read-only + a11y) to have `agy` critique what's left and
|
|
105
|
+
surface WCAG issues.
|
|
106
|
+
5. **Human-polish** the last ~10-20% and anything structural the loop can't
|
|
107
|
+
reach within scope.
|
|
108
|
+
|
|
109
|
+
**Sample config** - a Vite/React app, realigned screen-by-screen against
|
|
110
|
+
mockups in `./design/`:
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
model: "gemini-3.5-flash"
|
|
114
|
+
platform: web
|
|
115
|
+
|
|
116
|
+
# FE surface agy may edit/create.
|
|
117
|
+
allow:
|
|
118
|
+
- "src/**/*.css"
|
|
119
|
+
- "src/**/*.scss"
|
|
120
|
+
- "src/components/**"
|
|
121
|
+
- "src/**/*.tsx"
|
|
122
|
+
- "index.html"
|
|
123
|
+
|
|
124
|
+
# Backend / logic - always reverted, even if agy edits them.
|
|
125
|
+
deny:
|
|
126
|
+
- "**/api/**"
|
|
127
|
+
- "**/server/**"
|
|
128
|
+
- "**/*.server.*"
|
|
129
|
+
- "**/route.*"
|
|
130
|
+
|
|
131
|
+
# Sensitive entry points - reverted AND reported for a human to decide.
|
|
132
|
+
ambiguous:
|
|
133
|
+
- "src/main.tsx"
|
|
134
|
+
- "src/App.tsx"
|
|
135
|
+
- "vite.config.*"
|
|
136
|
+
|
|
137
|
+
serve:
|
|
138
|
+
cmd: "npm run dev"
|
|
139
|
+
url: "http://localhost:5173"
|
|
140
|
+
ready_timeout: 30
|
|
141
|
+
|
|
142
|
+
devices:
|
|
143
|
+
desktop: { width: 1440, height: 900 }
|
|
144
|
+
mobile: { name: "iPhone 13" } # full Playwright device emulation
|
|
145
|
+
|
|
146
|
+
# One capture per screen, each matched against its own design mockup.
|
|
147
|
+
# (targets supersedes the simple `viewports` list when present.)
|
|
148
|
+
targets:
|
|
149
|
+
- name: "home-desktop"
|
|
150
|
+
route: "/"
|
|
151
|
+
device: "desktop"
|
|
152
|
+
design_ref: "./design/home-desktop.png" # image OR an HTML/CSS demo render
|
|
153
|
+
|
|
154
|
+
- name: "dashboard-desktop"
|
|
155
|
+
route: "/dashboard"
|
|
156
|
+
device: "desktop"
|
|
157
|
+
design_ref: "./design/dashboard-desktop.png"
|
|
158
|
+
|
|
159
|
+
- name: "settings-mobile-dark"
|
|
160
|
+
route: "/settings"
|
|
161
|
+
device: "mobile"
|
|
162
|
+
color_scheme: "dark" # emulate prefers-color-scheme: dark
|
|
163
|
+
design_ref: "./design/settings-mobile-dark.png"
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Then drive each screen, e.g. `ui_implement(project_dir=".",
|
|
167
|
+
task="Match this screen to its design_ref", target_route="/dashboard")`. Targets
|
|
168
|
+
carry the per-screen mockup; `target_route` picks which one to work on.
|
|
169
|
+
|
|
170
|
+
## Platform support
|
|
171
|
+
|
|
172
|
+
The server runs on **macOS and Linux**. It spawns `agy` (and native
|
|
173
|
+
`flutter run`) through a Unix pseudo-terminal (`pty`) and manages process groups
|
|
174
|
+
with POSIX-only calls, so **native Windows is not supported** - run it under
|
|
175
|
+
**WSL2** (Windows Subsystem for Linux) instead.
|
|
176
|
+
|
|
177
|
+
| OS | Web + mobile web-targets | Native Android | Native iOS |
|
|
178
|
+
|---|---|---|---|
|
|
179
|
+
| **macOS** | yes | yes | yes |
|
|
180
|
+
| **Linux** | yes | yes | no (iOS needs macOS + Xcode) |
|
|
181
|
+
| **Windows (native)** | no | no | no |
|
|
182
|
+
| **Windows via WSL2** | yes | with adb/emulator setup | no |
|
|
183
|
+
|
|
184
|
+
Notes:
|
|
185
|
+
|
|
186
|
+
- **iOS always requires macOS + Xcode**, regardless of host OS.
|
|
187
|
+
- **WSL2:** install the Linux build of `agy` (and log in) and run
|
|
188
|
+
`playwright install chromium` inside WSL. Web and mobile web-targets work out
|
|
189
|
+
of the box; native Android additionally needs `adb`/emulator wiring (e.g.
|
|
190
|
+
connecting to a Windows-side emulator over TCP, or running the emulator inside
|
|
191
|
+
WSL2).
|
|
192
|
+
- A native-Windows port would require replacing the `pty` layer with ConPTY
|
|
193
|
+
(e.g. `pywinpty`) and the POSIX process-group calls; it is not implemented.
|
|
194
|
+
|
|
195
|
+
## How it works
|
|
196
|
+
|
|
197
|
+
- **PTY spawn.** `agy` is run as `agy -p "<prompt>"` through a Python
|
|
198
|
+
pseudo-terminal (`pty.openpty` + `subprocess.Popen`), because `agy` drops its
|
|
199
|
+
stdout when attached to a non-TTY pipe. Output is captured from the PTY
|
|
200
|
+
master; ANSI escapes and carriage returns are stripped.
|
|
201
|
+
- **Subscription auth.** `agy` authenticates via your existing Gemini
|
|
202
|
+
subscription/login - no `GEMINI_API_KEY` is passed by this server.
|
|
203
|
+
- **Diff-gate (the real guardrail).** Scope is **not** enforced inside `agy`.
|
|
204
|
+
Web runs happen in a throwaway **git worktree**; after each turn the server
|
|
205
|
+
classifies every changed path against your scope
|
|
206
|
+
(`deny > ambiguous > allow > default-deny`) and reverts anything not allowed
|
|
207
|
+
(ambiguous paths are reverted and reported as escalations). A staged edit is
|
|
208
|
+
restored from the baseline, not the index, so it cannot slip through.
|
|
209
|
+
- **Vision loop.** The orchestrator (this server) screenshots to files with
|
|
210
|
+
Playwright, embeds those paths in the prompt (`agy` opens them with its own
|
|
211
|
+
`read_file` tool - there is no image flag), lets `agy` edit, applies the
|
|
212
|
+
diff-gate, re-screenshots, and loops.
|
|
213
|
+
- **Native runs.** Native platforms run **in place** (no worktree, to reuse the
|
|
214
|
+
build cache). `flutter run` is launched under a PTY and **hot-reloaded** (`r`)
|
|
215
|
+
between iterations - with an automatic **hot-restart** (`R`) fallback when a
|
|
216
|
+
reload produces no visual change. A graceful quit (`q`) lets Flutter release
|
|
217
|
+
its lockfile cleanly.
|
|
218
|
+
- **In-place safety (snapshot-restore).** Before a native run, the server
|
|
219
|
+
snapshots your project's current state into a dangling git baseline commit
|
|
220
|
+
(without touching your index/HEAD/worktree) and records your pre-existing
|
|
221
|
+
untracked files. The diff-gate and reverts compare against that baseline, so
|
|
222
|
+
**only `agy`'s edits are gated/undone and your uncommitted work is preserved
|
|
223
|
+
exactly** - you do **not** need to commit or stash first. The only hard
|
|
224
|
+
requirement is that the project is a git repo with at least one commit; if it
|
|
225
|
+
isn't, the tool returns a structured `{"status": "blocked", ...}` result
|
|
226
|
+
explaining how to fix it (e.g. `git init`) instead of running unprotected.
|
|
227
|
+
|
|
228
|
+
## Requirements
|
|
229
|
+
|
|
230
|
+
- **Python ≥ 3.10**
|
|
231
|
+
- **The `agy` CLI**, installed and logged in (subscription auth)
|
|
232
|
+
- **Playwright Chromium** for web/a11y captures - **auto-installed on first use**
|
|
233
|
+
(or set `AGY_UI_CHROME_CHANNEL=chrome` to reuse an already-installed Chrome);
|
|
234
|
+
not needed for native-only use
|
|
235
|
+
- **`.agy-ui-scope` is optional** - it is auto-detected from your stack when
|
|
236
|
+
absent. Run `ui_init` to generate one, and see `templates/agy-ui.rule.md` for
|
|
237
|
+
the recommended agent rule block
|
|
238
|
+
- **Native iOS** (`ios-sim`): macOS + Xcode + a Flutter project, and a booted
|
|
239
|
+
iOS Simulator
|
|
240
|
+
- **Native Android** (`android-emu`): the Android SDK platform-tools (`adb`) and
|
|
241
|
+
an AVD; the adapter can auto-launch the AVD by name (`emulator -avd <name>`)
|
|
242
|
+
|
|
243
|
+
## Install
|
|
244
|
+
|
|
245
|
+
### One-liner (easiest)
|
|
246
|
+
|
|
247
|
+
Runs a self-contained installer straight from the internet (no clone needed). It
|
|
248
|
+
installs the server, tries to install Chromium, and offers to register with
|
|
249
|
+
Claude Code:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
curl -fsSL https://raw.githubusercontent.com/qdzsh/agy-ui-mcp/main/scripts/bootstrap.sh | bash
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### From GitHub (recommended)
|
|
256
|
+
|
|
257
|
+
The package is installed straight from the GitHub repo. (PyPI is intentionally
|
|
258
|
+
not used yet for security reasons; see "From PyPI" below.)
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
# 1. Install the server (gives you an `agy-ui-mcp` command on PATH)
|
|
262
|
+
pipx install git+https://github.com/qdzsh/agy-ui-mcp
|
|
263
|
+
# or: uv tool install git+https://github.com/qdzsh/agy-ui-mcp
|
|
264
|
+
|
|
265
|
+
# 2. Register it with Claude Code
|
|
266
|
+
claude mcp add agy-ui --scope user -- agy-ui-mcp
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### From PyPI (future, once published)
|
|
270
|
+
|
|
271
|
+
PyPI install is **not** available yet: the `agy-ui-mcp` name is not yet
|
|
272
|
+
published and reserved on PyPI, and installing an unclaimed name first would be
|
|
273
|
+
a name-squatting risk. Once the name is published and reserved, you will be able
|
|
274
|
+
to run:
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
# Available only AFTER the package is published + reserved on PyPI.
|
|
278
|
+
pipx install agy-ui-mcp
|
|
279
|
+
# or: uv tool install agy-ui-mcp
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Until then, use the GitHub install above (or the one-liner, which is git-backed).
|
|
283
|
+
|
|
284
|
+
The Chromium browser auto-installs on first use, so there is no manual
|
|
285
|
+
`playwright install chromium` step. (To reuse an already-installed Chrome and
|
|
286
|
+
skip the download, set `AGY_UI_CHROME_CHANNEL=chrome`.)
|
|
287
|
+
|
|
288
|
+
### From a clone (one command)
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
git clone https://github.com/qdzsh/agy-ui-mcp.git && cd agy-ui-mcp
|
|
292
|
+
./scripts/install.sh # installs the package + Chromium, and offers to
|
|
293
|
+
# register with Claude Code
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
`scripts/install.sh` is interactive and idempotent; re-run it any time.
|
|
297
|
+
|
|
298
|
+
## Zero-config quick start
|
|
299
|
+
|
|
300
|
+
You do **not** need a `.agy-ui-scope` file to get going - the server
|
|
301
|
+
auto-detects your stack (Vite/React, Next.js, Expo, Ionic, CRA, Flutter-web, or
|
|
302
|
+
generic web) and synthesizes a scope on the fly. The minimal flow:
|
|
303
|
+
|
|
304
|
+
1. **Install** (any option above).
|
|
305
|
+
2. **Log into `agy` once** (subscription auth - no API key).
|
|
306
|
+
3. **Drop a mockup image** into your project, e.g. `./design/home.png`.
|
|
307
|
+
4. **Ask the agent to match it** by calling:
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
ui_implement(project_dir=".", task="Match the running home screen to this mockup",
|
|
311
|
+
design_refs=["./design/home.png"], target_route="/")
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
That is enough for the loop to run with zero config files. When you want to
|
|
315
|
+
customize the scope (allow/deny globs, per-screen `targets`, serve command),
|
|
316
|
+
call `ui_init(project_dir=".")` once to detect your stack and write a real,
|
|
317
|
+
inspectable `.agy-ui-scope` you can edit (it never overwrites an existing one
|
|
318
|
+
unless `overwrite=True`). See `templates/agy-ui.rule.md` for a copy-paste rule
|
|
319
|
+
block that teaches your agent how to use this MCP correctly.
|
|
320
|
+
|
|
321
|
+
The Chromium browser **auto-installs on first use** (a one-time download; no
|
|
322
|
+
manual `playwright install chromium` step). Set `AGY_UI_CHROME_CHANNEL=chrome`
|
|
323
|
+
to reuse an already-installed Chrome and skip that download entirely.
|
|
324
|
+
|
|
325
|
+
## Wire into Claude Code
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
# If installed as a console script (pipx / uv tool / pip):
|
|
329
|
+
claude mcp add agy-ui --scope user -- agy-ui-mcp
|
|
330
|
+
|
|
331
|
+
# Or run the module directly (e.g. from an editable/venv install):
|
|
332
|
+
claude mcp add agy-ui --scope user -- python -m agy_ui_mcp
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Wire into Codex
|
|
336
|
+
|
|
337
|
+
Add to `~/.codex/config.toml`:
|
|
338
|
+
|
|
339
|
+
```toml
|
|
340
|
+
[mcp_servers.agy-ui]
|
|
341
|
+
command = "agy-ui-mcp" # or: command = "python", args = ["-m", "agy_ui_mcp"]
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## Configure a project
|
|
345
|
+
|
|
346
|
+
A `.agy-ui-scope` file is **optional** - without one the server auto-detects your
|
|
347
|
+
stack and synthesizes a scope (see "Zero-config quick start" above). Add a real
|
|
348
|
+
file only when you want to customize the allow/deny globs, serve command, or
|
|
349
|
+
per-screen `targets`. The easiest way is `ui_init(project_dir=".")`, which
|
|
350
|
+
detects your stack and writes a starter `.agy-ui-scope` you can then edit.
|
|
351
|
+
|
|
352
|
+
Alternatively, copy the fully annotated template and edit it for your stack:
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
cp .agy-ui-scope.example /path/to/your/app/.agy-ui-scope
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
A minimal web scope:
|
|
359
|
+
|
|
360
|
+
```yaml
|
|
361
|
+
# platform: web # default; also expo-web / ionic / flutter-web / ios-sim / android-emu
|
|
362
|
+
allow:
|
|
363
|
+
- "src/**/*.css"
|
|
364
|
+
- "src/components/**"
|
|
365
|
+
deny:
|
|
366
|
+
- "src/api/**" # backend - agy edits here are always reverted
|
|
367
|
+
- "**/*.server.*"
|
|
368
|
+
ambiguous:
|
|
369
|
+
- "src/main.tsx" # reverted AND reported for a human to decide
|
|
370
|
+
serve:
|
|
371
|
+
cmd: "npm run dev"
|
|
372
|
+
url: "http://localhost:5173"
|
|
373
|
+
ready_timeout: 30
|
|
374
|
+
viewports: [1440, 768, 390]
|
|
375
|
+
model: "gemini-3.5-flash"
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
A native (iOS) scope uses `targets` + a device registry instead of `viewports`:
|
|
379
|
+
|
|
380
|
+
```yaml
|
|
381
|
+
platform: ios-sim
|
|
382
|
+
serve:
|
|
383
|
+
cmd: "flutter run -d <simulator-udid>" # argv-split (no shell) for native
|
|
384
|
+
url: ""
|
|
385
|
+
ready_timeout: 600 # first Xcode/gradle build is slow
|
|
386
|
+
allow: ["lib/main.dart"]
|
|
387
|
+
deny: ["lib/data.dart"]
|
|
388
|
+
devices:
|
|
389
|
+
sim: { name: "iPhone 17" } # or udid: "..."
|
|
390
|
+
targets:
|
|
391
|
+
- { name: order-mobile, device: sim }
|
|
392
|
+
model: "gemini-3.5-flash"
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
See `.agy-ui-scope.example` for the full set of options (per-target
|
|
396
|
+
`design_ref`, `theme`, `rtl`, `color_scheme`, `forced_colors`, `media`,
|
|
397
|
+
`full_page`, `local_storage`, `pre_steps`, `serve.reload_cmd`, etc.).
|
|
398
|
+
|
|
399
|
+
## Tool reference
|
|
400
|
+
|
|
401
|
+
> **Zero-config:** every tool works with no `.agy-ui-scope` present - the server
|
|
402
|
+
> auto-detects your stack (Vite/React, Next.js, Expo, Ionic, CRA, Flutter-web, or
|
|
403
|
+
> generic web) and synthesizes a scope for the run.
|
|
404
|
+
|
|
405
|
+
**`ui_init(project_dir=".", overwrite=False)`** -> detects your stack and writes a
|
|
406
|
+
starter `.agy-ui-scope` (never clobbers an existing one unless `overwrite=True`).
|
|
407
|
+
Returns `status` (`ok`/`exists`/`error`), `scope_path`, `written`,
|
|
408
|
+
`detected` (`{framework, platform, serve_cmd, serve_url, package_manager}`),
|
|
409
|
+
`allow`, `deny`, `design_dir_found`, `next_steps`, and `warnings`.
|
|
410
|
+
|
|
411
|
+
**`ui_implement(project_dir, task, design_refs=None, target_route=None,
|
|
412
|
+
max_iters=4, apply=True, match_threshold=90)`** → returns `files_changed`,
|
|
413
|
+
`diff`, `escalations`, `iterations`, `shots_before`/`shots_after`, `targets`,
|
|
414
|
+
`applied`/`applied_files`, `match_score`, `match_gaps`, `warnings`. When `apply`
|
|
415
|
+
is true the surviving in-scope edits are written to your working tree.
|
|
416
|
+
|
|
417
|
+
**`ui_review(project_dir, target_route=None, against_design=None, a11y=True)`**
|
|
418
|
+
→ returns `critique`, `shots`, `targets`, `a11y` (`{target: [violations]}`),
|
|
419
|
+
`warnings`. Read-only.
|
|
420
|
+
|
|
421
|
+
Both may instead return `{"status": "blocked", "blocked_reason": "...", ...}`
|
|
422
|
+
when a native/in-place run can't be made safe (non-git or no commit yet) - the
|
|
423
|
+
`blocked_reason` tells you exactly what to do.
|
|
424
|
+
|
|
425
|
+
## Notes & limitations
|
|
426
|
+
|
|
427
|
+
- **Native needs a git repo + ≥1 commit.** This is by design (the in-place
|
|
428
|
+
safety snapshot). A dirty working tree is fine and is preserved; only a
|
|
429
|
+
non-git or commit-less project is refused (with a clear message). Web runs use
|
|
430
|
+
an isolated worktree and also require git.
|
|
431
|
+
- **Playwright Chromium auto-installs.** Web and a11y features need a Chromium
|
|
432
|
+
browser; the server installs it automatically on first use (a one-time
|
|
433
|
+
download, with a short stderr notice). Set `AGY_UI_CHROME_CHANNEL=chrome` to
|
|
434
|
+
reuse an installed Chrome/Edge and skip the download, or
|
|
435
|
+
`AGY_UI_NO_BROWSER_AUTOINSTALL=1` to opt out. Native-only use needs no browser.
|
|
436
|
+
- **Native serve command is argv-split** (`shlex.split`, no shell) so the PTY
|
|
437
|
+
can deliver `r`/`R`/`q` keystrokes to `flutter` directly - shell features
|
|
438
|
+
(`&&`, env-var expansion, `cd`) in `serve.cmd` won't work for native.
|
|
439
|
+
- **First native build can take minutes** (Xcode / Gradle). Set
|
|
440
|
+
`serve.ready_timeout` generously (e.g. 300-600s).
|
|
441
|
+
- **Flutter scaffolding.** If `flutter run` reports a missing `ios/` or
|
|
442
|
+
`android/` project, regenerate it with `flutter create --platforms=ios .`
|
|
443
|
+
(or `android`).
|
|
444
|
+
|
|
445
|
+
## Environment variables
|
|
446
|
+
|
|
447
|
+
| Variable | Effect |
|
|
448
|
+
|---|---|
|
|
449
|
+
| `AGY_UI_CHROME_CHANNEL` | Use an installed browser channel (e.g. `chrome` or `msedge`) instead of Playwright's bundled Chromium. Skips the one-time Chromium download. |
|
|
450
|
+
| `AGY_UI_CHROME_EXECUTABLE` | Explicit path to a Chromium-based browser executable to launch. |
|
|
451
|
+
| `AGY_UI_NO_BROWSER_AUTOINSTALL` | Set to a truthy value to disable the automatic `playwright install chromium` on first use. |
|
|
452
|
+
| `AGY_UI_DRY_RUN` | Skip every external side effect and return a stub payload (see below). |
|
|
453
|
+
|
|
454
|
+
## Offline / dry runs
|
|
455
|
+
|
|
456
|
+
Set `AGY_UI_DRY_RUN=1` to make the tools skip every external side effect
|
|
457
|
+
(spawning `agy`, launching Playwright, running git, starting a dev server) and
|
|
458
|
+
return a stub payload. The package imports and the scope/diff-gate logic
|
|
459
|
+
unit-test without `agy`, Playwright browsers, or a running dev server.
|
|
460
|
+
|
|
461
|
+
```bash
|
|
462
|
+
pip install -e ".[dev]" && python -m pytest -q
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
## License
|
|
466
|
+
|
|
467
|
+
MIT.
|