fenix-mcp 0.1.0__py3-none-any.whl → 0.2.2__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.
@@ -4,7 +4,7 @@
4
4
  from __future__ import annotations
5
5
 
6
6
  from enum import Enum
7
- from typing import Any, Dict, Iterable, List, Optional
7
+ from typing import Any, Dict, List, Optional
8
8
 
9
9
  from pydantic import Field
10
10
 
@@ -51,24 +51,36 @@ class TodoAction(str, Enum):
51
51
 
52
52
  ACTION_FIELD_DESCRIPTION = (
53
53
  "Ação de produtividade (TODO). Escolha um dos valores: "
54
- + ", ".join(f"`{member.value}` ({member.description.rstrip('.')})." for member in TodoAction)
54
+ + ", ".join(
55
+ f"`{member.value}` ({member.description.rstrip('.')})." for member in TodoAction
56
+ )
55
57
  )
56
58
 
57
59
 
58
60
  class ProductivityRequest(ToolRequest):
59
61
  action: TodoAction = Field(description=ACTION_FIELD_DESCRIPTION)
60
62
  id: Optional[str] = Field(default=None, description="Identificador do item TODO.")
61
- title: Optional[str] = Field(default=None, description="Título do TODO (obrigatório em create).")
62
- content: Optional[str] = Field(default=None, description="Conteúdo em Markdown (obrigatório em create).")
63
+ title: Optional[str] = Field(
64
+ default=None, description="Título do TODO (obrigatório em create)."
65
+ )
66
+ content: Optional[str] = Field(
67
+ default=None, description="Conteúdo em Markdown (obrigatório em create)."
68
+ )
63
69
  status: Optional[str] = Field(default="pending", description="Status do TODO.")
64
70
  priority: Optional[str] = Field(default="medium", description="Prioridade do TODO.")
65
71
  category: Optional[str] = Field(default=None, description="Categoria opcional.")
66
72
  tags: Optional[List[str]] = Field(default=None, description="Lista de tags.")
67
- due_date: Optional[str] = Field(default=None, description="Data de vencimento do TODO (ISO).")
68
- limit: int = Field(default=20, ge=1, le=100, description="Limite de resultados em list/search.")
73
+ due_date: Optional[str] = Field(
74
+ default=None, description="Data de vencimento do TODO (ISO)."
75
+ )
76
+ limit: int = Field(
77
+ default=20, ge=1, le=100, description="Limite de resultados em list/search."
78
+ )
69
79
  offset: int = Field(default=0, ge=0, description="Offset de paginação.")
70
80
  query: Optional[str] = Field(default=None, description="Termo de busca.")
71
- days: Optional[int] = Field(default=None, ge=1, le=30, description="Janela de dias para upcoming.")
81
+ days: Optional[int] = Field(
82
+ default=None, ge=1, le=30, description="Janela de dias para upcoming."
83
+ )
72
84
 
73
85
 
74
86
  class ProductivityTool(Tool):
@@ -175,7 +187,9 @@ class ProductivityTool(Tool):
175
187
  async def _handle_search(self, payload: ProductivityRequest):
176
188
  if not payload.query:
177
189
  return text("❌ Informe um termo de busca (query).")
178
- todos = await self._service.search(payload.query, limit=payload.limit, offset=payload.offset)
190
+ todos = await self._service.search(
191
+ payload.query, limit=payload.limit, offset=payload.offset
192
+ )
179
193
  if not todos:
180
194
  return text("🔍 Nenhum TODO encontrado para a busca.")
181
195
  body = "\n\n".join(ProductivityService.format_todo(todo) for todo in todos)
@@ -213,7 +227,10 @@ class ProductivityTool(Tool):
213
227
  return text(f"🔖 **Tags utilizadas:**\n{body}")
214
228
 
215
229
  async def _handle_help(self):
216
- return text("📚 **Ações disponíveis para productivity**\n\n" + TodoAction.formatted_help())
230
+ return text(
231
+ "📚 **Ações disponíveis para productivity**\n\n"
232
+ + TodoAction.formatted_help()
233
+ )
217
234
 
218
235
  @staticmethod
219
236
  def _format_single(todo: Dict[str, Any], *, header: str) -> str:
@@ -4,7 +4,7 @@
4
4
  from __future__ import annotations
5
5
 
6
6
  from enum import Enum
7
- from typing import Dict, List, Optional
7
+ from typing import Any, Dict, List, Optional
8
8
 
9
9
  from pydantic import Field
10
10
 
@@ -43,9 +43,9 @@ class UserConfigAction(str, Enum):
43
43
  return "\n".join(lines)
44
44
 
45
45
 
