grafana_ui
Access Grafana UI components, documentation, themes, and metadata to build Grafana-compatible interfaces using React components and design system tokens.
Instructions
Unified tool for accessing Grafana UI components, documentation, themes, and metadata
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | The action to perform | |
| componentName | No | Name of the Grafana UI component (e.g., "Button", "Alert") | |
| query | No | Search query string (required for search action) | |
| includeDescription | No | Whether to search in documentation content (default: false) | |
| category | No | Token category to filter by (colors, typography, spacing, shadows, etc.) | |
| deep | No | Whether to analyze dependencies recursively (default: false) | |
| path | No | Path within the repository (default: components directory) | |
| owner | No | Repository owner (default: "grafana") | |
| repo | No | Repository name (default: "grafana") | |
| branch | No | Branch name (default: "main") |
Implementation Reference
- src/tools.ts:397-558 (handler)Primary handler implementation for the 'grafana_ui' tool. This exported function is used by the MCP server's CallToolRequestSchema handler. It validates params, switches on 'action', and calls various axios and parser utilities to fetch component code, demos, metadata, etc.export const toolHandlers = { grafana_ui: async (params: any) => { try { switch (params.action) { case "get_component": const sourceCode = await axios.getComponentSource( params.componentName!, ); return createSuccessResponse(sourceCode); case "get_demo": const demoCode = await axios.getComponentDemo(params.componentName!); return createSuccessResponse(demoCode); case "list_components": const components = await axios.getAvailableComponents(); return createSuccessResponse({ components: components.sort(), total: components.length, }); case "get_metadata": const metadata = await axios.getComponentMetadata( params.componentName!, ); return createSuccessResponse(metadata); case "get_directory": const directoryTree = await axios.buildDirectoryTree( params.owner || axios.paths.REPO_OWNER, params.repo || axios.paths.REPO_NAME, params.path || axios.paths.COMPONENTS_PATH, params.branch || axios.paths.REPO_BRANCH, ); return createSuccessResponse(directoryTree); case "get_documentation": const mdxContent = await axios.getComponentDocumentation( params.componentName!, ); const parsedContent = parseMDXContent( params.componentName!, mdxContent, ); return createSuccessResponse({ title: parsedContent.title, sections: parsedContent.sections.map((section) => ({ title: section.title, level: section.level, content: section.content.substring(0, 500) + (section.content.length > 500 ? "..." : ""), examples: section.examples.length, })), totalExamples: parsedContent.examples.length, imports: parsedContent.imports, components: parsedContent.components, }); case "get_stories": const storyContent = await axios.getComponentDemo( params.componentName!, ); const storyMetadata = parseStoryMetadata( params.componentName!, storyContent, ); const examples = extractStoryExamples(storyContent); return createSuccessResponse({ component: storyMetadata.componentName, meta: storyMetadata.meta, totalStories: storyMetadata.totalStories, hasInteractiveStories: storyMetadata.hasInteractiveStories, examples: examples.slice(0, 5), rawStoryCode: storyContent.substring(0, 1000) + (storyContent.length > 1000 ? "..." : ""), }); case "get_tests": const testContent = await axios.getComponentTests( params.componentName!, ); const testDescriptions = []; const testRegex = /(describe|it|test)\s*\(\s*['`"]([^'`"]+)['`"]/g; let match; while ((match = testRegex.exec(testContent)) !== null) { testDescriptions.push({ type: match[1], description: match[2], }); } return createSuccessResponse({ component: params.componentName, testDescriptions: testDescriptions.slice(0, 10), totalTests: testDescriptions.filter( (t) => t.type === "it" || t.type === "test", ).length, testCode: testContent.substring(0, 2000) + (testContent.length > 2000 ? "..." : ""), }); case "search": const searchResults = await axios.searchComponents( params.query!, params.includeDescription || false, ); return createSuccessResponse({ query: params.query, includeDescription: params.includeDescription || false, results: searchResults, totalResults: searchResults.length, }); case "get_theme_tokens": const themeFiles = await axios.getThemeFiles(params.category); const processedThemes: any = {}; for (const [themeName, themeContent] of Object.entries( themeFiles.themes, )) { if (typeof themeContent === "string") { const tokens = extractThemeTokens(themeContent); const themeMetadata = extractThemeMetadata(themeContent); processedThemes[themeName] = { metadata: themeMetadata, tokens: params.category ? filterTokensByCategory(tokens, params.category) : tokens, }; } } return createSuccessResponse({ category: params.category || "all", themes: processedThemes, availableThemes: Object.keys(processedThemes), }); case "get_dependencies": const dependencies = await axios.getComponentDependencies( params.componentName!, params.deep || false, ); return createSuccessResponse(dependencies); default: throw new McpError( ErrorCode.InvalidParams, `Unknown action: ${params.action}`, ); } } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Failed to execute action "${params.action}": ${error instanceof Error ? error.message : String(error)}`, ); } }, };
- src/tools.ts:88-138 (schema)Zod schema used to validate input parameters for the grafana_ui tool, ensuring required fields like componentName or query based on the action.const unifiedToolSchema = z .object({ action: z.enum([ "get_component", "get_demo", "list_components", "get_metadata", "get_directory", "get_documentation", "get_stories", "get_tests", "search", "get_theme_tokens", "get_dependencies", ]), componentName: z.string().optional(), query: z.string().optional(), includeDescription: z.boolean().optional(), category: z.string().optional(), deep: z.boolean().optional(), path: z.string().optional(), owner: z.string().optional(), repo: z.string().optional(), branch: z.string().optional(), }) .refine( (data) => { // Validate required parameters based on action switch (data.action) { case "get_component": case "get_demo": case "get_metadata": case "get_documentation": case "get_stories": case "get_tests": case "get_dependencies": return !!data.componentName; case "search": return !!data.query; case "list_components": case "get_directory": case "get_theme_tokens": return true; default: return false; } }, { message: "Missing required parameters for the specified action", }, );
- src/tools.ts:321-390 (registration)Tool registration object used by ListToolsRequestSchema to advertise the available 'grafana_ui' tool with its description and input schema.export const tools = { grafana_ui: { name: "grafana_ui", description: "Unified tool for accessing Grafana UI components, documentation, themes, and metadata", inputSchema: { type: "object", properties: { action: { type: "string", enum: [ "get_component", "get_demo", "list_components", "get_metadata", "get_directory", "get_documentation", "get_stories", "get_tests", "search", "get_theme_tokens", "get_dependencies", ], description: "The action to perform", }, componentName: { type: "string", description: 'Name of the Grafana UI component (e.g., "Button", "Alert")', }, query: { type: "string", description: "Search query string (required for search action)", }, includeDescription: { type: "boolean", description: "Whether to search in documentation content (default: false)", }, category: { type: "string", description: "Token category to filter by (colors, typography, spacing, shadows, etc.)", }, deep: { type: "boolean", description: "Whether to analyze dependencies recursively (default: false)", }, path: { type: "string", description: "Path within the repository (default: components directory)", }, owner: { type: "string", description: 'Repository owner (default: "grafana")', }, repo: { type: "string", description: 'Repository name (default: "grafana")', }, branch: { type: "string", description: 'Branch name (default: "main")', }, }, required: ["action"], }, },
- src/handler.ts:163-180 (schema)Utility function that returns the unifiedToolSchema for 'grafana_ui' during tool execution validation in the main MCP request handler./** * Get Zod schema for tool validation if available * @param toolName Name of the tool * @returns Zod schema or undefined */ function getToolSchema(toolName: string): z.ZodType | undefined { try { switch (toolName) { case "grafana_ui": return unifiedToolSchema; default: return undefined; } } catch (error) { console.error("Error getting schema:", error); return undefined; } }
- src/tools.ts:29-37 (helper)Helper function used by the grafana_ui handler to format successful responses with text content.function createSuccessResponse(data: any) { return { content: [ { type: "text" as const, text: typeof data === "string" ? data : JSON.stringify(data, null, 2), }, ], };