@castandcrew/spotlight-mcp 0.1.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.
Files changed (57) hide show
  1. package/README.md +265 -0
  2. package/data/index.json +1541 -0
  3. package/dist/data/loader.d.ts +3 -0
  4. package/dist/data/loader.js +19 -0
  5. package/dist/data/loader.js.map +1 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +13 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/server.d.ts +2 -0
  10. package/dist/server.js +33 -0
  11. package/dist/server.js.map +1 -0
  12. package/dist/tools/ask_spotlight.d.ts +2 -0
  13. package/dist/tools/ask_spotlight.js +122 -0
  14. package/dist/tools/ask_spotlight.js.map +1 -0
  15. package/dist/tools/get_component.d.ts +2 -0
  16. package/dist/tools/get_component.js +19 -0
  17. package/dist/tools/get_component.js.map +1 -0
  18. package/dist/tools/get_migration_guide.d.ts +2 -0
  19. package/dist/tools/get_migration_guide.js +19 -0
  20. package/dist/tools/get_migration_guide.js.map +1 -0
  21. package/dist/tools/get_pattern.d.ts +2 -0
  22. package/dist/tools/get_pattern.js +28 -0
  23. package/dist/tools/get_pattern.js.map +1 -0
  24. package/dist/tools/get_recipe.d.ts +2 -0
  25. package/dist/tools/get_recipe.js +72 -0
  26. package/dist/tools/get_recipe.js.map +1 -0
  27. package/dist/tools/get_token.d.ts +2 -0
  28. package/dist/tools/get_token.js +18 -0
  29. package/dist/tools/get_token.js.map +1 -0
  30. package/dist/tools/identify_pattern.d.ts +2 -0
  31. package/dist/tools/identify_pattern.js +96 -0
  32. package/dist/tools/identify_pattern.js.map +1 -0
  33. package/dist/tools/list_deprecated.d.ts +2 -0
  34. package/dist/tools/list_deprecated.js +10 -0
  35. package/dist/tools/list_deprecated.js.map +1 -0
  36. package/dist/tools/search_components.d.ts +2 -0
  37. package/dist/tools/search_components.js +27 -0
  38. package/dist/tools/search_components.js.map +1 -0
  39. package/dist/tools/search_tokens.d.ts +2 -0
  40. package/dist/tools/search_tokens.js +33 -0
  41. package/dist/tools/search_tokens.js.map +1 -0
  42. package/dist/tools/search_utility_classes.d.ts +2 -0
  43. package/dist/tools/search_utility_classes.js +37 -0
  44. package/dist/tools/search_utility_classes.js.map +1 -0
  45. package/dist/tools/validate_usage.d.ts +2 -0
  46. package/dist/tools/validate_usage.js +42 -0
  47. package/dist/tools/validate_usage.js.map +1 -0
  48. package/dist/transports/http.d.ts +1 -0
  49. package/dist/transports/http.js +71 -0
  50. package/dist/transports/http.js.map +1 -0
  51. package/dist/transports/stdio.d.ts +1 -0
  52. package/dist/transports/stdio.js +9 -0
  53. package/dist/transports/stdio.js.map +1 -0
  54. package/dist/types.d.ts +120 -0
  55. package/dist/types.js +2 -0
  56. package/dist/types.js.map +1 -0
  57. package/package.json +46 -0
