Skip to main content
Glama
index.ts12.3 kB
/** * MCP CSS First - A Model Context Protocol server for CSS-only development * * This server enforces CSS-only solutions for UI implementation tasks. It strictly * provides modern CSS solutions using: * - Modern CSS features (2021-2025) with logical properties as default * - CSS-only carousels with modern pseudo-elements (::scroll-marker, ::scroll-button) * - light-dark() function for theme switching * - Container queries and dynamic viewport units * - Zero JavaScript solutions only * * @author MCP CSS First Team * @version 1.0.0 */ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { z } from "zod"; import express from "express"; import { randomUUID } from "crypto"; import { version } from "../package.json"; import { searchMDNForCSSProperties, fetchBrowserSupportFromMDN, fetchCSSPropertyDetailsFromMDN, generateBrowserSupportRecommendation, getAlternativeCSSProperties, getImplementationGuidance, type CSSPropertySuggestion, type BrowserSupportInfo, type CSSPropertyDetails, } from "./services/mdnApi.js"; // Export for testing purposes export { CSS_FEATURES, searchFeatures } from "./services/mdnApi.js"; /** The main MCP server instance */ const server = new McpServer({ name: "MCP CSS First", version }); /** * MCP Tool: CSS Property Suggestion * * This tool analyzes UI task descriptions and suggests appropriate CSS properties * from MDN documentation. It provides comprehensive information including browser * support data and asks for user consent before recommending usage. */ server.tool( "suggest_css_solution", "CSS-ONLY solution engine with strict enforcement of modern CSS features. Provides zero-JavaScript solutions using cutting-edge CSS (2021-2025) with logical properties, modern carousels, and light-dark() theming.", { task_description: z .string() .describe("Description of the UI task or problem to solve"), preferred_approach: z .enum(["modern", "compatible", "progressive"]) .optional() .describe( "Preferred CSS approach - modern (latest features), compatible (wide browser support), or progressive (with fallbacks)" ), target_browsers: z .array(z.string()) .optional() .describe( 'Target browsers/versions (e.g., ["Chrome 90+", "Firefox 88+", "Safari 14+"])' ), project_context: z .string() .optional() .describe( "Project context (framework, existing CSS patterns, constraints)" ), include_analysis: z .boolean() .optional() .describe("Include semantic analysis details in response"), }, async (args: { task_description: string; preferred_approach?: "modern" | "compatible" | "progressive"; target_browsers?: string[]; project_context?: string; include_analysis?: boolean; }) => { try { const { task_description, preferred_approach = "modern", project_context, include_analysis = false, } = args; // Enhanced semantic analysis and intelligent search const suggestions: CSSPropertySuggestion[] = await searchMDNForCSSProperties( task_description, preferred_approach, project_context ); // Get analysis details if requested let analysisDetails = null; if (include_analysis) { const { analyzeTaskIntent } = await import("./services/mdnApi.js"); analysisDetails = analyzeTaskIntent(task_description, project_context); } const result = suggestions.length === 0 ? { success: false, message: "No CSS-ONLY solutions found. This tool provides ONLY CSS solutions - no JavaScript alternatives will be suggested. Please rephrase your request to focus on CSS-achievable UI patterns.", suggestions: [], ...(analysisDetails && { analysis: analysisDetails }), } : { success: true, message: `Found ${suggestions.length} CSS-ONLY solution(s) using modern CSS features. All solutions are JavaScript-free and use logical properties by default.`, suggestions: suggestions.map((suggestion) => ({ ...suggestion, needs_consent: true, consent_message: `Do you want to use the CSS property "${suggestion.property}" which has ${suggestion.browser_support.overall_support}% browser support?`, })), ...(analysisDetails && { analysis: { ...analysisDetails, explanation: `Analyzed with ${Math.round( analysisDetails.confidence * 100 )}% confidence. Detected intent: ${analysisDetails.intent.join( ", " )}.`, }, }), }; return { content: [ { type: "text" as const, text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: JSON.stringify( { error: error instanceof Error ? error.message : "Unknown error", }, null, 2 ), }, ], }; } } ); /** * MCP Tool: Browser Support Checker * * This tool checks browser support for specific CSS properties using MDN data * and provides detailed compatibility information across different browsers. */ server.tool( "check_css_browser_support", "Checks browser support for specific CSS properties using MDN data and provides detailed compatibility information.", { css_property: z .string() .describe("CSS property name to check browser support for"), include_experimental: z .boolean() .optional() .describe("Include experimental/draft features in results"), }, async (args: { css_property: string; include_experimental?: boolean }) => { try { const { css_property, include_experimental = false } = args; const supportInfo: BrowserSupportInfo = await fetchBrowserSupportFromMDN( css_property, include_experimental ); const result = { property: css_property, browser_support: supportInfo, recommendation: generateBrowserSupportRecommendation(supportInfo), safe_to_use: supportInfo.overall_support >= 80, }; return { content: [ { type: "text" as const, text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: JSON.stringify( { error: error instanceof Error ? error.message : "Unknown error", }, null, 2 ), }, ], }; } } ); /** * MCP Tool: CSS Property Details * * This tool retrieves comprehensive information about a CSS property from MDN * documentation including syntax, examples, and use cases. */ server.tool( "get_css_property_details", "Retrieves comprehensive information about a CSS property from MDN documentation including syntax, examples, and use cases.", { css_property: z.string().describe("CSS property name to get details for"), include_examples: z .boolean() .optional() .describe("Include code examples in the response"), }, async (args: { css_property: string; include_examples?: boolean }) => { try { const { css_property, include_examples = true } = args; const details: CSSPropertyDetails = await fetchCSSPropertyDetailsFromMDN( css_property, include_examples ); const result = { property: css_property, details, mdn_url: `https://developer.mozilla.org/en-US/docs/Web/CSS/${css_property}`, }; return { content: [ { type: "text" as const, text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: JSON.stringify( { error: error instanceof Error ? error.message : "Unknown error", }, null, 2 ), }, ], }; } } ); /** * MCP Tool: User Consent Confirmation * * This tool confirms user consent for using a specific CSS property and provides * implementation guidance including code examples and best practices. */ server.tool( "confirm_css_property_usage", "Confirms user consent for using a specific CSS property and provides implementation guidance.", { css_property: z.string().describe("CSS property name user wants to use"), user_consent: z.boolean().describe("User consent to use this CSS property"), fallback_needed: z .boolean() .optional() .describe("Whether fallback solutions are needed"), }, async (args: { css_property: string; user_consent: boolean; fallback_needed?: boolean; }) => { try { const { css_property, user_consent, fallback_needed = false } = args; const result = !user_consent ? { message: `User declined to use ${css_property}. Here are alternative CSS-ONLY solutions (JavaScript solutions are not provided by this tool).`, alternative_suggestions: await getAlternativeCSSProperties( css_property ), } : { message: `User confirmed usage of ${css_property}. Here's the implementation guidance:`, implementation_guidance: await getImplementationGuidance( css_property, fallback_needed ), css_property: css_property, approved: true, }; return { content: [ { type: "text" as const, text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: JSON.stringify( { error: error instanceof Error ? error.message : "Unknown error", }, null, 2 ), }, ], }; } } ); /** * Start the MCP server * * Supports both stdio and HTTP transports based on command line arguments: * - Default: stdio transport for npx usage * - --port <number>: HTTP transport on specified port for Claude Code CLI */ async function startServer() { const args = process.argv.slice(2); const portIndex = args.indexOf("--port"); if (portIndex !== -1 && portIndex + 1 < args.length) { // HTTP transport mode for Claude Code CLI const port = parseInt(args[portIndex + 1]); if (isNaN(port)) { console.error("Invalid port number"); process.exit(1); } const app = express(); app.use(express.json()); const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), }); await server.connect(transport); app.post("/mcp", async (req, res) => { try { await transport.handleRequest(req, res, req.body); } catch (error) { console.error("Error handling MCP request:", error); res.status(500).json({ error: "Internal server error" }); } }); app.listen(port, () => { console.log(`MCP CSS First server running on HTTP port ${port}`); console.log(`Endpoint: http://localhost:${port}/mcp`); }); } else { // Default stdio transport for npx usage const transport = new StdioServerTransport(); await server.connect(transport); } } startServer().catch(console.error);

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/Luko248/css-first'

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