@recapt/mcp 0.0.27 → 0.0.29

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
@@ -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,18 +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."),
1580
- title: z32.string().optional().describe("User-friendly title describing the issue from the user's perspective. Example: 'Checkout button unresponsive during payment' instead of 'Dead clicks on .checkout-btn'")
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")
1581
1597
  }),
1582
1598
  handler: async (args) => {
1583
- const { issue_id, diagnosis, proposed_fix, code_snippet, affected_files, confidence, title } = args;
1599
+ const { issue_id, diagnosis, proposed_fix, code_snippet, affected_files, confidence, title, execution_metadata } = args;
1584
1600
  if (!isApiConfigured()) {
1585
1601
  return apiNotConfiguredResult();
1586
1602
  }
@@ -1591,7 +1607,8 @@ var proposeFixTool = {
1591
1607
  code_snippet,
1592
1608
  affected_files,
1593
1609
  confidence,
1594
- title
1610
+ title,
1611
+ execution_metadata
1595
1612
  });
1596
1613
  if (error) {
1597
1614
  return errorResult(error);
@@ -1707,10 +1724,10 @@ var getFixHistoryTool = {
1707
1724
  };
1708
1725
  var updateRemediationStatusTool = {
1709
1726
  name: "update_remediation_status",
1710
- 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).",
1711
1728
  inputSchema: z32.object({
1712
- remediation_id: z32.string().describe("The ID of the remediation to update"),
1713
- 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"),
1714
1731
  pr_url: z32.string().optional().describe("URL of the pull request"),
1715
1732
  pr_number: z32.number().optional().describe("PR number"),
1716
1733
  pr_merged_at: z32.string().optional().describe("ISO timestamp when PR was merged"),
@@ -1736,7 +1753,7 @@ var updateRemediationStatusTool = {
1736
1753
  };
1737
1754
  var listRemediationsByStatusTool = {
1738
1755
  name: "list_remediations_by_status",
1739
- 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.",
1740
1757
  inputSchema: z32.object({
1741
1758
  statuses: z32.array(z32.enum([
1742
1759
  "proposed",
@@ -1748,15 +1765,17 @@ var listRemediationsByStatusTool = {
1748
1765
  "failed",
1749
1766
  "reverted",
1750
1767
  "deferred"
1751
- ])).describe("List of statuses to filter by")
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")
1752
1770
  }),
1753
1771
  handler: async (args) => {
1754
- const { statuses } = args;
1772
+ const { statuses, include_execution } = args;
1755
1773
  if (!isApiConfigured()) {
1756
1774
  return apiNotConfiguredResult();
1757
1775
  }
1758
1776
  const { data, error } = await apiGet(`/remediations/by-status`, {
1759
- statuses: statuses.join(",")
1777
+ statuses: statuses.join(","),
1778
+ include_execution: include_execution ? "true" : void 0
1760
1779
  });
1761
1780
  if (error) {
1762
1781
  return errorResult(error);
@@ -1784,12 +1803,12 @@ var getRemediationByPrTool = {
1784
1803
  };
1785
1804
  var deferIssueTool = {
1786
1805
  name: "defer_issue",
1787
- description: "Defer an issue that needs more data before a fix can be proposed. Creates a remediation record with 'deferred' status to track the issue for future revisiting. Use when you understand the problem but lack sufficient data or confidence to propose a fix.",
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.",
1788
1807
  inputSchema: z32.object({
1789
- issue_id: z32.string().describe("The ID of the issue to defer"),
1790
- diagnosis: z32.string().describe("Analysis of what you think the problem might be"),
1791
- deferral_reason: z32.string().describe("Explanation of why the issue cannot be fixed yet"),
1792
- confidence: z32.number().min(0).max(1).describe("Confidence level in the diagnosis (0-1). Typically low for deferred issues."),
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."),
1793
1812
  affected_files: z32.array(z32.string()).optional().describe("List of files that might need to be modified")
1794
1813
  }),
1795
1814
  handler: async (args) => {
@@ -1873,11 +1892,11 @@ var DISMISS_REASONS = [
1873
1892
  ];
1874
1893
  var dismissIssueTool = {
1875
1894
  name: "dismiss_issue",
1876
- 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.",
1877
1896
  inputSchema: z35.object({
1878
- issue_id: z35.string().describe("The ID of the issue to dismiss"),
1879
- reason: z35.enum(DISMISS_REASONS).describe("Reason for dismissal: stale, intended, duplicate, or false_positive"),
1880
- 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"),
1881
1900
  create_knowledge: z35.boolean().optional().default(true).describe("Whether to create site knowledge entry (default: true)")
1882
1901
  }),
1883
1902
  handler: async (args) => {
@@ -2110,22 +2129,32 @@ var PROPOSAL_STATUSES = [
2110
2129
  ];
2111
2130
  var createProposalTool = {
2112
2131
  name: "create_proposal",
2113
- 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.",
2114
2133
  inputSchema: z37.object({
2115
2134
  issue_id: z37.string().optional().describe("The ID of the related issue (if any)"),
2116
2135
  improvement_run_id: z37.string().optional().describe("The ID of the improvement run creating this proposal"),
2117
- title: z37.string().describe("Short user-friendly title describing the issue from the user's perspective (not technical). Example: 'Checkout button appears unresponsive' instead of 'Dead clicks on .checkout-btn'"),
2118
- diagnosis: z37.string().describe("Detailed analysis of the root cause of the issue"),
2119
- 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"),
2120
2139
  affected_files: z37.array(z37.string()).optional().describe("List of files that likely need to be modified"),
2121
- confidence: z37.number().min(0).max(1).describe("Confidence level in the fix (0-1). Use <0.7 when uncertain."),
2122
- 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"),
2123
2142
  element_selector: z37.string().optional().describe("CSS selector of the affected element (if applicable)"),
2124
- category: z37.enum(ISSUE_CATEGORIES2).describe("Category of the issue being addressed"),
2125
- 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")
2126
2155
  }),
2127
2156
  handler: async (args) => {
2128
- 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;
2129
2158
  if (!isApiConfigured()) {
2130
2159
  return apiNotConfiguredResult();
2131
2160
  }
@@ -2140,7 +2169,8 @@ var createProposalTool = {
2140
2169
  page_path,
2141
2170
  element_selector,
2142
2171
  category,
2143
- severity
2172
+ severity,
2173
+ execution_metadata
2144
2174
  });
2145
2175
  if (error) {
2146
2176
  return errorResult(error);
@@ -2226,11 +2256,11 @@ var resolveProposalTool = {
2226
2256
  };
2227
2257
  var dismissProposalTool = {
2228
2258
  name: "dismiss_proposal",
2229
- 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.",
2230
2260
  inputSchema: z37.object({
2231
- proposal_id: z37.string().describe("The ID of the proposal to dismiss"),
2232
- reason: z37.string().describe("Explanation of why the proposal is being dismissed"),
2233
- 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")
2234
2264
  }),
2235
2265
  handler: async (args) => {
2236
2266
  const { proposal_id, reason, member_id } = args;
@@ -2325,10 +2355,11 @@ async function gitlabRequest(endpoint, options = {}) {
2325
2355
  const ctx = getGitContext();
2326
2356
  const baseUrl = ctx.baseUrl || "https://gitlab.com";
2327
2357
  const url = `${baseUrl}/api/v4${endpoint}`;
2358
+ const authHeader = ctx.token.startsWith("glpat-") ? { "PRIVATE-TOKEN": ctx.token } : { Authorization: `Bearer ${ctx.token}` };
2328
2359
  return fetch(url, {
2329
2360
  ...options,
2330
2361
  headers: {
2331
- "PRIVATE-TOKEN": ctx.token,
2362
+ ...authHeader,
2332
2363
  "Content-Type": "application/json",
2333
2364
  ...options.headers
2334
2365
  }
@@ -2449,13 +2480,13 @@ var getFileContentTool = {
2449
2480
  };
2450
2481
  var updateFileTool = {
2451
2482
  name: "update_file",
2452
- 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.",
2453
2484
  inputSchema: z38.object({
2454
2485
  path: z38.string().describe("Path to the file in the repository"),
2455
2486
  content: z38.string().describe("New content for the file"),
2456
2487
  message: z38.string().describe("Commit message"),
2457
2488
  branch: z38.string().describe("Branch to commit to"),
2458
- 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)")
2459
2490
  }),
2460
2491
  handler: async (args) => {
2461
2492
  try {
@@ -2464,8 +2495,15 @@ var updateFileTool = {
2464
2495
  const content = args.content;
2465
2496
  const message = args.message;
2466
2497
  const branch = args.branch;
2467
- const sha = args.sha;
2498
+ let sha = args.sha;
2468
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
+ }
2469
2507
  const body = {
2470
2508
  message,
2471
2509
  content: Buffer.from(content).toString("base64"),
@@ -2493,7 +2531,15 @@ var updateFileTool = {
2493
2531
  } else {
2494
2532
  const encodedPath = encodeURIComponent(ctx.repoFullName);
2495
2533
  const encodedFilePath = encodeURIComponent(filePath);
2496
- 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
+ }
2497
2543
  const res = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}`, {
2498
2544
  method,
2499
2545
  body: JSON.stringify({
@@ -2935,6 +2981,604 @@ var EXPOSED_TOOL_NAMES = [
2935
2981
  var exposedTools = allTools.filter((t) => EXPOSED_TOOL_NAMES.includes(t.name));
2936
2982
  var hiddenTools = allTools.filter((t) => !EXPOSED_TOOL_NAMES.includes(t.name));
2937
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
+
2938
3582
  // src/tools/catalog/searchTools.ts
2939
3583
  import { z as z39 } from "zod";
2940
3584
  import { readFileSync } from "fs";
@@ -3086,16 +3730,108 @@ function registerSearchTools(server) {
3086
3730
  }
3087
3731
 
3088
3732
  // src/tools/catalog/callTool.ts
3733
+ import { z as z41 } from "zod";
3734
+
3735
+ // src/tools/memory.ts
3089
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
3090
3826
  var toolRegistry = /* @__PURE__ */ new Map();
3091
3827
  function registerToolHandler(name, handler) {
3092
3828
  toolRegistry.set(name, handler);
3093
3829
  }
3094
- var callToolSchema = z40.object({
3095
- tool_name: z40.string().describe(
3830
+ var callToolSchema = z41.object({
3831
+ tool_name: z41.string().describe(
3096
3832
  "The exact name of the tool to call (e.g., 'get_page_metrics', 'analyze_flow', 'compare_cohorts')"
3097
3833
  ),
3098
- arguments: z40.record(z40.string(), z40.unknown()).describe(
3834
+ arguments: z41.record(z41.string(), z41.unknown()).describe(
3099
3835
  "The arguments to pass to the tool as a JSON object. Check the tool description from search_tools for required parameters."
3100
3836
  )
3101
3837
  });
@@ -3103,7 +3839,7 @@ function registerCallTool(server) {
3103
3839
  server.registerTool(
3104
3840
  "call_tool",
3105
3841
  {
3106
- 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' } })",
3107
3843
  inputSchema: callToolSchema
3108
3844
  },
3109
3845
  async ({ tool_name, arguments: args }) => {
@@ -3142,7 +3878,9 @@ function registerCallTool(server) {
3142
3878
  };
3143
3879
  }
3144
3880
  try {
3145
- return await handler(args);
3881
+ const rawResult = await handler(args);
3882
+ const memory = getSharedMemoryStore();
3883
+ return wrapToolResult(tool_name, args, rawResult, memory);
3146
3884
  } catch (err) {
3147
3885
  const message = err instanceof Error ? err.message : String(err);
3148
3886
  return {
@@ -3163,138 +3901,6 @@ function registerCallTool(server) {
3163
3901
  );
3164
3902
  }
3165
3903
 
3166
- // src/tools/memory.ts
3167
- import { z as z41 } from "zod";
3168
- var memoryStore = /* @__PURE__ */ new Map();
3169
- function registerMemoryTools(server) {
3170
- server.registerTool(
3171
- "memory_save",
3172
- {
3173
- 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.",
3174
- inputSchema: z41.object({
3175
- key: z41.string().describe("Unique key to store the value under"),
3176
- value: z41.string().describe("Value to store (will be stored as string)"),
3177
- annotation: z41.string().optional().describe("Optional note about what this data represents")
3178
- })
3179
- },
3180
- async ({
3181
- key,
3182
- value,
3183
- annotation
3184
- }) => {
3185
- memoryStore.set(key, {
3186
- key,
3187
- value,
3188
- timestamp: Date.now(),
3189
- annotation
3190
- });
3191
- return {
3192
- content: [
3193
- {
3194
- type: "text",
3195
- text: JSON.stringify({ success: true, key, size: value.length })
3196
- }
3197
- ]
3198
- };
3199
- }
3200
- );
3201
- server.registerTool(
3202
- "memory_recall",
3203
- {
3204
- description: "Retrieve a value from working memory by key. Use this to access data you previously stored with memory_save.",
3205
- inputSchema: z41.object({
3206
- key: z41.string().describe("Key to retrieve")
3207
- })
3208
- },
3209
- async ({ key }) => {
3210
- const entry = memoryStore.get(key);
3211
- if (!entry) {
3212
- return {
3213
- content: [
3214
- {
3215
- type: "text",
3216
- text: JSON.stringify({
3217
- error: `Key "${key}" not found in memory`
3218
- })
3219
- }
3220
- ],
3221
- isError: true
3222
- };
3223
- }
3224
- return {
3225
- content: [
3226
- {
3227
- type: "text",
3228
- text: entry.value
3229
- }
3230
- ]
3231
- };
3232
- }
3233
- );
3234
- server.registerTool(
3235
- "memory_list",
3236
- {
3237
- description: "List all keys currently stored in working memory. Returns key names, sizes, timestamps, and annotations.",
3238
- inputSchema: z41.object({})
3239
- },
3240
- async () => {
3241
- const entries = [...memoryStore.values()].map((entry) => ({
3242
- key: entry.key,
3243
- size: entry.value.length,
3244
- timestamp: new Date(entry.timestamp).toISOString(),
3245
- annotation: entry.annotation
3246
- }));
3247
- return {
3248
- content: [
3249
- {
3250
- type: "text",
3251
- text: JSON.stringify({ count: entries.length, entries })
3252
- }
3253
- ]
3254
- };
3255
- }
3256
- );
3257
- server.registerTool(
3258
- "memory_delete",
3259
- {
3260
- description: "Delete a value from working memory by key.",
3261
- inputSchema: z41.object({
3262
- key: z41.string().describe("Key to delete")
3263
- })
3264
- },
3265
- async ({ key }) => {
3266
- const existed = memoryStore.delete(key);
3267
- return {
3268
- content: [
3269
- {
3270
- type: "text",
3271
- text: JSON.stringify({ success: true, deleted: existed })
3272
- }
3273
- ]
3274
- };
3275
- }
3276
- );
3277
- server.registerTool(
3278
- "memory_clear",
3279
- {
3280
- description: "Clear all values from working memory. Use with caution.",
3281
- inputSchema: z41.object({})
3282
- },
3283
- async () => {
3284
- const count = memoryStore.size;
3285
- memoryStore.clear();
3286
- return {
3287
- content: [
3288
- {
3289
- type: "text",
3290
- text: JSON.stringify({ success: true, cleared: count })
3291
- }
3292
- ]
3293
- };
3294
- }
3295
- );
3296
- }
3297
-
3298
3904
  // src/index.ts
3299
3905
  function registerToolWithServer(server, tool) {
3300
3906
  server.tool(
@@ -3329,6 +3935,29 @@ call_tool({ tool_name: "analyze_flow", arguments: { start_page: "/pricing", end_
3329
3935
  \u2192 Returns: flow analysis with paths, bottlenecks, friction scores
3330
3936
  \`\`\`
3331
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
+
3332
3961
  ## Always-Available Tools
3333
3962
 
3334
3963
  These tools are always visible (no search needed):
@@ -3336,7 +3965,7 @@ These tools are always visible (no search needed):
3336
3965
  - **run_full_diagnostic** \u2014 Quick site health overview (lightweight, fast)
3337
3966
  - **triage_sessions** \u2014 Find sessions needing attention
3338
3967
  - **get_upgrade_options** \u2014 Check available plan upgrades and features
3339
- - **memory_*** \u2014 Store/retrieve intermediate results
3968
+ - **memory_*** \u2014 Store/retrieve intermediate results (memory_recall supports where, select, limit, offset)
3340
3969
 
3341
3970
  ## Tier-Based Access
3342
3971
 
@@ -3381,7 +4010,7 @@ All behavioral scores are 0-1 where higher = more of that signal:
3381
4010
  - Backtrack hotspots suggest confusion on subsequent pages
3382
4011
  - Low confidence + high confusion = users don't understand what to do
3383
4012
  - High frustration + low confusion = users know what to do but can't (broken UI)
3384
- - 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
3385
4014
 
3386
4015
  ## Tool Limits
3387
4016