46
- ACTION_FIELD_DESCRIPTION = (
47
- "Ação a executar. Escolha um dos valores: "
48
- + ", ".join(f"`{member.value}` ({member.description.rstrip('.')})." for member in UserConfigAction)
46
+ ACTION_FIELD_DESCRIPTION = "Ação a executar. Escolha um dos valores: " + ", ".join(
47
+ f"`{member.value}` ({member.description.rstrip('.')})."
48
+ for member in UserConfigAction
49
49
  )
50
50
 
51
51
 
@@ -53,12 +53,18 @@ class UserConfigRequest(ToolRequest):
53
53
  action: UserConfigAction = Field(description=ACTION_FIELD_DESCRIPTION)
54
54
  id: Optional[str] = Field(default=None, description="ID do documento.")
55
55
  name: Optional[str] = Field(default=None, description="Nome do documento.")
56
- content: Optional[str] = Field(default=None, description="Conteúdo em Markdown/JSON.")
56
+ content: Optional[str] = Field(
57
+ default=None, description="Conteúdo em Markdown/JSON."
58
+ )
57
59
  mode_id: Optional[str] = Field(default=None, description="ID do modo associado.")
58
- is_default: Optional[bool] = Field(default=None, description="Marca o documento como padrão.")
60
+ is_default: Optional[bool] = Field(
61
+ default=None, description="Marca o documento como padrão."
62
+ )
59
63
  limit: int = Field(default=20, ge=1, le=100, description="Limite para listagem.")
60
64
  offset: int = Field(default=0, ge=0, description="Offset para listagem.")
61
- return_content: Optional[bool] = Field(default=None, description="Retorna conteúdo completo.")
65
+ return_content: Optional[bool] = Field(
66
+ default=None, description="Retorna conteúdo completo."
67
+ )
62
68
 
63
69
 
64
70
  class UserConfigTool(Tool):
@@ -125,7 +131,7 @@ class UserConfigTool(Tool):
125
131
 
126
132
  if action is UserConfigAction.DELETE:
127
133
  if not payload.id:
128
- return text("❌ Informe o ID do documento." )
134
+ return text("❌ Informe o ID do documento.")
129
135
  await self._service.delete(payload.id)
130
136
  return text(f"🗑️ Documento {payload.id} removido.")
131
137
 
@@ -135,7 +141,10 @@ class UserConfigTool(Tool):
135
141
  )
136
142
 
137
143
  async def _handle_help(self):
138
- return text("📚 **Ações disponíveis para user_config**\n\n" + UserConfigAction.formatted_help())
144
+ return text(
145
+ "📚 **Ações disponíveis para user_config**\n\n"
146
+ + UserConfigAction.formatted_help()
147
+ )
139
148
 
140
149
 
141
150
  def _format_doc(doc: Dict[str, Any], header: Optional[str] = None) -> str:
@@ -25,7 +25,9 @@ class InitializationService:
25
25
  self._api = api_client
26
26
  self._logger = logger
27
27
 
28
- async def gather_data(self, *, include_user_docs: bool, limit: int) -> InitializationData:
28
+ async def gather_data(
29
+ self, *, include_user_docs: bool, limit: int
30
+ ) -> InitializationData:
29
31
  profile = await self._safe_call(self._api.get_profile)
