leglas-mcp 0.6.0 → 0.7.0
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.
- package/README.md +110 -54
- package/dist/tools.js +53 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
</p>
|
|
16
16
|
|
|
17
17
|
<p align="center">
|
|
18
|
-
<img src="https://raw.githubusercontent.com/FredAmartey/leglas/main/.github/assets/screenshots/rail-single.jpg" width="900" alt="The Leglas interface: a rail of
|
|
18
|
+
<img src="https://raw.githubusercontent.com/FredAmartey/leglas/main/.github/assets/screenshots/rail-single.jpg" width="900" alt="The Leglas interface: a rail of design directions on the left, one of them with a variant grouped under it, and the selected one running as the real app filling the rest of the window" />
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
21
|
<p align="center"><i>Every direction in the rail, the selected one running as your actual app. Arrow keys flip between them.</i></p>
|
|
@@ -46,7 +46,7 @@ and sessions that clean up after themselves.
|
|
|
46
46
|
## What you can do with Leglas
|
|
47
47
|
|
|
48
48
|
<p align="center">
|
|
49
|
-
<img src="https://raw.githubusercontent.com/FredAmartey/leglas/main/.github/assets/screenshots/compare-artboards.jpg" width="900" alt="The Leglas interface:
|
|
49
|
+
<img src="https://raw.githubusercontent.com/FredAmartey/leglas/main/.github/assets/screenshots/compare-artboards.jpg" width="900" alt="The Leglas interface: the rail on the left, and two directions running side by side as the real app, each labelled with its name and the width it is drawn at." />
|
|
50
50
|
</p>
|
|
51
51
|
|
|
52
52
|
<p align="center"><i>Two directions for the same page, running side by side as the actual app.</i></p>
|
|
@@ -77,13 +77,35 @@ and sessions that clean up after themselves.
|
|
|
77
77
|
once the agent uses a Leglas tool. Each run
|
|
78
78
|
reports in a card above the field: who is working, what file they
|
|
79
79
|
are touching, how long it has been, a stop button while it runs and
|
|
80
|
-
retry when it fails. Your agent, your subscription, no keys.
|
|
80
|
+
retry when it fails. Your agent, your subscription, no keys. Claude
|
|
81
|
+
Code, Codex and Cursor pick the conversation back up between
|
|
82
|
+
requests, so a run after the first goes straight to the change
|
|
83
|
+
instead of reading the project again. Claude Code and Codex warm up
|
|
84
|
+
while you type and let their process go once you have left Leglas
|
|
85
|
+
alone for a few minutes, so an open window is not holding one.
|
|
81
86
|
Prefer a terminal? `npx leglas watch` is the same loop with the
|
|
82
87
|
agent's own output scrolling by. In Claude Code, the Leglas MCP
|
|
83
88
|
server can also push each request straight into your open session as
|
|
84
89
|
a channel event (channels are a research preview: start Claude Code
|
|
85
90
|
with `--dangerously-load-development-channels
|
|
86
91
|
server:<your leglas server name>`).
|
|
92
|
+
- A change makes a variant, not a rewrite. Ask for something on the
|
|
93
|
+
direction you are looking at and the result registers under it in the
|
|
94
|
+
rail, with the original still there to compare against. A chip beside
|
|
95
|
+
the send button switches to changing the direction itself, for when a
|
|
96
|
+
change really is a fix. Hover any direction to see what it was built
|
|
97
|
+
from and the change that was asked for, in the words that were typed.
|
|
98
|
+
- Your agent sees what you see. Every change you send carries a screenshot
|
|
99
|
+
of the direction at the width you are looking at, a crop of whatever you
|
|
100
|
+
pointed at with a note, the other pane when you are comparing, and any
|
|
101
|
+
image you pasted into the composer as a reference. Leglas renders them
|
|
102
|
+
with a Chrome, Chromium, Brave or Edge already on your machine, nothing
|
|
103
|
+
to install; with none found, the request says so and goes anyway. Agents
|
|
104
|
+
can look for themselves too: `npx leglas show "Aurora" --screenshot`
|
|
105
|
+
writes a PNG of a direction (`--width 390` for the phone layout) and the
|
|
106
|
+
MCP `show` tool returns the image. The instructions Leglas writes for
|
|
107
|
+
agents ask for one look after each change, so a broken layout gets fixed
|
|
108
|
+
before you see it.
|
|
87
109
|
- Keep the winner with one command. Leglas moves it into your source
|
|
88
110
|
tree and clears the exploration away.
|
|
89
111
|
- No app yet or want plain HTML comparisons? Some people want exactly that, and it works fine. Same comparison, no dev server.
|
|
@@ -99,6 +121,9 @@ npx leglas
|
|
|
99
121
|
Leglas starts on port 4100, proxies your app, and opens
|
|
100
122
|
`http://localhost:4100/leglas`. With no configuration you get a single
|
|
101
123
|
preview of your app root. Add a config file to compare more than one thing.
|
|
124
|
+
If that port turns out to be served from outside your project, Leglas
|
|
125
|
+
says so and points at `devServer` and `--user-port` rather than quietly
|
|
126
|
+
proxying the wrong app.
|
|
102
127
|
|
|
103
128
|
It works with whatever you're building in. Leglas never imports or
|
|
104
129
|
executes your framework, so the target can be Next, Vite, Remix,
|
|
@@ -166,9 +191,10 @@ edit the hero. The supporting commands:
|
|
|
166
191
|
where switching is instant.
|
|
167
192
|
- `npx leglas show "Aurora" --json` answers for one direction: its entry, the
|
|
168
193
|
source file behind it, the variants based on it, what it is being compared
|
|
169
|
-
against, and anything still pending on it.
|
|
170
|
-
|
|
171
|
-
block
|
|
194
|
+
against, and anything still pending on it. Add `--screenshot` and it
|
|
195
|
+
renders the direction too, so an agent can look at what it built. Copying
|
|
196
|
+
a direction from the rail hands over a block that ends in this command, so
|
|
197
|
+
an agent given the block can go and get the rest.
|
|
172
198
|
- `npx leglas keep "Aurora" --to src/components/hero.tsx` moves the winner
|
|
173
199
|
into real source and ends the exploration.
|
|
174
200
|
|
|
@@ -176,7 +202,9 @@ Asking for a change works from the interface too. Type what you want
|
|
|
176
202
|
changed into the field under the rail (or press `R`) and Leglas composes a
|
|
177
203
|
prompt naming the direction and the file behind it, copies it to your
|
|
178
204
|
clipboard, and queues it. The direction it means is the one highlighted
|
|
179
|
-
directly above the field.
|
|
205
|
+
directly above the field. By default the request asks for a new variant
|
|
206
|
+
beside that direction; the chip next to the send button switches it to a
|
|
207
|
+
change in place. Your agent drains the queue with `npx leglas requests --json` and clears
|
|
180
208
|
it with `--clear`. Leglas runs no model of its own; your agent already
|
|
181
209
|
knows your conventions and your taste.
|
|
182
210
|
|
|
@@ -184,28 +212,30 @@ Most of what you would type into that field is the part describing where
|
|
|
184
212
|
the problem is, so you can point at it instead. Press `A` and the preview
|
|
185
213
|
becomes a picker: hovering outlines the element under the pointer,
|
|
186
214
|
clicking drops a numbered pin that takes a note, and dragging marks an
|
|
187
|
-
area and names every element inside it.
|
|
188
|
-
|
|
215
|
+
area and names every element inside it. Click a pin again to reread what
|
|
216
|
+
it says, reword it or drop it. The page still scrolls, so the thing three
|
|
217
|
+
screens down is as easy to mark as the headline. Annotations
|
|
189
218
|
are a request on their own, so the field can stay empty; leave three and
|
|
190
219
|
send once. Each one carries the element's own words, its tag and classes,
|
|
191
220
|
a path and the box it filled, and the request tells your agent which of
|
|
192
221
|
those to trust first, because the design moves under them by design. One
|
|
193
222
|
whose element has since gone turns amber rather than pointing confidently
|
|
194
|
-
at the wrong thing
|
|
223
|
+
at the wrong thing, and one already sent with a change takes a ring until
|
|
224
|
+
that change settles.
|
|
195
225
|
|
|
196
|
-
The
|
|
197
|
-
|
|
226
|
+
The card above the field is the whole status: what you have queued, who
|
|
227
|
+
has taken it and for how long, and what went wrong when a run fails.
|
|
198
228
|
|
|
199
229
|
<p align="center">
|
|
200
|
-
<img src="https://raw.githubusercontent.com/FredAmartey/leglas/main/.github/assets/screenshots/field-idle.png" width="290" alt="The change field
|
|
201
|
-
<img src="https://raw.githubusercontent.com/FredAmartey/leglas/main/.github/assets/screenshots/field-queued.png" width="290" alt="
|
|
202
|
-
<img src="https://raw.githubusercontent.com/FredAmartey/leglas/main/.github/assets/screenshots/field-pickedup.png" width="290" alt="
|
|
230
|
+
<img src="https://raw.githubusercontent.com/FredAmartey/leglas/main/.github/assets/screenshots/field-idle.png" width="290" alt="The change field, empty, with its agent picker reading Choose an agent" />
|
|
231
|
+
<img src="https://raw.githubusercontent.com/FredAmartey/leglas/main/.github/assets/screenshots/field-queued.png" width="290" alt="A card above the change field reading: Change queued, pick who runs your changes" />
|
|
232
|
+
<img src="https://raw.githubusercontent.com/FredAmartey/leglas/main/.github/assets/screenshots/field-pickedup.png" width="290" alt="A card above the change field reading: Codex is on it, editing .leglas/variants/hero/poster.tsx, 56s, with a stop button" />
|
|
203
233
|
</p>
|
|
204
234
|
|
|
205
235
|
<p align="center"><i>Nothing waiting, then a request queued, then an agent that has taken it.</i></p>
|
|
206
236
|
|
|
207
|
-
Pick an agent once and the same
|
|
208
|
-
editing, a
|
|
237
|
+
Pick an agent once and the same card shows it working: which file it is
|
|
238
|
+
editing, a stop if you change your mind, a retry when a run goes
|
|
209
239
|
wrong. `npx leglas watch` in another terminal is the same loop with the
|
|
210
240
|
agent's own output in view, and it needs no flag once an agent has been
|
|
211
241
|
picked in the interface.
|
|
@@ -298,9 +328,11 @@ export default {
|
|
|
298
328
|
| `branch` | no | Preview a git branch instead of the running dev server |
|
|
299
329
|
| `file` | no | An HTML file served by Leglas itself, instead of `url` |
|
|
300
330
|
| `basedOn` | no | Title of the direction this is a variant of; the rail groups the family |
|
|
331
|
+
| `askedFor` | no | The change that was asked for, in the words that were typed |
|
|
301
332
|
| `devServer` | no | Defaults to `http://localhost:3000` |
|
|
302
333
|
| `devCommand` | with `branch` | How to start the app. Must contain `{port}`. |
|
|
303
334
|
| `installCommand` | no | Defaults to `npm install` |
|
|
335
|
+
| `scanPreviews` | no | Set `false` to skip background duplicate scans for expensive apps |
|
|
304
336
|
|
|
305
337
|
A broken config never stops the server. Leglas starts anyway and the
|
|
306
338
|
interface reports what to fix, so you are not hunting through a stack
|
|
@@ -332,8 +364,8 @@ that is what the tools popover's "Scale each side to fit" switch is for.
|
|
|
332
364
|
|
|
333
365
|
Arrows move between directions, `1` to `9` jump straight to one, `R` asks
|
|
334
366
|
for a change to the one you are on, `A` annotates the design itself,
|
|
335
|
-
`Cmd K` (`Ctrl K` elsewhere) searches
|
|
336
|
-
for the whole keymap.
|
|
367
|
+
`Cmd K` (`Ctrl K` elsewhere) searches, `T` opens the tools popover and `B`
|
|
368
|
+
collapses the rail. Press `?` for the whole keymap.
|
|
337
369
|
|
|
338
370
|
A small tools widget floats over the stage and can be dragged to any
|
|
339
371
|
corner, because a floating control has a habit of sitting exactly where
|
|
@@ -348,36 +380,57 @@ the preview frame, never by altering what the proxy forwards.
|
|
|
348
380
|
## Command line
|
|
349
381
|
|
|
350
382
|
```text
|
|
351
|
-
|
|
352
|
-
leglas
|
|
353
|
-
leglas
|
|
354
|
-
leglas
|
|
355
|
-
leglas
|
|
356
|
-
leglas
|
|
357
|
-
leglas
|
|
358
|
-
leglas
|
|
359
|
-
leglas
|
|
360
|
-
leglas
|
|
361
|
-
|
|
362
|
-
--
|
|
363
|
-
|
|
364
|
-
|
|
383
|
+
Usage
|
|
384
|
+
leglas init Prepare a project and teach its agents
|
|
385
|
+
leglas [options] Start the server and open the interface
|
|
386
|
+
leglas new <surface> Scaffold a branch point for a surface
|
|
387
|
+
leglas explore <surface> Brief an agent's exploration of a surface
|
|
388
|
+
leglas classify Decide where a direction should live
|
|
389
|
+
leglas add --title T --url U Register a preview on this machine
|
|
390
|
+
leglas list Show every preview, shared and local
|
|
391
|
+
leglas show <title> Everything Leglas knows about one direction
|
|
392
|
+
leglas requests Show change requests made from the interface
|
|
393
|
+
leglas watch --run "<cmd>" Hand each request to your agent as it arrives
|
|
394
|
+
leglas keep <title> --to <path> Keep a winner and end the exploration
|
|
395
|
+
|
|
396
|
+
Options
|
|
397
|
+
--user-port <port> Port your dev server is on (default: from config, or 3000)
|
|
398
|
+
--port <port> Port for Leglas itself (default: 4100, next free if taken)
|
|
399
|
+
--config <path> Config file to use instead of searching upward
|
|
365
400
|
--no-open Do not open the browser
|
|
366
|
-
--json Print
|
|
367
|
-
|
|
368
|
-
--
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
--
|
|
372
|
-
--
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
--
|
|
376
|
-
--
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
--
|
|
380
|
-
|
|
401
|
+
--json Print a single machine-readable envelope
|
|
402
|
+
-h, --help Show this
|
|
403
|
+
-v, --version Show the version
|
|
404
|
+
|
|
405
|
+
Options for new
|
|
406
|
+
--print Print the scaffold instead of writing it
|
|
407
|
+
--from <path> Use an existing component as the baseline
|
|
408
|
+
|
|
409
|
+
Options for explore
|
|
410
|
+
--count <n> How many directions (default 3)
|
|
411
|
+
--based-on <title> Variants of an existing direction instead of new ones
|
|
412
|
+
|
|
413
|
+
Options for watch
|
|
414
|
+
--run <command> Your agent, with {prompt} where the request goes, for
|
|
415
|
+
example "claude -p {prompt}". Remembered after first use
|
|
416
|
+
--port <port> Port Leglas itself is on (default: 4100)
|
|
417
|
+
|
|
418
|
+
Options for classify
|
|
419
|
+
--change <path> A file the direction creates or wires up (repeatable)
|
|
420
|
+
--rewrite <path> An existing file whose behaviour it must change (repeatable)
|
|
421
|
+
|
|
422
|
+
Options for add
|
|
423
|
+
--note <text> Second line under the title
|
|
424
|
+
--tag <text> Repeatable
|
|
425
|
+
--branch <name> Back the preview with a checkout of this git branch
|
|
426
|
+
--file <path> Preview a plain HTML file served by Leglas itself
|
|
427
|
+
--based-on <title> The direction this is a variant of; groups the family
|
|
428
|
+
--asked-for <text> The change that was asked for, in the words that were typed
|
|
429
|
+
|
|
430
|
+
Options for show
|
|
431
|
+
--screenshot Render the direction and write a PNG
|
|
432
|
+
--width <n> Capture width from 320 to 3840 (needs --screenshot)
|
|
433
|
+
--port <port> Running Leglas port (needs --screenshot)
|
|
381
434
|
```
|
|
382
435
|
|
|
383
436
|
`leglas.config.ts` is the shared description of a project: commit it and a
|
|
@@ -454,9 +507,9 @@ cross-origin previews, which the browser will not let it read.
|
|
|
454
507
|
|
|
455
508
|
## Limitations
|
|
456
509
|
|
|
457
|
-
- Leglas
|
|
458
|
-
|
|
459
|
-
|
|
510
|
+
- Leglas runs no model of its own. Comparing existing routes costs
|
|
511
|
+
nothing, but a new direction is still code your agent writes; Leglas
|
|
512
|
+
hands it the request and shows the result.
|
|
460
513
|
- The duplicate check compares rendered markup only, and only when the
|
|
461
514
|
server renders some. Two previews that differ solely in a script are
|
|
462
515
|
reported as identical, and in a fully client-rendered app the check
|
|
@@ -487,9 +540,12 @@ terminal and `pnpm --filter @leglas/shell dev` in another.
|
|
|
487
540
|
|
|
488
541
|
Two packages are published, both unscoped: `leglas`, which bundles the
|
|
489
542
|
server and the built interface, and `leglas-mcp`. Releases are
|
|
490
|
-
tag-driven:
|
|
491
|
-
|
|
492
|
-
|
|
543
|
+
tag-driven: set the same version in both packages and `plugin.json`, turn
|
|
544
|
+
the changelog's Unreleased section into that version, push a `v<version>`
|
|
545
|
+
tag, and CI runs the suite and publishes through npm trusted publishing.
|
|
546
|
+
A tag that disagrees with the manifests is refused, and so is a patch tag
|
|
547
|
+
when `api-surface.txt` has moved since the previous one. No npm token
|
|
548
|
+
exists anywhere in the project.
|
|
493
549
|
|
|
494
550
|
## License
|
|
495
551
|
|
package/dist/tools.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { isOwnCapture, run, runAdd, runClassify, runExplore, runInit, runKeep, runList, runNew, runRequests, runShow, } from "leglas";
|
|
2
4
|
import { z } from "zod";
|
|
3
5
|
import { createEngagement } from "./engagement.js";
|
|
4
6
|
async function capture(invoke) {
|
|
@@ -77,7 +79,8 @@ export function registerLeglasTools(server, options) {
|
|
|
77
79
|
description: "Register a design direction on this machine so it appears in the rail. " +
|
|
78
80
|
"The rail picks it up within seconds, so when building a set register " +
|
|
79
81
|
"each direction as it lands rather than the whole set at the end. " +
|
|
80
|
-
"Use branch for a direction that lives on its own git branch."
|
|
82
|
+
"Use branch for a direction that lives on its own git branch. " +
|
|
83
|
+
"Then call show with screenshot: true to look at what you registered.",
|
|
81
84
|
inputSchema: {
|
|
82
85
|
title: z.string().min(1).describe("Unique title; identifies the preview."),
|
|
83
86
|
url: z.string().min(1).optional()
|
|
@@ -105,14 +108,60 @@ export function registerLeglasTools(server, options) {
|
|
|
105
108
|
description: "Everything Leglas knows about one direction: its full entry, the source file behind " +
|
|
106
109
|
"it, the variants based on it, the directions it is being compared against, and any " +
|
|
107
110
|
"change requests still pending on it. Call this when handed a direction's reference " +
|
|
108
|
-
"block."
|
|
111
|
+
"block. With screenshot: true, Leglas also renders the direction with a headless " +
|
|
112
|
+
"browser and returns the image, so you can see what you built. Do this after registering " +
|
|
113
|
+
"a direction and before saying it is done; width 390 shows the phone layout.",
|
|
109
114
|
inputSchema: {
|
|
110
115
|
title: z
|
|
111
116
|
.string()
|
|
112
117
|
.min(1)
|
|
113
118
|
.describe("The direction's title as the config spells it, not a renamed display name."),
|
|
119
|
+
screenshot: z.boolean().optional(),
|
|
120
|
+
width: z.number().int().min(320).max(3840).optional(),
|
|
114
121
|
},
|
|
115
|
-
}, async ({ title
|
|
122
|
+
}, async ({ title, screenshot, width }) => {
|
|
123
|
+
const located = await project.locate();
|
|
124
|
+
if (!located.ok) {
|
|
125
|
+
return {
|
|
126
|
+
content: [{ type: "text", text: JSON.stringify({ ok: false, error: located.reason }) }],
|
|
127
|
+
isError: true,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const result = await capture((deps) => runShow({
|
|
131
|
+
title,
|
|
132
|
+
json: true,
|
|
133
|
+
screenshot: screenshot ?? false,
|
|
134
|
+
width: width ?? null,
|
|
135
|
+
port: null,
|
|
136
|
+
cwd: located.directory,
|
|
137
|
+
}, deps));
|
|
138
|
+
if (screenshot !== true || result.isError === true)
|
|
139
|
+
return result;
|
|
140
|
+
const text = result.content.find((entry) => entry.type === "text");
|
|
141
|
+
if (text === undefined || text.type !== "text")
|
|
142
|
+
return result;
|
|
143
|
+
try {
|
|
144
|
+
const envelope = JSON.parse(text.text);
|
|
145
|
+
const file = envelope.screenshot?.file;
|
|
146
|
+
// The path comes back over a loopback socket, which a stale record
|
|
147
|
+
// can point at something that is not Leglas. Only a real file inside
|
|
148
|
+
// this project's captures is read and handed to the host.
|
|
149
|
+
if (typeof file !== "string" || !(await isOwnCapture(located.directory, file))) {
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
const image = await readFile(resolve(located.directory, file));
|
|
153
|
+
return {
|
|
154
|
+
...result,
|
|
155
|
+
content: [
|
|
156
|
+
...result.content,
|
|
157
|
+
{ type: "image", data: image.toString("base64"), mimeType: "image/png" },
|
|
158
|
+
],
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
116
165
|
server.registerTool("classify", {
|
|
117
166
|
title: "Decide where a direction should live",
|
|
118
167
|
description: "Before writing a direction, declare what it will touch and learn whether it can be " +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leglas-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Stdio MCP server exposing Leglas to agent hosts that cannot run a shell.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
17
17
|
"zod": "^3.25.76",
|
|
18
|
-
"leglas": "^0.
|
|
18
|
+
"leglas": "^0.7.0"
|
|
19
19
|
},
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"repository": {
|