@recapt/mcp 0.0.26 → 0.0.28

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
@@ -1181,7 +1181,7 @@ var updateImprovementRunTool = {
1181
1181
  inputSchema: z27.object({
1182
1182
  run_id: z27.string().describe("The ID of the improvement run to update"),
1183
1183
  status: z27.enum(["running", "completed", "failed", "cancelled"]).optional().describe("New status for the run"),
1184
- title: z27.string().optional().describe("A concise title summarizing what was fixed in this run (e.g., 'Fixed checkout button, improved mobile nav'). Set when completing the run."),
1184
+ title: z27.string().optional().describe("A user-friendly title summarizing what was improved (e.g., 'Fixed checkout responsiveness, improved mobile navigation'). Avoid technical jargon - write for non-developers."),
1185
1185
  phases: z27.array(z27.object({
1186
1186
  name: z27.string(),
1187
1187
  status: z27.enum([
@@ -1233,9 +1233,9 @@ var updateImprovementRunTool = {
1233
1233
  };
1234
1234
  var recordImprovementActionTool = {
1235
1235
  name: "record_improvement_action",
1236
- description: "Record an action taken during an improvement run. Use to track what was done for each issue: code fixes, deferrals, dismissals, etc. IMPORTANT: When recording code_fix actions, the code_changes.diff field must contain ACTUAL SOURCE CODE in unified diff format, not a description of what changed. Include the literal code lines with proper +/- prefixes.",
1236
+ description: "Record an action taken during an improvement run. REQUIRED: run_id, action_type, hypothesis, expected_improvement. For code_fix: include code_changes array with actual unified diff format (not descriptions). For needs_more_data: include deferral_reason. For dismissed: include dismissal_reason. Use expected_improvement='N/A' for non-fix actions.",
1237
1237
  inputSchema: z27.object({
1238
- run_id: z27.string().describe("The ID of the improvement run"),
1238
+ run_id: z27.string().describe("REQUIRED. The ID of the improvement run"),
1239
1239
  issue_id: z27.string().optional().describe("The ID of the issue this action relates to (if any)"),
1240
1240
  action_type: z27.enum([
1241
1241
  "code_fix",
@@ -1243,9 +1243,9 @@ var recordImprovementActionTool = {
1243
1243
  "dismissed",
1244
1244
  "marked_intended",
1245
1245
  "knowledge_added"
1246
- ]).describe("Type of action taken"),
1247
- hypothesis: z27.string().describe("AI's reasoning for this action"),
1248
- expected_improvement: z27.string().describe("What improvement is expected from this action"),
1246
+ ]).describe("REQUIRED. Type of action taken"),
1247
+ hypothesis: z27.string().describe("REQUIRED. AI's reasoning for this action"),
1248
+ expected_improvement: z27.string().describe("REQUIRED. What improvement is expected (use 'N/A' for non-fix actions)"),
1249
1249
  code_changes: z27.array(z27.object({
1250
1250
  file: z27.string().describe("Path to the changed file"),
1251
1251
  diff: z27.string().describe("Unified diff format code changes"),
@@ -1269,6 +1269,12 @@ var recordImprovementActionTool = {
1269
1269
  if (!isApiConfigured()) {
1270
1270
  return apiNotConfiguredResult();
1271
1271
  }
1272
+ if (remediation_id) {
1273
+ const { data: remediation, error: remediationError } = await apiGet(`/remediations/${remediation_id}`);
1274
+ if (remediationError || !remediation) {
1275
+ return errorResult(`Remediation ${remediation_id} not found`);
1276
+ }
1277
+ }
1272
1278
  const { data, error } = await apiPost(`/improvement-runs/${run_id}/actions`, {
1273
1279
  issue_id,
1274
1280
  action_type,
@@ -1569,17 +1575,28 @@ var predictOutcomesTool = {
1569
1575
  import { z as z32 } from "zod";
1570
1576
  var proposeFixTool = {
1571
1577
  name: "propose_fix",
1572
- description: "Propose a fix for an issue. Creates a remediation record with baseline metrics for later evaluation. Include detailed diagnosis and code suggestions when possible.",
1578
+ description: "Propose a fix for an issue. REQUIRED: issue_id, diagnosis, proposed_fix, confidence. Creates a remediation record with baseline metrics for later evaluation. Returns remediation_id for tracking.",
1573
1579
  inputSchema: z32.object({
1574
- issue_id: z32.string().describe("The ID of the issue to fix"),
1575
- diagnosis: z32.string().describe("Detailed analysis of the root cause of the issue"),
1576
- proposed_fix: z32.string().describe("Description of the proposed fix and how it addresses the root cause"),
1580
+ issue_id: z32.string().describe("REQUIRED. The ID of the issue to fix"),
1581
+ diagnosis: z32.string().describe("REQUIRED. Detailed analysis of the root cause of the issue"),
1582
+ proposed_fix: z32.string().describe("REQUIRED. Description of the proposed fix and how it addresses the root cause"),
1577
1583
  code_snippet: z32.string().optional().describe("Suggested code changes (if applicable)"),
1578
1584
  affected_files: z32.array(z32.string()).optional().describe("List of files that need to be modified"),
1579
- confidence: z32.number().min(0).max(1).describe("Confidence level in the fix (0-1). Use <0.7 when uncertain.")
1585
+ confidence: z32.number().min(0).max(1).describe("REQUIRED. Confidence level in the fix (0-1). Use <0.7 when uncertain."),
1586
+ title: z32.string().optional().describe("User-friendly title describing the issue from the user's perspective"),
1587
+ execution_metadata: z32.object({
1588
+ difficulty_class: z32.enum(["trivial", "light", "heavy"]),
1589
+ difficulty_reasoning: z32.string(),
1590
+ model_used: z32.string(),
1591
+ was_escalated: z32.boolean().optional(),
1592
+ escalation_reason: z32.string().optional(),
1593
+ iterations: z32.number().optional(),
1594
+ credits_used: z32.number().optional(),
1595
+ duration_ms: z32.number().optional()
1596
+ }).optional().describe("AI execution metadata for tracking model selection and performance")
1580
1597
  }),
1581
1598
  handler: async (args) => {
1582
- const { issue_id, diagnosis, proposed_fix, code_snippet, affected_files, confidence } = args;
1599
+ const { issue_id, diagnosis, proposed_fix, code_snippet, affected_files, confidence, title, execution_metadata } = args;
1583
1600
  if (!isApiConfigured()) {
1584
1601
  return apiNotConfiguredResult();
1585
1602
  }
@@ -1589,7 +1606,9 @@ var proposeFixTool = {
1589
1606
  proposed_fix,
1590
1607
  code_snippet,
1591
1608
  affected_files,
1592
- confidence
1609
+ confidence,
1610
+ title,
1611
+ execution_metadata
1593
1612
  });
1594
1613
  if (error) {
1595
1614
  return errorResult(error);
@@ -1624,7 +1643,8 @@ var listRemediationsTool = {
1624
1643
  "evaluating",
1625
1644
  "succeeded",
1626
1645
  "failed",
1627
- "reverted"
1646
+ "reverted",
1647
+ "deferred"
1628
1648
  ]).optional().describe("Filter by remediation status"),
1629
1649
  issue_id: z32.string().optional().describe("Filter by issue ID"),
1630
1650
  page_path: z32.string().optional().describe("Filter by page path"),
@@ -1704,10 +1724,10 @@ var getFixHistoryTool = {
1704
1724
  };
1705
1725
  var updateRemediationStatusTool = {
1706
1726
  name: "update_remediation_status",
1707
- description: "Update the status of a remediation based on PR events. Use to mark a fix as 'waiting' (PR created), 'deployed' (PR merged), or 'dismissed' (PR closed without merge).",
1727
+ description: "Update remediation status based on PR events. REQUIRED: remediation_id, status. Status values: 'waiting' (PR created), 'deployed' (PR merged), 'dismissed' (PR closed without merge).",
1708
1728
  inputSchema: z32.object({
1709
- remediation_id: z32.string().describe("The ID of the remediation to update"),
1710
- status: z32.enum(["waiting", "deployed", "dismissed"]).describe("New status: 'waiting' = PR open, 'deployed' = PR merged, 'dismissed' = PR closed without merge"),
1729
+ remediation_id: z32.string().describe("REQUIRED. The ID of the remediation to update"),
1730
+ status: z32.enum(["waiting", "deployed", "dismissed"]).describe("REQUIRED. One of: waiting, deployed, dismissed"),
1711
1731
  pr_url: z32.string().optional().describe("URL of the pull request"),
1712
1732
  pr_number: z32.number().optional().describe("PR number"),
1713
1733
  pr_merged_at: z32.string().optional().describe("ISO timestamp when PR was merged"),
@@ -1733,7 +1753,7 @@ var updateRemediationStatusTool = {
1733
1753
  };
1734
1754
  var listRemediationsByStatusTool = {
1735
1755
  name: "list_remediations_by_status",
1736
- description: "List remediations filtered by one or more statuses. Use to find all 'waiting' PRs or 'deployed' fixes ready for evaluation.",
1756
+ description: "List remediations filtered by one or more statuses. Use to find all 'waiting' PRs or 'deployed' fixes ready for evaluation. Set include_execution to get difficulty classification for model selection.",
1737
1757
  inputSchema: z32.object({
1738
1758
  statuses: z32.array(z32.enum([
1739
1759
  "proposed",
@@ -1743,16 +1763,19 @@ var listRemediationsByStatusTool = {
1743
1763
  "evaluating",
1744
1764
  "succeeded",
1745
1765
  "failed",
1746
- "reverted"
1747
- ])).describe("List of statuses to filter by")
1766
+ "reverted",
1767
+ "deferred"
1768
+ ])).describe("List of statuses to filter by"),
1769
+ include_execution: z32.boolean().optional().describe("Include execution metadata (difficulty class, model used) for each remediation")
1748
1770
  }),
1749
1771
  handler: async (args) => {
1750
- const { statuses } = args;
1772
+ const { statuses, include_execution } = args;
1751
1773
  if (!isApiConfigured()) {
1752
1774
  return apiNotConfiguredResult();
1753
1775
  }
1754
1776
  const { data, error } = await apiGet(`/remediations/by-status`, {
1755
- statuses: statuses.join(",")
1777
+ statuses: statuses.join(","),
1778
+ include_execution: include_execution ? "true" : void 0
1756
1779
  });
1757
1780
  if (error) {
1758
1781
  return errorResult(error);
@@ -1778,6 +1801,34 @@ var getRemediationByPrTool = {
1778
1801
  return successResult(data);
1779
1802
  }
1780
1803
  };
1804
+ var deferIssueTool = {
1805
+ name: "defer_issue",
1806
+ description: "Defer an issue that needs more data. REQUIRED: issue_id, diagnosis, deferral_reason, confidence. Creates a remediation record with 'deferred' status. Returns remediation_id for tracking.",
1807
+ inputSchema: z32.object({
1808
+ issue_id: z32.string().describe("REQUIRED. The ID of the issue to defer"),
1809
+ diagnosis: z32.string().describe("REQUIRED. Analysis of what you think the problem might be"),
1810
+ deferral_reason: z32.string().describe("REQUIRED. Explanation of why the issue cannot be fixed yet"),
1811
+ confidence: z32.number().min(0).max(1).describe("REQUIRED. Confidence level in the diagnosis (0-1). Typically low for deferred issues."),
1812
+ affected_files: z32.array(z32.string()).optional().describe("List of files that might need to be modified")
1813
+ }),
1814
+ handler: async (args) => {
1815
+ const { issue_id, diagnosis, deferral_reason, confidence, affected_files } = args;
1816
+ if (!isApiConfigured()) {
1817
+ return apiNotConfiguredResult();
1818
+ }
1819
+ const { data, error } = await apiPost("/remediations/defer", {
1820
+ issue_id,
1821
+ diagnosis,
1822
+ deferral_reason,
1823
+ confidence,
1824
+ affected_files
1825
+ });
1826
+ if (error) {
1827
+ return errorResult(error);
1828
+ }
1829
+ return successResult(data);
1830
+ }
1831
+ };
1781
1832
 
1782
1833
  // ../../libraries/mcp-tools/dist/tools/scanSite.js
1783
1834
  import { z as z33 } from "zod";
@@ -1841,11 +1892,11 @@ var DISMISS_REASONS = [
1841
1892
  ];
1842
1893
  var dismissIssueTool = {
1843
1894
  name: "dismiss_issue",
1844
- description: "Dismiss an issue as a false positive. Creates site knowledge to prevent re-flagging. Use when an issue is stale, intended behavior, a duplicate, or a false positive. Always confirm with user before dismissing.",
1895
+ description: "Dismiss an issue as a false positive. REQUIRED: issue_id, reason (enum), explanation. Creates site knowledge to prevent re-flagging. Confirm with user before dismissing in interactive mode.",
1845
1896
  inputSchema: z35.object({
1846
- issue_id: z35.string().describe("The ID of the issue to dismiss"),
1847
- reason: z35.enum(DISMISS_REASONS).describe("Reason for dismissal: stale, intended, duplicate, or false_positive"),
1848
- explanation: z35.string().describe("Detailed explanation of why this issue is being dismissed"),
1897
+ issue_id: z35.string().describe("REQUIRED. The ID of the issue to dismiss"),
1898
+ reason: z35.enum(DISMISS_REASONS).describe("REQUIRED. One of: stale, intended, duplicate, false_positive"),
1899
+ explanation: z35.string().describe("REQUIRED. Detailed explanation of why this issue is being dismissed"),
1849
1900
  create_knowledge: z35.boolean().optional().default(true).describe("Whether to create site knowledge entry (default: true)")
1850
1901
  }),
1851
1902
  handler: async (args) => {
@@ -2078,22 +2129,32 @@ var PROPOSAL_STATUSES = [
2078
2129
  ];
2079
2130
  var createProposalTool = {
2080
2131
  name: "create_proposal",
2081
- description: "Create a fix proposal for audit mode. Proposals are suggestions that developers review and implement manually. Use this in audit mode instead of propose_fix when you cannot make code changes directly.",
2132
+ description: "AUDIT MODE ONLY. Create a fix proposal. REQUIRED: title, diagnosis, proposed_fix, confidence, page_path, category, severity. Proposals are suggestions that developers review and implement manually. Do NOT use in fix mode - use propose_fix instead.",
2082
2133
  inputSchema: z37.object({
2083
2134
  issue_id: z37.string().optional().describe("The ID of the related issue (if any)"),
2084
2135
  improvement_run_id: z37.string().optional().describe("The ID of the improvement run creating this proposal"),
2085
- title: z37.string().describe("Short title describing the proposed fix"),
2086
- diagnosis: z37.string().describe("Detailed analysis of the root cause of the issue"),
2087
- proposed_fix: z37.string().describe("Description of the proposed fix and how it addresses the root cause"),
2136
+ title: z37.string().describe("REQUIRED. Short user-friendly title describing the issue from the user's perspective"),
2137
+ diagnosis: z37.string().describe("REQUIRED. Detailed analysis of the root cause of the issue"),
2138
+ proposed_fix: z37.string().describe("REQUIRED. Description of the proposed fix and how it addresses the root cause"),
2088
2139
  affected_files: z37.array(z37.string()).optional().describe("List of files that likely need to be modified"),
2089
- confidence: z37.number().min(0).max(1).describe("Confidence level in the fix (0-1). Use <0.7 when uncertain."),
2090
- page_path: z37.string().describe("The page path where the issue occurs"),
2140
+ confidence: z37.number().min(0).max(1).describe("REQUIRED. Confidence level in the fix (0-1). Use <0.7 when uncertain."),
2141
+ page_path: z37.string().describe("REQUIRED. The page path where the issue occurs"),
2091
2142
  element_selector: z37.string().optional().describe("CSS selector of the affected element (if applicable)"),
2092
- category: z37.enum(ISSUE_CATEGORIES2).describe("Category of the issue being addressed"),
2093
- severity: z37.enum(ISSUE_SEVERITIES).describe("Severity of the issue being addressed")
2143
+ category: z37.enum(ISSUE_CATEGORIES2).describe("REQUIRED. Category: code_error, dead_click, rage_click, ux_friction, performance, form_issue, behavioral_anomaly, structural_issue"),
2144
+ severity: z37.enum(ISSUE_SEVERITIES).describe("REQUIRED. Severity: critical, high, medium, low, info"),
2145
+ execution_metadata: z37.object({
2146
+ difficulty_class: z37.enum(["trivial", "light", "heavy"]),
2147
+ difficulty_reasoning: z37.string(),
2148
+ model_used: z37.string(),
2149
+ was_escalated: z37.boolean().optional(),
2150
+ escalation_reason: z37.string().optional(),
2151
+ iterations: z37.number().optional(),
2152
+ credits_used: z37.number().optional(),
2153
+ duration_ms: z37.number().optional()
2154
+ }).optional().describe("AI execution metadata for tracking model selection and performance")
2094
2155
  }),
2095
2156
  handler: async (args) => {
2096
- const { issue_id, improvement_run_id, title, diagnosis, proposed_fix, affected_files, confidence, page_path, element_selector, category, severity } = args;
2157
+ const { issue_id, improvement_run_id, title, diagnosis, proposed_fix, affected_files, confidence, page_path, element_selector, category, severity, execution_metadata } = args;
2097
2158
  if (!isApiConfigured()) {
2098
2159
  return apiNotConfiguredResult();
2099
2160
  }
@@ -2108,7 +2169,8 @@ var createProposalTool = {
2108
2169
  page_path,
2109
2170
  element_selector,
2110
2171
  category,
2111
- severity
2172
+ severity,
2173
+ execution_metadata
2112
2174
  });
2113
2175
  if (error) {
2114
2176
  return errorResult(error);
@@ -2194,11 +2256,11 @@ var resolveProposalTool = {
2194
2256
  };
2195
2257
  var dismissProposalTool = {
2196
2258
  name: "dismiss_proposal",
2197
- description: "Dismiss a proposal that won't be implemented. Use when the proposal is not applicable, the issue is intended behavior, or a different approach is preferred.",
2259
+ description: "Dismiss a proposal that won't be implemented. REQUIRED: proposal_id, reason. Use when the proposal is not applicable, the issue is intended behavior, or a different approach is preferred.",
2198
2260
  inputSchema: z37.object({
2199
- proposal_id: z37.string().describe("The ID of the proposal to dismiss"),
2200
- reason: z37.string().describe("Explanation of why the proposal is being dismissed"),
2201
- member_id: z37.string().optional().describe("The ID of the member who dismissed it (optional)")
2261
+ proposal_id: z37.string().describe("REQUIRED. The ID of the proposal to dismiss"),
2262
+ reason: z37.string().describe("REQUIRED. Explanation of why the proposal is being dismissed"),
2263
+ member_id: z37.string().optional().describe("The ID of the member who dismissed it")
2202
2264
  }),
2203
2265
  handler: async (args) => {
2204
2266
  const { proposal_id, reason, member_id } = args;
@@ -2293,10 +2355,11 @@ async function gitlabRequest(endpoint, options = {}) {
2293
2355
  const ctx = getGitContext();
2294
2356
  const baseUrl = ctx.baseUrl || "https://gitlab.com";
2295
2357
  const url = `${baseUrl}/api/v4${endpoint}`;
2358
+ const authHeader = ctx.token.startsWith("glpat-") ? { "PRIVATE-TOKEN": ctx.token } : { Authorization: `Bearer ${ctx.token}` };
2296
2359
  return fetch(url, {
2297
2360
  ...options,
2298
2361
  headers: {
2299
- "PRIVATE-TOKEN": ctx.token,
2362
+ ...authHeader,
2300
2363
  "Content-Type": "application/json",
2301
2364
  ...options.headers
2302
2365
  }
@@ -2417,13 +2480,13 @@ var getFileContentTool = {
2417
2480
  };
2418
2481
  var updateFileTool = {
2419
2482
  name: "update_file",
2420
- description: "Update or create a file in the repository. Works with both GitHub and GitLab.",
2483
+ description: "Update or create a file in the repository. Works with both GitHub and GitLab. SHA is auto-fetched for existing files.",
2421
2484
  inputSchema: z38.object({
2422
2485
  path: z38.string().describe("Path to the file in the repository"),
2423
2486
  content: z38.string().describe("New content for the file"),
2424
2487
  message: z38.string().describe("Commit message"),
2425
2488
  branch: z38.string().describe("Branch to commit to"),
2426
- sha: z38.string().optional().describe("SHA of the file being replaced (required for updates)")
2489
+ sha: z38.string().optional().describe("SHA of the file being replaced (auto-fetched if not provided)")
2427
2490
  }),
2428
2491
  handler: async (args) => {
2429
2492
  try {
@@ -2432,8 +2495,15 @@ var updateFileTool = {
2432
2495
  const content = args.content;
2433
2496
  const message = args.message;
2434
2497
  const branch = args.branch;
2435
- const sha = args.sha;
2498
+ let sha = args.sha;
2436
2499
  if (ctx.provider === "github") {
2500
+ if (!sha) {
2501
+ const checkRes = await githubRequest(`/repos/${ctx.repoFullName}/contents/${filePath}?ref=${branch}`);
2502
+ if (checkRes.ok) {
2503
+ const existingFile = await checkRes.json();
2504
+ sha = existingFile.sha;
2505
+ }
2506
+ }
2437
2507
  const body = {
2438
2508
  message,
2439
2509
  content: Buffer.from(content).toString("base64"),
@@ -2461,7 +2531,15 @@ var updateFileTool = {
2461
2531
  } else {
2462
2532
  const encodedPath = encodeURIComponent(ctx.repoFullName);
2463
2533
  const encodedFilePath = encodeURIComponent(filePath);
2464
- const method = sha ? "PUT" : "POST";
2534
+ let method = "POST";
2535
+ if (!sha) {
2536
+ const checkRes = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}?ref=${branch}`);
2537
+ if (checkRes.ok) {
2538
+ method = "PUT";
2539
+ }
2540
+ } else {
2541
+ method = "PUT";
2542
+ }
2465
2543
  const res = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}`, {
2466
2544
  method,
2467
2545
  body: JSON.stringify({
@@ -2859,6 +2937,7 @@ var allTools = [
2859
2937
  updateRemediationStatusTool,
2860
2938
  listRemediationsByStatusTool,
2861
2939
  getRemediationByPrTool,
2940
+ deferIssueTool,
2862
2941
  // Scan
2863
2942
  scanSiteTool,
2864
2943
  // Search
@@ -2902,6 +2981,604 @@ var EXPOSED_TOOL_NAMES = [
2902
2981
  var exposedTools = allTools.filter((t) => EXPOSED_TOOL_NAMES.includes(t.name));
2903
2982
  var hiddenTools = allTools.filter((t) => !EXPOSED_TOOL_NAMES.includes(t.name));
2904
2983
 
2984
+ // ../../libraries/mcp-tools/dist/memory.js
2985
+ var MAX_ENTRIES = 100;
2986
+ var MAX_SIZE_BYTES = 2 * 1024 * 1024;
2987
+ var PRUNE_TARGET_RATIO = 0.8;
2988
+ var MemoryStore = class _MemoryStore {
2989
+ store = /* @__PURE__ */ new Map();
2990
+ /**
2991
+ * Save a value to memory. Updates access tracking and triggers pruning if needed.
2992
+ */
2993
+ save(key, value, annotation) {
2994
+ const now = Date.now();
2995
+ const existing = this.store.get(key);
2996
+ this.store.set(key, {
2997
+ key,
2998
+ value,
2999
+ timestamp: existing?.timestamp ?? now,
3000
+ annotation,
3001
+ lastAccessedAt: now,
3002
+ accessCount: existing?.accessCount ?? 0
3003
+ });
3004
+ this.pruneIfNeeded();
3005
+ return {
3006
+ content: [
3007
+ {
3008
+ type: "text",
3009
+ text: JSON.stringify({ success: true, key, size: value.length })
3010
+ }
3011
+ ]
3012
+ };
3013
+ }
3014
+ /**
3015
+ * Save a value directly without returning ToolResult (for internal use).
3016
+ */
3017
+ saveRaw(key, value) {
3018
+ const now = Date.now();
3019
+ const existing = this.store.get(key);
3020
+ this.store.set(key, {
3021
+ key,
3022
+ value,
3023
+ timestamp: existing?.timestamp ?? now,
3024
+ annotation: void 0,
3025
+ lastAccessedAt: now,
3026
+ accessCount: existing?.accessCount ?? 0
3027
+ });
3028
+ this.pruneIfNeeded();
3029
+ }
3030
+ /**
3031
+ * Recall a value from memory. Updates access tracking.
3032
+ * Supports optional query parameters for filtering and selecting data.
3033
+ */
3034
+ recall(key, options) {
3035
+ const entry = this.store.get(key);
3036
+ if (!entry) {
3037
+ return {
3038
+ content: [
3039
+ {
3040
+ type: "text",
3041
+ text: JSON.stringify({
3042
+ error: `Key "${key}" not found in memory`,
3043
+ available_keys: this.keys().slice(0, 10)
3044
+ })
3045
+ }
3046
+ ],
3047
+ isError: true
3048
+ };
3049
+ }
3050
+ entry.lastAccessedAt = Date.now();
3051
+ entry.accessCount++;
3052
+ if (!options || !options.where && !options.select && !options.limit) {
3053
+ return {
3054
+ content: [
3055
+ {
3056
+ type: "text",
3057
+ text: entry.value
3058
+ }
3059
+ ]
3060
+ };
3061
+ }
3062
+ try {
3063
+ const data = JSON.parse(entry.value);
3064
+ const result = this.queryData(data, options);
3065
+ return {
3066
+ content: [
3067
+ {
3068
+ type: "text",
3069
+ text: JSON.stringify(result)
3070
+ }
3071
+ ]
3072
+ };
3073
+ } catch {
3074
+ return {
3075
+ content: [
3076
+ {
3077
+ type: "text",
3078
+ text: entry.value
3079
+ }
3080
+ ]
3081
+ };
3082
+ }
3083
+ }
3084
+ /**
3085
+ * Query data with filtering, selection, and pagination.
3086
+ */
3087
+ queryData(data, options) {
3088
+ let items;
3089
+ if (Array.isArray(data)) {
3090
+ items = data;
3091
+ } else if (this.isRecord(data)) {
3092
+ const arrayField = Object.entries(data).find(([, v]) => Array.isArray(v));
3093
+ if (arrayField) {
3094
+ items = arrayField[1];
3095
+ } else {
3096
+ return data;
3097
+ }
3098
+ } else {
3099
+ return data;
3100
+ }
3101
+ if (options.where && options.where.length > 0) {
3102
+ items = items.filter((item) => this.matchesWhere(item, options.where));
3103
+ }
3104
+ if (options.select && options.select.length > 0) {
3105
+ items = items.map((item) => this.selectFields(item, options.select));
3106
+ }
3107
+ const offset = options.offset ?? 0;
3108
+ const limit = options.limit ?? items.length;
3109
+ items = items.slice(offset, offset + limit);
3110
+ return {
3111
+ items,
3112
+ total: items.length,
3113
+ offset,
3114
+ limit
3115
+ };
3116
+ }
3117
+ isRecord(value) {
3118
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3119
+ }
3120
+ matchesWhere(item, conditions) {
3121
+ if (!this.isRecord(item))
3122
+ return true;
3123
+ for (const condition of conditions) {
3124
+ const match = condition.match(/^(\w+)\s*(>|<|>=|<=|==|!=|=)\s*(.+)$/);
3125
+ if (!match)
3126
+ continue;
3127
+ const [, field, op, rawValue] = match;
3128
+ const itemValue = item[field];
3129
+ let compareValue = rawValue.trim();
3130
+ if (compareValue === "true")
3131
+ compareValue = true;
3132
+ else if (compareValue === "false")
3133
+ compareValue = false;
3134
+ else if (!isNaN(Number(compareValue)))
3135
+ compareValue = Number(compareValue);
3136
+ else if (compareValue.startsWith('"') && compareValue.endsWith('"')) {
3137
+ compareValue = compareValue.slice(1, -1);
3138
+ }
3139
+ let passes = false;
3140
+ switch (op) {
3141
+ case ">":
3142
+ passes = Number(itemValue) > Number(compareValue);
3143
+ break;
3144
+ case "<":
3145
+ passes = Number(itemValue) < Number(compareValue);
3146
+ break;
3147
+ case ">=":
3148
+ passes = Number(itemValue) >= Number(compareValue);
3149
+ break;
3150
+ case "<=":
3151
+ passes = Number(itemValue) <= Number(compareValue);
3152
+ break;
3153
+ case "==":
3154
+ case "=":
3155
+ passes = itemValue === compareValue;
3156
+ break;
3157
+ case "!=":
3158
+ passes = itemValue !== compareValue;
3159
+ break;
3160
+ }
3161
+ if (!passes)
3162
+ return false;
3163
+ }
3164
+ return true;
3165
+ }
3166
+ selectFields(item, fields) {
3167
+ if (!this.isRecord(item))
3168
+ return {};
3169
+ const result = {};
3170
+ for (const field of fields) {
3171
+ if (field in item) {
3172
+ result[field] = item[field];
3173
+ }
3174
+ }
3175
+ return result;
3176
+ }
3177
+ /**
3178
+ * Recall a value directly without ToolResult wrapper (for internal use).
3179
+ */
3180
+ recallRaw(key) {
3181
+ const entry = this.store.get(key);
3182
+ if (!entry)
3183
+ return void 0;
3184
+ entry.lastAccessedAt = Date.now();
3185
+ entry.accessCount++;
3186
+ return entry.value;
3187
+ }
3188
+ /**
3189
+ * Check if a key exists in memory.
3190
+ */
3191
+ has(key) {
3192
+ return this.store.has(key);
3193
+ }
3194
+ /**
3195
+ * Get all keys in memory.
3196
+ */
3197
+ keys() {
3198
+ return [...this.store.keys()];
3199
+ }
3200
+ /**
3201
+ * Get the number of entries in memory.
3202
+ */
3203
+ get size() {
3204
+ return this.store.size;
3205
+ }
3206
+ /**
3207
+ * List all entries with metadata.
3208
+ */
3209
+ list() {
3210
+ const entries = [...this.store.values()].map((entry) => ({
3211
+ key: entry.key,
3212
+ size: entry.value.length,
3213
+ timestamp: new Date(entry.timestamp).toISOString(),
3214
+ annotation: entry.annotation,
3215
+ accessCount: entry.accessCount
3216
+ }));
3217
+ return {
3218
+ content: [
3219
+ {
3220
+ type: "text",
3221
+ text: JSON.stringify({ count: entries.length, entries })
3222
+ }
3223
+ ]
3224
+ };
3225
+ }
3226
+ /**
3227
+ * Delete a key from memory.
3228
+ */
3229
+ delete(key) {
3230
+ const existed = this.store.delete(key);
3231
+ return {
3232
+ content: [
3233
+ {
3234
+ type: "text",
3235
+ text: JSON.stringify({ success: true, deleted: existed })
3236
+ }
3237
+ ]
3238
+ };
3239
+ }
3240
+ /**
3241
+ * Clear all entries from memory.
3242
+ */
3243
+ clear() {
3244
+ const count = this.store.size;
3245
+ this.store.clear();
3246
+ return {
3247
+ content: [
3248
+ {
3249
+ type: "text",
3250
+ text: JSON.stringify({ success: true, cleared: count })
3251
+ }
3252
+ ]
3253
+ };
3254
+ }
3255
+ /**
3256
+ * Build a human-readable memory key from tool name + input parameters.
3257
+ */
3258
+ static buildKey(toolName, input) {
3259
+ const priorityFields = [
3260
+ "page_path",
3261
+ "session_id",
3262
+ "session_ids",
3263
+ "page_path_a",
3264
+ "page_path_b",
3265
+ "expression",
3266
+ "query",
3267
+ "moment_type",
3268
+ "action",
3269
+ "issue_id",
3270
+ "remediation_id",
3271
+ "proposal_id",
3272
+ "run_id"
3273
+ ];
3274
+ for (const field of priorityFields) {
3275
+ const value = input[field];
3276
+ if (value !== void 0 && value !== null && value !== "") {
3277
+ const strValue = Array.isArray(value) ? value.slice(0, 3).join(",") + (value.length > 3 ? "..." : "") : String(value);
3278
+ return `${toolName}:${strValue}`;
3279
+ }
3280
+ }
3281
+ const params = Object.entries(input).filter(([, v]) => v !== void 0 && v !== null).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${typeof v === "object" ? JSON.stringify(v) : v}`).join("&");
3282
+ if (params) {
3283
+ const hash = params.split("").reduce((acc, char) => {
3284
+ return (acc << 5) - acc + char.charCodeAt(0) | 0;
3285
+ }, 0);
3286
+ return `${toolName}:${Math.abs(hash).toString(36)}`;
3287
+ }
3288
+ return toolName;
3289
+ }
3290
+ /**
3291
+ * Get total size of all stored values in bytes.
3292
+ */
3293
+ getTotalSize() {
3294
+ let total = 0;
3295
+ for (const entry of this.store.values()) {
3296
+ total += entry.value.length;
3297
+ }
3298
+ return total;
3299
+ }
3300
+ /**
3301
+ * Prune entries if limits are exceeded.
3302
+ * Uses a scoring system based on recency, access frequency, and size.
3303
+ */
3304
+ pruneIfNeeded() {
3305
+ const overEntryLimit = this.store.size > MAX_ENTRIES;
3306
+ const overSizeLimit = this.getTotalSize() > MAX_SIZE_BYTES;
3307
+ if (!overEntryLimit && !overSizeLimit)
3308
+ return;
3309
+ const scored = [...this.store.entries()].map(([key, entry]) => ({
3310
+ key,
3311
+ entry,
3312
+ score: this.computePruneScore(key, entry)
3313
+ }));
3314
+ scored.sort((a, b) => a.score - b.score);
3315
+ const targetEntries = Math.floor(MAX_ENTRIES * PRUNE_TARGET_RATIO);
3316
+ const targetSize = Math.floor(MAX_SIZE_BYTES * PRUNE_TARGET_RATIO);
3317
+ while ((this.store.size > targetEntries || this.getTotalSize() > targetSize) && scored.length > 0) {
3318
+ const toPrune = scored.shift();
3319
+ if (!toPrune || toPrune.score >= 1e3)
3320
+ break;
3321
+ this.store.delete(toPrune.key);
3322
+ }
3323
+ }
3324
+ /**
3325
+ * Compute a score for pruning. Higher scores = more valuable = keep longer.
3326
+ * System keys ($meta, $findings, etc.) are protected.
3327
+ */
3328
+ computePruneScore(key, entry) {
3329
+ if (key.startsWith("$"))
3330
+ return 1e3;
3331
+ const now = Date.now();
3332
+ const msSinceAccess = now - entry.lastAccessedAt;
3333
+ const recencyScore = Math.max(0, 100 - msSinceAccess / 600);
3334
+ const accessScore = Math.min(entry.accessCount * 10, 50);
3335
+ const sizeScore = Math.max(0, 50 - entry.value.length / 1e3);
3336
+ return recencyScore + accessScore + sizeScore;
3337
+ }
3338
+ /**
3339
+ * Serialize all entries to a JSON string for external storage.
3340
+ */
3341
+ serialize() {
3342
+ const plain = {};
3343
+ for (const [k, entry] of this.store) {
3344
+ plain[k] = entry.value;
3345
+ }
3346
+ return JSON.stringify(plain);
3347
+ }
3348
+ /**
3349
+ * Hydrate a MemoryStore from a serialized JSON string.
3350
+ */
3351
+ static deserialize(json) {
3352
+ const memory = new _MemoryStore();
3353
+ const entries = JSON.parse(json);
3354
+ const now = Date.now();
3355
+ for (const [k, v] of Object.entries(entries)) {
3356
+ memory.store.set(k, {
3357
+ key: k,
3358
+ value: v,
3359
+ timestamp: now,
3360
+ annotation: void 0,
3361
+ lastAccessedAt: now,
3362
+ accessCount: 0
3363
+ });
3364
+ }
3365
+ return memory;
3366
+ }
3367
+ };
3368
+ function createMemoryStore() {
3369
+ return new MemoryStore();
3370
+ }
3371
+
3372
+ // ../../libraries/mcp-tools/dist/wrapToolsWithMemory.js
3373
+ var SUMMARIZE_THRESHOLD = 2e3;
3374
+ var MAX_ARRAY_ITEMS = 2;
3375
+ var MAX_STRING_LENGTH = 120;
3376
+ var MAX_DEPTH = 3;
3377
+ var PASSTHROUGH_TOOLS = /* @__PURE__ */ new Set([
3378
+ "memory_save",
3379
+ "memory_recall",
3380
+ "memory_list",
3381
+ "memory_delete",
3382
+ "memory_clear",
3383
+ "search_tools",
3384
+ "get_domains"
3385
+ ]);
3386
+ function isRecord(value) {
3387
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3388
+ }
3389
+ function wrapToolResult(toolName, args, result, memory, options) {
3390
+ if (PASSTHROUGH_TOOLS.has(toolName)) {
3391
+ return result;
3392
+ }
3393
+ const text = result.content[0]?.text;
3394
+ if (!text || result.isError) {
3395
+ return result;
3396
+ }
3397
+ if (text.length <= SUMMARIZE_THRESHOLD) {
3398
+ return result;
3399
+ }
3400
+ let parsed;
3401
+ try {
3402
+ parsed = JSON.parse(text);
3403
+ } catch {
3404
+ return result;
3405
+ }
3406
+ if (isRecord(parsed) && parsed.error) {
3407
+ return result;
3408
+ }
3409
+ const key = MemoryStore.buildKey(toolName, args);
3410
+ memory.saveRaw(key, text);
3411
+ const strategy = options?.strategy ?? "truncate";
3412
+ const summary = strategy === "schema" ? summarizeSchema(parsed, key) : summarizeTruncate(parsed, key);
3413
+ return {
3414
+ content: [
3415
+ {
3416
+ type: "text",
3417
+ text: JSON.stringify(summary)
3418
+ }
3419
+ ]
3420
+ };
3421
+ }
3422
+ function summarizeTruncate(data, memoryKey) {
3423
+ const summary = truncateDeep(data, 0);
3424
+ addMemoryMetadata(summary, memoryKey, "truncate");
3425
+ return isRecord(summary) ? summary : { data: summary, _memory_key: memoryKey };
3426
+ }
3427
+ function truncateDeep(value, depth) {
3428
+ if (value == null || typeof value === "number" || typeof value === "boolean")
3429
+ return value;
3430
+ if (typeof value === "string") {
3431
+ return value.length > MAX_STRING_LENGTH ? value.slice(0, 80) + "..." : value;
3432
+ }
3433
+ if (Array.isArray(value)) {
3434
+ const items = value.slice(0, MAX_ARRAY_ITEMS).map((v) => truncateDeep(v, depth + 1));
3435
+ if (value.length > MAX_ARRAY_ITEMS) {
3436
+ items.push(`+${value.length - MAX_ARRAY_ITEMS} more in memory`);
3437
+ }
3438
+ return items;
3439
+ }
3440
+ if (isRecord(value)) {
3441
+ if (depth >= MAX_DEPTH) {
3442
+ const keys = Object.keys(value);
3443
+ return `{${keys.slice(0, 5).join(", ")}${keys.length > 5 ? ", ..." : ""}}`;
3444
+ }
3445
+ const result = {};
3446
+ for (const [k, v] of Object.entries(value)) {
3447
+ result[k] = truncateDeep(v, depth + 1);
3448
+ }
3449
+ return result;
3450
+ }
3451
+ return value;
3452
+ }
3453
+ function summarizeSchema(data, memoryKey) {
3454
+ const summary = describeSchema(data, 0);
3455
+ addMemoryMetadata(summary, memoryKey, "schema");
3456
+ return isRecord(summary) ? summary : { data: summary, _memory_key: memoryKey };
3457
+ }
3458
+ var SCHEMA_MAX_DISTINCT = 6;
3459
+ var SCHEMA_PRIMITIVE_CAP = 5;
3460
+ var SCHEMA_DEPTH_LIMIT = 2;
3461
+ function round4(n) {
3462
+ return Math.round(n * 1e4) / 1e4;
3463
+ }
3464
+ function describeSchema(value, depth) {
3465
+ if (value == null)
3466
+ return null;
3467
+ if (typeof value === "number" || typeof value === "boolean")
3468
+ return value;
3469
+ if (typeof value === "string") {
3470
+ return value.length > 200 ? value.slice(0, 150) + "..." : value;
3471
+ }
3472
+ if (Array.isArray(value)) {
3473
+ if (value.length === 0)
3474
+ return [];
3475
+ if (value[0] !== void 0 && isRecord(value[0])) {
3476
+ return describeObjectArray(value);
3477
+ }
3478
+ if (value.length <= SCHEMA_PRIMITIVE_CAP)
3479
+ return value;
3480
+ return [
3481
+ ...value.slice(0, SCHEMA_PRIMITIVE_CAP),
3482
+ `+${value.length - SCHEMA_PRIMITIVE_CAP} more`
3483
+ ];
3484
+ }
3485
+ if (isRecord(value)) {
3486
+ if (depth >= SCHEMA_DEPTH_LIMIT) {
3487
+ return { _keys: Object.keys(value) };
3488
+ }
3489
+ const result = {};
3490
+ for (const [k, v] of Object.entries(value)) {
3491
+ result[k] = describeSchema(v, depth + 1);
3492
+ }
3493
+ return result;
3494
+ }
3495
+ return value;
3496
+ }
3497
+ function computeFieldStat(values) {
3498
+ if (values.length === 0)
3499
+ return { type: "null" };
3500
+ if (values.every((v) => typeof v === "number")) {
3501
+ const nums = values;
3502
+ return {
3503
+ type: "number",
3504
+ min: round4(Math.min(...nums)),
3505
+ max: round4(Math.max(...nums)),
3506
+ avg: round4(nums.reduce((a, b) => a + b, 0) / nums.length)
3507
+ };
3508
+ }
3509
+ if (values.every((v) => typeof v === "string")) {
3510
+ const unique = [...new Set(values)];
3511
+ return unique.length <= SCHEMA_MAX_DISTINCT ? { type: "string", values: unique } : { type: "string", distinct: unique.length, sample: unique.slice(0, 3) };
3512
+ }
3513
+ if (values.every((v) => typeof v === "boolean")) {
3514
+ const trueCount = values.filter(Boolean).length;
3515
+ return {
3516
+ type: "boolean",
3517
+ true_count: trueCount,
3518
+ false_count: values.length - trueCount
3519
+ };
3520
+ }
3521
+ if (values.every((v) => Array.isArray(v))) {
3522
+ const arrays = values;
3523
+ const avgLen = arrays.reduce((sum, a) => sum + a.length, 0) / arrays.length;
3524
+ return { type: "array", avg_length: round4(avgLen) };
3525
+ }
3526
+ if (values.every((v) => isRecord(v))) {
3527
+ const nestedKeys = /* @__PURE__ */ new Set();
3528
+ for (const v of values) {
3529
+ for (const k of Object.keys(v)) {
3530
+ nestedKeys.add(k);
3531
+ }
3532
+ }
3533
+ return { type: "object", keys: [...nestedKeys] };
3534
+ }
3535
+ return { type: "mixed" };
3536
+ }
3537
+ function describeObjectArray(items) {
3538
+ const allKeys = /* @__PURE__ */ new Set();
3539
+ for (const item of items) {
3540
+ for (const key of Object.keys(item))
3541
+ allKeys.add(key);
3542
+ }
3543
+ const fields = [...allKeys];
3544
+ const stats = {};
3545
+ for (const field of fields) {
3546
+ const values = items.map((item) => item[field]).filter((v) => v !== void 0 && v !== null);
3547
+ stats[field] = computeFieldStat(values);
3548
+ }
3549
+ return {
3550
+ _type: "array",
3551
+ _count: items.length,
3552
+ _fields: fields,
3553
+ _stats: stats
3554
+ };
3555
+ }
3556
+ function addMemoryMetadata(summary, memoryKey, strategy) {
3557
+ if (!isRecord(summary))
3558
+ return;
3559
+ summary._memory_key = memoryKey;
3560
+ if (strategy === "truncate") {
3561
+ summary._recall = `Full data available via memory_recall with key "${memoryKey}"`;
3562
+ } else {
3563
+ const arrayPaths = detectArrayPaths(summary);
3564
+ const pathHint = arrayPaths.length > 0 ? ` Array fields: ${arrayPaths.join(", ")}.` : "";
3565
+ summary._hint = `Use memory_recall with key "${memoryKey}" to get full data.${pathHint}`;
3566
+ }
3567
+ }
3568
+ function detectArrayPaths(data) {
3569
+ if (!isRecord(data))
3570
+ return [];
3571
+ const paths = [];
3572
+ for (const [key, value] of Object.entries(data)) {
3573
+ if (key.startsWith("_"))
3574
+ continue;
3575
+ if (Array.isArray(value) && value.length > 0) {
3576
+ paths.push(key);
3577
+ }
3578
+ }
3579
+ return paths;
3580
+ }
3581
+
2905
3582
  // src/tools/catalog/searchTools.ts
2906
3583
  import { z as z39 } from "zod";
2907
3584
  import { readFileSync } from "fs";
@@ -3053,16 +3730,108 @@ function registerSearchTools(server) {
3053
3730
  }
3054
3731
 
3055
3732
  // src/tools/catalog/callTool.ts
3733
+ import { z as z41 } from "zod";
3734
+
3735
+ // src/tools/memory.ts
3056
3736
  import { z as z40 } from "zod";
3737
+ var memoryStore = null;
3738
+ function getMemoryStore() {
3739
+ if (!memoryStore) {
3740
+ memoryStore = createMemoryStore();
3741
+ }
3742
+ return memoryStore;
3743
+ }
3744
+ function getSharedMemoryStore() {
3745
+ return getMemoryStore();
3746
+ }
3747
+ function registerMemoryTools(server) {
3748
+ const memory = getMemoryStore();
3749
+ server.registerTool(
3750
+ "memory_save",
3751
+ {
3752
+ description: "Save a value to working memory for later retrieval. Use this to store intermediate results from tool calls that you want to reference later.",
3753
+ inputSchema: z40.object({
3754
+ key: z40.string().describe("Unique key to store the value under"),
3755
+ value: z40.string().describe("Value to store (will be stored as string)"),
3756
+ annotation: z40.string().optional().describe("Optional note about what this data represents")
3757
+ })
3758
+ },
3759
+ async ({
3760
+ key,
3761
+ value,
3762
+ annotation
3763
+ }) => {
3764
+ return memory.save(key, value, annotation);
3765
+ }
3766
+ );
3767
+ server.registerTool(
3768
+ "memory_recall",
3769
+ {
3770
+ description: "Retrieve a value from working memory by key. Supports optional filtering and selection for querying stored data. Use this to access data you previously stored or that was auto-saved from tool responses.",
3771
+ inputSchema: z40.object({
3772
+ key: z40.string().describe("Key to retrieve"),
3773
+ where: z40.array(z40.string()).optional().describe(
3774
+ 'Filter conditions like ["frustration > 0.3", "status = active"]'
3775
+ ),
3776
+ select: z40.array(z40.string()).optional().describe("Fields to include in results"),
3777
+ limit: z40.number().optional().describe("Maximum number of items to return"),
3778
+ offset: z40.number().optional().describe("Number of items to skip")
3779
+ })
3780
+ },
3781
+ async ({
3782
+ key,
3783
+ where,
3784
+ select,
3785
+ limit,
3786
+ offset
3787
+ }) => {
3788
+ return memory.recall(key, { where, select, limit, offset });
3789
+ }
3790
+ );
3791
+ server.registerTool(
3792
+ "memory_list",
3793
+ {
3794
+ description: "List all keys currently stored in working memory. Returns key names, sizes, timestamps, annotations, and access counts.",
3795
+ inputSchema: z40.object({})
3796
+ },
3797
+ async () => {
3798
+ return memory.list();
3799
+ }
3800
+ );
3801
+ server.registerTool(
3802
+ "memory_delete",
3803
+ {
3804
+ description: "Delete a value from working memory by key.",
3805
+ inputSchema: z40.object({
3806
+ key: z40.string().describe("Key to delete")
3807
+ })
3808
+ },
3809
+ async ({ key }) => {
3810
+ return memory.delete(key);
3811
+ }
3812
+ );
3813
+ server.registerTool(
3814
+ "memory_clear",
3815
+ {
3816
+ description: "Clear all values from working memory. Use with caution.",
3817
+ inputSchema: z40.object({})
3818
+ },
3819
+ async () => {
3820
+ return memory.clear();
3821
+ }
3822
+ );
3823
+ }
3824
+
3825
+ // src/tools/catalog/callTool.ts
3057
3826
  var toolRegistry = /* @__PURE__ */ new Map();
3058
3827
  function registerToolHandler(name, handler) {
3059
3828
  toolRegistry.set(name, handler);
3060
3829
  }
3061
- var callToolSchema = z40.object({
3062
- tool_name: z40.string().describe(
3830
+ var callToolSchema = z41.object({
3831
+ tool_name: z41.string().describe(
3063
3832
  "The exact name of the tool to call (e.g., 'get_page_metrics', 'analyze_flow', 'compare_cohorts')"
3064
3833
  ),
3065
- arguments: z40.record(z40.string(), z40.unknown()).describe(
3834
+ arguments: z41.record(z41.string(), z41.unknown()).describe(
3066
3835
  "The arguments to pass to the tool as a JSON object. Check the tool description from search_tools for required parameters."
3067
3836
  )
3068
3837
  });
@@ -3070,7 +3839,7 @@ function registerCallTool(server) {
3070
3839
  server.registerTool(
3071
3840
  "call_tool",
3072
3841
  {
3073
- description: "Execute any analysis tool by name. Use search_tools first to discover available tools, then call them through this proxy. Pass the exact tool name and arguments as a JSON object. Example: call_tool({ tool_name: 'get_page_metrics', arguments: { page_path: '/checkout' } })",
3842
+ description: "Execute any analysis tool by name. Use search_tools first to discover available tools, then call them through this proxy. Pass the exact tool name and arguments as a JSON object. Large responses are automatically saved to memory - use memory_recall to retrieve full data. Example: call_tool({ tool_name: 'get_page_metrics', arguments: { page_path: '/checkout' } })",
3074
3843
  inputSchema: callToolSchema
3075
3844
  },
3076
3845
  async ({ tool_name, arguments: args }) => {
@@ -3109,7 +3878,9 @@ function registerCallTool(server) {
3109
3878
  };
3110
3879
  }
3111
3880
  try {
3112
- return await handler(args);
3881
+ const rawResult = await handler(args);
3882
+ const memory = getSharedMemoryStore();
3883
+ return wrapToolResult(tool_name, args, rawResult, memory);
3113
3884
  } catch (err) {
3114
3885
  const message = err instanceof Error ? err.message : String(err);
3115
3886
  return {
@@ -3130,138 +3901,6 @@ function registerCallTool(server) {
3130
3901
  );
3131
3902
  }
3132
3903
 
3133
- // src/tools/memory.ts
3134
- import { z as z41 } from "zod";
3135
- var memoryStore = /* @__PURE__ */ new Map();
3136
- function registerMemoryTools(server) {
3137
- server.registerTool(
3138
- "memory_save",
3139
- {
3140
- description: "Save a value to working memory for later retrieval. Use this to store intermediate results from tool calls that you want to reference later.",
3141
- inputSchema: z41.object({
3142
- key: z41.string().describe("Unique key to store the value under"),
3143
- value: z41.string().describe("Value to store (will be stored as string)"),
3144
- annotation: z41.string().optional().describe("Optional note about what this data represents")
3145
- })
3146
- },
3147
- async ({
3148
- key,
3149
- value,
3150
- annotation
3151
- }) => {
3152
- memoryStore.set(key, {
3153
- key,
3154
- value,
3155
- timestamp: Date.now(),
3156
- annotation
3157
- });
3158
- return {
3159
- content: [
3160
- {
3161
- type: "text",
3162
- text: JSON.stringify({ success: true, key, size: value.length })
3163
- }
3164
- ]
3165
- };
3166
- }
3167
- );
3168
- server.registerTool(
3169
- "memory_recall",
3170
- {
3171
- description: "Retrieve a value from working memory by key. Use this to access data you previously stored with memory_save.",
3172
- inputSchema: z41.object({
3173
- key: z41.string().describe("Key to retrieve")
3174
- })
3175
- },
3176
- async ({ key }) => {
3177
- const entry = memoryStore.get(key);
3178
- if (!entry) {
3179
- return {
3180
- content: [
3181
- {
3182
- type: "text",
3183
- text: JSON.stringify({
3184
- error: `Key "${key}" not found in memory`
3185
- })
3186
- }
3187
- ],
3188
- isError: true
3189
- };
3190
- }
3191
- return {
3192
- content: [
3193
- {
3194
- type: "text",
3195
- text: entry.value
3196
- }
3197
- ]
3198
- };
3199
- }
3200
- );
3201
- server.registerTool(
3202
- "memory_list",
3203
- {
3204
- description: "List all keys currently stored in working memory. Returns key names, sizes, timestamps, and annotations.",
3205
- inputSchema: z41.object({})
3206
- },
3207
- async () => {
3208
- const entries = [...memoryStore.values()].map((entry) => ({
3209
- key: entry.key,
3210
- size: entry.value.length,
3211
- timestamp: new Date(entry.timestamp).toISOString(),
3212
- annotation: entry.annotation
3213
- }));
3214
- return {
3215
- content: [
3216
- {
3217
- type: "text",
3218
- text: JSON.stringify({ count: entries.length, entries })
3219
- }
3220
- ]
3221
- };
3222
- }
3223
- );
3224
- server.registerTool(
3225
- "memory_delete",
3226
- {
3227
- description: "Delete a value from working memory by key.",
3228
- inputSchema: z41.object({
3229
- key: z41.string().describe("Key to delete")
3230
- })
3231
- },
3232
- async ({ key }) => {
3233
- const existed = memoryStore.delete(key);
3234
- return {
3235
- content: [
3236
- {
3237
- type: "text",
3238
- text: JSON.stringify({ success: true, deleted: existed })
3239
- }
3240
- ]
3241
- };
3242
- }
3243
- );
3244
- server.registerTool(
3245
- "memory_clear",
3246
- {
3247
- description: "Clear all values from working memory. Use with caution.",
3248
- inputSchema: z41.object({})
3249
- },
3250
- async () => {
3251
- const count = memoryStore.size;
3252
- memoryStore.clear();
3253
- return {
3254
- content: [
3255
- {
3256
- type: "text",
3257
- text: JSON.stringify({ success: true, cleared: count })
3258
- }
3259
- ]
3260
- };
3261
- }
3262
- );
3263
- }
3264
-
3265
3904
  // src/index.ts
3266
3905
  function registerToolWithServer(server, tool) {
3267
3906
  server.tool(
@@ -3296,6 +3935,29 @@ call_tool({ tool_name: "analyze_flow", arguments: { start_page: "/pricing", end_
3296
3935
  \u2192 Returns: flow analysis with paths, bottlenecks, friction scores
3297
3936
  \`\`\`
3298
3937
 
3938
+ ## Automatic Memory Storage
3939
+
3940
+ Large tool responses (>500 chars) are automatically saved to memory and you receive a summary instead.
3941
+ This keeps your context lean while preserving all data for later retrieval.
3942
+
3943
+ When you see a response with \`_memory_key\`, the full data is stored in memory:
3944
+ \`\`\`json
3945
+ {
3946
+ "sessions": [{...}, {...}, "+18 more in memory"],
3947
+ "_memory_key": "list_sessions:/checkout",
3948
+ "_recall": "Full data available via memory_recall..."
3949
+ }
3950
+ \`\`\`
3951
+
3952
+ To retrieve full data or query it:
3953
+ \`\`\`
3954
+ memory_recall({ key: "list_sessions:/checkout" })
3955
+ \u2192 Returns: Full JSON data
3956
+
3957
+ memory_recall({ key: "...", where: ["frustration > 0.5"], select: ["session_id", "frustration"] })
3958
+ \u2192 Returns: Filtered and selected fields only
3959
+ \`\`\`
3960
+
3299
3961
  ## Always-Available Tools
3300
3962
 
3301
3963
  These tools are always visible (no search needed):
@@ -3303,7 +3965,7 @@ These tools are always visible (no search needed):
3303
3965
  - **run_full_diagnostic** \u2014 Quick site health overview (lightweight, fast)
3304
3966
  - **triage_sessions** \u2014 Find sessions needing attention
3305
3967
  - **get_upgrade_options** \u2014 Check available plan upgrades and features
3306
- - **memory_*** \u2014 Store/retrieve intermediate results
3968
+ - **memory_*** \u2014 Store/retrieve intermediate results (memory_recall supports where, select, limit, offset)
3307
3969
 
3308
3970
  ## Tier-Based Access
3309
3971
 
@@ -3348,7 +4010,7 @@ All behavioral scores are 0-1 where higher = more of that signal:
3348
4010
  - Backtrack hotspots suggest confusion on subsequent pages
3349
4011
  - Low confidence + high confusion = users don't understand what to do
3350
4012
  - High frustration + low confusion = users know what to do but can't (broken UI)
3351
- - Use \`memory_save\` to store intermediate results when doing multi-step analysis
4013
+ - Large responses are auto-saved to memory - use \`memory_recall\` to query stored data
3352
4014
 
3353
4015
  ## Tool Limits
3354
4016