hanzo-mcp 0.6.13__py3-none-any.whl → 0.7.1__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.
Potentially problematic release.
This version of hanzo-mcp might be problematic. Click here for more details.
- hanzo_mcp/analytics/__init__.py +5 -0
- hanzo_mcp/analytics/posthog_analytics.py +364 -0
- hanzo_mcp/cli.py +3 -3
- hanzo_mcp/cli_enhanced.py +3 -3
- hanzo_mcp/config/settings.py +1 -1
- hanzo_mcp/config/tool_config.py +18 -4
- hanzo_mcp/server.py +34 -1
- hanzo_mcp/tools/__init__.py +65 -2
- hanzo_mcp/tools/agent/__init__.py +84 -3
- hanzo_mcp/tools/agent/agent_tool.py +102 -4
- hanzo_mcp/tools/agent/agent_tool_v2.py +492 -0
- hanzo_mcp/tools/agent/clarification_protocol.py +220 -0
- hanzo_mcp/tools/agent/clarification_tool.py +68 -0
- hanzo_mcp/tools/agent/claude_cli_tool.py +125 -0
- hanzo_mcp/tools/agent/claude_desktop_auth.py +508 -0
- hanzo_mcp/tools/agent/cli_agent_base.py +191 -0
- hanzo_mcp/tools/agent/code_auth.py +436 -0
- hanzo_mcp/tools/agent/code_auth_tool.py +194 -0
- hanzo_mcp/tools/agent/codex_cli_tool.py +123 -0
- hanzo_mcp/tools/agent/critic_tool.py +376 -0
- hanzo_mcp/tools/agent/gemini_cli_tool.py +128 -0
- hanzo_mcp/tools/agent/grok_cli_tool.py +128 -0
- hanzo_mcp/tools/agent/iching_tool.py +380 -0
- hanzo_mcp/tools/agent/network_tool.py +273 -0
- hanzo_mcp/tools/agent/prompt.py +62 -20
- hanzo_mcp/tools/agent/review_tool.py +433 -0
- hanzo_mcp/tools/agent/swarm_tool.py +535 -0
- hanzo_mcp/tools/agent/swarm_tool_v2.py +654 -0
- hanzo_mcp/tools/common/base.py +1 -0
- hanzo_mcp/tools/common/batch_tool.py +102 -10
- hanzo_mcp/tools/common/fastmcp_pagination.py +369 -0
- hanzo_mcp/tools/common/forgiving_edit.py +243 -0
- hanzo_mcp/tools/common/paginated_base.py +230 -0
- hanzo_mcp/tools/common/paginated_response.py +307 -0
- hanzo_mcp/tools/common/pagination.py +226 -0
- hanzo_mcp/tools/common/tool_list.py +3 -0
- hanzo_mcp/tools/common/truncate.py +101 -0
- hanzo_mcp/tools/filesystem/__init__.py +29 -0
- hanzo_mcp/tools/filesystem/ast_multi_edit.py +562 -0
- hanzo_mcp/tools/filesystem/directory_tree_paginated.py +338 -0
- hanzo_mcp/tools/lsp/__init__.py +5 -0
- hanzo_mcp/tools/lsp/lsp_tool.py +512 -0
- hanzo_mcp/tools/memory/__init__.py +76 -0
- hanzo_mcp/tools/memory/knowledge_tools.py +518 -0
- hanzo_mcp/tools/memory/memory_tools.py +456 -0
- hanzo_mcp/tools/search/__init__.py +6 -0
- hanzo_mcp/tools/search/find_tool.py +581 -0
- hanzo_mcp/tools/search/unified_search.py +953 -0
- hanzo_mcp/tools/shell/__init__.py +5 -0
- hanzo_mcp/tools/shell/auto_background.py +203 -0
- hanzo_mcp/tools/shell/base_process.py +53 -27
- hanzo_mcp/tools/shell/bash_tool.py +17 -33
- hanzo_mcp/tools/shell/npx_tool.py +15 -32
- hanzo_mcp/tools/shell/streaming_command.py +594 -0
- hanzo_mcp/tools/shell/uvx_tool.py +15 -32
- hanzo_mcp/types.py +23 -0
- {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.1.dist-info}/METADATA +229 -71
- {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.1.dist-info}/RECORD +61 -24
- hanzo_mcp-0.6.13.dist-info/licenses/LICENSE +0 -21
- {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.1.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.1.dist-info}/entry_points.txt +0 -0
- {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
"""Knowledge base and fact management tools for MCP.
|
|
2
|
+
|
|
3
|
+
These tools use the hanzo-memory package to manage knowledge bases and facts,
|
|
4
|
+
supporting hierarchical organization (session, project, global).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Dict, List, Optional, override, final
|
|
8
|
+
from mcp.server import FastMCP
|
|
9
|
+
from mcp.server.fastmcp import Context as MCPContext
|
|
10
|
+
|
|
11
|
+
from hanzo_mcp.tools.common.base import BaseTool
|
|
12
|
+
from hanzo_mcp.tools.common.context import create_tool_context
|
|
13
|
+
|
|
14
|
+
# Import from hanzo-memory package
|
|
15
|
+
try:
|
|
16
|
+
from hanzo_memory.services.memory import MemoryService, get_memory_service
|
|
17
|
+
from hanzo_memory.models.memory import Memory, MemoryWithScore
|
|
18
|
+
from hanzo_memory.models.knowledge import KnowledgeBase, Fact, FactCreate
|
|
19
|
+
KNOWLEDGE_AVAILABLE = True
|
|
20
|
+
except ImportError:
|
|
21
|
+
KNOWLEDGE_AVAILABLE = False
|
|
22
|
+
raise ImportError(
|
|
23
|
+
"hanzo-memory package is required for knowledge tools. "
|
|
24
|
+
"Install it from ~/work/hanzo/ide/pkg/memory"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class KnowledgeToolBase(BaseTool):
|
|
29
|
+
"""Base class for knowledge tools using hanzo-memory package."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, user_id: str = "default", project_id: str = "default", **kwargs):
|
|
32
|
+
"""Initialize knowledge tool.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
user_id: User ID for knowledge operations
|
|
36
|
+
project_id: Project ID for knowledge operations
|
|
37
|
+
**kwargs: Additional configuration
|
|
38
|
+
"""
|
|
39
|
+
self.user_id = user_id
|
|
40
|
+
self.project_id = project_id
|
|
41
|
+
# Use the memory service for knowledge management
|
|
42
|
+
self.service = get_memory_service()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@final
|
|
46
|
+
class RecallFactsTool(KnowledgeToolBase):
|
|
47
|
+
"""Tool for recalling facts from knowledge bases."""
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
@override
|
|
51
|
+
def name(self) -> str:
|
|
52
|
+
"""Get the tool name."""
|
|
53
|
+
return "recall_facts"
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
@override
|
|
57
|
+
def description(self) -> str:
|
|
58
|
+
"""Get the tool description."""
|
|
59
|
+
return """Recall facts from knowledge bases relevant to queries.
|
|
60
|
+
|
|
61
|
+
Facts are structured pieces of information stored in knowledge bases.
|
|
62
|
+
Supports different scopes: session, project, or global.
|
|
63
|
+
|
|
64
|
+
Usage:
|
|
65
|
+
recall_facts(queries=["Python best practices"], kb_name="coding_standards")
|
|
66
|
+
recall_facts(queries=["API endpoints"], scope="project")
|
|
67
|
+
recall_facts(queries=["company policies"], scope="global", limit=5)
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
@override
|
|
71
|
+
async def call(
|
|
72
|
+
self,
|
|
73
|
+
ctx: MCPContext,
|
|
74
|
+
queries: List[str],
|
|
75
|
+
kb_name: Optional[str] = None,
|
|
76
|
+
scope: str = "project",
|
|
77
|
+
limit: int = 10
|
|
78
|
+
) -> str:
|
|
79
|
+
"""Recall facts matching queries.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
ctx: MCP context
|
|
83
|
+
queries: Search queries
|
|
84
|
+
kb_name: Optional knowledge base name to search in
|
|
85
|
+
scope: Scope level (session, project, global)
|
|
86
|
+
limit: Max results per query
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Formatted fact results
|
|
90
|
+
"""
|
|
91
|
+
tool_ctx = create_tool_context(ctx)
|
|
92
|
+
await tool_ctx.set_tool_info(self.name)
|
|
93
|
+
|
|
94
|
+
await tool_ctx.info(f"Searching for facts in scope: {scope}")
|
|
95
|
+
|
|
96
|
+
# Determine the appropriate IDs based on scope
|
|
97
|
+
if scope == "global":
|
|
98
|
+
user_id = "global"
|
|
99
|
+
project_id = "global"
|
|
100
|
+
elif scope == "session":
|
|
101
|
+
# Session scope uses a session-specific ID
|
|
102
|
+
user_id = f"session_{self.user_id}"
|
|
103
|
+
project_id = self.project_id
|
|
104
|
+
else:
|
|
105
|
+
user_id = self.user_id
|
|
106
|
+
project_id = self.project_id
|
|
107
|
+
|
|
108
|
+
all_facts = []
|
|
109
|
+
for query in queries:
|
|
110
|
+
# Search for facts using memory service with fact metadata
|
|
111
|
+
search_query = f"fact: {query}"
|
|
112
|
+
if kb_name:
|
|
113
|
+
search_query = f"kb:{kb_name} {search_query}"
|
|
114
|
+
|
|
115
|
+
memories = self.service.search_memories(
|
|
116
|
+
user_id=user_id,
|
|
117
|
+
query=search_query,
|
|
118
|
+
project_id=project_id,
|
|
119
|
+
limit=limit
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Filter for fact-type memories
|
|
123
|
+
for memory in memories:
|
|
124
|
+
if memory.metadata and memory.metadata.get("type") == "fact":
|
|
125
|
+
all_facts.append(memory)
|
|
126
|
+
|
|
127
|
+
# Deduplicate by memory ID
|
|
128
|
+
seen = set()
|
|
129
|
+
unique_facts = []
|
|
130
|
+
for fact in all_facts:
|
|
131
|
+
if fact.memory_id not in seen:
|
|
132
|
+
seen.add(fact.memory_id)
|
|
133
|
+
unique_facts.append(fact)
|
|
134
|
+
|
|
135
|
+
if not unique_facts:
|
|
136
|
+
return "No relevant facts found."
|
|
137
|
+
|
|
138
|
+
# Format results
|
|
139
|
+
formatted = [f"Found {len(unique_facts)} relevant facts:\n"]
|
|
140
|
+
for i, fact in enumerate(unique_facts, 1):
|
|
141
|
+
kb_info = ""
|
|
142
|
+
if fact.metadata and fact.metadata.get("kb_name"):
|
|
143
|
+
kb_info = f" (KB: {fact.metadata['kb_name']})"
|
|
144
|
+
formatted.append(f"{i}. {fact.content}{kb_info}")
|
|
145
|
+
if fact.metadata and len(fact.metadata) > 2: # More than just type and kb_name
|
|
146
|
+
# Show other metadata
|
|
147
|
+
other_meta = {k: v for k, v in fact.metadata.items() if k not in ["type", "kb_name"]}
|
|
148
|
+
if other_meta:
|
|
149
|
+
formatted.append(f" Metadata: {other_meta}")
|
|
150
|
+
|
|
151
|
+
return "\n".join(formatted)
|
|
152
|
+
|
|
153
|
+
@override
|
|
154
|
+
def register(self, mcp_server: FastMCP) -> None:
|
|
155
|
+
"""Register this tool with the MCP server."""
|
|
156
|
+
tool_self = self
|
|
157
|
+
|
|
158
|
+
@mcp_server.tool(name=self.name, description=self.description)
|
|
159
|
+
async def recall_facts(
|
|
160
|
+
ctx: MCPContext,
|
|
161
|
+
queries: List[str],
|
|
162
|
+
kb_name: Optional[str] = None,
|
|
163
|
+
scope: str = "project",
|
|
164
|
+
limit: int = 10
|
|
165
|
+
) -> str:
|
|
166
|
+
return await tool_self.call(ctx, queries=queries, kb_name=kb_name, scope=scope, limit=limit)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@final
|
|
170
|
+
class StoreFactsTool(KnowledgeToolBase):
|
|
171
|
+
"""Tool for storing facts in knowledge bases."""
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
@override
|
|
175
|
+
def name(self) -> str:
|
|
176
|
+
"""Get the tool name."""
|
|
177
|
+
return "store_facts"
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
@override
|
|
181
|
+
def description(self) -> str:
|
|
182
|
+
"""Get the tool description."""
|
|
183
|
+
return """Store new facts in knowledge bases.
|
|
184
|
+
|
|
185
|
+
Facts can be stored at different scopes (session, project, global) and
|
|
186
|
+
organized into knowledge bases for better categorization.
|
|
187
|
+
|
|
188
|
+
Usage:
|
|
189
|
+
store_facts(facts=["Python uses indentation for blocks"], kb_name="python_basics")
|
|
190
|
+
store_facts(facts=["API rate limit: 100/hour"], scope="project", kb_name="api_docs")
|
|
191
|
+
store_facts(facts=["Company founded in 2020"], scope="global", kb_name="company_info")
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
@override
|
|
195
|
+
async def call(
|
|
196
|
+
self,
|
|
197
|
+
ctx: MCPContext,
|
|
198
|
+
facts: List[str],
|
|
199
|
+
kb_name: str = "general",
|
|
200
|
+
scope: str = "project",
|
|
201
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
202
|
+
) -> str:
|
|
203
|
+
"""Store new facts.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
ctx: MCP context
|
|
207
|
+
facts: Facts to store
|
|
208
|
+
kb_name: Knowledge base name
|
|
209
|
+
scope: Scope level (session, project, global)
|
|
210
|
+
metadata: Optional metadata for all facts
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
Success message
|
|
214
|
+
"""
|
|
215
|
+
tool_ctx = create_tool_context(ctx)
|
|
216
|
+
await tool_ctx.set_tool_info(self.name)
|
|
217
|
+
|
|
218
|
+
await tool_ctx.info(f"Storing {len(facts)} facts in {kb_name} (scope: {scope})")
|
|
219
|
+
|
|
220
|
+
# Determine the appropriate IDs based on scope
|
|
221
|
+
if scope == "global":
|
|
222
|
+
user_id = "global"
|
|
223
|
+
project_id = "global"
|
|
224
|
+
elif scope == "session":
|
|
225
|
+
user_id = f"session_{self.user_id}"
|
|
226
|
+
project_id = self.project_id
|
|
227
|
+
else:
|
|
228
|
+
user_id = self.user_id
|
|
229
|
+
project_id = self.project_id
|
|
230
|
+
|
|
231
|
+
created_facts = []
|
|
232
|
+
for fact_content in facts:
|
|
233
|
+
# Create fact as a memory with special metadata
|
|
234
|
+
fact_metadata = {"type": "fact", "kb_name": kb_name}
|
|
235
|
+
if metadata:
|
|
236
|
+
fact_metadata.update(metadata)
|
|
237
|
+
|
|
238
|
+
memory = self.service.create_memory(
|
|
239
|
+
user_id=user_id,
|
|
240
|
+
project_id=project_id,
|
|
241
|
+
content=f"fact: {fact_content}",
|
|
242
|
+
metadata=fact_metadata,
|
|
243
|
+
importance=1.5 # Facts have higher importance
|
|
244
|
+
)
|
|
245
|
+
created_facts.append(memory)
|
|
246
|
+
|
|
247
|
+
return f"Successfully stored {len(created_facts)} facts in {kb_name}."
|
|
248
|
+
|
|
249
|
+
@override
|
|
250
|
+
def register(self, mcp_server: FastMCP) -> None:
|
|
251
|
+
"""Register this tool with the MCP server."""
|
|
252
|
+
tool_self = self
|
|
253
|
+
|
|
254
|
+
@mcp_server.tool(name=self.name, description=self.description)
|
|
255
|
+
async def store_facts(
|
|
256
|
+
ctx: MCPContext,
|
|
257
|
+
facts: List[str],
|
|
258
|
+
kb_name: str = "general",
|
|
259
|
+
scope: str = "project",
|
|
260
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
261
|
+
) -> str:
|
|
262
|
+
return await tool_self.call(ctx, facts=facts, kb_name=kb_name, scope=scope, metadata=metadata)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@final
|
|
266
|
+
class SummarizeToMemoryTool(KnowledgeToolBase):
|
|
267
|
+
"""Tool for summarizing information and storing in memory."""
|
|
268
|
+
|
|
269
|
+
@property
|
|
270
|
+
@override
|
|
271
|
+
def name(self) -> str:
|
|
272
|
+
"""Get the tool name."""
|
|
273
|
+
return "summarize_to_memory"
|
|
274
|
+
|
|
275
|
+
@property
|
|
276
|
+
@override
|
|
277
|
+
def description(self) -> str:
|
|
278
|
+
"""Get the tool description."""
|
|
279
|
+
return """Summarize information and store it in memory for future reference.
|
|
280
|
+
|
|
281
|
+
This tool helps agents remember important information by summarizing it
|
|
282
|
+
and storing it at the appropriate scope level.
|
|
283
|
+
|
|
284
|
+
Usage:
|
|
285
|
+
summarize_to_memory(content="Long discussion about API design...", topic="API Design Decisions")
|
|
286
|
+
summarize_to_memory(content="User preferences...", topic="User Preferences", scope="session")
|
|
287
|
+
summarize_to_memory(content="Company guidelines...", topic="Guidelines", scope="global")
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
@override
|
|
291
|
+
async def call(
|
|
292
|
+
self,
|
|
293
|
+
ctx: MCPContext,
|
|
294
|
+
content: str,
|
|
295
|
+
topic: str,
|
|
296
|
+
scope: str = "project",
|
|
297
|
+
auto_facts: bool = True
|
|
298
|
+
) -> str:
|
|
299
|
+
"""Summarize content and store in memory.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
ctx: MCP context
|
|
303
|
+
content: Content to summarize
|
|
304
|
+
topic: Topic or title for the summary
|
|
305
|
+
scope: Scope level (session, project, global)
|
|
306
|
+
auto_facts: Whether to extract facts automatically
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
Success message with summary
|
|
310
|
+
"""
|
|
311
|
+
tool_ctx = create_tool_context(ctx)
|
|
312
|
+
await tool_ctx.set_tool_info(self.name)
|
|
313
|
+
|
|
314
|
+
await tool_ctx.info(f"Summarizing content about {topic}")
|
|
315
|
+
|
|
316
|
+
# Use the memory service to create a summary
|
|
317
|
+
# This would typically use an LLM to summarize, but for now we'll store as-is
|
|
318
|
+
summary = f"Summary of {topic}:\n{content[:500]}..." if len(content) > 500 else content
|
|
319
|
+
|
|
320
|
+
# Store the summary as a memory
|
|
321
|
+
from hanzo_memory.services.memory import get_memory_service
|
|
322
|
+
memory_service = get_memory_service()
|
|
323
|
+
|
|
324
|
+
# Determine scope
|
|
325
|
+
if scope == "global":
|
|
326
|
+
user_id = "global"
|
|
327
|
+
project_id = "global"
|
|
328
|
+
elif scope == "session":
|
|
329
|
+
user_id = f"session_{self.user_id}"
|
|
330
|
+
project_id = self.project_id
|
|
331
|
+
else:
|
|
332
|
+
user_id = self.user_id
|
|
333
|
+
project_id = self.project_id
|
|
334
|
+
|
|
335
|
+
memory = memory_service.create_memory(
|
|
336
|
+
user_id=user_id,
|
|
337
|
+
project_id=project_id,
|
|
338
|
+
content=summary,
|
|
339
|
+
metadata={"topic": topic, "type": "summary", "scope": scope}
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
result = f"Stored summary of {topic} in {scope} memory."
|
|
343
|
+
|
|
344
|
+
# Optionally extract facts
|
|
345
|
+
if auto_facts:
|
|
346
|
+
# In a real implementation, this would use LLM to extract key facts
|
|
347
|
+
# For now, we'll just note it
|
|
348
|
+
result += "\n(Auto-fact extraction would extract key facts from the summary)"
|
|
349
|
+
|
|
350
|
+
return result
|
|
351
|
+
|
|
352
|
+
@override
|
|
353
|
+
def register(self, mcp_server: FastMCP) -> None:
|
|
354
|
+
"""Register this tool with the MCP server."""
|
|
355
|
+
tool_self = self
|
|
356
|
+
|
|
357
|
+
@mcp_server.tool(name=self.name, description=self.description)
|
|
358
|
+
async def summarize_to_memory(
|
|
359
|
+
ctx: MCPContext,
|
|
360
|
+
content: str,
|
|
361
|
+
topic: str,
|
|
362
|
+
scope: str = "project",
|
|
363
|
+
auto_facts: bool = True
|
|
364
|
+
) -> str:
|
|
365
|
+
return await tool_self.call(ctx, content=content, topic=topic, scope=scope, auto_facts=auto_facts)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
@final
|
|
369
|
+
class ManageKnowledgeBasesTool(KnowledgeToolBase):
|
|
370
|
+
"""Tool for managing knowledge bases."""
|
|
371
|
+
|
|
372
|
+
@property
|
|
373
|
+
@override
|
|
374
|
+
def name(self) -> str:
|
|
375
|
+
"""Get the tool name."""
|
|
376
|
+
return "manage_knowledge_bases"
|
|
377
|
+
|
|
378
|
+
@property
|
|
379
|
+
@override
|
|
380
|
+
def description(self) -> str:
|
|
381
|
+
"""Get the tool description."""
|
|
382
|
+
return """Create, list, and manage knowledge bases.
|
|
383
|
+
|
|
384
|
+
Knowledge bases help organize facts by topic or domain.
|
|
385
|
+
They can exist at different scopes for better organization.
|
|
386
|
+
|
|
387
|
+
Usage:
|
|
388
|
+
manage_knowledge_bases(action="create", kb_name="api_docs", description="API documentation")
|
|
389
|
+
manage_knowledge_bases(action="list", scope="project")
|
|
390
|
+
manage_knowledge_bases(action="delete", kb_name="old_docs")
|
|
391
|
+
"""
|
|
392
|
+
|
|
393
|
+
@override
|
|
394
|
+
async def call(
|
|
395
|
+
self,
|
|
396
|
+
ctx: MCPContext,
|
|
397
|
+
action: str,
|
|
398
|
+
kb_name: Optional[str] = None,
|
|
399
|
+
description: Optional[str] = None,
|
|
400
|
+
scope: str = "project"
|
|
401
|
+
) -> str:
|
|
402
|
+
"""Manage knowledge bases.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
ctx: MCP context
|
|
406
|
+
action: Action to perform (create, list, delete)
|
|
407
|
+
kb_name: Knowledge base name (for create/delete)
|
|
408
|
+
description: Description (for create)
|
|
409
|
+
scope: Scope level
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
Result message
|
|
413
|
+
"""
|
|
414
|
+
tool_ctx = create_tool_context(ctx)
|
|
415
|
+
await tool_ctx.set_tool_info(self.name)
|
|
416
|
+
|
|
417
|
+
# Determine scope
|
|
418
|
+
if scope == "global":
|
|
419
|
+
user_id = "global"
|
|
420
|
+
project_id = "global"
|
|
421
|
+
elif scope == "session":
|
|
422
|
+
user_id = f"session_{self.user_id}"
|
|
423
|
+
project_id = self.project_id
|
|
424
|
+
else:
|
|
425
|
+
user_id = self.user_id
|
|
426
|
+
project_id = self.project_id
|
|
427
|
+
|
|
428
|
+
if action == "create":
|
|
429
|
+
if not kb_name:
|
|
430
|
+
return "Error: kb_name required for create action"
|
|
431
|
+
|
|
432
|
+
# Create a knowledge base entry as a special memory
|
|
433
|
+
kb_metadata = {
|
|
434
|
+
"type": "knowledge_base",
|
|
435
|
+
"kb_name": kb_name,
|
|
436
|
+
"description": description or "",
|
|
437
|
+
"scope": scope
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
memory = self.service.create_memory(
|
|
441
|
+
user_id=user_id,
|
|
442
|
+
project_id=project_id,
|
|
443
|
+
content=f"Knowledge Base: {kb_name}\nDescription: {description or 'No description'}",
|
|
444
|
+
metadata=kb_metadata,
|
|
445
|
+
importance=2.0 # KBs have high importance
|
|
446
|
+
)
|
|
447
|
+
return f"Created knowledge base '{kb_name}' in {scope} scope."
|
|
448
|
+
|
|
449
|
+
elif action == "list":
|
|
450
|
+
# Search for knowledge base entries
|
|
451
|
+
kbs = self.service.search_memories(
|
|
452
|
+
user_id=user_id,
|
|
453
|
+
query="type:knowledge_base",
|
|
454
|
+
project_id=project_id,
|
|
455
|
+
limit=100
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
# Filter for KB-type memories
|
|
459
|
+
kb_list = []
|
|
460
|
+
for memory in kbs:
|
|
461
|
+
if memory.metadata and memory.metadata.get("type") == "knowledge_base":
|
|
462
|
+
kb_list.append(memory)
|
|
463
|
+
|
|
464
|
+
if not kb_list:
|
|
465
|
+
return f"No knowledge bases found in {scope} scope."
|
|
466
|
+
|
|
467
|
+
formatted = [f"Knowledge bases in {scope} scope:"]
|
|
468
|
+
for kb in kb_list:
|
|
469
|
+
name = kb.metadata.get("kb_name", "Unknown")
|
|
470
|
+
desc = kb.metadata.get("description", "")
|
|
471
|
+
desc_text = f" - {desc}" if desc else ""
|
|
472
|
+
formatted.append(f"- {name}{desc_text}")
|
|
473
|
+
return "\n".join(formatted)
|
|
474
|
+
|
|
475
|
+
elif action == "delete":
|
|
476
|
+
if not kb_name:
|
|
477
|
+
return "Error: kb_name required for delete action"
|
|
478
|
+
|
|
479
|
+
# Search for the KB entry
|
|
480
|
+
kbs = self.service.search_memories(
|
|
481
|
+
user_id=user_id,
|
|
482
|
+
query=f"type:knowledge_base kb_name:{kb_name}",
|
|
483
|
+
project_id=project_id,
|
|
484
|
+
limit=10
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
deleted_count = 0
|
|
488
|
+
for memory in kbs:
|
|
489
|
+
if (memory.metadata and
|
|
490
|
+
memory.metadata.get("type") == "knowledge_base" and
|
|
491
|
+
memory.metadata.get("kb_name") == kb_name):
|
|
492
|
+
# Note: delete_memory is not fully implemented
|
|
493
|
+
# but we'll call it anyway
|
|
494
|
+
self.service.delete_memory(user_id, memory.memory_id)
|
|
495
|
+
deleted_count += 1
|
|
496
|
+
|
|
497
|
+
if deleted_count > 0:
|
|
498
|
+
return f"Deleted knowledge base '{kb_name}'."
|
|
499
|
+
else:
|
|
500
|
+
return f"Knowledge base '{kb_name}' not found."
|
|
501
|
+
|
|
502
|
+
else:
|
|
503
|
+
return f"Unknown action: {action}. Use create, list, or delete."
|
|
504
|
+
|
|
505
|
+
@override
|
|
506
|
+
def register(self, mcp_server: FastMCP) -> None:
|
|
507
|
+
"""Register this tool with the MCP server."""
|
|
508
|
+
tool_self = self
|
|
509
|
+
|
|
510
|
+
@mcp_server.tool(name=self.name, description=self.description)
|
|
511
|
+
async def manage_knowledge_bases(
|
|
512
|
+
ctx: MCPContext,
|
|
513
|
+
action: str,
|
|
514
|
+
kb_name: Optional[str] = None,
|
|
515
|
+
description: Optional[str] = None,
|
|
516
|
+
scope: str = "project"
|
|
517
|
+
) -> str:
|
|
518
|
+
return await tool_self.call(ctx, action=action, kb_name=kb_name, description=description, scope=scope)
|