fenix-mcp 1.5.0__py3-none-any.whl → 1.6.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 +1 -1
- fenix_mcp/application/tools/knowledge.py +102 -43
- fenix_mcp/domain/knowledge.py +6 -0
- fenix_mcp/infrastructure/fenix_api/client.py +6 -0
- {fenix_mcp-1.5.0.dist-info → fenix_mcp-1.6.0.dist-info}/METADATA +1 -1
- {fenix_mcp-1.5.0.dist-info → fenix_mcp-1.6.0.dist-info}/RECORD +9 -9
- {fenix_mcp-1.5.0.dist-info → fenix_mcp-1.6.0.dist-info}/WHEEL +0 -0
- {fenix_mcp-1.5.0.dist-info → fenix_mcp-1.6.0.dist-info}/entry_points.txt +0 -0
- {fenix_mcp-1.5.0.dist-info → fenix_mcp-1.6.0.dist-info}/top_level.txt +0 -0
fenix_mcp/__init__.py
CHANGED
|
@@ -39,18 +39,20 @@ class KnowledgeAction(str, Enum):
|
|
|
39
39
|
# Work items
|
|
40
40
|
WORK_CREATE = (
|
|
41
41
|
"work_create",
|
|
42
|
-
"Creates a work item with title, status and optional links.",
|
|
42
|
+
"Creates a work item with title, status and optional links. Use parent_key (e.g., DVPT-0001) to set parent.",
|
|
43
43
|
)
|
|
44
44
|
WORK_LIST = (
|
|
45
45
|
"work_list",
|
|
46
46
|
"Lists work items with status, priority and context filters.",
|
|
47
47
|
)
|
|
48
|
-
WORK_GET = (
|
|
48
|
+
WORK_GET = (
|
|
49
|
+
"work_get",
|
|
50
|
+
"Gets full details of a work item by ID or key (e.g., DVPT-0001).",
|
|
51
|
+
)
|
|
49
52
|
WORK_UPDATE = (
|
|
50
53
|
"work_update",
|
|
51
|
-
"Updates
|
|
54
|
+
"Updates allowed fields of an existing work item (title, description, priority, story_points, tags, due_date).",
|
|
52
55
|
)
|
|
53
|
-
WORK_DELETE = ("work_delete", "Permanently removes a work item.")
|
|
54
56
|
WORK_BACKLOG = ("work_backlog", "Lists backlog items for a team.")
|
|
55
57
|
WORK_SEARCH = (
|
|
56
58
|
"work_search",
|
|
@@ -60,7 +62,10 @@ class KnowledgeAction(str, Enum):
|
|
|
60
62
|
WORK_BY_BOARD = ("work_by_board", "Lists work items associated with a board.")
|
|
61
63
|
WORK_BY_SPRINT = ("work_by_sprint", "Lists work items associated with a sprint.")
|
|
62
64
|
WORK_BY_EPIC = ("work_by_epic", "Lists work items associated with an epic.")
|
|
63
|
-
WORK_CHILDREN = (
|
|
65
|
+
WORK_CHILDREN = (
|
|
66
|
+
"work_children",
|
|
67
|
+
"Lists child work items of a parent item by ID or key (e.g., DVPT-0001).",
|
|
68
|
+
)
|
|
64
69
|
WORK_STATUS_UPDATE = (
|
|
65
70
|
"work_status_update",
|
|
66
71
|
"Updates only the status of a work item.",
|
|
@@ -69,6 +74,10 @@ class KnowledgeAction(str, Enum):
|
|
|
69
74
|
"work_assign_sprint",
|
|
70
75
|
"Assigns work items to a sprint.",
|
|
71
76
|
)
|
|
77
|
+
WORK_ASSIGN_TO_ME = (
|
|
78
|
+
"work_assign_to_me",
|
|
79
|
+
"Assigns a work item to the current user.",
|
|
80
|
+
)
|
|
72
81
|
|
|
73
82
|
# Boards
|
|
74
83
|
BOARD_LIST = ("board_list", "Lists available boards with optional filters.")
|
|
@@ -203,6 +212,10 @@ class KnowledgeRequest(ToolRequest):
|
|
|
203
212
|
)
|
|
204
213
|
|
|
205
214
|
# Work item fields
|
|
215
|
+
work_key: Optional[str] = Field(
|
|
216
|
+
default=None,
|
|
217
|
+
description="Work item key (e.g., DVPT-0001). Use this instead of id for work_get and work_children.",
|
|
218
|
+
)
|
|
206
219
|
work_title: Optional[TitleStr] = Field(default=None, description="Work item title.")
|
|
207
220
|
work_description: Optional[MarkdownStr] = Field(
|
|
208
221
|
default=None, description="Work item description (Markdown)."
|
|
@@ -213,7 +226,7 @@ class KnowledgeRequest(ToolRequest):
|
|
|
213
226
|
)
|
|
214
227
|
work_status: Optional[str] = Field(
|
|
215
228
|
default=None,
|
|
216
|
-
description="Work item status (
|
|
229
|
+
description="Work item status ID (UUID of TeamStatus).",
|
|
217
230
|
)
|
|
218
231
|
work_priority: Optional[str] = Field(
|
|
219
232
|
default=None,
|
|
@@ -230,7 +243,12 @@ class KnowledgeRequest(ToolRequest):
|
|
|
230
243
|
default=None, description="Assignee ID (UUID)."
|
|
231
244
|
)
|
|
232
245
|
parent_id: Optional[UUIDStr] = Field(
|
|
233
|
-
default=None,
|
|
246
|
+
default=None,
|
|
247
|
+
description="Parent item ID (UUID). Prefer using parent_key instead.",
|
|
248
|
+
)
|
|
249
|
+
parent_key: Optional[str] = Field(
|
|
250
|
+
default=None,
|
|
251
|
+
description="Parent item key (e.g., DVPT-0001). Use this to set the parent of a work item.",
|
|
234
252
|
)
|
|
235
253
|
work_due_date: Optional[DateTimeStr] = Field(
|
|
236
254
|
default=None, description="Work item due date (ISO 8601)."
|
|
@@ -372,19 +390,25 @@ class KnowledgeTool(Tool):
|
|
|
372
390
|
return text(
|
|
373
391
|
"❌ Provide work_category to create the item. Values: backend, frontend, mobile, fullstack, devops, infra, platform, sre, database, security, data, analytics, ai_ml, qa, automation, design, research, product, project, agile, support, operations, documentation, training, architecture, planning, development."
|
|
374
392
|
)
|
|
393
|
+
|
|
394
|
+
# Resolve parent_key to parent_id if provided
|
|
395
|
+
parent_id = payload.parent_id
|
|
396
|
+
if payload.parent_key and not parent_id:
|
|
397
|
+
parent_work = await self._service.work_get_by_key(payload.parent_key)
|
|
398
|
+
parent_id = parent_work.get("id")
|
|
399
|
+
|
|
375
400
|
work = await self._service.work_create(
|
|
376
401
|
{
|
|
377
402
|
"title": payload.work_title,
|
|
378
403
|
"description": payload.work_description,
|
|
379
404
|
"item_type": payload.work_type,
|
|
380
|
-
"
|
|
405
|
+
"status_id": payload.work_status,
|
|
381
406
|
"priority": payload.work_priority,
|
|
382
407
|
"work_category": payload.work_category,
|
|
383
408
|
"story_points": payload.story_points,
|
|
384
409
|
"assignee_id": payload.assignee_id,
|
|
385
410
|
"sprint_id": payload.sprint_id,
|
|
386
|
-
"
|
|
387
|
-
"parent_id": payload.parent_id,
|
|
411
|
+
"parent_id": parent_id,
|
|
388
412
|
"due_date": payload.work_due_date,
|
|
389
413
|
"tags": payload.work_tags,
|
|
390
414
|
}
|
|
@@ -399,7 +423,6 @@ class KnowledgeTool(Tool):
|
|
|
399
423
|
type=payload.work_type,
|
|
400
424
|
assignee=payload.assignee_id,
|
|
401
425
|
sprint=payload.sprint_id,
|
|
402
|
-
board=payload.board_id,
|
|
403
426
|
)
|
|
404
427
|
if not items:
|
|
405
428
|
return text("🎯 No work items found.")
|
|
@@ -407,41 +430,51 @@ class KnowledgeTool(Tool):
|
|
|
407
430
|
return text(f"🎯 **Work items ({len(items)}):**\n\n{body}")
|
|
408
431
|
|
|
409
432
|
if action is KnowledgeAction.WORK_GET:
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
433
|
+
# Support both id and work_key
|
|
434
|
+
if payload.work_key:
|
|
435
|
+
work = await self._service.work_get_by_key(payload.work_key)
|
|
436
|
+
elif payload.id:
|
|
437
|
+
work = await self._service.work_get(payload.id)
|
|
438
|
+
else:
|
|
439
|
+
return text("❌ Provide id or work_key to get the work item.")
|
|
413
440
|
return text(
|
|
414
441
|
_format_work(work, header="🎯 Work item details", show_description=True)
|
|
415
442
|
)
|
|
416
443
|
|
|
417
444
|
if action is KnowledgeAction.WORK_UPDATE:
|
|
418
|
-
|
|
419
|
-
|
|
445
|
+
# Resolve work_key to id if needed
|
|
446
|
+
work_id = payload.id
|
|
447
|
+
if payload.work_key and not work_id:
|
|
448
|
+
work_item = await self._service.work_get_by_key(payload.work_key)
|
|
449
|
+
work_id = work_item.get("id")
|
|
450
|
+
if not work_id:
|
|
451
|
+
return text("❌ Provide id or work_key to update the work item.")
|
|
452
|
+
|
|
453
|
+
# Only allow safe fields to be updated via MCP
|
|
454
|
+
# Excluded: item_type, status_id, assignee_id, sprint_id, parent_id, work_category
|
|
420
455
|
work = await self._service.work_update(
|
|
421
|
-
|
|
456
|
+
work_id,
|
|
422
457
|
{
|
|
423
458
|
"title": payload.work_title,
|
|
424
459
|
"description": payload.work_description,
|
|
425
|
-
"item_type": payload.work_type,
|
|
426
|
-
"status": payload.work_status,
|
|
427
460
|
"priority": payload.work_priority,
|
|
428
|
-
"work_category": payload.work_category,
|
|
429
461
|
"story_points": payload.story_points,
|
|
430
|
-
"assignee_id": payload.assignee_id,
|
|
431
|
-
"sprint_id": payload.sprint_id,
|
|
432
|
-
"board_id": payload.board_id,
|
|
433
|
-
"parent_id": payload.parent_id,
|
|
434
462
|
"due_date": payload.work_due_date,
|
|
435
463
|
"tags": payload.work_tags,
|
|
436
464
|
},
|
|
437
465
|
)
|
|
438
466
|
return text(_format_work(work, header="✅ Work item updated"))
|
|
439
467
|
|
|
440
|
-
if action is KnowledgeAction.
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
468
|
+
if action is KnowledgeAction.WORK_ASSIGN_TO_ME:
|
|
469
|
+
# Resolve work_key to id if needed
|
|
470
|
+
work_id = payload.id
|
|
471
|
+
if payload.work_key and not work_id:
|
|
472
|
+
work_item = await self._service.work_get_by_key(payload.work_key)
|
|
473
|
+
work_id = work_item.get("id")
|
|
474
|
+
if not work_id:
|
|
475
|
+
return text("❌ Provide id or work_key to assign the work item.")
|
|
476
|
+
work = await self._service.work_assign_to_me(work_id)
|
|
477
|
+
return text(_format_work(work, header="✅ Work item assigned to you"))
|
|
445
478
|
|
|
446
479
|
if action is KnowledgeAction.WORK_BACKLOG:
|
|
447
480
|
items = await self._service.work_backlog()
|
|
@@ -503,22 +536,32 @@ class KnowledgeTool(Tool):
|
|
|
503
536
|
return text(f"📦 **Epic work items ({len(items)}):**\n\n{body}")
|
|
504
537
|
|
|
505
538
|
if action is KnowledgeAction.WORK_CHILDREN:
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
539
|
+
# Support both id and work_key
|
|
540
|
+
work_id = payload.id
|
|
541
|
+
if payload.work_key and not work_id:
|
|
542
|
+
work_item = await self._service.work_get_by_key(payload.work_key)
|
|
543
|
+
work_id = work_item.get("id")
|
|
544
|
+
if not work_id:
|
|
545
|
+
return text("❌ Provide id or work_key to list children.")
|
|
546
|
+
items = await self._service.work_children(work_id)
|
|
509
547
|
if not items:
|
|
510
548
|
return text("👶 No child items found.")
|
|
511
549
|
body = "\n\n".join(_format_work(item) for item in items)
|
|
512
550
|
return text(f"👶 **Child work items ({len(items)}):**\n\n{body}")
|
|
513
551
|
|
|
514
552
|
if action is KnowledgeAction.WORK_STATUS_UPDATE:
|
|
515
|
-
|
|
516
|
-
|
|
553
|
+
# Resolve work_key to id if needed
|
|
554
|
+
work_id = payload.id
|
|
555
|
+
if payload.work_key and not work_id:
|
|
556
|
+
work_item = await self._service.work_get_by_key(payload.work_key)
|
|
557
|
+
work_id = work_item.get("id")
|
|
558
|
+
if not work_id:
|
|
559
|
+
return text("❌ Provide id or work_key to update status.")
|
|
517
560
|
if not payload.work_status:
|
|
518
|
-
return text("❌ Provide work_status to update.")
|
|
561
|
+
return text("❌ Provide work_status (status_id) to update.")
|
|
519
562
|
work = await self._service.work_update_status(
|
|
520
|
-
|
|
521
|
-
{"
|
|
563
|
+
work_id,
|
|
564
|
+
{"status_id": payload.work_status},
|
|
522
565
|
)
|
|
523
566
|
return text(_format_work(work, header="✅ Status updated"))
|
|
524
567
|
|
|
@@ -1084,14 +1127,21 @@ def _format_work(
|
|
|
1084
1127
|
lines.append(header)
|
|
1085
1128
|
lines.append("")
|
|
1086
1129
|
|
|
1130
|
+
# Extract key (e.g., DVPT-0001)
|
|
1131
|
+
key = item.get("key", "")
|
|
1132
|
+
|
|
1087
1133
|
# Extract title
|
|
1088
1134
|
title = item.get("title") or item.get("name") or "Untitled"
|
|
1089
1135
|
|
|
1090
1136
|
# Extract type
|
|
1091
1137
|
item_type = item.get("item_type") or item.get("type") or "unknown"
|
|
1092
1138
|
|
|
1093
|
-
# Extract status
|
|
1094
|
-
|
|
1139
|
+
# Extract status - prefer status object with name, fallback to status_id
|
|
1140
|
+
status_obj = item.get("status")
|
|
1141
|
+
if isinstance(status_obj, dict):
|
|
1142
|
+
status = status_obj.get("name", "unknown")
|
|
1143
|
+
else:
|
|
1144
|
+
status = item.get("status_id") or status_obj or "unknown"
|
|
1095
1145
|
|
|
1096
1146
|
# Extract priority
|
|
1097
1147
|
priority = item.get("priority") or item.get("priority_level") or "undefined"
|
|
@@ -1102,15 +1152,19 @@ def _format_work(
|
|
|
1102
1152
|
# Extract assignee - check both assignee_id and assignee object
|
|
1103
1153
|
assignee = item.get("assignee_id")
|
|
1104
1154
|
if not assignee and item.get("assignee"):
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
)
|
|
1155
|
+
assignee_obj = item.get("assignee", {})
|
|
1156
|
+
assignee = assignee_obj.get("name") or assignee_obj.get("id")
|
|
1108
1157
|
if not assignee:
|
|
1109
1158
|
assignee = "N/A"
|
|
1110
1159
|
|
|
1160
|
+
# Format title line with key if available
|
|
1161
|
+
if key:
|
|
1162
|
+
lines.append(f"🎯 **[{key}] {title}**")
|
|
1163
|
+
else:
|
|
1164
|
+
lines.append(f"🎯 **{title}**")
|
|
1165
|
+
|
|
1111
1166
|
lines.extend(
|
|
1112
1167
|
[
|
|
1113
|
-
f"🎯 **{title}**",
|
|
1114
1168
|
f"ID: {item_id}",
|
|
1115
1169
|
f"Type: {item_type}",
|
|
1116
1170
|
f"Status: {status}",
|
|
@@ -1118,6 +1172,11 @@ def _format_work(
|
|
|
1118
1172
|
f"Assignee: {assignee}",
|
|
1119
1173
|
]
|
|
1120
1174
|
)
|
|
1175
|
+
|
|
1176
|
+
# Add key as separate line if present (for easy reference)
|
|
1177
|
+
if key:
|
|
1178
|
+
lines.append(f"Key: {key}")
|
|
1179
|
+
|
|
1121
1180
|
if item.get("due_date") or item.get("dueDate"):
|
|
1122
1181
|
lines.append(
|
|
1123
1182
|
f"Due date: {_format_date(item.get('due_date') or item.get('dueDate'))}"
|
fenix_mcp/domain/knowledge.py
CHANGED
|
@@ -72,6 +72,12 @@ class KnowledgeService:
|
|
|
72
72
|
async def work_get(self, work_id: str) -> Dict[str, Any]:
|
|
73
73
|
return await self._call_dict(self.api.get_work_item, work_id)
|
|
74
74
|
|
|
75
|
+
async def work_get_by_key(self, key: str) -> Dict[str, Any]:
|
|
76
|
+
return await self._call_dict(self.api.get_work_item_by_key, key)
|
|
77
|
+
|
|
78
|
+
async def work_assign_to_me(self, work_id: str) -> Dict[str, Any]:
|
|
79
|
+
return await self._call_dict(self.api.assign_work_item_to_me, work_id)
|
|
80
|
+
|
|
75
81
|
async def work_update(
|
|
76
82
|
self, work_id: str, payload: Dict[str, Any]
|
|
77
83
|
) -> Dict[str, Any]:
|
|
@@ -492,6 +492,12 @@ class FenixApiClient:
|
|
|
492
492
|
def get_work_item(self, item_id: str) -> Any:
|
|
493
493
|
return self._request("GET", f"/api/work-items/{item_id}")
|
|
494
494
|
|
|
495
|
+
def get_work_item_by_key(self, key: str) -> Any:
|
|
496
|
+
return self._request("GET", f"/api/work-items/by-key/{key}")
|
|
497
|
+
|
|
498
|
+
def assign_work_item_to_me(self, item_id: str) -> Any:
|
|
499
|
+
return self._request("POST", f"/api/work-items/{item_id}/assign-to-me")
|
|
500
|
+
|
|
495
501
|
def update_work_item(self, item_id: str, payload: Mapping[str, Any]) -> Any:
|
|
496
502
|
return self._request("PATCH", f"/api/work-items/{item_id}", json=payload)
|
|
497
503
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
fenix_mcp/__init__.py,sha256=
|
|
1
|
+
fenix_mcp/__init__.py,sha256=0hD_tbK6SIxOmfllb0TMUOmfIeKzQvRt5FZWd9Uhcgw,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=YJk7aSVGjXEvAkXrOHOuUjCFhYni9NPKFyPKiZqkrCc,4235
|
|
@@ -7,23 +7,23 @@ fenix_mcp/application/tools/__init__.py,sha256=Gi1YvYh-KdL9HD8gLVrknHrxiKKEOhHBE
|
|
|
7
7
|
fenix_mcp/application/tools/health.py,sha256=m5DxhoRbdwl6INzd6PISxv1NAv-ljCrezsr773VB0wE,834
|
|
8
8
|
fenix_mcp/application/tools/initialize.py,sha256=YfsE3fVYiqGEwvaI_jg5-0K7pGURXxpB3WNwETmGBPc,5499
|
|
9
9
|
fenix_mcp/application/tools/intelligence.py,sha256=fXfjBwAQmZCn3Zc8BqFnQFAJkpd9JsfOPa_uXJj-bMU,15778
|
|
10
|
-
fenix_mcp/application/tools/knowledge.py,sha256=
|
|
10
|
+
fenix_mcp/application/tools/knowledge.py,sha256=mQL3PmXCQIBpSMhpxCaWmF7silj__s1pc5dMJSciXUw,54650
|
|
11
11
|
fenix_mcp/application/tools/productivity.py,sha256=wyJ7-2VqgI2cdrliBD_ejwNvQhN1DecpXSQVrCxcUpQ,11231
|
|
12
12
|
fenix_mcp/application/tools/user_config.py,sha256=O5AVg7IUKL9uIoUoBSFovBDHl9jofhKWzhFK7CnKi4s,6470
|
|
13
13
|
fenix_mcp/domain/initialization.py,sha256=AZhdSNITQ7O3clELBuqGvjJc-c8pFKc7zQz-XR2xXPc,6933
|
|
14
14
|
fenix_mcp/domain/intelligence.py,sha256=j1kkxT-pjuzLQeAGDd2H8gd3O1aeUIRgHFnMGvNwQYg,8636
|
|
15
|
-
fenix_mcp/domain/knowledge.py,sha256=
|
|
15
|
+
fenix_mcp/domain/knowledge.py,sha256=X2terO-05fMzCpDTEmdIoAAzooEqx2odF90zIOXyf6I,20726
|
|
16
16
|
fenix_mcp/domain/productivity.py,sha256=PzY664eRPuBCfZGUY_Uv1GNeyMWsw6xqC54C-nobQns,6799
|
|
17
17
|
fenix_mcp/domain/user_config.py,sha256=8rzhJCNqIArfaCoKxxQXFoemCU7qww3hq0RDanIf_2Y,2028
|
|
18
18
|
fenix_mcp/infrastructure/config.py,sha256=zhJ3hhsP-bRfICcdq8rIDh5NGDe_u7AGpcgjcc2U1nY,1908
|
|
19
19
|
fenix_mcp/infrastructure/context.py,sha256=kiDiamiPbHZpTGyZMylcQwtLhfaDXrxAkWSst_DWQNw,470
|
|
20
20
|
fenix_mcp/infrastructure/http_client.py,sha256=QLIPhGYR_cBQGsbIO4RTR6ksyvkQt-OKHQU1JhPyap8,2470
|
|
21
21
|
fenix_mcp/infrastructure/logging.py,sha256=bHrWlSi_0HshRe3--BK_5nzUszW-gh37q6jsd0ShS2Y,1371
|
|
22
|
-
fenix_mcp/infrastructure/fenix_api/client.py,sha256=
|
|
22
|
+
fenix_mcp/infrastructure/fenix_api/client.py,sha256=5ewLAdZcNO59zgCkFLke0sTLU8dalo1W0nXKATt3F4Q,28301
|
|
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-1.
|
|
26
|
-
fenix_mcp-1.
|
|
27
|
-
fenix_mcp-1.
|
|
28
|
-
fenix_mcp-1.
|
|
29
|
-
fenix_mcp-1.
|
|
25
|
+
fenix_mcp-1.6.0.dist-info/METADATA,sha256=ZIVb_fHfqXzT-S6-vxJqVplXjwGxNTIYYpPAH4AHirE,7260
|
|
26
|
+
fenix_mcp-1.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
27
|
+
fenix_mcp-1.6.0.dist-info/entry_points.txt,sha256=o52x_YHBupEd-1Z1GSfUjv3gJrx5_I-EkHhCgt1WBaE,49
|
|
28
|
+
fenix_mcp-1.6.0.dist-info/top_level.txt,sha256=2G1UtKpwjaIGQyE7sRoHecxaGLeuexfjrOUjv9DDKh4,10
|
|
29
|
+
fenix_mcp-1.6.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|