@aaqiljamal/visual-editor-mcp 0.2.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/bin/visual-editor-mcp.cjs +21 -0
- package/dist/server.js +199 -0
- package/dist/server.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Launcher for `npx visual-editor-mcp`. The MCP server is shipped pre-built
|
|
5
|
+
* in dist/server.js (ESM); we delegate to it directly.
|
|
6
|
+
*/
|
|
7
|
+
const path = require("node:path");
|
|
8
|
+
const { spawn } = require("node:child_process");
|
|
9
|
+
const { pathToFileURL } = require("node:url");
|
|
10
|
+
|
|
11
|
+
const cli = path.join(__dirname, "..", "dist", "server.js");
|
|
12
|
+
const child = spawn(
|
|
13
|
+
process.execPath,
|
|
14
|
+
[pathToFileURL(cli).href, ...process.argv.slice(2)],
|
|
15
|
+
{ stdio: "inherit" },
|
|
16
|
+
);
|
|
17
|
+
child.on("exit", (code) => process.exit(code ?? 0));
|
|
18
|
+
child.on("error", (err) => {
|
|
19
|
+
process.stderr.write(`visual-editor-mcp: ${err.message}\n`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
});
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/server.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import * as fs from "fs/promises";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
var SERVER_URL = process.env.VISUAL_EDITOR_SERVER_URL ?? "http://127.0.0.1:7790";
|
|
10
|
+
async function resolveToken() {
|
|
11
|
+
if (process.env.VISUAL_EDITOR_TOKEN) return process.env.VISUAL_EDITOR_TOKEN;
|
|
12
|
+
const workspace = process.env.VISUAL_EDITOR_WORKSPACE_ROOT ?? process.cwd();
|
|
13
|
+
try {
|
|
14
|
+
const filePath = path.join(workspace, ".visual-editor", "session.json");
|
|
15
|
+
const json = JSON.parse(await fs.readFile(filePath, "utf8"));
|
|
16
|
+
if (typeof json.token === "string") return json.token;
|
|
17
|
+
} catch {
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const r = await fetch(`${SERVER_URL}/token`);
|
|
21
|
+
if (!r.ok) return null;
|
|
22
|
+
const body = await r.json();
|
|
23
|
+
return typeof body.token === "string" ? body.token : null;
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
var cachedToken = null;
|
|
29
|
+
async function getToken() {
|
|
30
|
+
if (cachedToken) return cachedToken;
|
|
31
|
+
cachedToken = await resolveToken();
|
|
32
|
+
return cachedToken;
|
|
33
|
+
}
|
|
34
|
+
var server = new McpServer({
|
|
35
|
+
name: "visual-editor",
|
|
36
|
+
version: "0.0.1"
|
|
37
|
+
});
|
|
38
|
+
function ok(text) {
|
|
39
|
+
return { content: [{ type: "text", text }] };
|
|
40
|
+
}
|
|
41
|
+
function fail(text) {
|
|
42
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
43
|
+
}
|
|
44
|
+
async function callServer(path2, init) {
|
|
45
|
+
const headers = init.body !== void 0 ? { "Content-Type": "application/json" } : {};
|
|
46
|
+
const token = await getToken();
|
|
47
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
48
|
+
const res = await fetch(`${SERVER_URL}${path2}`, {
|
|
49
|
+
method: init.method,
|
|
50
|
+
headers,
|
|
51
|
+
body: init.body !== void 0 ? JSON.stringify(init.body) : void 0
|
|
52
|
+
});
|
|
53
|
+
const text = await res.text();
|
|
54
|
+
let body = text;
|
|
55
|
+
try {
|
|
56
|
+
body = JSON.parse(text);
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
return { status: res.status, body };
|
|
60
|
+
}
|
|
61
|
+
server.registerTool(
|
|
62
|
+
"get_selected_element",
|
|
63
|
+
{
|
|
64
|
+
description: "Read the element the user has currently selected in the browser overlay. Returns the source location (file, line, col), data-oid, className string, tag name, component name, and how many DOM instances share this source (Principle 11). Returns selection=null when nothing is selected.",
|
|
65
|
+
inputSchema: {}
|
|
66
|
+
},
|
|
67
|
+
async () => {
|
|
68
|
+
try {
|
|
69
|
+
const { status, body } = await callServer("/selection", { method: "GET" });
|
|
70
|
+
if (status !== 200) return fail(`HTTP ${status}: ${JSON.stringify(body)}`);
|
|
71
|
+
return ok(JSON.stringify(body));
|
|
72
|
+
} catch (err) {
|
|
73
|
+
return fail(
|
|
74
|
+
`Could not reach visual-editor server at ${SERVER_URL}: ${err.message}. Is it running? \`npx tsx packages/server/src/cli.ts --port 7790 --workspace .\``
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
var mutationInputShape = {
|
|
80
|
+
file: z.string().describe("Workspace-relative file path (from data-oid)."),
|
|
81
|
+
line: z.number().int().positive().describe("1-based JSXOpeningElement line."),
|
|
82
|
+
col: z.number().int().min(0).describe("0-based JSXOpeningElement column."),
|
|
83
|
+
before: z.string().describe("Exact static className token to swap, e.g. 'p-4'."),
|
|
84
|
+
after: z.string().describe("Replacement token, e.g. 'p-6'.")
|
|
85
|
+
};
|
|
86
|
+
server.registerTool(
|
|
87
|
+
"propose_change",
|
|
88
|
+
{
|
|
89
|
+
description: "Diff a className mutation WITHOUT writing it. Returns a unified diff for review. Refuses dynamic-className contexts (cn/clsx/twMerge/cva/spread/template-literal/conditional) with a structured reason \u2014 see the `details` field on errors.",
|
|
90
|
+
inputSchema: mutationInputShape
|
|
91
|
+
},
|
|
92
|
+
async (args) => {
|
|
93
|
+
try {
|
|
94
|
+
const { status, body } = await callServer("/propose", {
|
|
95
|
+
method: "POST",
|
|
96
|
+
body: args
|
|
97
|
+
});
|
|
98
|
+
if (status === 200) return ok(JSON.stringify(body));
|
|
99
|
+
return fail(`HTTP ${status}: ${JSON.stringify(body)}`);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
return fail(`Could not reach visual-editor server: ${err.message}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
server.registerTool(
|
|
106
|
+
"apply_change",
|
|
107
|
+
{
|
|
108
|
+
description: "Apply a className mutation to disk. Conflict-checked: returns 409 if the file's current state no longer contains `before` at line:col (i.e. the IDE edited it between drag and apply). The successful apply is recorded in the revert history.",
|
|
109
|
+
inputSchema: mutationInputShape
|
|
110
|
+
},
|
|
111
|
+
async (args) => {
|
|
112
|
+
try {
|
|
113
|
+
const { status, body } = await callServer("/apply", {
|
|
114
|
+
method: "POST",
|
|
115
|
+
body: args
|
|
116
|
+
});
|
|
117
|
+
if (status === 200) return ok(JSON.stringify(body));
|
|
118
|
+
return fail(`HTTP ${status}: ${JSON.stringify(body)}`);
|
|
119
|
+
} catch (err) {
|
|
120
|
+
return fail(`Could not reach visual-editor server: ${err.message}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
server.registerTool(
|
|
125
|
+
"revert_change",
|
|
126
|
+
{
|
|
127
|
+
description: "Undo a previously applied mutation. Call with no args to undo the most-recent apply, or pass {file, line, col} to undo a specific older one. Returns 404 if the history buffer doesn't contain a matching entry.",
|
|
128
|
+
inputSchema: {
|
|
129
|
+
file: z.string().optional(),
|
|
130
|
+
line: z.number().int().positive().optional(),
|
|
131
|
+
col: z.number().int().min(0).optional()
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
async (args) => {
|
|
135
|
+
try {
|
|
136
|
+
const { status, body } = await callServer("/revert", {
|
|
137
|
+
method: "POST",
|
|
138
|
+
body: args
|
|
139
|
+
});
|
|
140
|
+
if (status === 200) return ok(JSON.stringify(body));
|
|
141
|
+
return fail(`HTTP ${status}: ${JSON.stringify(body)}`);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
return fail(`Could not reach visual-editor server: ${err.message}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
server.registerTool(
|
|
148
|
+
"apply_styled_property",
|
|
149
|
+
{
|
|
150
|
+
description: "Set a CSS property on a styled-components definition. Resolves `<Button>` to its same-file `const Button = styled.tagname\\`...\\`` and updates or inserts the property in the template's static text. Refuses on `${...}` interpolations, `.attrs()` / `.withConfig()` chains, `styled(Base)` extension form, or cross-file styled definitions.",
|
|
151
|
+
inputSchema: {
|
|
152
|
+
file: z.string().describe("Workspace-relative path to the JSX file (must also contain the styled defn)"),
|
|
153
|
+
line: z.number().int().positive(),
|
|
154
|
+
col: z.number().int().min(0),
|
|
155
|
+
property: z.string().describe("CSS property to set, e.g. 'padding', 'background'"),
|
|
156
|
+
value: z.string().describe("New value, e.g. '1.5rem', '#fff'")
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
async (args) => {
|
|
160
|
+
try {
|
|
161
|
+
const { status, body } = await callServer("/apply-styled-prop", {
|
|
162
|
+
method: "POST",
|
|
163
|
+
body: args
|
|
164
|
+
});
|
|
165
|
+
if (status === 200) return ok(JSON.stringify(body));
|
|
166
|
+
return fail(`HTTP ${status}: ${JSON.stringify(body)}`);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
return fail(`Could not reach visual-editor server: ${err.message}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
server.registerTool(
|
|
173
|
+
"apply_css_property",
|
|
174
|
+
{
|
|
175
|
+
description: "Set a CSS property on the rule referenced by a JSX element's CSS Module className. Resolves `<div className={styles.foo}>` to its `./Foo.module.css` file, finds the `.foo` rule, and updates or inserts the property. Refuses when the className isn't a `{identifier.property}` member expression, when the rule has a `composes:` chain (could leak), or when the import isn't a `.module.css` file.",
|
|
176
|
+
inputSchema: {
|
|
177
|
+
file: z.string().describe("Workspace-relative path to the JSX file containing the element"),
|
|
178
|
+
line: z.number().int().positive(),
|
|
179
|
+
col: z.number().int().min(0),
|
|
180
|
+
property: z.string().describe("CSS property to set, e.g. 'padding', 'background-color'"),
|
|
181
|
+
value: z.string().describe("New value, e.g. '1.5rem', '#fff'")
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
async (args) => {
|
|
185
|
+
try {
|
|
186
|
+
const { status, body } = await callServer("/apply-css-prop", {
|
|
187
|
+
method: "POST",
|
|
188
|
+
body: args
|
|
189
|
+
});
|
|
190
|
+
if (status === 200) return ok(JSON.stringify(body));
|
|
191
|
+
return fail(`HTTP ${status}: ${JSON.stringify(body)}`);
|
|
192
|
+
} catch (err) {
|
|
193
|
+
return fail(`Could not reach visual-editor server: ${err.message}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
var transport = new StdioServerTransport();
|
|
198
|
+
await server.connect(transport);
|
|
199
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Visual-edit MCP stdio server.\n *\n * Thin proxy to the local HTTP server (default http://127.0.0.1:7790).\n * Exposes four tools to Claude Code (or any MCP client):\n *\n * - get_selected_element : read the overlay's current selection\n * - propose_change : diff a className mutation without writing\n * - apply_change : write the mutation to disk\n * - revert_change : undo the most-recent apply (or by location)\n *\n * IMPORTANT: never call `console.log` or write to stdout outside of the\n * MCP transport. Stdout is the JSON-RPC channel and any extra bytes corrupt\n * the framing. Diagnostics go to stderr via `console.error`.\n *\n * Register with Claude Code:\n * claude mcp add visual-editor -- node --import tsx /abs/path/packages/mcp/src/server.ts\n */\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nconst SERVER_URL =\n process.env.VISUAL_EDITOR_SERVER_URL ?? \"http://127.0.0.1:7790\";\n\n// Token resolution: explicit env > workspace session.json > GET /token > none.\n// The workspace path is the same one the HTTP server's CLI was started with;\n// MCP clients pass it via VISUAL_EDITOR_WORKSPACE_ROOT.\nasync function resolveToken(): Promise<string | null> {\n if (process.env.VISUAL_EDITOR_TOKEN) return process.env.VISUAL_EDITOR_TOKEN;\n\n const workspace =\n process.env.VISUAL_EDITOR_WORKSPACE_ROOT ?? process.cwd();\n try {\n const filePath = path.join(workspace, \".visual-editor\", \"session.json\");\n const json = JSON.parse(await fs.readFile(filePath, \"utf8\")) as {\n token?: unknown;\n };\n if (typeof json.token === \"string\") return json.token;\n } catch {\n /* fall through to /token endpoint */\n }\n\n try {\n const r = await fetch(`${SERVER_URL}/token`);\n if (!r.ok) return null;\n const body = (await r.json()) as { token?: string };\n return typeof body.token === \"string\" ? body.token : null;\n } catch {\n return null;\n }\n}\n\nlet cachedToken: string | null = null;\nasync function getToken(): Promise<string | null> {\n if (cachedToken) return cachedToken;\n cachedToken = await resolveToken();\n return cachedToken;\n}\n\nconst server = new McpServer({\n name: \"visual-editor\",\n version: \"0.0.1\",\n});\n\n// Shared helpers ------------------------------------------------------------\n\ntype ToolResult = {\n content: Array<{ type: \"text\"; text: string }>;\n isError?: boolean;\n};\n\nfunction ok(text: string): ToolResult {\n return { content: [{ type: \"text\", text }] };\n}\n\nfunction fail(text: string): ToolResult {\n return { content: [{ type: \"text\", text }], isError: true };\n}\n\nasync function callServer(\n path: string,\n init: { method: string; body?: unknown },\n): Promise<{ status: number; body: unknown }> {\n const headers: Record<string, string> =\n init.body !== undefined ? { \"Content-Type\": \"application/json\" } : {};\n const token = await getToken();\n if (token) headers.Authorization = `Bearer ${token}`;\n const res = await fetch(`${SERVER_URL}${path}`, {\n method: init.method,\n headers,\n body: init.body !== undefined ? JSON.stringify(init.body) : undefined,\n });\n const text = await res.text();\n let body: unknown = text;\n try {\n body = JSON.parse(text);\n } catch {\n /* not JSON; keep raw text */\n }\n return { status: res.status, body };\n}\n\n// Tools ---------------------------------------------------------------------\n\nserver.registerTool(\n \"get_selected_element\",\n {\n description:\n \"Read the element the user has currently selected in the browser overlay. \" +\n \"Returns the source location (file, line, col), data-oid, className string, \" +\n \"tag name, component name, and how many DOM instances share this source (Principle 11). \" +\n \"Returns selection=null when nothing is selected.\",\n inputSchema: {},\n },\n async (): Promise<ToolResult> => {\n try {\n const { status, body } = await callServer(\"/selection\", { method: \"GET\" });\n if (status !== 200) return fail(`HTTP ${status}: ${JSON.stringify(body)}`);\n return ok(JSON.stringify(body));\n } catch (err) {\n return fail(\n `Could not reach visual-editor server at ${SERVER_URL}: ${(err as Error).message}. ` +\n `Is it running? \\`npx tsx packages/server/src/cli.ts --port 7790 --workspace .\\``,\n );\n }\n },\n);\n\nconst mutationInputShape = {\n file: z.string().describe(\"Workspace-relative file path (from data-oid).\"),\n line: z.number().int().positive().describe(\"1-based JSXOpeningElement line.\"),\n col: z.number().int().min(0).describe(\"0-based JSXOpeningElement column.\"),\n before: z\n .string()\n .describe(\"Exact static className token to swap, e.g. 'p-4'.\"),\n after: z.string().describe(\"Replacement token, e.g. 'p-6'.\"),\n};\n\nserver.registerTool(\n \"propose_change\",\n {\n description:\n \"Diff a className mutation WITHOUT writing it. Returns a unified diff for review. \" +\n \"Refuses dynamic-className contexts (cn/clsx/twMerge/cva/spread/template-literal/conditional) \" +\n \"with a structured reason — see the `details` field on errors.\",\n inputSchema: mutationInputShape,\n },\n async (args): Promise<ToolResult> => {\n try {\n const { status, body } = await callServer(\"/propose\", {\n method: \"POST\",\n body: args,\n });\n if (status === 200) return ok(JSON.stringify(body));\n return fail(`HTTP ${status}: ${JSON.stringify(body)}`);\n } catch (err) {\n return fail(`Could not reach visual-editor server: ${(err as Error).message}`);\n }\n },\n);\n\nserver.registerTool(\n \"apply_change\",\n {\n description:\n \"Apply a className mutation to disk. Conflict-checked: returns 409 if the file's \" +\n \"current state no longer contains `before` at line:col (i.e. the IDE edited it \" +\n \"between drag and apply). The successful apply is recorded in the revert history.\",\n inputSchema: mutationInputShape,\n },\n async (args): Promise<ToolResult> => {\n try {\n const { status, body } = await callServer(\"/apply\", {\n method: \"POST\",\n body: args,\n });\n if (status === 200) return ok(JSON.stringify(body));\n return fail(`HTTP ${status}: ${JSON.stringify(body)}`);\n } catch (err) {\n return fail(`Could not reach visual-editor server: ${(err as Error).message}`);\n }\n },\n);\n\nserver.registerTool(\n \"revert_change\",\n {\n description:\n \"Undo a previously applied mutation. Call with no args to undo the most-recent \" +\n \"apply, or pass {file, line, col} to undo a specific older one. Returns 404 if \" +\n \"the history buffer doesn't contain a matching entry.\",\n inputSchema: {\n file: z.string().optional(),\n line: z.number().int().positive().optional(),\n col: z.number().int().min(0).optional(),\n },\n },\n async (args): Promise<ToolResult> => {\n try {\n const { status, body } = await callServer(\"/revert\", {\n method: \"POST\",\n body: args,\n });\n if (status === 200) return ok(JSON.stringify(body));\n return fail(`HTTP ${status}: ${JSON.stringify(body)}`);\n } catch (err) {\n return fail(`Could not reach visual-editor server: ${(err as Error).message}`);\n }\n },\n);\n\nserver.registerTool(\n \"apply_styled_property\",\n {\n description:\n \"Set a CSS property on a styled-components definition. Resolves `<Button>` \" +\n \"to its same-file `const Button = styled.tagname\\\\`...\\\\`` and updates or \" +\n \"inserts the property in the template's static text. Refuses on `${...}` \" +\n \"interpolations, `.attrs()` / `.withConfig()` chains, `styled(Base)` \" +\n \"extension form, or cross-file styled definitions.\",\n inputSchema: {\n file: z\n .string()\n .describe(\"Workspace-relative path to the JSX file (must also contain the styled defn)\"),\n line: z.number().int().positive(),\n col: z.number().int().min(0),\n property: z\n .string()\n .describe(\"CSS property to set, e.g. 'padding', 'background'\"),\n value: z.string().describe(\"New value, e.g. '1.5rem', '#fff'\"),\n },\n },\n async (args): Promise<ToolResult> => {\n try {\n const { status, body } = await callServer(\"/apply-styled-prop\", {\n method: \"POST\",\n body: args,\n });\n if (status === 200) return ok(JSON.stringify(body));\n return fail(`HTTP ${status}: ${JSON.stringify(body)}`);\n } catch (err) {\n return fail(`Could not reach visual-editor server: ${(err as Error).message}`);\n }\n },\n);\n\nserver.registerTool(\n \"apply_css_property\",\n {\n description:\n \"Set a CSS property on the rule referenced by a JSX element's CSS Module \" +\n \"className. Resolves `<div className={styles.foo}>` to its `./Foo.module.css` \" +\n \"file, finds the `.foo` rule, and updates or inserts the property. Refuses \" +\n \"when the className isn't a `{identifier.property}` member expression, when \" +\n \"the rule has a `composes:` chain (could leak), or when the import isn't a \" +\n \"`.module.css` file.\",\n inputSchema: {\n file: z\n .string()\n .describe(\"Workspace-relative path to the JSX file containing the element\"),\n line: z.number().int().positive(),\n col: z.number().int().min(0),\n property: z\n .string()\n .describe(\"CSS property to set, e.g. 'padding', 'background-color'\"),\n value: z.string().describe(\"New value, e.g. '1.5rem', '#fff'\"),\n },\n },\n async (args): Promise<ToolResult> => {\n try {\n const { status, body } = await callServer(\"/apply-css-prop\", {\n method: \"POST\",\n body: args,\n });\n if (status === 200) return ok(JSON.stringify(body));\n return fail(`HTTP ${status}: ${JSON.stringify(body)}`);\n } catch (err) {\n return fail(`Could not reach visual-editor server: ${(err as Error).message}`);\n }\n },\n);\n\n// Connect ----------------------------------------------------------------\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n// No further output here — anything to stdout would corrupt the JSON-RPC channel.\n"],"mappings":"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","names":["path"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aaqiljamal/visual-editor-mcp",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "stdio MCP server for visual-editor — exposes 6 tools (get_selected_element, propose_change, apply_change, revert_change, apply_css_property, apply_styled_property) to Claude Code.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"visual-editor-mcp": "bin/visual-editor-mcp.cjs"
|
|
8
|
+
},
|
|
9
|
+
"files": ["dist", "bin", "README.md"],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsup",
|
|
12
|
+
"dev": "tsup --watch",
|
|
13
|
+
"start": "node --import tsx src/server.ts",
|
|
14
|
+
"test": "node --import tsx --test --test-timeout=20000 --test-reporter=spec test/smoke.test.ts",
|
|
15
|
+
"typecheck": "tsc --noEmit",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": ["visual-editor", "mcp", "claude-code"],
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"author": "Aaqil Jamal",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/The-Design-Alchemist/visual-editor.git",
|
|
24
|
+
"directory": "packages/mcp"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/The-Design-Alchemist/visual-editor#readme",
|
|
27
|
+
"bugs": "https://github.com/The-Design-Alchemist/visual-editor/issues",
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.18.0",
|
|
36
|
+
"zod": "^3.25.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@aaqiljamal/visual-editor-server": "0.2.0",
|
|
40
|
+
"@types/node": "^22.10.0",
|
|
41
|
+
"tsup": "^8.3.5",
|
|
42
|
+
"tsx": "^4.19.2",
|
|
43
|
+
"typescript": "^5.6.3"
|
|
44
|
+
}
|
|
45
|
+
}
|