30
32
  core_docs = await self._safe_call(
31
33
  self._api.list_core_documents,
@@ -35,29 +37,20 @@ class InitializationService:
35
37
  self._logger.debug("Core docs response", extra={"core_docs": core_docs})
36
38
  user_docs: List[Dict[str, Any]] = []
37
39
  if include_user_docs:
38
- user_docs = await self._safe_call(
39
- self._api.list_user_core_documents,
40
- return_content=True,
41
- ) or []
40
+ user_docs = (
41
+ await self._safe_call(
42
+ self._api.list_user_core_documents,
43
+ return_content=True,
44
+ )
45
+ or []
46
+ )
42
47
  if self._logger:
43
48
  self._logger.debug("User docs response", extra={"user_docs": user_docs})
44
- memories = await self._safe_call(
45
- self._api.list_memories,
46
- include_content=True,
47
- include_metadata=False,
48
- limit=3,
49
- offset=0,
50
- sortBy="created_at",
51
- sortOrder="desc",
52
- )
53
- if self._logger:
54
- self._logger.debug("Memories response", extra={"memories": memories})
55
-
56
49
  return InitializationData(
57
50
  profile=profile,
58
51
  core_documents=self._extract_items(core_docs, "coreDocuments"),
59
52
  user_documents=self._extract_items(user_docs, "userCoreDocuments"),
60
- recent_memories=self._extract_items(memories, "memories"),
53
+ recent_memories=[],
61
54
  )
62
55
 
63
56
  async def _safe_call(self, func, *args, **kwargs):
@@ -80,7 +73,9 @@ class InitializationService:
80
73
  return []
81
74
 
82
75
  @staticmethod
83
- def build_existing_user_summary(data: InitializationData, include_user_docs: bool) -> str:
76
+ def build_existing_user_summary(
77
+ data: InitializationData, include_user_docs: bool
78
+ ) -> str:
84
79
  profile = data.profile or {}
85
80
  user_info = profile.get("user") or {}
86
81
  tenant_info = profile.get("tenant") or {}
@@ -109,16 +104,21 @@ class InitializationService:
109
104
 
110
105
  if core_count:
111
106
  preview = ", ".join(
112
- doc.get("name", doc.get("title", "sem título")) for doc in data.core_documents[:5]
107
+ doc.get("name", doc.get("title", "sem título"))
108
+ for doc in data.core_documents[:5]
113
109
  )
114
110
  lines.append(f"- Exemplos de documentos principais: {preview}")
115
111
 
116
112
  if include_user_docs and user_count:
117
- preview = ", ".join(doc.get("name", "sem título") for doc in data.user_documents[:5])
113
+ preview = ", ".join(
114
+ doc.get("name", "sem título") for doc in data.user_documents[:5]
115
+ )
118
116
  lines.append(f"- Exemplos de documentos pessoais: {preview}")
119
117
 
120
118
  if memories_count:
121
- preview = ", ".join(mem.get("title", "sem título") for mem in data.recent_memories[:3])
119
+ preview = ", ".join(
120
+ mem.get("title", "sem título") for mem in data.recent_memories[:3]
121
+ )
122
122
  lines.append(f"- Memórias recentes: {preview}")
123
123
 
124
124
  lines.append("")
@@ -44,12 +44,15 @@ class IntelligenceService:
44
44
  params = _strip_none(filters)
45
45
  include_content = bool(params.pop("content", True))
46
46
  include_metadata = bool(params.pop("metadata", False))
47
- return await self._call(
48
- self.api.list_memories,
49
- include_content=include_content,
50
- include_metadata=include_metadata,
51
- **params,
52
- ) or []
47
+ return (
48
+ await self._call(
49
+ self.api.list_memories,
50
+ include_content=include_content,
51
+ include_metadata=include_metadata,
52
+ **params,
53
+ )
54
+ or []
55
+ )
53
56
 
54
57
  async def similar_memories(
55
58
  self, *, content: str, threshold: float, max_results: int
@@ -58,7 +61,9 @@ class IntelligenceService:
58
61
  "content": content,
59
62
  "threshold": threshold,
60
63
  }
61
- result = await self._call(self.api.find_similar_memories, _strip_none(payload)) or []
64
+ result = (
65
+ await self._call(self.api.find_similar_memories, _strip_none(payload)) or []
66
+ )
62
67
  if isinstance(result, list) and max_results:
63
68
  return result[:max_results]
64
69
  return result
@@ -78,15 +83,20 @@ class IntelligenceService:
78
83
  "sortBy": "priority_score",
79
84
  "sortOrder": "desc",
80
85
  }
81
- return await self._call(
82
- self.api.list_memories,
83
- include_content=False,
84
- include_metadata=False,
85
- **params,
86
- ) or []
86
+ return (
87
+ await self._call(
88
+ self.api.list_memories,
89
+ include_content=False,
90
+ include_metadata=False,
91
+ **params,
92
+ )
93
+ or []
94
+ )
87
95
 
88
96
  async def analytics(self, *, time_range: str, group_by: str) -> Dict[str, Any]:
89
- memories = await self.query_memories(limit=200, timeRange=time_range, groupBy=group_by)
97
+ memories = await self.query_memories(
98
+ limit=200, timeRange=time_range, groupBy=group_by
99
+ )
90
100
  summary: Dict[str, Any] = {
91
101
  "total_memories": len(memories),
92
102
  "by_group": {},
@@ -100,7 +110,9 @@ class IntelligenceService:
100
110
  async def update_memory(self, memory_id: str, **fields: Any) -> Dict[str, Any]:
101
111
  payload = _strip_none(fields)
102
112
  if "importance" in payload:
103
- payload["priority_score"] = _importance_to_priority(payload.pop("importance"))
113
+ payload["priority_score"] = _importance_to_priority(
114
+ payload.pop("importance")
115
+ )
104
116
  mapping = {
105
117
  "documentation_item_id": "documentationItemId",
106
118
  "mode_id": "modeId",