fenix-mcp 0.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.
- fenix_mcp-0.1.0/PKG-INFO +208 -0
- fenix_mcp-0.1.0/README.md +192 -0
- fenix_mcp-0.1.0/fenix_mcp/__init__.py +17 -0
- fenix_mcp-0.1.0/fenix_mcp/application/presenters.py +24 -0
- fenix_mcp-0.1.0/fenix_mcp/application/tool_base.py +46 -0
- fenix_mcp-0.1.0/fenix_mcp/application/tool_registry.py +37 -0
- fenix_mcp-0.1.0/fenix_mcp/application/tools/__init__.py +29 -0
- fenix_mcp-0.1.0/fenix_mcp/application/tools/health.py +30 -0
- fenix_mcp-0.1.0/fenix_mcp/application/tools/initialize.py +125 -0
- fenix_mcp-0.1.0/fenix_mcp/application/tools/intelligence.py +253 -0
- fenix_mcp-0.1.0/fenix_mcp/application/tools/knowledge.py +905 -0
- fenix_mcp-0.1.0/fenix_mcp/application/tools/productivity.py +220 -0
- fenix_mcp-0.1.0/fenix_mcp/application/tools/user_config.py +158 -0
- fenix_mcp-0.1.0/fenix_mcp/domain/initialization.py +180 -0
- fenix_mcp-0.1.0/fenix_mcp/domain/intelligence.py +133 -0
- fenix_mcp-0.1.0/fenix_mcp/domain/knowledge.py +437 -0
- fenix_mcp-0.1.0/fenix_mcp/domain/productivity.py +184 -0
- fenix_mcp-0.1.0/fenix_mcp/domain/user_config.py +42 -0
- fenix_mcp-0.1.0/fenix_mcp/infrastructure/config.py +56 -0
- fenix_mcp-0.1.0/fenix_mcp/infrastructure/context.py +20 -0
- fenix_mcp-0.1.0/fenix_mcp/infrastructure/fenix_api/client.py +623 -0
- fenix_mcp-0.1.0/fenix_mcp/infrastructure/http_client.py +84 -0
- fenix_mcp-0.1.0/fenix_mcp/infrastructure/logging.py +43 -0
- fenix_mcp-0.1.0/fenix_mcp/interface/mcp_server.py +78 -0
- fenix_mcp-0.1.0/fenix_mcp/interface/transports.py +227 -0
- fenix_mcp-0.1.0/fenix_mcp/main.py +90 -0
- fenix_mcp-0.1.0/fenix_mcp.egg-info/PKG-INFO +208 -0
- fenix_mcp-0.1.0/fenix_mcp.egg-info/SOURCES.txt +32 -0
- fenix_mcp-0.1.0/fenix_mcp.egg-info/dependency_links.txt +1 -0
- fenix_mcp-0.1.0/fenix_mcp.egg-info/entry_points.txt +2 -0
- fenix_mcp-0.1.0/fenix_mcp.egg-info/requires.txt +9 -0
- fenix_mcp-0.1.0/fenix_mcp.egg-info/top_level.txt +1 -0
- fenix_mcp-0.1.0/pyproject.toml +30 -0
- fenix_mcp-0.1.0/setup.cfg +4 -0
fenix_mcp-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fenix-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fênix Cloud MCP server implemented in Python
|
|
5
|
+
Author: Fenix Inc
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: pydantic>=2.5
|
|
9
|
+
Requires-Dist: requests>=2.31
|
|
10
|
+
Requires-Dist: urllib3>=2.0
|
|
11
|
+
Requires-Dist: aiohttp>=3.9
|
|
12
|
+
Requires-Dist: pydantic-settings>=2.0
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
16
|
+
|
|
17
|
+
# Fênix MCP — Live Access to Fênix Cloud Data
|
|
18
|
+
|
|
19
|
+
[](https://pypi.org/project/fenix-mcp/) [](https://www.python.org/) [](./LICENSE)
|
|
20
|
+
|
|
21
|
+
**Fênix MCP** connects MCP-compatible clients (Codex, Cursor, Context7, Windsurf, VS Code, etc.) directly to the Fênix Cloud APIs. Every tool invocation hits the live backend—no outdated snapshots or hallucinated IDs.
|
|
22
|
+
|
|
23
|
+
## ❌ Without Fênix MCP
|
|
24
|
+
|
|
25
|
+
- Manual lookups in the web console slow you down
|
|
26
|
+
- Agents fabricate document status, IDs, or team data
|
|
27
|
+
- Automation workflows stall on stale information
|
|
28
|
+
|
|
29
|
+
## ✅ With Fênix MCP
|
|
30
|
+
|
|
31
|
+
- Real-time API calls over STDIO or HTTP
|
|
32
|
+
- Rich toolset: documentation CRUD, work items, modes, rules, TODOs, memories
|
|
33
|
+
- Built for multi-user environments and multiple MCP clients
|
|
34
|
+
|
|
35
|
+
## 🛠 Requirements
|
|
36
|
+
|
|
37
|
+
- Python 3.10 or newer
|
|
38
|
+
- Fênix Cloud Personal Access Token (`FENIX_PAT_TOKEN`)
|
|
39
|
+
- Any MCP client (Codex, Cursor, VS Code MCP, etc.)
|
|
40
|
+
|
|
41
|
+
## 🚀 Installation
|
|
42
|
+
|
|
43
|
+
### With `pipx` (recommended)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pipx install fenix-mcp
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### With `pip`
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install --user fenix-mcp
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
To upgrade:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pipx upgrade fenix-mcp
|
|
59
|
+
# or
|
|
60
|
+
pip install --upgrade fenix-mcp
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## ▶️ Quick Start
|
|
64
|
+
|
|
65
|
+
Launch the STDIO server by providing your token (or set `FENIX_PAT_TOKEN` beforehand):
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
fenix-mcp --pat <your-token>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The command accepts all flags supported by `fenix_mcp.main` and responds over STDIO, ready for MCP clients.
|
|
72
|
+
|
|
73
|
+
## ⚙️ MCP Client Configuration
|
|
74
|
+
|
|
75
|
+
### Codex CLI (`~/.codex/config.toml`)
|
|
76
|
+
|
|
77
|
+
```toml
|
|
78
|
+
[mcp_servers.fenix]
|
|
79
|
+
command = "fenix-mcp"
|
|
80
|
+
args = ["--pat", "your-token"]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Cursor (`~/.cursor/mcp.json`)
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"mcpServers": {
|
|
88
|
+
"fenix": {
|
|
89
|
+
"command": "fenix-mcp",
|
|
90
|
+
"args": ["--pat", "your-token"],
|
|
91
|
+
"disabled": false
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### VS Code (Insiders) / Windsurf (`settings.json`)
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"modelContextProtocol.mcpServers": {
|
|
102
|
+
"fenix": {
|
|
103
|
+
"command": "fenix-mcp",
|
|
104
|
+
"args": ["--pat", "your-token"]
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
> 💡 Install with `pipx install fenix-mcp --python python3.11` to keep the CLI isolated from your global Python.
|
|
111
|
+
|
|
112
|
+
## 🌐 Optional HTTP Transport
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
export FENIX_TRANSPORT_MODE=http
|
|
116
|
+
export FENIX_HTTP_PORT=5003
|
|
117
|
+
fenix-mcp --pat <your-token>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Set `FENIX_TRANSPORT_MODE=both` to run STDIO and HTTP simultaneously. The default JSON-RPC endpoint is `http://127.0.0.1:5003/jsonrpc`.
|
|
121
|
+
|
|
122
|
+
## 🔧 Environment Variables
|
|
123
|
+
|
|
124
|
+
| Variable | Description | Default |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| `FENIX_API_URL` | Base URL of Fênix Cloud API | `https://fenix-api.devshire.app` |
|
|
127
|
+
| `FENIX_PAT_TOKEN` | Token used when `--pat` is omitted | empty |
|
|
128
|
+
| `FENIX_TRANSPORT_MODE` | `stdio`, `http`, or `both` | `stdio` |
|
|
129
|
+
| `FENIX_HTTP_HOST` | Host for HTTP transport | `127.0.0.1` |
|
|
130
|
+
| `FENIX_HTTP_PORT` | Port for HTTP transport | `5003` |
|
|
131
|
+
| `FENIX_LOG_LEVEL` | Global log level (`DEBUG`, `INFO`, …) | `INFO` |
|
|
132
|
+
|
|
133
|
+
> Copy `.env.example` to `.env` for easier customization.
|
|
134
|
+
|
|
135
|
+
## 🧪 Local Testing
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
pip install -e .[dev]
|
|
139
|
+
pytest
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## 🔄 Automation
|
|
143
|
+
|
|
144
|
+
- **CI (GitHub Actions)** – runs on pushes and pull requests targeting `main`. It installs dependencies, runs `pytest`, builds the distribution artifacts, and uploads them as workflow artifacts.
|
|
145
|
+
- **Publish workflow** – push a tag `v*` (or trigger the "Publish" workflow manually) to build the package and, if `PYPI_API_TOKEN` is set in repository secrets, upload artifacts to PyPI via `twine`.
|
|
146
|
+
|
|
147
|
+
## 🧰 Available Tools
|
|
148
|
+
|
|
149
|
+
- `knowledge` – documentation CRUD, work items, modes, rules
|
|
150
|
+
- `productivity` – TODO management
|
|
151
|
+
- `intelligence` – memories and smart operations
|
|
152
|
+
- `initialize` – personalized setup
|
|
153
|
+
- `health` – backend health check
|
|
154
|
+
|
|
155
|
+
## 🔐 Security Tips
|
|
156
|
+
|
|
157
|
+
- Store tokens securely (`pass`, keychain, `.env`) and never commit secrets.
|
|
158
|
+
- Revoke tokens when no longer needed.
|
|
159
|
+
- In shared environments, prefer `pipx + FENIX_PAT_TOKEN` exported per session.
|
|
160
|
+
|
|
161
|
+
## ❓ Troubleshooting
|
|
162
|
+
|
|
163
|
+
<details>
|
|
164
|
+
<summary><b>"command not found: fenix-mcp"</b></summary>
|
|
165
|
+
|
|
166
|
+
- Ensure the `pipx`/`pip --user` scripts directory is on your `PATH`.
|
|
167
|
+
- macOS/Linux: `export PATH="$PATH:~/.local/bin"`
|
|
168
|
+
- Windows: check `%APPDATA%\Python\Python311\Scripts` (adjust version as needed).
|
|
169
|
+
|
|
170
|
+
</details>
|
|
171
|
+
|
|
172
|
+
<details>
|
|
173
|
+
<summary><b>"401 Unauthorized" or authentication errors</b></summary>
|
|
174
|
+
|
|
175
|
+
- Confirm `--pat` or `FENIX_PAT_TOKEN` is set correctly.
|
|
176
|
+
- Regenerate tokens in Fênix Cloud if they have expired or been revoked.
|
|
177
|
+
|
|
178
|
+
</details>
|
|
179
|
+
|
|
180
|
+
<details>
|
|
181
|
+
<summary><b>Use HTTP and STDIO at the same time</b></summary>
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
export FENIX_TRANSPORT_MODE=both
|
|
185
|
+
fenix-mcp --pat <your-token>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
STDIO stays active for MCP clients; HTTP will listen on `FENIX_HTTP_HOST:FENIX_HTTP_PORT`.
|
|
189
|
+
|
|
190
|
+
</details>
|
|
191
|
+
|
|
192
|
+
## 🗺 Roadmap
|
|
193
|
+
|
|
194
|
+
- Official Docker image for Fênix MCP
|
|
195
|
+
- Convenience install scripts (`curl | sh`) for macOS/Linux/Windows
|
|
196
|
+
- Additional integrations (public core documents, more tools)
|
|
197
|
+
|
|
198
|
+
## 🤝 Contributing
|
|
199
|
+
|
|
200
|
+
1. Fork the repository
|
|
201
|
+
2. Create a branch: `git checkout -b feat/my-feature`
|
|
202
|
+
3. Install dev dependencies: `pip install -e .[dev]`
|
|
203
|
+
4. Run `pytest`
|
|
204
|
+
5. Open a Pull Request describing your changes
|
|
205
|
+
|
|
206
|
+
## 📄 License
|
|
207
|
+
|
|
208
|
+
Distributed under the [MIT License](./LICENSE).
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Fênix MCP — Live Access to Fênix Cloud Data
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/fenix-mcp/) [](https://www.python.org/) [](./LICENSE)
|
|
4
|
+
|
|
5
|
+
**Fênix MCP** connects MCP-compatible clients (Codex, Cursor, Context7, Windsurf, VS Code, etc.) directly to the Fênix Cloud APIs. Every tool invocation hits the live backend—no outdated snapshots or hallucinated IDs.
|
|
6
|
+
|
|
7
|
+
## ❌ Without Fênix MCP
|
|
8
|
+
|
|
9
|
+
- Manual lookups in the web console slow you down
|
|
10
|
+
- Agents fabricate document status, IDs, or team data
|
|
11
|
+
- Automation workflows stall on stale information
|
|
12
|
+
|
|
13
|
+
## ✅ With Fênix MCP
|
|
14
|
+
|
|
15
|
+
- Real-time API calls over STDIO or HTTP
|
|
16
|
+
- Rich toolset: documentation CRUD, work items, modes, rules, TODOs, memories
|
|
17
|
+
- Built for multi-user environments and multiple MCP clients
|
|
18
|
+
|
|
19
|
+
## 🛠 Requirements
|
|
20
|
+
|
|
21
|
+
- Python 3.10 or newer
|
|
22
|
+
- Fênix Cloud Personal Access Token (`FENIX_PAT_TOKEN`)
|
|
23
|
+
- Any MCP client (Codex, Cursor, VS Code MCP, etc.)
|
|
24
|
+
|
|
25
|
+
## 🚀 Installation
|
|
26
|
+
|
|
27
|
+
### With `pipx` (recommended)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pipx install fenix-mcp
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### With `pip`
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install --user fenix-mcp
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
To upgrade:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pipx upgrade fenix-mcp
|
|
43
|
+
# or
|
|
44
|
+
pip install --upgrade fenix-mcp
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## ▶️ Quick Start
|
|
48
|
+
|
|
49
|
+
Launch the STDIO server by providing your token (or set `FENIX_PAT_TOKEN` beforehand):
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
fenix-mcp --pat <your-token>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The command accepts all flags supported by `fenix_mcp.main` and responds over STDIO, ready for MCP clients.
|
|
56
|
+
|
|
57
|
+
## ⚙️ MCP Client Configuration
|
|
58
|
+
|
|
59
|
+
### Codex CLI (`~/.codex/config.toml`)
|
|
60
|
+
|
|
61
|
+
```toml
|
|
62
|
+
[mcp_servers.fenix]
|
|
63
|
+
command = "fenix-mcp"
|
|
64
|
+
args = ["--pat", "your-token"]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Cursor (`~/.cursor/mcp.json`)
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"fenix": {
|
|
73
|
+
"command": "fenix-mcp",
|
|
74
|
+
"args": ["--pat", "your-token"],
|
|
75
|
+
"disabled": false
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### VS Code (Insiders) / Windsurf (`settings.json`)
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"modelContextProtocol.mcpServers": {
|
|
86
|
+
"fenix": {
|
|
87
|
+
"command": "fenix-mcp",
|
|
88
|
+
"args": ["--pat", "your-token"]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
> 💡 Install with `pipx install fenix-mcp --python python3.11` to keep the CLI isolated from your global Python.
|
|
95
|
+
|
|
96
|
+
## 🌐 Optional HTTP Transport
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
export FENIX_TRANSPORT_MODE=http
|
|
100
|
+
export FENIX_HTTP_PORT=5003
|
|
101
|
+
fenix-mcp --pat <your-token>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Set `FENIX_TRANSPORT_MODE=both` to run STDIO and HTTP simultaneously. The default JSON-RPC endpoint is `http://127.0.0.1:5003/jsonrpc`.
|
|
105
|
+
|
|
106
|
+
## 🔧 Environment Variables
|
|
107
|
+
|
|
108
|
+
| Variable | Description | Default |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| `FENIX_API_URL` | Base URL of Fênix Cloud API | `https://fenix-api.devshire.app` |
|
|
111
|
+
| `FENIX_PAT_TOKEN` | Token used when `--pat` is omitted | empty |
|
|
112
|
+
| `FENIX_TRANSPORT_MODE` | `stdio`, `http`, or `both` | `stdio` |
|
|
113
|
+
| `FENIX_HTTP_HOST` | Host for HTTP transport | `127.0.0.1` |
|
|
114
|
+
| `FENIX_HTTP_PORT` | Port for HTTP transport | `5003` |
|
|
115
|
+
| `FENIX_LOG_LEVEL` | Global log level (`DEBUG`, `INFO`, …) | `INFO` |
|
|
116
|
+
|
|
117
|
+
> Copy `.env.example` to `.env` for easier customization.
|
|
118
|
+
|
|
119
|
+
## 🧪 Local Testing
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pip install -e .[dev]
|
|
123
|
+
pytest
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 🔄 Automation
|
|
127
|
+
|
|
128
|
+
- **CI (GitHub Actions)** – runs on pushes and pull requests targeting `main`. It installs dependencies, runs `pytest`, builds the distribution artifacts, and uploads them as workflow artifacts.
|
|
129
|
+
- **Publish workflow** – push a tag `v*` (or trigger the "Publish" workflow manually) to build the package and, if `PYPI_API_TOKEN` is set in repository secrets, upload artifacts to PyPI via `twine`.
|
|
130
|
+
|
|
131
|
+
## 🧰 Available Tools
|
|
132
|
+
|
|
133
|
+
- `knowledge` – documentation CRUD, work items, modes, rules
|
|
134
|
+
- `productivity` – TODO management
|
|
135
|
+
- `intelligence` – memories and smart operations
|
|
136
|
+
- `initialize` – personalized setup
|
|
137
|
+
- `health` – backend health check
|
|
138
|
+
|
|
139
|
+
## 🔐 Security Tips
|
|
140
|
+
|
|
141
|
+
- Store tokens securely (`pass`, keychain, `.env`) and never commit secrets.
|
|
142
|
+
- Revoke tokens when no longer needed.
|
|
143
|
+
- In shared environments, prefer `pipx + FENIX_PAT_TOKEN` exported per session.
|
|
144
|
+
|
|
145
|
+
## ❓ Troubleshooting
|
|
146
|
+
|
|
147
|
+
<details>
|
|
148
|
+
<summary><b>"command not found: fenix-mcp"</b></summary>
|
|
149
|
+
|
|
150
|
+
- Ensure the `pipx`/`pip --user` scripts directory is on your `PATH`.
|
|
151
|
+
- macOS/Linux: `export PATH="$PATH:~/.local/bin"`
|
|
152
|
+
- Windows: check `%APPDATA%\Python\Python311\Scripts` (adjust version as needed).
|
|
153
|
+
|
|
154
|
+
</details>
|
|
155
|
+
|
|
156
|
+
<details>
|
|
157
|
+
<summary><b>"401 Unauthorized" or authentication errors</b></summary>
|
|
158
|
+
|
|
159
|
+
- Confirm `--pat` or `FENIX_PAT_TOKEN` is set correctly.
|
|
160
|
+
- Regenerate tokens in Fênix Cloud if they have expired or been revoked.
|
|
161
|
+
|
|
162
|
+
</details>
|
|
163
|
+
|
|
164
|
+
<details>
|
|
165
|
+
<summary><b>Use HTTP and STDIO at the same time</b></summary>
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
export FENIX_TRANSPORT_MODE=both
|
|
169
|
+
fenix-mcp --pat <your-token>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
STDIO stays active for MCP clients; HTTP will listen on `FENIX_HTTP_HOST:FENIX_HTTP_PORT`.
|
|
173
|
+
|
|
174
|
+
</details>
|
|
175
|
+
|
|
176
|
+
## 🗺 Roadmap
|
|
177
|
+
|
|
178
|
+
- Official Docker image for Fênix MCP
|
|
179
|
+
- Convenience install scripts (`curl | sh`) for macOS/Linux/Windows
|
|
180
|
+
- Additional integrations (public core documents, more tools)
|
|
181
|
+
|
|
182
|
+
## 🤝 Contributing
|
|
183
|
+
|
|
184
|
+
1. Fork the repository
|
|
185
|
+
2. Create a branch: `git checkout -b feat/my-feature`
|
|
186
|
+
3. Install dev dependencies: `pip install -e .[dev]`
|
|
187
|
+
4. Run `pytest`
|
|
188
|
+
5. Open a Pull Request describing your changes
|
|
189
|
+
|
|
190
|
+
## 📄 License
|
|
191
|
+
|
|
192
|
+
Distributed under the [MIT License](./LICENSE).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2024 Bruno Fernandes
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Fênix Cloud MCP Server (Python edition).
|
|
6
|
+
|
|
7
|
+
This package follows a Clean Architecture layout inside the MCP ecosystem:
|
|
8
|
+
|
|
9
|
+
- interface: transports and MCP protocol glue code
|
|
10
|
+
- application: tools, registries, presenters and use-case orchestrators
|
|
11
|
+
- domain: pure business models and services
|
|
12
|
+
- infrastructure: API clients, config, logging and shared context
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
__all__ = ["__version__"]
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""Helpers to format MCP responses."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import Iterable, List
|
|
7
|
+
|
|
8
|
+
from fenix_mcp.application.tool_base import ToolResponse
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def text(content: str) -> ToolResponse:
|
|
12
|
+
return {"content": [{"type": "text", "text": content}]}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def bullet_list(title: str, items: Iterable[str]) -> ToolResponse:
|
|
16
|
+
body_lines: List[str] = [title, ""]
|
|
17
|
+
body_lines.extend(f"- {item}" for item in items)
|
|
18
|
+
return text("\n".join(body_lines))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def key_value(title: str, **values: str) -> ToolResponse:
|
|
22
|
+
lines = [title, ""]
|
|
23
|
+
lines.extend(f"{key}: {value}" for key, value in values.items())
|
|
24
|
+
return text("\n".join(lines))
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""Base abstractions for MCP tools."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
from typing import Any, Dict, Type
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, ConfigDict
|
|
10
|
+
|
|
11
|
+
from fenix_mcp.infrastructure.context import AppContext
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ToolRequest(BaseModel):
|
|
15
|
+
"""Base request payload."""
|
|
16
|
+
|
|
17
|
+
model_config = ConfigDict(extra="forbid")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
ToolResponse = Dict[str, Any]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Tool(ABC):
|
|
24
|
+
"""Interface implemented by all tools."""
|
|
25
|
+
|
|
26
|
+
name: str
|
|
27
|
+
description: str
|
|
28
|
+
request_model: Type[ToolRequest] = ToolRequest
|
|
29
|
+
|
|
30
|
+
def schema(self) -> Dict[str, Any]:
|
|
31
|
+
"""Return JSON schema describing the tool arguments."""
|
|
32
|
+
return {
|
|
33
|
+
"name": self.name,
|
|
34
|
+
"description": self.description,
|
|
35
|
+
"inputSchema": self.request_model.model_json_schema(),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async def execute(self, raw_arguments: Dict[str, Any], context: AppContext) -> ToolResponse:
|
|
39
|
+
"""Validate raw arguments and run the tool."""
|
|
40
|
+
payload = self.request_model.model_validate(raw_arguments or {})
|
|
41
|
+
return await self.run(payload, context)
|
|
42
|
+
|
|
43
|
+
@abstractmethod
|
|
44
|
+
async def run(self, payload: ToolRequest, context: AppContext) -> ToolResponse:
|
|
45
|
+
"""Execute business logic and return a MCP-formatted response."""
|
|
46
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""Registry that keeps the mapping between tool names and instances."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import Dict, Iterable, List
|
|
7
|
+
|
|
8
|
+
from fenix_mcp.application.tool_base import Tool, ToolResponse
|
|
9
|
+
from fenix_mcp.infrastructure.context import AppContext
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ToolRegistry:
|
|
13
|
+
"""Lookup table for tool execution."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, tools: Iterable[Tool]):
|
|
16
|
+
self._tools: Dict[str, Tool] = {}
|
|
17
|
+
for tool in tools:
|
|
18
|
+
if tool.name in self._tools:
|
|
19
|
+
raise ValueError(f"Duplicate tool name detected: {tool.name}")
|
|
20
|
+
self._tools[tool.name] = tool
|
|
21
|
+
|
|
22
|
+
def list_definitions(self) -> List[dict]:
|
|
23
|
+
return [tool.schema() for tool in self._tools.values()]
|
|
24
|
+
|
|
25
|
+
async def execute(self, name: str, arguments: dict, context: AppContext) -> ToolResponse:
|
|
26
|
+
try:
|
|
27
|
+
tool = self._tools[name]
|
|
28
|
+
except KeyError as exc:
|
|
29
|
+
raise KeyError(f"Unknown tool '{name}'") from exc
|
|
30
|
+
return await tool.execute(arguments, context)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def build_default_registry(context: AppContext) -> ToolRegistry:
|
|
34
|
+
from fenix_mcp.application.tools import build_tools
|
|
35
|
+
|
|
36
|
+
tools = build_tools(context)
|
|
37
|
+
return ToolRegistry(tools)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""Factory functions to instantiate all tools."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import List
|
|
7
|
+
|
|
8
|
+
from fenix_mcp.application.tool_base import Tool
|
|
9
|
+
from fenix_mcp.infrastructure.context import AppContext
|
|
10
|
+
|
|
11
|
+
from .health import HealthTool
|
|
12
|
+
from .initialize import InitializeTool
|
|
13
|
+
from .intelligence import IntelligenceTool
|
|
14
|
+
from .productivity import ProductivityTool
|
|
15
|
+
from .knowledge import KnowledgeTool
|
|
16
|
+
from .user_config import UserConfigTool
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def build_tools(context: AppContext) -> List[Tool]:
|
|
20
|
+
"""Instantiate all available tools."""
|
|
21
|
+
|
|
22
|
+
return [
|
|
23
|
+
HealthTool(context),
|
|
24
|
+
InitializeTool(context),
|
|
25
|
+
IntelligenceTool(context),
|
|
26
|
+
ProductivityTool(context),
|
|
27
|
+
KnowledgeTool(context),
|
|
28
|
+
UserConfigTool(context),
|
|
29
|
+
]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""Health check tool."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
from fenix_mcp.application.presenters import key_value, text
|
|
9
|
+
from fenix_mcp.application.tool_base import Tool, ToolRequest
|
|
10
|
+
from fenix_mcp.infrastructure.context import AppContext
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class _HealthRequest(ToolRequest):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class HealthTool(Tool):
|
|
18
|
+
name = "fenix_health_check"
|
|
19
|
+
description = "Check if Fênix Cloud Backend is healthy and accessible."
|
|
20
|
+
request_model = _HealthRequest
|
|
21
|
+
|
|
22
|
+
def __init__(self, context: AppContext):
|
|
23
|
+
self._context = context
|
|
24
|
+
|
|
25
|
+
async def run(self, payload: ToolRequest, context: AppContext):
|
|
26
|
+
api_health = self._context.api_client.get_health() or {}
|
|
27
|
+
return key_value(
|
|
28
|
+
"Fênix Cloud Backend Health",
|
|
29
|
+
status=str(api_health.get("status", "unknown")),
|
|
30
|
+
)
|