fenix-mcp 0.5.6__py3-none-any.whl → 0.5.7__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
@@ -8,4 +8,4 @@ Fênix Cloud MCP Server (Python edition).
8
8
  __all__ = ["__version__"]
9
9
 
10
10
 
11
- __version__ = "0.5.6"
11
+ __version__ = "0.5.7"
@@ -155,10 +155,21 @@ class IntelligenceTool(Tool):
155
155
  async def _handle_smart_create(self, payload: IntelligenceRequest):
156
156
  if not payload.title or not payload.content:
157
157
  return text("❌ Informe título e conteúdo para criar uma memória.")
158
+
159
+ if not payload.metadata or not payload.metadata.strip():
160
+ return text("❌ Informe metadata para criar uma memória.")
161
+
162
+ if not payload.source or not payload.source.strip():
163
+ return text("❌ Informe source para criar uma memória.")
164
+
158
165
  try:
159
166
  normalized_tags = _ensure_tag_sequence(payload.tags)
160
167
  except ValueError as exc:
161
168
  return text(f"❌ {exc}")
169
+
170
+ if not normalized_tags or len(normalized_tags) == 0:
171
+ return text("❌ Informe tags para criar uma memória.")
172
+
162
173
  memory = await self._service.smart_create_memory(
163
174
  title=payload.title,
164
175
  content=payload.content,
@@ -271,6 +282,7 @@ class IntelligenceTool(Tool):
271
282
  payload.metadata,
272
283
  importance=payload.importance,
273
284
  tags=normalized_tags,
285
+ source=payload.source,
274
286
  existing=existing.get("metadata") if isinstance(existing, dict) else None,
275
287
  )
276
288
  update_fields: Dict[str, Any] = {
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
 
6
6
  import asyncio
7
7
  from dataclasses import dataclass
8
- from typing import Any, Dict, Iterable, List, Optional, Union
8
+ from typing import Any, Dict, Iterable, List, Optional
9
9
 
10
10
  from fenix_mcp.infrastructure.fenix_api.client import FenixApiClient
11
11
 
@@ -20,18 +20,34 @@ class IntelligenceService:
20
20
  *,
21
21
  title: str,
22
22
  content: str,
23
- metadata: Optional[str],
23
+ metadata: str,
24
24
  context: Optional[str],
25
- source: Optional[str],
25
+ source: str,
26
26
  importance: str,
27
- tags: Optional[Union[Iterable[str], str]] = None,
27
+ tags: List[str],
28
28
  ) -> Dict[str, Any]:
29
+ # Validate required parameters
30
+ if not metadata or not metadata.strip():
31
+ raise ValueError("metadata is required and cannot be empty")
32
+
33
+ if not source or not source.strip():
34
+ raise ValueError("source is required and cannot be empty")
35
+
36
+ if not tags or not isinstance(tags, list) or len(tags) == 0:
37
+ raise ValueError("tags is required and must be a non-empty List[str]")
38
+
39
+ # Validate all tags are strings
40
+ for i, tag in enumerate(tags):
41
+ if not isinstance(tag, str) or not tag.strip():
42
+ raise ValueError(
43
+ f"All tags must be non-empty strings, got {type(tag)} at index {i}"
44
+ )
45
+
29
46
  importance_value = importance or "medium"
30
- normalized_tags = normalize_tags(tags)
31
47
  metadata_str = build_metadata(
32
48
  metadata,
33
49
  importance=importance_value,
34
- tags=normalized_tags,
50
+ tags=tags,
35
51
  context=context,
36
52
  source=source,
37
53
  )
@@ -40,7 +56,7 @@ class IntelligenceService:
40
56
  "content": content,
41
57
  "metadata": metadata_str,
42
58
  "priority_score": _importance_to_priority(importance_value),
43
- "tags": normalized_tags,
59
+ "tags": tags,
44
60
  }
45
61
  return await self._call(self.api.smart_create_memory, _strip_none(payload))
46
62
 
@@ -197,12 +213,12 @@ def _strip_none(data: Dict[str, Any]) -> Dict[str, Any]:
197
213
 
198
214
 
199
215
  def build_metadata(
200
- explicit: Optional[str],
216
+ explicit: str,
201
217
  *,
202
218
  importance: Optional[str],
203
- tags: Optional[Union[Iterable[str], str]],
219
+ tags: List[str],
204
220
  context: Optional[str] = None,
205
- source: Optional[str] = None,
221
+ source: str,
206
222
  existing: Optional[str] = None,
207
223
  ) -> str:
208
224
  if explicit and explicit.strip():
@@ -259,31 +275,37 @@ def _slugify(value: Optional[str]) -> str:
259
275
  return "-".join(part for part in sanitized.split() if part).lower()
