@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 +816 -187
- package/dist/tools/catalog/anthropicToolCatalog.json +204 -1
- package/dist/tools/catalog/toolCatalog.json +3656 -382
- package/package.json +1 -1
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.
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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("
|
|
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
|
|
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.
|
|
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("
|
|
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
|
|
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
|
|
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
|
|
2125
|
-
severity: z37.enum(ISSUE_SEVERITIES).describe("Severity
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
3095
|
-
tool_name:
|
|
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:
|
|
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
|
-
|
|
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
|
-
-
|
|
4013
|
+
- Large responses are auto-saved to memory - use \`memory_recall\` to query stored data
|
|
3385
4014
|
|
|
3386
4015
|
## Tool Limits
|
|
3387
4016
|
|