@splunk/splunk-ui-mcp 0.1.0 → 0.2.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 (33) hide show
  1. package/README.md +10 -3
  2. package/lib/constants/versions.js +3 -3
  3. package/lib/index.js +6 -4
  4. package/lib/resources/components.js +12 -6
  5. package/lib/resources/design-tokens.d.ts +17 -0
  6. package/lib/resources/design-tokens.js +113 -0
  7. package/lib/tools/find_component.d.ts +19 -0
  8. package/lib/tools/find_component.js +93 -0
  9. package/lib/tools/find_design_token.d.ts +16 -0
  10. package/lib/tools/find_design_token.js +126 -0
  11. package/lib/tools/{find_icons.d.ts → find_icon.d.ts} +2 -2
  12. package/lib/tools/{find_icons.js → find_icon.js} +5 -5
  13. package/lib/utils/component-catalog.d.ts +19 -2
  14. package/lib/utils/component-catalog.js +77 -29
  15. package/lib/utils/design-token-catalog.d.ts +33 -0
  16. package/lib/utils/design-token-catalog.js +158 -0
  17. package/lib/utils/package-assets.d.ts +8 -0
  18. package/lib/utils/package-assets.js +35 -0
  19. package/package.json +14 -10
  20. package/lib/resources/tests/components.unit.d.ts +0 -1
  21. package/lib/resources/tests/components.unit.js +0 -133
  22. package/lib/resources/tests/icons.unit.d.ts +0 -1
  23. package/lib/resources/tests/icons.unit.js +0 -161
  24. package/lib/tools/get_component_docs.d.ts +0 -19
  25. package/lib/tools/get_component_docs.js +0 -82
  26. package/lib/tools/tests/find_icons.unit.d.ts +0 -1
  27. package/lib/tools/tests/find_icons.unit.js +0 -149
  28. package/lib/tools/tests/get_component_docs.unit.d.ts +0 -1
  29. package/lib/tools/tests/get_component_docs.unit.js +0 -131
  30. package/lib/tools/tests/requirements.unit.d.ts +0 -1
  31. package/lib/tools/tests/requirements.unit.js +0 -34
  32. package/lib/utils/tests/component-catalog.unit.d.ts +0 -1
  33. package/lib/utils/tests/component-catalog.unit.js +0 -144
package/README.md CHANGED
@@ -10,7 +10,7 @@ A Model Context Protocol (MCP) server that gives AI assistants Splunk UI Design
10
10
 
11
11
  - Node.js 22 or newer
12
12
  - An MCP-compatible client (VS Code, Cursor, Windsurf, Claude Desktop, Goose, etc.)
13
- - Access to @splunk/react-ui and @splunk/react-icons (used for component docs and icon metadata)
13
+ - Access to @splunk/react-ui, @splunk/react-icons, and @splunk/themes
14
14
 
15
15
  ## Install and configure
16
16
 
