fenix-mcp 2.0.0__py3-none-any.whl → 2.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 +1 -1
- fenix_mcp/interface/mcp_server.py +105 -9
- {fenix_mcp-2.0.0.dist-info → fenix_mcp-2.1.0.dist-info}/METADATA +1 -1
- {fenix_mcp-2.0.0.dist-info → fenix_mcp-2.1.0.dist-info}/RECORD +7 -7
- {fenix_mcp-2.0.0.dist-info → fenix_mcp-2.1.0.dist-info}/WHEEL +0 -0
- {fenix_mcp-2.0.0.dist-info → fenix_mcp-2.1.0.dist-info}/entry_points.txt +0 -0
- {fenix_mcp-2.0.0.dist-info → fenix_mcp-2.1.0.dist-info}/top_level.txt +0 -0
fenix_mcp/__init__.py
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
+
import asyncio
|
|
6
7
|
import uuid
|
|
7
|
-
from dataclasses import dataclass
|
|
8
|
-
from typing import Any, Dict, Optional
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
9
10
|
|
|
10
11
|
from fenix_mcp.application.tool_registry import ToolRegistry, build_default_registry
|
|
11
12
|
from fenix_mcp.infrastructure.context import AppContext
|
|
@@ -15,29 +16,124 @@ class McpServerError(RuntimeError):
|
|
|
15
16
|
pass
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
@dataclass
|
|
19
|
+
@dataclass
|
|
19
20
|
class SimpleMcpServer:
|
|
20
21
|
context: AppContext
|
|
21
22
|
registry: ToolRegistry
|
|
22
23
|
session_id: str
|
|
24
|
+
_init_instructions: Optional[str] = field(default=None, repr=False)
|
|
23
25
|
|
|
24
26
|
def set_personal_access_token(self, token: Optional[str]) -> None:
|
|
25
27
|
self.context.api_client.update_token(token)
|
|
26
28
|
|
|
29
|
+
async def _build_auto_init_instructions(self) -> str:
|
|
30
|
+
"""Load Fenix context automatically on MCP protocol initialize."""
|
|
31
|
+
api = self.context.api_client
|
|
32
|
+
logger = self.context.logger
|
|
33
|
+
|
|
34
|
+
async def safe_call(func, *args, **kwargs):
|
|
35
|
+
try:
|
|
36
|
+
return await asyncio.to_thread(func, *args, **kwargs)
|
|
37
|
+
except Exception as exc:
|
|
38
|
+
logger.warning("Auto-init API call failed: %s", exc)
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
def extract_items(payload: Any, key: str) -> List[Dict[str, Any]]:
|
|
42
|
+
if payload is None:
|
|
43
|
+
return []
|
|
44
|
+
if isinstance(payload, list):
|
|
45
|
+
return [item for item in payload if isinstance(item, dict)]
|
|
46
|
+
if isinstance(payload, dict):
|
|
47
|
+
value = payload.get(key) or payload.get("data")
|
|
48
|
+
if isinstance(value, list):
|
|
49
|
+
return [item for item in value if isinstance(item, dict)]
|
|
50
|
+
return []
|
|
51
|
+
|
|
52
|
+
# Fetch profile, core docs, and user docs in parallel
|
|
53
|
+
profile_task = safe_call(api.get_profile)
|
|
54
|
+
core_docs_task = safe_call(api.list_core_documents, return_content=True)
|
|
55
|
+
user_docs_task = safe_call(api.list_user_core_documents, return_content=True)
|
|
56
|
+
|
|
57
|
+
profile, core_docs_raw, user_docs_raw = await asyncio.gather(
|
|
58
|
+
profile_task, core_docs_task, user_docs_task
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
core_documents = extract_items(core_docs_raw, "coreDocuments")
|
|
62
|
+
user_documents = extract_items(user_docs_raw, "userCoreDocuments")
|
|
63
|
+
|
|
64
|
+
# Build context summary
|
|
65
|
+
user_info = (profile or {}).get("user") or {}
|
|
66
|
+
tenant_info = (profile or {}).get("tenant") or {}
|
|
67
|
+
team_info = (profile or {}).get("team") or {}
|
|
68
|
+
|
|
69
|
+
lines = [
|
|
70
|
+
"# Fenix Cloud Context (Auto-initialized)",
|
|
71
|
+
"",
|
|
72
|
+
"## User Context",
|
|
73
|
+
f"- **User**: {user_info.get('name', 'Unknown')} (`{user_info.get('id', 'N/A')}`)",
|
|
74
|
+
f"- **Tenant**: {tenant_info.get('name', 'Unknown')} (`{tenant_info.get('id', 'N/A')}`)",
|
|
75
|
+
f"- **Team**: {team_info.get('name', 'Unknown')} (`{team_info.get('id', 'N/A')}`)",
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
if core_documents:
|
|
79
|
+
lines.extend(["", "## Core Documents"])
|
|
80
|
+
for doc in core_documents:
|
|
81
|
+
name = doc.get("name", "untitled")
|
|
82
|
+
content = doc.get("content", "")
|
|
83
|
+
metadata = doc.get("metadata", "")
|
|
84
|
+
lines.append(f"\n### {name}")
|
|
85
|
+
if content:
|
|
86
|
+
lines.append(content)
|
|
87
|
+
if metadata:
|
|
88
|
+
lines.append(f"\n**Metadata:**\n{metadata}")
|
|
89
|
+
|
|
90
|
+
if user_documents:
|
|
91
|
+
lines.extend(["", "## User Documents"])
|
|
92
|
+
for doc in user_documents:
|
|
93
|
+
name = doc.get("name", "untitled")
|
|
94
|
+
content = doc.get("content", "")
|
|
95
|
+
lines.append(f"\n### {name}")
|
|
96
|
+
if content:
|
|
97
|
+
lines.append(content)
|
|
98
|
+
|
|
99
|
+
lines.extend(
|
|
100
|
+
[
|
|
101
|
+
"",
|
|
102
|
+
"## Available Tools",
|
|
103
|
+
"Use `mcp__fenix__knowledge` for work items, docs, sprints, rules.",
|
|
104
|
+
"Use `mcp__fenix__intelligence` for semantic memory search/save.",
|
|
105
|
+
"Use `mcp__fenix__productivity` for TODOs.",
|
|
106
|
+
]
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return "\n".join(lines)
|
|
110
|
+
|
|
27
111
|
async def handle(self, request: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
28
112
|
method = request.get("method")
|
|
29
113
|
request_id = request.get("id")
|
|
30
114
|
|
|
31
115
|
if method == "initialize":
|
|
116
|
+
# Auto-load Fenix context
|
|
117
|
+
try:
|
|
118
|
+
self._init_instructions = await self._build_auto_init_instructions()
|
|
119
|
+
except Exception as exc:
|
|
120
|
+
self.context.logger.warning("Auto-init failed: %s", exc)
|
|
121
|
+
self._init_instructions = None
|
|
122
|
+
|
|
123
|
+
result = {
|
|
124
|
+
"protocolVersion": "2024-11-05",
|
|
125
|
+
"capabilities": {"tools": {}, "logging": {}},
|
|
126
|
+
"serverInfo": {"name": "fenix_cloud_mcp_py", "version": "0.1.0"},
|
|
127
|
+
"sessionId": self.session_id,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if self._init_instructions:
|
|
131
|
+
result["instructions"] = self._init_instructions
|
|
132
|
+
|
|
32
133
|
return {
|
|
33
134
|
"jsonrpc": "2.0",
|
|
34
135
|
"id": request_id,
|
|
35
|
-
"result":
|
|
36
|
-
"protocolVersion": "2024-11-05",
|
|
37
|
-
"capabilities": {"tools": {}, "logging": {}},
|
|
38
|
-
"serverInfo": {"name": "fenix_cloud_mcp_py", "version": "0.1.0"},
|
|
39
|
-
"sessionId": self.session_id,
|
|
40
|
-
},
|
|
136
|
+
"result": result,
|
|
41
137
|
}
|
|
42
138
|
|
|
43
139
|
if method == "tools/list":
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
fenix_mcp/__init__.py,sha256=
|
|
1
|
+
fenix_mcp/__init__.py,sha256=Af4xXfl8m_h0nG8Z0emgXTQoXsYRict2ECggE3SGLbY,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=ZCb9g4ij5Hbb0410NEZTYXvPWq-Zkg8ZCsinTa3gCY4,4741
|
|
@@ -21,10 +21,10 @@ fenix_mcp/infrastructure/http_client.py,sha256=uJwt_iBGSFa1XPFBeqtm7eznkEm8aZ1v2
|
|
|
21
21
|
fenix_mcp/infrastructure/logging.py,sha256=bHrWlSi_0HshRe3--BK_5nzUszW-gh37q6jsd0ShS2Y,1371
|
|
22
22
|
fenix_mcp/infrastructure/request_context.py,sha256=hAHXHh-SKizBN7-YgdaRv0JsRYXBdurO2sr9btHPjKI,1101
|
|
23
23
|
fenix_mcp/infrastructure/fenix_api/client.py,sha256=fkXIzo2_Qs4AfnkPX8Z1k-3dwTTJ5wEG-MBCfUZ8Axo,29119
|
|
24
|
-
fenix_mcp/interface/mcp_server.py,sha256=
|
|
24
|
+
fenix_mcp/interface/mcp_server.py,sha256=a9zTY-OckfwVZjOF8pwdYyw6C7QKbxyphsu2_p2YfT8,6600
|
|
25
25
|
fenix_mcp/interface/transports.py,sha256=2zJtc-L73zasyiwQoZbvFJ0yT1bggL5WAa7Nm7zID3k,8502
|
|
26
|
-
fenix_mcp-2.
|
|
27
|
-
fenix_mcp-2.
|
|
28
|
-
fenix_mcp-2.
|
|
29
|
-
fenix_mcp-2.
|
|
30
|
-
fenix_mcp-2.
|
|
26
|
+
fenix_mcp-2.1.0.dist-info/METADATA,sha256=9Vyu4_sAl8v3n6sWw2mXkycDZZDHOaRDp2N1qO_nGGk,7714
|
|
27
|
+
fenix_mcp-2.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
28
|
+
fenix_mcp-2.1.0.dist-info/entry_points.txt,sha256=o52x_YHBupEd-1Z1GSfUjv3gJrx5_I-EkHhCgt1WBaE,49
|
|
29
|
+
fenix_mcp-2.1.0.dist-info/top_level.txt,sha256=2G1UtKpwjaIGQyE7sRoHecxaGLeuexfjrOUjv9DDKh4,10
|
|
30
|
+
fenix_mcp-2.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|