@@ -0,0 +1,33 @@
1
+ import { z } from 'zod';
2
+ import Fuse from 'fuse.js';
3
+ import { loadIndex } from '../data/loader.js';
4
+ export function registerSearchTokens(server) {
5
+ server.tool('search_tokens', 'Search Spotlight design tokens by name, description, or category.', {
6
+ query: z.string().describe('Semantic description, e.g. "large spacing", "positive color", "border radius"'),
7
+ category: z.string().optional().describe('Filter by category: "spacing", "color-semantic", "typography", etc.'),
8
+ limit: z.number().optional().default(10).describe('Max results to return (default 10)'),
9
+ }, async ({ query, category, limit }) => {
10
+ const index = loadIndex();
11
+ const tokens = category
12
+ ? index.tokens.filter(t => t.category.toLowerCase().includes(category.toLowerCase()))
13
+ : index.tokens;
14
+ const fuse = new Fuse(tokens, {
15
+ keys: ['name', 'useCase', 'description', 'category', 'variant', 'property'],
16
+ threshold: 0.4,
17
+ ignoreLocation: true,
18
+ includeScore: true,
19
+ });
20
+ const results = fuse.search(query, { limit }).map(r => ({
21
+ name: r.item.name,
22
+ value: r.item.value,
23
+ category: r.item.category,
24
+ layer: r.item.layer,
25
+ useCase: r.item.useCase,
26
+ score: r.score,
27
+ }));
28
+ return {
29
+ content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
30
+ };
31
+ });
32
+ }
33
+ //# sourceMappingURL=search_tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search_tokens.js","sourceRoot":"","sources":["../../src/tools/search_tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,IAAI,MAAM,SAAS,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAG9C,MAAM,UAAU,oBAAoB,CAAC,MAAiB;IACpD,MAAM,CAAC,IAAI,CACT,eAAe,EACf,mEAAmE,EACnE;QACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,+EAA+E,CAAC;QAC3G,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qEAAqE,CAAC;QAC/G,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,oCAAoC,CAAC;KACxF,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE;QACnC,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,QAAQ;YACrB,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;YACrF,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;QAEjB,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,MAAM,EAAE;YAC5B,IAAI,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,CAAC;YAC3E,SAAS,EAAE,GAAG;YACd,cAAc,EAAE,IAAI;YACpB,YAAY,EAAE,IAAI;SACnB,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACtD,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI;YACjB,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK;YACnB,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ;YACzB,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK;YACnB,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO;YACvB,KAAK,EAAE,CAAC,CAAC,KAAK;SACf,CAAC,CAAC,CAAC;QACJ,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC7E,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerSearchUtilityClasses(server: McpServer): void;
@@ -0,0 +1,37 @@
1
+ import { z } from 'zod';
2
+ import Fuse from 'fuse.js';
3
+ import { loadIndex } from '../data/loader.js';
4
+ export function registerSearchUtilityClasses(server) {
5
+ server.tool('search_utility_classes', 'Search Spotlight CSS utility classes (e.g. ".spotlight_body-xs") by name, category, or subcategory. Use to find the helper class for typography, layout, spacing, alignment, etc.', {
6
+ query: z.string().describe('Search query, e.g. "body text", "heading", "flex", "grid column"'),
7
+ category: z.string().optional().describe('Filter by category: "typography", "layout", "spacing", etc.'),
8
+ limit: z.number().optional().default(10).describe('Max results to return (default 10)'),
9
+ }, async ({ query, category, limit }) => {
10
+ const index = loadIndex();
11
+ const classes = category
12
+ ? index.utilityClasses.filter(c => c.category.toLowerCase().includes(category.toLowerCase()))
13
+ : index.utilityClasses;
14
+ const fuse = new Fuse(classes, {
15
+ keys: ['name', 'category', 'subCategory', 'scale'],
16
+ threshold: 0.4,
17
+ ignoreLocation: true,
18
+ useExtendedSearch: true,
19
+ includeScore: true,
20
+ });
21
+ // OR each whitespace-separated word so multi-word queries ("body text")
22
+ // match items containing any term, instead of one fuzzy blob that matches none.
23
+ const orQuery = query.trim().split(/\s+/).map(t => `'${t}`).join(' | ');
24
+ const results = fuse.search(orQuery, { limit }).map(r => ({
25
+ name: r.item.name,
26
+ category: r.item.category,
27
+ subCategory: r.item.subCategory,
28
+ scale: r.item.scale,
29
+ responsive: r.item.responsive,
30
+ score: r.score,
31
+ }));
32
+ return {
33
+ content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
34
+ };
35
+ });
36
+ }
37
+ //# sourceMappingURL=search_utility_classes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search_utility_classes.js","sourceRoot":"","sources":["../../src/tools/search_utility_classes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,IAAI,MAAM,SAAS,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAG9C,MAAM,UAAU,4BAA4B,CAAC,MAAiB;IAC5D,MAAM,CAAC,IAAI,CACT,wBAAwB,EACxB,mLAAmL,EACnL;QACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kEAAkE,CAAC;QAC9F,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6DAA6D,CAAC;QACvG,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,oCAAoC,CAAC;KACxF,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE;QACnC,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,QAAQ;YACtB,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;YAC7F,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC;QAEzB,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE;YAC7B,IAAI,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,OAAO,CAAC;YAClD,SAAS,EAAE,GAAG;YACd,cAAc,EAAE,IAAI;YACpB,iBAAiB,EAAE,IAAI;YACvB,YAAY,EAAE,IAAI;SACnB,CAAC,CAAC;QACH,wEAAwE;QACxE,gFAAgF;QAChF,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxE,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACxD,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI;YACjB,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ;YACzB,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW;YAC/B,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK;YACnB,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU;YAC7B,KAAK,EAAE,CAAC,CAAC,KAAK;SACf,CAAC,CAAC,CAAC;QACJ,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC7E,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerValidateUsage(server: McpServer): void;
@@ -0,0 +1,42 @@
1
+ import { z } from 'zod';
2
+ import { loadIndex } from '../data/loader.js';
3
+ export function registerValidateUsage(server) {
4
+ server.tool('validate_usage', 'Check a JSX/TSX snippet against Spotlight rules and return any violations.', { code: z.string().describe('JSX or TSX code snippet to validate') }, async ({ code }) => {
5
+ const index = loadIndex();
6
+ const violations = [];
7
+ for (const rule of index.rules) {
8
+ const patternToCheck = rule.pattern ?? rule.incorrect;
9
+ if (!patternToCheck)
10
+ continue;
11
+ const matched = rule.pattern
12
+ ? new RegExp(rule.pattern).test(code)
13
+ : code.includes(patternToCheck.trim());
14
+ if (matched) {
15
+ violations.push({
16
+ ruleId: rule.id,
17
+ severity: rule.severity,
18
+ title: rule.title,
19
+ hint: rule.correct ? `Use instead: ${rule.correct}` : '',
20
+ });
21
+ }
22
+ }
23
+ // Check for deprecated component usage
24
+ for (const dep of index.deprecated) {
25
+ if (code.includes(`<${dep.component}`) || code.includes(`${dep.component} `)) {
26
+ violations.push({
27
+ ruleId: `deprecated-${dep.component.toLowerCase()}`,
28
+ severity: 'warning',
29
+ title: `${dep.component} is deprecated${dep.removalDate ? ` (removal: ${dep.removalDate})` : ''}`,
30
+ hint: dep.replacement ? `Use ${dep.replacement} instead.` : dep.notes ?? '',
31
+ });
32
+ }
33
+ }
34
+ const result = violations.length
35
+ ? { valid: false, violations }
36
+ : { valid: true, message: 'No violations found.' };
37
+ return {
38
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
39
+ };
40
+ });
41
+ }
42
+ //# sourceMappingURL=validate_usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate_usage.js","sourceRoot":"","sources":["../../src/tools/validate_usage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAG9C,MAAM,UAAU,qBAAqB,CAAC,MAAiB;IACrD,MAAM,CAAC,IAAI,CACT,gBAAgB,EAChB,4EAA4E,EAC5E,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC,EAAE,EACpE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACjB,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;QAC1B,MAAM,UAAU,GAAwE,EAAE,CAAC;QAE3F,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC/B,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC;YACtD,IAAI,CAAC,cAAc;gBAAE,SAAS;YAE9B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO;gBAC1B,CAAC,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;gBACrC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC;YAEzC,IAAI,OAAO,EAAE,CAAC;gBACZ,UAAU,CAAC,IAAI,CAAC;oBACd,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE;iBACzD,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,uCAAuC;QACvC,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACnC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;gBAC7E,UAAU,CAAC,IAAI,CAAC;oBACd,MAAM,EAAE,cAAc,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE;oBACnD,QAAQ,EAAE,SAAS;oBACnB,KAAK,EAAE,GAAG,GAAG,CAAC,SAAS,iBAAiB,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;oBACjG,IAAI,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,WAAW,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;iBAC5E,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM;YAC9B,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE;YAC9B,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC;QAErD,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC5E,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function runHttp(port?: number): Promise<void>;
@@ -0,0 +1,71 @@
1
+ import express from 'express';
2
+ import { randomUUID } from 'crypto';
3
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
4
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
5
+ import { createServer } from '../server.js';
6
+ const DEFAULT_PORT = Number(process.env.PORT ?? 3000);
7
+ export async function runHttp(port = DEFAULT_PORT) {
8
+ const app = express();
9
+ app.use(express.json());
10
+ // ── Streamable HTTP (MCP spec 2025-03-26, new standard) ──────────────────
11
+ const streamableSessions = new Map();
12
+ app.post('/mcp', async (req, res) => {
13
+ const sessionId = req.headers['mcp-session-id'];
14
+ if (sessionId && streamableSessions.has(sessionId)) {
15
+ await streamableSessions.get(sessionId).transport.handleRequest(req, res, req.body);
16
+ return;
17
+ }
18
+ const server = createServer();
19
+ const transport = new StreamableHTTPServerTransport({
20
+ sessionIdGenerator: () => randomUUID(),
21
+ onsessioninitialized: (id) => {
22
+ streamableSessions.set(id, { server, transport });
23
+ },
24
+ });
25
+ transport.onclose = () => {
26
+ if (transport.sessionId)
27
+ streamableSessions.delete(transport.sessionId);
28
+ };
29
+ await server.connect(transport);
30
+ await transport.handleRequest(req, res, req.body);
31
+ });
32
+ app.delete('/mcp', async (req, res) => {
33
+ const sessionId = req.headers['mcp-session-id'];
34
+ const session = sessionId ? streamableSessions.get(sessionId) : undefined;
35
+ if (!session) {
36
+ res.status(404).json({ error: 'Session not found' });
37
+ return;
38
+ }
39
+ await session.transport.handleRequest(req, res);
40
+ streamableSessions.delete(sessionId);
41
+ });
42
+ // ── SSE (legacy transport for older MCP clients) ─────────────────────────
43
+ const sseSessions = new Map();
44
+ app.get('/sse', async (req, res) => {
45
+ const transport = new SSEServerTransport('/messages', res);
46
+ sseSessions.set(transport.sessionId, transport);
47
+ res.on('close', () => sseSessions.delete(transport.sessionId));
48
+ const server = createServer();
49
+ await server.connect(transport);
50
+ });
51
+ app.post('/messages', async (req, res) => {
52
+ const sessionId = req.query.sessionId;
53
+ const transport = sseSessions.get(sessionId);
54
+ if (!transport) {
55
+ res.status(404).json({ error: 'Session not found' });
56
+ return;
57
+ }
58
+ await transport.handlePostMessage(req, res, req.body);
59
+ });
60
+ // ── Health ────────────────────────────────────────────────────────────────
61
+ app.get('/health', (_, res) => {
62
+ res.json({ status: 'ok', server: 'spotlight-mcp', version: '0.1.0' });
63
+ });
64
+ app.listen(port, () => {
65
+ console.error(`Spotlight MCP HTTP server running on port ${port}`);
66
+ console.error(` Streamable HTTP : POST http://localhost:${port}/mcp`);
67
+ console.error(` SSE (legacy) : GET http://localhost:${port}/sse`);
68
+ console.error(` Health : GET http://localhost:${port}/health`);
69
+ });
70
+ }
71
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/transports/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function runStdio(): Promise<void>;
@@ -0,0 +1,9 @@
1
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
2
+ import { createServer } from '../server.js';
3
+ export async function runStdio() {
4
+ const server = createServer();
5
+ const transport = new StdioServerTransport();
6
+ await server.connect(transport);
7
+ console.error('Spotlight MCP running on stdio');
8
+ }
9
+ //# sourceMappingURL=stdio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/transports/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;AAClD,CAAC"}
@@ -0,0 +1,120 @@
1
+ export interface ComponentProp {
2
+ name: string;
3
+ type: string;
4
+ required: boolean;
5
+ defaultValue?: string;
6
+ description: string;
7
+ importantNote?: string;
8
+ }
9
+ export interface ComponentStory {
10
+ name: string;
11
+ url: string;
12
+ /** Example code reconstructed from the story's args, or the story's render body. */
13
+ code?: string;
14
+ }
15
+ export interface ComponentUsage {
16
+ whenToUse?: string;
17
+ whenNotToUse?: string;
18
+ examples?: string[];
19
+ }
20
+ export interface Component {
21
+ id: string;
22
+ name: string;
23
+ category: string;
24
+ sourcePath: string;
25
+ status: 'stable' | 'deprecated' | 'legacy';
26
+ description: string;
27
+ muiEquivalent?: string;
28
+ reactAriaBase?: string;
29
+ props: ComponentProp[];
30
+ /** Where props came from: 'local' types.ts, or re-exported 'mui' types. */
31
+ propsSource?: 'local' | 'mui';
32
+ /** When propsSource is 'mui', the MUI props type name (e.g. "ButtonGroupProps"). */
33
+ muiPropsType?: string;
34
+ /** True when a content/components/*.md doc augmented this entry. */
35
+ manualDoc?: boolean;
36
+ stories: ComponentStory[];
37
+ usage: ComponentUsage;
38
+ tokens: string[];
39
+ relatedComponents: string[];
40
+ /** Patterns/guides relevant to this component (selection, placement, labeling, etc.). */
41
+ relatedPatterns?: {
42
+ id: string;
43
+ title: string;
44
+ }[];
45
+ importPath: string;
46
+ }
47
+ export interface Token {
48
+ name: string;
49
+ value: string;
50
+ category: string;
51
+ usedInComponents: string[];
52
+ description?: string;
53
+ pairedWith?: string;
54
+ /** Decomposed naming-formula segments (from token-dictionary.json). */
55
+ property?: string;
56
+ variant?: string;
57
+ element?: string;
58
+ hierarchy?: string;
59
+ scale?: string;
60
+ weight?: string;
61
+ states?: string[];
62
+ /** ref (raw palette) vs semantic (purpose-named, what you should use). */
63
+ layer?: 'ref' | 'semantic';
64
+ /** CSS selectors/states this token applies to (e.g. ".spotlight_interactive:focus"). */
65
+ appliesTo?: string[];
66
+ /** Human-readable intent derived from the name formula — "what to use this for". */
67
+ useCase?: string;
68
+ }
69
+ export interface Pattern {
70
+ id: string;
71
+ title: string;
72
+ category: string;
73
+ description: string;
74
+ content: string;
75
+ components: string[];
76
+ sourcePath: string;
77
+ }
78
+ export interface Rule {
79
+ id: string;
80
+ severity: 'error' | 'warning' | 'info';
81
+ title: string;
82
+ correct?: string;
83
+ incorrect?: string;
84
+ pattern?: string;
85
+ sourcePath: string;
86
+ }
87
+ export interface MigrationEntry {
88
+ fromComponent: string;
89
+ toComponent: string;
90
+ breaking: boolean;
91
+ deadline?: string;
92
+ propsMapping: Record<string, string>;
93
+ notes?: string;
94
+ }
95
+ export interface DeprecatedEntry {
96
+ component: string;
97
+ removalDate?: string;
98
+ replacement?: string;
99
+ status?: string;
100
+ notes?: string;
101
+ }
102
+ export interface UtilityClass {
103
+ name: string;
104
+ category: string;
105
+ subCategory?: string;
106
+ scale?: string;
107
+ responsive?: boolean;
108
+ value?: string | null;
109
+ }
110
+ export interface SpotlightIndex {
111
+ version: string;
112
+ generated: string;
113
+ components: Component[];
114
+ patterns: Pattern[];
115
+ tokens: Token[];
116
+ rules: Rule[];
117
+ migration: MigrationEntry[];
118
+ deprecated: DeprecatedEntry[];
119
+ utilityClasses: UtilityClass[];
120
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@castandcrew/spotlight-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for the Spotlight Design System",
5
+ "type": "module",
6
+ "bin": {
7
+ "spotlight-mcp": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "data"
12
+ ],
13
+ "scripts": {
14
+ "dev:stdio": "tsx src/index.ts",
15
+ "dev:http": "tsx src/index.ts --http",
16
+ "build": "tsc",
17
+ "prepare": "pnpm build",
18
+ "start": "node dist/index.js",
19
+ "start:http": "node dist/index.js --http",
20
+ "index": "tsx scripts/indexer.ts",
21
+ "test": "pnpm build && vitest run"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "dependencies": {
27
+ "@modelcontextprotocol/sdk": "^1.10.2",
28
+ "express": "^4.22.2",
29
+ "fuse.js": "^7.4.2",
30
+ "zod": "^3.24.2"
31
+ },
32
+ "devDependencies": {
33
+ "@types/express": "^5.0.1",
34
+ "@types/node": "^22.15.3",
35
+ "gray-matter": "^4.0.3",
36
+ "ts-morph": "^28.0.0",
37
+ "tsx": "^4.19.4",
38
+ "typescript": "^5.8.3",
39
+ "vitest": "^4.1.9"
40
+ },
41
+ "pnpm": {
42
+ "onlyBuiltDependencies": [
43
+ "esbuild"
44
+ ]
45
+ }
46
+ }