@@ -35,8 +35,9 @@ If you need the underlying tool or resource names (for debugging or client integ
35
35
 
36
36
  ### Tools
37
37
 
38
- - `get_component_docs`: Retrieve full docs for a `@splunk/react-ui` component.
39
- - `find_icons`: Search `@splunk/react-icons` and return a recommended icon plus alternatives.
38
+ - `find_component`: Find `@splunk/react-ui` components by exact name or fuzzy query; returns full docs for exact matches and suggestions otherwise.
39
+ - `find_icon`: Search `@splunk/react-icons` and return a recommended icon plus alternatives.
40
+ - `find_design_token`: Search `@splunk/themes` design tokens and return recommended matches.
40
41
  - `list_splunk_ui_requirements`: Return version requirements and peer dependencies.
41
42
 
42
43
  ### Resources
@@ -52,3 +53,9 @@ mcp://splunk-ui/components/{componentName}
52
53
  ```
53
54
  mcp://splunk-ui/icons/{iconName}
54
55
  ```
56
+
57
+ **Design tokens**
58
+
59
+ ```
60
+ mcp://splunk-ui/design-tokens/{tokenName}
61
+ ```
@@ -2,11 +2,11 @@
2
2
  // This file contains version information that gets injected at build time
3
3
  // DO NOT EDIT MANUALLY - this file is auto-generated during build
4
4
  const VERSIONS = {
5
- "@splunk/react-ui": "5.8.0",
5
+ "@splunk/react-ui": "5.9.0",
6
6
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
7
7
  "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0",
8
8
  "styled-components": "^5.3.10",
9
- "@splunk/themes": "1.5.0",
10
- "@splunk/react-icons": "5.8.0"
9
+ "@splunk/themes": "1.6.0",
10
+ "@splunk/react-icons": "5.9.0"
11
11
  };
12
12
  export default VERSIONS;
package/lib/index.js CHANGED
@@ -2,10 +2,12 @@
2
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import requirements from "./tools/requirements.js";
5
- import findIcons from "./tools/find_icons.js";
6
- import getComponentDocs from "./tools/get_component_docs.js";
5
+ import findIcon from "./tools/find_icon.js";
6
+ import findComponent from "./tools/find_component.js";
7
+ import findDesignToken from "./tools/find_design_token.js";
7
8
  import iconResource from "./resources/icons.js";
8
9
  import componentResource from "./resources/components.js";
10
+ import designTokenResource from "./resources/design-tokens.js";
9
11
  // prettier-ignore
10
12
  import packageJson from '../package.json' with { type: 'json' };
11
13
  // Create an MCP server
@@ -19,11 +21,11 @@ const server = new McpServer({
19
21
  },
20
22
  });
21
23
  // Register all tools
22
- const tools = [requirements, findIcons, getComponentDocs];
24
+ const tools = [requirements, findIcon, findComponent, findDesignToken];
23
25
  tools.forEach((tool) => {
24
26
  server.registerTool(tool.name, tool.config, tool.handler);
25
27
  });
26
- const resources = [iconResource, componentResource];
28
+ const resources = [iconResource, componentResource, designTokenResource];
27
29
  resources.forEach((resource) => {
28
30
  server.registerResource(resource.name, resource.template, resource.config, resource.handler);
29
31
  });
@@ -1,8 +1,8 @@
1
1
  import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { filterByKeywords } from '@splunk/ui-utils/filter.js';
3
- import { getComponentList, getComponentDocs, getComponentInfo, } from "../utils/component-catalog.js";
2
+ import { getComponentList, getComponentNames, getComponentDocs, getComponentInfo, searchComponents, } from "../utils/component-catalog.js";
4
3
  export const COMPONENT_RESOURCE_NAME = 'splunk-ui-components';
5
4
  export const COMPONENT_RESOURCE_URI_BASE = 'mcp://splunk-ui/components';
5
+ const CATEGORY_SEARCH_MIN_QUERY_LENGTH = 3;
6
6
  /**
7
7
  * Creates an MCP resource URI for a component
8
8
  */
