@getflip/swirl-mcp 0.3.0 → 0.4.1
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 +27 -11
- package/dist/{chunk-HWC76PLD.js → chunk-XV6F6YUM.js} +221 -11
- package/dist/transports/http.js +1 -1
- package/dist/transports/stdio.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,12 +32,17 @@ Add to your MCP settings:
|
|
|
32
32
|
}
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
## Local
|
|
35
|
+
## Local development
|
|
36
|
+
|
|
37
|
+
Inspect the server with the
|
|
38
|
+
[MCP Inspector](https://github.com/modelcontextprotocol/inspector) over either
|
|
39
|
+
transport. Prepend `SWIRL_AI_LOCAL=1` to either command to load artifacts
|
|
40
|
+
from the local monorepo instead of the unpkg CDN.
|
|
36
41
|
|
|
37
42
|
### stdio
|
|
38
43
|
|
|
39
44
|
```sh
|
|
40
|
-
npx @modelcontextprotocol/inspector node dist/transports/stdio.js
|
|
45
|
+
SWIRL_AI_LOCAL=1 npx @modelcontextprotocol/inspector node dist/transports/stdio.js
|
|
41
46
|
```
|
|
42
47
|
|
|
43
48
|
### HTTP
|
|
@@ -46,20 +51,31 @@ Start the server, then open the Inspector and connect with transport type
|
|
|
46
51
|
"Streamable HTTP" and URL `http://localhost:3000/mcp`:
|
|
47
52
|
|
|
48
53
|
```sh
|
|
49
|
-
npx tsx src/transports/http.ts
|
|
54
|
+
SWIRL_AI_LOCAL=1 npx tsx src/transports/http.ts
|
|
50
55
|
npx @modelcontextprotocol/inspector
|
|
51
56
|
```
|
|
52
57
|
|
|
58
|
+
### `SWIRL_AI_LOCAL`
|
|
59
|
+
|
|
60
|
+
When set, agent artifacts are read from `packages/swirl-ai/dist/agent` and
|
|
61
|
+
component source (`get_component_source`) is read from
|
|
62
|
+
`packages/swirl-components/src/components/<tag>/<tag>.{tsx,css}`. The
|
|
63
|
+
`version` parameter is ignored for cache keying. Make sure swirl-ai has been
|
|
64
|
+
built (`pnpm --filter @getflip/swirl-ai build`).
|
|
65
|
+
|
|
53
66
|
## Tools
|
|
54
67
|
|
|
55
|
-
| Tool
|
|
56
|
-
|
|
|
57
|
-
| **list_components**
|
|
58
|
-
| **list_icons**
|
|
59
|
-
| **list_symbols**
|
|
60
|
-
| **get_component_details**
|
|
61
|
-
| **get_component_source**
|
|
62
|
-
| **
|
|
68
|
+
| Tool | Description |
|
|
69
|
+
| ---------------------------- | ---------------------------------------------------------------------------- |
|
|
70
|
+
| **list_components** | Lists all Swirl UI components with brief summaries and related components. |
|
|
71
|
+
| **list_icons** | Lists all Swirl icon components. |
|
|
72
|
+
| **list_symbols** | Lists all Swirl symbol components. |
|
|
73
|
+
| **get_component_details** | Full component docs: props, events, slots, examples, and accessibility info. |
|
|
74
|
+
| **get_component_source** | Versioned original TSX and CSS source for a specific component. |
|
|
75
|
+
| **list_color_tokens** | Lists Swirl color tokens (light + dark) as CSS / SCSS / Tailwind keys. |
|
|
76
|
+
| **list_typography_tokens** | Lists Swirl typography tokens as CSS / SCSS / Tailwind keys. |
|
|
77
|
+
| **list_layout_tokens** | Lists Swirl layout tokens (spacing, radius, shadow, z-index, blur). |
|
|
78
|
+
| **get_started** | Installation and setup guide for Web Components, Angular, and React. |
|
|
63
79
|
|
|
64
80
|
All tools accept a `version` parameter matching the installed
|
|
65
81
|
`@getflip/swirl-components` version.
|
|
@@ -3,8 +3,67 @@
|
|
|
3
3
|
// src/create-server.ts
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
|
|
6
|
+
// package.json
|
|
7
|
+
var package_default = {
|
|
8
|
+
name: "@getflip/swirl-mcp",
|
|
9
|
+
version: "0.4.1",
|
|
10
|
+
description: "MCP server for Swirl Design System \u2014 lets AI agents discover and use Swirl components",
|
|
11
|
+
author: "Flip GmbH",
|
|
12
|
+
repository: {
|
|
13
|
+
type: "git",
|
|
14
|
+
url: "https://github.com/getflip/swirl"
|
|
15
|
+
},
|
|
16
|
+
license: "SEE LICENSE IN LICENSE.md",
|
|
17
|
+
bugs: {
|
|
18
|
+
url: "https://github.com/getflip/swirl/issues"
|
|
19
|
+
},
|
|
20
|
+
type: "module",
|
|
21
|
+
bin: {
|
|
22
|
+
"swirl-mcp": "dist/transports/stdio.js"
|
|
23
|
+
},
|
|
24
|
+
files: [
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
scripts: {
|
|
29
|
+
build: "tsup",
|
|
30
|
+
start: "node dist/transports/http.js",
|
|
31
|
+
dev: "tsx src/transports/stdio.ts",
|
|
32
|
+
"dev:server": "tsx src/transports/http.ts",
|
|
33
|
+
lint: "tsc --noEmit"
|
|
34
|
+
},
|
|
35
|
+
tsup: {
|
|
36
|
+
entry: {
|
|
37
|
+
"transports/stdio": "src/transports/stdio.ts",
|
|
38
|
+
"transports/http": "src/transports/http.ts"
|
|
39
|
+
},
|
|
40
|
+
format: [
|
|
41
|
+
"esm"
|
|
42
|
+
],
|
|
43
|
+
target: "node18",
|
|
44
|
+
outDir: "dist",
|
|
45
|
+
clean: true,
|
|
46
|
+
banner: {
|
|
47
|
+
js: "#!/usr/bin/env node"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
dependencies: {
|
|
51
|
+
"@modelcontextprotocol/sdk": "1.27.1",
|
|
52
|
+
zod: "3.24.0"
|
|
53
|
+
},
|
|
54
|
+
devDependencies: {
|
|
55
|
+
"@types/node": "25.3.0",
|
|
56
|
+
tsup: "^8.0.0",
|
|
57
|
+
tsx: "^4.7.0",
|
|
58
|
+
typescript: "5.9.3"
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
6
62
|
// src/data-source.ts
|
|
7
|
-
|
|
63
|
+
import { readFileSync } from "fs";
|
|
64
|
+
import { dirname, join, resolve } from "path";
|
|
65
|
+
import { fileURLToPath } from "url";
|
|
66
|
+
var RemoteDataSource = class {
|
|
8
67
|
constructor(version) {
|
|
9
68
|
this.version = version;
|
|
10
69
|
}
|
|
@@ -48,14 +107,43 @@ var DataSource = class {
|
|
|
48
107
|
return `https://raw.githubusercontent.com/getflip/swirl/@getflip/swirl-components@${this.version}`;
|
|
49
108
|
}
|
|
50
109
|
};
|
|
110
|
+
var LocalDataSource = class {
|
|
111
|
+
async readJson(relativePath) {
|
|
112
|
+
return JSON.parse(readFileSync(this.agentPath(relativePath), "utf8"));
|
|
113
|
+
}
|
|
114
|
+
async readText(relativePath) {
|
|
115
|
+
try {
|
|
116
|
+
return readFileSync(this.agentPath(relativePath), "utf8");
|
|
117
|
+
} catch {
|
|
118
|
+
return void 0;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async readComponentSource(tag) {
|
|
122
|
+
const componentDir = join(packagesDir, "swirl-components", "src", "components", tag);
|
|
123
|
+
const read = (ext) => {
|
|
124
|
+
try {
|
|
125
|
+
return readFileSync(join(componentDir, `${tag}.${ext}`), "utf8");
|
|
126
|
+
} catch {
|
|
127
|
+
return void 0;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
return { tsx: read("tsx"), css: read("css") };
|
|
131
|
+
}
|
|
132
|
+
agentPath(relativePath) {
|
|
133
|
+
return join(packagesDir, "swirl-ai", "dist", "agent", relativePath);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
var packagesDir = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
51
137
|
|
|
52
138
|
// src/artifact-library.ts
|
|
53
139
|
var ArtifactLibrary = class _ArtifactLibrary {
|
|
54
140
|
catalog;
|
|
55
141
|
tagIndex;
|
|
142
|
+
tokens;
|
|
56
143
|
dataSource;
|
|
57
|
-
constructor(catalog, dataSource) {
|
|
144
|
+
constructor(catalog, tokens, dataSource) {
|
|
58
145
|
this.catalog = catalog;
|
|
146
|
+
this.tokens = tokens;
|
|
59
147
|
this.dataSource = dataSource;
|
|
60
148
|
this.tagIndex = /* @__PURE__ */ new Map();
|
|
61
149
|
for (const entry of this.catalog) {
|
|
@@ -66,11 +154,15 @@ var ArtifactLibrary = class _ArtifactLibrary {
|
|
|
66
154
|
* Load artifacts from a remote base URL (CDN).
|
|
67
155
|
*/
|
|
68
156
|
static async fromRemote(version) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
157
|
+
return _ArtifactLibrary.fromDataSource(new RemoteDataSource(version));
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Load artifacts from the local monorepo (`packages/swirl-ai/dist` and
|
|
161
|
+
* `packages/swirl-components/src`) for development against an unpublished
|
|
162
|
+
* swirl-ai build.
|
|
163
|
+
*/
|
|
164
|
+
static async fromLocal() {
|
|
165
|
+
return _ArtifactLibrary.fromDataSource(new LocalDataSource());
|
|
74
166
|
}
|
|
75
167
|
getByCategory(category) {
|
|
76
168
|
return this.catalog.filter((c) => categorize(c.tag) === category);
|
|
@@ -87,6 +179,21 @@ var ArtifactLibrary = class _ArtifactLibrary {
|
|
|
87
179
|
async getGuide(name) {
|
|
88
180
|
return this.dataSource.readText(`${name}.md`);
|
|
89
181
|
}
|
|
182
|
+
getTokensByCategory(category) {
|
|
183
|
+
return this.tokens?.[category];
|
|
184
|
+
}
|
|
185
|
+
static async fromDataSource(dataSource) {
|
|
186
|
+
const components = await dataSource.readJson(
|
|
187
|
+
"components-index.json"
|
|
188
|
+
);
|
|
189
|
+
let tokens;
|
|
190
|
+
try {
|
|
191
|
+
tokens = await dataSource.readJson("tokens.json");
|
|
192
|
+
} catch {
|
|
193
|
+
tokens = void 0;
|
|
194
|
+
}
|
|
195
|
+
return new _ArtifactLibrary(components.components, tokens, dataSource);
|
|
196
|
+
}
|
|
90
197
|
};
|
|
91
198
|
function categorize(tag) {
|
|
92
199
|
if (tag.startsWith("swirl-icon-")) {
|
|
@@ -307,19 +414,119 @@ function registerListTool(server, loadLibrary2, name, description, category) {
|
|
|
307
414
|
);
|
|
308
415
|
}
|
|
309
416
|
|
|
417
|
+
// src/tools/list-tokens.ts
|
|
418
|
+
import { z as z5 } from "zod";
|
|
419
|
+
var VERSION_DESCRIPTION5 = "The @getflip/swirl-components version installed in the project. Read the user's package.json or node_modules/@getflip/swirl-components/package.json to find this.";
|
|
420
|
+
var FORMAT_DESCRIPTION = "Output format for the token key. 'css' - CSS custom property. 'scss' - SCSS variable. 'tailwind' - returns `key: '{name}'` + `namespace` (e.g. 'colors', 'fontSize', 'spacing') so the agent can build the right utility class. Pick the format that matches the project's styling stack.";
|
|
421
|
+
function registerListColorTokens(server, loadLibrary2) {
|
|
422
|
+
registerListTokensTool(
|
|
423
|
+
server,
|
|
424
|
+
loadLibrary2,
|
|
425
|
+
"list_color_tokens",
|
|
426
|
+
"List Swirl color tokens with light + dark values. Pass 'format' to choose css / scss / tailwind output. For tailwind, all colors use the 'colors' namespace (e.g. `text-{name}`, `bg-{name}`, `border-{name}`). Prefer these over hard-coded hex/rgb values.",
|
|
427
|
+
"colors"
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
function registerListTypographyTokens(server, loadLibrary2) {
|
|
431
|
+
registerListTokensTool(
|
|
432
|
+
server,
|
|
433
|
+
loadLibrary2,
|
|
434
|
+
"list_typography_tokens",
|
|
435
|
+
"List Swirl typography tokens. Pass 'format' to choose css / scss / tailwind output. Prefer these over ad-hoc font sizes/weights.",
|
|
436
|
+
"typography"
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
function registerListLayoutTokens(server, loadLibrary2) {
|
|
440
|
+
registerListTokensTool(
|
|
441
|
+
server,
|
|
442
|
+
loadLibrary2,
|
|
443
|
+
"list_layout_tokens",
|
|
444
|
+
"List Swirl layout tokens (spacing, border radius, border width, box shadows, z-index, blur). Pass 'format' to choose css / scss / tailwind output. Prefer these over arbitrary px/rem values.",
|
|
445
|
+
"layout"
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
function registerListTokensTool(server, loadLibrary2, name, description, category) {
|
|
449
|
+
server.registerTool(
|
|
450
|
+
name,
|
|
451
|
+
{
|
|
452
|
+
description,
|
|
453
|
+
inputSchema: {
|
|
454
|
+
version: z5.string().describe(VERSION_DESCRIPTION5),
|
|
455
|
+
format: z5.enum(["css", "scss", "tailwind"]).describe(FORMAT_DESCRIPTION)
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
// @ts-ignore - MCP SDK + zod 3.x causes excessively deep type instantiation
|
|
459
|
+
async ({ version, format }) => {
|
|
460
|
+
const lib = await loadLibrary2(version);
|
|
461
|
+
const tokens = lib.getTokensByCategory(category);
|
|
462
|
+
if (tokens === void 0) {
|
|
463
|
+
return {
|
|
464
|
+
isError: true,
|
|
465
|
+
content: [
|
|
466
|
+
{
|
|
467
|
+
type: "text",
|
|
468
|
+
text: `The "${name}" tool is not supported for the @getflip/swirl-components@${version} version`
|
|
469
|
+
}
|
|
470
|
+
]
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
const formatted = tokens.map((t) => formatToken(t, format)).filter((t) => t !== null);
|
|
474
|
+
return {
|
|
475
|
+
content: [{ type: "text", text: JSON.stringify(formatted) }]
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
function formatToken(token, format) {
|
|
481
|
+
if (format === "tailwind" && !token.tailwindNamespace) {
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
const out = { key: tokenKey(token, format) };
|
|
485
|
+
if (format === "tailwind") {
|
|
486
|
+
out.namespace = token.tailwindNamespace;
|
|
487
|
+
}
|
|
488
|
+
if (token.valueLight !== void 0) {
|
|
489
|
+
out.valueLight = token.valueLight;
|
|
490
|
+
out.valueDark = token.valueDark;
|
|
491
|
+
} else if (token.value !== void 0) {
|
|
492
|
+
out.value = token.value;
|
|
493
|
+
}
|
|
494
|
+
if (token.description) {
|
|
495
|
+
out.description = token.description;
|
|
496
|
+
}
|
|
497
|
+
return out;
|
|
498
|
+
}
|
|
499
|
+
function tokenKey(token, format) {
|
|
500
|
+
switch (format) {
|
|
501
|
+
case "css":
|
|
502
|
+
return `--s-${token.name}`;
|
|
503
|
+
case "scss":
|
|
504
|
+
return `$s-${token.name}`;
|
|
505
|
+
case "tailwind":
|
|
506
|
+
return token.name;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
310
510
|
// src/create-server.ts
|
|
311
511
|
var cache = new LibraryCache();
|
|
512
|
+
var useLocal = Boolean(process.env.SWIRL_AI_LOCAL);
|
|
312
513
|
async function loadLibrary(version) {
|
|
313
|
-
const
|
|
514
|
+
const cacheKey = useLocal ? "__local__" : version;
|
|
515
|
+
const cached = cache.get(cacheKey);
|
|
314
516
|
if (cached) {
|
|
315
517
|
return cached;
|
|
316
518
|
}
|
|
317
519
|
try {
|
|
318
|
-
const lib = await ArtifactLibrary.fromRemote(version);
|
|
319
|
-
cache.set(
|
|
520
|
+
const lib = useLocal ? await ArtifactLibrary.fromLocal() : await ArtifactLibrary.fromRemote(version);
|
|
521
|
+
cache.set(cacheKey, lib);
|
|
320
522
|
return lib;
|
|
321
523
|
} catch (error) {
|
|
322
524
|
const message = error instanceof Error ? error.message : String(error);
|
|
525
|
+
if (useLocal) {
|
|
526
|
+
throw new Error(
|
|
527
|
+
`Failed to load local swirl-ai artifacts. Make sure swirl-ai has been built (\`pnpm --filter @getflip/swirl-ai build\`). Details: ${message}`
|
|
528
|
+
);
|
|
529
|
+
}
|
|
323
530
|
throw new Error(
|
|
324
531
|
`Version "${version}" not found or failed to load. Make sure the version matches your installed @getflip/swirl-components version. Details: ${message}`
|
|
325
532
|
);
|
|
@@ -350,7 +557,7 @@ function createMcpServer() {
|
|
|
350
557
|
const server = new McpServer(
|
|
351
558
|
{
|
|
352
559
|
name: "swirl-mcp",
|
|
353
|
-
version:
|
|
560
|
+
version: package_default.version
|
|
354
561
|
},
|
|
355
562
|
{
|
|
356
563
|
instructions: INSTRUCTIONS
|
|
@@ -362,6 +569,9 @@ function createMcpServer() {
|
|
|
362
569
|
registerGetComponentDetails(server, loadLibrary);
|
|
363
570
|
registerGetComponentSource(server, loadLibrary);
|
|
364
571
|
registerGetStarted(server, loadLibrary);
|
|
572
|
+
registerListColorTokens(server, loadLibrary);
|
|
573
|
+
registerListTypographyTokens(server, loadLibrary);
|
|
574
|
+
registerListLayoutTokens(server, loadLibrary);
|
|
365
575
|
return server;
|
|
366
576
|
}
|
|
367
577
|
|
package/dist/transports/http.js
CHANGED
package/dist/transports/stdio.js
CHANGED