@santi020k/lumen-mcp 3.0.0 → 4.0.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/dist/server.js CHANGED
@@ -1,6 +1,9 @@
1
- import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import { McpServer, ResourceTemplate } from '@modelcontextprotocol/server';
2
2
  import * as z from 'zod';
3
+ import { checkCompatibility } from './compatibility.js';
4
+ import { compatibilityOutputSchema, componentOutputSchema, nativeComponentOutputSchema, recipeOutputSchema } from './contracts.js';
3
5
  import { loadLumenData } from './data.js';
6
+ import { getMigration } from './migration.js';
4
7
  import { diagnose, diffCatalog, getCatalogManifest, getComponent, getMeta, getNativeComponent, getRecipe, getRules, getTokens, listComponents, listNativeComponents, search } from './tools.js';
5
8
  const readOnlyAnnotations = {
6
9
  destructiveHint: false,
@@ -21,11 +24,6 @@ const listComponentsOutputSchema = z.strictObject({
21
24
  components: z.array(componentSummarySchema),
22
25
  count: z.int().min(0)
23
26
  });
24
- const componentOutputSchema = z.strictObject({
25
- component: z.record(z.string(), z.unknown()).optional(),
26
- found: z.boolean(),
27
- message: z.string().trim().optional()
28
- });
29
27
  const nativePlatformSchema = z.enum(['react-native', 'swiftui', 'compose']);
30
28
  const nativeComponentSummarySchema = z.strictObject({
31
29
  accessibility: z.string().trim(),
@@ -40,11 +38,6 @@ const listNativeComponentsOutputSchema = z.strictObject({
40
38
  components: z.array(nativeComponentSummarySchema),
41
39
  count: z.int().min(0)
42
40
  });
43
- const recipeOutputSchema = z.strictObject({
44
- found: z.boolean(),
45
- message: z.string().trim().optional(),
46
- recipe: z.record(z.string(), z.unknown()).optional()
47
- });
48
41
  const searchResultSchema = z.strictObject({
49
42
  category: z.string().trim().optional(),
50
43
  description: z.string().trim(),
@@ -62,10 +55,18 @@ const searchOutputSchema = z.strictObject({
62
55
  });
63
56
  const tokensOutputSchema = z.strictObject({
64
57
  tokens: z.strictObject({
65
- chart: z.record(z.string(), z.string().trim()),
66
- colors: z.record(z.string(), z.string().trim()),
67
- glass: z.record(z.string(), z.string().trim()),
58
+ presets: z.strictObject({
59
+ attribute: z.string().trim(),
60
+ names: z.array(z.enum(['default', 'studio', 'glass'])),
61
+ schemeAttribute: z.string().trim(),
62
+ materialPolicy: z.literal('explicit-surfaces')
63
+ }),
64
+ chart: z.record(z.string().trim(), z.string().trim()),
65
+ colors: z.record(z.string().trim(), z.string().trim()),
66
+ glass: z.record(z.string().trim(), z.string().trim()),
68
67
  semantic: z.array(z.string().trim()),
68
+ spacing: z.record(z.string().trim(), z.number().nonnegative()),
69
+ spacingRoles: z.record(z.string().trim(), z.string().trim()),
69
70
  themeAttribute: z.string().trim()
70
71
  })
71
72
  });
@@ -78,7 +79,7 @@ const metaOutputSchema = z.strictObject({
78
79
  componentCount: z.int().min(0),
79
80
  nativeComponentCount: z.int().min(0),
80
81
  packages: z.array(z.string().trim()),
81
- packageVersions: z.record(z.string(), z.string().trim()),
82
+ packageVersions: z.record(z.string().trim(), z.string().trim()),
82
83
  registryName: z.string().trim(),
83
84
  registryVersion: z.int().min(1),
84
85
  schemaVersion: z.int().min(1),
@@ -86,10 +87,10 @@ const metaOutputSchema = z.strictObject({
86
87
  })
87
88
  });
88
89
  const catalogManifestSchema = z.strictObject({
89
- components: z.record(z.string(), z.string().trim().regex(/^[a-f0-9]{64}$/)),
90
- nativeComponents: z.record(z.string(), z.string().trim().regex(/^[a-f0-9]{64}$/))
90
+ components: z.record(z.string().trim(), z.string().trim().regex(/^[a-f0-9]{64}$/)),
91
+ nativeComponents: z.record(z.string().trim(), z.string().trim().regex(/^[a-f0-9]{64}$/))
91
92
  .optional(),
92
- recipes: z.record(z.string(), z.string().trim().regex(/^[a-f0-9]{64}$/))
93
+ recipes: z.record(z.string().trim(), z.string().trim().regex(/^[a-f0-9]{64}$/))
93
94
  });
94
95
  const catalogChangesSchema = z.strictObject({
95
96
  added: z.array(z.string().trim()),
@@ -145,7 +146,7 @@ const jsonResource = (uri, data, mimeType = 'application/json') => ({
145
146
  export const createLumenServer = () => {
146
147
  const data = loadLumenData();
147
148
  const server = new McpServer({ name: '@santi020k/lumen-mcp', version: data.meta.serverVersion }, {
148
- instructions: 'Read lumen://meta, lumen://diagnostics, and lumen://rules before generating Lumen code. Search or list the catalog, ' +
149
+ instructions: 'Compare resolved installed versions with lumen_check_compatibility. On mismatch use installed public types or a matching catalog. Read lumen://meta, lumen://diagnostics, and lumen://rules before generating Lumen code. Search or list the catalog, ' +
149
150
  'then inspect the selected web or native component for the target framework or platform at usage detail before requesting source.'
150
151
  });
151
152
  server.registerTool('lumen_list_components', {
@@ -204,7 +205,7 @@ export const createLumenServer = () => {
204
205
  platform: nativePlatformSchema
205
206
  .meta({ description: 'Native implementation contract to return.' })
206
207
  }),
207
- outputSchema: componentOutputSchema
208
+ outputSchema: nativeComponentOutputSchema
208
209
  }, args => toMcpResult(getNativeComponent(args)));
209
210
  server.registerTool('lumen_get_recipe', {
210
211
  annotations: readOnlyAnnotations,
@@ -227,6 +228,8 @@ export const createLumenServer = () => {
227
228
  framework: z.enum(['astro', 'react', 'elements'])
228
229
  .optional()
229
230
  .meta({ description: 'Only search framework-specific component contracts for this target.' }),
231
+ kind: z.enum(['component', 'native-component', 'recipe', 'rule', 'token']).optional()
232
+ .meta({ description: 'Return only this kind of match; combine with limit for focused discovery.' }),
230
233
  limit: z.int().min(1).max(100)
231
234
  .optional()
232
235
  .meta({ description: 'Maximum results to return (1-100, default 20).' }),
@@ -237,6 +240,32 @@ export const createLumenServer = () => {
237
240
  }),
238
241
  outputSchema: searchOutputSchema
239
242
  }, args => toMcpResult(search(args)));
243
+ server.registerTool('lumen_check_compatibility', {
244
+ annotations: readOnlyAnnotations,
245
+ description: 'Compare resolved installed Lumen package versions with this snapshot before generating or migrating code. Does not read repositories or change dependencies.',
246
+ inputSchema: z.strictObject({
247
+ packageVersions: z.record(z.string().trim().min(1).max(160), z.string().trim().min(1).max(100))
248
+ .meta({ description: 'Resolved versions from installed package metadata or native lock files, not manifest ranges. Include only Lumen packages.' })
249
+ }),
250
+ outputSchema: compatibilityOutputSchema
251
+ }, args => toMcpResult(checkCompatibility(args)));
252
+ server.registerTool('lumen_get_migration', {
253
+ annotations: readOnlyAnnotations,
254
+ description: 'Read the v4 migration contract, affected packages and review guidance. Does not modify consumer code or versions.',
255
+ inputSchema: z.strictObject({ packageName: z.string().trim().min(1).max(160).optional() }),
256
+ outputSchema: z.strictObject({
257
+ changes: z.array(z.strictObject({
258
+ currentContract: z.string().trim(),
259
+ docs: z.array(z.string().trim()),
260
+ id: z.string().trim(),
261
+ migration: z.string().trim(),
262
+ packages: z.array(z.string().trim()),
263
+ replacement: z.string().trim()
264
+ })),
265
+ status: z.string().trim(),
266
+ targetVersion: z.string().trim()
267
+ })
268
+ }, args => toMcpResult(getMigration(args)));
240
269
  server.registerTool('lumen_get_meta', {
241
270
  annotations: readOnlyAnnotations,
242
271
  description: 'Return snapshot provenance: server and package versions, schema version, component count, ' +
@@ -270,7 +299,7 @@ export const createLumenServer = () => {
270
299
  }, () => toMcpResult(diagnose()));
271
300
  server.registerTool('lumen_get_tokens', {
272
301
  annotations: readOnlyAnnotations,
273
- description: 'Return structured Lumen semantic tokens, base color values, glass tokens, and theme attribute.',
302
+ description: 'Return Lumen spacing dimensions and content roles, semantic colors, glass tokens, and theme attribute.',
274
303
  inputSchema: z.strictObject({}),
275
304
  outputSchema: tokensOutputSchema
276
305
  }, () => toMcpResult(getTokens()));
package/dist/tools.d.ts CHANGED
@@ -80,6 +80,7 @@ declare const componentDetail: (component: LumenComponentSnapshot, framework: Lu
80
80
  name: string;
81
81
  type: string;
82
82
  }[];
83
+ runtimeBypass?: string;
83
84
  setup: string;
84
85
  };
85
86
  example: string;
@@ -214,6 +215,7 @@ export declare const getRecipe: (args: {
214
215
  }>;
215
216
  export declare const search: (args: {
216
217
  framework?: FrameworkFilter | undefined;
218
+ kind?: SearchResult["kind"] | undefined;
217
219
  limit?: number | undefined;
218
220
  platform?: NativePlatformFilter | undefined;
219
221
  query: string;
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACtB,SAAS,EACT,cAAc,EAEd,4BAA4B,EAC5B,iCAAiC,EACjC,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,WAAW,CAAA;AAGlB,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,SAAS,EAAE,MAAM,EAAE,CAAA;CACpB;AACD,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAA;AAEjE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,cAAc,EAAE,CAAA;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,MAAM,MAAM,eAAe,GAAG,cAAc,CAAA;AAC5C,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,CAAA;AAEtD,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,mBAAmB,EAAE,CAAA;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,eAAe,CAAC,IAAI,GAAG,OAAO;IAC7C,IAAI,EAAE,IAAI,CAAA;IACV,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,cAAc,EAAE,CAAA;IAC7B,IAAI,EAAE,WAAW,GAAG,kBAAkB,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAA;IACpE,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,mBAAmB,EAAE,CAAA;IACjC,KAAK,EAAE,MAAM,CAAA;CACd;AAqHD,eAAO,MAAM,gBAAgB,GAC3B,MAAM,MAAM,EACZ,OAAM,SAA2B,KAChC,sBAAsB,GAAG,SAQ3B,CAAA;AAED,eAAO,MAAM,aAAa,GACxB,MAAM,MAAM,EACZ,OAAM,SAA2B,KAChC,mBAAmB,GAAG,SAIxB,CAAA;AAED,eAAO,MAAM,sBAAsB,GACjC,MAAM,MAAM,EACZ,OAAM,SAA2B,KAChC,4BAA4B,GAAG,SAYjC,CAAA;AAqQD,eAAO,MAAM,cAAc,GACzB,OAAM;IACJ,SAAS,CAAC,EAAE,eAAe,GAAG,SAAS,CAAA;IACvC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACvB,EACN,OAAM,SAA2B,KAChC,eAAe,CAAC;IAAE,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAoDnE,CAAA;AA8BD,QAAA,MAAM,eAAe,GACnB,WAAW,sBAAsB,EACjC,WAAW,cAAc,EACzB,QAAQ,oBAAoB;;;;;;;;;;;;;sBAjZzB,CAAC;;;;;;uBAGE,CAAC;gBAAiB,CAAC;;mBAGe,CAAC;;;;;;yBAOrC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA5JK,MAAM;iBACH,MAAM,EAAE;iBACR,MAAM;gBACP,cAAc,EAAE;WACrB,MAAM;UACP,MAAM;aACH,MAAM,EAAE;;;;;;;;;;;;cANP,MAAM;iBACH,MAAM,EAAE;iBACR,MAAM;gBACP,cAAc,EAAE;WACrB,MAAM;UACP,MAAM;aACH,MAAM,EAAE;CA6jBlB,CAAA;AA+ID,eAAO,MAAM,YAAY,GACvB,MAAM;IACJ,MAAM,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAA;IACzC,SAAS,CAAC,EAAE,eAAe,GAAG,SAAS,CAAA;IACvC,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACnC,IAAI,EAAE,MAAM,CAAA;CACb,EACD,OAAM,SAA2B,KAChC,eAAe,CAAC;IACjB,SAAS,CAAC,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,CAAA;IAC9C,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAqCA,CAAA;AAED,eAAO,MAAM,oBAAoB,GAC/B,OAAM;IACJ,QAAQ,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAA;IAC3C,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACtB,EACN,OAAM,SAA2B,KAChC,eAAe,CAAC;IAAE,UAAU,EAAE,sBAAsB,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAsBzE,CAAA;AAED,QAAA,MAAM,qBAAqB,GACzB,WAAW,4BAA4B,EACvC,UAAU,mBAAmB,EAC7B,QAAQ,oBAAoB,EAC5B,MAAM,SAAS;;;;;mBA1xBA,MAAM;cACX,MAAM;QACZ,MAAM;UACJ,MAAM;eACD,mBAAmB,EAAE;aACvB,MAAM;UACT,MAAM;;;;;;;;;;mBANG,MAAM;cACX,MAAM;QACZ,MAAM;UACJ,MAAM;eACD,mBAAmB,EAAE;aACvB,MAAM;UACT,MAAM;;;;;;;mBANG,MAAM;cACX,MAAM;QACZ,MAAM;UACJ,MAAM;eACD,mBAAmB,EAAE;aACvB,MAAM;UACT,MAAM;aAozBb,CAAA;AAyDD,eAAO,MAAM,kBAAkB,GAC7B,MAAM;IACJ,MAAM,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAA;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,oBAAoB,CAAA;CAC/B,EACD,OAAM,SAA2B,KAChC,eAAe,CAAC;IACjB,SAAS,CAAC,EAAE,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAA;IACpD,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAwBA,CAAA;AAqBD,eAAO,MAAM,SAAS,GACpB,MAAM;IAAE,SAAS,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAC/D,OAAM,SAA2B,KAChC,eAAe,CAAC;IACjB,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,mBAAmB,CAAA;CAC7B,CAqCA,CAAA;AAED,eAAO,MAAM,MAAM,GACjB,MAAM;IACJ,SAAS,CAAC,EAAE,eAAe,GAAG,SAAS,CAAA;IACvC,IAAI,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG,SAAS,CAAA;IACvC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,QAAQ,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAA;IAC3C,KAAK,EAAE,MAAM,CAAA;CACd,EACD,OAAM,SAA2B,KAChC,eAAe,CAAC;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;CACd,CA8CA,CAAA;AAED,eAAO,MAAM,OAAO,GAClB,OAAM,SAA2B,KAChC,eAAe,CAAC;IAAE,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,CAAA;CAAE,CAe5C,CAAA;AAkCF,eAAO,MAAM,kBAAkB,GAC7B,OAAM,SAA2B,KAChC,eAAe,CAAC;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,oBAAoB,CAAA;IAC9B,aAAa,EAAE,MAAM,CAAA;CACtB,CAeC,CAAA;AAEF,eAAO,MAAM,WAAW,GACtB,MAAM;IACJ,QAAQ,EAAE,oBAAoB,CAAA;IAC9B,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACzC,EACD,OAAM,SAA2B,KAChC,eAAe,CAAC;IACjB,UAAU,EAAE,cAAc,CAAA;IAC1B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,gBAAgB,EAAE,cAAc,CAAA;IAChC,OAAO,EAAE,cAAc,CAAA;IACvB,SAAS,EAAE,OAAO,CAAA;CACnB,CAqDA,CAAA;AAED,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;CACxB;AA4FD,eAAO,MAAM,QAAQ,GACnB,OAAM,SAA2B,KAChC,eAAe,CAAC;IACjB,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,iBAAiB,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAA;IACjD,cAAc,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAA;IACnD,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAA;CAC7B,CAuCA,CAAA;AAED,eAAO,MAAM,SAAS,GACpB,OAAM,SAA2B,KAChC,eAAe,CAAC;IAAE,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAA;CAAE,CA2CjD,CAAA;AAED,eAAO,MAAM,QAAQ,GACnB,OAAM,SAA2B,KAChC,eAAe,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAanC,CAAA"}
package/dist/tools.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { loadLumenData } from './data.js';
2
2
  const normalize = (value) => value
3
+ .normalize('NFD')
4
+ .replaceAll(/[\u0300-\u036f]/g, '')
3
5
  .replaceAll(/([a-z0-9])([A-Z])/g, '$1-$2')
4
6
  .replaceAll(/[\s_]+/g, '-')
5
7
  .toLowerCase();
@@ -25,9 +27,40 @@ const searchStopWords = new Set([
25
27
  'the',
26
28
  'to',
27
29
  'ui',
28
- 'with'
30
+ 'with',
31
+ 'de',
32
+ 'del',
33
+ 'el',
34
+ 'la',
35
+ 'las',
36
+ 'los',
37
+ 'para',
38
+ 'un',
39
+ 'una',
40
+ 'y',
41
+ 'con',
42
+ 'accesible'
29
43
  ]);
30
44
  const searchTermAliases = {
45
+ boton: ['button'],
46
+ buscar: ['search'],
47
+ calendario: ['calendar'],
48
+ confirmar: ['confirm', 'confirmation'],
49
+ contrasena: ['password'],
50
+ eliminacion: ['delete', 'destructive'],
51
+ eliminar: ['delete', 'destructive'],
52
+ fecha: ['date'],
53
+ fechas: ['date'],
54
+ formulario: ['form'],
55
+ modal: ['modal', 'dialog'],
56
+ notificacion: ['toast', 'notification'],
57
+ oscuro: ['dark', 'theme'],
58
+ paginada: ['pagination', 'paginated'],
59
+ rango: ['range'],
60
+ registros: ['records', 'data-table'],
61
+ seleccion: ['select'],
62
+ selector: ['select', 'picker'],
63
+ tabla: ['table'],
31
64
  booking: ['booking', 'schedule', 'calendar', 'appointment', 'date'],
32
65
  dark: ['dark', 'theme'],
33
66
  dashboard: ['dashboard', 'data-table', 'sidebar', 'chart', 'stat', 'metric'],
@@ -141,19 +174,19 @@ const nativeComponentSearchText = (component, platform) => [
141
174
  ].join(' ');
142
175
  const scoreCandidate = (query, terms, searchableText, primaryName = '') => {
143
176
  const normalizedText = normalize(searchableText);
144
- const matchedTerms = terms.filter(term => (Reflect.get(searchTermAliases, term) ?? [term]).some(candidate => normalizedText.includes(candidate)));
177
+ const matchedTerms = terms.filter(term => ((Object.hasOwn(searchTermAliases, term) ? searchTermAliases[term] : undefined) ?? [term]).some(candidate => normalizedText.includes(candidate)));
145
178
  if (matchedTerms.length === 0)
146
179
  return { matchedTerms, score: 0 };
147
180
  const normalizedQuery = terms.join('-');
148
181
  const normalizedName = normalize(primaryName);
149
182
  const coverage = matchedTerms.length / terms.length;
150
183
  let score = matchedTerms.reduce((total, term) => total +
151
- ((Reflect.get(searchTermAliases, term) ?? [term]).some(candidate => normalizedName.includes(candidate)) ?
184
+ (((Object.hasOwn(searchTermAliases, term) ? searchTermAliases[term] : undefined) ?? [term]).some(candidate => normalizedName.includes(candidate)) ?
152
185
  20 :
153
186
  5), 0);
154
187
  score += Math.round(coverage * 80);
155
188
  score -= (terms.length - matchedTerms.length) * 3;
156
- if (terms.some(term => (Reflect.get(searchTermAliases, term) ?? [term]).includes(normalizedName)))
189
+ if (terms.some(term => ((Object.hasOwn(searchTermAliases, term) ? searchTermAliases[term] : undefined) ?? [term]).includes(normalizedName)))
157
190
  score += 150;
158
191
  if (normalizedText.includes(normalizedQuery))
159
192
  score += 100;
@@ -199,6 +232,12 @@ const collectComponentSearchResults = (query, terms, data, framework, platform)
199
232
  }
200
233
  return results;
201
234
  };
235
+ const spacingValue = (tokens, token) => {
236
+ const value = tokens.spacing[token];
237
+ if (value === undefined)
238
+ throw new Error(`Missing spacing dimension for ${token}.`);
239
+ return value;
240
+ };
202
241
  const collectReferenceSearchResults = (query, terms, data) => {
203
242
  const results = [];
204
243
  for (const recipe of data.recipes) {
@@ -216,6 +255,10 @@ const collectReferenceSearchResults = (query, terms, data) => {
216
255
  name: token
217
256
  }, query, terms, token);
218
257
  }
258
+ for (const [role, token] of Object.entries(data.tokens.spacingRoles)) {
259
+ const description = `Content flow spacing role ${role}: ${spacingValue(data.tokens, token)}px; CSS --ui-space-${role}.`;
260
+ appendSearchResult(results, { description, kind: 'token', name: role }, query, terms, `${description} layout gap padding rhythm`);
261
+ }
219
262
  for (const line of data.rules.split('\n').filter(rule => rule.trim())) {
220
263
  appendSearchResult(results, {
221
264
  description: line.trim(),
@@ -509,6 +552,22 @@ export const getNativeComponent = (args, data = loadLumenData()) => {
509
552
  text: formatNativeComponent(component, args.platform, detail, data)
510
553
  };
511
554
  };
555
+ const recipeExamples = (recipe, framework) => {
556
+ const frameworks = [framework];
557
+ return frameworks.flatMap(target => {
558
+ const source = recipe.examples[target];
559
+ if (!source)
560
+ return [];
561
+ return [
562
+ '',
563
+ `## Complete composition (${target})`,
564
+ 'Wire application actions and persistence before shipping; replace sample IDs when rendering multiple instances.',
565
+ `\`\`\`${{ astro: 'astro', elements: 'html', react: 'tsx' }[target]}`,
566
+ source,
567
+ '```'
568
+ ];
569
+ });
570
+ };
512
571
  export const getRecipe = (args, data = loadLumenData()) => {
513
572
  const recipe = resolveRecipe(args.name, data);
514
573
  if (!recipe) {
@@ -536,7 +595,8 @@ export const getRecipe = (args, data = loadLumenData()) => {
536
595
  `Install for ${framework}: ${getRecipeInstall(recipe.install, framework)}`,
537
596
  '',
538
597
  '## Components',
539
- ...componentLines
598
+ ...componentLines,
599
+ ...recipeExamples(recipe, framework)
540
600
  ].join('\n')
541
601
  };
542
602
  };
@@ -550,7 +610,7 @@ export const search = (args, data = loadLumenData()) => {
550
610
  };
551
611
  }
552
612
  const limit = Math.max(1, Math.min(args.limit ?? 20, 100));
553
- const results = collectSearchResults(query, meaningfulSearchTerms(query), data, args.framework, args.platform);
613
+ const results = collectSearchResults(query, meaningfulSearchTerms(query), data, args.framework, args.platform).filter(result => !args.kind || result.kind === args.kind);
554
614
  const kindPriority = {
555
615
  component: 0,
556
616
  'native-component': 1,
@@ -775,6 +835,16 @@ export const getTokens = (data = loadLumenData()) => {
775
835
  const text = [
776
836
  '# Lumen design tokens',
777
837
  `Theme attribute: ${tokens.themeAttribute} (values: light, dark)`,
838
+ `Appearance presets: ${tokens.presets.names.join(', ')} via ${tokens.presets.attribute}; scheme via ${tokens.presets.schemeAttribute}.`,
839
+ 'Studio follows the neutral PostLens appearance. Glass is explicit on supporting surfaces; native adapters use platform materials or opaque fallbacks.',
840
+ '',
841
+ '## Spacing scale (px at the default 16px root; CSS uses rem)',
842
+ Object.entries(tokens.spacing).map(([name, value]) => `- ${name}: ${value}px; --ui-space-${name}`).join('\n'),
843
+ '',
844
+ '## Content flow roles',
845
+ Object.entries(tokens.spacingRoles).map(([role, token]) => `- ${role}: ${token} (${spacingValue(tokens, token)}px); --ui-space-${role}`).join('\n'),
846
+ 'Stack and Grid own sibling gaps; surfaces own padding; Field owns control feedback spacing.',
847
+ 'Use related, group or section for Stack/Grid gap. Use inset for surface padding, not a gap prop.',
778
848
  '',
779
849
  '## Semantic token names (use as `text-ink`, `bg-surface`, `border-line`, etc.)',
780
850
  tokens.semantic.map(token => `- ${token}`).join('\n'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@santi020k/lumen-mcp",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -33,16 +33,19 @@
33
33
  "lumen-mcp-http": "./bin/lumen-mcp-http.mjs"
34
34
  },
35
35
  "dependencies": {
36
- "@modelcontextprotocol/sdk": "^1.30.1",
36
+ "@modelcontextprotocol/express": "^2.0.1",
37
+ "@modelcontextprotocol/server": "^2.2.0",
38
+ "express": "^5.2.1",
37
39
  "zod": "^4.6.5"
38
40
  },
39
41
  "devDependencies": {
40
42
  "@astrojs/compiler": "^4.0.0",
43
+ "@modelcontextprotocol/client": "^2.2.0",
41
44
  "@types/express": "^5.0.6",
42
- "@types/node": "^26.6.2",
43
- "@vitest/coverage-v8": "^5.0.1",
45
+ "@types/node": "^26.6.4",
46
+ "@vitest/coverage-v8": "^5.0.3",
44
47
  "typescript": "~6.0.3",
45
- "vitest": "^5.0.1"
48
+ "vitest": "^5.0.3"
46
49
  },
47
50
  "engines": {
48
51
  "node": ">=20.20.0"