fenix-mcp 0.4.0__py3-none-any.whl → 0.5.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.
fenix_mcp/__init__.py CHANGED
@@ -17,4 +17,4 @@ Updated with improved error handling and better documentation.
17
17
 
18
18
  __all__ = ["__version__"]
19
19
 
20
- __version__ = "0.4.0"
20
+ __version__ = "0.5.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="medium", description="Nível de importância da memória."
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
- metadata_parts: List[str] = []
29
- if context:
30
- metadata_parts.append(f"Contexto: {context}")
31
- if source:
32
- metadata_parts.append(f"Fonte: {source}")
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": "\n".join(metadata_parts) if metadata_parts else None,
38
- "priority_score": _importance_to_priority(importance),
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))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fenix-mcp
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Fênix Cloud MCP server implemented in Python
5
5
  Author: Fenix Inc
6
6
  Requires-Python: >=3.10
@@ -1,4 +1,4 @@
1
- fenix_mcp/__init__.py,sha256=RC_GHV0oxTvs3Y2i7XSIUtYYtjwepfTw40Tcnx2CdfU,615
1
+ fenix_mcp/__init__.py,sha256=TUKG6aTaYq_EQ3TiKSaS-m7s_p9d5sURwutx4MxHVvQ,615
2
2
  fenix_mcp/main.py,sha256=iJV-9btNMDJMObvcn7wBQdbLLKjkYCQ1ANGEwHGHlMU,2857
3
3
  fenix_mcp/application/presenters.py,sha256=fGME54PdCDhTBhXO-JUB9yLdBHiE1aeXLTC2fCuxnxM,689
4
4
  fenix_mcp/application/tool_base.py,sha256=qUcb46qx9gHQfrSHgj4RD4NCHW-OIvKQdR5G9uxZ5l4,1316
@@ -6,12 +6,12 @@ fenix_mcp/application/tool_registry.py,sha256=bPT5g8GfxG_qu28R1WaDOZHvtmG6TPDvZi
6
6
  fenix_mcp/application/tools/__init__.py,sha256=Gi1YvYh-KdL9HD8gLVrknHrxiKKEOhHBEZ02KBXJaKQ,796
7
7
  fenix_mcp/application/tools/health.py,sha256=m5DxhoRbdwl6INzd6PISxv1NAv-ljCrezsr773VB0wE,834
8
8
  fenix_mcp/application/tools/initialize.py,sha256=f33DNDn9u8IYwpqiBj6bjJ-wHgaUP1zEuAvUM1rMYPc,4674
9
- fenix_mcp/application/tools/intelligence.py,sha256=oRGmmaeRJG_6ckZaByAdZbMKmr2E1cZPxGeFwSFaCTE,12761
9
+ fenix_mcp/application/tools/intelligence.py,sha256=dyHjEdEW4PksDARcdPoYhy4fcu3gneWMnI_SjNzPBNo,13380
10
10
  fenix_mcp/application/tools/knowledge.py,sha256=4ClGoFRqyFIPuzzg2DAg-w2eMvTP37mH0THXDGftinw,44634
11
11
  fenix_mcp/application/tools/productivity.py,sha256=2IMkNdZ-Kd1CFAO7geruAVjtf_BWoDdbnwkl76vhtC8,9973
12
12
  fenix_mcp/application/tools/user_config.py,sha256=8mPOZuwszO0TapxgrA7Foe15VQE874_mvfQYlGzyv4Y,6230
13
13
  fenix_mcp/domain/initialization.py,sha256=AZhdSNITQ7O3clELBuqGvjJc-c8pFKc7zQz-XR2xXPc,6933
14
- fenix_mcp/domain/intelligence.py,sha256=rOQWxtEceTWGlnWMfUXrBTzjt-yATrMPgFktWF1L5QA,5707
14
+ fenix_mcp/domain/intelligence.py,sha256=Z2k7mrfNlUgIV_rw6suukS3TU3kfAQjv5-ZSvcRZXi4,8246
15
15
  fenix_mcp/domain/knowledge.py,sha256=fKQOTt20u5aa5Yo7gPeQ1Qxa_K5pBxn1yn8FEfOFltM,20241
16
16
  fenix_mcp/domain/productivity.py,sha256=nmHRuVJGRRu1s4eMoAv8vXHKsSauCPl-FvFx3I_yCTE,6661
17
17
  fenix_mcp/domain/user_config.py,sha256=LzBDCk31gLMtKHTbBmYb9VoFPHDW6OydpmDXeHHd0Mw,1642
@@ -22,8 +22,8 @@ fenix_mcp/infrastructure/logging.py,sha256=bHrWlSi_0HshRe3--BK_5nzUszW-gh37q6jsd
22
22
  fenix_mcp/infrastructure/fenix_api/client.py,sha256=Navi7cGAOradghcbYkFIQQINpjFrdINlNhdfZ4iSSYQ,28338
23
23
  fenix_mcp/interface/mcp_server.py,sha256=5UM2NJuNbwHkmCEprIFataJ5nFZiO8efTtP_oW3_iX0,2331
24
24
  fenix_mcp/interface/transports.py,sha256=PxdhfjH8UMl03f7nuCLc-M6tMx6-Y-btVz_mSqXKrSI,8138
25
- fenix_mcp-0.4.0.dist-info/METADATA,sha256=fIm7WGYy9JK5VwYIlc_iFWgLVh6XuNFvGJKkPrjS3O0,7260
26
- fenix_mcp-0.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
- fenix_mcp-0.4.0.dist-info/entry_points.txt,sha256=o52x_YHBupEd-1Z1GSfUjv3gJrx5_I-EkHhCgt1WBaE,49
28
- fenix_mcp-0.4.0.dist-info/top_level.txt,sha256=2G1UtKpwjaIGQyE7sRoHecxaGLeuexfjrOUjv9DDKh4,10
29
- fenix_mcp-0.4.0.dist-info/RECORD,,
25
+ fenix_mcp-0.5.0.dist-info/METADATA,sha256=CJDz4x6SYkyBzpIPXhk3WEjT1vamMXFp2yY5jqAhpRE,7260
26
+ fenix_mcp-0.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
+ fenix_mcp-0.5.0.dist-info/entry_points.txt,sha256=o52x_YHBupEd-1Z1GSfUjv3gJrx5_I-EkHhCgt1WBaE,49
28
+ fenix_mcp-0.5.0.dist-info/top_level.txt,sha256=2G1UtKpwjaIGQyE7sRoHecxaGLeuexfjrOUjv9DDKh4,10
29
+ fenix_mcp-0.5.0.dist-info/RECORD,,