fenix-mcp 2.0.0__tar.gz → 2.1.0__tar.gz

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.
Files changed (36) hide show
  1. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/PKG-INFO +1 -1
  2. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/__init__.py +1 -1
  3. fenix_mcp-2.1.0/fenix_mcp/interface/mcp_server.py +183 -0
  4. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp.egg-info/PKG-INFO +1 -1
  5. fenix_mcp-2.0.0/fenix_mcp/interface/mcp_server.py +0 -87
  6. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/README.md +0 -0
  7. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/application/presenters.py +0 -0
  8. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/application/tool_base.py +0 -0
  9. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/application/tool_registry.py +0 -0
  10. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/application/tools/__init__.py +0 -0
  11. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/application/tools/health.py +0 -0
  12. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/application/tools/initialize.py +0 -0
  13. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/application/tools/intelligence.py +0 -0
  14. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/application/tools/knowledge.py +0 -0
  15. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/application/tools/productivity.py +0 -0
  16. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/application/tools/user_config.py +0 -0
  17. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/domain/initialization.py +0 -0
  18. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/domain/intelligence.py +0 -0
  19. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/domain/knowledge.py +0 -0
  20. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/domain/productivity.py +0 -0
  21. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/domain/user_config.py +0 -0
  22. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/infrastructure/config.py +0 -0
  23. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/infrastructure/context.py +0 -0
  24. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/infrastructure/fenix_api/client.py +0 -0
  25. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/infrastructure/http_client.py +0 -0
  26. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/infrastructure/logging.py +0 -0
  27. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/infrastructure/request_context.py +0 -0
  28. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/interface/transports.py +0 -0
  29. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp/main.py +0 -0
  30. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp.egg-info/SOURCES.txt +0 -0
  31. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp.egg-info/dependency_links.txt +0 -0
  32. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp.egg-info/entry_points.txt +0 -0
  33. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp.egg-info/requires.txt +0 -0
  34. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/fenix_mcp.egg-info/top_level.txt +0 -0
  35. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/pyproject.toml +0 -0
  36. {fenix_mcp-2.0.0 → fenix_mcp-2.1.0}/setup.cfg +0 -0
@@ -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
@@ -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"
@@ -0,0 +1,183 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """Lightweight MCP server implementation backed by the tool registry."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import asyncio
7
+ import uuid
8
+ from dataclasses import dataclass, field
9
+ from typing import Any, Dict, List, Optional
10
+
11
+ from fenix_mcp.application.tool_registry import ToolRegistry, build_default_registry
12
+ from fenix_mcp.infrastructure.context import AppContext
13
+
14
+
15
+ class McpServerError(RuntimeError):
16
+ pass
17
+
18
+
19
+ @dataclass
20
+ class SimpleMcpServer:
21
+ context: AppContext
22
+ registry: ToolRegistry
23
+ session_id: str
24
+ _init_instructions: Optional[str] = field(default=None, repr=False)
25
+
26
+ def set_personal_access_token(self, token: Optional[str]) -> None:
27
+ self.context.api_client.update_token(token)
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
+
111
+ async def handle(self, request: Dict[str, Any]) -> Optional[Dict[str, Any]]:
112
+ method = request.get("method")
113
+ request_id = request.get("id")
114
+
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
+
133
+ return {
134
+ "jsonrpc": "2.0",
135
+ "id": request_id,
136
+ "result": result,
137
+ }
138
+
139
+ if method == "tools/list":
140
+ return {
141
+ "jsonrpc": "2.0",
142
+ "id": request_id,
143
+ "result": {"tools": self.registry.list_definitions()},
144
+ }
145
+
146
+ if method == "tools/call":
147
+ params = request.get("params") or {}
148
+ name = params.get("name")
149
+ arguments = params.get("arguments") or {}
150
+
151
+ if not name:
152
+ raise McpServerError("Missing tool name in tools/call payload")
153
+
154
+ result = await self.registry.execute(name, arguments, self.context)
155
+
156
+ return {"jsonrpc": "2.0", "id": request_id, "result": result}
157
+
158
+ if method == "notifications/initialized":
159
+ # Notifications do not require a response
160
+ return None
161
+
162
+ if method == "notifications/cancelled":
163
+ # Client cancelled a request - no response needed
164
+ return None
165
+
166
+ if method == "logging/setLevel":
167
+ # Acknowledge log level change request (we don't actually change anything)
168
+ return {
169
+ "jsonrpc": "2.0",
170
+ "id": request_id,
171
+ "result": {},
172
+ }
173
+
174
+ raise McpServerError(f"Unsupported method: {method}")
175
+
176
+
177
+ def build_server(context: AppContext) -> SimpleMcpServer:
178
+ registry = build_default_registry(context)
179
+ return SimpleMcpServer(
180
+ context=context,
181
+ registry=registry,
182
+ session_id=str(uuid.uuid4()),
183
+ )
@@ -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,87 +0,0 @@
1
- # SPDX-License-Identifier: MIT
2
- """Lightweight MCP server implementation backed by the tool registry."""
3
-
4
- from __future__ import annotations
5
-
6
- import uuid
7
- from dataclasses import dataclass
8
- from typing import Any, Dict, Optional
9
-
10
- from fenix_mcp.application.tool_registry import ToolRegistry, build_default_registry
11
- from fenix_mcp.infrastructure.context import AppContext
12
-
13
-
14
- class McpServerError(RuntimeError):
15
- pass
16
-
17
-
18
- @dataclass(slots=True)
19
- class SimpleMcpServer:
20
- context: AppContext
21
- registry: ToolRegistry
22
- session_id: str
23
-
24
- def set_personal_access_token(self, token: Optional[str]) -> None:
25
- self.context.api_client.update_token(token)
26
-
27
- async def handle(self, request: Dict[str, Any]) -> Optional[Dict[str, Any]]:
28
- method = request.get("method")
29
- request_id = request.get("id")
30
-
31
- if method == "initialize":
32
- return {
33
- "jsonrpc": "2.0",
34
- "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
- },
41
- }
42
-
43
- if method == "tools/list":
44
- return {
45
- "jsonrpc": "2.0",
46
- "id": request_id,
47
- "result": {"tools": self.registry.list_definitions()},
48
- }
49
-
50
- if method == "tools/call":
51
- params = request.get("params") or {}
52
- name = params.get("name")
53
- arguments = params.get("arguments") or {}
54
-
55
- if not name:
56
- raise McpServerError("Missing tool name in tools/call payload")
57
-
58
- result = await self.registry.execute(name, arguments, self.context)
59
-
60
- return {"jsonrpc": "2.0", "id": request_id, "result": result}
61
-
62
- if method == "notifications/initialized":
63
- # Notifications do not require a response
64
- return None
65
-
66
- if method == "notifications/cancelled":
67
- # Client cancelled a request - no response needed
68
- return None
69
-
70
- if method == "logging/setLevel":
71
- # Acknowledge log level change request (we don't actually change anything)
72
- return {
73
- "jsonrpc": "2.0",
74
- "id": request_id,
75
- "result": {},
76
- }
77
-
78
- raise McpServerError(f"Unsupported method: {method}")
79
-
80
-
81
- def build_server(context: AppContext) -> SimpleMcpServer:
82
- registry = build_default_registry(context)
83
- return SimpleMcpServer(
84
- context=context,
85
- registry=registry,
86
- session_id=str(uuid.uuid4()),
87
- )
File without changes
File without changes
File without changes
File without changes