260
276
 
261
277
 
262
- def _format_tags(tags: Optional[Union[Iterable[str], str]]) -> str:
263
- if tags is None or tags == "":
264
- return ""
265
- if isinstance(tags, str):
266
- iterable: Iterable[str] = [part.strip() for part in tags.split(",")]
267
- else:
268
- iterable = tags
269
- normalized = {
270
- _slugify(tag) for tag in iterable if isinstance(tag, str) and _slugify(tag)
271
- }
272
- if not normalized:
273
- return ""
274
- return ",".join(sorted(normalized))
275
-
276
-
277
- def normalize_tags(tags: Optional[Union[Iterable[str], str]]) -> Optional[List[str]]:
278
- if tags is None or tags == "":
279
- return None
280
- if isinstance(tags, str):
281
- parts = [part.strip() for part in tags.split(",") if part.strip()]
282
- else:
283
- parts = [str(part).strip() for part in tags if str(part).strip()]
284
- if not parts:
285
- return None
286
- slugged = [_slugify(part) for part in parts if _slugify(part)]
287
- if not slugged:
288
- return None
289
- return sorted(set(slugged))
278
+ def _format_tags(tags: List[str]) -> str:
279
+ """
280
+ Format tags list into a comma-separated string for metadata.
281
+
282
+ Args:
283
+ tags: List of string tags (required)
284
+
285
+ Returns:
286
+ Comma-separated string of tags
287
+
288
+ Raises:
289
+ TypeError: If tags is not a List[str]
290
+ ValueError: If tags is empty
291
+ """
292
+ if not isinstance(tags, list):
293
+ raise TypeError(f"tags must be List[str], got {type(tags)}")
294
+
295
+ if not tags:
296
+ raise ValueError("tags cannot be empty")
297
+
298
+ # Clean and validate tags
299
+ cleaned_tags = []
300
+ for tag in tags:
301
+ if isinstance(tag, str) and tag.strip():
302
+ cleaned_tags.append(tag.strip())
303
+ else:
304
+ raise ValueError(
305
+ f"All tags must be non-empty strings, got {type(tag)}: {tag}"
306
+ )
307
+
308
+ if not cleaned_tags:
309
+ raise ValueError("No valid tags found after cleaning")
310
+
311
+ return ",".join(sorted(set(cleaned_tags)))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fenix-mcp
3
- Version: 0.5.6
3
+ Version: 0.5.7
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=LV_5ywWiAnodLDI7k4GMy1VEsHv9aaa6jyvhAjBS4_g,180
1
+ fenix_mcp/__init__.py,sha256=Qi1wiiPapwJ_vgEtVqDYN13mhpWUaNqtkUJsEOkN2Ug,180
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=5qdCZbEK1IuKjhS5eDqgny0_zyT9lpYeZly-hM6ilxs,14165
9
+ fenix_mcp/application/tools/intelligence.py,sha256=rwQzBVq9nE4tIzdLuu7HPXnr3OX3n47OShN-05ZjNZg,14605
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=IyZUA_fbCHDgHaWCgxkahXAaAT5qQlaz5AT7Cw2Gwko,9006
14
+ fenix_mcp/domain/intelligence.py,sha256=2V70uZygyMcukHS4hVinkB-20VokGOw1gQR7jziX9-M,9515
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.5.6.dist-info/METADATA,sha256=uORCg1OqP0TIm_d39HHxxpatgW5JFL6yHt1yUspe9bI,7260
26
- fenix_mcp-0.5.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
- fenix_mcp-0.5.6.dist-info/entry_points.txt,sha256=o52x_YHBupEd-1Z1GSfUjv3gJrx5_I-EkHhCgt1WBaE,49
28
- fenix_mcp-0.5.6.dist-info/top_level.txt,sha256=2G1UtKpwjaIGQyE7sRoHecxaGLeuexfjrOUjv9DDKh4,10
29
- fenix_mcp-0.5.6.dist-info/RECORD,,
25
+ fenix_mcp-0.5.7.dist-info/METADATA,sha256=fADlVEpBStpboe2N24s8MQzXhnnY_4Syen-sQJKrGg4,7260
26
+ fenix_mcp-0.5.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
+ fenix_mcp-0.5.7.dist-info/entry_points.txt,sha256=o52x_YHBupEd-1Z1GSfUjv3gJrx5_I-EkHhCgt1WBaE,49
28
+ fenix_mcp-0.5.7.dist-info/top_level.txt,sha256=2G1UtKpwjaIGQyE7sRoHecxaGLeuexfjrOUjv9DDKh4,10
29
+ fenix_mcp-0.5.7.dist-info/RECORD,,