@recapt/mcp 0.0.2-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +12 -0
- package/dist/api/client.js +67 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +217 -0
- package/dist/tools/analyzeFlow.d.ts +7 -0
- package/dist/tools/analyzeFlow.js +68 -0
- package/dist/tools/analyzeFunnel.d.ts +7 -0
- package/dist/tools/analyzeFunnel.js +63 -0
- package/dist/tools/compareCohorts.d.ts +6 -0
- package/dist/tools/compareCohorts.js +84 -0
- package/dist/tools/comparePeriods.d.ts +6 -0
- package/dist/tools/comparePeriods.js +54 -0
- package/dist/tools/detectDrift.d.ts +6 -0
- package/dist/tools/detectDrift.js +55 -0
- package/dist/tools/detectRegressions.d.ts +6 -0
- package/dist/tools/detectRegressions.js +46 -0
- package/dist/tools/discoverPersonas.d.ts +6 -0
- package/dist/tools/discoverPersonas.js +50 -0
- package/dist/tools/getActionableIssues.d.ts +7 -0
- package/dist/tools/getActionableIssues.js +55 -0
- package/dist/tools/getAnomalies.d.ts +6 -0
- package/dist/tools/getAnomalies.js +53 -0
- package/dist/tools/getConsoleErrors.d.ts +6 -0
- package/dist/tools/getConsoleErrors.js +61 -0
- package/dist/tools/getDeadClicks.d.ts +6 -0
- package/dist/tools/getDeadClicks.js +42 -0
- package/dist/tools/getDomains.d.ts +6 -0
- package/dist/tools/getDomains.js +34 -0
- package/dist/tools/getElementFriction.d.ts +6 -0
- package/dist/tools/getElementFriction.js +45 -0
- package/dist/tools/getFlowFriction.d.ts +7 -0
- package/dist/tools/getFlowFriction.js +57 -0
- package/dist/tools/getFormFriction.d.ts +6 -0
- package/dist/tools/getFormFriction.js +42 -0
- package/dist/tools/getIssues.d.ts +6 -0
- package/dist/tools/getIssues.js +82 -0
- package/dist/tools/getJourneyPatterns.d.ts +7 -0
- package/dist/tools/getJourneyPatterns.js +50 -0
- package/dist/tools/getPageMetrics.d.ts +6 -0
- package/dist/tools/getPageMetrics.js +47 -0
- package/dist/tools/getPageTrends.d.ts +6 -0
- package/dist/tools/getPageTrends.js +46 -0
- package/dist/tools/getSessionDetails.d.ts +6 -0
- package/dist/tools/getSessionDetails.js +70 -0
- package/dist/tools/getUxHealthReport.d.ts +7 -0
- package/dist/tools/getUxHealthReport.js +50 -0
- package/dist/tools/listPages.d.ts +6 -0
- package/dist/tools/listPages.js +50 -0
- package/dist/tools/listSessions.d.ts +7 -0
- package/dist/tools/listSessions.js +67 -0
- package/dist/tools/memory.d.ts +7 -0
- package/dist/tools/memory.js +119 -0
- package/dist/tools/predictOutcomes.d.ts +6 -0
- package/dist/tools/predictOutcomes.js +66 -0
- package/dist/tools/scanSite.d.ts +6 -0
- package/dist/tools/scanSite.js +51 -0
- package/dist/tools/searchSessions.d.ts +6 -0
- package/dist/tools/searchSessions.js +51 -0
- package/dist/tools/triageSessions.d.ts +8 -0
- package/dist/tools/triageSessions.js +195 -0
- package/dist/utils/orgContext.d.ts +6 -0
- package/dist/utils/orgContext.js +22 -0
- package/package.json +37 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* detect_regressions tool
|
|
3
|
+
*
|
|
4
|
+
* Detect behavioral regressions by comparing recent data against baselines.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { apiGet, isApiConfigured } from "../api/client.js";
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
export function registerDetectRegressions(server) {
|
|
10
|
+
server.registerTool("detect_regressions", {
|
|
11
|
+
description: "Detect behavioral regressions by comparing recent data (last 24h by default) against " +
|
|
12
|
+
"rolling 7-day baselines. Returns pages where frustration, confusion, or rage click rate " +
|
|
13
|
+
"have deviated significantly from normal levels, with z-scores indicating severity. " +
|
|
14
|
+
"Critical (z>3) means an extreme deviation, high (z>2) is notable, medium (z>1.5) is worth monitoring. " +
|
|
15
|
+
"Use this to catch deployments or changes that degraded user experience.",
|
|
16
|
+
inputSchema: z.object({
|
|
17
|
+
window_hours: z
|
|
18
|
+
.number()
|
|
19
|
+
.optional()
|
|
20
|
+
.default(24)
|
|
21
|
+
.describe("How many recent hours to compare against baseline (default 24)"),
|
|
22
|
+
}),
|
|
23
|
+
}, async ({ window_hours }) => {
|
|
24
|
+
if (!isApiConfigured()) {
|
|
25
|
+
return {
|
|
26
|
+
content: [
|
|
27
|
+
{
|
|
28
|
+
type: "text",
|
|
29
|
+
text: JSON.stringify({ error: "API not configured" }),
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
isError: true,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const { data, error } = await apiGet("/regressions", {
|
|
36
|
+
window_hours: window_hours ?? 24,
|
|
37
|
+
});
|
|
38
|
+
if (error) {
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
41
|
+
isError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* discover_personas tool
|
|
3
|
+
*
|
|
4
|
+
* Discover behavioral personas - groups of users who behave similarly.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { apiGet, isApiConfigured } from "../api/client.js";
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
export function registerDiscoverPersonas(server) {
|
|
10
|
+
server.registerTool("discover_personas", {
|
|
11
|
+
description: "Discover behavioral personas - groups of users who behave similarly " +
|
|
12
|
+
"(e.g. confident completers, frustrated battlers, lost explorers). " +
|
|
13
|
+
"Returns rich profiles with descriptions, risk factors, and recommended interventions. " +
|
|
14
|
+
"Uses K-means clustering on behavioral embeddings. Optionally scope to a specific page.",
|
|
15
|
+
inputSchema: z.object({
|
|
16
|
+
page_path: z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("Scope persona discovery to a specific page"),
|
|
20
|
+
num_personas: z
|
|
21
|
+
.number()
|
|
22
|
+
.optional()
|
|
23
|
+
.default(4)
|
|
24
|
+
.describe("Number of personas to discover (default 4, max 8)"),
|
|
25
|
+
}),
|
|
26
|
+
}, async ({ page_path, num_personas, }) => {
|
|
27
|
+
if (!isApiConfigured()) {
|
|
28
|
+
return {
|
|
29
|
+
content: [
|
|
30
|
+
{
|
|
31
|
+
type: "text",
|
|
32
|
+
text: JSON.stringify({ error: "API not configured" }),
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
isError: true,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const { data, error } = await apiGet("/personas", {
|
|
39
|
+
page_path,
|
|
40
|
+
num_personas: num_personas ?? 4,
|
|
41
|
+
});
|
|
42
|
+
if (error) {
|
|
43
|
+
return {
|
|
44
|
+
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
45
|
+
isError: true,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get_actionable_issues tool
|
|
3
|
+
*
|
|
4
|
+
* Returns UX issues with element context for investigation.
|
|
5
|
+
* Thin proxy to external-api /actionable-issues endpoint.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { apiGet, isApiConfigured } from "../api/client.js";
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
export function registerGetActionableIssues(server) {
|
|
11
|
+
server.registerTool("get_actionable_issues", {
|
|
12
|
+
description: "Get UX issues with element context. Returns detected issues (rage clicks, dead clicks, " +
|
|
13
|
+
"errors, etc.) along with the most affected elements on each page. " +
|
|
14
|
+
"Use this when you want to understand and fix specific UX problems.",
|
|
15
|
+
inputSchema: z.object({
|
|
16
|
+
page_path: z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("Filter to a specific page path (e.g., /checkout)"),
|
|
20
|
+
severity: z
|
|
21
|
+
.enum(["critical", "high", "medium", "low", "info"])
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("Minimum severity level to include"),
|
|
24
|
+
limit: z
|
|
25
|
+
.number()
|
|
26
|
+
.optional()
|
|
27
|
+
.default(10)
|
|
28
|
+
.describe("Maximum number of issues to return (default: 10)"),
|
|
29
|
+
}),
|
|
30
|
+
}, async ({ page_path, severity, limit, }) => {
|
|
31
|
+
if (!isApiConfigured()) {
|
|
32
|
+
return {
|
|
33
|
+
content: [
|
|
34
|
+
{
|
|
35
|
+
type: "text",
|
|
36
|
+
text: JSON.stringify({ error: "API not configured" }),
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
isError: true,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const { data, error } = await apiGet("/actionable-issues", {
|
|
43
|
+
page_path,
|
|
44
|
+
severity,
|
|
45
|
+
limit: limit ?? 10,
|
|
46
|
+
});
|
|
47
|
+
if (error) {
|
|
48
|
+
return {
|
|
49
|
+
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
50
|
+
isError: true,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get_anomalies tool
|
|
3
|
+
*
|
|
4
|
+
* Detects anomalous sessions and frustration spikes.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { apiGet, isApiConfigured } from "../api/client.js";
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
export function registerGetAnomalies(server) {
|
|
10
|
+
server.registerTool("get_anomalies", {
|
|
11
|
+
description: "Detect anomalous sessions and frustration spikes. Finds sessions that deviate from normal behavior patterns.",
|
|
12
|
+
inputSchema: z.object({
|
|
13
|
+
threshold: z
|
|
14
|
+
.number()
|
|
15
|
+
.optional()
|
|
16
|
+
.default(0.35)
|
|
17
|
+
.describe("Anomaly threshold (0-1, default: 0.35)"),
|
|
18
|
+
days: z
|
|
19
|
+
.number()
|
|
20
|
+
.optional()
|
|
21
|
+
.default(7)
|
|
22
|
+
.describe("Days to analyze for frustration spikes (default: 7)"),
|
|
23
|
+
page_path: z
|
|
24
|
+
.string()
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("Optional: filter to a specific page"),
|
|
27
|
+
}),
|
|
28
|
+
}, async ({ threshold, days, page_path, }) => {
|
|
29
|
+
if (!isApiConfigured()) {
|
|
30
|
+
return {
|
|
31
|
+
content: [
|
|
32
|
+
{
|
|
33
|
+
type: "text",
|
|
34
|
+
text: JSON.stringify({ error: "API not configured" }),
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
isError: true,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const { data, error } = await apiGet("/anomalies", {
|
|
41
|
+
threshold: threshold ?? 0.35,
|
|
42
|
+
days: days ?? 7,
|
|
43
|
+
page_path,
|
|
44
|
+
});
|
|
45
|
+
if (error) {
|
|
46
|
+
return {
|
|
47
|
+
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
48
|
+
isError: true,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
52
|
+
});
|
|
53
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get_console_errors tool
|
|
3
|
+
*
|
|
4
|
+
* Query JavaScript console errors and warnings from user sessions.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { apiGet, isApiConfigured } from "../api/client.js";
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
export function registerGetConsoleErrors(server) {
|
|
10
|
+
server.registerTool("get_console_errors", {
|
|
11
|
+
description: "Query JavaScript console errors and warnings from user sessions. " +
|
|
12
|
+
"Use this when you suspect page unresponsiveness, broken buttons, or rendering issues. " +
|
|
13
|
+
"Returns the most common errors grouped by message, with session impact counts. " +
|
|
14
|
+
"High rage clicks on root/body elements often correlate with JS errors.",
|
|
15
|
+
inputSchema: z.object({
|
|
16
|
+
page_path: z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("Filter errors to a specific page path"),
|
|
20
|
+
session_id: z
|
|
21
|
+
.string()
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("Filter errors to a specific session"),
|
|
24
|
+
severity: z
|
|
25
|
+
.enum(["error", "warn", "all"])
|
|
26
|
+
.optional()
|
|
27
|
+
.default("error")
|
|
28
|
+
.describe('Filter by severity: "error" (default), "warn", or "all"'),
|
|
29
|
+
limit: z
|
|
30
|
+
.number()
|
|
31
|
+
.optional()
|
|
32
|
+
.default(200)
|
|
33
|
+
.describe("Max log documents to scan (default 200)"),
|
|
34
|
+
}),
|
|
35
|
+
}, async ({ page_path, session_id, severity, limit, }) => {
|
|
36
|
+
if (!isApiConfigured()) {
|
|
37
|
+
return {
|
|
38
|
+
content: [
|
|
39
|
+
{
|
|
40
|
+
type: "text",
|
|
41
|
+
text: JSON.stringify({ error: "API not configured" }),
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
isError: true,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const { data, error } = await apiGet("/console-errors", {
|
|
48
|
+
page_path,
|
|
49
|
+
session_id,
|
|
50
|
+
severity: severity ?? "error",
|
|
51
|
+
limit: limit ?? 200,
|
|
52
|
+
});
|
|
53
|
+
if (error) {
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
56
|
+
isError: true,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get_dead_clicks tool
|
|
3
|
+
*
|
|
4
|
+
* Find buttons and links that users click but nothing happens (dead clicks).
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { apiGet, isApiConfigured } from "../api/client.js";
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
export function registerGetDeadClicks(server) {
|
|
10
|
+
server.registerTool("get_dead_clicks", {
|
|
11
|
+
description: "Find buttons and links that users click but nothing happens (dead clicks). " +
|
|
12
|
+
"These are interactive elements where the click produced no visible DOM change. " +
|
|
13
|
+
"Use this to identify BROKEN or UNRESPONSIVE UI elements that frustrate users.",
|
|
14
|
+
inputSchema: z.object({
|
|
15
|
+
page_path: z
|
|
16
|
+
.string()
|
|
17
|
+
.describe("Page path to check for dead clicks (required)"),
|
|
18
|
+
}),
|
|
19
|
+
}, async ({ page_path }) => {
|
|
20
|
+
if (!isApiConfigured()) {
|
|
21
|
+
return {
|
|
22
|
+
content: [
|
|
23
|
+
{
|
|
24
|
+
type: "text",
|
|
25
|
+
text: JSON.stringify({ error: "API not configured" }),
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
isError: true,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const { data, error } = await apiGet("/dead-clicks", {
|
|
32
|
+
page_path,
|
|
33
|
+
});
|
|
34
|
+
if (error) {
|
|
35
|
+
return {
|
|
36
|
+
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
37
|
+
isError: true,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get_domains tool
|
|
3
|
+
*
|
|
4
|
+
* Returns the list of domains configured for the organization.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { apiGet, isApiConfigured } from "../api/client.js";
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
export function registerGetDomains(server) {
|
|
10
|
+
server.registerTool("get_domains", {
|
|
11
|
+
description: "Get the list of domains configured for the organization. Domains represent different websites or applications being tracked.",
|
|
12
|
+
inputSchema: z.object({}),
|
|
13
|
+
}, async () => {
|
|
14
|
+
if (!isApiConfigured()) {
|
|
15
|
+
return {
|
|
16
|
+
content: [
|
|
17
|
+
{
|
|
18
|
+
type: "text",
|
|
19
|
+
text: JSON.stringify({ error: "API not configured" }),
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
isError: true,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const { data, error } = await apiGet("/domains");
|
|
26
|
+
if (error) {
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
29
|
+
isError: true,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get_element_friction tool
|
|
3
|
+
*
|
|
4
|
+
* Returns per-element friction data for a specific page.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { apiGet, isApiConfigured } from "../api/client.js";
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
export function registerGetElementFriction(server) {
|
|
10
|
+
server.registerTool("get_element_friction", {
|
|
11
|
+
description: "Get per-element friction data for a page. Shows which elements cause user frustration, with click counts and dominant frustration signals.",
|
|
12
|
+
inputSchema: z.object({
|
|
13
|
+
page_path: z
|
|
14
|
+
.string()
|
|
15
|
+
.describe("Page path to analyze (e.g., /checkout)"),
|
|
16
|
+
selector_filter: z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("Optional: filter elements by selector substring"),
|
|
20
|
+
}),
|
|
21
|
+
}, async ({ page_path, selector_filter, }) => {
|
|
22
|
+
if (!isApiConfigured()) {
|
|
23
|
+
return {
|
|
24
|
+
content: [
|
|
25
|
+
{
|
|
26
|
+
type: "text",
|
|
27
|
+
text: JSON.stringify({ error: "API not configured" }),
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
isError: true,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const { data, error } = await apiGet("/elements", {
|
|
34
|
+
page_path,
|
|
35
|
+
selector_filter,
|
|
36
|
+
});
|
|
37
|
+
if (error) {
|
|
38
|
+
return {
|
|
39
|
+
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
40
|
+
isError: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get_flow_friction tool
|
|
3
|
+
*
|
|
4
|
+
* Discovers high-friction user flows by analyzing navigation patterns
|
|
5
|
+
* and behavioral data. Thin proxy to external-api /flow-friction endpoint.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { apiGet, isApiConfigured } from "../api/client.js";
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
export function registerGetFlowFriction(server) {
|
|
11
|
+
server.registerTool("get_flow_friction", {
|
|
12
|
+
description: "Discover user flows with behavioral metrics. Returns navigation transitions " +
|
|
13
|
+
"with frustration, confusion, and health scores for each page in the flow. " +
|
|
14
|
+
"Also flags backtrack hotspots (pages users return to) and dropoff pages. " +
|
|
15
|
+
"Use this to find which flows need optimization.",
|
|
16
|
+
inputSchema: z.object({
|
|
17
|
+
page_path: z
|
|
18
|
+
.string()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("Focus on flows involving this page (e.g., /checkout)"),
|
|
21
|
+
days: z
|
|
22
|
+
.number()
|
|
23
|
+
.optional()
|
|
24
|
+
.default(7)
|
|
25
|
+
.describe("Number of days to analyze (default: 7)"),
|
|
26
|
+
limit: z
|
|
27
|
+
.number()
|
|
28
|
+
.optional()
|
|
29
|
+
.default(10)
|
|
30
|
+
.describe("Maximum number of flows to return (default: 10)"),
|
|
31
|
+
}),
|
|
32
|
+
}, async ({ page_path, days, limit, }) => {
|
|
33
|
+
if (!isApiConfigured()) {
|
|
34
|
+
return {
|
|
35
|
+
content: [
|
|
36
|
+
{
|
|
37
|
+
type: "text",
|
|
38
|
+
text: JSON.stringify({ error: "API not configured" }),
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
isError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const { data, error } = await apiGet("/flow-friction", {
|
|
45
|
+
page_path,
|
|
46
|
+
days: days ?? 7,
|
|
47
|
+
limit: limit ?? 10,
|
|
48
|
+
});
|
|
49
|
+
if (error) {
|
|
50
|
+
return {
|
|
51
|
+
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
52
|
+
isError: true,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get_form_friction tool
|
|
3
|
+
*
|
|
4
|
+
* Analyze which form fields cause friction.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { apiGet, isApiConfigured } from "../api/client.js";
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
export function registerGetFormFriction(server) {
|
|
10
|
+
server.registerTool("get_form_friction", {
|
|
11
|
+
description: "Analyze which form fields cause friction on a page. " +
|
|
12
|
+
"Returns per-field metrics including dwell time, correction rate, and abandonment rate. " +
|
|
13
|
+
"Use this to identify problematic form fields in checkout, signup, or other forms.",
|
|
14
|
+
inputSchema: z.object({
|
|
15
|
+
page_path: z
|
|
16
|
+
.string()
|
|
17
|
+
.describe("Page path containing the form to analyze (required)"),
|
|
18
|
+
}),
|
|
19
|
+
}, async ({ page_path }) => {
|
|
20
|
+
if (!isApiConfigured()) {
|
|
21
|
+
return {
|
|
22
|
+
content: [
|
|
23
|
+
{
|
|
24
|
+
type: "text",
|
|
25
|
+
text: JSON.stringify({ error: "API not configured" }),
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
isError: true,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const { data, error } = await apiGet("/forms/friction", {
|
|
32
|
+
page_path,
|
|
33
|
+
});
|
|
34
|
+
if (error) {
|
|
35
|
+
return {
|
|
36
|
+
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
37
|
+
isError: true,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get_issues tool
|
|
3
|
+
*
|
|
4
|
+
* Returns detected UX issues.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { apiGet, isApiConfigured } from "../api/client.js";
|
|
8
|
+
const VALID_CATEGORIES = [
|
|
9
|
+
"code_error",
|
|
10
|
+
"dead_click",
|
|
11
|
+
"rage_click",
|
|
12
|
+
"ux_friction",
|
|
13
|
+
"performance",
|
|
14
|
+
"form_issue",
|
|
15
|
+
"behavioral_anomaly",
|
|
16
|
+
"structural_issue",
|
|
17
|
+
];
|
|
18
|
+
const VALID_SEVERITIES = ["critical", "high", "medium", "low", "info"];
|
|
19
|
+
const VALID_STATUSES = [
|
|
20
|
+
"pending",
|
|
21
|
+
"active",
|
|
22
|
+
"escalated",
|
|
23
|
+
"resolved",
|
|
24
|
+
"dismissed",
|
|
25
|
+
"expired",
|
|
26
|
+
];
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
export function registerGetIssues(server) {
|
|
29
|
+
server.registerTool("get_issues", {
|
|
30
|
+
description: "Get detected UX issues. Issues are automatically detected from session recordings and include rage clicks, dead clicks, form issues, and behavioral anomalies.",
|
|
31
|
+
inputSchema: z.object({
|
|
32
|
+
page_path: z
|
|
33
|
+
.string()
|
|
34
|
+
.optional()
|
|
35
|
+
.describe("Filter issues to a specific page path"),
|
|
36
|
+
category: z
|
|
37
|
+
.enum(VALID_CATEGORIES)
|
|
38
|
+
.optional()
|
|
39
|
+
.describe("Filter by issue category"),
|
|
40
|
+
severity: z
|
|
41
|
+
.enum(VALID_SEVERITIES)
|
|
42
|
+
.optional()
|
|
43
|
+
.describe("Filter by severity level"),
|
|
44
|
+
status: z
|
|
45
|
+
.enum(VALID_STATUSES)
|
|
46
|
+
.optional()
|
|
47
|
+
.default("active")
|
|
48
|
+
.describe("Filter by status (default: active)"),
|
|
49
|
+
limit: z
|
|
50
|
+
.number()
|
|
51
|
+
.optional()
|
|
52
|
+
.default(20)
|
|
53
|
+
.describe("Maximum number of issues to return (default: 20)"),
|
|
54
|
+
}),
|
|
55
|
+
}, async ({ page_path, category, severity, status, limit, }) => {
|
|
56
|
+
if (!isApiConfigured()) {
|
|
57
|
+
return {
|
|
58
|
+
content: [
|
|
59
|
+
{
|
|
60
|
+
type: "text",
|
|
61
|
+
text: JSON.stringify({ error: "API not configured" }),
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
isError: true,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const { data, error } = await apiGet("/issues", {
|
|
68
|
+
page_path,
|
|
69
|
+
category,
|
|
70
|
+
severity,
|
|
71
|
+
status: status ?? "active",
|
|
72
|
+
limit: limit ?? 20,
|
|
73
|
+
});
|
|
74
|
+
if (error) {
|
|
75
|
+
return {
|
|
76
|
+
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
77
|
+
isError: true,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
81
|
+
});
|
|
82
|
+
}
|