@lumeo-ui/mcp-server 2.0.0-rc.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 +129 -0
- package/dist/components.js +709 -0
- package/dist/index.js +357 -0
- package/dist/registry.js +86 -0
- package/package.json +39 -0
- package/src/registry.json +2538 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Lumeo MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Exposes Lumeo's full 125-component catalog (sourced from the generated
|
|
6
|
+
* registry.json, enriched by hand-curated rich entries for the top 35)
|
|
7
|
+
* to MCP-compatible LLM clients (Claude Desktop, Cursor, Copilot, etc.):
|
|
8
|
+
*
|
|
9
|
+
* Tools:
|
|
10
|
+
* - lumeo_list_components — list/filter the full catalog
|
|
11
|
+
* - lumeo_get_component — rich schema when curated, thin otherwise
|
|
12
|
+
* - lumeo_search — fuzzy text search across all 125
|
|
13
|
+
*
|
|
14
|
+
* Resources (URI template):
|
|
15
|
+
* - lumeo://component/{name} — markdown reference per component
|
|
16
|
+
* - lumeo://category/{name} — all components in a category
|
|
17
|
+
*
|
|
18
|
+
* Transport: stdio (the standard for spawned MCP servers).
|
|
19
|
+
*/
|
|
20
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
21
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
22
|
+
import { CallToolRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
23
|
+
import { catalog, CATEGORIES, registry, } from "./components.js";
|
|
24
|
+
const DOCS_BASE = "https://lumeo.nativ.sh";
|
|
25
|
+
// ───────────────── Helpers ─────────────────
|
|
26
|
+
const byName = new Map(catalog.map((c) => [c.name.toLowerCase(), c]));
|
|
27
|
+
function findComponent(name) {
|
|
28
|
+
return byName.get(name.toLowerCase());
|
|
29
|
+
}
|
|
30
|
+
function score(c, q) {
|
|
31
|
+
const needle = q.toLowerCase();
|
|
32
|
+
if (!needle)
|
|
33
|
+
return 0;
|
|
34
|
+
let s = 0;
|
|
35
|
+
if (c.name.toLowerCase() === needle)
|
|
36
|
+
s += 100;
|
|
37
|
+
if (c.name.toLowerCase().startsWith(needle))
|
|
38
|
+
s += 50;
|
|
39
|
+
if (c.name.toLowerCase().includes(needle))
|
|
40
|
+
s += 25;
|
|
41
|
+
if (c.category.toLowerCase().includes(needle))
|
|
42
|
+
s += 10;
|
|
43
|
+
if (c.description.toLowerCase().includes(needle))
|
|
44
|
+
s += 5;
|
|
45
|
+
return s;
|
|
46
|
+
}
|
|
47
|
+
function searchCatalog(query, category) {
|
|
48
|
+
let pool = catalog;
|
|
49
|
+
if (category) {
|
|
50
|
+
pool = pool.filter((c) => c.category.toLowerCase() === category.toLowerCase());
|
|
51
|
+
}
|
|
52
|
+
if (!query)
|
|
53
|
+
return pool;
|
|
54
|
+
return pool
|
|
55
|
+
.map((c) => ({ c, s: score(c, query) }))
|
|
56
|
+
.filter((x) => x.s > 0)
|
|
57
|
+
.sort((a, b) => b.s - a.s)
|
|
58
|
+
.map((x) => x.c);
|
|
59
|
+
}
|
|
60
|
+
function docsUrl(c) {
|
|
61
|
+
return `${DOCS_BASE}/components/${c.slug}`;
|
|
62
|
+
}
|
|
63
|
+
function toRichMarkdown(c) {
|
|
64
|
+
const paramRows = c.params
|
|
65
|
+
.map((p) => `| \`${p.name}\` | \`${p.type}\` | \`${p.default}\` | ${p.description} |`)
|
|
66
|
+
.join("\n");
|
|
67
|
+
const slotRows = c.slots
|
|
68
|
+
.map((s) => `| \`${s.name}\` | ${s.description} |`)
|
|
69
|
+
.join("\n");
|
|
70
|
+
const cssVarsBlock = c.cssVars.length
|
|
71
|
+
? `## CSS Variables\n\n${c.cssVars.map((v) => `- \`${v}\``).join("\n")}\n\n`
|
|
72
|
+
: "";
|
|
73
|
+
return [
|
|
74
|
+
`# ${c.name}`,
|
|
75
|
+
``,
|
|
76
|
+
`**Category:** ${c.category}`,
|
|
77
|
+
``,
|
|
78
|
+
c.description,
|
|
79
|
+
``,
|
|
80
|
+
`## Parameters`,
|
|
81
|
+
``,
|
|
82
|
+
`| Param | Type | Default | Description |`,
|
|
83
|
+
`|---|---|---|---|`,
|
|
84
|
+
paramRows || `| _(none)_ | | | |`,
|
|
85
|
+
``,
|
|
86
|
+
c.slots.length ? `## Slots\n\n| Slot | Description |\n|---|---|\n${slotRows}\n` : ``,
|
|
87
|
+
`## Example`,
|
|
88
|
+
``,
|
|
89
|
+
"```razor",
|
|
90
|
+
c.example,
|
|
91
|
+
"```",
|
|
92
|
+
``,
|
|
93
|
+
cssVarsBlock,
|
|
94
|
+
`_Docs: ${docsUrl(c)}_`,
|
|
95
|
+
].filter(Boolean).join("\n");
|
|
96
|
+
}
|
|
97
|
+
function toThinMarkdown(c) {
|
|
98
|
+
const filesBlock = c.files.length
|
|
99
|
+
? `## Files\n\n${c.files.map((f) => `- \`${f}\``).join("\n")}\n\n`
|
|
100
|
+
: "";
|
|
101
|
+
const cssVarsBlock = c.cssVars.length
|
|
102
|
+
? `## CSS Variables\n\n${c.cssVars.map((v) => `- \`${v}\``).join("\n")}\n\n`
|
|
103
|
+
: "";
|
|
104
|
+
const depsBlock = c.dependencies.length
|
|
105
|
+
? `## Dependencies\n\n${c.dependencies.map((d) => `- \`${d}\``).join("\n")}\n\n`
|
|
106
|
+
: "";
|
|
107
|
+
return [
|
|
108
|
+
`# ${c.name}`,
|
|
109
|
+
``,
|
|
110
|
+
`**Category:** ${c.category}`,
|
|
111
|
+
``,
|
|
112
|
+
c.description,
|
|
113
|
+
``,
|
|
114
|
+
`> Rich schema (parameters, slots, Razor example) is coming soon for this component.`,
|
|
115
|
+
`> See the docs site for full usage: [${docsUrl(c)}](${docsUrl(c)})`,
|
|
116
|
+
``,
|
|
117
|
+
filesBlock,
|
|
118
|
+
depsBlock,
|
|
119
|
+
cssVarsBlock,
|
|
120
|
+
].filter(Boolean).join("\n");
|
|
121
|
+
}
|
|
122
|
+
function toComponentMarkdown(c) {
|
|
123
|
+
return c.thin ? toThinMarkdown(c) : toRichMarkdown(c);
|
|
124
|
+
}
|
|
125
|
+
function toCategoryMarkdown(category) {
|
|
126
|
+
const inCat = catalog.filter((c) => c.category.toLowerCase() === category.toLowerCase());
|
|
127
|
+
if (inCat.length === 0) {
|
|
128
|
+
return `# ${category}\n\nNo components documented in this category yet.`;
|
|
129
|
+
}
|
|
130
|
+
const rows = inCat
|
|
131
|
+
.map((c) => `| [\`${c.name}\`](lumeo://component/${c.name}) | ${c.thin ? "" : "*"}${c.description}${c.thin ? "" : "*"} |`)
|
|
132
|
+
.join("\n");
|
|
133
|
+
return [
|
|
134
|
+
`# ${category}`,
|
|
135
|
+
``,
|
|
136
|
+
`${inCat.length} component${inCat.length === 1 ? "" : "s"}:`,
|
|
137
|
+
``,
|
|
138
|
+
`| Component | Description |`,
|
|
139
|
+
`|---|---|`,
|
|
140
|
+
rows,
|
|
141
|
+
``,
|
|
142
|
+
].join("\n");
|
|
143
|
+
}
|
|
144
|
+
function toListPayload(c) {
|
|
145
|
+
return {
|
|
146
|
+
name: c.name,
|
|
147
|
+
category: c.category,
|
|
148
|
+
description: c.description,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// ───────────────── Server setup ─────────────────
|
|
152
|
+
const server = new Server({
|
|
153
|
+
name: "lumeo-mcp",
|
|
154
|
+
version: "2.0.0",
|
|
155
|
+
}, {
|
|
156
|
+
capabilities: {
|
|
157
|
+
tools: {},
|
|
158
|
+
resources: {},
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
// ───── Tools ─────
|
|
162
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
163
|
+
tools: [
|
|
164
|
+
{
|
|
165
|
+
name: "lumeo_list_components",
|
|
166
|
+
description: `List all ${catalog.length} Lumeo components, optionally filtered by category or a free-text query. ` +
|
|
167
|
+
"Returns an array of { name, category, description }. " +
|
|
168
|
+
`Known categories: ${CATEGORIES.join(", ")}.`,
|
|
169
|
+
inputSchema: {
|
|
170
|
+
type: "object",
|
|
171
|
+
properties: {
|
|
172
|
+
category: {
|
|
173
|
+
type: "string",
|
|
174
|
+
description: `Filter by category (${CATEGORIES.join(", ")}).`,
|
|
175
|
+
},
|
|
176
|
+
query: {
|
|
177
|
+
type: "string",
|
|
178
|
+
description: "Free-text query matched against name, category, and description.",
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: "lumeo_get_component",
|
|
185
|
+
description: "Get the reference for a single Lumeo component. " +
|
|
186
|
+
"For the ~35 hand-curated components this returns full schema " +
|
|
187
|
+
"(parameters, slots, Razor example, CSS variables). " +
|
|
188
|
+
"For the remaining components, returns { name, category, description, files, cssVars, dependencies, note } with a link to the docs site.",
|
|
189
|
+
inputSchema: {
|
|
190
|
+
type: "object",
|
|
191
|
+
required: ["name"],
|
|
192
|
+
properties: {
|
|
193
|
+
name: {
|
|
194
|
+
type: "string",
|
|
195
|
+
description: "Component name (e.g. \"Button\", \"DataGrid\"). Case-insensitive.",
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: "lumeo_search",
|
|
202
|
+
description: `Fuzzy search across all ${catalog.length} Lumeo components (names, categories, descriptions). Returns best matches first.`,
|
|
203
|
+
inputSchema: {
|
|
204
|
+
type: "object",
|
|
205
|
+
required: ["query"],
|
|
206
|
+
properties: {
|
|
207
|
+
query: {
|
|
208
|
+
type: "string",
|
|
209
|
+
description: "Search terms (e.g. \"modal\", \"date\", \"chat message\").",
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
}));
|
|
216
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
217
|
+
const { name, arguments: args } = req.params;
|
|
218
|
+
const a = (args ?? {});
|
|
219
|
+
switch (name) {
|
|
220
|
+
case "lumeo_list_components": {
|
|
221
|
+
const category = typeof a.category === "string" ? a.category : undefined;
|
|
222
|
+
const query = typeof a.query === "string" ? a.query : "";
|
|
223
|
+
const results = searchCatalog(query, category).map(toListPayload);
|
|
224
|
+
return {
|
|
225
|
+
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
case "lumeo_get_component": {
|
|
229
|
+
const wanted = typeof a.name === "string" ? a.name : "";
|
|
230
|
+
const c = findComponent(wanted);
|
|
231
|
+
if (!c) {
|
|
232
|
+
return {
|
|
233
|
+
isError: true,
|
|
234
|
+
content: [{
|
|
235
|
+
type: "text",
|
|
236
|
+
text: `Component "${wanted}" not found. Use lumeo_list_components or lumeo_search to discover available components.`,
|
|
237
|
+
}],
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const payload = c.thin
|
|
241
|
+
? {
|
|
242
|
+
name: c.name,
|
|
243
|
+
category: c.category,
|
|
244
|
+
description: c.description,
|
|
245
|
+
files: c.files,
|
|
246
|
+
cssVars: c.cssVars,
|
|
247
|
+
dependencies: c.dependencies,
|
|
248
|
+
note: `Rich schema coming soon — see ${docsUrl(c)}`,
|
|
249
|
+
}
|
|
250
|
+
: {
|
|
251
|
+
name: c.name,
|
|
252
|
+
category: c.category,
|
|
253
|
+
description: c.description,
|
|
254
|
+
params: c.params,
|
|
255
|
+
slots: c.slots,
|
|
256
|
+
example: c.example,
|
|
257
|
+
cssVars: c.cssVars,
|
|
258
|
+
docs: docsUrl(c),
|
|
259
|
+
};
|
|
260
|
+
return {
|
|
261
|
+
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
case "lumeo_search": {
|
|
265
|
+
const query = typeof a.query === "string" ? a.query : "";
|
|
266
|
+
const results = searchCatalog(query).map(toListPayload);
|
|
267
|
+
return {
|
|
268
|
+
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
default:
|
|
272
|
+
return {
|
|
273
|
+
isError: true,
|
|
274
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
// ───── Resources ─────
|
|
279
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
280
|
+
resources: [
|
|
281
|
+
...catalog.map((c) => ({
|
|
282
|
+
uri: `lumeo://component/${c.name}`,
|
|
283
|
+
name: `${c.name} (Lumeo component)`,
|
|
284
|
+
description: c.description,
|
|
285
|
+
mimeType: "text/markdown",
|
|
286
|
+
})),
|
|
287
|
+
...CATEGORIES.map((cat) => ({
|
|
288
|
+
uri: `lumeo://category/${cat}`,
|
|
289
|
+
name: `Lumeo ${cat} components`,
|
|
290
|
+
description: `Overview of all Lumeo components in the ${cat} category.`,
|
|
291
|
+
mimeType: "text/markdown",
|
|
292
|
+
})),
|
|
293
|
+
],
|
|
294
|
+
}));
|
|
295
|
+
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
296
|
+
resourceTemplates: [
|
|
297
|
+
{
|
|
298
|
+
uriTemplate: "lumeo://component/{name}",
|
|
299
|
+
name: "Lumeo component reference",
|
|
300
|
+
description: "Markdown reference for a single Lumeo component. Rich (params/slots/example) for curated components, thin (files/cssVars + docs link) otherwise.",
|
|
301
|
+
mimeType: "text/markdown",
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
uriTemplate: "lumeo://category/{name}",
|
|
305
|
+
name: "Lumeo category overview",
|
|
306
|
+
description: "Markdown overview of all components in a Lumeo category.",
|
|
307
|
+
mimeType: "text/markdown",
|
|
308
|
+
},
|
|
309
|
+
],
|
|
310
|
+
}));
|
|
311
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
|
|
312
|
+
const uri = req.params.uri;
|
|
313
|
+
const componentMatch = /^lumeo:\/\/component\/(.+)$/i.exec(uri);
|
|
314
|
+
if (componentMatch) {
|
|
315
|
+
const name = decodeURIComponent(componentMatch[1]);
|
|
316
|
+
const c = findComponent(name);
|
|
317
|
+
if (!c) {
|
|
318
|
+
throw new Error(`Unknown Lumeo component: ${name}`);
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
contents: [{
|
|
322
|
+
uri,
|
|
323
|
+
mimeType: "text/markdown",
|
|
324
|
+
text: toComponentMarkdown(c),
|
|
325
|
+
}],
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
const categoryMatch = /^lumeo:\/\/category\/(.+)$/i.exec(uri);
|
|
329
|
+
if (categoryMatch) {
|
|
330
|
+
const cat = decodeURIComponent(categoryMatch[1]);
|
|
331
|
+
return {
|
|
332
|
+
contents: [{
|
|
333
|
+
uri,
|
|
334
|
+
mimeType: "text/markdown",
|
|
335
|
+
text: toCategoryMarkdown(cat),
|
|
336
|
+
}],
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
throw new Error(`Unsupported resource URI: ${uri}`);
|
|
340
|
+
});
|
|
341
|
+
// ───── Start ─────
|
|
342
|
+
async function main() {
|
|
343
|
+
const transport = new StdioServerTransport();
|
|
344
|
+
await server.connect(transport);
|
|
345
|
+
// stderr is safe; stdout is the MCP transport
|
|
346
|
+
const richCount = catalog.filter((c) => !c.thin).length;
|
|
347
|
+
const thinCount = catalog.length - richCount;
|
|
348
|
+
const registryNote = registry
|
|
349
|
+
? `, registry v${registry.version}`
|
|
350
|
+
: " (no registry — curated-only mode)";
|
|
351
|
+
process.stderr.write(`[lumeo-mcp] ready — ${catalog.length} components, ${CATEGORIES.length} categories ` +
|
|
352
|
+
`(${richCount} rich, ${thinCount} thin)${registryNote}\n`);
|
|
353
|
+
}
|
|
354
|
+
main().catch((err) => {
|
|
355
|
+
process.stderr.write(`[lumeo-mcp] fatal: ${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
|
|
356
|
+
process.exit(1);
|
|
357
|
+
});
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loads Lumeo's generated registry.json (synced into src/registry.json at
|
|
3
|
+
* prebuild time — see `scripts/sync-registry.mjs`). All 125 components are
|
|
4
|
+
* surfaced to the MCP server through this file so `lumeo_list_components`,
|
|
5
|
+
* `lumeo_get_component`, and `lumeo_search` can cover the full catalog.
|
|
6
|
+
*
|
|
7
|
+
* Shape of the file:
|
|
8
|
+
* {
|
|
9
|
+
* "$schema": "...",
|
|
10
|
+
* "version": "...",
|
|
11
|
+
* "generated": "...",
|
|
12
|
+
* "components": {
|
|
13
|
+
* "<slug>": {
|
|
14
|
+
* "name": "ComponentName",
|
|
15
|
+
* "category": "Forms",
|
|
16
|
+
* "description": "...",
|
|
17
|
+
* "files": [...],
|
|
18
|
+
* "dependencies": [...],
|
|
19
|
+
* "cssVars": [...],
|
|
20
|
+
* "registryUrl": "https://lumeo.nativ.sh/registry/<slug>.json"
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* Failures are swallowed — the MCP server stays functional (just with the
|
|
26
|
+
* hand-curated catalog only) when the sync step didn't run.
|
|
27
|
+
*/
|
|
28
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
29
|
+
import { fileURLToPath } from "node:url";
|
|
30
|
+
import { dirname, resolve } from "node:path";
|
|
31
|
+
function findRegistryPath() {
|
|
32
|
+
try {
|
|
33
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
34
|
+
// dist/ → tools/lumeo-mcp; src/ → tools/lumeo-mcp
|
|
35
|
+
const candidates = [
|
|
36
|
+
resolve(here, "../src/registry.json"),
|
|
37
|
+
resolve(here, "./registry.json"),
|
|
38
|
+
// Fall back to the monorepo source if the sync step never ran.
|
|
39
|
+
resolve(here, "../../..", "src/Lumeo/registry/registry.json"),
|
|
40
|
+
resolve(here, "../..", "src/Lumeo/registry/registry.json"),
|
|
41
|
+
];
|
|
42
|
+
for (const c of candidates) {
|
|
43
|
+
if (existsSync(c))
|
|
44
|
+
return c;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// ignore
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
export function loadRegistry() {
|
|
53
|
+
const path = findRegistryPath();
|
|
54
|
+
if (!path)
|
|
55
|
+
return null;
|
|
56
|
+
try {
|
|
57
|
+
const raw = readFileSync(path, "utf8");
|
|
58
|
+
const parsed = JSON.parse(raw);
|
|
59
|
+
if (!parsed || typeof parsed !== "object" || !parsed.components)
|
|
60
|
+
return null;
|
|
61
|
+
const components = [];
|
|
62
|
+
for (const [slug, entry] of Object.entries(parsed.components)) {
|
|
63
|
+
if (!entry || !entry.name)
|
|
64
|
+
continue;
|
|
65
|
+
components.push({
|
|
66
|
+
slug,
|
|
67
|
+
name: entry.name,
|
|
68
|
+
category: entry.category ?? "Unknown",
|
|
69
|
+
description: entry.description ?? "",
|
|
70
|
+
files: entry.files ?? [],
|
|
71
|
+
dependencies: entry.dependencies ?? [],
|
|
72
|
+
cssVars: entry.cssVars ?? [],
|
|
73
|
+
registryUrl: entry.registryUrl,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
version: parsed.version ?? "unknown",
|
|
78
|
+
generated: parsed.generated ?? "",
|
|
79
|
+
components,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// swallow — fall back to the hand-curated catalog only
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lumeo-ui/mcp-server",
|
|
3
|
+
"version": "2.0.0-rc.1",
|
|
4
|
+
"description": "Model Context Protocol server for the Lumeo Blazor component library. Lets LLMs (Claude, Copilot, Cursor) author correct Lumeo markup.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"lumeo-mcp": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"src/registry.json",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"prebuild": "node scripts/sync-registry.mjs",
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"start": "node dist/index.js",
|
|
22
|
+
"dev": "tsc --watch"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"mcp",
|
|
26
|
+
"model-context-protocol",
|
|
27
|
+
"lumeo",
|
|
28
|
+
"blazor",
|
|
29
|
+
"llm"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^22.0.0",
|
|
37
|
+
"typescript": "^5.6.0"
|
|
38
|
+
}
|
|
39
|
+
}
|