@recapt/mcp 0.0.29 → 0.0.31
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.js +180 -78
- package/dist/tools/catalog/anthropicToolCatalog.json +2 -2
- package/dist/tools/catalog/toolCatalog.json +69 -25864
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -18,6 +18,9 @@ function getSecretKey() {
|
|
|
18
18
|
function getOrganizationId() {
|
|
19
19
|
return _config.organizationId || process.env.MCP_ORGANIZATION_ID;
|
|
20
20
|
}
|
|
21
|
+
function getDomainFilter() {
|
|
22
|
+
return _config.domainFilter;
|
|
23
|
+
}
|
|
21
24
|
function isApiConfigured() {
|
|
22
25
|
return !!getSecretKey();
|
|
23
26
|
}
|
|
@@ -36,6 +39,17 @@ async function request(options) {
|
|
|
36
39
|
}
|
|
37
40
|
}
|
|
38
41
|
}
|
|
42
|
+
const domainFilter = getDomainFilter();
|
|
43
|
+
const skipDomainFilter = ["/domains", "/upgrade-options", "/remediations"];
|
|
44
|
+
const shouldFilter = domainFilter?.length && !skipDomainFilter.some((p) => path.startsWith(p));
|
|
45
|
+
let requestBody = body;
|
|
46
|
+
if (shouldFilter) {
|
|
47
|
+
if (method === "GET") {
|
|
48
|
+
url.searchParams.set("domain", domainFilter.join(","));
|
|
49
|
+
} else if (requestBody) {
|
|
50
|
+
requestBody = { ...requestBody, domain: domainFilter.join(",") };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
39
53
|
const controller = new AbortController();
|
|
40
54
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
41
55
|
const headers = {
|
|
@@ -50,7 +64,7 @@ async function request(options) {
|
|
|
50
64
|
const res = await fetch(url.toString(), {
|
|
51
65
|
method,
|
|
52
66
|
headers,
|
|
53
|
-
body:
|
|
67
|
+
body: requestBody ? JSON.stringify(requestBody) : void 0,
|
|
54
68
|
signal: controller.signal
|
|
55
69
|
});
|
|
56
70
|
clearTimeout(timeout);
|
|
@@ -267,6 +281,156 @@ function registerAllToolHandlers() {
|
|
|
267
281
|
}
|
|
268
282
|
registerAllToolHandlers();
|
|
269
283
|
|
|
284
|
+
// ../../libraries/mcp-tools/dist/embedder.js
|
|
285
|
+
var embedder = null;
|
|
286
|
+
var loadingPromise = null;
|
|
287
|
+
var loadError = null;
|
|
288
|
+
async function getEmbedder() {
|
|
289
|
+
if (embedder)
|
|
290
|
+
return embedder;
|
|
291
|
+
if (loadError)
|
|
292
|
+
throw loadError;
|
|
293
|
+
if (!loadingPromise) {
|
|
294
|
+
loadingPromise = (async () => {
|
|
295
|
+
try {
|
|
296
|
+
const transformers = await import("@xenova/transformers");
|
|
297
|
+
if (transformers.env?.backends?.onnx?.wasm) {
|
|
298
|
+
transformers.env.backends.onnx.wasm.numThreads = 1;
|
|
299
|
+
}
|
|
300
|
+
const model = await transformers.pipeline("feature-extraction", "Xenova/all-MiniLM-L6-v2", { quantized: true });
|
|
301
|
+
embedder = model;
|
|
302
|
+
return model;
|
|
303
|
+
} catch (err) {
|
|
304
|
+
loadError = err instanceof Error ? err : new Error(String(err));
|
|
305
|
+
loadingPromise = null;
|
|
306
|
+
throw loadError;
|
|
307
|
+
}
|
|
308
|
+
})();
|
|
309
|
+
}
|
|
310
|
+
return loadingPromise;
|
|
311
|
+
}
|
|
312
|
+
async function embedText(text) {
|
|
313
|
+
const model = await getEmbedder();
|
|
314
|
+
const output = await model(text, { pooling: "mean", normalize: true });
|
|
315
|
+
return Array.from(output.data);
|
|
316
|
+
}
|
|
317
|
+
function cosineSimilarity(a, b) {
|
|
318
|
+
if (a.length === 0 || b.length === 0 || a.length !== b.length)
|
|
319
|
+
return 0;
|
|
320
|
+
let dot = 0;
|
|
321
|
+
for (let i = 0; i < a.length; i++) {
|
|
322
|
+
dot += a[i] * b[i];
|
|
323
|
+
}
|
|
324
|
+
return dot;
|
|
325
|
+
}
|
|
326
|
+
function preloadEmbedder() {
|
|
327
|
+
getEmbedder().catch(() => {
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ../../libraries/mcp-tools/dist/search.js
|
|
332
|
+
var _catalog = null;
|
|
333
|
+
var _catalogLoader = null;
|
|
334
|
+
function setCatalogLoader(loader) {
|
|
335
|
+
_catalogLoader = loader;
|
|
336
|
+
_catalog = null;
|
|
337
|
+
}
|
|
338
|
+
function loadCatalog() {
|
|
339
|
+
if (_catalog)
|
|
340
|
+
return _catalog;
|
|
341
|
+
if (_catalogLoader) {
|
|
342
|
+
_catalog = _catalogLoader();
|
|
343
|
+
return _catalog;
|
|
344
|
+
}
|
|
345
|
+
console.warn("[searchTools] No catalog loader set, using empty catalog");
|
|
346
|
+
_catalog = [];
|
|
347
|
+
return _catalog;
|
|
348
|
+
}
|
|
349
|
+
function searchByKeyword(query, limit) {
|
|
350
|
+
const catalog = loadCatalog();
|
|
351
|
+
const normalizedQuery = query.toLowerCase();
|
|
352
|
+
const queryWords = normalizedQuery.split(/[\s_-]+/).filter((w) => w.length >= 2);
|
|
353
|
+
const queryPhrases = [normalizedQuery];
|
|
354
|
+
const synonyms = {
|
|
355
|
+
error: ["console", "js", "javascript", "bug", "crash", "exception"],
|
|
356
|
+
page: ["pages", "route", "url", "path"],
|
|
357
|
+
user: ["users", "session", "visitor"],
|
|
358
|
+
click: ["clicks", "tap", "press", "rage"],
|
|
359
|
+
form: ["forms", "input", "field", "submit"],
|
|
360
|
+
flow: ["flows", "journey", "funnel", "navigation", "path"],
|
|
361
|
+
issue: ["issues", "problem", "bug", "friction"],
|
|
362
|
+
fix: ["fixes", "remediation", "repair", "resolve"],
|
|
363
|
+
compare: ["comparison", "diff", "versus", "cohort"],
|
|
364
|
+
health: ["score", "metrics", "ux"],
|
|
365
|
+
dead: ["unresponsive", "broken", "stuck"],
|
|
366
|
+
rage: ["angry", "frustrated", "frustration"],
|
|
367
|
+
git: ["branch", "file", "repository", "pr", "merge", "commit", "push"],
|
|
368
|
+
pr: ["merge", "request", "pull", "mr"],
|
|
369
|
+
branch: ["git", "create", "checkout"],
|
|
370
|
+
file: ["read", "write", "update", "content", "repository"],
|
|
371
|
+
repository: ["repo", "git", "github", "gitlab"],
|
|
372
|
+
status: ["state", "mark", "set", "change", "update"],
|
|
373
|
+
waiting: ["pending", "open", "pr"],
|
|
374
|
+
deployed: ["merged", "live", "production"],
|
|
375
|
+
dismissed: ["closed", "rejected", "cancelled"],
|
|
376
|
+
remediation: ["fix", "fixes", "repair", "patch", "solution"],
|
|
377
|
+
propose: ["create", "suggest", "recommend"],
|
|
378
|
+
update: ["change", "modify", "set", "mark", "edit"],
|
|
379
|
+
create: ["add", "new", "make", "propose"],
|
|
380
|
+
list: ["get", "fetch", "find", "show", "query"]
|
|
381
|
+
};
|
|
382
|
+
const expandedWords = new Set(queryWords);
|
|
383
|
+
for (const word of queryWords) {
|
|
384
|
+
if (synonyms[word]) {
|
|
385
|
+
synonyms[word].forEach((syn) => expandedWords.add(syn));
|
|
386
|
+
}
|
|
387
|
+
for (const [key, values] of Object.entries(synonyms)) {
|
|
388
|
+
if (values.includes(word)) {
|
|
389
|
+
expandedWords.add(key);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return catalog.map((tool) => {
|
|
394
|
+
const toolName = tool.name.toLowerCase().replace(/_/g, " ");
|
|
395
|
+
const toolNameParts = tool.name.toLowerCase().split("_");
|
|
396
|
+
const text = `${toolName} ${tool.description} ${tool.category}`.toLowerCase();
|
|
397
|
+
let score = 0;
|
|
398
|
+
for (const phrase of queryPhrases) {
|
|
399
|
+
if (phrase.length > 3 && text.includes(phrase))
|
|
400
|
+
score += 5;
|
|
401
|
+
}
|
|
402
|
+
for (const word of expandedWords) {
|
|
403
|
+
if (toolNameParts.includes(word))
|
|
404
|
+
score += 4;
|
|
405
|
+
if (toolName.includes(word))
|
|
406
|
+
score += 3;
|
|
407
|
+
if (tool.category.toLowerCase() === word)
|
|
408
|
+
score += 2;
|
|
409
|
+
if (text.includes(word))
|
|
410
|
+
score += 1;
|
|
411
|
+
}
|
|
412
|
+
return { tool, score };
|
|
413
|
+
}).filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((s) => s.tool);
|
|
414
|
+
}
|
|
415
|
+
async function searchBySemantic(limit, queryEmbedding) {
|
|
416
|
+
const catalog = loadCatalog();
|
|
417
|
+
return catalog.filter((t) => t.embedding && t.embedding.length > 0).map((tool) => ({
|
|
418
|
+
tool,
|
|
419
|
+
score: cosineSimilarity(queryEmbedding, tool.embedding)
|
|
420
|
+
})).sort((a, b) => b.score - a.score).slice(0, limit).map((s) => s.tool);
|
|
421
|
+
}
|
|
422
|
+
async function searchTools(query, limit = 5, queryEmbedding) {
|
|
423
|
+
if (queryEmbedding && queryEmbedding.length > 0) {
|
|
424
|
+
return searchBySemantic(limit, queryEmbedding);
|
|
425
|
+
}
|
|
426
|
+
try {
|
|
427
|
+
const embedding = await embedText(query);
|
|
428
|
+
return searchBySemantic(limit, embedding);
|
|
429
|
+
} catch {
|
|
430
|
+
return searchByKeyword(query, limit);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
270
434
|
// ../../node_modules/zod-to-json-schema/dist/esm/selectParser.js
|
|
271
435
|
import { ZodFirstPartyTypeKind as ZodFirstPartyTypeKind3 } from "zod/v3";
|
|
272
436
|
|
|
@@ -3586,92 +3750,30 @@ import { fileURLToPath } from "url";
|
|
|
3586
3750
|
import { dirname, join } from "path";
|
|
3587
3751
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
3588
3752
|
var __dirname2 = dirname(__filename2);
|
|
3589
|
-
var
|
|
3590
|
-
function
|
|
3591
|
-
if (
|
|
3753
|
+
var _catalog2 = null;
|
|
3754
|
+
function loadCatalog2() {
|
|
3755
|
+
if (_catalog2) return _catalog2;
|
|
3592
3756
|
try {
|
|
3593
3757
|
const catalogPath = join(__dirname2, "toolCatalog.json");
|
|
3594
3758
|
const raw = readFileSync(catalogPath, "utf-8");
|
|
3595
|
-
|
|
3596
|
-
return
|
|
3759
|
+
_catalog2 = JSON.parse(raw);
|
|
3760
|
+
return _catalog2;
|
|
3597
3761
|
} catch {
|
|
3598
3762
|
console.warn(
|
|
3599
3763
|
"[searchTools] Failed to load toolCatalog.json, using empty catalog"
|
|
3600
3764
|
);
|
|
3601
|
-
|
|
3602
|
-
return
|
|
3603
|
-
}
|
|
3604
|
-
}
|
|
3605
|
-
function cosineSimilarity(a, b) {
|
|
3606
|
-
if (a.length === 0 || b.length === 0 || a.length !== b.length) return 0;
|
|
3607
|
-
let dot = 0;
|
|
3608
|
-
for (let i = 0; i < a.length; i++) {
|
|
3609
|
-
dot += a[i] * b[i];
|
|
3610
|
-
}
|
|
3611
|
-
return dot;
|
|
3612
|
-
}
|
|
3613
|
-
function searchByKeyword(query, limit) {
|
|
3614
|
-
const catalog = loadCatalog();
|
|
3615
|
-
const normalizedQuery = query.toLowerCase();
|
|
3616
|
-
const queryWords = normalizedQuery.split(/[\s_-]+/).filter((w) => w.length >= 2);
|
|
3617
|
-
const queryPhrases = [normalizedQuery];
|
|
3618
|
-
const synonyms = {
|
|
3619
|
-
error: ["console", "js", "javascript", "bug", "crash", "exception"],
|
|
3620
|
-
page: ["pages", "route", "url", "path"],
|
|
3621
|
-
user: ["users", "session", "visitor"],
|
|
3622
|
-
click: ["clicks", "tap", "press", "rage"],
|
|
3623
|
-
form: ["forms", "input", "field", "submit"],
|
|
3624
|
-
flow: ["flows", "journey", "funnel", "navigation", "path"],
|
|
3625
|
-
issue: ["issues", "problem", "bug", "friction"],
|
|
3626
|
-
fix: ["fixes", "remediation", "repair", "resolve"],
|
|
3627
|
-
compare: ["comparison", "diff", "versus", "cohort"],
|
|
3628
|
-
health: ["score", "metrics", "ux"],
|
|
3629
|
-
dead: ["unresponsive", "broken", "stuck"],
|
|
3630
|
-
rage: ["angry", "frustrated", "frustration"]
|
|
3631
|
-
};
|
|
3632
|
-
const expandedWords = new Set(queryWords);
|
|
3633
|
-
for (const word of queryWords) {
|
|
3634
|
-
if (synonyms[word]) {
|
|
3635
|
-
synonyms[word].forEach((syn) => expandedWords.add(syn));
|
|
3636
|
-
}
|
|
3637
|
-
for (const [key, values] of Object.entries(synonyms)) {
|
|
3638
|
-
if (values.includes(word)) {
|
|
3639
|
-
expandedWords.add(key);
|
|
3640
|
-
}
|
|
3641
|
-
}
|
|
3765
|
+
_catalog2 = [];
|
|
3766
|
+
return _catalog2;
|
|
3642
3767
|
}
|
|
3643
|
-
return catalog.map((tool) => {
|
|
3644
|
-
const toolName = tool.name.toLowerCase().replace(/_/g, " ");
|
|
3645
|
-
const toolNameParts = tool.name.toLowerCase().split("_");
|
|
3646
|
-
const text = `${toolName} ${tool.description} ${tool.category}`.toLowerCase();
|
|
3647
|
-
let score = 0;
|
|
3648
|
-
for (const phrase of queryPhrases) {
|
|
3649
|
-
if (phrase.length > 3 && text.includes(phrase)) score += 5;
|
|
3650
|
-
}
|
|
3651
|
-
for (const word of expandedWords) {
|
|
3652
|
-
if (toolNameParts.includes(word)) score += 4;
|
|
3653
|
-
if (toolName.includes(word)) score += 3;
|
|
3654
|
-
if (tool.category.toLowerCase() === word) score += 2;
|
|
3655
|
-
if (text.includes(word)) score += 1;
|
|
3656
|
-
}
|
|
3657
|
-
return { tool, score };
|
|
3658
|
-
}).filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((s) => s.tool);
|
|
3659
3768
|
}
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
})).sort((a, b) => b.score - a.score).slice(0, limit).map((s) => s.tool);
|
|
3666
|
-
}
|
|
3667
|
-
async function searchTools2(query, limit = 5, queryEmbedding) {
|
|
3668
|
-
if (queryEmbedding && queryEmbedding.length > 0) {
|
|
3669
|
-
return searchBySemantic(query, limit, queryEmbedding);
|
|
3670
|
-
}
|
|
3671
|
-
return searchByKeyword(query, limit);
|
|
3769
|
+
setCatalogLoader(loadCatalog2);
|
|
3770
|
+
preloadEmbedder();
|
|
3771
|
+
async function searchTools2(query, limit = 5) {
|
|
3772
|
+
const results = await searchTools(query, limit);
|
|
3773
|
+
return results;
|
|
3672
3774
|
}
|
|
3673
3775
|
function getToolByName3(name) {
|
|
3674
|
-
return
|
|
3776
|
+
return loadCatalog2().find((t) => t.name === name);
|
|
3675
3777
|
}
|
|
3676
3778
|
var DEFAULT_TOOL_LIMIT = 5;
|
|
3677
3779
|
var searchToolsSchema = z39.object({
|
|
@@ -3684,7 +3786,7 @@ function registerSearchTools(server) {
|
|
|
3684
3786
|
server.registerTool(
|
|
3685
3787
|
"search_tools",
|
|
3686
3788
|
{
|
|
3687
|
-
description: "Discover analysis tools by describing what data or capability you need. You have access to 40+ specialized tools covering sessions, pages, behaviors, journeys, forms, errors, performance, cohorts, issues, and remediation.
|
|
3789
|
+
description: "Discover analysis tools by describing what data or capability you need. You have access to 40+ specialized tools covering sessions, pages, behaviors, journeys, forms, errors, performance, cohorts, issues, and remediation. Semantic search matches your intent to relevant tools. Returns tool names, descriptions, categories, and parameters. Use call_tool to execute discovered tools.",
|
|
3688
3790
|
inputSchema: searchToolsSchema
|
|
3689
3791
|
},
|
|
3690
3792
|
async ({ query, limit }) => {
|
|
@@ -3697,7 +3799,7 @@ function registerSearchTools(server) {
|
|
|
3697
3799
|
type: "text",
|
|
3698
3800
|
text: JSON.stringify({
|
|
3699
3801
|
tools: [],
|
|
3700
|
-
message: "No matching tools found. Try rephrasing your query or
|
|
3802
|
+
message: "No matching tools found. Try rephrasing your query or describing what you want to analyze."
|
|
3701
3803
|
})
|
|
3702
3804
|
}
|
|
3703
3805
|
]
|
|
@@ -755,7 +755,7 @@
|
|
|
755
755
|
},
|
|
756
756
|
{
|
|
757
757
|
"name": "propose_fix",
|
|
758
|
-
"description": "Propose a fix for an issue. Creates a remediation record with baseline metrics for later evaluation.",
|
|
758
|
+
"description": "Propose a fix for an issue. Creates a remediation record with baseline metrics for later evaluation. Use this to create a new remediation, even without an existing issue ID.",
|
|
759
759
|
"input_schema": {
|
|
760
760
|
"type": "object",
|
|
761
761
|
"properties": {
|
|
@@ -848,7 +848,7 @@
|
|
|
848
848
|
},
|
|
849
849
|
{
|
|
850
850
|
"name": "update_remediation_status",
|
|
851
|
-
"description": "Update the status of a remediation record. Use after PR
|
|
851
|
+
"description": "Update the status of a remediation record. Use to mark as 'waiting' (PR created), 'deployed' (PR merged), or 'dismissed' (PR closed). Call after creating a PR to set status to waiting, or after PR merge to mark as deployed.",
|
|
852
852
|
"input_schema": {
|
|
853
853
|
"type": "object",
|
|
854
854
|
"properties": {
|