@recapt/mcp 0.0.30 → 0.0.32

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.
Files changed (2) hide show
  1. package/dist/index.js +185 -78
  2. 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: 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,161 @@ 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
+ const results = await searchBySemantic(limit, queryEmbedding);
425
+ if (results.length > 0)
426
+ return results;
427
+ return searchByKeyword(query, limit);
428
+ }
429
+ try {
430
+ const embedding = await embedText(query);
431
+ const results = await searchBySemantic(limit, embedding);
432
+ if (results.length > 0)
433
+ return results;
434
+ } catch {
435
+ }
436
+ return searchByKeyword(query, limit);
437
+ }
438
+
270
439
  // ../../node_modules/zod-to-json-schema/dist/esm/selectParser.js
271
440
  import { ZodFirstPartyTypeKind as ZodFirstPartyTypeKind3 } from "zod/v3";
272
441
 
@@ -3586,92 +3755,30 @@ import { fileURLToPath } from "url";
3586
3755
  import { dirname, join } from "path";
3587
3756
  var __filename2 = fileURLToPath(import.meta.url);
3588
3757
  var __dirname2 = dirname(__filename2);
3589
- var _catalog = null;
3590
- function loadCatalog() {
3591
- if (_catalog) return _catalog;
3758
+ var _catalog2 = null;
3759
+ function loadCatalog2() {
3760
+ if (_catalog2) return _catalog2;
3592
3761
  try {
3593
3762
  const catalogPath = join(__dirname2, "toolCatalog.json");
3594
3763
  const raw = readFileSync(catalogPath, "utf-8");
3595
- _catalog = JSON.parse(raw);
3596
- return _catalog;
3764
+ _catalog2 = JSON.parse(raw);
3765
+ return _catalog2;
3597
3766
  } catch {
3598
3767
  console.warn(
3599
3768
  "[searchTools] Failed to load toolCatalog.json, using empty catalog"
3600
3769
  );
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];
3770
+ _catalog2 = [];
3771
+ return _catalog2;
3610
3772
  }
3611
- return dot;
3612
3773
  }
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
- }
3642
- }
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
- }
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);
3774
+ setCatalogLoader(loadCatalog2);
3775
+ preloadEmbedder();
3776
+ async function searchTools2(query, limit = 5) {
3777
+ const results = await searchTools(query, limit);
3778
+ return results;
3672
3779
  }
3673
3780
  function getToolByName3(name) {
3674
- return loadCatalog().find((t) => t.name === name);
3781
+ return loadCatalog2().find((t) => t.name === name);
3675
3782
  }
3676
3783
  var DEFAULT_TOOL_LIMIT = 5;
3677
3784
  var searchToolsSchema = z39.object({
@@ -3684,7 +3791,7 @@ function registerSearchTools(server) {
3684
3791
  server.registerTool(
3685
3792
  "search_tools",
3686
3793
  {
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.",
3794
+ 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
3795
  inputSchema: searchToolsSchema
3689
3796
  },
3690
3797
  async ({ query, limit }) => {
@@ -3697,7 +3804,7 @@ function registerSearchTools(server) {
3697
3804
  type: "text",
3698
3805
  text: JSON.stringify({
3699
3806
  tools: [],
3700
- message: "No matching tools found. Try rephrasing your query or use broader terms like 'page', 'session', 'issue', 'flow', or 'form'."
3807
+ message: "No matching tools found. Try rephrasing your query or describing what you want to analyze."
3701
3808
  })
3702
3809
  }
3703
3810
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@recapt/mcp",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
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",