@recapt/mcp 0.0.19-beta → 0.0.20-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/cli/index.d.ts +0 -11
- package/dist/cli/index.js +1059 -21
- package/dist/index.d.ts +0 -19
- package/dist/index.js +3210 -89
- package/package.json +5 -3
- package/dist/api/client.d.ts +0 -6
- package/dist/api/client.js +0 -6
- package/dist/cli/commands/setup-self-improvement-gh.d.ts +0 -7
- package/dist/cli/commands/setup-self-improvement-gh.js +0 -310
- package/dist/cli/commands/setup.d.ts +0 -7
- package/dist/cli/commands/setup.js +0 -178
- package/dist/cli/commands/skill.d.ts +0 -24
- package/dist/cli/commands/skill.js +0 -264
- package/dist/cli/utils/ide-config.d.ts +0 -31
- package/dist/cli/utils/ide-config.js +0 -294
- package/dist/cli/utils/prompts.d.ts +0 -22
- package/dist/cli/utils/prompts.js +0 -71
- package/dist/tools/analyzeFlow.d.ts +0 -7
- package/dist/tools/analyzeFlow.js +0 -68
- package/dist/tools/analyzeFunnel.d.ts +0 -7
- package/dist/tools/analyzeFunnel.js +0 -63
- package/dist/tools/catalog/callTool.d.ts +0 -22
- package/dist/tools/catalog/callTool.js +0 -92
- package/dist/tools/catalog/index.d.ts +0 -11
- package/dist/tools/catalog/index.js +0 -11
- package/dist/tools/catalog/searchTools.d.ts +0 -22
- package/dist/tools/catalog/searchTools.js +0 -194
- package/dist/tools/compareCohorts.d.ts +0 -6
- package/dist/tools/compareCohorts.js +0 -84
- package/dist/tools/comparePeriods.d.ts +0 -6
- package/dist/tools/comparePeriods.js +0 -54
- package/dist/tools/detectDrift.d.ts +0 -6
- package/dist/tools/detectDrift.js +0 -55
- package/dist/tools/detectRegressions.d.ts +0 -6
- package/dist/tools/detectRegressions.js +0 -63
- package/dist/tools/diagnostic.d.ts +0 -6
- package/dist/tools/diagnostic.js +0 -109
- package/dist/tools/discoverPersonas.d.ts +0 -6
- package/dist/tools/discoverPersonas.js +0 -50
- package/dist/tools/getActionableIssues.d.ts +0 -7
- package/dist/tools/getActionableIssues.js +0 -55
- package/dist/tools/getAnomalies.d.ts +0 -6
- package/dist/tools/getAnomalies.js +0 -53
- package/dist/tools/getConsoleErrors.d.ts +0 -6
- package/dist/tools/getConsoleErrors.js +0 -61
- package/dist/tools/getDeadClicks.d.ts +0 -6
- package/dist/tools/getDeadClicks.js +0 -42
- package/dist/tools/getDomains.d.ts +0 -6
- package/dist/tools/getDomains.js +0 -34
- package/dist/tools/getElementFriction.d.ts +0 -6
- package/dist/tools/getElementFriction.js +0 -45
- package/dist/tools/getFlowFriction.d.ts +0 -7
- package/dist/tools/getFlowFriction.js +0 -57
- package/dist/tools/getFormFriction.d.ts +0 -6
- package/dist/tools/getFormFriction.js +0 -42
- package/dist/tools/getIssues.d.ts +0 -6
- package/dist/tools/getIssues.js +0 -82
- package/dist/tools/getJourneyPatterns.d.ts +0 -7
- package/dist/tools/getJourneyPatterns.js +0 -50
- package/dist/tools/getPageMetrics.d.ts +0 -6
- package/dist/tools/getPageMetrics.js +0 -47
- package/dist/tools/getPageTrends.d.ts +0 -6
- package/dist/tools/getPageTrends.js +0 -46
- package/dist/tools/getSessionDetails.d.ts +0 -6
- package/dist/tools/getSessionDetails.js +0 -70
- package/dist/tools/getSessionPages.d.ts +0 -7
- package/dist/tools/getSessionPages.js +0 -74
- package/dist/tools/getUxHealthReport.d.ts +0 -7
- package/dist/tools/getUxHealthReport.js +0 -50
- package/dist/tools/improvementRun.d.ts +0 -6
- package/dist/tools/improvementRun.js +0 -386
- package/dist/tools/knowledge.d.ts +0 -6
- package/dist/tools/knowledge.js +0 -186
- package/dist/tools/listPages.d.ts +0 -6
- package/dist/tools/listPages.js +0 -50
- package/dist/tools/listSessions.d.ts +0 -7
- package/dist/tools/listSessions.js +0 -67
- package/dist/tools/memory.d.ts +0 -7
- package/dist/tools/memory.js +0 -119
- package/dist/tools/predictOutcomes.d.ts +0 -6
- package/dist/tools/predictOutcomes.js +0 -66
- package/dist/tools/remediation.d.ts +0 -6
- package/dist/tools/remediation.js +0 -335
- package/dist/tools/scanSite.d.ts +0 -6
- package/dist/tools/scanSite.js +0 -51
- package/dist/tools/searchSessions.d.ts +0 -6
- package/dist/tools/searchSessions.js +0 -51
- package/dist/tools/triage.d.ts +0 -6
- package/dist/tools/triage.js +0 -114
- package/dist/tools/triageSessions.d.ts +0 -8
- package/dist/tools/triageSessions.js +0 -197
- package/dist/tools/upgradeOptions.d.ts +0 -7
- package/dist/tools/upgradeOptions.js +0 -67
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* analyze_flow tool
|
|
3
|
-
*
|
|
4
|
-
* Analyzes user navigation flows between pages.
|
|
5
|
-
* Thin proxy to external-api /flows/analyze endpoint.
|
|
6
|
-
*/
|
|
7
|
-
import { z } from "zod";
|
|
8
|
-
import { apiPost, isApiConfigured } from "../api/client.js";
|
|
9
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
-
export function registerAnalyzeFlow(server) {
|
|
11
|
-
server.registerTool("analyze_flow", {
|
|
12
|
-
description: "Analyze user navigation flows between pages. Returns paths with steps, " +
|
|
13
|
-
"behavioral metrics (frustration, confusion), success rates, and bottlenecks. " +
|
|
14
|
-
"Use this to understand how users navigate between specific pages.",
|
|
15
|
-
inputSchema: z.object({
|
|
16
|
-
start_page: z
|
|
17
|
-
.string()
|
|
18
|
-
.optional()
|
|
19
|
-
.describe("Starting page path (e.g., /pricing). Partial match supported."),
|
|
20
|
-
end_page: z
|
|
21
|
-
.string()
|
|
22
|
-
.optional()
|
|
23
|
-
.describe("Ending page path (e.g., /checkout). Partial match supported."),
|
|
24
|
-
days: z
|
|
25
|
-
.number()
|
|
26
|
-
.optional()
|
|
27
|
-
.default(7)
|
|
28
|
-
.describe("Number of days to analyze (default: 7)"),
|
|
29
|
-
}),
|
|
30
|
-
}, async ({ start_page, end_page, days, }) => {
|
|
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
|
-
if (!start_page && !end_page) {
|
|
43
|
-
return {
|
|
44
|
-
content: [
|
|
45
|
-
{
|
|
46
|
-
type: "text",
|
|
47
|
-
text: JSON.stringify({
|
|
48
|
-
error: "At least one of start_page or end_page is required",
|
|
49
|
-
}),
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
|
-
isError: true,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
const { data, error } = await apiPost("/flows/analyze", {
|
|
56
|
-
start_page,
|
|
57
|
-
end_page,
|
|
58
|
-
days: days ?? 7,
|
|
59
|
-
});
|
|
60
|
-
if (error) {
|
|
61
|
-
return {
|
|
62
|
-
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
63
|
-
isError: true,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
67
|
-
});
|
|
68
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* analyze_funnel tool
|
|
3
|
-
*
|
|
4
|
-
* Analyzes conversion funnels through a defined sequence of pages.
|
|
5
|
-
* Thin proxy to external-api /flows/funnel endpoint.
|
|
6
|
-
*/
|
|
7
|
-
import { z } from "zod";
|
|
8
|
-
import { apiPost, isApiConfigured } from "../api/client.js";
|
|
9
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
-
export function registerAnalyzeFunnel(server) {
|
|
11
|
-
server.registerTool("analyze_funnel", {
|
|
12
|
-
description: "Analyze a conversion funnel through a sequence of pages. Returns per-step metrics " +
|
|
13
|
-
"(entered, converted, dropped, dwell time, frustration, confusion) and dropoff analysis " +
|
|
14
|
-
'showing where users exit. Supports wildcards: "/checkout*" matches all checkout pages.',
|
|
15
|
-
inputSchema: z.object({
|
|
16
|
-
steps: z
|
|
17
|
-
.array(z.string())
|
|
18
|
-
.min(2)
|
|
19
|
-
.describe('Ordered page paths forming the funnel (e.g., ["/cart", "/checkout", "/payment"])'),
|
|
20
|
-
days: z
|
|
21
|
-
.number()
|
|
22
|
-
.optional()
|
|
23
|
-
.default(7)
|
|
24
|
-
.describe("Number of days to analyze (default: 7)"),
|
|
25
|
-
}),
|
|
26
|
-
}, async ({ steps, days }) => {
|
|
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
|
-
if (!steps || steps.length < 2) {
|
|
39
|
-
return {
|
|
40
|
-
content: [
|
|
41
|
-
{
|
|
42
|
-
type: "text",
|
|
43
|
-
text: JSON.stringify({
|
|
44
|
-
error: "At least 2 steps are required for funnel analysis",
|
|
45
|
-
}),
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
|
-
isError: true,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
const { data, error } = await apiPost("/flows/funnel", {
|
|
52
|
-
steps,
|
|
53
|
-
days: days ?? 7,
|
|
54
|
-
});
|
|
55
|
-
if (error) {
|
|
56
|
-
return {
|
|
57
|
-
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
58
|
-
isError: true,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
62
|
-
});
|
|
63
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Call Tool — Universal tool proxy for executing any tool by name.
|
|
3
|
-
*
|
|
4
|
-
* This tool allows the agent to invoke ANY tool by name without having
|
|
5
|
-
* all tool descriptions in context. The agent discovers tools via search_tools,
|
|
6
|
-
* then calls them through this proxy.
|
|
7
|
-
*
|
|
8
|
-
* Benefits:
|
|
9
|
-
* - Minimal context: Only ~10 tools have descriptions in the prompt
|
|
10
|
-
* - All 40+ analysis tools accessible on-demand
|
|
11
|
-
* - Scales to any number of tools without context bloat
|
|
12
|
-
*/
|
|
13
|
-
export type ToolHandler = (args: Record<string, unknown>) => Promise<{
|
|
14
|
-
content: Array<{
|
|
15
|
-
type: string;
|
|
16
|
-
text: string;
|
|
17
|
-
}>;
|
|
18
|
-
isError?: boolean;
|
|
19
|
-
}>;
|
|
20
|
-
export declare function registerToolHandler(name: string, handler: ToolHandler): void;
|
|
21
|
-
export declare function getToolHandler(name: string): ToolHandler | undefined;
|
|
22
|
-
export declare function registerCallTool(server: any): void;
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Call Tool — Universal tool proxy for executing any tool by name.
|
|
3
|
-
*
|
|
4
|
-
* This tool allows the agent to invoke ANY tool by name without having
|
|
5
|
-
* all tool descriptions in context. The agent discovers tools via search_tools,
|
|
6
|
-
* then calls them through this proxy.
|
|
7
|
-
*
|
|
8
|
-
* Benefits:
|
|
9
|
-
* - Minimal context: Only ~10 tools have descriptions in the prompt
|
|
10
|
-
* - All 40+ analysis tools accessible on-demand
|
|
11
|
-
* - Scales to any number of tools without context bloat
|
|
12
|
-
*/
|
|
13
|
-
import { z } from "zod";
|
|
14
|
-
import { getToolByName } from "./searchTools.js";
|
|
15
|
-
const toolRegistry = new Map();
|
|
16
|
-
export function registerToolHandler(name, handler) {
|
|
17
|
-
toolRegistry.set(name, handler);
|
|
18
|
-
}
|
|
19
|
-
export function getToolHandler(name) {
|
|
20
|
-
return toolRegistry.get(name);
|
|
21
|
-
}
|
|
22
|
-
const callToolSchema = z.object({
|
|
23
|
-
tool_name: z
|
|
24
|
-
.string()
|
|
25
|
-
.describe("The exact name of the tool to call (e.g., 'get_page_metrics', 'analyze_flow', 'compare_cohorts')"),
|
|
26
|
-
arguments: z
|
|
27
|
-
.record(z.string(), z.unknown())
|
|
28
|
-
.describe("The arguments to pass to the tool as a JSON object. Check the tool description from search_tools for required parameters."),
|
|
29
|
-
});
|
|
30
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
-
export function registerCallTool(server) {
|
|
32
|
-
server.registerTool("call_tool", {
|
|
33
|
-
description: "Execute any analysis tool by name. Use search_tools first to discover available tools, " +
|
|
34
|
-
"then call them through this proxy. Pass the exact tool name and arguments as a JSON object. " +
|
|
35
|
-
"Example: call_tool({ tool_name: 'get_page_metrics', arguments: { page_path: '/checkout' } })",
|
|
36
|
-
inputSchema: callToolSchema,
|
|
37
|
-
}, async ({ tool_name, arguments: args }) => {
|
|
38
|
-
const handler = toolRegistry.get(tool_name);
|
|
39
|
-
if (!handler) {
|
|
40
|
-
const catalogEntry = getToolByName(tool_name);
|
|
41
|
-
if (catalogEntry) {
|
|
42
|
-
return {
|
|
43
|
-
content: [
|
|
44
|
-
{
|
|
45
|
-
type: "text",
|
|
46
|
-
text: JSON.stringify({
|
|
47
|
-
error: `Tool "${tool_name}" exists in catalog but is not registered. This may be a server configuration issue.`,
|
|
48
|
-
tool_info: {
|
|
49
|
-
name: catalogEntry.name,
|
|
50
|
-
description: catalogEntry.description,
|
|
51
|
-
category: catalogEntry.category,
|
|
52
|
-
},
|
|
53
|
-
}),
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
isError: true,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
return {
|
|
60
|
-
content: [
|
|
61
|
-
{
|
|
62
|
-
type: "text",
|
|
63
|
-
text: JSON.stringify({
|
|
64
|
-
error: `Unknown tool: ${tool_name}`,
|
|
65
|
-
hint: "Use search_tools to discover available tools first.",
|
|
66
|
-
}),
|
|
67
|
-
},
|
|
68
|
-
],
|
|
69
|
-
isError: true,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
try {
|
|
73
|
-
return await handler(args);
|
|
74
|
-
}
|
|
75
|
-
catch (err) {
|
|
76
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
77
|
-
return {
|
|
78
|
-
content: [
|
|
79
|
-
{
|
|
80
|
-
type: "text",
|
|
81
|
-
text: JSON.stringify({
|
|
82
|
-
error: `Tool execution failed: ${message}`,
|
|
83
|
-
tool_name,
|
|
84
|
-
arguments: args,
|
|
85
|
-
}),
|
|
86
|
-
},
|
|
87
|
-
],
|
|
88
|
-
isError: true,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool Catalog — Registry and discovery for MCP tools.
|
|
3
|
-
*
|
|
4
|
-
* Exports:
|
|
5
|
-
* - searchTools: Find tools by natural language query
|
|
6
|
-
* - registerSearchTools: Register the search_tools MCP tool
|
|
7
|
-
* - registerCallTool: Register the call_tool MCP tool
|
|
8
|
-
* - registerToolHandler: Register a tool handler for call_tool to invoke
|
|
9
|
-
*/
|
|
10
|
-
export { searchTools, getToolByName, getAllTools, registerSearchTools, type ToolEntry, } from "./searchTools.js";
|
|
11
|
-
export { registerCallTool, registerToolHandler, getToolHandler, type ToolHandler, } from "./callTool.js";
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool Catalog — Registry and discovery for MCP tools.
|
|
3
|
-
*
|
|
4
|
-
* Exports:
|
|
5
|
-
* - searchTools: Find tools by natural language query
|
|
6
|
-
* - registerSearchTools: Register the search_tools MCP tool
|
|
7
|
-
* - registerCallTool: Register the call_tool MCP tool
|
|
8
|
-
* - registerToolHandler: Register a tool handler for call_tool to invoke
|
|
9
|
-
*/
|
|
10
|
-
export { searchTools, getToolByName, getAllTools, registerSearchTools, } from "./searchTools.js";
|
|
11
|
-
export { registerCallTool, registerToolHandler, getToolHandler, } from "./callTool.js";
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Search Tools — Semantic and keyword search over the tool catalog.
|
|
3
|
-
*
|
|
4
|
-
* Allows the agent to discover specialized tools by describing what
|
|
5
|
-
* it needs in natural language. Uses pre-computed embeddings for
|
|
6
|
-
* fast, accurate matching with fallback to keyword search.
|
|
7
|
-
*/
|
|
8
|
-
export interface ToolEntry {
|
|
9
|
-
name: string;
|
|
10
|
-
description: string;
|
|
11
|
-
category: string;
|
|
12
|
-
parameters: Record<string, {
|
|
13
|
-
type: string;
|
|
14
|
-
required?: boolean;
|
|
15
|
-
description?: string;
|
|
16
|
-
}>;
|
|
17
|
-
embedding: number[];
|
|
18
|
-
}
|
|
19
|
-
export declare function searchTools(query: string, limit?: number, queryEmbedding?: number[]): Promise<ToolEntry[]>;
|
|
20
|
-
export declare function getToolByName(name: string): ToolEntry | undefined;
|
|
21
|
-
export declare function getAllTools(): ToolEntry[];
|
|
22
|
-
export declare function registerSearchTools(server: any): void;
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Search Tools — Semantic and keyword search over the tool catalog.
|
|
3
|
-
*
|
|
4
|
-
* Allows the agent to discover specialized tools by describing what
|
|
5
|
-
* it needs in natural language. Uses pre-computed embeddings for
|
|
6
|
-
* fast, accurate matching with fallback to keyword search.
|
|
7
|
-
*/
|
|
8
|
-
import { z } from "zod";
|
|
9
|
-
import { readFileSync } from "node:fs";
|
|
10
|
-
import { fileURLToPath } from "node:url";
|
|
11
|
-
import { dirname, join } from "node:path";
|
|
12
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
-
const __dirname = dirname(__filename);
|
|
14
|
-
let _catalog = null;
|
|
15
|
-
function loadCatalog() {
|
|
16
|
-
if (_catalog)
|
|
17
|
-
return _catalog;
|
|
18
|
-
try {
|
|
19
|
-
const catalogPath = join(__dirname, "toolCatalog.json");
|
|
20
|
-
const raw = readFileSync(catalogPath, "utf-8");
|
|
21
|
-
_catalog = JSON.parse(raw);
|
|
22
|
-
return _catalog;
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
console.warn("[searchTools] Failed to load toolCatalog.json, using empty catalog");
|
|
26
|
-
_catalog = [];
|
|
27
|
-
return _catalog;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function cosineSimilarity(a, b) {
|
|
31
|
-
if (a.length === 0 || b.length === 0 || a.length !== b.length)
|
|
32
|
-
return 0;
|
|
33
|
-
let dot = 0;
|
|
34
|
-
for (let i = 0; i < a.length; i++) {
|
|
35
|
-
dot += a[i] * b[i];
|
|
36
|
-
}
|
|
37
|
-
return dot;
|
|
38
|
-
}
|
|
39
|
-
function searchByKeyword(query, limit) {
|
|
40
|
-
const catalog = loadCatalog();
|
|
41
|
-
const normalizedQuery = query.toLowerCase();
|
|
42
|
-
// Extract words, keeping short ones that might be meaningful (e.g., "ux", "js")
|
|
43
|
-
const queryWords = normalizedQuery
|
|
44
|
-
.split(/[\s_-]+/)
|
|
45
|
-
.filter((w) => w.length >= 2);
|
|
46
|
-
// Also check for the full query as a phrase
|
|
47
|
-
const queryPhrases = [normalizedQuery];
|
|
48
|
-
// Common synonyms and related terms
|
|
49
|
-
const synonyms = {
|
|
50
|
-
error: ["console", "js", "javascript", "bug", "crash", "exception"],
|
|
51
|
-
page: ["pages", "route", "url", "path"],
|
|
52
|
-
user: ["users", "session", "visitor"],
|
|
53
|
-
click: ["clicks", "tap", "press", "rage"],
|
|
54
|
-
form: ["forms", "input", "field", "submit"],
|
|
55
|
-
flow: ["flows", "journey", "funnel", "navigation", "path"],
|
|
56
|
-
issue: ["issues", "problem", "bug", "friction"],
|
|
57
|
-
fix: ["fixes", "remediation", "repair", "resolve"],
|
|
58
|
-
compare: ["comparison", "diff", "versus", "cohort"],
|
|
59
|
-
health: ["score", "metrics", "ux"],
|
|
60
|
-
dead: ["unresponsive", "broken", "stuck"],
|
|
61
|
-
rage: ["angry", "frustrated", "frustration"],
|
|
62
|
-
};
|
|
63
|
-
// Expand query words with synonyms
|
|
64
|
-
const expandedWords = new Set(queryWords);
|
|
65
|
-
for (const word of queryWords) {
|
|
66
|
-
if (synonyms[word]) {
|
|
67
|
-
synonyms[word].forEach((syn) => expandedWords.add(syn));
|
|
68
|
-
}
|
|
69
|
-
// Also check if query word is a synonym value
|
|
70
|
-
for (const [key, values] of Object.entries(synonyms)) {
|
|
71
|
-
if (values.includes(word)) {
|
|
72
|
-
expandedWords.add(key);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return catalog
|
|
77
|
-
.map((tool) => {
|
|
78
|
-
const toolName = tool.name.toLowerCase().replace(/_/g, " ");
|
|
79
|
-
const toolNameParts = tool.name.toLowerCase().split("_");
|
|
80
|
-
const text = `${toolName} ${tool.description} ${tool.category}`.toLowerCase();
|
|
81
|
-
let score = 0;
|
|
82
|
-
// Phrase match in description (highest value)
|
|
83
|
-
for (const phrase of queryPhrases) {
|
|
84
|
-
if (phrase.length > 3 && text.includes(phrase))
|
|
85
|
-
score += 5;
|
|
86
|
-
}
|
|
87
|
-
// Word matches
|
|
88
|
-
for (const word of expandedWords) {
|
|
89
|
-
// Exact word in tool name parts (e.g., "page" matches "get_page_metrics")
|
|
90
|
-
if (toolNameParts.includes(word))
|
|
91
|
-
score += 4;
|
|
92
|
-
// Word appears in tool name
|
|
93
|
-
if (toolName.includes(word))
|
|
94
|
-
score += 3;
|
|
95
|
-
// Category match
|
|
96
|
-
if (tool.category.toLowerCase() === word)
|
|
97
|
-
score += 2;
|
|
98
|
-
// Word in description
|
|
99
|
-
if (text.includes(word))
|
|
100
|
-
score += 1;
|
|
101
|
-
}
|
|
102
|
-
return { tool, score };
|
|
103
|
-
})
|
|
104
|
-
.filter((s) => s.score > 0)
|
|
105
|
-
.sort((a, b) => b.score - a.score)
|
|
106
|
-
.slice(0, limit)
|
|
107
|
-
.map((s) => s.tool);
|
|
108
|
-
}
|
|
109
|
-
async function searchBySemantic(query, limit, queryEmbedding) {
|
|
110
|
-
const catalog = loadCatalog();
|
|
111
|
-
return catalog
|
|
112
|
-
.filter((t) => t.embedding && t.embedding.length > 0)
|
|
113
|
-
.map((tool) => ({
|
|
114
|
-
tool,
|
|
115
|
-
score: cosineSimilarity(queryEmbedding, tool.embedding),
|
|
116
|
-
}))
|
|
117
|
-
.sort((a, b) => b.score - a.score)
|
|
118
|
-
.slice(0, limit)
|
|
119
|
-
.map((s) => s.tool);
|
|
120
|
-
}
|
|
121
|
-
export async function searchTools(query, limit = 5, queryEmbedding) {
|
|
122
|
-
if (queryEmbedding && queryEmbedding.length > 0) {
|
|
123
|
-
return searchBySemantic(query, limit, queryEmbedding);
|
|
124
|
-
}
|
|
125
|
-
return searchByKeyword(query, limit);
|
|
126
|
-
}
|
|
127
|
-
export function getToolByName(name) {
|
|
128
|
-
return loadCatalog().find((t) => t.name === name);
|
|
129
|
-
}
|
|
130
|
-
export function getAllTools() {
|
|
131
|
-
return loadCatalog();
|
|
132
|
-
}
|
|
133
|
-
const DEFAULT_TOOL_LIMIT = 5;
|
|
134
|
-
const searchToolsSchema = z.object({
|
|
135
|
-
query: z
|
|
136
|
-
.string()
|
|
137
|
-
.describe("Natural language description of what data or analysis capability you need. " +
|
|
138
|
-
"Describe what you want to learn or investigate."),
|
|
139
|
-
limit: z
|
|
140
|
-
.number()
|
|
141
|
-
.optional()
|
|
142
|
-
.describe("Maximum number of tools to return (default 5)"),
|
|
143
|
-
});
|
|
144
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
145
|
-
export function registerSearchTools(server) {
|
|
146
|
-
server.registerTool("search_tools", {
|
|
147
|
-
description: "Discover analysis tools by describing what data or capability you need. " +
|
|
148
|
-
"You have access to 40+ specialized tools covering sessions, pages, behaviors, journeys, " +
|
|
149
|
-
"forms, errors, performance, cohorts, issues, and remediation. Keyword search matches your intent to " +
|
|
150
|
-
"relevant tools. Returns tool names, descriptions, categories, and parameters. " +
|
|
151
|
-
"Use call_tool to execute discovered tools.",
|
|
152
|
-
inputSchema: searchToolsSchema,
|
|
153
|
-
}, async ({ query, limit }) => {
|
|
154
|
-
const searchLimit = limit ?? DEFAULT_TOOL_LIMIT;
|
|
155
|
-
const matches = await searchTools(query, searchLimit);
|
|
156
|
-
if (matches.length === 0) {
|
|
157
|
-
return {
|
|
158
|
-
content: [
|
|
159
|
-
{
|
|
160
|
-
type: "text",
|
|
161
|
-
text: JSON.stringify({
|
|
162
|
-
tools: [],
|
|
163
|
-
message: "No matching tools found. Try rephrasing your query or use broader terms like 'page', 'session', 'issue', 'flow', or 'form'.",
|
|
164
|
-
}),
|
|
165
|
-
},
|
|
166
|
-
],
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
const tools = matches.map((tool) => ({
|
|
170
|
-
name: tool.name,
|
|
171
|
-
description: tool.description.length > 250
|
|
172
|
-
? tool.description.slice(0, 250) + "..."
|
|
173
|
-
: tool.description,
|
|
174
|
-
category: tool.category,
|
|
175
|
-
parameters: Object.entries(tool.parameters).map(([name, prop]) => ({
|
|
176
|
-
name,
|
|
177
|
-
type: prop.type,
|
|
178
|
-
required: prop.required ?? false,
|
|
179
|
-
description: prop.description,
|
|
180
|
-
})),
|
|
181
|
-
}));
|
|
182
|
-
return {
|
|
183
|
-
content: [
|
|
184
|
-
{
|
|
185
|
-
type: "text",
|
|
186
|
-
text: JSON.stringify({
|
|
187
|
-
tools,
|
|
188
|
-
usage: "Use call_tool with the tool name and arguments to execute. Example: call_tool({ tool_name: 'get_page_metrics', arguments: { page_path: '/checkout' } })",
|
|
189
|
-
}),
|
|
190
|
-
},
|
|
191
|
-
],
|
|
192
|
-
};
|
|
193
|
-
});
|
|
194
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* compare_cohorts tool
|
|
3
|
-
*
|
|
4
|
-
* Compare two user cohorts side by side.
|
|
5
|
-
*/
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
import { apiPost, isApiConfigured } from "../api/client.js";
|
|
8
|
-
const cohortFilterSchema = z.object({
|
|
9
|
-
device: z
|
|
10
|
-
.enum(["desktop", "tablet", "mobile"])
|
|
11
|
-
.optional()
|
|
12
|
-
.describe("Filter by device type"),
|
|
13
|
-
outcome: z
|
|
14
|
-
.enum(["COMPLETED", "STRUGGLED", "BLOCKED", "DISENGAGED"])
|
|
15
|
-
.optional()
|
|
16
|
-
.describe("Filter by session outcome"),
|
|
17
|
-
pattern: z.string().optional().describe("Filter by behavioral pattern"),
|
|
18
|
-
page_path: z.string().optional().describe("Filter by page path"),
|
|
19
|
-
has_friction: z
|
|
20
|
-
.boolean()
|
|
21
|
-
.optional()
|
|
22
|
-
.describe("Filter for sessions with friction"),
|
|
23
|
-
has_rage_clicks: z
|
|
24
|
-
.boolean()
|
|
25
|
-
.optional()
|
|
26
|
-
.describe("Filter for sessions with rage clicks"),
|
|
27
|
-
});
|
|
28
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
-
export function registerCompareCohorts(server) {
|
|
30
|
-
server.registerTool("compare_cohorts", {
|
|
31
|
-
description: "Compare two user cohorts side by side. Each cohort is defined by filter criteria: " +
|
|
32
|
-
"device (desktop/tablet/mobile), outcome (COMPLETED/STRUGGLED/BLOCKED/DISENGAGED), " +
|
|
33
|
-
"pattern, has_friction, has_rage_clicks, or page_path. " +
|
|
34
|
-
"Returns behavioral metrics for each cohort and statistically significant differences. " +
|
|
35
|
-
'Use this to answer "how do mobile users differ from desktop?", "what distinguishes ' +
|
|
36
|
-
'users who complete vs abandon?", or "do form users struggle more than non-form users?"',
|
|
37
|
-
inputSchema: z.object({
|
|
38
|
-
cohort_a: cohortFilterSchema.describe('Filters for cohort A, e.g. {"device": "mobile"} or {"outcome": "COMPLETED"}'),
|
|
39
|
-
cohort_b: cohortFilterSchema.describe('Filters for cohort B, e.g. {"device": "desktop"} or {"outcome": "BLOCKED"}'),
|
|
40
|
-
cohort_a_label: z
|
|
41
|
-
.string()
|
|
42
|
-
.optional()
|
|
43
|
-
.describe("Human label for cohort A"),
|
|
44
|
-
cohort_b_label: z
|
|
45
|
-
.string()
|
|
46
|
-
.optional()
|
|
47
|
-
.describe("Human label for cohort B"),
|
|
48
|
-
page_path: z
|
|
49
|
-
.string()
|
|
50
|
-
.optional()
|
|
51
|
-
.describe("Scope comparison to a specific page"),
|
|
52
|
-
date_from: z.string().optional().describe("Start date (ISO format)"),
|
|
53
|
-
date_to: z.string().optional().describe("End date (ISO format)"),
|
|
54
|
-
}),
|
|
55
|
-
}, async ({ cohort_a, cohort_b, cohort_a_label, cohort_b_label, page_path, date_from, date_to, }) => {
|
|
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 apiPost("/cohorts/compare", {
|
|
68
|
-
cohort_a,
|
|
69
|
-
cohort_b,
|
|
70
|
-
cohort_a_label,
|
|
71
|
-
cohort_b_label,
|
|
72
|
-
page_path,
|
|
73
|
-
date_from,
|
|
74
|
-
date_to,
|
|
75
|
-
});
|
|
76
|
-
if (error) {
|
|
77
|
-
return {
|
|
78
|
-
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
79
|
-
isError: true,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
83
|
-
});
|
|
84
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* compare_periods tool
|
|
3
|
-
*
|
|
4
|
-
* Compare metrics between two time periods for a page.
|
|
5
|
-
*/
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
import { apiPost, isApiConfigured } from "../api/client.js";
|
|
8
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
-
export function registerComparePeriods(server) {
|
|
10
|
-
server.registerTool("compare_periods", {
|
|
11
|
-
description: "Compare behavioral metrics between two time periods for a specific page. " +
|
|
12
|
-
"Returns metrics for each period and the deltas between them. " +
|
|
13
|
-
"Use this to measure the impact of changes or compare week-over-week performance.",
|
|
14
|
-
inputSchema: z.object({
|
|
15
|
-
page_path: z.string().describe("Page path to compare"),
|
|
16
|
-
period_a: z
|
|
17
|
-
.object({
|
|
18
|
-
from: z.string().describe("Start date for period A (ISO format)"),
|
|
19
|
-
to: z.string().describe("End date for period A (ISO format)"),
|
|
20
|
-
})
|
|
21
|
-
.describe("First time period"),
|
|
22
|
-
period_b: z
|
|
23
|
-
.object({
|
|
24
|
-
from: z.string().describe("Start date for period B (ISO format)"),
|
|
25
|
-
to: z.string().describe("End date for period B (ISO format)"),
|
|
26
|
-
})
|
|
27
|
-
.describe("Second time period"),
|
|
28
|
-
}),
|
|
29
|
-
}, async ({ page_path, period_a, period_b, }) => {
|
|
30
|
-
if (!isApiConfigured()) {
|
|
31
|
-
return {
|
|
32
|
-
content: [
|
|
33
|
-
{
|
|
34
|
-
type: "text",
|
|
35
|
-
text: JSON.stringify({ error: "API not configured" }),
|
|
36
|
-
},
|
|
37
|
-
],
|
|
38
|
-
isError: true,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
const { data, error } = await apiPost("/pages/compare-periods", {
|
|
42
|
-
page_path,
|
|
43
|
-
period_a,
|
|
44
|
-
period_b,
|
|
45
|
-
});
|
|
46
|
-
if (error) {
|
|
47
|
-
return {
|
|
48
|
-
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
49
|
-
isError: true,
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
53
|
-
});
|
|
54
|
-
}
|