UNPKG

@microsoft/clarity-mcp-server

Version:

MCP Server for Microsoft Clarity based on data export API

202 lines (176 loc) 6.24 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; // Get configuration from environment variables or command-line arguments const getConfigValue = (name: string, fallback?: string): string | undefined => { // Check command line args first (format: --name=value) const commandArg = process.argv.find(arg => arg.startsWith(`--${name}=`)); if (commandArg) { return commandArg.split('=')[1]; } // Then check environment variables if (process.env[name] || process.env[name.toUpperCase()]) { return process.env[name] || process.env[name.toUpperCase()]; } return fallback; }; // Get configuration const CLARITY_API_TOKEN = getConfigValue('clarity_api_token'); // Create server instance const server = new McpServer({ name: "@microsoft/clarity-mcp-server", version: "1.0.0", capabilities: { resources: {}, tools: {}, }, }); // Constants for API const API_BASE_URL = "https://www.clarity.ms/export-data/api/v1/project-live-insights"; // Available metrics that may be returned by the API const AVAILABLE_METRICS = [ "ScrollDepth", "EngagementTime", "Traffic", "PopularPages", "Browser", "Device", "OS", "Country/Region", "PageTitle", "ReferrerURL", "DeadClickCount", "ExcessiveScroll", "RageClickCount", "QuickbackClick", "ScriptErrorCount", "ErrorClickCount" ]; // Available dimensions that can be used in queries const AVAILABLE_DIMENSIONS = [ "Browser", "Device", "Country/Region", "OS", "Source", "Medium", "Campaign", "Channel", "URL" ]; // Helper function to make API requests async function fetchClarityData( token: string, numOfDays: number, dimensions: string[] = [] ): Promise<any> { try { // Build parameters for the API request const params = new URLSearchParams(); params.append("numOfDays", numOfDays.toString()); // Add dimensions if specified (maximum 3 allowed) dimensions.slice(0, 3).forEach((dim, index) => { params.append(`dimension${index + 1}`, dim); }); // Make the API request const url = `${API_BASE_URL}?${params.toString()}&src=mcp`; console.error(`Making request to: ${url}`); const response = await fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }); if (!response.ok) { throw new Error(`API request failed with status ${response.status}`); } return await response.json(); } catch (error) { console.error("Error fetching Clarity data:", error); return { error: error instanceof Error ? error.message : "Unknown error" }; } } // Register the get-clarity-data tool server.tool( "get-clarity-data", "Fetch Microsoft Clarity analytics data", { numOfDays: z.number().min(1).max(3).describe("Number of days to retrieve data for (1-3)"), dimensions: z.array(z.string()).optional().describe("Up to 3 dimensions to filter by (Browser, Device, Country/Region, OS, Source, Medium, Campaign, Channel, URL)"), metrics: z.array(z.string()).optional().describe("Metrics to retrieve (Scroll Depth, Engagement Time, Traffic, Popular Pages, Browser, Device, OS, Country/Region, etc.)"), token: z.string().optional().describe("Your Clarity API token (optional if provided via environment or command line)"), }, async ({ numOfDays, dimensions = [], metrics = [], token }) => { // Use provided token or fallback to environment/command-line variables const finalToken = token || CLARITY_API_TOKEN; // Check if we have the necessary credentials if (!finalToken) { return { content: [ { type: "text", text: "No Clarity API token provided. Please provide a token via the 'token' parameter, CLARITY_API_TOKEN environment variable, or --clarity_api_token command-line argument.", }, ], }; } // Validate dimensions against known valid dimensions const filteredDimensions = dimensions.filter(d => AVAILABLE_DIMENSIONS.includes(d)); if (filteredDimensions.length < dimensions.length) { console.warn("Some dimensions were invalid and have been filtered out"); } // Fetch data from Clarity API const data = await fetchClarityData(finalToken, numOfDays, filteredDimensions); // Check for errors if (data.error) { return { content: [ { type: "text", text: `Error fetching data: ${data.error}`, }, ], }; } // Filter metrics if specified let formattedResult = data; if (metrics && metrics.length > 0) { // Filter the metrics if requested (case-insensitive match for user convenience) formattedResult = data.filter((item: any) => metrics.some(m => item.metricName.toLowerCase() === m.toLowerCase() || item.metricName.replace(/\s+/g, '').toLowerCase() === m.replace(/\s+/g, '').toLowerCase() ) ); } const resultText = JSON.stringify(formattedResult, null, 2); return { content: [ { type: "text", text: resultText, }, ], }; }, ); // Main function async function main() { // Log configuration status if (CLARITY_API_TOKEN) { console.error("Clarity API token configured via environment/command-line"); } else { console.error("No Clarity API token configured, it must be provided with each request"); } console.error(`Supported metrics: ${AVAILABLE_METRICS.join(", ")}`); console.error(`Supported dimensions: ${AVAILABLE_DIMENSIONS.join(", ")}`); const transport = new StdioServerTransport(); await server.connect(transport); console.error("Microsoft Clarity Data Export MCP Server running on stdio"); } // Run the server main().catch((error) => { console.error("Fatal error in main():", error); process.exit(1); });