From 2f57ac01a147ccf573263baa33fe2fd816b47c46 Mon Sep 17 00:00:00 2001 From: yuriassuncx Date: Sun, 19 Apr 2026 18:36:43 -0300 Subject: [PATCH] feat: initialize Microsoft Clarity MCP server with analytics, recording, and documentation tools --- deploy.json | 11 +- microsoft-clarity/.gitignore | 33 ++ microsoft-clarity/README.md | 42 ++ microsoft-clarity/app.json | 32 ++ microsoft-clarity/app.json.example | 25 ++ microsoft-clarity/package.json | 27 ++ microsoft-clarity/server/instructions.ts | 70 ++++ microsoft-clarity/server/lib/clarity.ts | 43 ++ microsoft-clarity/server/main.ts | 31 ++ microsoft-clarity/server/tools/analytics.ts | 26 ++ microsoft-clarity/server/tools/docs.ts | 27 ++ .../server/tools/example-tool.ts.example | 202 +++++++++ microsoft-clarity/server/tools/index.ts | 9 + microsoft-clarity/server/tools/recordings.ts | 47 +++ microsoft-clarity/server/types/clarity.ts | 391 ++++++++++++++++++ microsoft-clarity/server/types/env.ts | 64 +++ microsoft-clarity/tsconfig.json | 32 ++ 17 files changed, 1111 insertions(+), 1 deletion(-) create mode 100644 microsoft-clarity/.gitignore create mode 100644 microsoft-clarity/README.md create mode 100644 microsoft-clarity/app.json create mode 100644 microsoft-clarity/app.json.example create mode 100644 microsoft-clarity/package.json create mode 100644 microsoft-clarity/server/instructions.ts create mode 100644 microsoft-clarity/server/lib/clarity.ts create mode 100644 microsoft-clarity/server/main.ts create mode 100644 microsoft-clarity/server/tools/analytics.ts create mode 100644 microsoft-clarity/server/tools/docs.ts create mode 100644 microsoft-clarity/server/tools/example-tool.ts.example create mode 100644 microsoft-clarity/server/tools/index.ts create mode 100644 microsoft-clarity/server/tools/recordings.ts create mode 100644 microsoft-clarity/server/types/clarity.ts create mode 100644 microsoft-clarity/server/types/env.ts create mode 100644 microsoft-clarity/tsconfig.json diff --git a/deploy.json b/deploy.json index 82861f09..5872a562 100644 --- a/deploy.json +++ b/deploy.json @@ -430,5 +430,14 @@ "veo/**", "shared/**" ] + }, + "microsoft-clarity": { + "site": "microsoft-clarity", + "entrypoint": "./dist/server/main.js", + "platformName": "kubernetes-bun", + "watch": [ + "microsoft-clarity/**", + "shared/**" + ] } -} +} \ No newline at end of file diff --git a/microsoft-clarity/.gitignore b/microsoft-clarity/.gitignore new file mode 100644 index 00000000..9a0afafb --- /dev/null +++ b/microsoft-clarity/.gitignore @@ -0,0 +1,33 @@ +# Dependencies +node_modules/ + +# Build output +dist/ + +# Environment +.env +.env.local + +# OS files +.DS_Store +Thumbs.db + +# IDE +.vscode/ +.idea/ + +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Data +data/ +*.db +*.sqlite + +# Template files +# Note: Keep app.json.example, ignore app.json if it's just a copy +# app.json + diff --git a/microsoft-clarity/README.md b/microsoft-clarity/README.md new file mode 100644 index 00000000..de10a73a --- /dev/null +++ b/microsoft-clarity/README.md @@ -0,0 +1,42 @@ +# Microsoft Clarity MCP Server + +Official Microsoft Clarity integration for the Model Context Protocol (MCP). This server allows AI agents to query analytics data, list session recordings, and access documentation using natural language. + +## Features + +- **query-analytics-dashboard**: Get metrics like scroll depth, engagement time, and traffic segments. +- **list-session-recordings**: Filter and find specific user sessions based on behavior and technical criteria. +- **query-documentation-resources**: Search official Clarity guides and troubleshooting steps. + +## Installation + +When installing in Deco Mesh, you will be prompted for your **Microsoft Clarity API Token**. + +### 🔑 Obtaining an API Token +1. Log in to your [Microsoft Clarity](https://clarity.microsoft.com/) project. +2. Navigate to **Settings** -> **Data Export**. +3. Select **Generate new API token**. +4. Copy and store the token safely. + +## Usage Guidelines & Limits + +> [!IMPORTANT] +> The Microsoft Clarity Data Export API has several built-in constraints: +> - **Quota**: 10 API requests per project per day. +> - **Horizon**: Access up to the last 3 days of analytics data. +> - **Dimensions**: Filter by up to 3 dimensions per query. + +### Example Queries +- "Show me the top pages by traffic for the last 3 days." +- "List mobile sessions from the last 24 hours that had rage clicks." +- "How do I set up custom tags in Clarity?" + +## Development + +```bash +bun install +bun run dev +``` + +## License +MIT diff --git a/microsoft-clarity/app.json b/microsoft-clarity/app.json new file mode 100644 index 00000000..190eed0b --- /dev/null +++ b/microsoft-clarity/app.json @@ -0,0 +1,32 @@ +{ + "scopeName": "deco", + "name": "microsoft-clarity", + "friendlyName": "Microsoft Clarity", + "connection": { + "type": "HTTP", + "url": "https://sites-microsoft-clarity.decocache.com/mcp" + }, + "description": "official Microsoft Clarity MCP integration for analytics, session recordings, and documentation access.", + "icon": "https://claritystatic.azureedge.net/images/logo.ico", + "unlisted": false, + "auth": { + "type": "token", + "header": "Authorization", + "prefix": "Bearer" + }, + "metadata": { + "categories": [ + "Analytics", + "Productivity" + ], + "official": true, + "tags": [ + "clarity", + "analytics", + "ux", + "microsoft" + ], + "short_description": "Microsoft Clarity analytics and recordings", + "mesh_description": "**Microsoft Clarity MCP Server** provides AI-driven access to your website analytics and user behavioral data. \n\n**Key Features**\n- **Natural Language Analytics**: Query traffic, engagement, and scroll depth metrics using plain English.\n- **Session Search**: List and filter session recordings based on advanced behavior criteria (rage clicks, dead clicks, device type, etc.).\n- **Expert Documentation**: Get instant answers about Clarity setup and features directly from the official documentation.\n\n**Use Cases**\n- Marketing analysis to optimize campaign performance.\n- UX research to identify navigation friction and drop-off points.\n- Technical debugging using JavaScript error reports and performance heatmaps.\n\n**Authentication**\nRequires a **Microsoft Clarity API token** generated from your project settings (Settings -> Data Export).\n\n**Limits**\nEach project allows **10 API requests per day**, with a limit of **3 days' data** and up to **3 dimensions per request**." + } +} \ No newline at end of file diff --git a/microsoft-clarity/app.json.example b/microsoft-clarity/app.json.example new file mode 100644 index 00000000..0b846366 --- /dev/null +++ b/microsoft-clarity/app.json.example @@ -0,0 +1,25 @@ +{ + "scopeName": "deco", + "name": "my-mcp", + "friendlyName": "My MCP", + "connection": { + "type": "HTTP", + "url": "https://sites-my-mcp.decocache.com/mcp" + }, + "description": "Short description of what this MCP does (1-2 sentences)", + "icon": "https://assets.decocache.com/mcp/{uuid}/icon.png", + "unlisted": false, + "auth": { + "type": "token", + "header": "Authorization", + "prefix": "Bearer" + }, + "metadata": { + "categories": ["Productivity"], + "official": false, + "tags": ["example", "template", "mcp"], + "short_description": "Short description of what this MCP does", + "mesh_description": "Detailed description of your MCP (max 1500 characters). Explain what it does, key features, use cases, and authentication method. Use **bold** for feature names. Example: **Key Features** - Feature 1 description. **Use Cases** - Use case description. **Authentication** - How to authenticate. Perfect for developers who need [your use case]. Provides [your benefit]." + } +} + diff --git a/microsoft-clarity/package.json b/microsoft-clarity/package.json new file mode 100644 index 00000000..df10d03b --- /dev/null +++ b/microsoft-clarity/package.json @@ -0,0 +1,27 @@ +{ + "name": "microsoft-clarity", + "version": "1.0.0", + "description": "MCP server for Microsoft Clarity analytics and session recordings", + "private": true, + "type": "module", + "scripts": { + "dev": "bun run --hot server/main.ts", + "check": "tsc --noEmit", + "build:server": "NODE_ENV=production bun build server/main.ts --target=bun --outfile=dist/server/main.js", + "build": "bun run build:server", + "publish": "cat app.json | deco registry publish -w /shared/deco -y" + }, + "dependencies": { + "@decocms/runtime": "1.2.5", + "zod": "^4.0.0" + }, + "devDependencies": { + "@decocms/mcps-shared": "workspace:*", + "@modelcontextprotocol/sdk": "^1.25.1", + "deco-cli": "^0.28.0", + "typescript": "^5.7.2" + }, + "engines": { + "node": ">=22.0.0" + } +} diff --git a/microsoft-clarity/server/instructions.ts b/microsoft-clarity/server/instructions.ts new file mode 100644 index 00000000..ad4b8d7d --- /dev/null +++ b/microsoft-clarity/server/instructions.ts @@ -0,0 +1,70 @@ +export const SYSTEM_INSTRUCTIONS_PROMPT = ` +This MCP server provides access to Microsoft Clarity analytics dashboard data, documentation resources and session recordings. + +### 1. Session Recordings Tool: \`list-session-recordings\` +Lists Microsoft Clarity session recordings with metadata including session links, duration, and user interaction timelines. + +**Parameters:** +- filters: Optional filters for sessions (date range, device type, etc.) +- sortBy: Sort option using SortOptions enum (default: SessionStart_DESC) +- count: Number of sessions to retrieve (1-250, default: 100) + +**Sort Options:** +- SessionStart_DESC (newest first - default) +- SessionStart_ASC (oldest first) +- SessionDuration_ASC (shortest duration first) +- SessionDuration_DESC (longest duration first) +- SessionClickCount_ASC (fewest clicks first) +- SessionClickCount_DESC (most clicks first) +- PageCount_ASC (fewest pages first) +- PageCount_DESC (most pages first) + +**Example Usage:** +- Get 10 newest sessions: { "count": 10, "sortBy": "SessionStart_DESC" } +- Get 20 longest sessions from date range: { "filters": { "date": { "start": "2024-01-01T00:00:00.000Z", "end": "2024-01-31T23:59:59.999Z" } }, "sortBy": "SessionDuration_DESC", "count": 20 } +- Get 15 mobile sessions with most clicks: { "filters": { "deviceType": ["Mobile"] }, "sortBy": "SessionClickCount_DESC", "count": 15 } +- Get oldest sessions first: { "sortBy": "SessionStart_ASC", "count": 100 } +- Get sessions with most page views: { "sortBy": "PageCount_DESC", "count": 100 } + +### 2. Analytics Dashboard Tool: \`query-analytics-dashboard\` +This tool is your **primary and authoritative data source** for all dashboard-related insights and must be used to retrieve accurate, real-time data from the Microsoft Clarity dashboard. + +#### Capabilities & Output +Microsoft Clarity dashboard provides comprehensive insights into the behavior and performance of the website, including: +- **User Analytics**: Unique and returning users, sessions, device types, browsers, operating systems +- **Geographic Data**: Countries, regions, traffic sources +- **Content Performance**: Popular pages, referrers, channels, campaigns, sources +- **User Behavior**: Smart events, scroll depth, click patterns +- **Technical Metrics**: JavaScript errors, URL performance +- **Performance Indicators**: Core Web Vitals +- **User Experience**: Quick backs, dead clicks, rage clicks, session duration + +**IMPORTANT GUIDELINES:** +- Use SIMPLE, SINGLE-PURPOSE queries only +- Always specify time ranges, full URLs and parameters explicitly; prompt the user if not provided +- Break complex requests into multiple separate queries +- Focus on ONE trend or aggregated metric per query +- **LIMITS**: Each project allows 10 API requests per day, with a limit of 3 days' data and up to 3 dimensions per request. + +**Good Examples:** +- "Page views count for the last 3 days" +- "Top javascript errors for PC in the last 2 days" +- "Top pages for mobile in the last 24 hours" + +### 3. Documentation Tool: \`query-documentation-resources\` +Authoritative answers to Clarity setup, features, troubleshooting, and integrations. The tool covers topics including: +- Getting Started & Installation (Setup, Verification, Troubleshooting) +- Clarity for Mobile Apps (Android, iOS, SDKs) +- Dashboard & Insights (Overview, Features) +- Session Recordings & Heatmaps (Inline player, Click maps, Scroll maps) +- Filters & Segments (Exclusion filters, Regex) +- Settings & Management (Masking, IP blocking, Funnels) +- Copilot in Clarity (Overview, Chat, Insights) +- API Reference & Troubleshooting + +**Best Practices:** +✅ Be specific about topics +✅ Focus on one specific question per query +✅ Use clear, actionable language +❌ Don't combine multiple unrelated topics +`; diff --git a/microsoft-clarity/server/lib/clarity.ts b/microsoft-clarity/server/lib/clarity.ts new file mode 100644 index 00000000..879e2f95 --- /dev/null +++ b/microsoft-clarity/server/lib/clarity.ts @@ -0,0 +1,43 @@ +import type { Env } from "../main.ts"; + +const API_BASE_URL = "https://clarity.microsoft.com/mcp"; + +interface ClarityRequestOptions { + method: string; + body?: any; + token?: string; +} + +export async function callClarityApi( + env: Env, + endpoint: string, + options: ClarityRequestOptions, +) { + const state = env.state as any; + const token = + options.token || + state.API_CREDENTIALS?.API_KEY || + process.env.CLARITY_API_TOKEN; + + if (!token) { + throw new Error( + "Clarity API token not configured. Please provide it in the tool parameters or MCP configuration.", + ); + } + + const response = await fetch(`${API_BASE_URL}${endpoint}`, { + method: options.method, + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: options.body ? JSON.stringify(options.body) : undefined, + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`Clarity API error (${response.status}): ${errorText}`); + } + + return response.json(); +} diff --git a/microsoft-clarity/server/main.ts b/microsoft-clarity/server/main.ts new file mode 100644 index 00000000..2f6240f5 --- /dev/null +++ b/microsoft-clarity/server/main.ts @@ -0,0 +1,31 @@ +/** + * Microsoft Clarity MCP Server + * + * This MCP provides tools for interacting with Microsoft Clarity Data Export API, + * including analytics dashboard, session recordings, and documentation access. + */ +import { withRuntime } from "@decocms/runtime"; +import { serve } from "@decocms/mcps-shared/serve"; + +import { tools } from "./tools/index.ts"; +import { type Env, StateSchema } from "./types/env.ts"; + +// Export Env type for use in other files +export type { Env }; + +/** + * Configure the MCP runtime + */ +const runtime = withRuntime({ + configuration: { + state: StateSchema, + }, + + // Register tools using the factory pattern for each tool + tools: (env: Env) => tools.map((createTool) => createTool(env)), +}); + +// Start the server +if (runtime.fetch) { + serve(runtime.fetch); +} diff --git a/microsoft-clarity/server/tools/analytics.ts b/microsoft-clarity/server/tools/analytics.ts new file mode 100644 index 00000000..308d2b58 --- /dev/null +++ b/microsoft-clarity/server/tools/analytics.ts @@ -0,0 +1,26 @@ +import { createPrivateTool } from "@decocms/runtime/tools"; +import { SearchRequest } from "../types/clarity.ts"; +import { callClarityApi } from "../lib/clarity.ts"; +import { z } from "zod"; +import type { Env } from "../main.ts"; + +export const queryAnalyticsDashboard = (env: Env) => + createPrivateTool({ + id: "query-analytics-dashboard", + description: + "This tool is your primary and authoritative data source for all dashboard-related insights and must be used to retrieve accurate, real-time data from the Microsoft Clarity dashboard. Capabilities include User Analytics, Geographic Data, Content Performance, User Behavior, Technical Metrics, and Performance Indicators.", + inputSchema: SearchRequest, + outputSchema: z.any().describe("Clarity analytics data output"), + execute: async ({ context }) => { + const { query, token } = context; + const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; + + const result = await callClarityApi(env, "/dashboard/query", { + method: "POST", + token, + body: { query, timezone }, + }); + + return result; + }, + }); diff --git a/microsoft-clarity/server/tools/docs.ts b/microsoft-clarity/server/tools/docs.ts new file mode 100644 index 00000000..caac2ea0 --- /dev/null +++ b/microsoft-clarity/server/tools/docs.ts @@ -0,0 +1,27 @@ +import { createPrivateTool } from "@decocms/runtime/tools"; +import { SearchRequest } from "../types/clarity.ts"; +import { callClarityApi } from "../lib/clarity.ts"; +import { z } from "zod"; +import type { Env } from "../main.ts"; + +export const queryDocumentationResources = (env: Env) => + createPrivateTool({ + id: "query-documentation-resources", + description: + "Authoritative answers to Clarity setup, features, troubleshooting, and integrations. This tool retrieves snippets from Microsoft Clarity documentation to find answers to user questions.", + inputSchema: SearchRequest, + outputSchema: z + .any() + .describe("Clarity documentation snippets and answers"), + execute: async ({ context }) => { + const { query, token } = context; + + const result = await callClarityApi(env, "/documentation/query", { + method: "POST", + token, + body: { query }, + }); + + return result; + }, + }); diff --git a/microsoft-clarity/server/tools/example-tool.ts.example b/microsoft-clarity/server/tools/example-tool.ts.example new file mode 100644 index 00000000..d47467f9 --- /dev/null +++ b/microsoft-clarity/server/tools/example-tool.ts.example @@ -0,0 +1,202 @@ +/** + * Example Tool + * + * This is an example tool implementation. + * Copy this file and rename it to create your own tools. + */ + +import { createPrivateTool } from "@decocms/runtime"; +import { z } from "zod"; +import type { Env } from "../types/env.ts"; + +/** + * Example tool that echoes back the input + */ +export const exampleToolFactory = (env: Env) => + createPrivateTool({ + id: "example_echo", + description: "Echoes back the input message with a greeting", + inputSchema: z.object({ + message: z.string().describe("The message to echo back"), + uppercase: z + .boolean() + .default(false) + .optional() + .describe("Convert message to uppercase"), + }), + outputSchema: z.object({ + result: z.string().describe("The echoed message"), + timestamp: z.string().describe("When the message was processed"), + }), + execute: async ({ input }) => { + // Access environment/configuration + // const apiKey = env.MESH_REQUEST_CONTEXT?.state?.API_KEY; + + // Process the input + let result = `Hello! You said: ${input.message}`; + + if (input.uppercase) { + result = result.toUpperCase(); + } + + return { + result, + timestamp: new Date().toISOString(), + }; + }, + }); + +/** + * Example tool with API call + */ +export const exampleApiToolFactory = (env: Env) => + createPrivateTool({ + id: "example_api_call", + description: "Makes an API call to an external service", + inputSchema: z.object({ + endpoint: z.string().describe("The API endpoint to call"), + }), + outputSchema: z.object({ + success: z.boolean(), + data: z.unknown().optional(), + error: z.string().optional(), + }), + execute: async ({ input }) => { + try { + // Example: Get API credentials from environment + // const apiKey = env.MESH_REQUEST_CONTEXT?.state?.API_CREDENTIALS?.API_KEY; + + // Make API call + const response = await fetch(input.endpoint, { + method: "GET", + headers: { + "Content-Type": "application/json", + // "Authorization": `Bearer ${apiKey}`, + }, + }); + + if (!response.ok) { + return { + success: false, + error: `API error: ${response.status} ${response.statusText}`, + }; + } + + const data: unknown = await response.json(); + + return { + success: true, + data, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Unknown error", + }; + } + }, + }); + +/** + * Example tool using database binding + */ +export const exampleDatabaseToolFactory = (env: Env) => + createPrivateTool({ + id: "example_database_query", + description: "Queries the database (requires DATABASE binding)", + inputSchema: z.object({ + userId: z.string().describe("User ID to query"), + }), + outputSchema: z.object({ + success: z.boolean(), + user: z + .object({ + id: z.string(), + name: z.string(), + email: z.string(), + }) + .optional(), + error: z.string().optional(), + }), + execute: async ({ input }) => { + try { + // Access database binding + const db = env.MESH_REQUEST_CONTEXT?.state?.DATABASE; + + if (!db) { + return { + success: false, + error: "Database binding not configured", + }; + } + + // Run SQL query + const response = await db.DATABASES_RUN_SQL({ + sql: "SELECT id, name, email FROM users WHERE id = ?", + params: [input.userId], + }); + + const rows = response.result[0]?.results ?? []; + + if (rows.length === 0) { + return { + success: false, + error: "User not found", + }; + } + + return { + success: true, + user: rows[0] as { id: string; name: string; email: string }, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Database error", + }; + } + }, + }); + +/** + * Example tool publishing events + */ +export const exampleEventToolFactory = (env: Env) => + createPrivateTool({ + id: "example_publish_event", + description: "Publishes an event to the event bus (requires EVENT_BUS binding)", + inputSchema: z.object({ + eventType: z.string().describe("Type of event to publish"), + data: z.record(z.unknown()).describe("Event data"), + }), + outputSchema: z.object({ + success: z.boolean(), + error: z.string().optional(), + }), + execute: async ({ input }) => { + try { + const eventBus = env.MESH_REQUEST_CONTEXT?.state?.EVENT_BUS; + + if (!eventBus) { + return { + success: false, + error: "Event bus binding not configured", + }; + } + + await eventBus.EVENT_PUBLISH({ + type: input.eventType, + subject: crypto.randomUUID(), + data: input.data, + }); + + return { success: true }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Event publish error", + }; + } + }, + }); + diff --git a/microsoft-clarity/server/tools/index.ts b/microsoft-clarity/server/tools/index.ts new file mode 100644 index 00000000..9c3092f9 --- /dev/null +++ b/microsoft-clarity/server/tools/index.ts @@ -0,0 +1,9 @@ +import { queryAnalyticsDashboard } from "./analytics.ts"; +import { listSessionRecordings } from "./recordings.ts"; +import { queryDocumentationResources } from "./docs.ts"; + +export const tools = [ + queryAnalyticsDashboard, + listSessionRecordings, + queryDocumentationResources, +]; diff --git a/microsoft-clarity/server/tools/recordings.ts b/microsoft-clarity/server/tools/recordings.ts new file mode 100644 index 00000000..10f2f3c3 --- /dev/null +++ b/microsoft-clarity/server/tools/recordings.ts @@ -0,0 +1,47 @@ +import { createPrivateTool } from "@decocms/runtime/tools"; +import { ListRecordingsRequest, SortOptionsEnum } from "../types/clarity.ts"; +import { callClarityApi } from "../lib/clarity.ts"; +import { z } from "zod"; +import type { Env } from "../main.ts"; + +export const listSessionRecordings = (env: Env) => + createPrivateTool({ + id: "list-session-recordings", + description: + "Lists Microsoft Clarity session recordings with metadata including session links, duration, and user interaction timelines. The date filter is required and must be in UTC ISO 8601 format.", + inputSchema: ListRecordingsRequest, + outputSchema: z.any().describe("List of session recordings with metadata"), + execute: async ({ context }) => { + const { + filters, + sortBy = "SessionStart_DESC", + count = 100, + token, + } = context; + + const now = new Date().toISOString(); + const endDateString = filters?.date?.end || now; + const startDateString = filters?.date?.start || now; + + const endDate = new Date(endDateString); + const startDate = new Date(startDateString); + + if (!filters?.date?.start) { + startDate.setDate(endDate.getDate() - 2); + } + + const result = await callClarityApi(env, "/recordings/sample", { + method: "POST", + token, + body: { + sortBy: SortOptionsEnum[sortBy as keyof typeof SortOptionsEnum], + start: startDate.toISOString(), + end: endDate.toISOString(), + filters, + count, + }, + }); + + return result; + }, + }); diff --git a/microsoft-clarity/server/types/clarity.ts b/microsoft-clarity/server/types/clarity.ts new file mode 100644 index 00000000..47b04804 --- /dev/null +++ b/microsoft-clarity/server/types/clarity.ts @@ -0,0 +1,391 @@ +import { z } from "zod"; + +export enum SortOptionsEnum { + SessionStart_DESC = 0, + SessionStart_ASC = 1, + SessionDuration_ASC = 2, + SessionDuration_DESC = 3, + SessionClickCount_ASC = 4, + SessionClickCount_DESC = 5, + PageCount_ASC = 6, + PageCount_DESC = 7, +} + +const UrlFilter = z.object({ + url: z.string(), + operator: z.enum([ + "contains", + "startsWith", + "endsWith", + "excludes", + "isExactly", + "isExactlyNot", + "matchesRegex", + "excludesRegex", + ]), +}); + +const createRangeFilter = (min: number, max: number) => + z.object({ + min: z.number().min(min).max(max).nullable(), + max: z.number().min(min).max(max).nullable(), + }); + +const NullableRangeFilter = z.object({ + min: z.number().nullable(), + max: z.number().nullable(), +}); + +export const Filters = z + .object({ + referringUrl: z + .string() + .optional() + .describe("Filter by the referring URL that brought users to the site"), + userType: z + .enum(["NewUser", "ReturningUser"]) + .optional() + .describe("Filter by user type. Accepted values: NewUser, ReturningUser"), + sessionIntent: z + .enum(["Low Intention", "Medium Intention", "High Intention"]) + .optional() + .describe( + "Filter by session intention level/user behavior classification.", + ), + visitedUrls: z + .array(UrlFilter) + .optional() + .describe( + "Filter by URLs visited during the session with pattern matching.", + ), + entryUrls: z + .array(UrlFilter) + .optional() + .describe("Filter by entry/landing page URLs with pattern matching."), + exitUrls: z + .array(UrlFilter) + .optional() + .describe("Filter by exit page URLs with pattern matching."), + country: z + .array(z.string()) + .optional() + .describe( + "Filter by country names (e.g., ['United States', 'Canada', 'United Kingdom'])", + ), + city: z + .array(z.string()) + .optional() + .describe("Filter by city names (e.g., ['New York', 'London', 'Tokyo'])"), + state: z + .array(z.string()) + .optional() + .describe( + "Filter by state/province names (e.g., ['California', 'Ontario', 'Bavaria'])", + ), + deviceType: z + .array(z.enum(["Mobile", "Tablet", "PC", "Email", "Other"])) + .optional() + .describe("Filter by device types."), + browser: z + .array( + z.enum([ + "Bot", + "MiuiBrowser", + "Chrome", + "CoralWebView", + "Edge", + "Other", + "Firefox", + "IE", + "Unknown", + "Headless", + "MobileApp", + "Opera", + "OperaMini", + "Safari", + "Samsung", + "SamsungInternet", + "Sogou", + "UCBrowser", + "YandexBrowser", + "QQBrowser", + ]), + ) + .optional() + .describe("Filter by browser types."), + os: z + .array( + z.enum([ + "BlackBerry", + "Android", + "ChromeOS", + "iOS", + "Linux", + "MacOS", + "Other", + "Windows", + "WindowsMobile", + ]), + ) + .optional() + .describe("Filter by operating systems."), + source: z + .array(z.string()) + .optional() + .describe( + "Filter by UTM source parameter values (e.g., ['google', 'facebook', 'direct'])", + ), + medium: z + .array(z.string()) + .optional() + .describe( + "Filter by UTM medium parameter values. Common values: ['organic', 'cpc', 'email', 'social', 'referral']", + ), + campaign: z + .array(z.string()) + .optional() + .describe( + "Filter by UTM campaign parameter values (e.g., ['summer_sale', 'product_launch'])", + ), + channel: z + .array( + z.enum([ + "OrganicSearch", + "Direct", + "Email", + "Display", + "Social", + "PaidSearch", + "Other", + "Affiliate", + "Referral", + "Video", + "Audio", + "SMS", + "AITools", + "PaidAITools", + ]), + ) + .optional() + .describe("Filter by marketing channel classifications."), + smartEvents: z + .array(z.string()) + .optional() + .describe( + "Filter by smart event names/IDs. Can be user-defined events or Clarity auto events: 'Purchase', 'ContactUs', 'SubmitForm', 'AddToCart', 'RequestQuote', 'SignUp', 'BeginCheckout', 'Download', 'Login', 'Search', 'Play', 'Deposit', 'Schedule', 'Subscribe', 'FindLocation', 'OutboundClick', 'ShowMore', 'Book', 'RetryRefresh', 'Pay', 'Pause', 'Upload', 'CheckAvailability', 'Withdraw', 'Export', 'SeeReviews', 'AddPaymentMethod', 'AddToWishlist', 'NotifyMe', 'CheckIn', 'TransferMoney', 'Upgrade', 'ContinueAsGuest', 'Mute', 'Exchange', 'ApplyCoupon', 'Unsubscribe', 'AddToCalendar', 'Unmute', 'EnterFullScreen', 'DeleteAccount', 'AppInstall', 'Checkout', 'OrderSuccess'", + ), + javascriptErrors: z + .array(z.string()) + .optional() + .describe( + "Filter by JavaScript error messages or patterns. Use empty string '' to match any JavaScript error", + ), + clickErrors: z + .array(z.string()) + .optional() + .describe( + "Filter by click error patterns or messages. Use empty string '' to match any click error", + ), + clickedText: z + .string() + .optional() + .describe( + "Filter by specific text content that was clicked (partial match supported)", + ), + enteredTextPresent: z + .boolean() + .optional() + .describe( + "Filter sessions where text input events occurred. Set to true to include only sessions with text input", + ), + selectedTextPresent: z + .boolean() + .optional() + .describe( + "Filter sessions where text selection events occurred. Set to true to include only sessions with text selection", + ), + resizeEventPresent: z + .boolean() + .optional() + .describe( + "Filter sessions where page resize events occurred. Set to true to include only sessions with resize events", + ), + cursorMovement: z + .boolean() + .optional() + .describe( + "Filter sessions with cursor/pointer movement activity. Set to true to include only sessions with cursor movement", + ), + deadClickPresent: z + .boolean() + .optional() + .describe( + "Filter sessions containing dead clicks (clicks with no response). Set to true to include only sessions with dead clicks", + ), + rageClickPresent: z + .boolean() + .optional() + .describe( + "Filter sessions containing rage clicks (rapid repeated clicks). Set to true to include only sessions with rage clicks", + ), + excessiveScrollPresent: z + .boolean() + .optional() + .describe( + "Filter sessions with excessive scrolling behavior. Set to true to include only sessions with excessive scrolling", + ), + quickbackClickPresent: z + .boolean() + .optional() + .describe( + "Filter sessions with quick back navigation clicks. Set to true to include only sessions with quick back clicks", + ), + visiblePageDuration: NullableRangeFilter.optional().describe( + "Filter by time spent on visible pages in minutes. Set to null to ignore this filter.", + ), + hiddenPageDuration: NullableRangeFilter.optional().describe( + "Filter by time spent on hidden/background pages in minutes. Set to null to ignore this filter.", + ), + pageDuration: NullableRangeFilter.optional().describe( + "Filter by total page duration in minutes. Set to null to ignore this filter.", + ), + sessionDuration: NullableRangeFilter.optional().describe( + "Filter by total session duration in minutes. Set to null to ignore this filter.", + ), + scrollDepth: createRangeFilter(0, 100) + .optional() + .describe( + "Filter by maximum scroll depth percentage. Set to null to ignore this filter.", + ), + pagesCount: NullableRangeFilter.optional().describe( + "Filter by number of pages visited in session. Set to null to ignore this filter.", + ), + pageClickEventCount: NullableRangeFilter.optional().describe( + "Filter by number of clicks per page. Set to null to ignore this filter.", + ), + sessionClickEventCount: NullableRangeFilter.optional().describe( + "Filter by total clicks per session. Set to null to ignore this filter.", + ), + performanceScore: createRangeFilter(0, 100) + .optional() + .describe( + "Filter by overall performance score. Set to null to ignore this filter.", + ), + largestContentfulPaint: NullableRangeFilter.optional().describe( + "Filter by Largest Contentful Paint web vital in seconds. Set to null to ignore this filter.", + ), + cumulativeLayoutShift: NullableRangeFilter.optional().describe( + "Filter by Cumulative Layout Shift web vital in seconds. Set to null to ignore this filter.", + ), + firstInputDelay: NullableRangeFilter.optional().describe( + "Filter by First Input Delay web vital in milliseconds. Set to null to ignore this filter.", + ), + productRating: NullableRangeFilter.optional().describe( + "Filter by product ratings (e.g., 1-5 stars). Set to null to ignore this filter.", + ), + productRatingsCount: NullableRangeFilter.optional().describe( + "Filter by number of product ratings. Set to null to ignore this filter.", + ), + productPrice: NullableRangeFilter.optional().describe( + "Filter by product price range. Set to null to ignore this filter.", + ), + productName: z + .string() + .optional() + .describe( + "Filter by product name (partial match supported using contains operator)", + ), + productPurchases: z + .boolean() + .optional() + .describe( + "Filter sessions with checkout conversion/purchases. Set to true to include only sessions with purchases", + ), + productAvailability: z + .boolean() + .optional() + .describe( + "Filter by product availability status. Set to true to include only sessions with available products", + ), + productBrand: z + .array(z.string()) + .optional() + .describe( + "Filter by product brand names (e.g., ['Nike', 'Apple', 'Samsung'])", + ), + checkoutAbandonmentStep: z + .array(z.string()) + .optional() + .describe( + "Filter by checkout abandonment steps/stages (e.g., ['cart', 'shipping', 'payment'])", + ), + date: z + .object({ + start: z + .string() + .describe( + "The start date of the time interval in UTC ISO 8601 with milliseconds format (yyyy-MM-ddTHH:mm:ss.fffZ).", + ), + end: z + .string() + .describe( + "The end date of the time interval in UTC ISO 8601 with milliseconds format (yyyy-MM-ddTHH:mm:ss.fffZ).", + ), + }) + .optional(), + }) + .describe( + "A set of filters that can be applied to the Microsoft Clarity to session recordings. This allows you to filter recordings based on various criteria such as URLs, device types, browser, OS, country, city, and more. The date filter is required and must be in UTC ISO 8601 format.", + ); + +export type FiltersType = z.infer; + +export const SortOptions = z + .enum([ + "SessionStart_DESC", + "SessionStart_ASC", + "SessionDuration_ASC", + "SessionDuration_DESC", + "SessionClickCount_ASC", + "SessionClickCount_DESC", + "PageCount_ASC", + "PageCount_DESC", + ]) + .default("SessionStart_DESC") + .describe( + "Sort option for session recordings. Default is SessionStart_DESC (newest first).", + ); + +export type SortOptionsType = z.infer; + +export const SampleCount = z + .number() + .lte(250, "Maximum sample count is 250") + .default(100) + .describe( + "The number of sample session recordings to return. Default is 100. Maximum is 250.", + ); + +export const SearchQuery = z + .string() + .describe( + "A natural language search query string for filtering and shaping analytics data. The query should be specific and include temporal constraints when available. (e.g., 'Top browsers last 3 days', 'The active time duration for mobile devices in United States last week'). Time ranges should be explicitly specified when possible. If no time range is provided, prompt the user to specify one.", + ); + +export const ListRecordingsRequest = z.object({ + filters: Filters.optional(), + sortBy: SortOptions.optional(), + count: SampleCount.optional(), + token: z + .string() + .optional() + .describe("Clarity API Token (overrides installation config)"), +}); + +export const SearchRequest = z.object({ + query: SearchQuery, + token: z + .string() + .optional() + .describe("Clarity API Token (overrides installation config)"), +}); diff --git a/microsoft-clarity/server/types/env.ts b/microsoft-clarity/server/types/env.ts new file mode 100644 index 00000000..873ffceb --- /dev/null +++ b/microsoft-clarity/server/types/env.ts @@ -0,0 +1,64 @@ +/** + * Environment Type Definitions + * + * This file defines the StateSchema (configuration form in Mesh UI) + * and the Env type used throughout your MCP. + */ + +import type { DefaultEnv } from "@decocms/runtime"; +// import { BindingOf } from "@decocms/runtime"; // Uncomment when using bindings +import { z } from "zod"; + +/** + * State Schema - Configuration form for your MCP + * + * Users fill this form when installing your MCP in Mesh. + * Organize fields by category for better UX (see examples below). + */ +export const StateSchema = z.object({ + // ======================================== + // 1. BINDINGS (optional, uncomment if needed) + // ======================================== + // EVENT_BUS: BindingOf("@deco/event-bus").optional(), + // DATABASE: BindingOf("@deco/postgres"), + // MODEL_PROVIDER: BindingOf("@deco/llm") + // .optional() + // .describe("AI Model Provider connection"), + // ======================================== + // 2. API CREDENTIALS + // ======================================== + API_CREDENTIALS: z + + .object({ + API_KEY: z + .string() + .describe("Your Microsoft Clarity API token (Settings -> Data Export)"), + }) + + .describe("Microsoft Clarity authentication credentials"), + // ======================================== + // 3. CONFIGURATION OPTIONS (example) + // ======================================== + // FEATURE_CONFIG: z + // .object({ + // ENABLED: z.boolean().default(true).describe("Enable this feature"), + // MAX_ITEMS: z.number().default(100).describe("Maximum items to process"), + // }) + // .optional() + // .describe("Feature configuration settings"), + // ======================================== + // 4. SIMPLE FIELDS (example) + // ======================================== + // CONNECTION_NAME: z + // .string() + // .optional() + // .describe("Friendly name for this connection (appears in logs)"), +}); + +/** + * Environment Type + * + * This type is used throughout your MCP to access configuration, + * bindings, and the Mesh request context. + */ +export type Env = DefaultEnv; diff --git a/microsoft-clarity/tsconfig.json b/microsoft-clarity/tsconfig.json new file mode 100644 index 00000000..b590c229 --- /dev/null +++ b/microsoft-clarity/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2023", "ES2024"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "verbatimModuleSyntax": false, + "moduleDetection": "force", + "noEmit": true, + "allowJs": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + + /* Path Aliases */ + "baseUrl": ".", + "paths": { + "server/*": ["./server/*"] + } + }, + "include": ["server"] +}