open-edison 0.1.17__tar.gz → 0.1.19__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.
- {open_edison-0.1.17 → open_edison-0.1.19}/.gitignore +2 -1
- {open_edison-0.1.17 → open_edison-0.1.19}/PKG-INFO +88 -34
- {open_edison-0.1.17 → open_edison-0.1.19}/README.md +87 -33
- {open_edison-0.1.17 → open_edison-0.1.19}/config.json +15 -1
- {open_edison-0.1.17 → open_edison-0.1.19}/pyproject.toml +2 -1
- {open_edison-0.1.17 → open_edison-0.1.19}/src/middleware/data_access_tracker.py +31 -2
- {open_edison-0.1.17 → open_edison-0.1.19}/src/server.py +82 -7
- {open_edison-0.1.17 → open_edison-0.1.19}/src/single_user_mcp.py +94 -94
- {open_edison-0.1.17 → open_edison-0.1.19}/tool_permissions.json +79 -0
- open_edison-0.1.17/frontend/configurations/prompt_permissions.json +0 -14
- open_edison-0.1.17/frontend/configurations/resource_permissions.json +0 -14
- open_edison-0.1.17/frontend/configurations/tool_permissions.json +0 -170
- {open_edison-0.1.17 → open_edison-0.1.19}/LICENSE +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/desktop_ext/README.md +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/docs/README.md +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/docs/architecture/single_user_design.md +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/docs/core/configuration.md +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/docs/core/project_structure.md +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/docs/core/proxy_usage.md +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/docs/deployment/docker.md +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/docs/deployment/local.md +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/docs/development/contributing.md +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/docs/development/development_guide.md +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/docs/development/testing.md +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/docs/quick-reference/api_reference.md +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/docs/quick-reference/config_quick_start.md +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/prompt_permissions.json +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/resource_permissions.json +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/src/__init__.py +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/src/__main__.py +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/src/cli.py +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/src/config.py +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/src/middleware/session_tracking.py +0 -0
- {open_edison-0.1.17 → open_edison-0.1.19}/src/telemetry.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: open-edison
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.19
|
4
4
|
Summary: Open-source MCP security, aggregation, and monitoring. Single-user, self-hosted MCP proxy.
|
5
5
|
Author-email: Hugo Berg <hugo@edison.watch>
|
6
6
|
License-File: LICENSE
|
@@ -27,9 +27,24 @@ Description-Content-Type: text/markdown
|
|
27
27
|
|
28
28
|
# OpenEdison
|
29
29
|
|
30
|
-
Open-source MCP security gateway that prevents data exfiltration—via direct access or tool chaining—with full monitoring for local single‑user deployments. Provides core functionality of <https://edison.watch> for local
|
30
|
+
Open-source single-user MCP security gateway that prevents data exfiltration—via direct access or tool chaining—with full monitoring for local single‑user deployments. Provides core functionality of <https://edison.watch> for local use.
|
31
31
|
|
32
|
-
|
32
|
+
<div align="center">
|
33
|
+
<h2>📧 Interested in connecting AI to your business software with proper access controls? <a href="mailto:hello@edison.watch">Contact us</a> to discuss.</h2>
|
34
|
+
</div>
|
35
|
+
|
36
|
+
## Features
|
37
|
+
|
38
|
+
- **Single-user MCP proxy** - No multi-user complexity, just a simple proxy for your MCP servers
|
39
|
+
- **JSON configuration** - Easy to configure and manage your MCP servers
|
40
|
+
- **Simple local frontend** - Track and monitor your MCP interactions, servers, and sessions.
|
41
|
+
- **Session tracking** - Track and monitor your MCP interactions
|
42
|
+
- **Simple API** - REST API for managing MCP servers and proxying requests
|
43
|
+
- **Docker support** - Run in a container for easy deployment
|
44
|
+
|
45
|
+
## Quick Start
|
46
|
+
|
47
|
+
The fastest way to get started:
|
33
48
|
|
34
49
|
```bash
|
35
50
|
# Installs uv (via Astral installer) and launches open-edison with uvx.
|
@@ -39,36 +54,30 @@ curl -fsSL https://raw.githubusercontent.com/Edison-Watch/open-edison/main/curl_
|
|
39
54
|
|
40
55
|
Run locally with uvx: `uvx open-edison --config-dir ~/edison-config`
|
41
56
|
|
57
|
+
<details>
|
58
|
+
<summary>Install Node.js/npm (optional for MCP tools)</summary>
|
59
|
+
|
42
60
|
If you need `npx` (for Node-based MCP tools like `mcp-remote`), install Node.js as well:
|
43
61
|
|
44
|
-
-
|
45
|
-
- uv: `curl -fsSL https://astral.sh/uv/install.sh | sh`
|
46
|
-
- Node/npx: `brew install node`
|
47
|
-
- Linux (Debian/Ubuntu):
|
48
|
-
- uv: `curl -fsSL https://astral.sh/uv/install.sh | sh`
|
49
|
-
- Node/npx: `sudo apt-get update && sudo apt-get install -y nodejs npm`
|
50
|
-
- Windows (PowerShell):
|
51
|
-
- uv: `powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"`
|
52
|
-
- Node/npx: `winget install -e --id OpenJS.NodeJS`
|
62
|
+

|
53
63
|
|
54
|
-
|
64
|
+
- uv: `curl -fsSL https://astral.sh/uv/install.sh | sh`
|
65
|
+
- Node/npx: `brew install node`
|
55
66
|
|
56
|
-
|
57
|
-
<h2>📧 Interested in connecting AI to your business software with proper access controls? <a href="mailto:hello@edison.watch">Contact us</a> to discuss.</h2>
|
58
|
-
</div>
|
67
|
+

|
59
68
|
|
60
|
-
|
69
|
+
- uv: `curl -fsSL https://astral.sh/uv/install.sh | sh`
|
70
|
+
- Node/npx: `sudo apt-get update && sudo apt-get install -y nodejs npm`
|
61
71
|
|
62
|
-
-
|
63
|
-
- **JSON configuration** - Easy to configure and manage your MCP servers
|
64
|
-
- **Simple local frontend** - Track and monitor your MCP interactions, servers, and sessions.
|
65
|
-
- **Session tracking** - Track and monitor your MCP interactions
|
66
|
-
- **Simple API** - REST API for managing MCP servers and proxying requests
|
67
|
-
- **Docker support** - Run in a container for easy deployment
|
72
|
+

