iflow-mcp_developermode-korea_reversecore-mcp 1.0.0__py3-none-any.whl
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.
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/METADATA +543 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/RECORD +79 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/WHEEL +5 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/entry_points.txt +2 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/licenses/LICENSE +21 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/top_level.txt +1 -0
- reversecore_mcp/__init__.py +9 -0
- reversecore_mcp/core/__init__.py +78 -0
- reversecore_mcp/core/audit.py +101 -0
- reversecore_mcp/core/binary_cache.py +138 -0
- reversecore_mcp/core/command_spec.py +357 -0
- reversecore_mcp/core/config.py +432 -0
- reversecore_mcp/core/container.py +288 -0
- reversecore_mcp/core/decorators.py +152 -0
- reversecore_mcp/core/error_formatting.py +93 -0
- reversecore_mcp/core/error_handling.py +142 -0
- reversecore_mcp/core/evidence.py +229 -0
- reversecore_mcp/core/exceptions.py +296 -0
- reversecore_mcp/core/execution.py +240 -0
- reversecore_mcp/core/ghidra.py +642 -0
- reversecore_mcp/core/ghidra_helper.py +481 -0
- reversecore_mcp/core/ghidra_manager.py +234 -0
- reversecore_mcp/core/json_utils.py +131 -0
- reversecore_mcp/core/loader.py +73 -0
- reversecore_mcp/core/logging_config.py +206 -0
- reversecore_mcp/core/memory.py +721 -0
- reversecore_mcp/core/metrics.py +198 -0
- reversecore_mcp/core/mitre_mapper.py +365 -0
- reversecore_mcp/core/plugin.py +45 -0
- reversecore_mcp/core/r2_helpers.py +404 -0
- reversecore_mcp/core/r2_pool.py +403 -0
- reversecore_mcp/core/report_generator.py +268 -0
- reversecore_mcp/core/resilience.py +252 -0
- reversecore_mcp/core/resource_manager.py +169 -0
- reversecore_mcp/core/result.py +132 -0
- reversecore_mcp/core/security.py +213 -0
- reversecore_mcp/core/validators.py +238 -0
- reversecore_mcp/dashboard/__init__.py +221 -0
- reversecore_mcp/prompts/__init__.py +56 -0
- reversecore_mcp/prompts/common.py +24 -0
- reversecore_mcp/prompts/game.py +280 -0
- reversecore_mcp/prompts/malware.py +1219 -0
- reversecore_mcp/prompts/report.py +150 -0
- reversecore_mcp/prompts/security.py +136 -0
- reversecore_mcp/resources.py +329 -0
- reversecore_mcp/server.py +727 -0
- reversecore_mcp/tools/__init__.py +49 -0
- reversecore_mcp/tools/analysis/__init__.py +74 -0
- reversecore_mcp/tools/analysis/capa_tools.py +215 -0
- reversecore_mcp/tools/analysis/die_tools.py +180 -0
- reversecore_mcp/tools/analysis/diff_tools.py +643 -0
- reversecore_mcp/tools/analysis/lief_tools.py +272 -0
- reversecore_mcp/tools/analysis/signature_tools.py +591 -0
- reversecore_mcp/tools/analysis/static_analysis.py +479 -0
- reversecore_mcp/tools/common/__init__.py +58 -0
- reversecore_mcp/tools/common/file_operations.py +352 -0
- reversecore_mcp/tools/common/memory_tools.py +516 -0
- reversecore_mcp/tools/common/patch_explainer.py +230 -0
- reversecore_mcp/tools/common/server_tools.py +115 -0
- reversecore_mcp/tools/ghidra/__init__.py +19 -0
- reversecore_mcp/tools/ghidra/decompilation.py +975 -0
- reversecore_mcp/tools/ghidra/ghidra_tools.py +1052 -0
- reversecore_mcp/tools/malware/__init__.py +61 -0
- reversecore_mcp/tools/malware/adaptive_vaccine.py +579 -0
- reversecore_mcp/tools/malware/dormant_detector.py +756 -0
- reversecore_mcp/tools/malware/ioc_tools.py +228 -0
- reversecore_mcp/tools/malware/vulnerability_hunter.py +519 -0
- reversecore_mcp/tools/malware/yara_tools.py +214 -0
- reversecore_mcp/tools/patch_explainer.py +19 -0
- reversecore_mcp/tools/radare2/__init__.py +13 -0
- reversecore_mcp/tools/radare2/r2_analysis.py +972 -0
- reversecore_mcp/tools/radare2/r2_session.py +376 -0
- reversecore_mcp/tools/radare2/radare2_mcp_tools.py +1183 -0
- reversecore_mcp/tools/report/__init__.py +4 -0
- reversecore_mcp/tools/report/email.py +82 -0
- reversecore_mcp/tools/report/report_mcp_tools.py +344 -0
- reversecore_mcp/tools/report/report_tools.py +1076 -0
- reversecore_mcp/tools/report/session.py +194 -0
- reversecore_mcp/tools/report_tools.py +11 -0
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Memory Tools for AI Long-term Memory.
|
|
3
|
+
|
|
4
|
+
This module provides MCP-compatible tools for managing AI analysis memories,
|
|
5
|
+
enabling multi-session memory persistence and cross-project knowledge transfer.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import hashlib
|
|
11
|
+
import time
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from fastmcp import FastMCP
|
|
16
|
+
|
|
17
|
+
from reversecore_mcp.core.logging_config import get_logger
|
|
18
|
+
from reversecore_mcp.core.memory import get_memory_store, initialize_memory_store
|
|
19
|
+
from reversecore_mcp.core.plugin import Plugin
|
|
20
|
+
|
|
21
|
+
logger = get_logger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class MemoryToolsPlugin(Plugin):
|
|
25
|
+
"""Plugin for AI memory management tools."""
|
|
26
|
+
|
|
27
|
+
name = "memory_tools"
|
|
28
|
+
description = "AI long-term memory management for analysis sessions"
|
|
29
|
+
|
|
30
|
+
def register(self, mcp: FastMCP) -> None:
|
|
31
|
+
"""Register all memory tools with the MCP server."""
|
|
32
|
+
|
|
33
|
+
@mcp.tool()
|
|
34
|
+
async def create_analysis_session(
|
|
35
|
+
name: str,
|
|
36
|
+
binary_name: str | None = None,
|
|
37
|
+
binary_path: str | None = None,
|
|
38
|
+
) -> dict[str, Any]:
|
|
39
|
+
"""
|
|
40
|
+
Create a new analysis session to store memories.
|
|
41
|
+
|
|
42
|
+
Use this when starting a new reverse engineering analysis.
|
|
43
|
+
The session name should be descriptive and follow a template format
|
|
44
|
+
like 'malware_analysis_2024_001' or 'game_cheat_detection'.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
name: Template name for the session (e.g., 'malware_sample_001')
|
|
48
|
+
binary_name: Name of the binary being analyzed (optional)
|
|
49
|
+
binary_path: Path to binary for automatic hash calculation (optional)
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Session information including ID for future reference
|
|
53
|
+
"""
|
|
54
|
+
store = get_memory_store()
|
|
55
|
+
await store.initialize()
|
|
56
|
+
|
|
57
|
+
# Calculate hash if path provided
|
|
58
|
+
binary_hash = None
|
|
59
|
+
if binary_path:
|
|
60
|
+
try:
|
|
61
|
+
path = Path(binary_path)
|
|
62
|
+
if path.exists():
|
|
63
|
+
binary_hash = hashlib.sha256(path.read_bytes()).hexdigest()
|
|
64
|
+
if not binary_name:
|
|
65
|
+
binary_name = path.name
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.warning(f"Could not hash binary: {e}")
|
|
68
|
+
|
|
69
|
+
session_id = await store.create_session(
|
|
70
|
+
name=name,
|
|
71
|
+
binary_name=binary_name,
|
|
72
|
+
binary_hash=binary_hash,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
"status": "success",
|
|
77
|
+
"session_id": session_id,
|
|
78
|
+
"name": name,
|
|
79
|
+
"binary_name": binary_name,
|
|
80
|
+
"binary_hash": binary_hash,
|
|
81
|
+
"message": f"Analysis session '{name}' created. Use this session_id to save memories.",
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@mcp.tool()
|
|
85
|
+
async def save_analysis_memory(
|
|
86
|
+
session_id: str,
|
|
87
|
+
memory_type: str,
|
|
88
|
+
content: str,
|
|
89
|
+
category: str | None = None,
|
|
90
|
+
user_prompt: str | None = None,
|
|
91
|
+
importance: int = 5,
|
|
92
|
+
) -> dict[str, Any]:
|
|
93
|
+
"""
|
|
94
|
+
Save important information to long-term memory.
|
|
95
|
+
|
|
96
|
+
Use this to remember:
|
|
97
|
+
- Function addresses and their purposes
|
|
98
|
+
- Vulnerability patterns discovered
|
|
99
|
+
- API call sequences
|
|
100
|
+
- User instructions and preferences
|
|
101
|
+
- Interesting strings or structures
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
session_id: Session ID from create_analysis_session
|
|
105
|
+
memory_type: Type of memory:
|
|
106
|
+
- 'finding': Analysis discoveries
|
|
107
|
+
- 'pattern': Code/behavior patterns
|
|
108
|
+
- 'instruction': User preferences/instructions
|
|
109
|
+
- 'context': General context information
|
|
110
|
+
category: Optional category:
|
|
111
|
+
- 'function': Function-related info
|
|
112
|
+
- 'vulnerability': Security issues
|
|
113
|
+
- 'string': Important strings
|
|
114
|
+
- 'structure': Data structures
|
|
115
|
+
- 'api': API usage patterns
|
|
116
|
+
content: The actual content to remember (text or JSON string)
|
|
117
|
+
user_prompt: The user's prompt when this was discovered (optional)
|
|
118
|
+
importance: Importance level 1-10 (default 5, higher = more important)
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Confirmation with memory ID
|
|
122
|
+
"""
|
|
123
|
+
store = get_memory_store()
|
|
124
|
+
await store.initialize()
|
|
125
|
+
|
|
126
|
+
memory_id = await store.save_memory(
|
|
127
|
+
session_id=session_id,
|
|
128
|
+
memory_type=memory_type,
|
|
129
|
+
content=content,
|
|
130
|
+
category=category,
|
|
131
|
+
user_prompt=user_prompt,
|
|
132
|
+
importance=importance,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
"status": "success",
|
|
137
|
+
"memory_id": memory_id,
|
|
138
|
+
"message": f"Memory saved (ID: {memory_id}, importance: {importance}/10)",
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
@mcp.tool()
|
|
142
|
+
async def recall_analysis_memory(
|
|
143
|
+
query: str,
|
|
144
|
+
session_id: str | None = None,
|
|
145
|
+
memory_type: str | None = None,
|
|
146
|
+
limit: int = 10,
|
|
147
|
+
) -> dict[str, Any]:
|
|
148
|
+
"""
|
|
149
|
+
Search and recall memories from past analyses.
|
|
150
|
+
|
|
151
|
+
Use this when you need to remember something from earlier,
|
|
152
|
+
or when the user asks about previous findings.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
query: Search query (keywords or phrases)
|
|
156
|
+
session_id: Limit search to specific session (optional)
|
|
157
|
+
memory_type: Filter by type ('finding', 'pattern', 'instruction', 'context')
|
|
158
|
+
limit: Maximum number of results (default 10)
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
List of matching memories with context
|
|
162
|
+
"""
|
|
163
|
+
store = get_memory_store()
|
|
164
|
+
await store.initialize()
|
|
165
|
+
|
|
166
|
+
memories = await store.recall_memories(
|
|
167
|
+
query=query,
|
|
168
|
+
session_id=session_id,
|
|
169
|
+
memory_type=memory_type,
|
|
170
|
+
limit=limit,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
"status": "success",
|
|
175
|
+
"count": len(memories),
|
|
176
|
+
"memories": memories,
|
|
177
|
+
"message": f"Found {len(memories)} relevant memories",
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
@mcp.tool()
|
|
181
|
+
async def list_analysis_sessions(
|
|
182
|
+
status: str | None = None,
|
|
183
|
+
limit: int = 20,
|
|
184
|
+
) -> dict[str, Any]:
|
|
185
|
+
"""
|
|
186
|
+
List all analysis sessions with timestamps and status.
|
|
187
|
+
|
|
188
|
+
Use this to see what analyses have been done before,
|
|
189
|
+
or to find a session to resume.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
status: Filter by status ('in_progress', 'completed', 'paused')
|
|
193
|
+
limit: Maximum number of sessions to return
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
List of sessions with metadata
|
|
197
|
+
"""
|
|
198
|
+
store = get_memory_store()
|
|
199
|
+
await store.initialize()
|
|
200
|
+
|
|
201
|
+
sessions = await store.list_sessions(status=status, limit=limit)
|
|
202
|
+
|
|
203
|
+
# Format timestamps for readability
|
|
204
|
+
for session in sessions:
|
|
205
|
+
if session.get("analysis_duration_seconds"):
|
|
206
|
+
duration = session["analysis_duration_seconds"]
|
|
207
|
+
hours = int(duration // 3600)
|
|
208
|
+
minutes = int((duration % 3600) // 60)
|
|
209
|
+
session["analysis_duration_formatted"] = f"{hours}h {minutes}m"
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
"status": "success",
|
|
213
|
+
"count": len(sessions),
|
|
214
|
+
"sessions": sessions,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
@mcp.tool()
|
|
218
|
+
async def get_session_detail(
|
|
219
|
+
session_id: str,
|
|
220
|
+
) -> dict[str, Any]:
|
|
221
|
+
"""
|
|
222
|
+
Get complete details and context for a specific session.
|
|
223
|
+
|
|
224
|
+
Use this when resuming an analysis or reviewing past work.
|
|
225
|
+
Returns all memories and patterns associated with the session.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
session_id: Session ID to retrieve
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
Full session context including all memories and patterns
|
|
232
|
+
"""
|
|
233
|
+
store = get_memory_store()
|
|
234
|
+
await store.initialize()
|
|
235
|
+
|
|
236
|
+
context = await store.get_session_context(session_id)
|
|
237
|
+
|
|
238
|
+
if not context:
|
|
239
|
+
return {
|
|
240
|
+
"status": "error",
|
|
241
|
+
"message": f"Session '{session_id}' not found",
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
"status": "success",
|
|
246
|
+
"session": context["session"],
|
|
247
|
+
"memories": context["memories"],
|
|
248
|
+
"patterns": context["patterns"],
|
|
249
|
+
"summary": {
|
|
250
|
+
"memory_count": context["memory_count"],
|
|
251
|
+
"pattern_count": context["pattern_count"],
|
|
252
|
+
},
|
|
253
|
+
"message": "Session context retrieved. Use this to resume analysis.",
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
@mcp.tool()
|
|
257
|
+
async def resume_session(
|
|
258
|
+
session_id: str | None = None,
|
|
259
|
+
binary_name: str | None = None,
|
|
260
|
+
) -> dict[str, Any]:
|
|
261
|
+
"""
|
|
262
|
+
Resume a previous analysis session with full context restoration.
|
|
263
|
+
|
|
264
|
+
Use this when the user says "continue where we left off" or
|
|
265
|
+
"resume yesterday's analysis".
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
session_id: Specific session ID to resume (optional)
|
|
269
|
+
binary_name: Find latest session for this binary (optional)
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
Full session context for resumption
|
|
273
|
+
|
|
274
|
+
Note:
|
|
275
|
+
If neither argument provided, resumes the most recent session.
|
|
276
|
+
"""
|
|
277
|
+
store = get_memory_store()
|
|
278
|
+
await store.initialize()
|
|
279
|
+
|
|
280
|
+
# Find session to resume
|
|
281
|
+
if session_id:
|
|
282
|
+
session = await store.get_session(session_id)
|
|
283
|
+
else:
|
|
284
|
+
session = await store.find_latest_session(binary_name)
|
|
285
|
+
|
|
286
|
+
if not session:
|
|
287
|
+
return {
|
|
288
|
+
"status": "error",
|
|
289
|
+
"message": "No session found to resume. Start a new session with create_analysis_session.",
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
# Update session status
|
|
293
|
+
await store.update_session(session["id"], status="in_progress")
|
|
294
|
+
|
|
295
|
+
# Get full context
|
|
296
|
+
context = await store.get_session_context(session["id"])
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
"status": "success",
|
|
300
|
+
"message": f"Resuming session '{session['name']}' with {context['memory_count']} memories",
|
|
301
|
+
"session": context["session"],
|
|
302
|
+
"memories": context["memories"],
|
|
303
|
+
"patterns": context["patterns"],
|
|
304
|
+
"instructions": [
|
|
305
|
+
m for m in context["memories"] if m.get("memory_type") == "instruction"
|
|
306
|
+
],
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
@mcp.tool()
|
|
310
|
+
async def complete_session(
|
|
311
|
+
session_id: str,
|
|
312
|
+
summary: str,
|
|
313
|
+
) -> dict[str, Any]:
|
|
314
|
+
"""
|
|
315
|
+
Mark an analysis session as completed with a summary.
|
|
316
|
+
|
|
317
|
+
Use this when finishing an analysis to save a summary
|
|
318
|
+
for future reference.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
session_id: Session ID to complete
|
|
322
|
+
summary: AI-generated summary of the analysis findings
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
Confirmation of completion
|
|
326
|
+
"""
|
|
327
|
+
store = get_memory_store()
|
|
328
|
+
await store.initialize()
|
|
329
|
+
|
|
330
|
+
success = await store.update_session(
|
|
331
|
+
session_id=session_id,
|
|
332
|
+
status="completed",
|
|
333
|
+
summary=summary,
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
if not success:
|
|
337
|
+
return {
|
|
338
|
+
"status": "error",
|
|
339
|
+
"message": f"Session '{session_id}' not found",
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
"status": "success",
|
|
344
|
+
"message": "Session marked as completed",
|
|
345
|
+
"summary": summary,
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
@mcp.tool()
|
|
349
|
+
async def save_pattern(
|
|
350
|
+
session_id: str,
|
|
351
|
+
pattern_type: str,
|
|
352
|
+
pattern_signature: str,
|
|
353
|
+
description: str | None = None,
|
|
354
|
+
) -> dict[str, Any]:
|
|
355
|
+
"""
|
|
356
|
+
Save a code/behavior pattern for cross-session similarity search.
|
|
357
|
+
|
|
358
|
+
Use this when you discover a notable pattern that might appear
|
|
359
|
+
in other samples. This enables "Hey, this looks similar to before!"
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
session_id: Current session ID
|
|
363
|
+
pattern_type: Type of pattern:
|
|
364
|
+
- 'api_sequence': Sequence of API calls
|
|
365
|
+
- 'code_pattern': Assembly/code pattern
|
|
366
|
+
- 'behavior': Behavioral pattern
|
|
367
|
+
pattern_signature: Normalized pattern signature for matching
|
|
368
|
+
Example: "VirtualAlloc,WriteProcessMemory,CreateRemoteThread"
|
|
369
|
+
description: Human-readable description of the pattern
|
|
370
|
+
|
|
371
|
+
Returns:
|
|
372
|
+
Confirmation with pattern ID
|
|
373
|
+
"""
|
|
374
|
+
store = get_memory_store()
|
|
375
|
+
await store.initialize()
|
|
376
|
+
|
|
377
|
+
pattern_id = await store.save_pattern(
|
|
378
|
+
session_id=session_id,
|
|
379
|
+
pattern_type=pattern_type,
|
|
380
|
+
pattern_signature=pattern_signature,
|
|
381
|
+
description=description,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
return {
|
|
385
|
+
"status": "success",
|
|
386
|
+
"pattern_id": pattern_id,
|
|
387
|
+
"message": f"Pattern saved for cross-session matching",
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
@mcp.tool()
|
|
391
|
+
async def find_similar_patterns(
|
|
392
|
+
pattern_signature: str,
|
|
393
|
+
pattern_type: str | None = None,
|
|
394
|
+
current_session_id: str | None = None,
|
|
395
|
+
limit: int = 10,
|
|
396
|
+
) -> dict[str, Any]:
|
|
397
|
+
"""
|
|
398
|
+
Find similar patterns from previous analyses.
|
|
399
|
+
|
|
400
|
+
Use this to check if current findings match anything
|
|
401
|
+
from past analyses. Enables knowledge transfer across projects.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
pattern_signature: Pattern to search for
|
|
405
|
+
pattern_type: Limit to specific type (optional)
|
|
406
|
+
current_session_id: Exclude current session from results
|
|
407
|
+
limit: Maximum results
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
List of similar patterns with session context
|
|
411
|
+
"""
|
|
412
|
+
store = get_memory_store()
|
|
413
|
+
await store.initialize()
|
|
414
|
+
|
|
415
|
+
similar = await store.find_similar_patterns(
|
|
416
|
+
pattern_signature=pattern_signature,
|
|
417
|
+
pattern_type=pattern_type,
|
|
418
|
+
exclude_session=current_session_id,
|
|
419
|
+
limit=limit,
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
if similar:
|
|
423
|
+
message = f"Found {len(similar)} similar patterns from previous analyses!"
|
|
424
|
+
else:
|
|
425
|
+
message = "No similar patterns found in previous analyses."
|
|
426
|
+
|
|
427
|
+
return {
|
|
428
|
+
"status": "success",
|
|
429
|
+
"count": len(similar),
|
|
430
|
+
"similar_patterns": similar,
|
|
431
|
+
"message": message,
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
@mcp.tool()
|
|
435
|
+
async def get_relevant_context(
|
|
436
|
+
description: str,
|
|
437
|
+
current_session_id: str | None = None,
|
|
438
|
+
limit: int = 5,
|
|
439
|
+
) -> dict[str, Any]:
|
|
440
|
+
"""
|
|
441
|
+
Get relevant context from past analyses for current work.
|
|
442
|
+
|
|
443
|
+
Use this proactively when analyzing something new to check
|
|
444
|
+
if there's relevant knowledge from previous sessions.
|
|
445
|
+
|
|
446
|
+
Args:
|
|
447
|
+
description: Description of what you're currently analyzing
|
|
448
|
+
current_session_id: Current session to exclude
|
|
449
|
+
limit: Maximum relevant items
|
|
450
|
+
|
|
451
|
+
Returns:
|
|
452
|
+
Relevant memories from past sessions
|
|
453
|
+
"""
|
|
454
|
+
store = get_memory_store()
|
|
455
|
+
await store.initialize()
|
|
456
|
+
|
|
457
|
+
relevant = await store.get_relevant_context(
|
|
458
|
+
current_analysis=description,
|
|
459
|
+
current_session_id=current_session_id,
|
|
460
|
+
limit=limit,
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
if relevant:
|
|
464
|
+
message = f"Found {len(relevant)} relevant memories from past analyses"
|
|
465
|
+
else:
|
|
466
|
+
message = "No relevant past context found"
|
|
467
|
+
|
|
468
|
+
return {
|
|
469
|
+
"status": "success",
|
|
470
|
+
"count": len(relevant),
|
|
471
|
+
"relevant_memories": relevant,
|
|
472
|
+
"message": message,
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
@mcp.tool()
|
|
476
|
+
async def update_analysis_time(
|
|
477
|
+
session_id: str,
|
|
478
|
+
duration_seconds: float,
|
|
479
|
+
) -> dict[str, Any]:
|
|
480
|
+
"""
|
|
481
|
+
Update the cumulative analysis time for a session.
|
|
482
|
+
|
|
483
|
+
Call this periodically to track how long an analysis takes.
|
|
484
|
+
|
|
485
|
+
Args:
|
|
486
|
+
session_id: Session ID
|
|
487
|
+
duration_seconds: Additional time to add (in seconds)
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
Confirmation
|
|
491
|
+
"""
|
|
492
|
+
store = get_memory_store()
|
|
493
|
+
await store.initialize()
|
|
494
|
+
|
|
495
|
+
success = await store.update_session(
|
|
496
|
+
session_id=session_id,
|
|
497
|
+
add_duration=duration_seconds,
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
return {
|
|
501
|
+
"status": "success" if success else "error",
|
|
502
|
+
"message": f"Added {duration_seconds:.1f}s to analysis time",
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
logger.info(f"Registered {self.name} plugin with 11 tools")
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def register_memory_tools(mcp: FastMCP) -> None:
|
|
509
|
+
"""
|
|
510
|
+
Register memory tools with an MCP server instance.
|
|
511
|
+
|
|
512
|
+
Args:
|
|
513
|
+
mcp: FastMCP server instance
|
|
514
|
+
"""
|
|
515
|
+
plugin = MemoryToolsPlugin()
|
|
516
|
+
plugin.register(mcp)
|