@@ -76,12 +76,13 @@ const componentResourceTemplate = new ResourceTemplate(`${COMPONENT_RESOURCE_URI
76
76
  complete: {
77
77
  componentName: async (value) => {
78
78
  const input = value?.toString() ?? '';
79
+ const componentNames = getComponentNames();
80
+ const includeCategory = input.trim().length >= CATEGORY_SEARCH_MIN_QUERY_LENGTH;
79
81
  // Return all component names if no input is provided
80
82
  if (!input) {
81
- return getComponentList().map((component) => component.name);
83
+ return componentNames;
82
84
  }
83
- const componentNames = getComponentList().map((component) => component.name);
84
- return filterByKeywords(componentNames, input);
85
+ return searchComponents(input, { includeCategory });
85
86
  },
86
87
  },
87
88
  });
@@ -101,7 +102,12 @@ const componentResource = {
101
102
  // Prefer the componentName param, but fall back to extracting from the path.
102
103
  let componentName;
103
104
  if (typeof componentNameParam === 'string' && componentNameParam.length > 0) {
104
- componentName = componentNameParam;
105
+ try {
106
+ componentName = decodeURIComponent(componentNameParam);
107
+ }
108
+ catch {
109
+ throw new Error(`Invalid component name in resource params: ${componentNameParam}`);
110
+ }
105
111
  }
106
112
  else {
107
113
  // Extract from path, but exclude 'components' itself
@@ -0,0 +1,17 @@
1
+ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ResourceLink, ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';
3
+ export declare const DESIGN_TOKEN_RESOURCE_NAME = "splunk-ui-design-tokens";
4
+ export declare const DESIGN_TOKEN_RESOURCE_URI_BASE = "mcp://splunk-ui/design-tokens";
5
+ export declare const createDesignTokenResourceUri: (tokenName: string) => string;
6
+ export declare const createDesignTokenResourceLink: (tokenName: string) => ResourceLink;
7
+ declare const designTokenResource: {
8
+ name: string;
9
+ template: ResourceTemplate;
10
+ config: {
11
+ title: string;
12
+ description: string;
13
+ mimeType: string;
14
+ };
15
+ handler: (uri: URL, params: Record<string, unknown>) => ReadResourceResult;
16
+ };
17
+ export default designTokenResource;
@@ -0,0 +1,113 @@
1
+ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { filterByKeywords, stringToKeywords } from '@splunk/ui-utils/filter.js';
3
+ import { extractReplacementTokenName, getDesignTokenInfo, getDesignTokenList, } from "../utils/design-token-catalog.js";
4
+ export const DESIGN_TOKEN_RESOURCE_NAME = 'splunk-ui-design-tokens';
5
+ export const DESIGN_TOKEN_RESOURCE_URI_BASE = 'mcp://splunk-ui/design-tokens';
6
+ export const createDesignTokenResourceUri = (tokenName) => `${DESIGN_TOKEN_RESOURCE_URI_BASE}/${encodeURIComponent(tokenName)}`;
7
+ const createDesignTokenResourceMetadata = (tokenName) => {
8
+ return {
9
+ name: tokenName,
10
+ title: tokenName,
11
+ uri: createDesignTokenResourceUri(tokenName),
12
+ description: getDesignTokenInfo(tokenName)?.description,
13
+ mimeType: 'application/json',
14
+ };
15
+ };
16
+ export const createDesignTokenResourceLink = (tokenName) => ({
17
+ type: 'resource_link',
18
+ ...createDesignTokenResourceMetadata(tokenName),
19
+ });
20
+ const createDesignTokenResourcePayload = (token) => {
21
+ const replacementToken = extractReplacementTokenName(token.deprecated);
22
+ return {
23
+ name: token.name,
24
+ description: token.description,
25
+ type: token.type,
26
+ deprecated: token.deprecated,
27
+ usage: `variables.${token.name}`,
28
+ replacementToken,
29
+ replacementUsage: replacementToken ? `variables.${replacementToken}` : undefined,
30
+ };
31
+ };
32
+ const createListResult = () => ({
33
+ resources: getDesignTokenList().map((token) => ({
34
+ name: token.name,
35
+ title: token.name,
36
+ uri: createDesignTokenResourceUri(token.name),
37
+ description: token.description,
38
+ mimeType: 'application/json',
39
+ })),
40
+ });
41
+ const readDesignTokenResource = (tokenName) => {
42
+ const token = getDesignTokenInfo(tokenName);
43
+ if (!token) {
44
+ throw new Error(`Unknown design token "${tokenName}"`);
45
+ }
46
+ const resourcePayload = createDesignTokenResourcePayload(token);
47
+ return {
48
+ contents: [
49
+ {
50
+ uri: createDesignTokenResourceUri(token.name),
51
+ mimeType: 'application/json',
52
+ text: `${JSON.stringify(resourcePayload, null, 2)}\n`,
53
+ },
54
+ ],
55
+ };
56
+ };
57
+ const designTokenResourceTemplate = new ResourceTemplate(`${DESIGN_TOKEN_RESOURCE_URI_BASE}/{tokenName}`, {
58
+ list: async () => createListResult(),
59
+ complete: {
60
+ tokenName: async (value) => {
61
+ const input = value?.toString() ?? '';
62
+ const tokenNames = getDesignTokenList().map((token) => token.name);
63
+ if (!input) {
64
+ return tokenNames.slice(0, 50);
65
+ }
66
+ if (stringToKeywords(input).filter(Boolean).length === 0) {
67
+ return [];
68
+ }
69
+ return filterByKeywords(tokenNames, input).slice(0, 50);
70
+ },
71
+ },
72
+ });
73
+ const designTokenResource = {
74
+ name: DESIGN_TOKEN_RESOURCE_NAME,
75
+ template: designTokenResourceTemplate,
76
+ config: {
77
+ title: 'Splunk UI Design Tokens',
78
+ description: 'Metadata and usage guidance for design tokens from @splunk/themes.',
79
+ mimeType: 'application/json',
80
+ },
81
+ handler: (uri, params) => {
82
+ const { tokenName: tokenNameParam } = params;
83
+ let tokenName;
84
+ if (typeof tokenNameParam === 'string' && tokenNameParam.length > 0) {
85
+ try {
86
+ tokenName = decodeURIComponent(tokenNameParam);
87
+ }
88
+ catch {
89
+ throw new Error(`Invalid design token name in resource params: ${tokenNameParam}`);
90
+ }
91
+ }
92
+ else {
93
+ const pathParts = uri.pathname.split('/').filter(Boolean);
94
+ const encodedTokenName = pathParts[pathParts.length - 1];
95
+ if (encodedTokenName === 'design-tokens') {
96
+ tokenName = undefined;
97
+ }
98
+ else if (encodedTokenName) {
99
+ try {
100
+ tokenName = decodeURIComponent(encodedTokenName);
101
+ }
102
+ catch {
103
+ throw new Error(`Invalid design token name in resource path: ${encodedTokenName}`);
104
+ }
105
+ }
106
+ }
107
+ if (!tokenName) {
108
+ throw new Error(`Design token name missing in resource request: ${uri.toString()}`);
109
+ }
110
+ return readDesignTokenResource(tokenName);
111
+ },
112
+ };
113
+ export default designTokenResource;
@@ -0,0 +1,19 @@
1
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ import { z } from 'zod';
3
+ export declare const findComponentHandler: ({ query }: {
4
+ query?: string;
5
+ }) => CallToolResult;
6
+ declare const findComponentTool: {
7
+ name: string;
8
+ config: {
9
+ title: string;
10
+ description: string;
11
+ inputSchema: {
12
+ query: z.ZodString;
13
+ };
14
+ };
15
+ handler: ({ query }: {
16
+ query?: string;
17
+ }) => CallToolResult;
18
+ };
19
+ export default findComponentTool;
@@ -0,0 +1,93 @@
1
+ import { z } from 'zod';
2
+ import { createComponentResourceLink } from "../resources/components.js";
3
+ import { getComponentDocs, getComponentInfo, getComponentNames, searchComponents, } from "../utils/component-catalog.js";
4
+ const MAX_SUGGESTIONS = 5;
5
+ const MAX_EXAMPLES = 10;
6
+ const CATEGORY_SEARCH_MIN_QUERY_LENGTH = 3;
7
+ const getSuggestedComponents = (trimmedQuery) => {
8
+ const includeCategory = trimmedQuery.length >= CATEGORY_SEARCH_MIN_QUERY_LENGTH;
9
+ return searchComponents(trimmedQuery, {
10
+ includeCategory,
11
+ limit: MAX_SUGGESTIONS,
12
+ });
13
+ };
14
+ export const findComponentHandler = ({ query }) => {
15
+ if (!query || query.trim().length === 0) {
16
+ return {
17
+ content: [
18
+ {
19
+ type: 'text',
20
+ text: 'Component query is required. Provide a component name or keywords.',
21
+ },
22
+ ],
23
+ isError: true,
24
+ };
25
+ }
26
+ const normalizedQuery = query.trim();
27
+ const componentInfo = getComponentInfo(normalizedQuery);
28
+ if (componentInfo) {
29
+ try {
30
+ const docs = getComponentDocs(componentInfo.name);
31
+ return {
32
+ content: [
33
+ {
34
+ type: 'text',
35
+ text: docs,
36
+ },
37
+ createComponentResourceLink(componentInfo.name),
38
+ ],
39
+ structuredContent: {
40
+ query: normalizedQuery,
41
+ resultType: 'exact',
42
+ component: componentInfo.name,
43
+ },
44
+ };
45
+ }
46
+ catch (error) {
47
+ return {
48
+ content: [
49
+ {
50
+ type: 'text',
51
+ text: `Failed to retrieve documentation for "${componentInfo.name}": ${error.message}`,
52
+ },
53
+ ],
54
+ isError: true,
55
+ };
56
+ }
57
+ }
58
+ const suggestions = getSuggestedComponents(normalizedQuery);
59
+ const sampleNames = getComponentNames().slice(0, MAX_EXAMPLES).join(', ');
60
+ const hasSuggestions = suggestions.length > 0;
61
+ const suggestionText = hasSuggestions
62
+ ? `Top matches: ${suggestions.join(', ')}.\n\nUse one of the resource links below to open docs directly.`
63
+ : `No close matches found.\n\nAvailable components include: ${sampleNames}, and more.`;
64
+ return {
65
+ content: [
66
+ {
67
+ type: 'text',
68
+ text: `No exact component match for "${normalizedQuery}".\n\n${suggestionText}`,
69
+ },
70
+ ...suggestions.map((name) => createComponentResourceLink(name)),
71
+ ],
72
+ structuredContent: {
73
+ query: normalizedQuery,
74
+ resultType: 'suggestions',
75
+ suggestions,
76
+ },
77
+ };
78
+ };
79
+ const findComponentTool = {
80
+ name: 'find_component',
81
+ config: {
82
+ title: 'Find Component Documentation',
83
+ description: 'Finds @splunk/react-ui components by exact name or fuzzy query. ' +
84
+ 'Returns full docs for exact matches and ranked suggestions for discovery queries.',
85
+ inputSchema: {
86
+ query: z
87
+ .string()
88
+ .describe('Component name or keywords (e.g., "Button", "CardLayout", "table", "form rows").'),
89
+ },
90
+ },
91
+ handler: findComponentHandler,
92
+ };
93
+ export default findComponentTool;
@@ -0,0 +1,16 @@
1
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ import { z } from 'zod';
3
+ declare const findDesignTokenTool: {
4
+ name: string;
5
+ config: {
6
+ title: string;
7
+ description: string;
8
+ inputSchema: {
9
+ query: z.ZodString;
10
+ };
11
+ };
12
+ handler: ({ query }: {
13
+ query?: string;
14
+ }) => CallToolResult;
15
+ };
16
+ export default findDesignTokenTool;
@@ -0,0 +1,126 @@
1
+ import { z } from 'zod';
2
+ import { createDesignTokenResourceLink, createDesignTokenResourceUri, } from "../resources/design-tokens.js";
3
+ import { extractReplacementTokenName, findDesignTokens, getDesignTokenInfo, isDeprecatedToken, } from "../utils/design-token-catalog.js";
4
+ const NO_RESULTS_RESULT = {
5
+ content: [
6
+ {
7
+ type: 'text',
8
+ text: 'No design tokens found. Try keywords like "text", "background", "border", ' +
9
+ '"spacing", or "shadow".',
10
+ },
11
+ ],
12
+ };
13
+ const getReplacementToken = (token) => {
14
+ if (!token || !isDeprecatedToken(token)) {
15
+ return undefined;
16
+ }
17
+ const replacementTokenName = extractReplacementTokenName(token.deprecated);
18
+ return replacementTokenName ? getDesignTokenInfo(replacementTokenName) : undefined;
19
+ };
20
+ const addUsageReference = (token) => ({
21
+ name: token.name,
22
+ description: token.description,
23
+ type: token.type,
24
+ deprecated: token.deprecated,
25
+ usage: `variables.${token.name}`,
26
+ });
27
+ const formatTokenDetails = (token, options = {}) => {
28
+ const lines = [];
29
+ if (token.description) {
30
+ lines.push(`- Description: ${token.description}`);
31
+ }
32
+ if (token.type) {
33
+ lines.push(`- Type: \`${token.type}\``);
34
+ }
35
+ lines.push(`- Usage: \`variables.${token.name}\``);
36
+ if (isDeprecatedToken(token)) {
37
+ lines.push(options.deprecatedAsBoolean
38
+ ? '- Deprecated: true'
39
+ : `- Deprecated: ${String(token.deprecated)}`);
40
+ }
41
+ const resourceLabel = options.resourceLabel ?? 'Resource';
42
+ lines.push(`- ${resourceLabel}: \`${createDesignTokenResourceUri(token.name)}\``);
43
+ return lines.join('\n');
44
+ };
45
+ const findDesignTokenHandler = ({ query }) => {
46
+ const normalizedQuery = query?.trim() ?? '';
47
+ if (normalizedQuery.length === 0) {
48
+ return NO_RESULTS_RESULT;
49
+ }
50
+ const exactMatch = getDesignTokenInfo(normalizedQuery);
51
+ const deprecatedExactMatch = exactMatch && isDeprecatedToken(exactMatch) ? exactMatch : undefined;
52
+ const exactReplacement = deprecatedExactMatch
53
+ ? getReplacementToken(deprecatedExactMatch)
54
+ : undefined;
55
+ const searchResults = exactMatch ? [] : findDesignTokens(normalizedQuery);
56
+ if (!exactMatch && searchResults.length === 0) {
57
+ return NO_RESULTS_RESULT;
58
+ }
59
+ let results = searchResults;
60
+ if (exactReplacement) {
61
+ results = [exactReplacement];
62
+ }
63
+ else if (exactMatch) {
64
+ results = [exactMatch];
65
+ }
66
+ const [recommended, ...others] = results;
67
+ const alternativeTokens = others.slice(0, 4);
68
+ const summarySections = [];
69
+ if (exactMatch && !deprecatedExactMatch) {
70
+ summarySections.push(`# \`${exactMatch.name}\``, formatTokenDetails(recommended, { resourceLabel: 'Resource link' }), 'Follow the resource links below for full token metadata.');
71
+ }
72
+ else if (deprecatedExactMatch && !exactReplacement) {
73
+ const deprecatedGuidance = typeof deprecatedExactMatch.deprecated === 'string'
74
+ ? `Query matched deprecated token \`${deprecatedExactMatch.name}\`. ${deprecatedExactMatch.deprecated}`
75
+ : `Query matched deprecated token \`${deprecatedExactMatch.name}\`. No replacement recommendation is available from token metadata.`;
76
+ summarySections.push(`# \`${deprecatedExactMatch.name}\``, deprecatedGuidance, formatTokenDetails(recommended, { deprecatedAsBoolean: true }), 'Follow the resource links below for full token metadata.');
77
+ }
78
+ else if (deprecatedExactMatch && exactReplacement) {
79
+ const deprecatedGuidance = typeof deprecatedExactMatch.deprecated === 'string'
80
+ ? `Query matched deprecated token \`${deprecatedExactMatch.name}\`. ${deprecatedExactMatch.deprecated}`
81
+ : `Query matched deprecated token \`${deprecatedExactMatch.name}\`.`;
82
+ summarySections.push(`# \`${deprecatedExactMatch.name}\``, deprecatedGuidance, '## Recommended Token', `Recommended to use \`${recommended.name}\` for "${normalizedQuery}"`, formatTokenDetails(recommended), 'Follow the resource links below for full token metadata.');
83
+ }
84
+ else {
85
+ summarySections.push(`# Design Tokens for "${normalizedQuery}"`, '## Recommended Token', `Recommended to use \`${recommended.name}\` for "${normalizedQuery}"`, formatTokenDetails(recommended), 'Follow the resource links below for full token metadata.');
86
+ }
87
+ if (alternativeTokens.length > 0) {
88
+ summarySections.push('## Alternative Tokens', alternativeTokens
89
+ .map((token) => `- \`${token.name}\`${token.description ? `: ${token.description}` : ''}`)
90
+ .join('\n'));
91
+ }
92
+ return {
93
+ content: [
94
+ {
95
+ type: 'text',
96
+ text: summarySections.join('\n\n'),
97
+ },
98
+ createDesignTokenResourceLink(recommended.name),
99
+ ...alternativeTokens.map((token) => createDesignTokenResourceLink(token.name)),
100
+ ],
101
+ structuredContent: {
102
+ recommended: addUsageReference(getDesignTokenInfo(recommended.name) ?? recommended),
103
+ alternatives: alternativeTokens
104
+ .map((token) => getDesignTokenInfo(token.name) ?? token)
105
+ .filter((token) => !isDeprecatedToken(token))
106
+ .map((token) => addUsageReference(token)),
107
+ },
108
+ };
109
+ };
110
+ const findDesignTokenTool = {
111
+ name: 'find_design_token',
112
+ config: {
113
+ title: 'Find Design Tokens',
114
+ description: 'Search for a Splunk UI design token from @splunk/themes. ' +
115
+ 'In Splunk UI projects, all styling must use styled-components and design tokens for CSS properties as much as possible — ' +
116
+ 'never hardcode hex values, rgb(), or CSS custom properties like var(--something). ' +
117
+ 'Call this tool whenever you need a color, spacing, typography, shadow, or z-index value.',
118
+ inputSchema: {
119
+ query: z
120
+ .string()
121
+ .describe('What you are trying to style, e.g. "button background", "paragraph text color", "input border", "modal background", "spacing between elements", "focus shadow".'),
122
+ },
123
+ },
124
+ handler: findDesignTokenHandler,
125
+ };
126
+ export default findDesignTokenTool;
@@ -1,6 +1,6 @@
1
1
  import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
