@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 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: body ? JSON.stringify(body) : void 0,
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 _catalog = null;
3590
- function loadCatalog() {
3591
- if (_catalog) return _catalog;
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
- _catalog = JSON.parse(raw);
3596
- return _catalog;
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
- _catalog = [];
3602
- return _catalog;
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
- async function searchBySemantic(query, limit, queryEmbedding) {
3661
- const catalog = loadCatalog();
3662
- return catalog.filter((t) => t.embedding && t.embedding.length > 0).map((tool) => ({
3663
- tool,
3664
- score: cosineSimilarity(queryEmbedding, tool.embedding)
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 loadCatalog().find((t) => t.name === name);
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. Keyword search matches your intent to relevant tools. Returns tool names, descriptions, categories, and parameters. Use call_tool to execute discovered tools.",
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 use broader terms like 'page', 'session', 'issue', 'flow', or 'form'."
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 merge to mark as deployed, or after PR close to mark as dismissed.",
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": {