Skip to main content
Glama
GetCSSClassesTool.ts6.49 kB
/** * GetCSSClassesTool * * DESIGN PATTERNS: * - Tool pattern with getDefinition() and execute() methods * - Service delegation for business logic via factory pattern * - JSON Schema validation for inputs * * CODING STANDARDS: * - Implement Tool interface from ../types * - Use TOOL_NAME constant with snake_case (e.g., 'get_css_classes') * - Return CallToolResult with content array * - Handle errors with isError flag * - Delegate complex logic to services * * AVOID: * - Complex business logic in execute method * - Unhandled promise rejections * - Missing input validation */ import path from 'node:path'; import { TemplatesManagerService } from '@agiflowai/aicode-utils'; import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import { getAppDesignSystemConfig, getGetCssClassesConfig } from '../config'; import type { BaseCSSClassesService, CSSClassCategory } from '../services/CssClasses'; import { CSSClassesServiceFactory } from '../services/CssClasses'; import type { Tool, ToolDefinition } from '../types'; /** * Valid CSS class category values */ const VALID_CATEGORIES = ['colors', 'typography', 'spacing', 'effects', 'all'] as const; /** * Input parameters for GetCSSClassesTool */ interface GetCSSClassesInput { category?: string; appPath?: string; } /** * Type guard to validate category input * @param value - Value to check * @returns True if value is a valid CSSClassCategory */ function isValidCategory(value: string): value is CSSClassCategory { return VALID_CATEGORIES.includes(value as CSSClassCategory); } /** * MCP Tool for extracting CSS classes from theme files. * * Uses the CSSClassesServiceFactory to create the appropriate service * based on configuration, supporting Tailwind and custom CSS frameworks. * * @example * ```typescript * const tool = new GetCSSClassesTool(); * const result = await tool.execute({ category: 'colors' }); * ``` */ export class GetCSSClassesTool implements Tool<GetCSSClassesInput> { static readonly TOOL_NAME = 'get_css_classes'; private static readonly CSS_REUSE_INSTRUCTION = 'IMPORTANT: Always reuse these existing CSS classes from the theme as much as possible instead of creating custom styles. This ensures design consistency and reduces CSS bloat.'; private serviceFactory: CSSClassesServiceFactory; private service: BaseCSSClassesService | null = null; private defaultThemePath: string; /** * Creates a new GetCSSClassesTool instance * @param defaultThemePath - Default path to theme CSS file (relative to workspace root) */ constructor(defaultThemePath: string = 'packages/frontend/web-theme/src/agimon-theme.css') { this.serviceFactory = new CSSClassesServiceFactory(); this.defaultThemePath = defaultThemePath; } /** * Returns the tool definition for MCP registration * @returns Tool definition with name, description, and input schema */ getDefinition(): ToolDefinition { return { name: GetCSSClassesTool.TOOL_NAME, description: 'Extract and return all supported CSS classes from the theme file. Call this tool BEFORE writing any component styles or class names to ensure you use existing theme classes.', inputSchema: { type: 'object', properties: { category: { type: 'string', enum: ['colors', 'typography', 'spacing', 'effects', 'all'], description: "Category filter: 'colors', 'typography', 'spacing', 'effects', 'all' (default)", }, appPath: { type: 'string', description: 'Optional app path (relative or absolute) to read theme path from project.json style-system config (e.g., "apps/agiflow-app")', }, }, additionalProperties: false, }, }; } /** * Executes the CSS class extraction * @param input - Tool input parameters * @returns CallToolResult with extracted CSS classes or error */ async execute(input: GetCSSClassesInput): Promise<CallToolResult> { try { // Validate and normalize category input const category = input.category || 'all'; if (!isValidCategory(category)) { throw new Error( `Invalid category: '${category}'. Must be one of: ${VALID_CATEGORIES.join(', ')}`, ); } // Resolve theme path from config or use default const themePath = await this.resolveThemePath(input.appPath); // Initialize service lazily using factory with toolkit.yaml config if (!this.service) { const toolkitConfig = await getGetCssClassesConfig(); this.service = await this.serviceFactory.createService({ customServicePath: toolkitConfig?.customService, }); } // Delegate extraction to service const result = await this.service.extractClasses(category, themePath); return { content: [ { type: 'text', text: `${GetCSSClassesTool.CSS_REUSE_INSTRUCTION}\n\n${JSON.stringify(result, null, 2)}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } /** * Resolves the theme file path based on app configuration or defaults. * * Resolution strategy: * 1. If appPath provided, read themePath from project.json style-system config * 2. themePath is resolved relative to the app directory (where project.json is) * 3. Fall back to default theme path if not configured * * @param appPath - Optional app path to read config from * @returns Absolute path to the theme file */ private async resolveThemePath(appPath?: string): Promise<string> { const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync(); if (appPath) { // Read theme path from app's project.json style-system config const config = await getAppDesignSystemConfig(appPath); if (config.themePath) { // Resolve themePath relative to app directory const resolvedAppPath = path.isAbsolute(appPath) ? appPath : path.join(workspaceRoot, appPath); return path.resolve(resolvedAppPath, config.themePath); } } // Use default theme path (relative to workspace root) return path.resolve(workspaceRoot, this.defaultThemePath); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/AgiFlow/aicode-toolkit'

If you have feedback or need assistance with the MCP directory API, please join our Discord server