2
  import { z } from 'zod';
3
- declare const findIconsTool: {
3
+ declare const findIconTool: {
4
4
  name: string;
5
5
  config: {
6
6
  title: string;
@@ -13,4 +13,4 @@ declare const findIconsTool: {
13
13
  query?: string;
14
14
  }) => CallToolResult;
15
15
  };
16
- export default findIconsTool;
16
+ export default findIconTool;
@@ -30,7 +30,7 @@ const formatIconDetails = (details) => {
30
30
  lines.push(details.usageExample);
31
31
  return lines.join('\n');
32
32
  };
33
- const findIconsHandler = ({ query }) => {
33
+ const findIconHandler = ({ query }) => {
34
34
  if (!query || query.trim().length === 0) {
35
35
  return NO_RESULTS_RESULT;
36
36
  }
@@ -86,8 +86,8 @@ const findIconsHandler = ({ query }) => {
86
86
  structuredContent,
87
87
  };
88
88
  };
89
- const findIconsTool = {
90
- name: 'find_icons',
89
+ const findIconTool = {
90
+ name: 'find_icon',
91
91
  config: {
92
92
  title: 'Find Icons',
93
93
  description: 'Search @splunk/react-icons for the best match and a few alternatives based on your keyword.',
@@ -97,6 +97,6 @@ const findIconsTool = {
97
97
  .describe('The search query for the icon, e.g., "search", "notification", "user".'),
98
98
  },
99
99
  },
100
- handler: findIconsHandler,
100
+ handler: findIconHandler,
101
101
  };
102
- export default findIconsTool;
102
+ export default findIconTool;
@@ -8,14 +8,30 @@ export type ComponentInfo = {
8
8
  * @internal
9
9
  */
10
10
  export declare function setDocsLlmPath(path: string | null): void;
11
+ /**
12
+ * Normalizes component names for case-insensitive and format-insensitive matching.
13
+ * Examples:
14
+ * - "Card Layout" -> "cardlayout"
15
+ * - "CardLayout" -> "cardlayout"
16
+ * - "card-layout" -> "cardlayout"
17
+ */
18
+ export declare function normalizeComponentName(name: string): string;
11
19
  /**
12
20
  * Returns the cached list of all components
13
21
  */
14
22
  export declare function getComponentList(): ComponentInfo[];
15
23
  /**
16
- * Checks if a component exists in the catalog
24
+ * Returns the cached list of component names
25
+ */
26
+ export declare function getComponentNames(): string[];
27
+ type SearchComponentsOptions = {
28
+ includeCategory?: boolean;
29
+ limit?: number;
30
+ };
31
+ /**
32
+ * Finds components by keyword and normalized substring matching.
17
33
  */
18
- export declare function componentExists(componentName: string): boolean;
34
+ export declare function searchComponents(query: string, { limit, includeCategory }?: SearchComponentsOptions): string[];
19
35
  /**
20
36
  * Gets component info by name
21
37
  */
@@ -24,3 +40,4 @@ export declare function getComponentInfo(componentName: string): ComponentInfo |
24
40
  * Reads the markdown documentation for a component
25
41
  */
26
42
  export declare function getComponentDocs(componentName: string): string;
43
+ export {};