fenix-mcp 0.5.5__tar.gz → 0.5.7__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.
Files changed (34) hide show
  1. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/PKG-INFO +1 -1
  2. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/__init__.py +1 -1
  3. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/application/tools/intelligence.py +24 -4
  4. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/domain/intelligence.py +60 -38
  5. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp.egg-info/PKG-INFO +1 -1
  6. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/README.md +0 -0
  7. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/application/presenters.py +0 -0
  8. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/application/tool_base.py +0 -0
  9. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/application/tool_registry.py +0 -0
  10. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/application/tools/__init__.py +0 -0
  11. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/application/tools/health.py +0 -0
  12. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/application/tools/initialize.py +0 -0
  13. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/application/tools/knowledge.py +0 -0
  14. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/application/tools/productivity.py +0 -0
  15. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/application/tools/user_config.py +0 -0
  16. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/domain/initialization.py +0 -0
  17. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/domain/knowledge.py +0 -0
  18. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/domain/productivity.py +0 -0
  19. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/domain/user_config.py +0 -0
  20. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/infrastructure/config.py +0 -0
  21. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/infrastructure/context.py +0 -0
  22. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/infrastructure/fenix_api/client.py +0 -0
  23. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/infrastructure/http_client.py +0 -0
  24. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/infrastructure/logging.py +0 -0
  25. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/interface/mcp_server.py +0 -0
  26. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/interface/transports.py +0 -0
  27. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp/main.py +0 -0
  28. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp.egg-info/SOURCES.txt +0 -0
  29. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp.egg-info/dependency_links.txt +0 -0
  30. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp.egg-info/entry_points.txt +0 -0
  31. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp.egg-info/requires.txt +0 -0
  32. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/fenix_mcp.egg-info/top_level.txt +0 -0
  33. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/pyproject.toml +0 -0
  34. {fenix_mcp-0.5.5 → fenix_mcp-0.5.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fenix-mcp
3
- Version: 0.5.5
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
@@ -8,4 +8,4 @@ Fênix Cloud MCP Server (Python edition).
8
8
  __all__ = ["__version__"]
9
9
 
10
10
 
11
- __version__ = "0.5.5"
11
+ __version__ = "0.5.7"
@@ -155,7 +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
- normalized_tags = _ensure_tag_sequence(payload.tags)
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
+
165
+ try:
166
+ normalized_tags = _ensure_tag_sequence(payload.tags)
167
+ except ValueError as exc:
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
+
159
173
  memory = await self._service.smart_create_memory(
160
174
  title=payload.title,
161
175
  content=payload.content,
@@ -260,11 +274,15 @@ class IntelligenceTool(Tool):
260
274
  existing = await self._service.get_memory(
261
275
  payload.id, include_content=False, include_metadata=True
262
276
  )
263
- normalized_tags = _ensure_tag_sequence(payload.tags)
277
+ try:
278
+ normalized_tags = _ensure_tag_sequence(payload.tags)
279
+ except ValueError as exc:
280
+ return text(f"❌ {exc}")
264
281
  metadata = build_metadata(
265
282
  payload.metadata,
266
283
  importance=payload.importance,
267
284
  tags=normalized_tags,
285
+ source=payload.source,
268
286
  existing=existing.get("metadata") if isinstance(existing, dict) else None,
269
287
  )
270
288
  update_fields: Dict[str, Any] = {
@@ -324,6 +342,8 @@ def _ensure_tag_sequence(raw: Optional[Any]) -> Optional[List[str]]:
324
342
  result = [str(item).strip() for item in raw if str(item).strip()]
325
343
  return result or None
326
344
  if isinstance(raw, str):
327
- items = [part.strip() for part in raw.split(",") if part.strip()]
328
- return items or None
345
+ raise ValueError(
346
+ "O campo `tags` deve ser enviado como array JSON, por exemplo: "
347
+ '["tag1", "tag2"].'
348
+ )
329
349
  return [str(raw).strip()]
@@ -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.5
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
File without changes
File without changes
File without changes
File without changes