@recapt/mcp 0.0.4-beta → 0.0.5-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/index.d.ts +12 -1
- package/dist/index.js +244 -245
- package/dist/tools/catalog/callTool.d.ts +22 -0
- package/dist/tools/catalog/callTool.js +92 -0
- package/dist/tools/catalog/index.d.ts +11 -0
- package/dist/tools/catalog/index.js +11 -0
- package/dist/tools/catalog/searchTools.d.ts +22 -0
- package/dist/tools/catalog/searchTools.js +149 -0
- package/package.json +6 -2
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
* MCP Server entry point.
|
|
4
4
|
*
|
|
5
5
|
* Exposes recapt behavioral intelligence as tools for AI coding agents.
|
|
6
|
-
*
|
|
6
|
+
* Uses a search_tools + call_tool pattern to minimize context while
|
|
7
|
+
* providing access to 40+ analysis tools.
|
|
8
|
+
*
|
|
9
|
+
* Exposed tools (10):
|
|
10
|
+
* - search_tools: Discover tools by intent
|
|
11
|
+
* - call_tool: Execute discovered tools
|
|
12
|
+
* - get_domains: List tracked domains
|
|
13
|
+
* - run_full_diagnostic: Self-healing entry point
|
|
14
|
+
* - triage_sessions: Find sessions needing attention
|
|
15
|
+
* - memory_save, memory_recall, memory_list, memory_delete, memory_clear
|
|
16
|
+
*
|
|
17
|
+
* Hidden tools (38): Accessible via call_tool after discovery with search_tools
|
|
7
18
|
*/
|
|
8
19
|
import "dotenv/config";
|
package/dist/index.js
CHANGED
|
@@ -3,62 +3,216 @@
|
|
|
3
3
|
* MCP Server entry point.
|
|
4
4
|
*
|
|
5
5
|
* Exposes recapt behavioral intelligence as tools for AI coding agents.
|
|
6
|
-
*
|
|
6
|
+
* Uses a search_tools + call_tool pattern to minimize context while
|
|
7
|
+
* providing access to 40+ analysis tools.
|
|
8
|
+
*
|
|
9
|
+
* Exposed tools (10):
|
|
10
|
+
* - search_tools: Discover tools by intent
|
|
11
|
+
* - call_tool: Execute discovered tools
|
|
12
|
+
* - get_domains: List tracked domains
|
|
13
|
+
* - run_full_diagnostic: Self-healing entry point
|
|
14
|
+
* - triage_sessions: Find sessions needing attention
|
|
15
|
+
* - memory_save, memory_recall, memory_list, memory_delete, memory_clear
|
|
16
|
+
*
|
|
17
|
+
* Hidden tools (38): Accessible via call_tool after discovery with search_tools
|
|
7
18
|
*/
|
|
8
19
|
import "dotenv/config";
|
|
9
20
|
// @ts-ignore - MCP SDK types are complex
|
|
10
21
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11
22
|
// @ts-ignore - MCP SDK types are complex
|
|
12
23
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
|
-
import { isApiConfigured, getApiUrl } from "./api/client.js";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
import { registerGetSessionDetails } from "./tools/getSessionDetails.js";
|
|
19
|
-
import { registerGetPageTrends } from "./tools/getPageTrends.js";
|
|
20
|
-
import { registerGetAnomalies } from "./tools/getAnomalies.js";
|
|
24
|
+
import { isApiConfigured, getApiUrl, apiGet, apiPost, apiPatch, apiDelete, } from "./api/client.js";
|
|
25
|
+
import { registerSearchTools, registerCallTool, registerToolHandler, } from "./tools/catalog/index.js";
|
|
26
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
27
|
+
// Exposed Tools - Registered directly with the MCP server
|
|
28
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
21
29
|
import { registerGetDomains } from "./tools/getDomains.js";
|
|
22
|
-
import { registerGetActionableIssues } from "./tools/getActionableIssues.js";
|
|
23
|
-
import { registerGetUxHealthReport } from "./tools/getUxHealthReport.js";
|
|
24
|
-
import { registerAnalyzeFlow } from "./tools/analyzeFlow.js";
|
|
25
|
-
import { registerAnalyzeFunnel } from "./tools/analyzeFunnel.js";
|
|
26
|
-
import { registerGetFlowFriction } from "./tools/getFlowFriction.js";
|
|
27
|
-
import { registerGetJourneyPatterns } from "./tools/getJourneyPatterns.js";
|
|
28
|
-
import { registerGetDeadClicks } from "./tools/getDeadClicks.js";
|
|
29
|
-
import { registerGetConsoleErrors } from "./tools/getConsoleErrors.js";
|
|
30
|
-
import { registerCompareCohorts } from "./tools/compareCohorts.js";
|
|
31
|
-
import { registerDetectRegressions } from "./tools/detectRegressions.js";
|
|
32
|
-
import { registerDiscoverPersonas } from "./tools/discoverPersonas.js";
|
|
33
|
-
import { registerGetFormFriction } from "./tools/getFormFriction.js";
|
|
34
|
-
import { registerScanSite } from "./tools/scanSite.js";
|
|
35
|
-
import { registerListPages } from "./tools/listPages.js";
|
|
36
|
-
import { registerComparePeriods } from "./tools/comparePeriods.js";
|
|
37
|
-
import { registerDetectDrift } from "./tools/detectDrift.js";
|
|
38
|
-
import { registerPredictOutcomes } from "./tools/predictOutcomes.js";
|
|
39
30
|
import { registerMemoryTools } from "./tools/memory.js";
|
|
40
|
-
import { registerListSessions } from "./tools/listSessions.js";
|
|
41
31
|
import { registerTriageSessions } from "./tools/triageSessions.js";
|
|
42
32
|
import { registerDiagnosticTools } from "./tools/diagnostic.js";
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
33
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
34
|
+
// Hidden Tool Handlers - Registered with call_tool registry
|
|
35
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
36
|
+
function createApiHandler(method, endpoint, buildParams) {
|
|
37
|
+
return async (args) => {
|
|
38
|
+
if (!isApiConfigured()) {
|
|
39
|
+
return {
|
|
40
|
+
content: [
|
|
41
|
+
{
|
|
42
|
+
type: "text",
|
|
43
|
+
text: JSON.stringify({ error: "API not configured" }),
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
isError: true,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const url = typeof endpoint === "function" ? endpoint(args) : endpoint;
|
|
50
|
+
const params = buildParams ? buildParams(args) : args;
|
|
51
|
+
let result;
|
|
52
|
+
switch (method) {
|
|
53
|
+
case "GET":
|
|
54
|
+
result = await apiGet(url, params);
|
|
55
|
+
break;
|
|
56
|
+
case "POST":
|
|
57
|
+
result = await apiPost(url, params);
|
|
58
|
+
break;
|
|
59
|
+
case "PATCH":
|
|
60
|
+
result = await apiPatch(url, params);
|
|
61
|
+
break;
|
|
62
|
+
case "DELETE":
|
|
63
|
+
result = await apiDelete(url);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
if (result.error) {
|
|
67
|
+
return {
|
|
68
|
+
content: [
|
|
69
|
+
{ type: "text", text: JSON.stringify({ error: result.error }) },
|
|
70
|
+
],
|
|
71
|
+
isError: true,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return { content: [{ type: "text", text: JSON.stringify(result.data) }] };
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function registerHiddenTools() {
|
|
78
|
+
// Page Analysis
|
|
79
|
+
registerToolHandler("get_page_metrics", createApiHandler("GET", "/pages"));
|
|
80
|
+
registerToolHandler("get_element_friction", createApiHandler("GET", "/elements"));
|
|
81
|
+
registerToolHandler("get_page_trends", createApiHandler("GET", "/trends"));
|
|
82
|
+
registerToolHandler("get_dead_clicks", createApiHandler("GET", "/dead-clicks"));
|
|
83
|
+
registerToolHandler("get_console_errors", createApiHandler("GET", "/console-errors"));
|
|
84
|
+
registerToolHandler("get_form_friction", createApiHandler("GET", "/forms/friction"));
|
|
85
|
+
registerToolHandler("list_pages", createApiHandler("GET", "/pages/list"));
|
|
86
|
+
registerToolHandler("scan_site", createApiHandler("GET", "/site/overview", (args) => ({
|
|
87
|
+
top_n: args.top_n ?? 10,
|
|
88
|
+
offset: args.offset ?? 0,
|
|
89
|
+
})));
|
|
90
|
+
registerToolHandler("get_ux_health_report", createApiHandler("GET", "/health-report"));
|
|
91
|
+
// Flow Analysis
|
|
92
|
+
registerToolHandler("analyze_flow", createApiHandler("POST", "/flows/analyze"));
|
|
93
|
+
registerToolHandler("analyze_funnel", createApiHandler("POST", "/flows/funnel"));
|
|
94
|
+
registerToolHandler("get_flow_friction", createApiHandler("GET", "/flow-friction"));
|
|
95
|
+
registerToolHandler("get_journey_patterns", createApiHandler("GET", "/flows/patterns"));
|
|
96
|
+
// Issue Detection
|
|
97
|
+
registerToolHandler("get_issues", createApiHandler("GET", "/issues"));
|
|
98
|
+
registerToolHandler("get_actionable_issues", createApiHandler("GET", "/actionable-issues"));
|
|
99
|
+
registerToolHandler("get_anomalies", createApiHandler("GET", "/anomalies"));
|
|
100
|
+
registerToolHandler("detect_regressions", createApiHandler("GET", "/regressions"));
|
|
101
|
+
registerToolHandler("detect_drift", createApiHandler("GET", "/drift"));
|
|
102
|
+
// Comparison & Segmentation
|
|
103
|
+
registerToolHandler("compare_cohorts", createApiHandler("POST", "/cohorts/compare"));
|
|
104
|
+
registerToolHandler("compare_periods", createApiHandler("POST", "/pages/compare-periods"));
|
|
105
|
+
registerToolHandler("discover_personas", createApiHandler("GET", "/personas"));
|
|
106
|
+
// Session Analysis
|
|
107
|
+
registerToolHandler("search_sessions", createApiHandler("POST", "/sessions/search"));
|
|
108
|
+
registerToolHandler("list_sessions", createApiHandler("GET", "/sessions"));
|
|
109
|
+
registerToolHandler("get_session_details", async (args) => {
|
|
110
|
+
if (!isApiConfigured()) {
|
|
111
|
+
return {
|
|
112
|
+
content: [
|
|
113
|
+
{
|
|
114
|
+
type: "text",
|
|
115
|
+
text: JSON.stringify({ error: "API not configured" }),
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
isError: true,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const { session_id, session_ids } = args;
|
|
122
|
+
if (!session_id && (!session_ids || session_ids.length === 0)) {
|
|
123
|
+
return {
|
|
124
|
+
content: [
|
|
125
|
+
{
|
|
126
|
+
type: "text",
|
|
127
|
+
text: JSON.stringify({
|
|
128
|
+
error: "Either session_id or session_ids must be provided",
|
|
129
|
+
}),
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
isError: true,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (session_id && !session_ids) {
|
|
136
|
+
const { data, error } = await apiGet(`/sessions/${session_id}`);
|
|
137
|
+
if (error) {
|
|
138
|
+
return {
|
|
139
|
+
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
140
|
+
isError: true,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
144
|
+
}
|
|
145
|
+
const ids = session_ids ?? [session_id];
|
|
146
|
+
const { data, error } = await apiPost("/sessions/details", {
|
|
147
|
+
session_ids: ids,
|
|
148
|
+
});
|
|
149
|
+
if (error) {
|
|
150
|
+
return {
|
|
151
|
+
content: [{ type: "text", text: JSON.stringify({ error }) }],
|
|
152
|
+
isError: true,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
156
|
+
});
|
|
157
|
+
registerToolHandler("predict_outcomes", createApiHandler("POST", "/predict"));
|
|
158
|
+
// Diagnostic (investigate_issue and validate_issue - run_full_diagnostic is exposed)
|
|
159
|
+
registerToolHandler("investigate_issue", createApiHandler("GET", (args) => `/issues/${args.issue_id}/investigate`, (args) => ({ days: args.days ?? 7 })));
|
|
160
|
+
registerToolHandler("validate_issue", createApiHandler("POST", (args) => `/issues/${args.issue_id}/validate`, (args) => ({ lookback_days: args.lookback_days ?? 3 })));
|
|
161
|
+
// Triage
|
|
162
|
+
registerToolHandler("dismiss_issue", createApiHandler("POST", (args) => `/issues/${args.issue_id}/dismiss`, (args) => ({
|
|
163
|
+
reason: args.reason,
|
|
164
|
+
explanation: args.explanation,
|
|
165
|
+
create_knowledge: args.create_knowledge ?? true,
|
|
166
|
+
})));
|
|
167
|
+
registerToolHandler("mark_intended_behavior", createApiHandler("POST", (args) => `/issues/${args.issue_id}/mark-intended`, (args) => ({ explanation: args.explanation })));
|
|
168
|
+
registerToolHandler("get_issue_history", createApiHandler("GET", (args) => `/issues/${args.issue_id}/history`));
|
|
169
|
+
// Remediation
|
|
170
|
+
registerToolHandler("propose_fix", createApiHandler("POST", "/remediations"));
|
|
171
|
+
registerToolHandler("list_pending_fixes", createApiHandler("GET", "/remediations/pending"));
|
|
172
|
+
registerToolHandler("list_remediations", createApiHandler("GET", "/remediations"));
|
|
173
|
+
registerToolHandler("confirm_deployment", createApiHandler("PATCH", (args) => `/remediations/${args.remediation_id}/deploy`, () => ({})));
|
|
174
|
+
registerToolHandler("evaluate_fix", createApiHandler("POST", (args) => `/remediations/${args.remediation_id}/evaluate`, (args) => ({ min_hours: args.min_hours ?? 24 })));
|
|
175
|
+
registerToolHandler("get_fix_history", createApiHandler("GET", (args) => `/remediations/history/${args.issue_id}`));
|
|
176
|
+
// Knowledge
|
|
177
|
+
registerToolHandler("get_site_knowledge", createApiHandler("GET", "/knowledge"));
|
|
178
|
+
registerToolHandler("add_site_knowledge", createApiHandler("POST", "/knowledge", (args) => ({
|
|
179
|
+
...args,
|
|
180
|
+
source: "agent",
|
|
181
|
+
})));
|
|
182
|
+
registerToolHandler("delete_site_knowledge", createApiHandler("DELETE", (args) => `/knowledge/${args.knowledge_id}`));
|
|
183
|
+
registerToolHandler("get_similar_fixes", createApiHandler("GET", "/knowledge/similar-fixes"));
|
|
184
|
+
}
|
|
185
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
186
|
+
// Server Instructions
|
|
187
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
188
|
+
const SERVER_INSTRUCTIONS = `# Recapt Behavioral Intelligence
|
|
59
189
|
|
|
60
190
|
This server provides behavioral data from session recordings. Use these tools to understand user behavior, identify UX issues, and find friction points.
|
|
61
191
|
|
|
192
|
+
## Tool Discovery Pattern
|
|
193
|
+
|
|
194
|
+
You have access to 40+ analysis tools. Most are hidden to reduce context size. Use this pattern:
|
|
195
|
+
|
|
196
|
+
1. **search_tools(query)** — Describe what you need in natural language
|
|
197
|
+
2. **call_tool(tool_name, arguments)** — Execute the discovered tool
|
|
198
|
+
|
|
199
|
+
Example:
|
|
200
|
+
\`\`\`
|
|
201
|
+
search_tools({ query: "analyze user navigation between pages" })
|
|
202
|
+
→ Returns: analyze_flow, analyze_funnel, get_journey_patterns...
|
|
203
|
+
|
|
204
|
+
call_tool({ tool_name: "analyze_flow", arguments: { start_page: "/pricing", end_page: "/checkout" } })
|
|
205
|
+
→ Returns: flow analysis with paths, bottlenecks, friction scores
|
|
206
|
+
\`\`\`
|
|
207
|
+
|
|
208
|
+
## Always-Available Tools
|
|
209
|
+
|
|
210
|
+
These tools are always visible (no search needed):
|
|
211
|
+
- **get_domains** — List tracked domains (good starting point)
|
|
212
|
+
- **run_full_diagnostic** — Comprehensive site analysis with prioritized issues
|
|
213
|
+
- **triage_sessions** — Find sessions needing attention
|
|
214
|
+
- **memory_*** — Store/retrieve intermediate results
|
|
215
|
+
|
|
62
216
|
## Metric Interpretation
|
|
63
217
|
|
|
64
218
|
All behavioral scores are 0-1 where higher = more of that signal:
|
|
@@ -69,232 +223,77 @@ All behavioral scores are 0-1 where higher = more of that signal:
|
|
|
69
223
|
- **drop_off_rate** > 0.3: Major leak point in a flow
|
|
70
224
|
- **spike_ratio** > 2: Critical frustration spike, likely recent regression
|
|
71
225
|
|
|
72
|
-
##
|
|
226
|
+
## Common Workflows
|
|
73
227
|
|
|
74
228
|
### Starting point - understand the landscape
|
|
75
|
-
1.
|
|
76
|
-
2.
|
|
77
|
-
3.
|
|
229
|
+
1. \`get_domains\` to see tracked domains
|
|
230
|
+
2. \`run_full_diagnostic\` for comprehensive overview
|
|
231
|
+
3. Or search: "site overview" → scan_site, list_pages, get_ux_health_report
|
|
78
232
|
|
|
79
233
|
### Diagnosing a problematic page
|
|
80
|
-
|
|
81
|
-
2. If frustration high → \`get_element_friction\` to find specific problematic elements
|
|
82
|
-
3. If confusion high → \`get_dead_clicks\` to find misleading UI elements users click expecting action
|
|
83
|
-
4. Check \`get_console_errors\` for JS errors that may cause unresponsiveness
|
|
84
|
-
5. Use \`get_form_friction\` if the page has forms to find problematic fields
|
|
234
|
+
Search: "page analysis" or "element friction" → get_page_metrics, get_element_friction, get_dead_clicks, get_console_errors, get_form_friction
|
|
85
235
|
|
|
86
236
|
### Understanding user flows
|
|
87
|
-
|
|
88
|
-
2. \`analyze_flow\` between specific pages to see paths, bottlenecks, and friction
|
|
89
|
-
3. \`analyze_funnel\` for conversion analysis through defined steps (cart → checkout → payment)
|
|
90
|
-
4. \`get_flow_friction\` to automatically discover high-friction flows
|
|
237
|
+
Search: "user navigation" or "funnel" → analyze_flow, analyze_funnel, get_journey_patterns, get_flow_friction
|
|
91
238
|
|
|
92
239
|
### Finding issues to fix
|
|
93
|
-
1. \`
|
|
94
|
-
2.
|
|
95
|
-
3. \`detect_regressions\` to find recent degradations (compare last 24h vs baseline)
|
|
96
|
-
4. \`get_anomalies\` for unusual sessions and frustration spikes
|
|
240
|
+
1. \`run_full_diagnostic\` (always available)
|
|
241
|
+
2. Or search: "issues" → get_issues, get_actionable_issues, get_anomalies, detect_regressions
|
|
97
242
|
|
|
98
243
|
### Comparing segments
|
|
99
|
-
|
|
100
|
-
2. \`compare_periods\` to measure impact of changes (before vs after deployment)
|
|
101
|
-
3. \`discover_personas\` to find behavioral user segments
|
|
244
|
+
Search: "compare users" or "cohorts" → compare_cohorts, compare_periods, discover_personas
|
|
102
245
|
|
|
103
246
|
### Deep-diving a session
|
|
104
|
-
|
|
105
|
-
2. \`list_sessions\` to browse by domain, status, or device
|
|
106
|
-
3. \`get_session_details\` for behavioral timeline of a specific session
|
|
107
|
-
|
|
108
|
-
### Monitoring over time
|
|
109
|
-
1. \`get_page_trends\` for daily behavioral trends on a page
|
|
110
|
-
2. \`detect_drift\` for gradual behavioral changes over weeks
|
|
111
|
-
3. \`detect_regressions\` for sudden changes (deployment impact)
|
|
247
|
+
Search: "session details" → search_sessions, list_sessions, get_session_details, predict_outcomes
|
|
112
248
|
|
|
113
249
|
## Reasoning Tips
|
|
114
250
|
|
|
115
|
-
- High rage clicks on body/root elements often indicate JS errors
|
|
116
|
-
- Backtrack hotspots
|
|
117
|
-
-
|
|
118
|
-
-
|
|
119
|
-
- When frustration spikes recently, use \`detect_regressions\` to correlate with deployments
|
|
120
|
-
- Low confidence + high confusion = users don't understand what to do next
|
|
121
|
-
- High frustration + low confusion = users know what to do but can't (broken UI, errors)
|
|
251
|
+
- High rage clicks on body/root elements often indicate JS errors - search "console errors"
|
|
252
|
+
- Backtrack hotspots suggest confusion on subsequent pages
|
|
253
|
+
- Low confidence + high confusion = users don't understand what to do
|
|
254
|
+
- High frustration + low confusion = users know what to do but can't (broken UI)
|
|
122
255
|
- Use \`memory_save\` to store intermediate results when doing multi-step analysis
|
|
123
256
|
|
|
124
|
-
## Available Tools
|
|
125
|
-
|
|
126
|
-
### Site Overview
|
|
127
|
-
- \`get_domains\`: List configured domains
|
|
128
|
-
- \`scan_site\`: Quick health scan of top pages
|
|
129
|
-
- \`list_pages\`: List all tracked pages with metrics
|
|
130
|
-
- \`get_ux_health_report\`: Comprehensive health report
|
|
131
|
-
|
|
132
|
-
### Page Analysis
|
|
133
|
-
- \`get_page_metrics\`: Behavioral metrics for a page
|
|
134
|
-
- \`get_element_friction\`: Per-element friction data
|
|
135
|
-
- \`get_page_trends\`: Daily trends over time
|
|
136
|
-
- \`get_dead_clicks\`: Unresponsive elements users click
|
|
137
|
-
- \`get_console_errors\`: JavaScript errors
|
|
138
|
-
- \`get_form_friction\`: Form field friction analysis
|
|
139
|
-
|
|
140
|
-
### Flow Analysis
|
|
141
|
-
- \`analyze_flow\`: Navigation between specific pages
|
|
142
|
-
- \`analyze_funnel\`: Conversion through page sequence
|
|
143
|
-
- \`get_flow_friction\`: Discover high-friction flows
|
|
144
|
-
- \`get_journey_patterns\`: Navigation patterns and hotspots
|
|
145
|
-
|
|
146
|
-
### Issue Detection
|
|
147
|
-
- \`get_issues\`: Auto-detected UX issues
|
|
148
|
-
- \`get_actionable_issues\`: Issues with element context
|
|
149
|
-
- \`get_anomalies\`: Unusual sessions and spikes
|
|
150
|
-
- \`detect_regressions\`: Recent degradations
|
|
151
|
-
|
|
152
|
-
### Comparison & Segmentation
|
|
153
|
-
- \`compare_cohorts\`: Compare user segments
|
|
154
|
-
- \`compare_periods\`: Compare time periods
|
|
155
|
-
- \`discover_personas\`: Behavioral clustering
|
|
156
|
-
- \`detect_drift\`: Long-term behavioral changes
|
|
157
|
-
|
|
158
|
-
### Session Analysis
|
|
159
|
-
- \`search_sessions\`: Natural language session search
|
|
160
|
-
- \`list_sessions\`: Filter sessions by criteria
|
|
161
|
-
- \`get_session_details\`: Session behavioral timeline
|
|
162
|
-
- \`predict_outcomes\`: Predict session outcomes
|
|
163
|
-
- \`triage_sessions\`: Auto-triage sessions with compromised UX
|
|
164
|
-
|
|
165
|
-
### Session Triage
|
|
166
|
-
Use \`triage_sessions\` to automatically identify sessions that need attention:
|
|
167
|
-
- Surfaces sessions with user comments (direct feedback)
|
|
168
|
-
- Flags high-frustration sessions with rage clicks
|
|
169
|
-
- Includes console errors as evidence
|
|
170
|
-
- Returns replay URLs for quick investigation
|
|
171
|
-
|
|
172
|
-
Severity levels:
|
|
173
|
-
- **critical**: triage_score >= 0.7 OR has comment + high frustration
|
|
174
|
-
- **high**: triage_score >= 0.5
|
|
175
|
-
- **medium**: triage_score >= 0.3
|
|
176
|
-
- **low**: triage_score < 0.3
|
|
177
|
-
|
|
178
|
-
When triaging, prioritize:
|
|
179
|
-
1. Sessions with user comments (explicit feedback is gold)
|
|
180
|
-
2. Critical severity with rage clicks (users actively struggling)
|
|
181
|
-
3. Sessions with console errors + high frustration (likely bugs)
|
|
182
|
-
|
|
183
|
-
### Working Memory
|
|
184
|
-
- \`memory_save\`, \`memory_recall\`, \`memory_list\`, \`memory_delete\`, \`memory_clear\`: Store and retrieve intermediate results
|
|
185
|
-
|
|
186
257
|
## Self-Healing Workflow
|
|
187
258
|
|
|
188
|
-
When asked to "fix all issues"
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
- Show user all pending fixes with \`list_pending_fixes\`
|
|
214
|
-
- When user confirms "I have deployed this fix", call \`confirm_deployment\`
|
|
215
|
-
- Explain that evaluation will happen after 24-48 hours of data collection
|
|
216
|
-
|
|
217
|
-
### 5. Evaluate Results
|
|
218
|
-
- After deployment window (24-48h), call \`evaluate_fix\`
|
|
219
|
-
- If outcome is "success": celebrate, the issue will be auto-resolved
|
|
220
|
-
- If outcome is "partial": discuss with user whether to iterate
|
|
221
|
-
- If outcome is "failed": analyze why, propose alternative approach
|
|
222
|
-
- Store learnings with \`add_site_knowledge\` for future reference
|
|
223
|
-
|
|
224
|
-
### Evaluation Thresholds
|
|
225
|
-
- **Success**: frustration decreased >15% OR health score increased >10 points
|
|
226
|
-
- **Partial**: frustration decreased 5-15% OR health score increased 3-10 points
|
|
227
|
-
- **Failed**: no improvement or regression
|
|
228
|
-
|
|
229
|
-
### Asking for Confirmation
|
|
230
|
-
Always ask user before:
|
|
231
|
-
- Dismissing an issue as false positive
|
|
232
|
-
- Marking behavior as intended
|
|
233
|
-
- When confidence in fix is <0.7
|
|
234
|
-
- Before evaluating (ensure they've actually deployed)
|
|
235
|
-
|
|
236
|
-
### Self-Healing Tools
|
|
237
|
-
|
|
238
|
-
#### Diagnostic
|
|
239
|
-
- \`run_full_diagnostic\`: Comprehensive site analysis with prioritized issues
|
|
240
|
-
- \`investigate_issue\`: Deep-dive into specific issue with sessions and errors
|
|
241
|
-
- \`validate_issue\`: Check if issue is still occurring (not stale)
|
|
242
|
-
|
|
243
|
-
#### Triage
|
|
244
|
-
- \`dismiss_issue\`: Mark issue as false positive with reason
|
|
245
|
-
- \`mark_intended_behavior\`: Flag behavior as by design
|
|
246
|
-
- \`get_issue_history\`: View past occurrences and fix attempts
|
|
247
|
-
|
|
248
|
-
#### Remediation
|
|
249
|
-
- \`propose_fix\`: Create remediation record with baseline metrics
|
|
250
|
-
- \`list_pending_fixes\`: Show fixes awaiting deployment
|
|
251
|
-
- \`list_remediations\`: Browse all remediation records
|
|
252
|
-
- \`confirm_deployment\`: Mark fix as deployed, start evaluation timer
|
|
253
|
-
- \`evaluate_fix\`: Compare post-deploy metrics to baseline
|
|
254
|
-
- \`get_fix_history\`: View all fix attempts for an issue
|
|
255
|
-
|
|
256
|
-
#### Knowledge
|
|
257
|
-
- \`get_site_knowledge\`: Retrieve learned patterns and false positives
|
|
258
|
-
- \`add_site_knowledge\`: Store new learning for future reference
|
|
259
|
-
- \`delete_site_knowledge\`: Remove outdated knowledge
|
|
260
|
-
- \`get_similar_fixes\`: Find past fixes for similar issues`,
|
|
259
|
+
When asked to "fix all issues" or "heal the site":
|
|
260
|
+
|
|
261
|
+
1. **Diagnose**: \`run_full_diagnostic\` (always available)
|
|
262
|
+
2. **Investigate**: search "investigate issue" → investigate_issue, validate_issue
|
|
263
|
+
3. **Triage**: search "dismiss issue" → dismiss_issue, mark_intended_behavior
|
|
264
|
+
(you should wait here for user input, recommend continuing the workflow if the user is happy)
|
|
265
|
+
4. **Fix**: search "propose fix" → propose_fix, get_similar_fixes, get_fix_history
|
|
266
|
+
5. **Track**: search "deployment" → confirm_deployment, evaluate_fix, list_pending_fixes
|
|
267
|
+
6. **Learn**: search "site knowledge" → get_site_knowledge, add_site_knowledge`;
|
|
268
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
269
|
+
// Main
|
|
270
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
271
|
+
async function main() {
|
|
272
|
+
console.error("[MCP] Starting recapt behavioral intelligence server...");
|
|
273
|
+
if (!isApiConfigured()) {
|
|
274
|
+
console.error("[MCP] ERROR: RECAPT_SECRET_KEY environment variable is required");
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
console.error(`[MCP] API URL: ${getApiUrl()}`);
|
|
278
|
+
const server = new McpServer({
|
|
279
|
+
name: "recapt",
|
|
280
|
+
version: "1.0.0",
|
|
281
|
+
}, {
|
|
282
|
+
capabilities: { logging: {} },
|
|
283
|
+
instructions: SERVER_INSTRUCTIONS,
|
|
261
284
|
});
|
|
285
|
+
// Register hidden tool handlers first (for call_tool to use)
|
|
286
|
+
registerHiddenTools();
|
|
287
|
+
// Register exposed tools
|
|
288
|
+
registerSearchTools(server);
|
|
289
|
+
registerCallTool(server);
|
|
262
290
|
registerGetDomains(server);
|
|
263
|
-
|
|
264
|
-
registerGetElementFriction(server);
|
|
265
|
-
registerSearchSessions(server);
|
|
266
|
-
registerGetIssues(server);
|
|
267
|
-
registerGetSessionDetails(server);
|
|
268
|
-
registerGetPageTrends(server);
|
|
269
|
-
registerGetAnomalies(server);
|
|
270
|
-
registerGetActionableIssues(server);
|
|
271
|
-
registerGetUxHealthReport(server);
|
|
272
|
-
registerAnalyzeFlow(server);
|
|
273
|
-
registerAnalyzeFunnel(server);
|
|
274
|
-
registerGetFlowFriction(server);
|
|
275
|
-
registerGetJourneyPatterns(server);
|
|
276
|
-
registerGetDeadClicks(server);
|
|
277
|
-
registerGetConsoleErrors(server);
|
|
278
|
-
registerCompareCohorts(server);
|
|
279
|
-
registerDetectRegressions(server);
|
|
280
|
-
registerDiscoverPersonas(server);
|
|
281
|
-
registerGetFormFriction(server);
|
|
282
|
-
registerScanSite(server);
|
|
283
|
-
registerListPages(server);
|
|
284
|
-
registerComparePeriods(server);
|
|
285
|
-
registerDetectDrift(server);
|
|
286
|
-
registerPredictOutcomes(server);
|
|
287
|
-
registerMemoryTools(server);
|
|
288
|
-
registerListSessions(server);
|
|
291
|
+
registerDiagnosticTools(server); // Includes run_full_diagnostic
|
|
289
292
|
registerTriageSessions(server);
|
|
290
|
-
|
|
291
|
-
registerDiagnosticTools(server);
|
|
292
|
-
registerTriageTools(server);
|
|
293
|
-
registerRemediationTools(server);
|
|
294
|
-
registerKnowledgeTools(server);
|
|
293
|
+
registerMemoryTools(server);
|
|
295
294
|
const transport = new StdioServerTransport();
|
|
296
295
|
await server.connect(transport);
|
|
297
|
-
console.error("[MCP] Server running on stdio");
|
|
296
|
+
console.error("[MCP] Server running on stdio (10 exposed tools, 38 hidden)");
|
|
298
297
|
}
|
|
299
298
|
main().catch((err) => {
|
|
300
299
|
console.error("[MCP] Fatal error:", err);
|
|
@@ -0,0 +1,22 @@
|
|
|
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;
|
|
@@ -0,0 +1,92 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
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";
|
|
@@ -0,0 +1,11 @@
|
|
|
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";
|
|
@@ -0,0 +1,22 @@
|
|
|
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;
|
|
@@ -0,0 +1,149 @@
|
|
|
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 queryWords = query
|
|
42
|
+
.toLowerCase()
|
|
43
|
+
.split(/\s+/)
|
|
44
|
+
.filter((w) => w.length > 2);
|
|
45
|
+
return catalog
|
|
46
|
+
.map((tool) => {
|
|
47
|
+
const text = `${tool.name} ${tool.description} ${tool.category}`.toLowerCase();
|
|
48
|
+
let score = 0;
|
|
49
|
+
for (const word of queryWords) {
|
|
50
|
+
if (text.includes(word))
|
|
51
|
+
score += 1;
|
|
52
|
+
if (tool.name.toLowerCase().includes(word))
|
|
53
|
+
score += 2;
|
|
54
|
+
if (tool.category.toLowerCase() === word)
|
|
55
|
+
score += 1;
|
|
56
|
+
}
|
|
57
|
+
return { tool, score };
|
|
58
|
+
})
|
|
59
|
+
.filter((s) => s.score > 0)
|
|
60
|
+
.sort((a, b) => b.score - a.score)
|
|
61
|
+
.slice(0, limit)
|
|
62
|
+
.map((s) => s.tool);
|
|
63
|
+
}
|
|
64
|
+
async function searchBySemantic(query, limit, queryEmbedding) {
|
|
65
|
+
const catalog = loadCatalog();
|
|
66
|
+
return catalog
|
|
67
|
+
.filter((t) => t.embedding && t.embedding.length > 0)
|
|
68
|
+
.map((tool) => ({
|
|
69
|
+
tool,
|
|
70
|
+
score: cosineSimilarity(queryEmbedding, tool.embedding),
|
|
71
|
+
}))
|
|
72
|
+
.sort((a, b) => b.score - a.score)
|
|
73
|
+
.slice(0, limit)
|
|
74
|
+
.map((s) => s.tool);
|
|
75
|
+
}
|
|
76
|
+
export async function searchTools(query, limit = 5, queryEmbedding) {
|
|
77
|
+
if (queryEmbedding && queryEmbedding.length > 0) {
|
|
78
|
+
return searchBySemantic(query, limit, queryEmbedding);
|
|
79
|
+
}
|
|
80
|
+
return searchByKeyword(query, limit);
|
|
81
|
+
}
|
|
82
|
+
export function getToolByName(name) {
|
|
83
|
+
return loadCatalog().find((t) => t.name === name);
|
|
84
|
+
}
|
|
85
|
+
export function getAllTools() {
|
|
86
|
+
return loadCatalog();
|
|
87
|
+
}
|
|
88
|
+
const DEFAULT_TOOL_LIMIT = 5;
|
|
89
|
+
const searchToolsSchema = z.object({
|
|
90
|
+
query: z
|
|
91
|
+
.string()
|
|
92
|
+
.describe("Natural language description of what data or analysis capability you need. " +
|
|
93
|
+
"Describe what you want to learn or investigate."),
|
|
94
|
+
limit: z
|
|
95
|
+
.number()
|
|
96
|
+
.optional()
|
|
97
|
+
.describe("Maximum number of tools to return (default 5)"),
|
|
98
|
+
});
|
|
99
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
100
|
+
export function registerSearchTools(server) {
|
|
101
|
+
server.registerTool("search_tools", {
|
|
102
|
+
description: "Discover analysis tools by describing what data or capability you need. " +
|
|
103
|
+
"You have access to 40+ specialized tools covering sessions, pages, behaviors, journeys, " +
|
|
104
|
+
"forms, errors, performance, cohorts, issues, and remediation. Keyword search matches your intent to " +
|
|
105
|
+
"relevant tools. Returns tool names, descriptions, categories, and parameters. " +
|
|
106
|
+
"Use call_tool to execute discovered tools.",
|
|
107
|
+
inputSchema: searchToolsSchema,
|
|
108
|
+
}, async ({ query, limit }) => {
|
|
109
|
+
const searchLimit = limit ?? DEFAULT_TOOL_LIMIT;
|
|
110
|
+
const matches = await searchTools(query, searchLimit);
|
|
111
|
+
if (matches.length === 0) {
|
|
112
|
+
return {
|
|
113
|
+
content: [
|
|
114
|
+
{
|
|
115
|
+
type: "text",
|
|
116
|
+
text: JSON.stringify({
|
|
117
|
+
tools: [],
|
|
118
|
+
message: "No matching tools found. Try rephrasing your query or use broader terms like 'page', 'session', 'issue', 'flow', or 'form'.",
|
|
119
|
+
}),
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const tools = matches.map((tool) => ({
|
|
125
|
+
name: tool.name,
|
|
126
|
+
description: tool.description.length > 250
|
|
127
|
+
? tool.description.slice(0, 250) + "..."
|
|
128
|
+
: tool.description,
|
|
129
|
+
category: tool.category,
|
|
130
|
+
parameters: Object.entries(tool.parameters).map(([name, prop]) => ({
|
|
131
|
+
name,
|
|
132
|
+
type: prop.type,
|
|
133
|
+
required: prop.required ?? false,
|
|
134
|
+
description: prop.description,
|
|
135
|
+
})),
|
|
136
|
+
}));
|
|
137
|
+
return {
|
|
138
|
+
content: [
|
|
139
|
+
{
|
|
140
|
+
type: "text",
|
|
141
|
+
text: JSON.stringify({
|
|
142
|
+
tools,
|
|
143
|
+
usage: "Use call_tool with the tool name and arguments to execute. Example: call_tool({ tool_name: 'get_page_metrics', arguments: { page_path: '/checkout' } })",
|
|
144
|
+
}),
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@recapt/mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5-beta",
|
|
4
4
|
"description": "MCP exposing recapt behavioral intelligence to AI coding agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"dev": "tsx --watch src/index.ts",
|
|
12
12
|
"start": "node dist/index.js",
|
|
13
13
|
"prettify": "prettier --write .",
|
|
14
|
-
"type-check": "tsc --noEmit"
|
|
14
|
+
"type-check": "tsc --noEmit",
|
|
15
|
+
"generate-catalog": "tsx scripts/generateCatalog.ts"
|
|
15
16
|
},
|
|
16
17
|
"bin": {
|
|
17
18
|
"recapt-mcp": "./dist/index.js"
|
|
@@ -26,6 +27,9 @@
|
|
|
26
27
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
27
28
|
"zod": "^3.24.0"
|
|
28
29
|
},
|
|
30
|
+
"optionalDependencies": {
|
|
31
|
+
"@xenova/transformers": "^2.17.0"
|
|
32
|
+
},
|
|
29
33
|
"peerDependencies": {
|
|
30
34
|
"@types/node": "^24.3.1",
|
|
31
35
|
"dotenv": "^17.2.2",
|