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 CHANGED
@@ -8,4 +8,4 @@ Fênix Cloud MCP Server (Python edition).
8
8
  __all__ = ["__version__"]
9
9
 
10
10
 
11
- __version__ = "2.0.0"
11
+ __version__ = "2.1.0"
@@ -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(slots=True)
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fenix-mcp
3
- Version: 2.0.0
3
+ Version: 2.1.0
4
4
  Summary: Fênix Cloud MCP server implemented in Python
5
5
  Author: Fenix Inc
6
6
  Requires-Python: >=3.10
@@ -1,4 +1,4 @@
1
- fenix_mcp/__init__.py,sha256=ewwBzUPQxM4BTmesnfhTvSBnnkCQQPE4nOkAVo-lKYM,180
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=xSIMZwOaEwHLJ1BKy_agdMiep7HFhIgsUhOVz49DsyA,2728
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.0.0.dist-info/METADATA,sha256=6oZbIkzpboBC9i1IzXjedPEo_mSECyo_9tbZ36oHQFA,7714
27
- fenix_mcp-2.0.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
28
- fenix_mcp-2.0.0.dist-info/entry_points.txt,sha256=o52x_YHBupEd-1Z1GSfUjv3gJrx5_I-EkHhCgt1WBaE,49
29
- fenix_mcp-2.0.0.dist-info/top_level.txt,sha256=2G1UtKpwjaIGQyE7sRoHecxaGLeuexfjrOUjv9DDKh4,10
30
- fenix_mcp-2.0.0.dist-info/RECORD,,
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,,