open-edison 0.1.16__py3-none-any.whl → 0.1.19__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: open-edison
3
- Version: 0.1.16
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,15 +27,7 @@ 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, single-user use.
31
-
32
- Just want to run it?
33
-
34
- ```bash
35
- curl -fsSL https://raw.githubusercontent.com/Edison-Watch/open-edison/main/curl_pipe_bash.sh | bash
36
- ```
37
-
38
- Run locally with uvx: `uvx open-edison --config-dir ~/edison-config`
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.
39
31
 
40
32
  <div align="center">
41
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>
@@ -52,7 +44,40 @@ Run locally with uvx: `uvx open-edison --config-dir ~/edison-config`
52
44
 
53
45
  ## Quick Start
54
46
 
55
- ### Install from PyPI
47
+ The fastest way to get started:
48
+
49
+ ```bash
50
+ # Installs uv (via Astral installer) and launches open-edison with uvx.
51
+ # Note: This does NOT install Node/npx. Install Node if you plan to use npx-based tools like mcp-remote.
52
+ curl -fsSL https://raw.githubusercontent.com/Edison-Watch/open-edison/main/curl_pipe_bash.sh | bash
53
+ ```
54
+
55
+ Run locally with uvx: `uvx open-edison --config-dir ~/edison-config`
56
+
57
+ <details>
58
+ <summary>Install Node.js/npm (optional for MCP tools)</summary>
59
+
60
+ If you need `npx` (for Node-based MCP tools like `mcp-remote`), install Node.js as well:
61
+
62
+ ![macOS](https://img.shields.io/badge/mac%20os-000000?style=for-the-badge&logo=apple&logoColor=white)
63
+
64
+ - uv: `curl -fsSL https://astral.sh/uv/install.sh | sh`
65
+ - Node/npx: `brew install node`
66
+
67
+ ![Linux](https://img.shields.io/badge/Linux-FCC624?style=for-the-badge&logo=linux&logoColor=black)
68
+
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`
71
+
72
+ ![Windows](https://img.shields.io/badge/Windows-0078D6?style=for-the-badge&logo=windows&logoColor=white)
73
+
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.
78
+
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>
56
81
 
57
82
  #### Prerequisites
58
83
 
@@ -75,7 +100,10 @@ open-edison run --config-dir ~/edison-config
75
100
  OPEN_EDISON_CONFIG_DIR=~/edison-config open-edison run
76
101
  ```
77
102
 
78
- ### Run with Docker
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>
79
107
 
80
108
  There is a dockerfile for simple local setup.
81
109
 
@@ -94,7 +122,10 @@ make docker_run
94
122
 
95
123
  The MCP server will be available at `http://localhost:3000` and the api + frontend at `http://localhost:3001`.
96
124
 
97
- ### Run from source
125
+ </details>
126
+
127
+ <details>
128
+ <summary>⚙️ Run from source</summary>
98
129
 
99
130
  1. Clone the repository:
100
131
 
@@ -116,7 +147,7 @@ make setup
116
147
  "server": { "host": "0.0.0.0", "port": 3000, "api_key": "..." },
117
148
  "logging": { "level": "INFO", "database_path": "sessions.db" },
118
149
  "mcp_servers": [
119
- { "name": "filesystem", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"], "enabled": true },
150
+ { "name": "filesystem", "command": "uvx", "args": ["mcp-server-filesystem", "/tmp"], "enabled": true },
120
151
  { "name": "github", "enabled": false, "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "..." } }
121
152
  ]
122
153
  }
@@ -132,9 +163,12 @@ open-edison run
132
163
 
133
164
  The server will be available at `http://localhost:3000`.
134
165
 
135
- ## MCP Connection
166
+ </details>
136
167
 
137
- Connect any MCP client to Open Edison:
168
+ <details>
169
+ <summary>MCP Connection</summary>
170
+
171
+ Connect any MCP client to Open Edison (requires Node.js/npm for `npx`):
138
172
 
139
173
  ```bash
140
174
  npx -y mcp-remote http://localhost:3000/mcp/ --http-only --header "Authorization: Bearer your-api-key"
@@ -153,13 +187,17 @@ Or add to your MCP client config:
153
187
  }
154
188
  ```
155
189
 
156
- ## Usage
190
+ </details>
191
+
192
+ <details>
193
+ <summary>Usage</summary>
157
194
 
158
195
  ### API Endpoints
159
196
 
160
197
  See [API Reference](docs/quick-reference/api_reference.md) for full API documentation.
161
198
 
162
- ## Development
199
+ <details>
200
+ <summary>Development</summary>
163
201
 
164
202
  ### Setup
165
203
 
@@ -181,6 +219,11 @@ We expect `make ci` to return cleanly.
181
219
  make ci
182
220
  ```
183
221
 
222
+ </details>
223
+
224
+ <details>
225
+ <summary>⚙️ Configuration (config.json)</summary>
226
+
184
227
  ## Configuration
185
228
 
186
229
  The `config.json` file contains all configuration:
@@ -199,14 +242,23 @@ Each MCP server configuration includes:
199
242
  - `env` - Environment variables (optional)
200
243
  - `enabled` - Whether to auto-start this server
201
244
 
202
- ## Security & Permissions System
245
+ </details>
246
+
247
+ </details>
203
248
 
204
- Open Edison includes a comprehensive security monitoring system that tracks the "lethal trifecta" of AI agent risks:
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%">
205
255
 
206
256
  1. **Private data access** - Access to sensitive local files/data
207
257
  2. **Untrusted content exposure** - Exposure to external/web content
208
258
  3. **External communication** - Ability to write/send data externally
209
259
 
260
+ <img src="media/pam-diagram.png" alt="Privileged Access Management (PAM) example showing the lethal trifecta in action" width="60%">
261
+
210
262
  The configuration allows you to classify these risks across **tools**, **resources**, and **prompts** using separate configuration files.
211
263
 
212
264
  In addition to trifecta, we track Access Control Level (ACL) for each tool call,
@@ -230,6 +282,9 @@ Defines security classifications for MCP tools. See full file: [tool_permissions
230
282
  }
231
283
  ```
232
284
 
285
+ <details>
286
+ <summary>Resource Permissions (`resource_permissions.json`)</summary>
287
+
233
288
  ### Resource Permissions (`resource_permissions.json`)
234
289
 
235
290
  Defines security classifications for resource access patterns. See full file: [resource_permissions.json](resource_permissions.json), it looks like:
@@ -241,6 +296,11 @@ Defines security classifications for resource access patterns. See full file: [r
241
296
  }
242
297
  ```
243
298
 
299
+ </details>
300
+
301
+ <details>
302
+ <summary>Prompt Permissions (`prompt_permissions.json`)</summary>
303
+
244
304
  ### Prompt Permissions (`prompt_permissions.json`)
245
305
 
246
306
  Defines security classifications for prompt types. See full file: [prompt_permissions.json](prompt_permissions.json), it looks like:
@@ -252,6 +312,8 @@ Defines security classifications for prompt types. See full file: [prompt_permis
252
312
  }
253
313
  ```
254
314
 
315
+ </details>
316
+
255
317
  ### Wildcard Patterns
256
318
 
257
319
  All permission types support wildcard patterns:
@@ -266,7 +328,10 @@ All permission types support wildcard patterns:
266
328
 
267
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.
268
330
 
269
- ## Documentation
331
+ </details>
332
+
333
+ <details>
334
+ <summary>Documentation</summary>
270
335
 
271
336
  📚 **Complete documentation available in [`docs/`](docs/)**
272
337
 
@@ -275,6 +340,11 @@ Use the `get_security_status` tool to monitor your session's current risk level
275
340
  - **[API Reference](docs/quick-reference/api_reference.md)** - REST API documentation
276
341
  - **[Development Guide](docs/development/development_guide.md)** - Contributing and development
277
342
 
278
- ## License
343
+ </details>
344
+
345
+ <details>
346
+ <summary>License</summary>
279
347
 
280
348
  GPL-3.0 License - see [LICENSE](LICENSE) for details.
349
+
350
+ </details>
@@ -0,0 +1,14 @@
1
+ src/__init__.py,sha256=QWeZdjAm2D2B0eWhd8m2-DPpWvIP26KcNJxwEoU1oEQ,254
2
+ src/__main__.py,sha256=kQsaVyzRa_ESC57JpKDSQJAHExuXme0rM5beJsYxFeA,161
3
+ src/cli.py,sha256=9cJN6mRvjbCcpTyTdUVl47J7OB7bxzSy0h8tfVbHuQU,9982
4
+ src/config.py,sha256=2a5rdImQmNGggL690PQprqZVsRUAJcdo8KS2Foj9N-U,9345
5
+ src/server.py,sha256=cXW16m6UMUofQFbtM6E2EasxClhWAS-955BuasNupmM,29557
6
+ src/single_user_mcp.py,sha256=3pDBMant1DNlNPeW_NWD-uFyLrA-qNrx6sDHgDKsDfM,14457
7
+ src/telemetry.py,sha256=M8iZ7nTPA6BhbPna_xsEoTOOa7A81YyvZ0CkVYa_pPg,12619
8
+ src/middleware/data_access_tracker.py,sha256=N4g_T-JF9W7yzRIFRasY-JA7ha-Zt_Ov4nSn-TCq-Ps,27026
9
+ src/middleware/session_tracking.py,sha256=O-n8RvEVCUGAFGYny_gA7-MMQYSlvND-lj3oBZLCT3U,20046
10
+ open_edison-0.1.19.dist-info/METADATA,sha256=88QHl-ngXl0uFZwYUO2dVMtxmQ0T3eyVUE7NyP6jXAY,10905
11
+ open_edison-0.1.19.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
12
+ open_edison-0.1.19.dist-info/entry_points.txt,sha256=qNAkJcnoTXRhj8J--3PDmXz_TQKdB8H_0C9wiCtDIyA,72
13
+ open_edison-0.1.19.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
14
+ open_edison-0.1.19.dist-info/RECORD,,
@@ -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
- {message}
610
- """
639
+ """
611
640
  super().__init__(message)
@@ -102,7 +102,7 @@ def create_db_session() -> Generator[Session, None, None]:
102
102
 
103
103
  # Ensure changes are flushed to the main database file (avoid WAL for sql.js compatibility)
104
104
  @event.listens_for(engine, "connect")
105
- def _set_sqlite_pragmas(dbapi_connection, connection_record): # type: ignore[no-untyped-def]
105
+ def _set_sqlite_pragmas(dbapi_connection, connection_record): # type: ignore[no-untyped-def] # noqa
106
106
  cur = dbapi_connection.cursor() # type: ignore[attr-defined]
107
107
  try:
108
108
  cur.execute("PRAGMA journal_mode=DELETE") # type: ignore[attr-defined]