@lumeo-ui/mcp-server 2.0.0-rc.2 → 2.0.0-rc.20
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/dist/componentsApi.js +46 -0
- package/dist/index.js +149 -160
- package/package.json +2 -1
- package/src/components-api.json +34892 -0
- package/src/registry.json +909 -10
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loads the auto-generated `components-api.json` produced by
|
|
3
|
+
* `tools/Lumeo.RegistryGen` (Roslyn-based scan of every `[Parameter]` /
|
|
4
|
+
* `[CascadingParameter]` property across every Razor component in the repo).
|
|
5
|
+
*
|
|
6
|
+
* This is the source-of-truth schema for ALL 131 Lumeo components. The
|
|
7
|
+
* legacy hand-curated `components.ts` is kept as an OPTIONAL example overlay:
|
|
8
|
+
* when it has an entry for a component we surface its `example` Razor snippet
|
|
9
|
+
* verbatim alongside the auto-generated parameter list.
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
12
|
+
import { dirname, resolve } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
function findPath() {
|
|
15
|
+
try {
|
|
16
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
// dist/ -> ../src/components-api.json
|
|
18
|
+
// src/ -> ./components-api.json
|
|
19
|
+
const candidates = [
|
|
20
|
+
resolve(here, "../src/components-api.json"),
|
|
21
|
+
resolve(here, "./components-api.json"),
|
|
22
|
+
// monorepo fallback when running uninstalled
|
|
23
|
+
resolve(here, "../..", "components-api.json"),
|
|
24
|
+
];
|
|
25
|
+
for (const c of candidates)
|
|
26
|
+
if (existsSync(c))
|
|
27
|
+
return c;
|
|
28
|
+
}
|
|
29
|
+
catch { /* ignore */ }
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
export function loadComponentsApi() {
|
|
33
|
+
const path = findPath();
|
|
34
|
+
if (!path)
|
|
35
|
+
return null;
|
|
36
|
+
try {
|
|
37
|
+
const raw = readFileSync(path, "utf8");
|
|
38
|
+
const parsed = JSON.parse(raw);
|
|
39
|
+
if (!parsed?.components)
|
|
40
|
+
return null;
|
|
41
|
+
return parsed;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,29 +1,44 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Lumeo MCP Server
|
|
3
|
+
* Lumeo MCP Server v2.0.0
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Source-of-truth schema for ALL 131 Lumeo components, generated at build time
|
|
6
|
+
* by `tools/Lumeo.RegistryGen` from the actual Razor source via Roslyn. Every
|
|
7
|
+
* component now ships full parameter / enum / event / sub-component metadata —
|
|
8
|
+
* no thin/rich split, no manual catalog drift.
|
|
8
9
|
*
|
|
9
10
|
* Tools:
|
|
10
|
-
* - lumeo_list_components — list/filter
|
|
11
|
-
* - lumeo_get_component —
|
|
12
|
-
* - lumeo_search — fuzzy
|
|
11
|
+
* - lumeo_list_components — list/filter all 131 components (name+category+description)
|
|
12
|
+
* - lumeo_get_component — full schema (params, enums, events, sub-components, files)
|
|
13
|
+
* - lumeo_search — fuzzy search across name/category/description
|
|
13
14
|
*
|
|
14
15
|
* Resources (URI template):
|
|
15
16
|
* - lumeo://component/{name} — markdown reference per component
|
|
16
|
-
* - lumeo://category/{name} — all components in a category
|
|
17
|
+
* - lumeo://category/{name} — overview of all components in a category
|
|
17
18
|
*
|
|
18
19
|
* Transport: stdio (the standard for spawned MCP servers).
|
|
19
20
|
*/
|
|
20
21
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
21
22
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
22
23
|
import { CallToolRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
23
|
-
import {
|
|
24
|
+
import { loadComponentsApi, } from "./componentsApi.js";
|
|
25
|
+
import { components as curatedExamples } from "./components.js";
|
|
24
26
|
const DOCS_BASE = "https://lumeo.nativ.sh";
|
|
27
|
+
// ───────────────── Load source-of-truth schema ─────────────────
|
|
28
|
+
const api = loadComponentsApi() ?? {
|
|
29
|
+
version: "0.0.0",
|
|
30
|
+
generated: "",
|
|
31
|
+
stats: { componentCount: 0, totalParameters: 0, totalEnums: 0, totalRecords: 0, thinFallbacks: [] },
|
|
32
|
+
components: {},
|
|
33
|
+
};
|
|
34
|
+
const components = Object.values(api.components).sort((a, b) => a.name.localeCompare(b.name));
|
|
35
|
+
const byName = new Map(components.map((c) => [c.name.toLowerCase(), c]));
|
|
36
|
+
const CATEGORIES = Array.from(new Set(components.map((c) => c.category))).sort();
|
|
37
|
+
// Hand-curated examples overlay (~30 components). Auto-gen schema wins for
|
|
38
|
+
// parameters/enums/events; the curated `example` Razor snippet is preserved
|
|
39
|
+
// as a documentation aid because LLMs benefit from seeing real usage.
|
|
40
|
+
const curatedExampleByName = new Map(curatedExamples.map((c) => [c.name.toLowerCase(), c.example]));
|
|
25
41
|
// ───────────────── Helpers ─────────────────
|
|
26
|
-
const byName = new Map(catalog.map((c) => [c.name.toLowerCase(), c]));
|
|
27
42
|
function findComponent(name) {
|
|
28
43
|
return byName.get(name.toLowerCase());
|
|
29
44
|
}
|
|
@@ -45,10 +60,9 @@ function score(c, q) {
|
|
|
45
60
|
return s;
|
|
46
61
|
}
|
|
47
62
|
function searchCatalog(query, category) {
|
|
48
|
-
let pool =
|
|
49
|
-
if (category)
|
|
63
|
+
let pool = components;
|
|
64
|
+
if (category)
|
|
50
65
|
pool = pool.filter((c) => c.category.toLowerCase() === category.toLowerCase());
|
|
51
|
-
}
|
|
52
66
|
if (!query)
|
|
53
67
|
return pool;
|
|
54
68
|
return pool
|
|
@@ -58,94 +72,123 @@ function searchCatalog(query, category) {
|
|
|
58
72
|
.map((x) => x.c);
|
|
59
73
|
}
|
|
60
74
|
function docsUrl(c) {
|
|
61
|
-
|
|
75
|
+
// Convert PascalCase to kebab-case for the docs URL
|
|
76
|
+
const slug = c.name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
77
|
+
return `${DOCS_BASE}/components/${slug}`;
|
|
78
|
+
}
|
|
79
|
+
function paramRow(p) {
|
|
80
|
+
const def = p.default ?? "—";
|
|
81
|
+
const desc = p.description ?? "";
|
|
82
|
+
const flags = [];
|
|
83
|
+
if (p.isCascading)
|
|
84
|
+
flags.push("cascading");
|
|
85
|
+
if (p.captureUnmatched)
|
|
86
|
+
flags.push("captures unmatched");
|
|
87
|
+
const flagStr = flags.length ? ` _(${flags.join(", ")})_` : "";
|
|
88
|
+
return `| \`${p.name}\` | \`${p.type}\` | \`${def}\` | ${desc}${flagStr} |`;
|
|
62
89
|
}
|
|
63
|
-
function
|
|
64
|
-
const paramRows = c.
|
|
65
|
-
|
|
90
|
+
function toComponentMarkdown(c) {
|
|
91
|
+
const paramRows = c.parameters.map(paramRow).join("\n");
|
|
92
|
+
const enumRows = c.enums
|
|
93
|
+
.map((e) => `- **${e.name}**: ${e.values.join(", ")}${e.description ? ` — ${e.description}` : ""}`)
|
|
66
94
|
.join("\n");
|
|
67
|
-
const
|
|
68
|
-
.map((
|
|
95
|
+
const eventRows = c.events
|
|
96
|
+
.map((e) => `- **${e.name}** \`${e.type}\`${e.description ? ` — ${e.description}` : ""}`)
|
|
97
|
+
.join("\n");
|
|
98
|
+
const subRows = Object.values(c.subComponents)
|
|
99
|
+
.map((s) => `- **${s.componentName}** (${s.parameters.length} params)`)
|
|
69
100
|
.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
101
|
const filesBlock = c.files.length
|
|
99
|
-
?
|
|
100
|
-
: "";
|
|
101
|
-
const cssVarsBlock = c.cssVars.length
|
|
102
|
-
? `## CSS Variables\n\n${c.cssVars.map((v) => `- \`${v}\``).join("\n")}\n\n`
|
|
102
|
+
? c.files.map((f) => `- \`${f}\``).join("\n")
|
|
103
103
|
: "";
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
: "";
|
|
107
|
-
return [
|
|
104
|
+
const example = curatedExampleByName.get(c.name.toLowerCase());
|
|
105
|
+
const sections = [
|
|
108
106
|
`# ${c.name}`,
|
|
109
|
-
|
|
110
|
-
`**Category:** ${c.category}`,
|
|
111
|
-
|
|
107
|
+
"",
|
|
108
|
+
`**Category:** ${c.category}${c.subcategory ? ` › ${c.subcategory}` : ""}`,
|
|
109
|
+
`**NuGet:** \`${c.nugetPackage}\``,
|
|
110
|
+
`**Namespace:** \`${c.namespace ?? "Lumeo"}\``,
|
|
111
|
+
"",
|
|
112
112
|
c.description,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
]
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
113
|
+
"",
|
|
114
|
+
"## Parameters",
|
|
115
|
+
"",
|
|
116
|
+
"| Name | Type | Default | Description |",
|
|
117
|
+
"|---|---|---|---|",
|
|
118
|
+
paramRows || "| _(none)_ | | | |",
|
|
119
|
+
"",
|
|
120
|
+
];
|
|
121
|
+
if (c.enums.length)
|
|
122
|
+
sections.push("## Enums", "", enumRows, "");
|
|
123
|
+
if (c.events.length)
|
|
124
|
+
sections.push("## Events", "", eventRows, "");
|
|
125
|
+
if (Object.keys(c.subComponents).length)
|
|
126
|
+
sections.push("## Sub-components", "", subRows, "");
|
|
127
|
+
if (example)
|
|
128
|
+
sections.push("## Example", "", "```razor", example, "```", "");
|
|
129
|
+
if (filesBlock)
|
|
130
|
+
sections.push("## Source files", "", filesBlock, "");
|
|
131
|
+
sections.push(`_Docs: ${docsUrl(c)}_`);
|
|
132
|
+
return sections.join("\n");
|
|
124
133
|
}
|
|
125
134
|
function toCategoryMarkdown(category) {
|
|
126
|
-
const inCat =
|
|
127
|
-
if (inCat.length === 0)
|
|
128
|
-
return `# ${category}\n\nNo components
|
|
129
|
-
}
|
|
135
|
+
const inCat = components.filter((c) => c.category.toLowerCase() === category.toLowerCase());
|
|
136
|
+
if (inCat.length === 0)
|
|
137
|
+
return `# ${category}\n\nNo components in this category.`;
|
|
130
138
|
const rows = inCat
|
|
131
|
-
.map((c) => `| [\`${c.name}\`](lumeo://component/${c.name}) | ${c.
|
|
139
|
+
.map((c) => `| [\`${c.name}\`](lumeo://component/${c.name}) | ${c.description} |`)
|
|
132
140
|
.join("\n");
|
|
133
141
|
return [
|
|
134
142
|
`# ${category}`,
|
|
135
|
-
|
|
143
|
+
"",
|
|
136
144
|
`${inCat.length} component${inCat.length === 1 ? "" : "s"}:`,
|
|
137
|
-
|
|
145
|
+
"",
|
|
138
146
|
`| Component | Description |`,
|
|
139
147
|
`|---|---|`,
|
|
140
148
|
rows,
|
|
141
|
-
|
|
149
|
+
"",
|
|
142
150
|
].join("\n");
|
|
143
151
|
}
|
|
144
152
|
function toListPayload(c) {
|
|
145
153
|
return {
|
|
146
154
|
name: c.name,
|
|
147
155
|
category: c.category,
|
|
156
|
+
subcategory: c.subcategory,
|
|
148
157
|
description: c.description,
|
|
158
|
+
nugetPackage: c.nugetPackage,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function toGetPayload(c) {
|
|
162
|
+
// Build a rich JSON payload covering everything Claude Code needs to write
|
|
163
|
+
// correct Razor without consulting external docs.
|
|
164
|
+
const subComponents = Object.values(c.subComponents).map((s) => ({
|
|
165
|
+
name: s.componentName,
|
|
166
|
+
namespace: s.namespace,
|
|
167
|
+
inheritsFrom: s.inheritsFrom,
|
|
168
|
+
implements: s.implements,
|
|
169
|
+
parameters: s.parameters,
|
|
170
|
+
events: s.events,
|
|
171
|
+
enums: s.enums,
|
|
172
|
+
records: s.records,
|
|
173
|
+
}));
|
|
174
|
+
return {
|
|
175
|
+
name: c.name,
|
|
176
|
+
category: c.category,
|
|
177
|
+
subcategory: c.subcategory,
|
|
178
|
+
description: c.description,
|
|
179
|
+
nugetPackage: c.nugetPackage,
|
|
180
|
+
namespace: c.namespace,
|
|
181
|
+
inheritsFrom: c.inheritsFrom,
|
|
182
|
+
implements: c.implements,
|
|
183
|
+
parameters: c.parameters,
|
|
184
|
+
events: c.events,
|
|
185
|
+
enums: c.enums,
|
|
186
|
+
records: c.records,
|
|
187
|
+
cssVars: c.cssVars,
|
|
188
|
+
files: c.files,
|
|
189
|
+
subComponents,
|
|
190
|
+
example: curatedExampleByName.get(c.name.toLowerCase()) ?? null,
|
|
191
|
+
docs: docsUrl(c),
|
|
149
192
|
};
|
|
150
193
|
}
|
|
151
194
|
// ───────────────── Server setup ─────────────────
|
|
@@ -163,51 +206,44 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
163
206
|
tools: [
|
|
164
207
|
{
|
|
165
208
|
name: "lumeo_list_components",
|
|
166
|
-
description: `List all ${
|
|
167
|
-
"Returns
|
|
168
|
-
`
|
|
209
|
+
description: `List all ${components.length} Lumeo components, optionally filtered by category or query. ` +
|
|
210
|
+
"Returns { name, category, subcategory, description, nugetPackage } per component. " +
|
|
211
|
+
`Categories: ${CATEGORIES.join(", ")}.`,
|
|
169
212
|
inputSchema: {
|
|
170
213
|
type: "object",
|
|
171
214
|
properties: {
|
|
172
|
-
category: {
|
|
173
|
-
|
|
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
|
-
},
|
|
215
|
+
category: { type: "string", description: `Filter by category (${CATEGORIES.join(", ")}).` },
|
|
216
|
+
query: { type: "string", description: "Free-text query matched against name, category, description." },
|
|
180
217
|
},
|
|
181
218
|
},
|
|
182
219
|
},
|
|
183
220
|
{
|
|
184
221
|
name: "lumeo_get_component",
|
|
185
|
-
description: "Get the
|
|
186
|
-
"
|
|
187
|
-
"
|
|
188
|
-
"
|
|
222
|
+
description: "Get the COMPLETE schema for a Lumeo component: every [Parameter] " +
|
|
223
|
+
"(name, type, default, XML doc summary), nested enums and records, " +
|
|
224
|
+
"EventCallback events, sub-components (e.g. Dialog → DialogContent, " +
|
|
225
|
+
"DialogHeader, DialogTrigger, ...), CSS variables, source files, and a " +
|
|
226
|
+
"hand-curated Razor example when available. Sourced from the actual " +
|
|
227
|
+
"Razor source via Roslyn — always in sync with the library.",
|
|
189
228
|
inputSchema: {
|
|
190
229
|
type: "object",
|
|
191
230
|
required: ["name"],
|
|
192
231
|
properties: {
|
|
193
232
|
name: {
|
|
194
233
|
type: "string",
|
|
195
|
-
description: "Component name (e.g. \"Button\", \"DataGrid\"). Case-insensitive.",
|
|
234
|
+
description: "Component name (e.g. \"Button\", \"DataGrid\", \"Sheet\"). Case-insensitive.",
|
|
196
235
|
},
|
|
197
236
|
},
|
|
198
237
|
},
|
|
199
238
|
},
|
|
200
239
|
{
|
|
201
240
|
name: "lumeo_search",
|
|
202
|
-
description: `Fuzzy search across all ${
|
|
241
|
+
description: `Fuzzy search across all ${components.length} Lumeo components (name, category, description). Best matches first.`,
|
|
203
242
|
inputSchema: {
|
|
204
243
|
type: "object",
|
|
205
244
|
required: ["query"],
|
|
206
245
|
properties: {
|
|
207
|
-
query: {
|
|
208
|
-
type: "string",
|
|
209
|
-
description: "Search terms (e.g. \"modal\", \"date\", \"chat message\").",
|
|
210
|
-
},
|
|
246
|
+
query: { type: "string", description: "Search terms (e.g. \"modal\", \"date\", \"chat message\")." },
|
|
211
247
|
},
|
|
212
248
|
},
|
|
213
249
|
},
|
|
@@ -221,9 +257,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
221
257
|
const category = typeof a.category === "string" ? a.category : undefined;
|
|
222
258
|
const query = typeof a.query === "string" ? a.query : "";
|
|
223
259
|
const results = searchCatalog(query, category).map(toListPayload);
|
|
224
|
-
return {
|
|
225
|
-
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
226
|
-
};
|
|
260
|
+
return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
|
|
227
261
|
}
|
|
228
262
|
case "lumeo_get_component": {
|
|
229
263
|
const wanted = typeof a.name === "string" ? a.name : "";
|
|
@@ -237,48 +271,21 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
237
271
|
}],
|
|
238
272
|
};
|
|
239
273
|
}
|
|
240
|
-
|
|
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
|
-
};
|
|
274
|
+
return { content: [{ type: "text", text: JSON.stringify(toGetPayload(c), null, 2) }] };
|
|
263
275
|
}
|
|
264
276
|
case "lumeo_search": {
|
|
265
277
|
const query = typeof a.query === "string" ? a.query : "";
|
|
266
278
|
const results = searchCatalog(query).map(toListPayload);
|
|
267
|
-
return {
|
|
268
|
-
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
269
|
-
};
|
|
279
|
+
return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
|
|
270
280
|
}
|
|
271
281
|
default:
|
|
272
|
-
return {
|
|
273
|
-
isError: true,
|
|
274
|
-
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
275
|
-
};
|
|
282
|
+
return { isError: true, content: [{ type: "text", text: `Unknown tool: ${name}` }] };
|
|
276
283
|
}
|
|
277
284
|
});
|
|
278
285
|
// ───── Resources ─────
|
|
279
286
|
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
280
287
|
resources: [
|
|
281
|
-
...
|
|
288
|
+
...components.map((c) => ({
|
|
282
289
|
uri: `lumeo://component/${c.name}`,
|
|
283
290
|
name: `${c.name} (Lumeo component)`,
|
|
284
291
|
description: c.description,
|
|
@@ -297,7 +304,7 @@ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
|
297
304
|
{
|
|
298
305
|
uriTemplate: "lumeo://component/{name}",
|
|
299
306
|
name: "Lumeo component reference",
|
|
300
|
-
description: "Markdown reference for a single Lumeo component
|
|
307
|
+
description: "Markdown reference for a single Lumeo component, generated from Razor source.",
|
|
301
308
|
mimeType: "text/markdown",
|
|
302
309
|
},
|
|
303
310
|
{
|
|
@@ -312,29 +319,16 @@ server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
|
|
|
312
319
|
const uri = req.params.uri;
|
|
313
320
|
const componentMatch = /^lumeo:\/\/component\/(.+)$/i.exec(uri);
|
|
314
321
|
if (componentMatch) {
|
|
315
|
-
const
|
|
316
|
-
const c = findComponent(
|
|
317
|
-
if (!c)
|
|
318
|
-
throw new Error(`Unknown Lumeo component: ${
|
|
319
|
-
}
|
|
320
|
-
return {
|
|
321
|
-
contents: [{
|
|
322
|
-
uri,
|
|
323
|
-
mimeType: "text/markdown",
|
|
324
|
-
text: toComponentMarkdown(c),
|
|
325
|
-
}],
|
|
326
|
-
};
|
|
322
|
+
const wanted = decodeURIComponent(componentMatch[1]);
|
|
323
|
+
const c = findComponent(wanted);
|
|
324
|
+
if (!c)
|
|
325
|
+
throw new Error(`Unknown Lumeo component: ${wanted}`);
|
|
326
|
+
return { contents: [{ uri, mimeType: "text/markdown", text: toComponentMarkdown(c) }] };
|
|
327
327
|
}
|
|
328
328
|
const categoryMatch = /^lumeo:\/\/category\/(.+)$/i.exec(uri);
|
|
329
329
|
if (categoryMatch) {
|
|
330
330
|
const cat = decodeURIComponent(categoryMatch[1]);
|
|
331
|
-
return {
|
|
332
|
-
contents: [{
|
|
333
|
-
uri,
|
|
334
|
-
mimeType: "text/markdown",
|
|
335
|
-
text: toCategoryMarkdown(cat),
|
|
336
|
-
}],
|
|
337
|
-
};
|
|
331
|
+
return { contents: [{ uri, mimeType: "text/markdown", text: toCategoryMarkdown(cat) }] };
|
|
338
332
|
}
|
|
339
333
|
throw new Error(`Unsupported resource URI: ${uri}`);
|
|
340
334
|
});
|
|
@@ -342,14 +336,9 @@ server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
|
|
|
342
336
|
async function main() {
|
|
343
337
|
const transport = new StdioServerTransport();
|
|
344
338
|
await server.connect(transport);
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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`);
|
|
339
|
+
process.stderr.write(`[lumeo-mcp] ready — ${components.length} components, ${CATEGORIES.length} categories, ` +
|
|
340
|
+
`${api.stats.totalParameters} params, ${api.stats.totalEnums} enums, ` +
|
|
341
|
+
`api v${api.version}, generated ${api.generated}\n`);
|
|
353
342
|
}
|
|
354
343
|
main().catch((err) => {
|
|
355
344
|
process.stderr.write(`[lumeo-mcp] fatal: ${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumeo-ui/mcp-server",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
3
|
+
"version": "2.0.0-rc.20",
|
|
4
4
|
"description": "Model Context Protocol server for the Lumeo Blazor component library. Lets LLMs (Claude, Copilot, Cursor) author correct Lumeo markup.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
12
12
|
"src/registry.json",
|
|
13
|
+
"src/components-api.json",
|
|
13
14
|
"README.md"
|
|
14
15
|
],
|
|
15
16
|
"publishConfig": {
|