|
68
73
|
|
69
|
-
|
74
|
+
- uv: `powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"`
|
75
|
+
- Node/npx: `winget install -e --id OpenJS.NodeJS`
|
76
|
+
|
77
|
+
After installation, ensure that `npx` is available on PATH.
|
70
78
|
|
71
|
-
|
79
|
+
<details>
|
80
|
+
<summary><img src="https://img.shields.io/badge/pypi-3775A9?style=for-the-badge&logo=pypi&logoColor=white" alt="PyPI"> Install from PyPI</summary>
|
72
81
|
|
73
82
|
#### Prerequisites
|
74
83
|
|
@@ -91,7 +100,10 @@ open-edison run --config-dir ~/edison-config
|
|
91
100
|
OPEN_EDISON_CONFIG_DIR=~/edison-config open-edison run
|
92
101
|
```
|
93
102
|
|
94
|
-
|
103
|
+
</details>
|
104
|
+
|
105
|
+
<details>
|
106
|
+
<summary><img src="https://img.shields.io/badge/Docker-2CA5E0?style=for-the-badge&logo=docker&logoColor=white" alt="Docker"> Run with Docker</summary>
|
95
107
|
|
96
108
|
There is a dockerfile for simple local setup.
|
97
109
|
|
@@ -110,7 +122,10 @@ make docker_run
|
|
110
122
|
|
111
123
|
The MCP server will be available at `http://localhost:3000` and the api + frontend at `http://localhost:3001`.
|
112
124
|
|
113
|
-
|
125
|
+
</details>
|
126
|
+
|
127
|
+
<details>
|
128
|
+
<summary>⚙️ Run from source</summary>
|
114
129
|
|
115
130
|
1. Clone the repository:
|
116
131
|
|
@@ -148,7 +163,10 @@ open-edison run
|
|
148
163
|
|
149
164
|
The server will be available at `http://localhost:3000`.
|
150
165
|
|
151
|
-
|
166
|
+
</details>
|
167
|
+
|
168
|
+
<details>
|
169
|
+
<summary>MCP Connection</summary>
|
152
170
|
|
153
171
|
Connect any MCP client to Open Edison (requires Node.js/npm for `npx`):
|
154
172
|
|
@@ -169,13 +187,17 @@ Or add to your MCP client config:
|
|
169
187
|
}
|
170
188
|
```
|
171
189
|
|
172
|
-
|
190
|
+
</details>
|
191
|
+
|
192
|
+
<details>
|
193
|
+
<summary>Usage</summary>
|
173
194
|
|
174
195
|
### API Endpoints
|
175
196
|
|
176
197
|
See [API Reference](docs/quick-reference/api_reference.md) for full API documentation.
|
177
198
|
|
178
|
-
|
199
|
+
<details>
|
200
|
+
<summary>Development</summary>
|
179
201
|
|
180
202
|
### Setup
|
181
203
|
|
@@ -197,6 +219,11 @@ We expect `make ci` to return cleanly.
|
|
197
219
|
make ci
|
198
220
|
```
|
199
221
|
|
222
|
+
</details>
|
223
|
+
|
224
|
+
<details>
|
225
|
+
<summary>⚙️ Configuration (config.json)</summary>
|
226
|
+
|
200
227
|
## Configuration
|
201
228
|
|
202
229
|
The `config.json` file contains all configuration:
|
@@ -215,14 +242,23 @@ Each MCP server configuration includes:
|
|
215
242
|
- `env` - Environment variables (optional)
|
216
243
|
- `enabled` - Whether to auto-start this server
|
217
244
|
|
218
|
-
|
245
|
+
</details>
|
219
246
|
|
220
|
-
|
247
|
+
</details>
|
248
|
+
|
249
|
+
<details>
|
250
|
+
<summary>Security & Permissions System</summary>
|
251
|
+
|
252
|
+
Open Edison includes a comprehensive security monitoring system that tracks the "lethal trifecta" of AI agent risks, as described in [Simon Willison's blog post](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/):
|
253
|
+
|
254
|
+
<img src="media/lethal-trifecta.png" alt="The lethal trifecta diagram showing the three key AI agent security risks" width="30%">
|
221
255
|
|
222
256
|
1. **Private data access** - Access to sensitive local files/data
|
223
257
|
2. **Untrusted content exposure** - Exposure to external/web content
|
224
258
|
3. **External communication** - Ability to write/send data externally
|
225
259
|
|
260
|
+
<img src="media/pam-diagram.png" alt="Privileged Access Management (PAM) example showing the lethal trifecta in action" width="60%">
|
261
|
+
|
226
262
|
The configuration allows you to classify these risks across **tools**, **resources**, and **prompts** using separate configuration files.
|
227
263
|
|
228
264
|
In addition to trifecta, we track Access Control Level (ACL) for each tool call,
|
@@ -246,6 +282,9 @@ Defines security classifications for MCP tools. See full file: [tool_permissions
|
|
246
282
|
}
|
247
283
|
```
|
248
284
|
|
285
|
+
<details>
|
286
|
+
<summary>Resource Permissions (`resource_permissions.json`)</summary>
|
287
|
+
|
249
288
|
### Resource Permissions (`resource_permissions.json`)
|
250
289
|
|
251
290
|
Defines security classifications for resource access patterns. See full file: [resource_permissions.json](resource_permissions.json), it looks like:
|
@@ -257,6 +296,11 @@ Defines security classifications for resource access patterns. See full file: [r
|
|
257
296
|
}
|
258
297
|
```
|
259
298
|
|
299
|
+
</details>
|
300
|
+
|
301
|
+
<details>
|
302
|
+
<summary>Prompt Permissions (`prompt_permissions.json`)</summary>
|
303
|
+
|
260
304
|
### Prompt Permissions (`prompt_permissions.json`)
|
261
305
|
|
262
306
|
Defines security classifications for prompt types. See full file: [prompt_permissions.json](prompt_permissions.json), it looks like:
|
@@ -268,6 +312,8 @@ Defines security classifications for prompt types. See full file: [prompt_permis
|
|
268
312
|
}
|
269
313
|
```
|
270
314
|
|
315
|
+
</details>
|
316
|
+
|
271
317
|
### Wildcard Patterns
|
272
318
|
|
273
319
|
All permission types support wildcard patterns:
|
@@ -282,7 +328,10 @@ All permission types support wildcard patterns:
|
|
282
328
|
|
283
329
|
Use the `get_security_status` tool to monitor your session's current risk level and see which capabilities have been accessed. When the lethal trifecta is achieved (all three risk flags set), further potentially dangerous operations are blocked.
|
284
330
|
|
285
|
-
|
331
|
+
</details>
|
332
|
+
|
333
|
+
<details>
|
334
|
+
<summary>Documentation</summary>
|
286
335
|
|
287
336
|
📚 **Complete documentation available in [`docs/`](docs/)**
|
288
337
|
|
@@ -291,6 +340,11 @@ Use the `get_security_status` tool to monitor your session's current risk level
|
|
291
340
|
- **[API Reference](docs/quick-reference/api_reference.md)** - REST API documentation
|
292
341
|
- **[Development Guide](docs/development/development_guide.md)** - Contributing and development
|
293
342
|
|
294
|
-
|
343
|
+
</details>
|
344
|
+
|
345
|
+
<details>
|
346
|
+
<summary>License</summary>
|
295
347
|
|
296
348
|
GPL-3.0 License - see [LICENSE](LICENSE) for details.
|
349
|
+
|
350
|
+
</details>
|
@@ -1,8 +1,23 @@
|
|
1
1
|
# OpenEdison
|
2
2
|
|
3
|
-
Open-source MCP security gateway that prevents data exfiltration—via direct access or tool chaining—with full monitoring for local single‑user deployments. Provides core functionality of <https://edison.watch> for local
|
3
|
+
Open-source single-user MCP security gateway that prevents data exfiltration—via direct access or tool chaining—with full monitoring for local single‑user deployments. Provides core functionality of <https://edison.watch> for local use.
|
4
4
|
|
5
|
-
|
5
|
+
<div align="center">
|
6
|
+
<h2>📧 Interested in connecting AI to your business software with proper access controls? <a href="mailto:hello@edison.watch">Contact us</a> to discuss.</h2>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
## Features
|
10
|
+
|
11
|
+
- **Single-user MCP proxy** - No multi-user complexity, just a simple proxy for your MCP servers
|
12
|
+
- **JSON configuration** - Easy to configure and manage your MCP servers
|
13
|
+
- **Simple local frontend** - Track and monitor your MCP interactions, servers, and sessions.
|
14
|
+
- **Session tracking** - Track and monitor your MCP interactions
|
15
|
+
- **Simple API** - REST API for managing MCP servers and proxying requests
|
16
|
+
- **Docker support** - Run in a container for easy deployment
|
17
|
+
|
18
|
+
## Quick Start
|
19
|
+
|
20
|
+
The fastest way to get started:
|
6
21
|
|
7
22
|
```bash
|
8
23
|
# Installs uv (via Astral installer) and launches open-edison with uvx.
|
@@ -12,36 +27,30 @@ curl -fsSL https://raw.githubusercontent.com/Edison-Watch/open-edison/main/curl_
|
|
12
27
|
|
13
28
|
Run locally with uvx: `uvx open-edison --config-dir ~/edison-config`
|
14
29
|
|
30
|
+
<details>
|
31
|
+
<summary>Install Node.js/npm (optional for MCP tools)</summary>
|
32
|
+
|
15
33
|
If you need `npx` (for Node-based MCP tools like `mcp-remote`), install Node.js as well:
|
16
34
|
|
17
|
-
-
|
18
|
-
- uv: `curl -fsSL https://astral.sh/uv/install.sh | sh`
|
19
|
-
- Node/npx: `brew install node`
|
20
|
-
- Linux (Debian/Ubuntu):
|
21
|
-
- uv: `curl -fsSL https://astral.sh/uv/install.sh | sh`
|
22
|
-
- Node/npx: `sudo apt-get update && sudo apt-get install -y nodejs npm`
|
23
|
-
- Windows (PowerShell):
|
24
|
-
- uv: `powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"`
|
25
|
-
- Node/npx: `winget install -e --id OpenJS.NodeJS`
|
35
|
+

|
26
36
|
|
27
|
-
|
37
|
+
- uv: `curl -fsSL https://astral.sh/uv/install.sh | sh`
|
38
|
+
- Node/npx: `brew install node`
|
28
39
|
|
29
|
-
|
30
|
-
<h2>📧 Interested in connecting AI to your business software with proper access controls? <a href="mailto:hello@edison.watch">Contact us</a> to discuss.</h2>
|
31
|
-
</div>
|
40
|
+

|
32
41
|
|
33
|
-
|
42
|
+
- uv: `curl -fsSL https://astral.sh/uv/install.sh | sh`
|
43
|
+
- Node/npx: `sudo apt-get update && sudo apt-get install -y nodejs npm`
|
34
44
|
|
35
|
-
-
|
36
|
-
- **JSON configuration** - Easy to configure and manage your MCP servers
|
37
|
-
- **Simple local frontend** - Track and monitor your MCP interactions, servers, and sessions.
|
38
|
-
- **Session tracking** - Track and monitor your MCP interactions
|
39
|
-
- **Simple API** - REST API for managing MCP servers and proxying requests
|
40
|
-
- **Docker support** - Run in a container for easy deployment
|
45
|
+

|
41
46
|
|
42
|
-
|
47
|
+
- uv: `powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"`
|
48
|
+
- Node/npx: `winget install -e --id OpenJS.NodeJS`
|
49
|
+
|
50
|
+
After installation, ensure that `npx` is available on PATH.
|
43
51
|
|
44
|
-
|
52
|
+
<details>
|
53
|
+
<summary><img src="https://img.shields.io/badge/pypi-3775A9?style=for-the-badge&logo=pypi&logoColor=white" alt="PyPI"> Install from PyPI</summary>
|
45
54
|
|
46
55
|
#### Prerequisites
|
47
56
|
|
@@ -64,7 +73,10 @@ open-edison run --config-dir ~/edison-config
|
|
64
73
|
OPEN_EDISON_CONFIG_DIR=~/edison-config open-edison run
|
65
74
|
```
|
66
75
|
|
67
|
-
|
76
|
+
</details>
|
77
|
+
|
78
|
+
<details>
|
79
|
+
<summary><img src="https://img.shields.io/badge/Docker-2CA5E0?style=for-the-badge&logo=docker&logoColor=white" alt="Docker"> Run with Docker</summary>
|
68
80
|
|
69
81
|
There is a dockerfile for simple local setup.
|
70
82
|
|
@@ -83,7 +95,10 @@ make docker_run
|
|
83
95
|
|
84
96
|
The MCP server will be available at `http://localhost:3000` and the api + frontend at `http://localhost:3001`.
|
85
97
|
|
86
|
-
|
98
|
+
</details>
|
99
|
+
|
100
|
+
<details>
|
101
|
+
<summary>⚙️ Run from source</summary>
|
87
102
|
|
88
103
|
1. Clone the repository:
|
89
104
|
|
@@ -121,7 +136,10 @@ open-edison run
|
|
121
136
|
|
122
137
|
The server will be available at `http://localhost:3000`.
|
123
138
|
|
124
|
-
|
139
|
+
</details>
|
140
|
+
|
141
|
+
<details>
|
142
|
+
<summary>MCP Connection</summary>
|
125
143
|
|
126
144
|
Connect any MCP client to Open Edison (requires Node.js/npm for `npx`):
|
127
145
|
|
@@ -142,13 +160,17 @@ Or add to your MCP client config:
|
|
142
160
|
}
|
143
161
|
```
|
144
162
|
|
145
|
-
|
163
|
+
</details>
|
164
|
+
|
165
|
+
<details>
|
166
|
+
<summary>Usage</summary>
|
146
167
|
|
147
168
|
### API Endpoints
|
148
169
|
|
149
170
|
See [API Reference](docs/quick-reference/api_reference.md) for full API documentation.
|
150
171
|
|
151
|
-
|
172
|
+
<details>
|
173
|
+
<summary>Development</summary>
|
152
174
|
|
153
175
|
### Setup
|
154
176
|
|
@@ -170,6 +192,11 @@ We expect `make ci` to return cleanly.
|
|
170
192
|
make ci
|
171
193
|
```
|
172
194
|
|
195
|
+
</details>
|
196
|
+
|
197
|
+
<details>
|
198
|
+
<summary>⚙️ Configuration (config.json)</summary>
|
199
|
+
|
173
200
|
## Configuration
|
174
201
|
|
175
202
|
The `config.json` file contains all configuration:
|
@@ -188,14 +215,23 @@ Each MCP server configuration includes:
|
|
188
215
|
- `env` - Environment variables (optional)
|
189
216
|
- `enabled` - Whether to auto-start this server
|
190
217
|
|
191
|
-
|
218
|
+
</details>
|
192
219
|
|
193
|
-
|
220
|
+
</details>
|
221
|
+
|
222
|
+
<details>
|
223
|
+
<summary>Security & Permissions System</summary>
|
224
|
+
|
225
|
+
Open Edison includes a comprehensive security monitoring system that tracks the "lethal trifecta" of AI agent risks, as described in [Simon Willison's blog post](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/):
|
226
|
+
|
227
|
+
<img src="media/lethal-trifecta.png" alt="The lethal trifecta diagram showing the three key AI agent security risks" width="30%">
|
194
228
|
|
195
229
|
1. **Private data access** - Access to sensitive local files/data
|
196
230
|
2. **Untrusted content exposure** - Exposure to external/web content
|
197
231
|
3. **External communication** - Ability to write/send data externally
|
198
232
|
|
233
|
+
<img src="media/pam-diagram.png" alt="Privileged Access Management (PAM) example showing the lethal trifecta in action" width="60%">
|
234
|
+
|
199
235
|
The configuration allows you to classify these risks across **tools**, **resources**, and **prompts** using separate configuration files.
|
200
236
|
|
201
237
|
In addition to trifecta, we track Access Control Level (ACL) for each tool call,
|
@@ -219,6 +255,9 @@ Defines security classifications for MCP tools. See full file: [tool_permissions
|
|
219
255
|
}
|
220
256
|
```
|
221
257
|
|
258
|
+
<details>
|
259
|
+
<summary>Resource Permissions (`resource_permissions.json`)</summary>
|
260
|
+
|
222
261
|
### Resource Permissions (`resource_permissions.json`)
|
223
262
|
|
224
263
|
Defines security classifications for resource access patterns. See full file: [resource_permissions.json](resource_permissions.json), it looks like:
|
@@ -230,6 +269,11 @@ Defines security classifications for resource access patterns. See full file: [r
|
|
230
269
|
}
|
231
270
|
```
|
232
271
|
|
272
|
+
</details>
|
273
|
+
|
274
|
+
<details>
|
275
|
+
<summary>Prompt Permissions (`prompt_permissions.json`)</summary>
|
276
|
+
|
233
277
|
### Prompt Permissions (`prompt_permissions.json`)
|
234
278
|
|
235
279
|
Defines security classifications for prompt types. See full file: [prompt_permissions.json](prompt_permissions.json), it looks like:
|
@@ -241,6 +285,8 @@ Defines security classifications for prompt types. See full file: [prompt_permis
|
|
241
285
|
}
|
242
286
|
```
|
243
287
|
|
288
|
+
</details>
|
289
|
+
|
244
290
|
### Wildcard Patterns
|
245
291
|
|
246
292
|
All permission types support wildcard patterns:
|
@@ -255,7 +301,10 @@ All permission types support wildcard patterns:
|
|
255
301
|
|
256
302
|
Use the `get_security_status` tool to monitor your session's current risk level and see which capabilities have been accessed. When the lethal trifecta is achieved (all three risk flags set), further potentially dangerous operations are blocked.
|
257
303
|
|
258
|
-
|
304
|
+
</details>
|
305
|
+
|
306
|
+
<details>
|
307
|
+
<summary>Documentation</summary>
|
259
308
|
|
260
309
|
📚 **Complete documentation available in [`docs/`](docs/)**
|
261
310
|
|
@@ -264,6 +313,11 @@ Use the `get_security_status` tool to monitor your session's current risk level
|
|
264
313
|
- **[API Reference](docs/quick-reference/api_reference.md)** - REST API documentation
|
265
314
|
- **[Development Guide](docs/development/development_guide.md)** - Contributing and development
|
266
315
|
|
267
|
-
|
316
|
+
</details>
|
317
|
+
|
318
|
+
<details>
|
319
|
+
<summary>License</summary>
|
268
320
|
|
269
321
|
GPL-3.0 License - see [LICENSE](LICENSE) for details.
|
322
|
+
|
323
|
+
</details>
|
@@ -2,12 +2,14 @@
|
|
2
2
|
"server": {
|
3
3
|
"host": "0.0.0.0",
|
4
4
|
"port": 3000,
|
5
|
-
"api_key": "dev-api-key-change-me
|
5
|
+
"api_key": "dev-api-key-change-me"
|
6
6
|
},
|
7
|
+
"autoconfig_url": "https://api.edison.watch/api/config-perms",
|
7
8
|
"logging": {
|
8
9
|
"level": "INFO",
|
9
10
|
"database_path": "sessions.db"
|
10
11
|
},
|
12
|
+
"edison-watch-api-key": "change-me",
|
11
13
|
"mcp_servers": [
|
12
14
|
{
|
13
15
|
"name": "filesystem",
|
@@ -90,6 +92,18 @@
|
|
90
92
|
],
|
91
93
|
"env": {},
|
92
94
|
"enabled": false
|
95
|
+
},
|
96
|
+
{
|
97
|
+
"name": "zapier",
|
98
|
+
"command": "npx",
|
99
|
+
"args": [
|
100
|
+
"-y",
|
101
|
+
"mcp-remote",
|
102
|
+
"https://mcp.zapier.com/api/mcp/s/{access_token}/mcp"
|
103
|
+
],
|
104
|
+
"env": {},
|
105
|
+
"enabled": false,
|
106
|
+
"roots": []
|
93
107
|
}
|
94
108
|
]
|
95
109
|
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "open-edison"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.19"
|
4
4
|
description = "Open-source MCP security, aggregation, and monitoring. Single-user, self-hosted MCP proxy."
|
5
5
|
readme = "README.md"
|
6
6
|
authors = [
|
@@ -34,6 +34,7 @@ build-backend = "hatchling.build"
|
|
34
34
|
|
35
35
|
[tool.rye]
|
36
36
|
managed = true
|
37
|
+
python = "3.12"
|
37
38
|
dev-dependencies = [
|
38
39
|
"basedpyright>=1.21.0",
|
39
40
|
"ruff>=0.12.3",
|
@@ -171,6 +171,12 @@ def _load_tool_permissions_cached() -> dict[str, dict[str, Any]]:
|
|
171
171
|
return {}
|
172
172
|
|
173
173
|
|
174
|
+
def clear_tool_permissions_cache() -> None:
|
175
|
+
"""Clear the tool permissions cache to force reload from file."""
|
176
|
+
_load_tool_permissions_cached.cache_clear()
|
177
|
+
log.info("Tool permissions cache cleared")
|
178
|
+
|
179
|
+
|
174
180
|
@cache
|
175
181
|
def _load_resource_permissions_cached() -> dict[str, dict[str, Any]]:
|
176
182
|
"""Load resource permissions from JSON configuration file with LRU caching."""
|
@@ -186,6 +192,12 @@ def _load_resource_permissions_cached() -> dict[str, dict[str, Any]]:
|
|
186
192
|
return {}
|
187
193
|
|
188
194
|
|
195
|
+
def clear_resource_permissions_cache() -> None:
|
196
|
+
"""Clear the resource permissions cache to force reload from file."""
|
197
|
+
_load_resource_permissions_cached.cache_clear()
|
198
|
+
log.info("Resource permissions cache cleared")
|
199
|
+
|
200
|
+
|
189
201
|
@cache
|
190
202
|
def _load_prompt_permissions_cached() -> dict[str, dict[str, Any]]:
|
191
203
|
"""Load prompt permissions from JSON configuration file with LRU caching."""
|
@@ -201,6 +213,20 @@ def _load_prompt_permissions_cached() -> dict[str, dict[str, Any]]:
|
|
201
213
|
return {}
|
202
214
|
|
203
215
|
|
216
|
+
def clear_prompt_permissions_cache() -> None:
|
217
|
+
"""Clear the prompt permissions cache to force reload from file."""
|
218
|
+
_load_prompt_permissions_cached.cache_clear()
|
219
|
+
log.info("Prompt permissions cache cleared")
|
220
|
+
|
221
|
+
|
222
|
+
def clear_all_permissions_caches() -> None:
|
223
|
+
"""Clear all permission caches to force reload from files."""
|
224
|
+
clear_tool_permissions_cache()
|
225
|
+
clear_resource_permissions_cache()
|
226
|
+
clear_prompt_permissions_cache()
|
227
|
+
log.info("All permission caches cleared")
|
228
|
+
|
229
|
+
|
204
230
|
@cache
|
205
231
|
def _classify_tool_permissions_cached(tool_name: str) -> dict[str, Any]:
|
206
232
|
"""Classify tool permissions with LRU caching."""
|
@@ -351,6 +377,10 @@ class DataAccessTracker:
|
|
351
377
|
"""Load prompt permissions from JSON configuration file with caching."""
|
352
378
|
return _load_prompt_permissions_cached()
|
353
379
|
|
380
|
+
def clear_caches(self) -> None:
|
381
|
+
"""Clear all permission caches to force reload from configuration files."""
|
382
|
+
clear_all_permissions_caches()
|
383
|
+
|
354
384
|
def _classify_by_tool_name(self, tool_name: str) -> dict[str, Any]:
|
355
385
|
"""Classify permissions based on external JSON configuration only."""
|
356
386
|
return _classify_tool_permissions_cached(tool_name)
|
@@ -606,6 +636,5 @@ class SecurityError(Exception):
|
|
606
636
|
████ ████ ████ ████ ████ ████
|
607
637
|
██ ████ ████ ████ ████ ████ █
|
608
638
|
████ ████ ████ ████ ████ ████
|
609
|
-
|
610
|
-
"""
|
639
|
+
"""
|
611
640
|
super().__init__(message)
|
@@ -363,7 +363,6 @@ class OpenEdisonProxy:
|
|
363
363
|
"/mcp/status",
|
364
364
|
self.mcp_status,
|
365
365
|
methods=["GET"],
|
366
|
-
dependencies=[Depends(self.verify_api_key)],
|
367
366
|
)
|
368
367
|
app.add_api_route(
|
369
368
|
"/mcp/validate",
|
@@ -377,12 +376,24 @@ class OpenEdisonProxy:
|
|
377
376
|
methods=["GET"],
|
378
377
|
dependencies=[Depends(self.verify_api_key)],
|
379
378
|
)
|
379
|
+
app.add_api_route(
|
380
|
+
"/mcp/reinitialize",
|
381
|
+
self.reinitialize_mcp_servers,
|
382
|
+
methods=["POST"],
|
383
|
+
dependencies=[Depends(self.verify_api_key)],
|
384
|
+
)
|
380
385
|
# Public sessions endpoint (no auth) for simple local dashboard
|
381
386
|
app.add_api_route(
|
382
387
|
"/sessions",
|
383
388
|
self.get_sessions,
|
384
389
|
methods=["GET"],
|
385
390
|
)
|
391
|
+
# Cache invalidation endpoint (no auth required - allowed to fail)
|
392
|
+
app.add_api_route(
|
393
|
+
"/api/clear-caches",
|
394
|
+
self.clear_caches,
|
395
|
+
methods=["POST"],
|
396
|
+
)
|
386
397
|
|
387
398
|
async def verify_api_key(
|
388
399
|
self, credentials: HTTPAuthorizationCredentials = _auth_dependency
|
@@ -446,6 +457,49 @@ class OpenEdisonProxy:
|
|
446
457
|
detail=f"Failed to get mounted servers: {str(e)}",
|
447
458
|
) from e
|
448
459
|
|
460
|
+
async def reinitialize_mcp_servers(self) -> dict[str, Any]:
|
461
|
+
"""Reinitialize all MCP servers by creating a fresh instance and reloading config."""
|
462
|
+
old_mcp = None
|
463
|
+
try:
|
464
|
+
log.info("🔄 Reinitializing MCP servers via API endpoint")
|
465
|
+
|
466
|
+
# Reload configuration from disk
|
467
|
+
log.info("Reloading configuration from disk")
|
468
|
+
from src.config import Config
|
469
|
+
|
470
|
+
fresh_config = Config.load()
|
471
|
+
log.info("✅ Configuration reloaded from disk")
|
472
|
+
|
473
|
+
# Create a completely new SingleUserMCP instance to ensure clean state
|
474
|
+
old_mcp = self.single_user_mcp
|
475
|
+
self.single_user_mcp = SingleUserMCP()
|
476
|
+
|
477
|
+
# Initialize the new instance with fresh config
|
478
|
+
await self.single_user_mcp.initialize(fresh_config)
|
479
|
+
|
480
|
+
# Get final status
|
481
|
+
final_mounted = await self.single_user_mcp.get_mounted_servers()
|
482
|
+
|
483
|
+
result = {
|
484
|
+
"status": "success",
|
485
|
+
"message": "MCP servers reinitialized successfully",
|
486
|
+
"final_mounted_servers": [server["name"] for server in final_mounted],
|
487
|
+
"total_final_mounted": len(final_mounted),
|
488
|
+
}
|
489
|
+
|
490
|
+
log.info("✅ MCP servers reinitialized successfully via API")
|
491
|
+
return result
|
492
|
+
|
493
|
+
except Exception as e:
|
494
|
+
log.error(f"❌ Failed to reinitialize MCP servers: {e}")
|
495
|
+
# Restore the old instance on failure
|
496
|
+
if old_mcp is not None:
|
497
|
+
self.single_user_mcp = old_mcp
|
498
|
+
raise HTTPException(
|
499
|
+
status_code=500,
|
500
|
+
detail=f"Failed to reinitialize MCP servers: {str(e)}",
|
501
|
+
) from e
|
502
|
+
|
449
503
|
async def get_sessions(self) -> dict[str, Any]:
|
450
504
|
"""Return recent MCP session summaries from local SQLite.
|
451
505
|
|
@@ -495,6 +549,21 @@ class OpenEdisonProxy:
|
|
495
549
|
log.error(f"Failed to fetch sessions: {e}")
|
496
550
|
raise HTTPException(status_code=500, detail="Failed to fetch sessions") from e
|
497
551
|
|
552
|
+
async def clear_caches(self) -> dict[str, str]:
|
553
|
+
"""Clear all permission caches to force reload from configuration files."""
|
554
|
+
try:
|
555
|
+
from src.middleware.data_access_tracker import clear_all_permissions_caches
|
556
|
+
|
557
|
+
log.info("🔄 Clearing all permission caches via API endpoint")
|
558
|
+
clear_all_permissions_caches()
|
559
|
+
log.info("✅ All permission caches cleared successfully")
|
560
|
+
|
561
|
+
return {"status": "success", "message": "All permission caches cleared"}
|
562
|
+
except Exception as e:
|
563
|
+
log.error(f"❌ Failed to clear permission caches: {e}")
|
564
|
+
# Don't raise HTTPException - allow to fail gracefully as requested
|
565
|
+
return {"status": "error", "message": f"Failed to clear caches: {str(e)}"}
|
566
|
+
|
498
567
|
# ---- MCP validation ----
|
499
568
|
class _ValidateRequest(BaseModel):
|
500
569
|
name: str | None = Field(None, description="Optional server name label")
|
@@ -551,9 +620,9 @@ class OpenEdisonProxy:
|
|
551
620
|
"args": body.args,
|
552
621
|
"has_roots": bool(body.roots),
|
553
622
|
},
|
554
|
-
"tools": [self._safe_tool(t) for t in tools],
|
623
|
+
"tools": [self._safe_tool(t, prefix=server_name) for t in tools],
|
555
624
|
"resources": [self._safe_resource(r) for r in resources],
|
556
|
-
"prompts": [self._safe_prompt(p) for p in prompts],
|
625
|
+
"prompts": [self._safe_prompt(p, prefix=server_name) for p in prompts],
|
557
626
|
}
|
558
627
|
except TimeoutError as te: # noqa: PERF203
|
559
628
|
log.error(f"MCP validation timed out: {te}\n{traceback.format_exc()}")
|
@@ -624,10 +693,13 @@ class OpenEdisonProxy:
|
|
624
693
|
timeout = body.timeout_s if isinstance(body.timeout_s, (int | float)) else 20.0
|
625
694
|
return await asyncio.wait_for(list_all(), timeout=timeout)
|
626
695
|
|
627
|
-
def _safe_tool(self, t: Any) -> dict[str, Any]:
|
696
|
+
def _safe_tool(self, t: Any, prefix: str) -> dict[str, Any]:
|
628
697
|
name = getattr(t, "name", None)
|
629
698
|
description = getattr(t, "description", None)
|
630
|
-
return {
|
699
|
+
return {
|
700
|
+
"name": prefix + "_" + str(name) if name is not None else "",
|
701
|
+
"description": description,
|
702
|
+
}
|
631
703
|
|
632
704
|
def _safe_resource(self, r: Any) -> dict[str, Any]:
|
633
705
|
uri = getattr(r, "uri", None)
|
@@ -638,7 +710,10 @@ class OpenEdisonProxy:
|
|
638
710
|
description = getattr(r, "description", None)
|
639
711
|
return {"uri": uri_str, "description": description}
|
640
712
|
|
641
|
-
def _safe_prompt(self, p: Any) -> dict[str, Any]:
|
713
|
+
def _safe_prompt(self, p: Any, prefix: str) -> dict[str, Any]:
|
642
714
|
name = getattr(p, "name", None)
|
643
715
|
description = getattr(p, "description", None)
|
644
|
-
return {
|
716
|
+
return {
|
717
|
+
"name": prefix + "_" + str(name) if name is not None else "",
|
718
|
+
"description": description,
|
719
|
+
}
|
@@ -7,6 +7,7 @@ Handles MCP protocol communication with running servers using a unified composit
|
|
7
7
|
|
8
8
|
from typing import Any, TypedDict
|
9
9
|
|
10
|
+
from fastmcp import Client as FastMCPClient
|
10
11
|
from fastmcp import FastMCP
|
11
12
|
from loguru import logger as log
|
12
13
|
|
@@ -107,103 +108,27 @@ class SingleUserMCP(FastMCP[Any]):
|
|
107
108
|
Returns:
|
108
109
|
True if composite proxy was created successfully, False otherwise
|
109
110
|
"""
|
110
|
-
|
111
|
-
|
112
|
-
log.info("No real servers to mount in composite proxy")
|
113
|
-
return True
|
114
|
-
|
115
|
-
# Convert to FastMCP config format
|
116
|
-
fastmcp_config = self._convert_to_fastmcp_config(enabled_servers)
|
117
|
-
|
118
|
-
log.info(
|
119
|
-
f"Creating composite proxy for servers: {list(fastmcp_config['mcpServers'].keys())}"
|
120
|
-
)
|
121
|
-
|
122
|
-
# Create the composite proxy using FastMCP's multi-server support
|
123
|
-
self.composite_proxy = FastMCP.as_proxy(
|
124
|
-
backend=fastmcp_config, name="open-edison-composite-proxy"
|
125
|
-
)
|
126
|
-
|
127
|
-
# Import the composite proxy into this main server
|
128
|
-
# Tools and resources will be automatically namespaced by server name
|
129
|
-
await self.import_server(self.composite_proxy)
|
130
|
-
|
131
|
-
# Track mounted servers for status reporting
|
132
|
-
for server_config in enabled_servers:
|
133
|
-
self.mounted_servers[server_config.name] = MountedServerInfo(
|
134
|
-
config=server_config, proxy=self.composite_proxy
|
135
|
-
)
|
136
|
-
|
137
|
-
log.info(f"✅ Created composite proxy with {len(enabled_servers)} servers")
|
111
|
+
if not enabled_servers:
|
112
|
+
log.info("No real servers to mount in composite proxy")
|
138
113
|
return True
|
139
114
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
True if mounting was successful, False otherwise
|
153
|
-
"""
|
154
|
-
try:
|
155
|
-
# Check if server is already mounted
|
156
|
-
if server_config.name in self.mounted_servers:
|
157
|
-
log.info(f"Server {server_config.name} is already mounted")
|
158
|
-
return True
|
159
|
-
|
160
|
-
# Handle test servers separately
|
161
|
-
if server_config.command == "echo":
|
162
|
-
return await self._mount_test_server(server_config)
|
163
|
-
|
164
|
-
# For real servers, we need to rebuild the composite proxy
|
165
|
-
log.info(f"Mounting server {server_config.name} via composite proxy rebuild")
|
166
|
-
|
167
|
-
# Get currently mounted servers and add the new one
|
168
|
-
current_configs = [mounted["config"] for mounted in self.mounted_servers.values()]
|
169
|
-
|
170
|
-
# Add the new server if not already there
|
171
|
-
if server_config not in current_configs:
|
172
|
-
current_configs.append(server_config)
|
173
|
-
|
174
|
-
# Rebuild composite proxy with new server list
|
175
|
-
return await self.create_composite_proxy(current_configs)
|
176
|
-
|
177
|
-
except Exception as e:
|
178
|
-
log.error(f"❌ Failed to mount server {server_config.name}: {e}")
|
179
|
-
return False
|
180
|
-
|
181
|
-
async def unmount_server(self, server_name: str) -> bool:
|
182
|
-
"""
|
183
|
-
Unmount an MCP server and stop its subprocess.
|
184
|
-
|
185
|
-
NOTE: For servers in the composite proxy, this will require rebuilding
|
186
|
-
the entire composite proxy without the specified server.
|
187
|
-
"""
|
188
|
-
try:
|
189
|
-
# Check if this is a test server (individually mounted)
|
190
|
-
if server_name in self.mounted_servers:
|
191
|
-
mounted = self.mounted_servers[server_name]
|
192
|
-
if mounted["config"].command == "echo":
|
193
|
-
# Test server - handle individually
|
194
|
-
await self._cleanup_mounted_server(server_name)
|
195
|
-
return True
|
196
|
-
|
197
|
-
# Real server in composite proxy - needs full rebuild
|
198
|
-
log.warning(f"Unmounting {server_name} requires rebuilding composite proxy")
|
199
|
-
return await self._rebuild_composite_proxy_without(server_name)
|
200
|
-
|
201
|
-
log.warning(f"Server {server_name} not found in mounted servers")
|
202
|
-
return False
|
115
|
+
# Import the composite proxy into this main server
|
116
|
+
# Tools and resources will be automatically namespaced by server name
|
117
|
+
for server_config in enabled_servers:
|
118
|
+
server_name = server_config.name
|
119
|
+
# Skip if this server would produce an empty config (e.g., misconfigured)
|
120
|
+
fastmcp_config = self._convert_to_fastmcp_config([server_config])
|
121
|
+
if not fastmcp_config.get("mcpServers"):
|
122
|
+
log.warning(f"Skipping server '{server_name}' due to empty MCP config")
|
123
|
+
continue
|
124
|
+
proxy = FastMCP.as_proxy(FastMCPClient(fastmcp_config))
|
125
|
+
self.mount(proxy, prefix=server_name)
|
126
|
+
self.mounted_servers[server_name] = MountedServerInfo(config=server_config, proxy=proxy)
|
203
127
|
|
204
|
-
|
205
|
-
|
206
|
-
|
128
|
+
log.info(
|
129
|
+
f"✅ Created composite proxy with {len(enabled_servers)} servers ({self.mounted_servers.keys()})"
|
130
|
+
)
|
131
|
+
return True
|
207
132
|
|
208
133
|
async def _rebuild_composite_proxy_without(self, excluded_server: str) -> bool:
|
209
134
|
"""Rebuild the composite proxy without the specified server."""
|
@@ -233,6 +158,7 @@ class SingleUserMCP(FastMCP[Any]):
|
|
233
158
|
|
234
159
|
async def _cleanup_mounted_server(self, server_name: str) -> None:
|
235
160
|
"""Clean up mounted server resources."""
|
161
|
+
# TODO not sure this is possible for the self object? i.e. there is no self.unmount
|
236
162
|
if server_name in self.mounted_servers:
|
237
163
|
del self.mounted_servers[server_name]
|
238
164
|
log.info(f"✅ Unmounted MCP server: {server_name}")
|
@@ -270,6 +196,80 @@ class SingleUserMCP(FastMCP[Any]):
|
|
270
196
|
|
271
197
|
log.info("✅ Single User MCP server initialized with composite proxy")
|
272
198
|
|
199
|
+
async def reinitialize(self, test_config: Any | None = None) -> dict[str, Any]:
|
200
|
+
"""
|
201
|
+
Reinitialize all MCP servers by cleaning up existing ones and reloading config.
|
202
|
+
|
203
|
+
This method:
|
204
|
+
1. Cleans up all mounted servers and MCP proxies
|
205
|
+
2. Reloads the configuration
|
206
|
+
3. Reinitializes all enabled servers
|
207
|
+
|
208
|
+
Args:
|
209
|
+
test_config: Optional test configuration to use instead of reloading from disk
|
210
|
+
|
211
|
+
Returns:
|
212
|
+
Dictionary with reinitialization status and details
|
213
|
+
"""
|
214
|
+
log.info("🔄 Reinitializing all MCP servers")
|
215
|
+
|
216
|
+
try:
|
217
|
+
# Step 1: Clean up existing mounted servers and proxies
|
218
|
+
log.info("Cleaning up existing mounted servers and proxies")
|
219
|
+
|
220
|
+
# Clean up composite proxy if it exists
|
221
|
+
if self.composite_proxy is not None:
|
222
|
+
log.info("Cleaning up composite proxy")
|
223
|
+
self.composite_proxy = None
|
224
|
+
|
225
|
+
# Clean up all mounted servers
|
226
|
+
mounted_server_names = list(self.mounted_servers.keys())
|
227
|
+
for server_name in mounted_server_names:
|
228
|
+
await self._cleanup_mounted_server(server_name)
|
229
|
+
|
230
|
+
# Clear the mounted servers dictionary completely
|
231
|
+
self.mounted_servers.clear()
|
232
|
+
|
233
|
+
log.info(f"✅ Cleaned up {len(mounted_server_names)} mounted servers")
|
234
|
+
|
235
|
+
# Step 2: Reload configuration if not using test config
|
236
|
+
config_to_use = test_config
|
237
|
+
if test_config is None:
|
238
|
+
log.info("Reloading configuration from disk")
|
239
|
+
# Import here to avoid circular imports
|
240
|
+
from src.config import Config
|
241
|
+
|
242
|
+
config_to_use = Config.load()
|
243
|
+
log.info("✅ Configuration reloaded from disk")
|
244
|
+
|
245
|
+
# Step 3: Reinitialize all servers
|
246
|
+
log.info("Reinitializing servers with fresh configuration")
|
247
|
+
await self.initialize(config_to_use)
|
248
|
+
|
249
|
+
# Step 4: Get final status
|
250
|
+
final_mounted = await self.get_mounted_servers()
|
251
|
+
|
252
|
+
result = {
|
253
|
+
"status": "success",
|
254
|
+
"message": "MCP servers reinitialized successfully",
|
255
|
+
"cleaned_up_servers": mounted_server_names,
|
256
|
+
"final_mounted_servers": [server["name"] for server in final_mounted],
|
257
|
+
"total_final_mounted": len(final_mounted),
|
258
|
+
}
|
259
|
+
|
260
|
+
log.info(
|
261
|
+
f"✅ Reinitialization complete. Final mounted servers: {result['final_mounted_servers']}"
|
262
|
+
)
|
263
|
+
return result
|
264
|
+
|
265
|
+
except Exception as e:
|
266
|
+
log.error(f"❌ Failed to reinitialize MCP servers: {e}")
|
267
|
+
return {
|
268
|
+
"status": "error",
|
269
|
+
"message": f"Failed to reinitialize MCP servers: {str(e)}",
|
270
|
+
"error": str(e),
|
271
|
+
}
|
272
|
+
|
273
273
|
def _calculate_risk_level(self, trifecta: dict[str, bool]) -> str:
|
274
274
|
"""
|
275
275
|
Calculate a human-readable risk level based on trifecta flags.
|
@@ -256,5 +256,84 @@
|
|
256
256
|
"read_untrusted_public_data": false,
|
257
257
|
"acl": "PRIVATE"
|
258
258
|
}
|
259
|
+
},
|
260
|
+
"atlassian": {
|
261
|
+
"getAccessibleAtlassianResources": {
|
262
|
+
"enabled": true,
|
263
|
+
"write_operation": false,
|
264
|
+
"read_private_data": true,
|
265
|
+
"read_untrusted_public_data": true,
|
266
|
+
"acl": "PUBLIC"
|
267
|
+
},
|
268
|
+
"getConfluenceSpaces": {
|
269
|
+
"enabled": true,
|
270
|
+
"write_operation": false,
|
271
|
+
"read_private_data": true,
|
272
|
+
"read_untrusted_public_data": true,
|
273
|
+
"acl": "PUBLIC"
|
274
|
+
},
|
275
|
+
"getPagesInConfluenceSpace": {
|
276
|
+
"enabled": true,
|
277
|
+
"write_operation": false,
|
278
|
+
"read_private_data": true,
|
279
|
+
"read_untrusted_public_data": true,
|
280
|
+
"acl": "PUBLIC"
|
281
|
+
},
|
282
|
+
"searchConfluenceUsingCql": {
|
283
|
+
"enabled": true,
|
284
|
+
"write_operation": false,
|
285
|
+
"read_private_data": true,
|
286
|
+
"read_untrusted_public_data": true,
|
287
|
+
"acl": "PUBLIC"
|
288
|
+
},
|
289
|
+
"createConfluencePage": {
|
290
|
+
"enabled": true,
|
291
|
+
"write_operation": true,
|
292
|
+
"read_private_data": false,
|
293
|
+
"read_untrusted_public_data": false,
|
294
|
+
"acl": "PUBLIC"
|
295
|
+
}
|
296
|
+
},
|
297
|
+
"zapier": {
|
298
|
+
"confluence_cloud_search_for_page_or_blog_post": {
|
299
|
+
"enabled": true,
|
300
|
+
"write_operation": false,
|
301
|
+
"read_private_data": true,
|
302
|
+
"read_untrusted_public_data": true,
|
303
|
+
"acl": "PUBLIC",
|
304
|
+
"description": "Searches for pages or blog posts in Confluence Cloud"
|
305
|
+
},
|
306
|
+
"confluence_cloud_create_page_or_blog_post": {
|
307
|
+
"enabled": true,
|
308
|
+
"write_operation": true,
|
309
|
+
"read_private_data": false,
|
310
|
+
"read_untrusted_public_data": false,
|
311
|
+
"acl": "PUBLIC",
|
312
|
+
"description": "Creates a new page or blog post in Confluence Cloud"
|
313
|
+
},
|
314
|
+
"confluence_cloud_api_request_beta": {
|
315
|
+
"enabled": false,
|
316
|
+
"write_operation": true,
|
317
|
+
"read_private_data": true,
|
318
|
+
"read_untrusted_public_data": true,
|
319
|
+
"acl": "PUBLIC",
|
320
|
+
"description": "Makes raw HTTP requests with Confluence Cloud authentication (advanced action). Caution: Disabled because it can perform reads and writes"
|
321
|
+
},
|
322
|
+
"add_tools": {
|
323
|
+
"enabled": true,
|
324
|
+
"write_operation": false,
|
325
|
+
"read_private_data": false,
|
326
|
+
"read_untrusted_public_data": false,
|
327
|
+
"acl": "PUBLIC",
|
328
|
+
"description": "Adds a new tool to the tool registry. Caution: Do not disable it is required for zapier to work properly"
|
329
|
+
},
|
330
|
+
"edit_tools": {
|
331
|
+
"enabled": true,
|
332
|
+
"write_operation": false,
|
333
|
+
"read_private_data": false,
|
334
|
+
"read_untrusted_public_data": false,
|
335
|
+
"acl": "PUBLIC",
|
336
|
+
"description": "Edit your existing MCP provider actions. TODO: Unknown side-effects"
|
337
|
+
}
|
259
338
|
}
|
260
339
|
}
|
@@ -1,14 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"_metadata": {
|
3
|
-
"description": "Prompt security classifications for Open Edison data access tracker",
|
4
|
-
"last_updated": "2025-08-07"
|
5
|
-
},
|
6
|
-
"builtin": {
|
7
|
-
"summarize_text": {
|
8
|
-
"enabled": true,
|
9
|
-
"write_operation": false,
|
10
|
-
"read_private_data": false,
|
11
|
-
"read_untrusted_public_data": false
|
12
|
-
}
|
13
|
-
}
|
14
|
-
}
|
@@ -1,14 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"_metadata": {
|
3
|
-
"description": "Resource security classifications for Open Edison data access tracker",
|
4
|
-
"last_updated": "2025-08-07"
|
5
|
-
},
|
6
|
-
"builtin": {
|
7
|
-
"config://app": {
|
8
|
-
"enabled": true,
|
9
|
-
"write_operation": false,
|
10
|
-
"read_private_data": false,
|
11
|
-
"read_untrusted_public_data": false
|
12
|
-
}
|
13
|
-
}
|
14
|
-
}
|
@@ -1,170 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"_metadata": {
|
3
|
-
"description": "Tool security classifications for Open Edison data access tracker",
|
4
|
-
"last_updated": "2025-08-07"
|
5
|
-
},
|
6
|
-
"builtin": {
|
7
|
-
"echo": {
|
8
|
-
"enabled": true,
|
9
|
-
"write_operation": false,
|
10
|
-
"read_private_data": false,
|
11
|
-
"read_untrusted_public_data": false
|
12
|
-
},
|
13
|
-
"get_server_info": {
|
14
|
-
"enabled": true,
|
15
|
-
"write_operation": false,
|
16
|
-
"read_private_data": false,
|
17
|
-
"read_untrusted_public_data": false
|
18
|
-
},
|
19
|
-
"get_security_status": {
|
20
|
-
"enabled": true,
|
21
|
-
"write_operation": false,
|
22
|
-
"read_private_data": false,
|
23
|
-
"read_untrusted_public_data": false
|
24
|
-
}
|
25
|
-
},
|
26
|
-
"filesystem": {
|
27
|
-
"filesystem_read_file": {
|
28
|
-
"enabled": true,
|
29
|
-
"write_operation": false,
|
30
|
-
"read_private_data": true,
|
31
|
-
"read_untrusted_public_data": false
|
32
|
-
},
|
33
|
-
"filesystem_read_text_file": {
|
34
|
-
"enabled": true,
|
35
|
-
"write_operation": false,
|
36
|
-
"read_private_data": true,
|
37
|
-
"read_untrusted_public_data": false
|
38
|
-
},
|
39
|
-
"filesystem_read_media_file": {
|
40
|
-
"enabled": true,
|
41
|
-
"write_operation": false,
|
42
|
-
"read_private_data": true,
|
43
|
-
"read_untrusted_public_data": false
|
44
|
-
},
|
45
|
-
"filesystem_read_multiple_files": {
|
46
|
-
"enabled": true,
|
47
|
-
"write_operation": false,
|
48
|
-
"read_private_data": true,
|
49
|
-
"read_untrusted_public_data": false
|
50
|
-
},
|
51
|
-
"filesystem_write_file": {
|
52
|
-
"enabled": true,
|
53
|
-
"write_operation": true,
|
54
|
-
"read_private_data": true,
|
55
|
-
"read_untrusted_public_data": false
|
56
|
-
},
|
57
|
-
"filesystem_edit_file": {
|
58
|
-
"enabled": true,
|
59
|
-
"write_operation": true,
|
60
|
-
"read_private_data": true,
|
61
|
-
"read_untrusted_public_data": false
|
62
|
-
},
|
63
|
-
"filesystem_create_directory": {
|
64
|
-
"enabled": true,
|
65
|
-
"write_operation": true,
|
66
|
-
"read_private_data": false,
|
67
|
-
"read_untrusted_public_data": false
|
68
|
-
},
|
69
|
-
"filesystem_list_directory": {
|
70
|
-
"enabled": true,
|
71
|
-
"write_operation": false,
|
72
|
-
"read_private_data": true,
|
73
|
-
"read_untrusted_public_data": false
|
74
|
-
},
|
75
|
-
"filesystem_list_directory_with_sizes": {
|
76
|
-
"enabled": true,
|
77
|
-
"write_operation": false,
|
78
|
-
"read_private_data": true,
|
79
|
-
"read_untrusted_public_data": false
|
80
|
-
},
|
81
|
-
"filesystem_directory_tree": {
|
82
|
-
"enabled": true,
|
83
|
-
"write_operation": false,
|
84
|
-
"read_private_data": true,
|
85
|
-
"read_untrusted_public_data": false
|
86
|
-
},
|
87
|
-
"filesystem_move_file": {
|
88
|
-
"enabled": true,
|
89
|
-
"write_operation": true,
|
90
|
-
"read_private_data": true,
|
91
|
-
"read_untrusted_public_data": false
|
92
|
-
},
|
93
|
-
"filesystem_search_files": {
|
94
|
-
"enabled": true,
|
95
|
-
"write_operation": false,
|
96
|
-
"read_private_data": true,
|
97
|
-
"read_untrusted_public_data": false
|
98
|
-
},
|
99
|
-
"filesystem_get_file_info": {
|
100
|
-
"enabled": true,
|
101
|
-
"write_operation": false,
|
102
|
-
"read_private_data": true,
|
103
|
-
"read_untrusted_public_data": false
|
104
|
-
},
|
105
|
-
"filesystem_list_allowed_directories": {
|
106
|
-
"enabled": true,
|
107
|
-
"write_operation": false,
|
108
|
-
"read_private_data": false,
|
109
|
-
"read_untrusted_public_data": false
|
110
|
-
}
|
111
|
-
},
|
112
|
-
"sqlite": {
|
113
|
-
"sqlite_db_info": {
|
114
|
-
"enabled": true,
|
115
|
-
"write_operation": false,
|
116
|
-
"read_private_data": true,
|
117
|
-
"read_untrusted_public_data": false
|
118
|
-
},
|
119
|
-
"sqlite_query": {
|
120
|
-
"enabled": true,
|
121
|
-
"write_operation": true,
|
122
|
-
"read_private_data": true,
|
123
|
-
"read_untrusted_public_data": false
|
124
|
-
},
|
125
|
-
"sqlite_list_tables": {
|
126
|
-
"enabled": true,
|
127
|
-
"write_operation": false,
|
128
|
-
"read_private_data": true,
|
129
|
-
"read_untrusted_public_data": false
|
130
|
-
},
|
131
|
-
"sqlite_get_table_schema": {
|
132
|
-
"enabled": true,
|
133
|
-
"write_operation": false,
|
134
|
-
"read_private_data": true,
|
135
|
-
"read_untrusted_public_data": false
|
136
|
-
},
|
137
|
-
"sqlite_create_record": {
|
138
|
-
"enabled": true,
|
139
|
-
"write_operation": true,
|
140
|
-
"read_private_data": true,
|
141
|
-
"read_untrusted_public_data": false
|
142
|
-
},
|
143
|
-
"sqlite_read_records": {
|
144
|
-
"enabled": true,
|
145
|
-
"write_operation": false,
|
146
|
-
"read_private_data": true,
|
147
|
-
"read_untrusted_public_data": false
|
148
|
-
},
|
149
|
-
"sqlite_update_records": {
|
150
|
-
"enabled": true,
|
151
|
-
"write_operation": true,
|
152
|
-
"read_private_data": true,
|
153
|
-
"read_untrusted_public_data": false
|
154
|
-
},
|
155
|
-
"sqlite_delete_records": {
|
156
|
-
"enabled": true,
|
157
|
-
"write_operation": true,
|
158
|
-
"read_private_data": true,
|
159
|
-
"read_untrusted_public_data": false
|
160
|
-
}
|
161
|
-
},
|
162
|
-
"test_server": {
|
163
|
-
"test_server/*": {
|
164
|
-
"enabled": true,
|
165
|
-
"write_operation": false,
|
166
|
-
"read_private_data": true,
|
167
|
-
"read_untrusted_public_data": false
|
168
|
-
}
|
169
|
-
}
|
170
|
-
}
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|