fenix-mcp 0.1.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 +17 -0
- fenix_mcp/application/presenters.py +24 -0
- fenix_mcp/application/tool_base.py +46 -0
- fenix_mcp/application/tool_registry.py +37 -0
- fenix_mcp/application/tools/__init__.py +29 -0
- fenix_mcp/application/tools/health.py +30 -0
- fenix_mcp/application/tools/initialize.py +125 -0
- fenix_mcp/application/tools/intelligence.py +253 -0
- fenix_mcp/application/tools/knowledge.py +905 -0
- fenix_mcp/application/tools/productivity.py +220 -0
- fenix_mcp/application/tools/user_config.py +158 -0
- fenix_mcp/domain/initialization.py +180 -0
- fenix_mcp/domain/intelligence.py +133 -0
- fenix_mcp/domain/knowledge.py +437 -0
- fenix_mcp/domain/productivity.py +184 -0
- fenix_mcp/domain/user_config.py +42 -0
- fenix_mcp/infrastructure/config.py +56 -0
- fenix_mcp/infrastructure/context.py +20 -0
- fenix_mcp/infrastructure/fenix_api/client.py +623 -0
- fenix_mcp/infrastructure/http_client.py +84 -0
- fenix_mcp/infrastructure/logging.py +43 -0
- fenix_mcp/interface/mcp_server.py +78 -0
- fenix_mcp/interface/transports.py +227 -0
- fenix_mcp/main.py +90 -0
- fenix_mcp-0.1.0.dist-info/METADATA +208 -0
- fenix_mcp-0.1.0.dist-info/RECORD +29 -0
- fenix_mcp-0.1.0.dist-info/WHEEL +5 -0
- fenix_mcp-0.1.0.dist-info/entry_points.txt +2 -0
- fenix_mcp-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""HTTP client wrapper aligned with the Fênix Cloud Swagger."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from typing import Any, Dict, Mapping, Optional
|
|
8
|
+
|
|
9
|
+
from fenix_mcp.infrastructure.http_client import HttpClient
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class FenixApiError(RuntimeError):
|
|
13
|
+
"""Represents an error returned by the Fênix API."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _to_query_value(value: Any) -> Any:
|
|
17
|
+
if isinstance(value, bool):
|
|
18
|
+
return "true" if value else "false"
|
|
19
|
+
return value
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _strip_none(data: Mapping[str, Any]) -> Dict[str, Any]:
|
|
23
|
+
return {key: _to_query_value(value) for key, value in data.items() if value is not None}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(slots=True)
|
|
27
|
+
class FenixApiClient:
|
|
28
|
+
"""Facade used by tools to communicate with the REST API."""
|
|
29
|
+
|
|
30
|
+
base_url: str
|
|
31
|
+
personal_access_token: Optional[str]
|
|
32
|
+
core_documents_token: Optional[str] = None
|
|
33
|
+
timeout: float = 30.0
|
|
34
|
+
_http: HttpClient = field(init=False, repr=False)
|
|
35
|
+
|
|
36
|
+
def __post_init__(self) -> None:
|
|
37
|
+
headers: Dict[str, str] = {
|
|
38
|
+
"User-Agent": "fenix-mcp-py/0.1.0",
|
|
39
|
+
"Content-Type": "application/json",
|
|
40
|
+
"Accept": "application/json",
|
|
41
|
+
}
|
|
42
|
+
if self.personal_access_token:
|
|
43
|
+
headers["Authorization"] = f"Bearer {self.personal_access_token}"
|
|
44
|
+
|
|
45
|
+
object.__setattr__(
|
|
46
|
+
self,
|
|
47
|
+
"_http",
|
|
48
|
+
HttpClient(
|
|
49
|
+
base_url=self.base_url,
|
|
50
|
+
timeout=self.timeout,
|
|
51
|
+
default_headers=headers,
|
|
52
|
+
),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# ------------------------------------------------------------------
|
|
56
|
+
# Helpers
|
|
57
|
+
# ------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
def update_token(self, token: Optional[str]) -> None:
|
|
60
|
+
"""Update bearer token used for subsequent requests."""
|
|
61
|
+
|
|
62
|
+
self.personal_access_token = token
|
|
63
|
+
headers = dict(self._http.default_headers or {})
|
|
64
|
+
if token:
|
|
65
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
66
|
+
else:
|
|
67
|
+
headers.pop("Authorization", None)
|
|
68
|
+
self._http.default_headers = headers
|
|
69
|
+
|
|
70
|
+
def update_core_documents_token(self, token: Optional[str]) -> None:
|
|
71
|
+
"""Update the MCP token used to access public core document endpoints."""
|
|
72
|
+
|
|
73
|
+
self.core_documents_token = token
|
|
74
|
+
|
|
75
|
+
def _build_params(
|
|
76
|
+
self,
|
|
77
|
+
*,
|
|
78
|
+
required: Optional[Mapping[str, Any]] = None,
|
|
79
|
+
optional: Optional[Mapping[str, Any]] = None,
|
|
80
|
+
) -> Dict[str, Any]:
|
|
81
|
+
params: Dict[str, Any] = {}
|
|
82
|
+
|
|
83
|
+
if required:
|
|
84
|
+
for key, value in required.items():
|
|
85
|
+
if value is None:
|
|
86
|
+
raise ValueError(f"Missing required query parameter: {key}")
|
|
87
|
+
params[key] = _to_query_value(value)
|
|
88
|
+
|
|
89
|
+
if optional:
|
|
90
|
+
for key, value in optional.items():
|
|
91
|
+
if value is None:
|
|
92
|
+
continue
|
|
93
|
+
params[key] = _to_query_value(value)
|
|
94
|
+
|
|
95
|
+
return params
|
|
96
|
+
|
|
97
|
+
def _request(
|
|
98
|
+
self,
|
|
99
|
+
method: str,
|
|
100
|
+
endpoint: str,
|
|
101
|
+
*,
|
|
102
|
+
params: Optional[Mapping[str, Any]] = None,
|
|
103
|
+
json: Optional[Mapping[str, Any]] = None,
|
|
104
|
+
headers: Optional[Mapping[str, str]] = None,
|
|
105
|
+
) -> Any:
|
|
106
|
+
response = self._http.request(method, endpoint, params=params, json=json, headers=headers)
|
|
107
|
+
if response.status_code == 204:
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
payload = response.json()
|
|
112
|
+
except ValueError as exc: # pragma: no cover - defensive
|
|
113
|
+
raise FenixApiError(f"Invalid JSON response from {endpoint}") from exc
|
|
114
|
+
|
|
115
|
+
if not response.ok:
|
|
116
|
+
message = payload.get("message") or payload.get("error") or response.text
|
|
117
|
+
raise FenixApiError(f"HTTP {response.status_code} calling {endpoint}: {message}")
|
|
118
|
+
|
|
119
|
+
return payload.get("data", payload)
|
|
120
|
+
|
|
121
|
+
# ------------------------------------------------------------------
|
|
122
|
+
# Health / profile
|
|
123
|
+
# ------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
def get_health(self) -> Any:
|
|
126
|
+
return self._request("GET", "/health")
|
|
127
|
+
|
|
128
|
+
def get_profile(self) -> Any:
|
|
129
|
+
return self._request("GET", "/api/auth/profile")
|
|
130
|
+
|
|
131
|
+
# ------------------------------------------------------------------
|
|
132
|
+
# Core documents
|
|
133
|
+
# ------------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
def list_core_documents(self, *, return_content: bool = False) -> Any:
|
|
136
|
+
params = self._build_params(optional={"returnContent": return_content})
|
|
137
|
+
headers = {"x-mcp-token": self.core_documents_token} if self.core_documents_token else None
|
|
138
|
+
return self._request("GET", "/api/core-documents/mcp/all", params=params, headers=headers)
|
|
139
|
+
|
|
140
|
+
def get_core_document_by_name(self, name: str, *, return_content: bool = False) -> Any:
|
|
141
|
+
params = self._build_params(optional={"returnContent": return_content})
|
|
142
|
+
headers = {"x-mcp-token": self.core_documents_token} if self.core_documents_token else None
|
|
143
|
+
return self._request("GET", f"/api/core-documents/mcp/{name}", params=params, headers=headers)
|
|
144
|
+
|
|
145
|
+
def list_core_documents_auth(self, *, return_content: bool = False) -> Any:
|
|
146
|
+
params = self._build_params(optional={"returnContent": return_content})
|
|
147
|
+
return self._request("GET", "/api/core-documents", params=params)
|
|
148
|
+
|
|
149
|
+
def get_core_document(self, name: str, *, return_content: bool = False) -> Any:
|
|
150
|
+
params = self._build_params(optional={"returnContent": return_content})
|
|
151
|
+
return self._request("GET", f"/api/core-documents/{name}", params=params)
|
|
152
|
+
|
|
153
|
+
# ------------------------------------------------------------------
|
|
154
|
+
# User core documents
|
|
155
|
+
# ------------------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
def list_user_core_documents(self, *, return_content: bool = False) -> Any:
|
|
158
|
+
params = self._build_params(optional={"returnContent": return_content})
|
|
159
|
+
return self._request("GET", "/api/user-core-documents", params=params)
|
|
160
|
+
|
|
161
|
+
def get_user_core_document(self, document_id: str, *, return_content: bool = False) -> Any:
|
|
162
|
+
params = self._build_params(optional={"returnContent": return_content})
|
|
163
|
+
return self._request("GET", f"/api/user-core-documents/{document_id}", params=params)
|
|
164
|
+
|
|
165
|
+
def create_user_core_document(self, payload: Mapping[str, Any]) -> Any:
|
|
166
|
+
return self._request("POST", "/api/user-core-documents", json=payload)
|
|
167
|
+
|
|
168
|
+
def update_user_core_document(self, document_id: str, payload: Mapping[str, Any]) -> Any:
|
|
169
|
+
return self._request("PATCH", f"/api/user-core-documents/{document_id}", json=payload)
|
|
170
|
+
|
|
171
|
+
def delete_user_core_document(self, document_id: str) -> Any:
|
|
172
|
+
return self._request("DELETE", f"/api/user-core-documents/{document_id}")
|
|
173
|
+
|
|
174
|
+
# ------------------------------------------------------------------
|
|
175
|
+
# Productivity (todo items)
|
|
176
|
+
# ------------------------------------------------------------------
|
|
177
|
+
|
|
178
|
+
def create_todo_item(self, payload: Mapping[str, Any]) -> Any:
|
|
179
|
+
return self._request("POST", "/api/todo-items", json=payload)
|
|
180
|
+
|
|
181
|
+
def list_todo_items(self, **filters: Any) -> Any:
|
|
182
|
+
params = _strip_none(filters)
|
|
183
|
+
return self._request("GET", "/api/todo-items", params=params)
|
|
184
|
+
|
|
185
|
+
def get_todo_item(self, item_id: str, *, return_content: bool = False) -> Any:
|
|
186
|
+
params = self._build_params(optional={"returnContent": return_content})
|
|
187
|
+
return self._request("GET", f"/api/todo-items/{item_id}", params=params)
|
|
188
|
+
|
|
189
|
+
def update_todo_item(self, item_id: str, payload: Mapping[str, Any]) -> Any:
|
|
190
|
+
return self._request("PATCH", f"/api/todo-items/{item_id}", json=payload)
|
|
191
|
+
|
|
192
|
+
def delete_todo_item(self, item_id: str) -> Any:
|
|
193
|
+
return self._request("DELETE", f"/api/todo-items/{item_id}")
|
|
194
|
+
|
|
195
|
+
def update_todo_item_status(self, item_id: str, payload: Mapping[str, Any]) -> Any:
|
|
196
|
+
return self._request("PATCH", f"/api/todo-items/{item_id}/status", json=payload)
|
|
197
|
+
|
|
198
|
+
def update_todo_item_priority(self, item_id: str, payload: Mapping[str, Any]) -> Any:
|
|
199
|
+
return self._request("PATCH", f"/api/todo-items/{item_id}/priority", json=payload)
|
|
200
|
+
|
|
201
|
+
def get_todo_stats(self) -> Any:
|
|
202
|
+
return self._request("GET", "/api/todo-items/stats")
|
|
203
|
+
|
|
204
|
+
def get_todo_categories(self) -> Any:
|
|
205
|
+
return self._request("GET", "/api/todo-items/categories")
|
|
206
|
+
|
|
207
|
+
def get_todo_tags(self) -> Any:
|
|
208
|
+
return self._request("GET", "/api/todo-items/tags")
|
|
209
|
+
|
|
210
|
+
def search_todo_items(self, *, query: str) -> Any:
|
|
211
|
+
params = self._build_params(required={"q": query})
|
|
212
|
+
return self._request("GET", "/api/todo-items/search", params=params)
|
|
213
|
+
|
|
214
|
+
def list_todo_overdue(self) -> Any:
|
|
215
|
+
return self._request("GET", "/api/todo-items/overdue")
|
|
216
|
+
|
|
217
|
+
def list_todo_upcoming(self, *, days: Optional[int] = None) -> Any:
|
|
218
|
+
params = self._build_params(optional={"days": days})
|
|
219
|
+
return self._request("GET", "/api/todo-items/upcoming", params=params)
|
|
220
|
+
|
|
221
|
+
# ------------------------------------------------------------------
|
|
222
|
+
# Memories
|
|
223
|
+
# ------------------------------------------------------------------
|
|
224
|
+
|
|
225
|
+
def create_memory(self, payload: Mapping[str, Any]) -> Any:
|
|
226
|
+
return self._request("POST", "/api/memories", json=payload)
|
|
227
|
+
|
|
228
|
+
def list_memories(
|
|
229
|
+
self,
|
|
230
|
+
*,
|
|
231
|
+
include_content: bool = True,
|
|
232
|
+
include_metadata: bool = True,
|
|
233
|
+
**filters: Any,
|
|
234
|
+
) -> Any:
|
|
235
|
+
params = self._build_params(
|
|
236
|
+
required={"content": include_content, "metadata": include_metadata},
|
|
237
|
+
optional=filters,
|
|
238
|
+
)
|
|
239
|
+
return self._request("GET", "/api/memories", params=params)
|
|
240
|
+
|
|
241
|
+
def get_memory(
|
|
242
|
+
self,
|
|
243
|
+
memory_id: str,
|
|
244
|
+
*,
|
|
245
|
+
include_content: bool = True,
|
|
246
|
+
include_metadata: bool = True,
|
|
247
|
+
) -> Any:
|
|
248
|
+
params = self._build_params(required={"content": include_content, "metadata": include_metadata})
|
|
249
|
+
return self._request("GET", f"/api/memories/{memory_id}", params=params)
|
|
250
|
+
|
|
251
|
+
def update_memory(self, memory_id: str, payload: Mapping[str, Any]) -> Any:
|
|
252
|
+
return self._request("PATCH", f"/api/memories/{memory_id}", json=payload)
|
|
253
|
+
|
|
254
|
+
def delete_memory(self, memory_id: str) -> Any:
|
|
255
|
+
return self._request("DELETE", f"/api/memories/{memory_id}")
|
|
256
|
+
|
|
257
|
+
def list_memories_by_tags(self, *, tags: str) -> Any:
|
|
258
|
+
params = self._build_params(required={"tags": tags})
|
|
259
|
+
return self._request("GET", "/api/memories/tags", params=params)
|
|
260
|
+
|
|
261
|
+
def record_memory_access(self, memory_id: str) -> Any:
|
|
262
|
+
return self._request("POST", f"/api/memories/{memory_id}/access")
|
|
263
|
+
|
|
264
|
+
def find_similar_memories(self, payload: Mapping[str, Any]) -> Any:
|
|
265
|
+
return self._request("POST", "/api/memory-intelligence/similarity", json=payload)
|
|
266
|
+
|
|
267
|
+
def consolidate_memories(self, payload: Mapping[str, Any]) -> Any:
|
|
268
|
+
return self._request("POST", "/api/memory-intelligence/consolidate", json=payload)
|
|
269
|
+
|
|
270
|
+
def smart_create_memory(self, payload: Mapping[str, Any]) -> Any:
|
|
271
|
+
return self._request("POST", "/api/memory-intelligence/smart-create", json=payload)
|
|
272
|
+
|
|
273
|
+
# ------------------------------------------------------------------
|
|
274
|
+
# Configuration: modes and rules
|
|
275
|
+
# ------------------------------------------------------------------
|
|
276
|
+
|
|
277
|
+
def list_modes(
|
|
278
|
+
self,
|
|
279
|
+
*,
|
|
280
|
+
include_rules: Optional[bool] = None,
|
|
281
|
+
return_description: Optional[bool] = None,
|
|
282
|
+
return_metadata: Optional[bool] = None,
|
|
283
|
+
) -> Any:
|
|
284
|
+
params = self._build_params(
|
|
285
|
+
optional={
|
|
286
|
+
"includeRules": include_rules,
|
|
287
|
+
"returnDescription": return_description,
|
|
288
|
+
"returnMetadata": return_metadata,
|
|
289
|
+
}
|
|
290
|
+
)
|
|
291
|
+
return self._request("GET", "/api/modes", params=params)
|
|
292
|
+
|
|
293
|
+
def get_mode(
|
|
294
|
+
self,
|
|
295
|
+
mode_id: str,
|
|
296
|
+
*,
|
|
297
|
+
return_description: Optional[bool] = None,
|
|
298
|
+
return_metadata: Optional[bool] = None,
|
|
299
|
+
) -> Any:
|
|
300
|
+
params = self._build_params(
|
|
301
|
+
optional={
|
|
302
|
+
"returnDescription": return_description,
|
|
303
|
+
"returnMetadata": return_metadata,
|
|
304
|
+
}
|
|
305
|
+
)
|
|
306
|
+
return self._request("GET", f"/api/modes/{mode_id}", params=params)
|
|
307
|
+
|
|
308
|
+
def create_mode(self, payload: Mapping[str, Any]) -> Any:
|
|
309
|
+
return self._request("POST", "/api/modes", json=payload)
|
|
310
|
+
|
|
311
|
+
def update_mode(self, mode_id: str, payload: Mapping[str, Any]) -> Any:
|
|
312
|
+
return self._request("PATCH", f"/api/modes/{mode_id}", json=payload)
|
|
313
|
+
|
|
314
|
+
def delete_mode(self, mode_id: str) -> Any:
|
|
315
|
+
return self._request("DELETE", f"/api/modes/{mode_id}")
|
|
316
|
+
|
|
317
|
+
def list_rules(
|
|
318
|
+
self,
|
|
319
|
+
*,
|
|
320
|
+
return_description: Optional[bool] = None,
|
|
321
|
+
return_metadata: Optional[bool] = None,
|
|
322
|
+
return_modes: Optional[bool] = None,
|
|
323
|
+
) -> Any:
|
|
324
|
+
params = self._build_params(
|
|
325
|
+
optional={
|
|
326
|
+
"returnDescription": return_description,
|
|
327
|
+
"returnMetadata": return_metadata,
|
|
328
|
+
"returnModes": return_modes,
|
|
329
|
+
}
|
|
330
|
+
)
|
|
331
|
+
return self._request("GET", "/api/rules", params=params)
|
|
332
|
+
|
|
333
|
+
def get_rule(
|
|
334
|
+
self,
|
|
335
|
+
rule_id: str,
|
|
336
|
+
*,
|
|
337
|
+
return_description: Optional[bool] = None,
|
|
338
|
+
return_metadata: Optional[bool] = None,
|
|
339
|
+
return_modes: Optional[bool] = None,
|
|
340
|
+
) -> Any:
|
|
341
|
+
params = self._build_params(
|
|
342
|
+
optional={
|
|
343
|
+
"returnDescription": return_description,
|
|
344
|
+
"returnMetadata": return_metadata,
|
|
345
|
+
"returnModes": return_modes,
|
|
346
|
+
}
|
|
347
|
+
)
|
|
348
|
+
return self._request("GET", f"/api/rules/{rule_id}", params=params)
|
|
349
|
+
|
|
350
|
+
def create_rule(self, payload: Mapping[str, Any]) -> Any:
|
|
351
|
+
return self._request("POST", "/api/rules", json=payload)
|
|
352
|
+
|
|
353
|
+
def update_rule(self, rule_id: str, payload: Mapping[str, Any]) -> Any:
|
|
354
|
+
return self._request("PATCH", f"/api/rules/{rule_id}", json=payload)
|
|
355
|
+
|
|
356
|
+
def delete_rule(self, rule_id: str) -> Any:
|
|
357
|
+
return self._request("DELETE", f"/api/rules/{rule_id}")
|
|
358
|
+
|
|
359
|
+
def add_mode_rule(self, mode_id: str, rule_id: str) -> Any:
|
|
360
|
+
payload = {"modeId": mode_id, "ruleId": rule_id}
|
|
361
|
+
return self._request("POST", "/api/mode-rules", json=payload)
|
|
362
|
+
|
|
363
|
+
def remove_mode_rule(self, mode_id: str, rule_id: str) -> Any:
|
|
364
|
+
return self._request("DELETE", f"/api/mode-rules/mode/{mode_id}/rule/{rule_id}")
|
|
365
|
+
|
|
366
|
+
def list_rules_by_mode(self, mode_id: str) -> Any:
|
|
367
|
+
return self._request("GET", f"/api/mode-rules/mode/{mode_id}/rules")
|
|
368
|
+
|
|
369
|
+
def list_modes_by_rule(self, rule_id: str) -> Any:
|
|
370
|
+
return self._request("GET", f"/api/mode-rules/rule/{rule_id}/modes")
|
|
371
|
+
|
|
372
|
+
# ------------------------------------------------------------------
|
|
373
|
+
# Knowledge: documentation
|
|
374
|
+
# ------------------------------------------------------------------
|
|
375
|
+
|
|
376
|
+
def list_documentation_items(self, **filters: Any) -> Any:
|
|
377
|
+
return self._request("GET", "/api/documentation", params=_strip_none(filters))
|
|
378
|
+
|
|
379
|
+
def get_documentation_item(self, item_id: str, **filters: Any) -> Any:
|
|
380
|
+
return self._request("GET", f"/api/documentation/{item_id}", params=_strip_none(filters))
|
|
381
|
+
|
|
382
|
+
def create_documentation_item(self, payload: Mapping[str, Any]) -> Any:
|
|
383
|
+
return self._request("POST", "/api/documentation", json=payload)
|
|
384
|
+
|
|
385
|
+
def update_documentation_item(self, item_id: str, payload: Mapping[str, Any]) -> Any:
|
|
386
|
+
return self._request("PATCH", f"/api/documentation/{item_id}", json=payload)
|
|
387
|
+
|
|
388
|
+
def delete_documentation_item(self, item_id: str) -> Any:
|
|
389
|
+
return self._request("DELETE", f"/api/documentation/{item_id}")
|
|
390
|
+
|
|
391
|
+
def get_documentation_children(self, item_id: str) -> Any:
|
|
392
|
+
return self._request("GET", f"/api/documentation/{item_id}/children")
|
|
393
|
+
|
|
394
|
+
def get_documentation_tree(self, item_id: str) -> Any:
|
|
395
|
+
return self._request("GET", f"/api/documentation/{item_id}/tree")
|
|
396
|
+
|
|
397
|
+
def get_documentation_full_tree(self) -> Any:
|
|
398
|
+
return self._request("GET", "/api/documentation/tree")
|
|
399
|
+
|
|
400
|
+
def search_documentation_items(self, *, query: str, team_id: str, limit: int) -> Any:
|
|
401
|
+
params = self._build_params(required={"q": query, "team_id": team_id, "limit": limit})
|
|
402
|
+
return self._request("GET", "/api/documentation/search", params=params)
|
|
403
|
+
|
|
404
|
+
def list_documentation_roots(self, *, team_id: str) -> Any:
|
|
405
|
+
params = self._build_params(required={"team_id": team_id})
|
|
406
|
+
return self._request("GET", "/api/documentation/roots", params=params)
|
|
407
|
+
|
|
408
|
+
def list_documentation_recent(self, *, team_id: str, limit: int) -> Any:
|
|
409
|
+
params = self._build_params(required={"team_id": team_id, "limit": limit})
|
|
410
|
+
return self._request("GET", "/api/documentation/recent", params=params)
|
|
411
|
+
|
|
412
|
+
def get_documentation_analytics(self, *, team_id: str) -> Any:
|
|
413
|
+
params = self._build_params(required={"team_id": team_id})
|
|
414
|
+
return self._request("GET", "/api/documentation/analytics", params=params)
|
|
415
|
+
|
|
416
|
+
def move_documentation_item(self, item_id: str, payload: Mapping[str, Any]) -> Any:
|
|
417
|
+
return self._request("PATCH", f"/api/documentation/{item_id}/move", json=payload)
|
|
418
|
+
|
|
419
|
+
def publish_documentation_item(self, item_id: str) -> Any:
|
|
420
|
+
return self._request("PATCH", f"/api/documentation/{item_id}/publish")
|
|
421
|
+
|
|
422
|
+
def create_documentation_version(self, item_id: str, payload: Mapping[str, Any]) -> Any:
|
|
423
|
+
return self._request("POST", f"/api/documentation/{item_id}/version", json=payload)
|
|
424
|
+
|
|
425
|
+
def duplicate_documentation_item(self, item_id: str, payload: Mapping[str, Any]) -> Any:
|
|
426
|
+
return self._request("POST", f"/api/documentation/{item_id}/duplicate", json=payload)
|
|
427
|
+
|
|
428
|
+
# ------------------------------------------------------------------
|
|
429
|
+
# Knowledge: work items
|
|
430
|
+
# ------------------------------------------------------------------
|
|
431
|
+
|
|
432
|
+
def create_work_item(self, payload: Mapping[str, Any]) -> Any:
|
|
433
|
+
return self._request("POST", "/api/work-items", json=payload)
|
|
434
|
+
|
|
435
|
+
def list_work_items(self, **filters: Any) -> Any:
|
|
436
|
+
return self._request("GET", "/api/work-items", params=_strip_none(filters))
|
|
437
|
+
|
|
438
|
+
def get_work_item(self, item_id: str) -> Any:
|
|
439
|
+
return self._request("GET", f"/api/work-items/{item_id}")
|
|
440
|
+
|
|
441
|
+
def update_work_item(self, item_id: str, payload: Mapping[str, Any]) -> Any:
|
|
442
|
+
return self._request("PATCH", f"/api/work-items/{item_id}", json=payload)
|
|
443
|
+
|
|
444
|
+
def delete_work_item(self, item_id: str) -> Any:
|
|
445
|
+
return self._request("DELETE", f"/api/work-items/{item_id}")
|
|
446
|
+
|
|
447
|
+
def list_work_items_backlog(self, *, team_id: str) -> Any:
|
|
448
|
+
params = self._build_params(required={"team_id": team_id})
|
|
449
|
+
return self._request("GET", "/api/work-items/backlog", params=params)
|
|
450
|
+
|
|
451
|
+
def search_work_items(self, *, query: str, team_id: str, limit: int) -> Any:
|
|
452
|
+
params = self._build_params(required={"q": query, "team_id": team_id, "limit": limit})
|
|
453
|
+
return self._request("GET", "/api/work-items/search", params=params)
|
|
454
|
+
|
|
455
|
+
def get_work_items_analytics(self, *, team_id: str) -> Any:
|
|
456
|
+
params = self._build_params(required={"team_id": team_id})
|
|
457
|
+
return self._request("GET", "/api/work-items/analytics", params=params)
|
|
458
|
+
|
|
459
|
+
def get_work_items_velocity(self, *, team_id: str, sprints_count: int) -> Any:
|
|
460
|
+
params = self._build_params(required={"sprints_count": sprints_count})
|
|
461
|
+
return self._request("GET", f"/api/work-items/velocity/{team_id}", params=params)
|
|
462
|
+
|
|
463
|
+
def list_work_items_by_sprint(self, *, sprint_id: str) -> Any:
|
|
464
|
+
return self._request("GET", f"/api/work-items/by-sprint/{sprint_id}")
|
|
465
|
+
|
|
466
|
+
def get_work_items_burndown(self, *, sprint_id: str) -> Any:
|
|
467
|
+
return self._request("GET", f"/api/work-items/burndown/{sprint_id}")
|
|
468
|
+
|
|
469
|
+
def list_work_items_by_epic(self, *, epic_id: str) -> Any:
|
|
470
|
+
return self._request("GET", f"/api/work-items/by-epic/{epic_id}")
|
|
471
|
+
|
|
472
|
+
def get_work_items_epic_progress(self, *, epic_id: str) -> Any:
|
|
473
|
+
return self._request("GET", f"/api/work-items/epic-progress/{epic_id}")
|
|
474
|
+
|
|
475
|
+
def list_work_items_by_board(self, *, board_id: str) -> Any:
|
|
476
|
+
return self._request("GET", f"/api/work-items/by-board/{board_id}")
|
|
477
|
+
|
|
478
|
+
def get_work_item_children(self, item_id: str) -> Any:
|
|
479
|
+
return self._request("GET", f"/api/work-items/{item_id}/children")
|
|
480
|
+
|
|
481
|
+
def move_work_item(self, item_id: str, payload: Mapping[str, Any]) -> Any:
|
|
482
|
+
return self._request("PATCH", f"/api/work-items/{item_id}/move", json=payload)
|
|
483
|
+
|
|
484
|
+
def update_work_item_status(self, item_id: str, payload: Mapping[str, Any]) -> Any:
|
|
485
|
+
return self._request("PATCH", f"/api/work-items/{item_id}/status", json=payload)
|
|
486
|
+
|
|
487
|
+
def move_work_item_to_board(self, item_id: str, payload: Mapping[str, Any]) -> Any:
|
|
488
|
+
return self._request("PATCH", f"/api/work-items/{item_id}/board", json=payload)
|
|
489
|
+
|
|
490
|
+
def link_work_item(self, item_id: str, payload: Mapping[str, Any]) -> Any:
|
|
491
|
+
return self._request("PATCH", f"/api/work-items/{item_id}/link", json=payload)
|
|
492
|
+
|
|
493
|
+
def assign_work_items_to_sprint(self, payload: Mapping[str, Any]) -> Any:
|
|
494
|
+
return self._request("POST", "/api/work-items/assign-to-sprint", json=payload)
|
|
495
|
+
|
|
496
|
+
def bulk_update_work_items(self, payload: Mapping[str, Any]) -> Any:
|
|
497
|
+
return self._request("PATCH", "/api/work-items/bulk-update", json=payload)
|
|
498
|
+
|
|
499
|
+
# ------------------------------------------------------------------
|
|
500
|
+
# Knowledge: boards
|
|
501
|
+
# ------------------------------------------------------------------
|
|
502
|
+
|
|
503
|
+
def create_work_board(self, payload: Mapping[str, Any]) -> Any:
|
|
504
|
+
return self._request("POST", "/api/work-boards", json=payload)
|
|
505
|
+
|
|
506
|
+
def list_work_boards(self, **filters: Any) -> Any:
|
|
507
|
+
return self._request("GET", "/api/work-boards", params=_strip_none(filters))
|
|
508
|
+
|
|
509
|
+
def list_work_boards_by_team(self, *, team_id: str) -> Any:
|
|
510
|
+
return self._request("GET", f"/api/work-boards/by-team/{team_id}")
|
|
511
|
+
|
|
512
|
+
def list_favorite_work_boards(self) -> Any:
|
|
513
|
+
return self._request("GET", "/api/work-boards/favorites")
|
|
514
|
+
|
|
515
|
+
def search_work_boards(self, *, query: str, team_id: str, limit: int) -> Any:
|
|
516
|
+
params = self._build_params(required={"q": query, "team_id": team_id, "limit": limit})
|
|
517
|
+
return self._request("GET", "/api/work-boards/search", params=params)
|
|
518
|
+
|
|
519
|
+
def list_recent_work_boards(self, *, limit: int) -> Any:
|
|
520
|
+
params = self._build_params(required={"limit": limit})
|
|
521
|
+
return self._request("GET", "/api/work-boards/recent", params=params)
|
|
522
|
+
|
|
523
|
+
def get_work_board(self, board_id: str) -> Any:
|
|
524
|
+
return self._request("GET", f"/api/work-boards/{board_id}")
|
|
525
|
+
|
|
526
|
+
def update_work_board(self, board_id: str, payload: Mapping[str, Any]) -> Any:
|
|
527
|
+
return self._request("PATCH", f"/api/work-boards/{board_id}", json=payload)
|
|
528
|
+
|
|
529
|
+
def delete_work_board(self, board_id: str) -> Any:
|
|
530
|
+
return self._request("DELETE", f"/api/work-boards/{board_id}")
|
|
531
|
+
|
|
532
|
+
def get_work_board_analytics(self, board_id: str) -> Any:
|
|
533
|
+
return self._request("GET", f"/api/work-boards/{board_id}/analytics")
|
|
534
|
+
|
|
535
|
+
def list_work_board_columns(self, board_id: str) -> Any:
|
|
536
|
+
return self._request("GET", f"/api/work-boards/{board_id}/columns")
|
|
537
|
+
|
|
538
|
+
def toggle_work_board_favorite(self, board_id: str, payload: Mapping[str, Any]) -> Any:
|
|
539
|
+
return self._request("PATCH", f"/api/work-boards/{board_id}/favorite", json=payload)
|
|
540
|
+
|
|
541
|
+
def clone_work_board(self, board_id: str, payload: Mapping[str, Any]) -> Any:
|
|
542
|
+
return self._request("POST", f"/api/work-boards/{board_id}/clone", json=payload)
|
|
543
|
+
|
|
544
|
+
def reorder_work_boards(self, payload: Mapping[str, Any]) -> Any:
|
|
545
|
+
return self._request("PATCH", "/api/work-boards/reorder", json=payload)
|
|
546
|
+
|
|
547
|
+
def create_work_board_column(self, payload: Mapping[str, Any]) -> Any:
|
|
548
|
+
return self._request("POST", "/api/work-boards/columns", json=payload)
|
|
549
|
+
|
|
550
|
+
def reorder_work_board_columns(self, payload: Mapping[str, Any]) -> Any:
|
|
551
|
+
return self._request("PATCH", "/api/work-boards/columns/reorder", json=payload)
|
|
552
|
+
|
|
553
|
+
def get_work_board_column(self, column_id: str) -> Any:
|
|
554
|
+
return self._request("GET", f"/api/work-boards/columns/{column_id}")
|
|
555
|
+
|
|
556
|
+
def update_work_board_column(self, column_id: str, payload: Mapping[str, Any]) -> Any:
|
|
557
|
+
return self._request("PATCH", f"/api/work-boards/columns/{column_id}", json=payload)
|
|
558
|
+
|
|
559
|
+
def delete_work_board_column(self, column_id: str) -> Any:
|
|
560
|
+
return self._request("DELETE", f"/api/work-boards/columns/{column_id}")
|
|
561
|
+
|
|
562
|
+
# ------------------------------------------------------------------
|
|
563
|
+
# Knowledge: sprints
|
|
564
|
+
# ------------------------------------------------------------------
|
|
565
|
+
|
|
566
|
+
def create_sprint(self, payload: Mapping[str, Any]) -> Any:
|
|
567
|
+
return self._request("POST", "/api/sprints", json=payload)
|
|
568
|
+
|
|
569
|
+
def list_sprints(self, **filters: Any) -> Any:
|
|
570
|
+
return self._request("GET", "/api/sprints", params=_strip_none(filters))
|
|
571
|
+
|
|
572
|
+
def get_sprint(self, sprint_id: str) -> Any:
|
|
573
|
+
return self._request("GET", f"/api/sprints/{sprint_id}")
|
|
574
|
+
|
|
575
|
+
def update_sprint(self, sprint_id: str, payload: Mapping[str, Any]) -> Any:
|
|
576
|
+
return self._request("PATCH", f"/api/sprints/{sprint_id}", json=payload)
|
|
577
|
+
|
|
578
|
+
def delete_sprint(self, sprint_id: str) -> Any:
|
|
579
|
+
return self._request("DELETE", f"/api/sprints/{sprint_id}")
|
|
580
|
+
|
|
581
|
+
def list_sprints_by_team(self, *, team_id: str) -> Any:
|
|
582
|
+
return self._request("GET", f"/api/sprints/by-team/{team_id}")
|
|
583
|
+
|
|
584
|
+
def list_recent_sprints(self, *, team_id: str, limit: int) -> Any:
|
|
585
|
+
params = self._build_params(required={"limit": limit})
|
|
586
|
+
return self._request("GET", f"/api/sprints/recent/{team_id}", params=params)
|
|
587
|
+
|
|
588
|
+
def search_sprints(self, *, query: str, team_id: str, limit: int) -> Any:
|
|
589
|
+
params = self._build_params(required={"q": query, "team_id": team_id, "limit": limit})
|
|
590
|
+
return self._request("GET", "/api/sprints/search", params=params)
|
|
591
|
+
|
|
592
|
+
def get_active_sprint(self, *, team_id: str) -> Any:
|
|
593
|
+
return self._request("GET", f"/api/sprints/active/{team_id}")
|
|
594
|
+
|
|
595
|
+
def get_sprints_velocity(self) -> Any:
|
|
596
|
+
return self._request("GET", "/api/sprints/velocity")
|
|
597
|
+
|
|
598
|
+
def get_sprints_burndown(self) -> Any:
|
|
599
|
+
return self._request("GET", "/api/sprints/burndown")
|
|
600
|
+
|
|
601
|
+
def get_sprint_work_items(self, sprint_id: str) -> Any:
|
|
602
|
+
return self._request("GET", f"/api/sprints/{sprint_id}/work-items")
|
|
603
|
+
|
|
604
|
+
def add_work_items_to_sprint(self, sprint_id: str, payload: Mapping[str, Any]) -> Any:
|
|
605
|
+
return self._request("POST", f"/api/sprints/{sprint_id}/work-items", json=payload)
|
|
606
|
+
|
|
607
|
+
def remove_work_items_from_sprint(self, payload: Mapping[str, Any]) -> Any:
|
|
608
|
+
return self._request("DELETE", "/api/sprints/work-items", json=payload)
|
|
609
|
+
|
|
610
|
+
def get_sprint_analytics(self, sprint_id: str) -> Any:
|
|
611
|
+
return self._request("GET", f"/api/sprints/{sprint_id}/analytics")
|
|
612
|
+
|
|
613
|
+
def get_sprint_capacity(self, sprint_id: str) -> Any:
|
|
614
|
+
return self._request("GET", f"/api/sprints/{sprint_id}/capacity")
|
|
615
|
+
|
|
616
|
+
def start_sprint(self, sprint_id: str, payload: Mapping[str, Any]) -> Any:
|
|
617
|
+
return self._request("PATCH", f"/api/sprints/{sprint_id}/start", json=payload)
|
|
618
|
+
|
|
619
|
+
def complete_sprint(self, sprint_id: str, payload: Mapping[str, Any]) -> Any:
|
|
620
|
+
return self._request("PATCH", f"/api/sprints/{sprint_id}/complete", json=payload)
|
|
621
|
+
|
|
622
|
+
def cancel_sprint(self, sprint_id: str) -> Any:
|
|
623
|
+
return self._request("PATCH", f"/api/sprints/{sprint_id}/cancel")
|