fenix-mcp 0.4.0__tar.gz → 0.5.0__tar.gz
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.
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/PKG-INFO +1 -1
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/__init__.py +1 -1
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/application/tools/intelligence.py +18 -3
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/domain/intelligence.py +99 -8
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp.egg-info/PKG-INFO +1 -1
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/README.md +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/application/presenters.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/application/tool_base.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/application/tool_registry.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/application/tools/__init__.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/application/tools/health.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/application/tools/initialize.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/application/tools/knowledge.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/application/tools/productivity.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/application/tools/user_config.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/domain/initialization.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/domain/knowledge.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/domain/productivity.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/domain/user_config.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/infrastructure/config.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/infrastructure/context.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/infrastructure/fenix_api/client.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/infrastructure/http_client.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/infrastructure/logging.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/interface/mcp_server.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/interface/transports.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp/main.py +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp.egg-info/SOURCES.txt +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp.egg-info/dependency_links.txt +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp.egg-info/entry_points.txt +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp.egg-info/requires.txt +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/fenix_mcp.egg-info/top_level.txt +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/pyproject.toml +0 -0
- {fenix_mcp-0.4.0 → fenix_mcp-0.5.0}/setup.cfg +0 -0
|
@@ -10,7 +10,7 @@ from pydantic import Field
|
|
|
10
10
|
|
|
11
11
|
from fenix_mcp.application.presenters import text
|
|
12
12
|
from fenix_mcp.application.tool_base import Tool, ToolRequest
|
|
13
|
-
from fenix_mcp.domain.intelligence import IntelligenceService
|
|
13
|
+
from fenix_mcp.domain.intelligence import IntelligenceService, build_metadata
|
|
14
14
|
from fenix_mcp.infrastructure.context import AppContext
|
|
15
15
|
|
|
16
16
|
|
|
@@ -66,10 +66,14 @@ class IntelligenceRequest(ToolRequest):
|
|
|
66
66
|
content: Optional[str] = Field(
|
|
67
67
|
default=None, description="Conteúdo/texto da memória."
|
|
68
68
|
)
|
|
69
|
+
metadata: Optional[str] = Field(
|
|
70
|
+
default=None,
|
|
71
|
+
description="Metadata estruturada da memória (formato pipe, toml compacto, etc.).",
|
|
72
|
+
)
|
|
69
73
|
context: Optional[str] = Field(default=None, description="Contexto adicional.")
|
|
70
74
|
source: Optional[str] = Field(default=None, description="Fonte da memória.")
|
|
71
|
-
importance: str = Field(
|
|
72
|
-
default=
|
|
75
|
+
importance: Optional[str] = Field(
|
|
76
|
+
default=None, description="Nível de importância da memória."
|
|
73
77
|
)
|
|
74
78
|
include_content: bool = Field(
|
|
75
79
|
default=False,
|
|
@@ -154,6 +158,7 @@ class IntelligenceTool(Tool):
|
|
|
154
158
|
memory = await self._service.smart_create_memory(
|
|
155
159
|
title=payload.title,
|
|
156
160
|
content=payload.content,
|
|
161
|
+
metadata=payload.metadata,
|
|
157
162
|
context=payload.context,
|
|
158
163
|
source=payload.source,
|
|
159
164
|
importance=payload.importance,
|
|
@@ -251,9 +256,19 @@ class IntelligenceTool(Tool):
|
|
|
251
256
|
async def _handle_update(self, payload: IntelligenceRequest):
|
|
252
257
|
if not payload.id:
|
|
253
258
|
return text("❌ Informe o ID da memória para atualização.")
|
|
259
|
+
existing = await self._service.get_memory(
|
|
260
|
+
payload.id, include_content=False, include_metadata=True
|
|
261
|
+
)
|
|
262
|
+
metadata = build_metadata(
|
|
263
|
+
payload.metadata,
|
|
264
|
+
importance=payload.importance,
|
|
265
|
+
tags=payload.tags,
|
|
266
|
+
existing=existing.get("metadata") if isinstance(existing, dict) else None,
|
|
267
|
+
)
|
|
254
268
|
update_fields: Dict[str, Any] = {
|
|
255
269
|
"title": payload.title,
|
|
256
270
|
"content": payload.content,
|
|
271
|
+
"metadata": metadata,
|
|
257
272
|
"tags": payload.tags,
|
|
258
273
|
"documentation_item_id": payload.documentation_item_id,
|
|
259
274
|
"mode_id": payload.mode_id,
|
|
@@ -20,22 +20,25 @@ class IntelligenceService:
|
|
|
20
20
|
*,
|
|
21
21
|
title: str,
|
|
22
22
|
content: str,
|
|
23
|
+
metadata: Optional[str],
|
|
23
24
|
context: Optional[str],
|
|
24
25
|
source: Optional[str],
|
|
25
26
|
importance: str,
|
|
26
27
|
tags: Optional[Iterable[str]] = None,
|
|
27
28
|
) -> Dict[str, Any]:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
importance_value = importance or "medium"
|
|
30
|
+
metadata_str = build_metadata(
|
|
31
|
+
metadata,
|
|
32
|
+
importance=importance_value,
|
|
33
|
+
tags=tags,
|
|
34
|
+
context=context,
|
|
35
|
+
source=source,
|
|
36
|
+
)
|
|
34
37
|
payload = {
|
|
35
38
|
"title": title,
|
|
36
39
|
"content": content,
|
|
37
|
-
"metadata":
|
|
38
|
-
"priority_score": _importance_to_priority(
|
|
40
|
+
"metadata": metadata_str,
|
|
41
|
+
"priority_score": _importance_to_priority(importance_value),
|
|
39
42
|
"tags": list(tags) if tags else None,
|
|
40
43
|
}
|
|
41
44
|
return await self._call(self.api.smart_create_memory, _strip_none(payload))
|
|
@@ -145,6 +148,20 @@ class IntelligenceService:
|
|
|
145
148
|
async def _call(self, func, *args, **kwargs):
|
|
146
149
|
return await asyncio.to_thread(func, *args, **kwargs)
|
|
147
150
|
|
|
151
|
+
async def get_memory(
|
|
152
|
+
self,
|
|
153
|
+
memory_id: str,
|
|
154
|
+
*,
|
|
155
|
+
include_content: bool = False,
|
|
156
|
+
include_metadata: bool = False,
|
|
157
|
+
) -> Dict[str, Any]:
|
|
158
|
+
return await self._call(
|
|
159
|
+
self.api.get_memory,
|
|
160
|
+
memory_id,
|
|
161
|
+
include_content=include_content,
|
|
162
|
+
include_metadata=include_metadata,
|
|
163
|
+
)
|
|
164
|
+
|
|
148
165
|
|
|
149
166
|
def _importance_to_priority(importance: Optional[str]) -> float:
|
|
150
167
|
mapping = {
|
|
@@ -176,3 +193,77 @@ def _coerce_bool(value: Any, default: bool = False) -> bool:
|
|
|
176
193
|
|
|
177
194
|
def _strip_none(data: Dict[str, Any]) -> Dict[str, Any]:
|
|
178
195
|
return {key: value for key, value in data.items() if value not in (None, "")}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def build_metadata(
|
|
199
|
+
explicit: Optional[str],
|
|
200
|
+
*,
|
|
201
|
+
importance: Optional[str],
|
|
202
|
+
tags: Optional[Iterable[str]],
|
|
203
|
+
context: Optional[str] = None,
|
|
204
|
+
source: Optional[str] = None,
|
|
205
|
+
existing: Optional[str] = None,
|
|
206
|
+
) -> str:
|
|
207
|
+
if explicit and explicit.strip():
|
|
208
|
+
return explicit.strip()
|
|
209
|
+
|
|
210
|
+
existing_map = _parse_metadata(existing) if existing else {}
|
|
211
|
+
metadata_map: Dict[str, str] = {}
|
|
212
|
+
|
|
213
|
+
metadata_map["t"] = existing_map.get("t", "memory")
|
|
214
|
+
metadata_map["src"] = _slugify(source) if source else existing_map.get("src", "mcp")
|
|
215
|
+
|
|
216
|
+
ctx_value = _slugify(context) if context else existing_map.get("ctx")
|
|
217
|
+
if ctx_value:
|
|
218
|
+
metadata_map["ctx"] = ctx_value
|
|
219
|
+
|
|
220
|
+
priority_key = importance.lower() if importance else existing_map.get("p")
|
|
221
|
+
if priority_key:
|
|
222
|
+
metadata_map["p"] = priority_key
|
|
223
|
+
|
|
224
|
+
tag_string = _format_tags(tags)
|
|
225
|
+
if tag_string:
|
|
226
|
+
metadata_map["tags"] = tag_string
|
|
227
|
+
elif "tags" in existing_map:
|
|
228
|
+
metadata_map["tags"] = existing_map["tags"]
|
|
229
|
+
|
|
230
|
+
for key, value in existing_map.items():
|
|
231
|
+
if key not in metadata_map:
|
|
232
|
+
metadata_map[key] = value
|
|
233
|
+
|
|
234
|
+
if not metadata_map:
|
|
235
|
+
metadata_map["t"] = "memory"
|
|
236
|
+
metadata_map["src"] = "mcp"
|
|
237
|
+
|
|
238
|
+
return "|".join(f"{key}:{metadata_map[key]}" for key in metadata_map)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _parse_metadata(metadata: str) -> Dict[str, str]:
|
|
242
|
+
items = {}
|
|
243
|
+
for entry in metadata.split("|"):
|
|
244
|
+
if ":" not in entry:
|
|
245
|
+
continue
|
|
246
|
+
key, value = entry.split(":", 1)
|
|
247
|
+
key = key.strip()
|
|
248
|
+
value = value.strip()
|
|
249
|
+
if key:
|
|
250
|
+
items[key] = value
|
|
251
|
+
return items
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _slugify(value: Optional[str]) -> str:
|
|
255
|
+
if not value:
|
|
256
|
+
return ""
|
|
257
|
+
sanitized = value.replace("|", " ").replace(":", " ")
|
|
258
|
+
return "-".join(part for part in sanitized.split() if part).lower()
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _format_tags(tags: Optional[Iterable[str]]) -> str:
|
|
262
|
+
if not tags:
|
|
263
|
+
return ""
|
|
264
|
+
normalized = {
|
|
265
|
+
_slugify(tag) for tag in tags if isinstance(tag, str) and _slugify(tag)
|
|
266
|
+
}
|
|
267
|
+
if not normalized:
|
|
268
|
+
return ""
|
|
269
|
+
return ",".join(sorted(normalized))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|