draw2agent 2.0.0 → 2.0.2
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 +79 -61
- package/dist/index.js +71 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,61 +1,79 @@
|
|
|
1
|
-
# draw2agent ✏️
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/draw2agent)
|
|
4
|
+
[](https://registry.modelcontextprotocol.io/?q=draw2agent)
|
|
5
|
+
|
|
6
|
+
Draw on your website. Your AI agent sees it.
|
|
7
|
+
|
|
8
|
+
**draw2agent** is an MCP server that lets you draw annotations directly on top of your local dev page. When you submit, your IDE agent receives a screenshot, structured DOM data, and annotation context to make precise code edits.
|
|
9
|
+
|
|
10
|
+
👉 **Try it out at:** [draw2agent.vercel.app](https://draw2agent.vercel.app)
|
|
11
|
+
|
|
12
|
+
## Demo
|
|
13
|
+
|
|
14
|
+
[](https://youtu.be/siv1ioOnOXk)
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
### 1. Add to your IDE (one-time)
|
|
19
|
+
|
|
20
|
+
**Cursor** (`~/.cursor/mcp.json`):
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"draw2agent": {
|
|
25
|
+
"command": "npx",
|
|
26
|
+
"args": ["-y", "draw2agent@latest"]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 2. Use it
|
|
33
|
+
|
|
34
|
+
Tell your agent:
|
|
35
|
+
> "Use draw2agent to fix the navbar"
|
|
36
|
+
|
|
37
|
+
1. 🌐 Agent opens your browser with drawing tools on your page
|
|
38
|
+
2. ✏️ Draw circles, arrows, text directly on your website
|
|
39
|
+
3. 📸 Click **Submit**
|
|
40
|
+
4. 🤖 Agent reads the visual context and applies code changes
|
|
41
|
+
|
|
42
|
+
## How It Works
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
Your Dev Page (proxied)
|
|
46
|
+
├── Your original page content
|
|
47
|
+
└── Excalidraw overlay (transparent, on top)
|
|
48
|
+
├── Draw mode: annotate directly on the page
|
|
49
|
+
├── Select mode: interact with the page normally (Esc)
|
|
50
|
+
└── Submit: screenshot + DOM + annotations → agent
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Tools
|
|
54
|
+
|
|
55
|
+
The MCP server exposes the following tools:
|
|
56
|
+
|
|
57
|
+
| Tool | Description |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `launch_canvas` | Opens your dev page with the drawing overlay |
|
|
60
|
+
| `launch_ipad_canvas` | Creates a tunnel and returns a QR code for remote drawing from iPad/mobile (does not block) |
|
|
61
|
+
| `wait_for_ipad_submission` | Blocks and waits for the user to submit their drawing from the iPad. Must be called after `launch_ipad_canvas`. |
|
|
62
|
+
| `launch_scratch` | Opens a standalone Excalidraw whiteboard for freehand sketching |
|
|
63
|
+
| `get_drawing_state` | Returns screenshot, DOM nodes, and annotations for the current state |
|
|
64
|
+
|
|
65
|
+
### `launch_canvas`
|
|
66
|
+
The core tool — proxies your localhost dev server and injects an Excalidraw overlay. Draw annotations directly on your running app, then submit to send visual context to your agent. The tool blocks until you submit.
|
|
67
|
+
|
|
68
|
+
### `launch_ipad_canvas` & `wait_for_ipad_submission`
|
|
69
|
+
Exposes the proxy over the internet via a secure tunnel. Returns a QR code image to your agent so it can be displayed in the chat. You can scan it from your iPad or phone to draw annotations with touch. `wait_for_ipad_submission` blocks and waits for the actual drawing data.
|
|
70
|
+
|
|
71
|
+
### `launch_scratch`
|
|
72
|
+
Opens a blank Excalidraw whiteboard — no target URL needed. Sketch UI mockups, wireframes, or diagrams from scratch. Your agent receives the drawing and implements the design.
|
|
73
|
+
|
|
74
|
+
### `get_drawing_state`
|
|
75
|
+
Returns the last captured drawing state (screenshot, DOM nodes, annotations) without launching a new session. Useful for re-fetching context.
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -13827,6 +13827,9 @@ function rejectState(errorMsg) {
|
|
|
13827
13827
|
rejectPendingState = null;
|
|
13828
13828
|
}
|
|
13829
13829
|
}
|
|
13830
|
+
function getState() {
|
|
13831
|
+
return currentState;
|
|
13832
|
+
}
|
|
13830
13833
|
function clearState() {
|
|
13831
13834
|
currentState = null;
|
|
13832
13835
|
isSessionClosed = false;
|
|
@@ -14303,7 +14306,7 @@ function createMcpServer() {
|
|
|
14303
14306
|
);
|
|
14304
14307
|
server2.tool(
|
|
14305
14308
|
"launch_ipad_canvas",
|
|
14306
|
-
"Launch a remote drawing canvas accessible from an iPad or mobile device. Creates a tunnel
|
|
14309
|
+
"Launch a remote drawing canvas accessible from an iPad or mobile device. Creates a tunnel and returns a QR code image to show in the chat. You MUST immediately call `wait_for_ipad_submission` right after this tool to receive the actual drawing.",
|
|
14307
14310
|
{
|
|
14308
14311
|
targetUrl: external_exports.string().describe("The URL of the local dev server to overlay (e.g. http://localhost:3000)"),
|
|
14309
14312
|
port: external_exports.number().optional().describe("Port for the draw2agent proxy server (default: 9742)")
|
|
@@ -14334,11 +14337,34 @@ function createMcpServer() {
|
|
|
14334
14337
|
}
|
|
14335
14338
|
const tunnelUrl = await startTunnel(proxyPort);
|
|
14336
14339
|
const qr = await generateQR(tunnelUrl);
|
|
14337
|
-
console.error(`
|
|
14338
|
-
[draw2agent] \u{1F4F1} iPad Canvas Ready!`);
|
|
14339
|
-
console.error(`[draw2agent] \u{1F517} Scan this QR code or open: ${tunnelUrl}`);
|
|
14340
|
-
console.error(qr.ascii);
|
|
14341
14340
|
clearState();
|
|
14341
|
+
return {
|
|
14342
|
+
content: [
|
|
14343
|
+
{
|
|
14344
|
+
type: "text",
|
|
14345
|
+
text: `iPad Canvas Ready! The QR code is attached. The user needs to scan it with their iPad.
|
|
14346
|
+
|
|
14347
|
+
IMPORTANT: You must NOW immediately call the \`wait_for_ipad_submission\` tool without asking the user. That tool will wait for them to finish drawing and return the result.`
|
|
14348
|
+
},
|
|
14349
|
+
{
|
|
14350
|
+
type: "image",
|
|
14351
|
+
data: qr.dataUrl.replace(/^data:image\/\w+;base64,/, ""),
|
|
14352
|
+
mimeType: "image/png"
|
|
14353
|
+
}
|
|
14354
|
+
]
|
|
14355
|
+
};
|
|
14356
|
+
} catch (err) {
|
|
14357
|
+
await stopTunnel();
|
|
14358
|
+
return handleToolError(err, "launch_ipad_canvas");
|
|
14359
|
+
}
|
|
14360
|
+
}
|
|
14361
|
+
);
|
|
14362
|
+
server2.tool(
|
|
14363
|
+
"wait_for_ipad_submission",
|
|
14364
|
+
"Wait for the user to submit their drawing from the iPad. Must be called immediately after `launch_ipad_canvas`.",
|
|
14365
|
+
{},
|
|
14366
|
+
async () => {
|
|
14367
|
+
try {
|
|
14342
14368
|
const state = await waitForState();
|
|
14343
14369
|
await stopTunnel();
|
|
14344
14370
|
const customInstructions = readPromptFile(
|
|
@@ -14360,7 +14386,7 @@ function createMcpServer() {
|
|
|
14360
14386
|
};
|
|
14361
14387
|
} catch (err) {
|
|
14362
14388
|
await stopTunnel();
|
|
14363
|
-
return handleToolError(err, "
|
|
14389
|
+
return handleToolError(err, "wait_for_ipad_submission");
|
|
14364
14390
|
}
|
|
14365
14391
|
}
|
|
14366
14392
|
);
|
|
@@ -14399,6 +14425,45 @@ function createMcpServer() {
|
|
|
14399
14425
|
}
|
|
14400
14426
|
}
|
|
14401
14427
|
);
|
|
14428
|
+
server2.tool(
|
|
14429
|
+
"get_drawing_state",
|
|
14430
|
+
"Returns the current drawing state including screenshot, DOM nodes, and annotations. Use this to retrieve the latest captured state without launching a new canvas session. Returns an error if no state has been captured yet.",
|
|
14431
|
+
{},
|
|
14432
|
+
async () => {
|
|
14433
|
+
const state = getState();
|
|
14434
|
+
if (!state) {
|
|
14435
|
+
return {
|
|
14436
|
+
content: [
|
|
14437
|
+
{
|
|
14438
|
+
type: "text",
|
|
14439
|
+
text: "\u274C No drawing state available. The user has not submitted any drawings yet. Use `launch_canvas`, `launch_ipad_canvas`, or `launch_scratch` first."
|
|
14440
|
+
}
|
|
14441
|
+
],
|
|
14442
|
+
isError: true
|
|
14443
|
+
};
|
|
14444
|
+
}
|
|
14445
|
+
return {
|
|
14446
|
+
content: [
|
|
14447
|
+
{
|
|
14448
|
+
type: "text",
|
|
14449
|
+
text: JSON.stringify({
|
|
14450
|
+
timestamp: state.timestamp,
|
|
14451
|
+
targetUrl: state.targetUrl,
|
|
14452
|
+
viewportSize: state.viewportSize,
|
|
14453
|
+
drawingBounds: state.drawingBounds,
|
|
14454
|
+
domNodes: state.domNodes,
|
|
14455
|
+
annotationCount: state.annotations.length
|
|
14456
|
+
}, null, 2)
|
|
14457
|
+
},
|
|
14458
|
+
{
|
|
14459
|
+
type: "image",
|
|
14460
|
+
data: state.annotatedScreenshot.replace(/^data:image\/\w+;base64,/, ""),
|
|
14461
|
+
mimeType: "image/png"
|
|
14462
|
+
}
|
|
14463
|
+
]
|
|
14464
|
+
};
|
|
14465
|
+
}
|
|
14466
|
+
);
|
|
14402
14467
|
return server2;
|
|
14403
14468
|
}
|
|
14404
14469
|
|