@pandacss/mcp 1.11.4 → 1.12.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 +10 -0
- package/bin.js +8 -0
- package/dist/chunk-IBM3OGUU.mjs +320 -0
- package/dist/cli.js +390 -0
- package/dist/cli.mjs +45 -0
- package/dist/index.js +3 -1
- package/dist/index.mjs +8 -314
- package/package.json +15 -10
package/README.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# @pandacss/mcp
|
|
2
|
+
|
|
3
|
+
MCP server for Panda CSS AI assistants.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx -y @pandacss/mcp
|
|
7
|
+
npx -y @pandacss/mcp init --client cursor
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
MCP lives in this package on purpose so `@pandacss/dev` installs do not pull the MCP SDK (and Hono) into every project.
|
package/bin.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
// src/clients.ts
|
|
2
|
+
var MCP_CLIENTS = {
|
|
3
|
+
claude: {
|
|
4
|
+
name: "claude",
|
|
5
|
+
label: "Claude (.mcp.json)",
|
|
6
|
+
configPath: ".mcp.json",
|
|
7
|
+
configKey: "mcpServers"
|
|
8
|
+
},
|
|
9
|
+
cursor: {
|
|
10
|
+
name: "cursor",
|
|
11
|
+
label: "Cursor (.cursor/mcp.json)",
|
|
12
|
+
configPath: ".cursor/mcp.json",
|
|
13
|
+
configKey: "mcpServers"
|
|
14
|
+
},
|
|
15
|
+
vscode: {
|
|
16
|
+
name: "vscode",
|
|
17
|
+
label: "VS Code (.vscode/mcp.json)",
|
|
18
|
+
configPath: ".vscode/mcp.json",
|
|
19
|
+
configKey: "servers"
|
|
20
|
+
},
|
|
21
|
+
windsurf: {
|
|
22
|
+
name: "windsurf",
|
|
23
|
+
label: "Windsurf (.windsurf/mcp.json)",
|
|
24
|
+
configPath: ".windsurf/mcp.json",
|
|
25
|
+
configKey: "mcpServers"
|
|
26
|
+
},
|
|
27
|
+
codex: {
|
|
28
|
+
name: "codex",
|
|
29
|
+
label: "Codex (.codex/mcp.json)",
|
|
30
|
+
configPath: ".codex/mcp.json",
|
|
31
|
+
configKey: "mcpServers"
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var CLIENT_NAMES = Object.keys(MCP_CLIENTS);
|
|
35
|
+
function isValidClient(client) {
|
|
36
|
+
return CLIENT_NAMES.includes(client);
|
|
37
|
+
}
|
|
38
|
+
function getClientConfig(client) {
|
|
39
|
+
return MCP_CLIENTS[client];
|
|
40
|
+
}
|
|
41
|
+
function generateMcpConfig(clientConfig) {
|
|
42
|
+
const serverConfig = {
|
|
43
|
+
command: "npx",
|
|
44
|
+
args: ["-y", "@pandacss/mcp"]
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
[clientConfig.configKey]: {
|
|
48
|
+
panda: serverConfig
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// src/init.ts
|
|
54
|
+
import * as p from "@clack/prompts";
|
|
55
|
+
import { logger } from "@pandacss/logger";
|
|
56
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
57
|
+
import { dirname, resolve } from "path";
|
|
58
|
+
async function initMcpConfig(options = {}) {
|
|
59
|
+
const { cwd = process.cwd() } = options;
|
|
60
|
+
let { clients } = options;
|
|
61
|
+
p.intro("Panda MCP Setup");
|
|
62
|
+
if (!clients || clients.length === 0) {
|
|
63
|
+
const selected = await p.multiselect({
|
|
64
|
+
message: "Select AI clients to configure:",
|
|
65
|
+
options: CLIENT_NAMES.map((client) => ({
|
|
66
|
+
value: client,
|
|
67
|
+
label: MCP_CLIENTS[client].label
|
|
68
|
+
})),
|
|
69
|
+
required: true
|
|
70
|
+
});
|
|
71
|
+
if (p.isCancel(selected)) {
|
|
72
|
+
p.cancel("Setup cancelled.");
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
clients = selected;
|
|
76
|
+
}
|
|
77
|
+
const validClients = clients.filter((client) => {
|
|
78
|
+
if (!isValidClient(client)) {
|
|
79
|
+
logger.warn("mcp:init", `Unknown client: ${client}`);
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
});
|
|
84
|
+
if (validClients.length === 0) {
|
|
85
|
+
p.cancel("No valid clients selected.");
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
const results = [];
|
|
89
|
+
for (const client of validClients) {
|
|
90
|
+
const clientConfig = getClientConfig(client);
|
|
91
|
+
const configPath = resolve(cwd, clientConfig.configPath);
|
|
92
|
+
const configDir = dirname(configPath);
|
|
93
|
+
if (!existsSync(configDir)) {
|
|
94
|
+
mkdirSync(configDir, { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
const newConfig = generateMcpConfig(clientConfig);
|
|
97
|
+
let finalConfig = newConfig;
|
|
98
|
+
if (existsSync(configPath)) {
|
|
99
|
+
try {
|
|
100
|
+
const existingContent = readFileSync(configPath, "utf-8");
|
|
101
|
+
const existingConfig = JSON.parse(existingContent);
|
|
102
|
+
finalConfig = {
|
|
103
|
+
...existingConfig,
|
|
104
|
+
[clientConfig.configKey]: {
|
|
105
|
+
...existingConfig[clientConfig.configKey],
|
|
106
|
+
...newConfig[clientConfig.configKey]
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
} catch {
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
writeFileSync(configPath, JSON.stringify(finalConfig, null, 2));
|
|
113
|
+
results.push({
|
|
114
|
+
client,
|
|
115
|
+
path: clientConfig.configPath,
|
|
116
|
+
created: true
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
p.note(results.map((r) => `${r.client}: ${r.path}`).join("\n"), "Created MCP configurations");
|
|
120
|
+
p.outro("MCP setup complete! Your AI assistants can now use Panda CSS tools.");
|
|
121
|
+
return results;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/server.ts
|
|
125
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
126
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
127
|
+
import { logger as logger2 } from "@pandacss/logger";
|
|
128
|
+
import { analyze, loadConfigAndCreateContext } from "@pandacss/node";
|
|
129
|
+
import { TOKEN_CATEGORIES } from "@pandacss/token-dictionary";
|
|
130
|
+
import { resolve as resolve2 } from "path";
|
|
131
|
+
import * as z from "zod/v4";
|
|
132
|
+
var tokenCategorySchema = z.enum(TOKEN_CATEGORIES).optional().describe("Filter by token category");
|
|
133
|
+
var json = (data) => ({
|
|
134
|
+
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
135
|
+
});
|
|
136
|
+
function createMcpServer(options) {
|
|
137
|
+
const { ctx } = options;
|
|
138
|
+
const server = new McpServer({
|
|
139
|
+
name: "@pandacss/mcp",
|
|
140
|
+
version: "1.0.0"
|
|
141
|
+
});
|
|
142
|
+
const recipeNames = ctx.recipes.keys;
|
|
143
|
+
const patternNames = ctx.patterns.keys;
|
|
144
|
+
const recipeNameSchema = recipeNames.length ? z.enum(recipeNames).optional().describe("Filter by recipe name") : z.string().optional().describe("Filter by recipe name");
|
|
145
|
+
const patternNameSchema = patternNames.length ? z.enum(patternNames).optional().describe("Filter by pattern name") : z.string().optional().describe("Filter by pattern name");
|
|
146
|
+
server.registerTool(
|
|
147
|
+
"get_tokens",
|
|
148
|
+
{
|
|
149
|
+
description: "Get all design tokens with their values, CSS variables, and usage examples",
|
|
150
|
+
inputSchema: { category: tokenCategorySchema }
|
|
151
|
+
},
|
|
152
|
+
async ({ category }) => {
|
|
153
|
+
const spec = ctx.getSpecOfType("tokens");
|
|
154
|
+
const data = category ? spec.data.filter((group) => group.type === category) : spec.data;
|
|
155
|
+
return json({ type: spec.type, data });
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
server.registerTool(
|
|
159
|
+
"get_semantic_tokens",
|
|
160
|
+
{
|
|
161
|
+
description: "Get semantic tokens with their conditional values (responsive, color modes)",
|
|
162
|
+
inputSchema: { category: tokenCategorySchema }
|
|
163
|
+
},
|
|
164
|
+
async ({ category }) => {
|
|
165
|
+
const spec = ctx.getSpecOfType("semantic-tokens");
|
|
166
|
+
const data = category ? spec.data.filter((group) => group.type === category) : spec.data;
|
|
167
|
+
return json({ type: spec.type, data });
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
server.registerTool(
|
|
171
|
+
"get_recipes",
|
|
172
|
+
{
|
|
173
|
+
description: "Get component recipes with their variants, default values, and usage examples",
|
|
174
|
+
inputSchema: { name: recipeNameSchema }
|
|
175
|
+
},
|
|
176
|
+
async ({ name }) => {
|
|
177
|
+
const spec = ctx.getSpecOfType("recipes");
|
|
178
|
+
const data = name ? spec.data.filter((item) => item.name === name) : spec.data;
|
|
179
|
+
return json({ type: spec.type, data });
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
server.registerTool(
|
|
183
|
+
"get_patterns",
|
|
184
|
+
{
|
|
185
|
+
description: "Get layout patterns with their properties and usage examples",
|
|
186
|
+
inputSchema: { name: patternNameSchema }
|
|
187
|
+
},
|
|
188
|
+
async ({ name }) => {
|
|
189
|
+
const spec = ctx.getSpecOfType("patterns");
|
|
190
|
+
const data = name ? spec.data.filter((item) => item.name === name) : spec.data;
|
|
191
|
+
return json({ type: spec.type, data });
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
server.registerTool(
|
|
195
|
+
"get_conditions",
|
|
196
|
+
{ description: "Get all conditions (breakpoints, pseudo-classes, color modes) and their CSS values" },
|
|
197
|
+
async () => json(ctx.getSpecOfType("conditions"))
|
|
198
|
+
);
|
|
199
|
+
server.registerTool(
|
|
200
|
+
"get_keyframes",
|
|
201
|
+
{ description: "Get keyframe animations defined in the theme" },
|
|
202
|
+
async () => json(ctx.getSpecOfType("keyframes"))
|
|
203
|
+
);
|
|
204
|
+
server.registerTool(
|
|
205
|
+
"get_text_styles",
|
|
206
|
+
{ description: "Get text style compositions for typography" },
|
|
207
|
+
async () => json(ctx.getSpecOfType("text-styles"))
|
|
208
|
+
);
|
|
209
|
+
server.registerTool(
|
|
210
|
+
"get_layer_styles",
|
|
211
|
+
{ description: "Get layer style compositions for visual styling" },
|
|
212
|
+
async () => json(ctx.getSpecOfType("layer-styles"))
|
|
213
|
+
);
|
|
214
|
+
server.registerTool(
|
|
215
|
+
"get_animation_styles",
|
|
216
|
+
{ description: "Get animation style compositions" },
|
|
217
|
+
async () => json(ctx.getSpecOfType("animation-styles"))
|
|
218
|
+
);
|
|
219
|
+
server.registerTool(
|
|
220
|
+
"get_color_palette",
|
|
221
|
+
{ description: "Get the color palette with all color values" },
|
|
222
|
+
async () => json(ctx.getSpecOfType("color-palette"))
|
|
223
|
+
);
|
|
224
|
+
server.registerTool(
|
|
225
|
+
"get_config",
|
|
226
|
+
{ description: "Get the resolved Panda CSS configuration including paths, JSX settings, and output options" },
|
|
227
|
+
async () => json(ctx.config)
|
|
228
|
+
);
|
|
229
|
+
server.registerTool(
|
|
230
|
+
"get_usage_report",
|
|
231
|
+
{
|
|
232
|
+
description: "Get a usage report of design tokens and recipes across the codebase. Shows which tokens/recipes are used, unused, or missing. Useful for auditing, cleanup, and identifying dead code.",
|
|
233
|
+
inputSchema: {
|
|
234
|
+
scope: z.enum(["all", "token", "recipe"]).optional().describe("Analysis scope: token, recipe, or all (default)")
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
async ({ scope }) => {
|
|
238
|
+
const result = analyze(ctx);
|
|
239
|
+
const includeTokens = !scope || scope === "all" || scope === "token";
|
|
240
|
+
const includeRecipes = !scope || scope === "all" || scope === "recipe";
|
|
241
|
+
const report = {};
|
|
242
|
+
if (includeTokens && !ctx.tokens.isEmpty) {
|
|
243
|
+
const tokenReport = result.getTokenReport();
|
|
244
|
+
report.tokens = tokenReport.report.getSummary();
|
|
245
|
+
}
|
|
246
|
+
if (includeRecipes && !ctx.recipes.isEmpty()) {
|
|
247
|
+
const recipeReport = result.getRecipeReport();
|
|
248
|
+
report.recipes = recipeReport.report;
|
|
249
|
+
}
|
|
250
|
+
return json(report);
|
|
251
|
+
}
|
|
252
|
+
);
|
|
253
|
+
return server;
|
|
254
|
+
}
|
|
255
|
+
async function startMcpServer(options = {}) {
|
|
256
|
+
const { cwd = process.cwd(), config: configPath, silent = false, transport } = options;
|
|
257
|
+
if (silent) {
|
|
258
|
+
logger2.level = "silent";
|
|
259
|
+
}
|
|
260
|
+
const resolvedCwd = resolve2(cwd);
|
|
261
|
+
const resolvedConfigPath = configPath ? resolve2(configPath) : void 0;
|
|
262
|
+
const ctx = await loadConfigAndCreateContext({
|
|
263
|
+
cwd: resolvedCwd,
|
|
264
|
+
configPath: resolvedConfigPath
|
|
265
|
+
});
|
|
266
|
+
const server = createMcpServer({ ctx });
|
|
267
|
+
const serverTransport = transport ?? new StdioServerTransport();
|
|
268
|
+
await server.connect(serverTransport);
|
|
269
|
+
if (!transport) {
|
|
270
|
+
watchProcessLifecycle(server);
|
|
271
|
+
}
|
|
272
|
+
console.error("Panda CSS MCP server started");
|
|
273
|
+
console.error(`Working directory: ${resolvedCwd}`);
|
|
274
|
+
if (resolvedConfigPath) {
|
|
275
|
+
console.error(`Config path: ${resolvedConfigPath}`);
|
|
276
|
+
}
|
|
277
|
+
return server;
|
|
278
|
+
}
|
|
279
|
+
function watchProcessLifecycle(server, options = {}) {
|
|
280
|
+
const proc = options.process ?? process;
|
|
281
|
+
const exit = options.exit ?? ((code) => process.exit(code));
|
|
282
|
+
let exiting = false;
|
|
283
|
+
const shutdown = (code) => {
|
|
284
|
+
if (exiting) return;
|
|
285
|
+
exiting = true;
|
|
286
|
+
void Promise.resolve(server.close()).catch(() => void 0);
|
|
287
|
+
exit(code);
|
|
288
|
+
};
|
|
289
|
+
proc.stdin.on("end", () => shutdown(0));
|
|
290
|
+
proc.stdin.on("close", () => shutdown(0));
|
|
291
|
+
proc.on("SIGINT", () => shutdown(0));
|
|
292
|
+
proc.on("SIGTERM", () => shutdown(0));
|
|
293
|
+
for (const stream of [proc.stdout, proc.stderr]) {
|
|
294
|
+
stream.on("error", (err) => {
|
|
295
|
+
if (err?.code === "EPIPE") shutdown(0);
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
const onFatal = (label) => (err) => {
|
|
299
|
+
try {
|
|
300
|
+
proc.stderr.write(`[panda mcp] ${label}, exiting: ${formatError(err)}
|
|
301
|
+
`);
|
|
302
|
+
} catch {
|
|
303
|
+
}
|
|
304
|
+
shutdown(1);
|
|
305
|
+
};
|
|
306
|
+
proc.on("uncaughtException", onFatal("uncaught exception"));
|
|
307
|
+
proc.on("unhandledRejection", onFatal("unhandled rejection"));
|
|
308
|
+
return shutdown;
|
|
309
|
+
}
|
|
310
|
+
function formatError(err) {
|
|
311
|
+
return err instanceof Error ? err.stack ?? err.message : String(err);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export {
|
|
315
|
+
MCP_CLIENTS,
|
|
316
|
+
generateMcpConfig,
|
|
317
|
+
initMcpConfig,
|
|
318
|
+
createMcpServer,
|
|
319
|
+
startMcpServer
|
|
320
|
+
};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/cli.ts
|
|
31
|
+
var cli_exports = {};
|
|
32
|
+
__export(cli_exports, {
|
|
33
|
+
main: () => main
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(cli_exports);
|
|
36
|
+
var import_cac = require("cac");
|
|
37
|
+
var import_path3 = require("path");
|
|
38
|
+
|
|
39
|
+
// package.json
|
|
40
|
+
var version = "1.12.0";
|
|
41
|
+
|
|
42
|
+
// src/init.ts
|
|
43
|
+
var p = __toESM(require("@clack/prompts"));
|
|
44
|
+
var import_logger = require("@pandacss/logger");
|
|
45
|
+
var import_fs = require("fs");
|
|
46
|
+
var import_path = require("path");
|
|
47
|
+
|
|
48
|
+
// src/clients.ts
|
|
49
|
+
var MCP_CLIENTS = {
|
|
50
|
+
claude: {
|
|
51
|
+
name: "claude",
|
|
52
|
+
label: "Claude (.mcp.json)",
|
|
53
|
+
configPath: ".mcp.json",
|
|
54
|
+
configKey: "mcpServers"
|
|
55
|
+
},
|
|
56
|
+
cursor: {
|
|
57
|
+
name: "cursor",
|
|
58
|
+
label: "Cursor (.cursor/mcp.json)",
|
|
59
|
+
configPath: ".cursor/mcp.json",
|
|
60
|
+
configKey: "mcpServers"
|
|
61
|
+
},
|
|
62
|
+
vscode: {
|
|
63
|
+
name: "vscode",
|
|
64
|
+
label: "VS Code (.vscode/mcp.json)",
|
|
65
|
+
configPath: ".vscode/mcp.json",
|
|
66
|
+
configKey: "servers"
|
|
67
|
+
},
|
|
68
|
+
windsurf: {
|
|
69
|
+
name: "windsurf",
|
|
70
|
+
label: "Windsurf (.windsurf/mcp.json)",
|
|
71
|
+
configPath: ".windsurf/mcp.json",
|
|
72
|
+
configKey: "mcpServers"
|
|
73
|
+
},
|
|
74
|
+
codex: {
|
|
75
|
+
name: "codex",
|
|
76
|
+
label: "Codex (.codex/mcp.json)",
|
|
77
|
+
configPath: ".codex/mcp.json",
|
|
78
|
+
configKey: "mcpServers"
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var CLIENT_NAMES = Object.keys(MCP_CLIENTS);
|
|
82
|
+
function isValidClient(client) {
|
|
83
|
+
return CLIENT_NAMES.includes(client);
|
|
84
|
+
}
|
|
85
|
+
function getClientConfig(client) {
|
|
86
|
+
return MCP_CLIENTS[client];
|
|
87
|
+
}
|
|
88
|
+
function generateMcpConfig(clientConfig) {
|
|
89
|
+
const serverConfig = {
|
|
90
|
+
command: "npx",
|
|
91
|
+
args: ["-y", "@pandacss/mcp"]
|
|
92
|
+
};
|
|
93
|
+
return {
|
|
94
|
+
[clientConfig.configKey]: {
|
|
95
|
+
panda: serverConfig
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/init.ts
|
|
101
|
+
async function initMcpConfig(options = {}) {
|
|
102
|
+
const { cwd = process.cwd() } = options;
|
|
103
|
+
let { clients } = options;
|
|
104
|
+
p.intro("Panda MCP Setup");
|
|
105
|
+
if (!clients || clients.length === 0) {
|
|
106
|
+
const selected = await p.multiselect({
|
|
107
|
+
message: "Select AI clients to configure:",
|
|
108
|
+
options: CLIENT_NAMES.map((client) => ({
|
|
109
|
+
value: client,
|
|
110
|
+
label: MCP_CLIENTS[client].label
|
|
111
|
+
})),
|
|
112
|
+
required: true
|
|
113
|
+
});
|
|
114
|
+
if (p.isCancel(selected)) {
|
|
115
|
+
p.cancel("Setup cancelled.");
|
|
116
|
+
process.exit(0);
|
|
117
|
+
}
|
|
118
|
+
clients = selected;
|
|
119
|
+
}
|
|
120
|
+
const validClients = clients.filter((client) => {
|
|
121
|
+
if (!isValidClient(client)) {
|
|
122
|
+
import_logger.logger.warn("mcp:init", `Unknown client: ${client}`);
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
return true;
|
|
126
|
+
});
|
|
127
|
+
if (validClients.length === 0) {
|
|
128
|
+
p.cancel("No valid clients selected.");
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
const results = [];
|
|
132
|
+
for (const client of validClients) {
|
|
133
|
+
const clientConfig = getClientConfig(client);
|
|
134
|
+
const configPath = (0, import_path.resolve)(cwd, clientConfig.configPath);
|
|
135
|
+
const configDir = (0, import_path.dirname)(configPath);
|
|
136
|
+
if (!(0, import_fs.existsSync)(configDir)) {
|
|
137
|
+
(0, import_fs.mkdirSync)(configDir, { recursive: true });
|
|
138
|
+
}
|
|
139
|
+
const newConfig = generateMcpConfig(clientConfig);
|
|
140
|
+
let finalConfig = newConfig;
|
|
141
|
+
if ((0, import_fs.existsSync)(configPath)) {
|
|
142
|
+
try {
|
|
143
|
+
const existingContent = (0, import_fs.readFileSync)(configPath, "utf-8");
|
|
144
|
+
const existingConfig = JSON.parse(existingContent);
|
|
145
|
+
finalConfig = {
|
|
146
|
+
...existingConfig,
|
|
147
|
+
[clientConfig.configKey]: {
|
|
148
|
+
...existingConfig[clientConfig.configKey],
|
|
149
|
+
...newConfig[clientConfig.configKey]
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
} catch {
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
(0, import_fs.writeFileSync)(configPath, JSON.stringify(finalConfig, null, 2));
|
|
156
|
+
results.push({
|
|
157
|
+
client,
|
|
158
|
+
path: clientConfig.configPath,
|
|
159
|
+
created: true
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
p.note(results.map((r) => `${r.client}: ${r.path}`).join("\n"), "Created MCP configurations");
|
|
163
|
+
p.outro("MCP setup complete! Your AI assistants can now use Panda CSS tools.");
|
|
164
|
+
return results;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/server.ts
|
|
168
|
+
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
169
|
+
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
170
|
+
var import_logger2 = require("@pandacss/logger");
|
|
171
|
+
var import_node = require("@pandacss/node");
|
|
172
|
+
var import_token_dictionary = require("@pandacss/token-dictionary");
|
|
173
|
+
var import_path2 = require("path");
|
|
174
|
+
var z = __toESM(require("zod/v4"));
|
|
175
|
+
var tokenCategorySchema = z.enum(import_token_dictionary.TOKEN_CATEGORIES).optional().describe("Filter by token category");
|
|
176
|
+
var json = (data) => ({
|
|
177
|
+
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
178
|
+
});
|
|
179
|
+
function createMcpServer(options) {
|
|
180
|
+
const { ctx } = options;
|
|
181
|
+
const server = new import_mcp.McpServer({
|
|
182
|
+
name: "@pandacss/mcp",
|
|
183
|
+
version: "1.0.0"
|
|
184
|
+
});
|
|
185
|
+
const recipeNames = ctx.recipes.keys;
|
|
186
|
+
const patternNames = ctx.patterns.keys;
|
|
187
|
+
const recipeNameSchema = recipeNames.length ? z.enum(recipeNames).optional().describe("Filter by recipe name") : z.string().optional().describe("Filter by recipe name");
|
|
188
|
+
const patternNameSchema = patternNames.length ? z.enum(patternNames).optional().describe("Filter by pattern name") : z.string().optional().describe("Filter by pattern name");
|
|
189
|
+
server.registerTool(
|
|
190
|
+
"get_tokens",
|
|
191
|
+
{
|
|
192
|
+
description: "Get all design tokens with their values, CSS variables, and usage examples",
|
|
193
|
+
inputSchema: { category: tokenCategorySchema }
|
|
194
|
+
},
|
|
195
|
+
async ({ category }) => {
|
|
196
|
+
const spec = ctx.getSpecOfType("tokens");
|
|
197
|
+
const data = category ? spec.data.filter((group) => group.type === category) : spec.data;
|
|
198
|
+
return json({ type: spec.type, data });
|
|
199
|
+
}
|
|
200
|
+
);
|
|
201
|
+
server.registerTool(
|
|
202
|
+
"get_semantic_tokens",
|
|
203
|
+
{
|
|
204
|
+
description: "Get semantic tokens with their conditional values (responsive, color modes)",
|
|
205
|
+
inputSchema: { category: tokenCategorySchema }
|
|
206
|
+
},
|
|
207
|
+
async ({ category }) => {
|
|
208
|
+
const spec = ctx.getSpecOfType("semantic-tokens");
|
|
209
|
+
const data = category ? spec.data.filter((group) => group.type === category) : spec.data;
|
|
210
|
+
return json({ type: spec.type, data });
|
|
211
|
+
}
|
|
212
|
+
);
|
|
213
|
+
server.registerTool(
|
|
214
|
+
"get_recipes",
|
|
215
|
+
{
|
|
216
|
+
description: "Get component recipes with their variants, default values, and usage examples",
|
|
217
|
+
inputSchema: { name: recipeNameSchema }
|
|
218
|
+
},
|
|
219
|
+
async ({ name }) => {
|
|
220
|
+
const spec = ctx.getSpecOfType("recipes");
|
|
221
|
+
const data = name ? spec.data.filter((item) => item.name === name) : spec.data;
|
|
222
|
+
return json({ type: spec.type, data });
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
server.registerTool(
|
|
226
|
+
"get_patterns",
|
|
227
|
+
{
|
|
228
|
+
description: "Get layout patterns with their properties and usage examples",
|
|
229
|
+
inputSchema: { name: patternNameSchema }
|
|
230
|
+
},
|
|
231
|
+
async ({ name }) => {
|
|
232
|
+
const spec = ctx.getSpecOfType("patterns");
|
|
233
|
+
const data = name ? spec.data.filter((item) => item.name === name) : spec.data;
|
|
234
|
+
return json({ type: spec.type, data });
|
|
235
|
+
}
|
|
236
|
+
);
|
|
237
|
+
server.registerTool(
|
|
238
|
+
"get_conditions",
|
|
239
|
+
{ description: "Get all conditions (breakpoints, pseudo-classes, color modes) and their CSS values" },
|
|
240
|
+
async () => json(ctx.getSpecOfType("conditions"))
|
|
241
|
+
);
|
|
242
|
+
server.registerTool(
|
|
243
|
+
"get_keyframes",
|
|
244
|
+
{ description: "Get keyframe animations defined in the theme" },
|
|
245
|
+
async () => json(ctx.getSpecOfType("keyframes"))
|
|
246
|
+
);
|
|
247
|
+
server.registerTool(
|
|
248
|
+
"get_text_styles",
|
|
249
|
+
{ description: "Get text style compositions for typography" },
|
|
250
|
+
async () => json(ctx.getSpecOfType("text-styles"))
|
|
251
|
+
);
|
|
252
|
+
server.registerTool(
|
|
253
|
+
"get_layer_styles",
|
|
254
|
+
{ description: "Get layer style compositions for visual styling" },
|
|
255
|
+
async () => json(ctx.getSpecOfType("layer-styles"))
|
|
256
|
+
);
|
|
257
|
+
server.registerTool(
|
|
258
|
+
"get_animation_styles",
|
|
259
|
+
{ description: "Get animation style compositions" },
|
|
260
|
+
async () => json(ctx.getSpecOfType("animation-styles"))
|
|
261
|
+
);
|
|
262
|
+
server.registerTool(
|
|
263
|
+
"get_color_palette",
|
|
264
|
+
{ description: "Get the color palette with all color values" },
|
|
265
|
+
async () => json(ctx.getSpecOfType("color-palette"))
|
|
266
|
+
);
|
|
267
|
+
server.registerTool(
|
|
268
|
+
"get_config",
|
|
269
|
+
{ description: "Get the resolved Panda CSS configuration including paths, JSX settings, and output options" },
|
|
270
|
+
async () => json(ctx.config)
|
|
271
|
+
);
|
|
272
|
+
server.registerTool(
|
|
273
|
+
"get_usage_report",
|
|
274
|
+
{
|
|
275
|
+
description: "Get a usage report of design tokens and recipes across the codebase. Shows which tokens/recipes are used, unused, or missing. Useful for auditing, cleanup, and identifying dead code.",
|
|
276
|
+
inputSchema: {
|
|
277
|
+
scope: z.enum(["all", "token", "recipe"]).optional().describe("Analysis scope: token, recipe, or all (default)")
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
async ({ scope }) => {
|
|
281
|
+
const result = (0, import_node.analyze)(ctx);
|
|
282
|
+
const includeTokens = !scope || scope === "all" || scope === "token";
|
|
283
|
+
const includeRecipes = !scope || scope === "all" || scope === "recipe";
|
|
284
|
+
const report = {};
|
|
285
|
+
if (includeTokens && !ctx.tokens.isEmpty) {
|
|
286
|
+
const tokenReport = result.getTokenReport();
|
|
287
|
+
report.tokens = tokenReport.report.getSummary();
|
|
288
|
+
}
|
|
289
|
+
if (includeRecipes && !ctx.recipes.isEmpty()) {
|
|
290
|
+
const recipeReport = result.getRecipeReport();
|
|
291
|
+
report.recipes = recipeReport.report;
|
|
292
|
+
}
|
|
293
|
+
return json(report);
|
|
294
|
+
}
|
|
295
|
+
);
|
|
296
|
+
return server;
|
|
297
|
+
}
|
|
298
|
+
async function startMcpServer(options = {}) {
|
|
299
|
+
const { cwd = process.cwd(), config: configPath, silent = false, transport } = options;
|
|
300
|
+
if (silent) {
|
|
301
|
+
import_logger2.logger.level = "silent";
|
|
302
|
+
}
|
|
303
|
+
const resolvedCwd = (0, import_path2.resolve)(cwd);
|
|
304
|
+
const resolvedConfigPath = configPath ? (0, import_path2.resolve)(configPath) : void 0;
|
|
305
|
+
const ctx = await (0, import_node.loadConfigAndCreateContext)({
|
|
306
|
+
cwd: resolvedCwd,
|
|
307
|
+
configPath: resolvedConfigPath
|
|
308
|
+
});
|
|
309
|
+
const server = createMcpServer({ ctx });
|
|
310
|
+
const serverTransport = transport ?? new import_stdio.StdioServerTransport();
|
|
311
|
+
await server.connect(serverTransport);
|
|
312
|
+
if (!transport) {
|
|
313
|
+
watchProcessLifecycle(server);
|
|
314
|
+
}
|
|
315
|
+
console.error("Panda CSS MCP server started");
|
|
316
|
+
console.error(`Working directory: ${resolvedCwd}`);
|
|
317
|
+
if (resolvedConfigPath) {
|
|
318
|
+
console.error(`Config path: ${resolvedConfigPath}`);
|
|
319
|
+
}
|
|
320
|
+
return server;
|
|
321
|
+
}
|
|
322
|
+
function watchProcessLifecycle(server, options = {}) {
|
|
323
|
+
const proc = options.process ?? process;
|
|
324
|
+
const exit = options.exit ?? ((code) => process.exit(code));
|
|
325
|
+
let exiting = false;
|
|
326
|
+
const shutdown = (code) => {
|
|
327
|
+
if (exiting) return;
|
|
328
|
+
exiting = true;
|
|
329
|
+
void Promise.resolve(server.close()).catch(() => void 0);
|
|
330
|
+
exit(code);
|
|
331
|
+
};
|
|
332
|
+
proc.stdin.on("end", () => shutdown(0));
|
|
333
|
+
proc.stdin.on("close", () => shutdown(0));
|
|
334
|
+
proc.on("SIGINT", () => shutdown(0));
|
|
335
|
+
proc.on("SIGTERM", () => shutdown(0));
|
|
336
|
+
for (const stream of [proc.stdout, proc.stderr]) {
|
|
337
|
+
stream.on("error", (err) => {
|
|
338
|
+
if (err?.code === "EPIPE") shutdown(0);
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
const onFatal = (label) => (err) => {
|
|
342
|
+
try {
|
|
343
|
+
proc.stderr.write(`[panda mcp] ${label}, exiting: ${formatError(err)}
|
|
344
|
+
`);
|
|
345
|
+
} catch {
|
|
346
|
+
}
|
|
347
|
+
shutdown(1);
|
|
348
|
+
};
|
|
349
|
+
proc.on("uncaughtException", onFatal("uncaught exception"));
|
|
350
|
+
proc.on("unhandledRejection", onFatal("unhandled rejection"));
|
|
351
|
+
return shutdown;
|
|
352
|
+
}
|
|
353
|
+
function formatError(err) {
|
|
354
|
+
return err instanceof Error ? err.stack ?? err.message : String(err);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// src/cli.ts
|
|
358
|
+
function parseClients(value) {
|
|
359
|
+
if (!value) return void 0;
|
|
360
|
+
const values = Array.isArray(value) ? value : [value];
|
|
361
|
+
return values.flatMap((client) => client.split(",")).map((client) => client.trim()).filter(Boolean);
|
|
362
|
+
}
|
|
363
|
+
async function main(argv = process.argv.slice(2)) {
|
|
364
|
+
const cli = (0, import_cac.cac)("panda-mcp");
|
|
365
|
+
const cwd = process.cwd();
|
|
366
|
+
cli.command("[command]", "Start MCP server for AI assistants").option("-c, --config <path>", "Path to panda config file").option("--cwd <cwd>", "Current working directory", { default: cwd }).option("--silent", "Suppress startup logs").action(async (command, flags) => {
|
|
367
|
+
if (command) {
|
|
368
|
+
throw new Error(`Unknown command: ${command}`);
|
|
369
|
+
}
|
|
370
|
+
await startMcpServer({
|
|
371
|
+
cwd: (0, import_path3.resolve)(flags.cwd ?? cwd),
|
|
372
|
+
config: flags.config,
|
|
373
|
+
silent: flags.silent
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
cli.command("init", "Initialize MCP configuration for AI clients").option("--cwd <cwd>", "Current working directory", { default: cwd }).option("--client <clients>", "AI clients to configure (claude, cursor, vscode, windsurf, codex)").action(async (flags) => {
|
|
377
|
+
await initMcpConfig({
|
|
378
|
+
cwd: (0, import_path3.resolve)(flags.cwd ?? cwd),
|
|
379
|
+
clients: parseClients(flags.client)
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
cli.help();
|
|
383
|
+
cli.version(version);
|
|
384
|
+
cli.parse(["node", "panda-mcp", ...argv], { run: false });
|
|
385
|
+
await cli.runMatchedCommand();
|
|
386
|
+
}
|
|
387
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
388
|
+
0 && (module.exports = {
|
|
389
|
+
main
|
|
390
|
+
});
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
initMcpConfig,
|
|
3
|
+
startMcpServer
|
|
4
|
+
} from "./chunk-IBM3OGUU.mjs";
|
|
5
|
+
|
|
6
|
+
// src/cli.ts
|
|
7
|
+
import { cac } from "cac";
|
|
8
|
+
import { resolve } from "path";
|
|
9
|
+
|
|
10
|
+
// package.json
|
|
11
|
+
var version = "1.12.0";
|
|
12
|
+
|
|
13
|
+
// src/cli.ts
|
|
14
|
+
function parseClients(value) {
|
|
15
|
+
if (!value) return void 0;
|
|
16
|
+
const values = Array.isArray(value) ? value : [value];
|
|
17
|
+
return values.flatMap((client) => client.split(",")).map((client) => client.trim()).filter(Boolean);
|
|
18
|
+
}
|
|
19
|
+
async function main(argv = process.argv.slice(2)) {
|
|
20
|
+
const cli = cac("panda-mcp");
|
|
21
|
+
const cwd = process.cwd();
|
|
22
|
+
cli.command("[command]", "Start MCP server for AI assistants").option("-c, --config <path>", "Path to panda config file").option("--cwd <cwd>", "Current working directory", { default: cwd }).option("--silent", "Suppress startup logs").action(async (command, flags) => {
|
|
23
|
+
if (command) {
|
|
24
|
+
throw new Error(`Unknown command: ${command}`);
|
|
25
|
+
}
|
|
26
|
+
await startMcpServer({
|
|
27
|
+
cwd: resolve(flags.cwd ?? cwd),
|
|
28
|
+
config: flags.config,
|
|
29
|
+
silent: flags.silent
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
cli.command("init", "Initialize MCP configuration for AI clients").option("--cwd <cwd>", "Current working directory", { default: cwd }).option("--client <clients>", "AI clients to configure (claude, cursor, vscode, windsurf, codex)").action(async (flags) => {
|
|
33
|
+
await initMcpConfig({
|
|
34
|
+
cwd: resolve(flags.cwd ?? cwd),
|
|
35
|
+
clients: parseClients(flags.client)
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
cli.help();
|
|
39
|
+
cli.version(version);
|
|
40
|
+
cli.parse(["node", "panda-mcp", ...argv], { run: false });
|
|
41
|
+
await cli.runMatchedCommand();
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
main
|
|
45
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
MCP_CLIENTS: () => MCP_CLIENTS,
|
|
34
34
|
createMcpServer: () => createMcpServer,
|
|
35
|
+
generateMcpConfig: () => generateMcpConfig,
|
|
35
36
|
initMcpConfig: () => initMcpConfig,
|
|
36
37
|
startMcpServer: () => startMcpServer
|
|
37
38
|
});
|
|
@@ -276,7 +277,7 @@ function getClientConfig(client) {
|
|
|
276
277
|
function generateMcpConfig(clientConfig) {
|
|
277
278
|
const serverConfig = {
|
|
278
279
|
command: "npx",
|
|
279
|
-
args: ["
|
|
280
|
+
args: ["-y", "@pandacss/mcp"]
|
|
280
281
|
};
|
|
281
282
|
return {
|
|
282
283
|
[clientConfig.configKey]: {
|
|
@@ -355,6 +356,7 @@ async function initMcpConfig(options = {}) {
|
|
|
355
356
|
0 && (module.exports = {
|
|
356
357
|
MCP_CLIENTS,
|
|
357
358
|
createMcpServer,
|
|
359
|
+
generateMcpConfig,
|
|
358
360
|
initMcpConfig,
|
|
359
361
|
startMcpServer
|
|
360
362
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -1,320 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import * as z from "zod/v4";
|
|
9
|
-
var tokenCategorySchema = z.enum(TOKEN_CATEGORIES).optional().describe("Filter by token category");
|
|
10
|
-
var json = (data) => ({
|
|
11
|
-
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
12
|
-
});
|
|
13
|
-
function createMcpServer(options) {
|
|
14
|
-
const { ctx } = options;
|
|
15
|
-
const server = new McpServer({
|
|
16
|
-
name: "@pandacss/mcp",
|
|
17
|
-
version: "1.0.0"
|
|
18
|
-
});
|
|
19
|
-
const recipeNames = ctx.recipes.keys;
|
|
20
|
-
const patternNames = ctx.patterns.keys;
|
|
21
|
-
const recipeNameSchema = recipeNames.length ? z.enum(recipeNames).optional().describe("Filter by recipe name") : z.string().optional().describe("Filter by recipe name");
|
|
22
|
-
const patternNameSchema = patternNames.length ? z.enum(patternNames).optional().describe("Filter by pattern name") : z.string().optional().describe("Filter by pattern name");
|
|
23
|
-
server.registerTool(
|
|
24
|
-
"get_tokens",
|
|
25
|
-
{
|
|
26
|
-
description: "Get all design tokens with their values, CSS variables, and usage examples",
|
|
27
|
-
inputSchema: { category: tokenCategorySchema }
|
|
28
|
-
},
|
|
29
|
-
async ({ category }) => {
|
|
30
|
-
const spec = ctx.getSpecOfType("tokens");
|
|
31
|
-
const data = category ? spec.data.filter((group) => group.type === category) : spec.data;
|
|
32
|
-
return json({ type: spec.type, data });
|
|
33
|
-
}
|
|
34
|
-
);
|
|
35
|
-
server.registerTool(
|
|
36
|
-
"get_semantic_tokens",
|
|
37
|
-
{
|
|
38
|
-
description: "Get semantic tokens with their conditional values (responsive, color modes)",
|
|
39
|
-
inputSchema: { category: tokenCategorySchema }
|
|
40
|
-
},
|
|
41
|
-
async ({ category }) => {
|
|
42
|
-
const spec = ctx.getSpecOfType("semantic-tokens");
|
|
43
|
-
const data = category ? spec.data.filter((group) => group.type === category) : spec.data;
|
|
44
|
-
return json({ type: spec.type, data });
|
|
45
|
-
}
|
|
46
|
-
);
|
|
47
|
-
server.registerTool(
|
|
48
|
-
"get_recipes",
|
|
49
|
-
{
|
|
50
|
-
description: "Get component recipes with their variants, default values, and usage examples",
|
|
51
|
-
inputSchema: { name: recipeNameSchema }
|
|
52
|
-
},
|
|
53
|
-
async ({ name }) => {
|
|
54
|
-
const spec = ctx.getSpecOfType("recipes");
|
|
55
|
-
const data = name ? spec.data.filter((item) => item.name === name) : spec.data;
|
|
56
|
-
return json({ type: spec.type, data });
|
|
57
|
-
}
|
|
58
|
-
);
|
|
59
|
-
server.registerTool(
|
|
60
|
-
"get_patterns",
|
|
61
|
-
{
|
|
62
|
-
description: "Get layout patterns with their properties and usage examples",
|
|
63
|
-
inputSchema: { name: patternNameSchema }
|
|
64
|
-
},
|
|
65
|
-
async ({ name }) => {
|
|
66
|
-
const spec = ctx.getSpecOfType("patterns");
|
|
67
|
-
const data = name ? spec.data.filter((item) => item.name === name) : spec.data;
|
|
68
|
-
return json({ type: spec.type, data });
|
|
69
|
-
}
|
|
70
|
-
);
|
|
71
|
-
server.registerTool(
|
|
72
|
-
"get_conditions",
|
|
73
|
-
{ description: "Get all conditions (breakpoints, pseudo-classes, color modes) and their CSS values" },
|
|
74
|
-
async () => json(ctx.getSpecOfType("conditions"))
|
|
75
|
-
);
|
|
76
|
-
server.registerTool(
|
|
77
|
-
"get_keyframes",
|
|
78
|
-
{ description: "Get keyframe animations defined in the theme" },
|
|
79
|
-
async () => json(ctx.getSpecOfType("keyframes"))
|
|
80
|
-
);
|
|
81
|
-
server.registerTool(
|
|
82
|
-
"get_text_styles",
|
|
83
|
-
{ description: "Get text style compositions for typography" },
|
|
84
|
-
async () => json(ctx.getSpecOfType("text-styles"))
|
|
85
|
-
);
|
|
86
|
-
server.registerTool(
|
|
87
|
-
"get_layer_styles",
|
|
88
|
-
{ description: "Get layer style compositions for visual styling" },
|
|
89
|
-
async () => json(ctx.getSpecOfType("layer-styles"))
|
|
90
|
-
);
|
|
91
|
-
server.registerTool(
|
|
92
|
-
"get_animation_styles",
|
|
93
|
-
{ description: "Get animation style compositions" },
|
|
94
|
-
async () => json(ctx.getSpecOfType("animation-styles"))
|
|
95
|
-
);
|
|
96
|
-
server.registerTool(
|
|
97
|
-
"get_color_palette",
|
|
98
|
-
{ description: "Get the color palette with all color values" },
|
|
99
|
-
async () => json(ctx.getSpecOfType("color-palette"))
|
|
100
|
-
);
|
|
101
|
-
server.registerTool(
|
|
102
|
-
"get_config",
|
|
103
|
-
{ description: "Get the resolved Panda CSS configuration including paths, JSX settings, and output options" },
|
|
104
|
-
async () => json(ctx.config)
|
|
105
|
-
);
|
|
106
|
-
server.registerTool(
|
|
107
|
-
"get_usage_report",
|
|
108
|
-
{
|
|
109
|
-
description: "Get a usage report of design tokens and recipes across the codebase. Shows which tokens/recipes are used, unused, or missing. Useful for auditing, cleanup, and identifying dead code.",
|
|
110
|
-
inputSchema: {
|
|
111
|
-
scope: z.enum(["all", "token", "recipe"]).optional().describe("Analysis scope: token, recipe, or all (default)")
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
async ({ scope }) => {
|
|
115
|
-
const result = analyze(ctx);
|
|
116
|
-
const includeTokens = !scope || scope === "all" || scope === "token";
|
|
117
|
-
const includeRecipes = !scope || scope === "all" || scope === "recipe";
|
|
118
|
-
const report = {};
|
|
119
|
-
if (includeTokens && !ctx.tokens.isEmpty) {
|
|
120
|
-
const tokenReport = result.getTokenReport();
|
|
121
|
-
report.tokens = tokenReport.report.getSummary();
|
|
122
|
-
}
|
|
123
|
-
if (includeRecipes && !ctx.recipes.isEmpty()) {
|
|
124
|
-
const recipeReport = result.getRecipeReport();
|
|
125
|
-
report.recipes = recipeReport.report;
|
|
126
|
-
}
|
|
127
|
-
return json(report);
|
|
128
|
-
}
|
|
129
|
-
);
|
|
130
|
-
return server;
|
|
131
|
-
}
|
|
132
|
-
async function startMcpServer(options = {}) {
|
|
133
|
-
const { cwd = process.cwd(), config: configPath, silent = false, transport } = options;
|
|
134
|
-
if (silent) {
|
|
135
|
-
logger.level = "silent";
|
|
136
|
-
}
|
|
137
|
-
const resolvedCwd = resolve(cwd);
|
|
138
|
-
const resolvedConfigPath = configPath ? resolve(configPath) : void 0;
|
|
139
|
-
const ctx = await loadConfigAndCreateContext({
|
|
140
|
-
cwd: resolvedCwd,
|
|
141
|
-
configPath: resolvedConfigPath
|
|
142
|
-
});
|
|
143
|
-
const server = createMcpServer({ ctx });
|
|
144
|
-
const serverTransport = transport ?? new StdioServerTransport();
|
|
145
|
-
await server.connect(serverTransport);
|
|
146
|
-
if (!transport) {
|
|
147
|
-
watchProcessLifecycle(server);
|
|
148
|
-
}
|
|
149
|
-
console.error("Panda CSS MCP server started");
|
|
150
|
-
console.error(`Working directory: ${resolvedCwd}`);
|
|
151
|
-
if (resolvedConfigPath) {
|
|
152
|
-
console.error(`Config path: ${resolvedConfigPath}`);
|
|
153
|
-
}
|
|
154
|
-
return server;
|
|
155
|
-
}
|
|
156
|
-
function watchProcessLifecycle(server, options = {}) {
|
|
157
|
-
const proc = options.process ?? process;
|
|
158
|
-
const exit = options.exit ?? ((code) => process.exit(code));
|
|
159
|
-
let exiting = false;
|
|
160
|
-
const shutdown = (code) => {
|
|
161
|
-
if (exiting) return;
|
|
162
|
-
exiting = true;
|
|
163
|
-
void Promise.resolve(server.close()).catch(() => void 0);
|
|
164
|
-
exit(code);
|
|
165
|
-
};
|
|
166
|
-
proc.stdin.on("end", () => shutdown(0));
|
|
167
|
-
proc.stdin.on("close", () => shutdown(0));
|
|
168
|
-
proc.on("SIGINT", () => shutdown(0));
|
|
169
|
-
proc.on("SIGTERM", () => shutdown(0));
|
|
170
|
-
for (const stream of [proc.stdout, proc.stderr]) {
|
|
171
|
-
stream.on("error", (err) => {
|
|
172
|
-
if (err?.code === "EPIPE") shutdown(0);
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
const onFatal = (label) => (err) => {
|
|
176
|
-
try {
|
|
177
|
-
proc.stderr.write(`[panda mcp] ${label}, exiting: ${formatError(err)}
|
|
178
|
-
`);
|
|
179
|
-
} catch {
|
|
180
|
-
}
|
|
181
|
-
shutdown(1);
|
|
182
|
-
};
|
|
183
|
-
proc.on("uncaughtException", onFatal("uncaught exception"));
|
|
184
|
-
proc.on("unhandledRejection", onFatal("unhandled rejection"));
|
|
185
|
-
return shutdown;
|
|
186
|
-
}
|
|
187
|
-
function formatError(err) {
|
|
188
|
-
return err instanceof Error ? err.stack ?? err.message : String(err);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// src/init.ts
|
|
192
|
-
import * as p from "@clack/prompts";
|
|
193
|
-
import { logger as logger2 } from "@pandacss/logger";
|
|
194
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
195
|
-
import { dirname, resolve as resolve2 } from "path";
|
|
196
|
-
|
|
197
|
-
// src/clients.ts
|
|
198
|
-
var MCP_CLIENTS = {
|
|
199
|
-
claude: {
|
|
200
|
-
name: "claude",
|
|
201
|
-
label: "Claude (.mcp.json)",
|
|
202
|
-
configPath: ".mcp.json",
|
|
203
|
-
configKey: "mcpServers"
|
|
204
|
-
},
|
|
205
|
-
cursor: {
|
|
206
|
-
name: "cursor",
|
|
207
|
-
label: "Cursor (.cursor/mcp.json)",
|
|
208
|
-
configPath: ".cursor/mcp.json",
|
|
209
|
-
configKey: "mcpServers"
|
|
210
|
-
},
|
|
211
|
-
vscode: {
|
|
212
|
-
name: "vscode",
|
|
213
|
-
label: "VS Code (.vscode/mcp.json)",
|
|
214
|
-
configPath: ".vscode/mcp.json",
|
|
215
|
-
configKey: "servers"
|
|
216
|
-
},
|
|
217
|
-
windsurf: {
|
|
218
|
-
name: "windsurf",
|
|
219
|
-
label: "Windsurf (.windsurf/mcp.json)",
|
|
220
|
-
configPath: ".windsurf/mcp.json",
|
|
221
|
-
configKey: "mcpServers"
|
|
222
|
-
},
|
|
223
|
-
codex: {
|
|
224
|
-
name: "codex",
|
|
225
|
-
label: "Codex (.codex/mcp.json)",
|
|
226
|
-
configPath: ".codex/mcp.json",
|
|
227
|
-
configKey: "mcpServers"
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
var CLIENT_NAMES = Object.keys(MCP_CLIENTS);
|
|
231
|
-
function isValidClient(client) {
|
|
232
|
-
return CLIENT_NAMES.includes(client);
|
|
233
|
-
}
|
|
234
|
-
function getClientConfig(client) {
|
|
235
|
-
return MCP_CLIENTS[client];
|
|
236
|
-
}
|
|
237
|
-
function generateMcpConfig(clientConfig) {
|
|
238
|
-
const serverConfig = {
|
|
239
|
-
command: "npx",
|
|
240
|
-
args: ["panda", "mcp"]
|
|
241
|
-
};
|
|
242
|
-
return {
|
|
243
|
-
[clientConfig.configKey]: {
|
|
244
|
-
panda: serverConfig
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// src/init.ts
|
|
250
|
-
async function initMcpConfig(options = {}) {
|
|
251
|
-
const { cwd = process.cwd() } = options;
|
|
252
|
-
let { clients } = options;
|
|
253
|
-
p.intro("Panda MCP Setup");
|
|
254
|
-
if (!clients || clients.length === 0) {
|
|
255
|
-
const selected = await p.multiselect({
|
|
256
|
-
message: "Select AI clients to configure:",
|
|
257
|
-
options: CLIENT_NAMES.map((client) => ({
|
|
258
|
-
value: client,
|
|
259
|
-
label: MCP_CLIENTS[client].label
|
|
260
|
-
})),
|
|
261
|
-
required: true
|
|
262
|
-
});
|
|
263
|
-
if (p.isCancel(selected)) {
|
|
264
|
-
p.cancel("Setup cancelled.");
|
|
265
|
-
process.exit(0);
|
|
266
|
-
}
|
|
267
|
-
clients = selected;
|
|
268
|
-
}
|
|
269
|
-
const validClients = clients.filter((client) => {
|
|
270
|
-
if (!isValidClient(client)) {
|
|
271
|
-
logger2.warn("mcp:init", `Unknown client: ${client}`);
|
|
272
|
-
return false;
|
|
273
|
-
}
|
|
274
|
-
return true;
|
|
275
|
-
});
|
|
276
|
-
if (validClients.length === 0) {
|
|
277
|
-
p.cancel("No valid clients selected.");
|
|
278
|
-
process.exit(1);
|
|
279
|
-
}
|
|
280
|
-
const results = [];
|
|
281
|
-
for (const client of validClients) {
|
|
282
|
-
const clientConfig = getClientConfig(client);
|
|
283
|
-
const configPath = resolve2(cwd, clientConfig.configPath);
|
|
284
|
-
const configDir = dirname(configPath);
|
|
285
|
-
if (!existsSync(configDir)) {
|
|
286
|
-
mkdirSync(configDir, { recursive: true });
|
|
287
|
-
}
|
|
288
|
-
const newConfig = generateMcpConfig(clientConfig);
|
|
289
|
-
let finalConfig = newConfig;
|
|
290
|
-
if (existsSync(configPath)) {
|
|
291
|
-
try {
|
|
292
|
-
const existingContent = readFileSync(configPath, "utf-8");
|
|
293
|
-
const existingConfig = JSON.parse(existingContent);
|
|
294
|
-
finalConfig = {
|
|
295
|
-
...existingConfig,
|
|
296
|
-
[clientConfig.configKey]: {
|
|
297
|
-
...existingConfig[clientConfig.configKey],
|
|
298
|
-
...newConfig[clientConfig.configKey]
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
} catch {
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
writeFileSync(configPath, JSON.stringify(finalConfig, null, 2));
|
|
305
|
-
results.push({
|
|
306
|
-
client,
|
|
307
|
-
path: clientConfig.configPath,
|
|
308
|
-
created: true
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
p.note(results.map((r) => `${r.client}: ${r.path}`).join("\n"), "Created MCP configurations");
|
|
312
|
-
p.outro("MCP setup complete! Your AI assistants can now use Panda CSS tools.");
|
|
313
|
-
return results;
|
|
314
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
MCP_CLIENTS,
|
|
3
|
+
createMcpServer,
|
|
4
|
+
generateMcpConfig,
|
|
5
|
+
initMcpConfig,
|
|
6
|
+
startMcpServer
|
|
7
|
+
} from "./chunk-IBM3OGUU.mjs";
|
|
315
8
|
export {
|
|
316
9
|
MCP_CLIENTS,
|
|
317
10
|
createMcpServer,
|
|
11
|
+
generateMcpConfig,
|
|
318
12
|
initMcpConfig,
|
|
319
13
|
startMcpServer
|
|
320
14
|
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pandacss/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "MCP server for Panda CSS AI assistants",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"panda-mcp": "bin.js"
|
|
10
|
+
},
|
|
8
11
|
"author": "Segun Adebayo <joseshegs@gmail.com>",
|
|
9
12
|
"license": "MIT",
|
|
10
13
|
"exports": {
|
|
@@ -30,23 +33,25 @@
|
|
|
30
33
|
"access": "public"
|
|
31
34
|
},
|
|
32
35
|
"files": [
|
|
33
|
-
"dist"
|
|
36
|
+
"dist",
|
|
37
|
+
"bin.js"
|
|
34
38
|
],
|
|
35
39
|
"dependencies": {
|
|
36
|
-
"@clack/prompts": "
|
|
40
|
+
"@clack/prompts": "1.6.0",
|
|
37
41
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
42
|
+
"cac": "6.7.14",
|
|
38
43
|
"zod": "^4.0.0",
|
|
39
|
-
"@pandacss/logger": "1.
|
|
40
|
-
"@pandacss/node": "1.
|
|
41
|
-
"@pandacss/token-dictionary": "1.
|
|
42
|
-
"@pandacss/types": "1.
|
|
44
|
+
"@pandacss/logger": "1.12.0",
|
|
45
|
+
"@pandacss/node": "1.12.0",
|
|
46
|
+
"@pandacss/token-dictionary": "1.12.0",
|
|
47
|
+
"@pandacss/types": "1.12.0"
|
|
43
48
|
},
|
|
44
49
|
"engines": {
|
|
45
|
-
"node": ">=20"
|
|
50
|
+
"node": ">=20.12.0"
|
|
46
51
|
},
|
|
47
52
|
"scripts": {
|
|
48
|
-
"build": "tsup src/index.ts --format=esm,cjs",
|
|
49
|
-
"build-fast": "tsup src/index.ts --format=esm,cjs",
|
|
53
|
+
"build": "tsup src/index.ts src/cli.ts --format=esm,cjs",
|
|
54
|
+
"build-fast": "tsup src/index.ts src/cli.ts --format=esm,cjs",
|
|
50
55
|
"dev": "pnpm build-fast --watch"
|
|
51
56
|
}
|
|
52
57
|
}
|