open-edison 0.1.26__py3-none-any.whl → 0.1.30__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.26
3
+ Version: 0.1.30
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
@@ -9,6 +9,7 @@ Requires-Dist: aiohttp>=3.12.14
9
9
  Requires-Dist: aiosqlite>=0.20.0
10
10
  Requires-Dist: fastapi>=0.116.1
11
11
  Requires-Dist: fastmcp>=2.10.5
12
+ Requires-Dist: hatchling>=1.27.0
12
13
  Requires-Dist: httpx>=0.28.1
13
14
  Requires-Dist: loguru>=0.7.3
14
15
  Requires-Dist: opentelemetry-api>=1.36.0
@@ -27,7 +28,11 @@ Description-Content-Type: text/markdown
27
28
 
28
29
  # OpenEdison 🔒⚡️
29
30
 
30
- 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
+ > The secure MCP proxy gateway
32
+
33
+ Connect AI to your data/software securely without risk of data exfiltration. Gain visibility, block threats, and get alerts on the data your agent is reading/writing. No more "approve fatigue" with the MCP tool-call approvals.
34
+
35
+ OpenEdison solves the [lethal trifecta problem](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/), which can cause agent hijacking & data exfiltration by malicious actors.
31
36
 
32
37
  <p align="center">
33
38
  <img src="media/trifecta520p.gif" alt="Trifecta Security Risk Animation" width="520">
@@ -42,22 +47,23 @@ MCP security gateway that prevents data exfiltration—via direct access or tool
42
47
  <img alt="Python Version" src="https://img.shields.io/badge/python-3.12-blue?logo=python">
43
48
  <img src="https://img.shields.io/badge/License-GPLv3-blue" alt="License">
44
49
 
45
-
46
50
  </p>
47
51
 
48
- ---
49
-
52
+ ---
50
53
 
51
54
  ## Features ✨
52
55
 
53
- - 🛑 **Prevent Data Leaks** - Edison automatically blocks any data leaks, even if your AI gets jailbroken
54
- - 👤 **Single-user MCP proxy** - No multi-user complexity, just a simple proxy for your MCP servers
55
- - 🗂️ **JSON configuration** - Easy to configure and manage your MCP servers
56
- - 🖥️ **Simple local frontend** - Track and monitor your MCP interactions, servers, and sessions.
57
- - 📊 **Session tracking** - Track and monitor your MCP interactions
56
+ - 🛑 **Data leak blocker** - Edison automatically blocks any data leaks, even if your AI gets jailbroken
57
+ - 🕰️ **Deterministic execution** - Deterministic execution. Guaranteed data exfiltration blocker.
58
+ - 🗂️ **Easily configurable** - Easy to configure and manage your MCP servers
59
+ - 📊 **Visibility into agent interactions** - Track and monitor your agents and their interactions with connected software/data via MCP calls
58
60
  - 🔗 **Simple API** - REST API for managing MCP servers and proxying requests
59
61
  - 🐳 **Docker support** - Run in a container for easy deployment
60
62
 
63
+ ## About Edison.watch 🏢
64
+
65
+ Edison helps you gain observability, control, and policy enforcement for all AI interactions with systems of records, existing company software and data. Prevent AI from causing data leakage, lightning-fast setup for cross-system governance.
66
+
61
67
  ## Quick Start 🚀
62
68
 
63
69
  The fastest way to get started:
@@ -68,7 +74,7 @@ The fastest way to get started:
68
74
  curl -fsSL https://raw.githubusercontent.com/Edison-Watch/open-edison/main/curl_pipe_bash.sh | bash
69
75
  ```
70
76
 
71
- Run locally with uvx: `uvx open-edison --config-dir ~/edison-config`
77
+ Run locally with uvx: `uvx open-edison`
72
78
 
73
79
  <details>
74
80
  <summary>⬇️ Install Node.js/npm (optional for MCP tools)</summary>
@@ -102,11 +108,11 @@ After installation, ensure that `npx` is available on PATH.
102
108
 
103
109
  ```bash
104
110
  # Using uvx
105
- uvx open-edison --help
111
+ uvx open-edison
106
112
 
107
113
  # Using pipx
108
114
  pipx install open-edison
109
- open-edison --help
115
+ open-edison
110
116
  ```
111
117
 
112
118
  Run with a custom config directory:
@@ -349,8 +355,6 @@ Use the `get_security_status` tool to monitor your session's current risk level
349
355
 
350
356
  </details>
351
357
 
352
-
353
-
354
358
  ## Documentation 📚
355
359
 
356
360
  📚 **Complete documentation available in [`docs/`](docs/)**
@@ -360,7 +364,6 @@ Use the `get_security_status` tool to monitor your session's current risk level
360
364
  - 📡 **[API Reference](docs/quick-reference/api_reference.md)** - REST API documentation
361
365
  - 🧑‍💻 **[Development Guide](docs/development/development_guide.md)** - Contributing and development
362
366
 
363
-
364
367
  <details>
365
368
  <summary>📄 License</summary>
366
369
 
@@ -0,0 +1,21 @@
1
+ src/__init__.py,sha256=QWeZdjAm2D2B0eWhd8m2-DPpWvIP26KcNJxwEoU1oEQ,254
2
+ src/__main__.py,sha256=kQsaVyzRa_ESC57JpKDSQJAHExuXme0rM5beJsYxFeA,161
3
+ src/cli.py,sha256=57XJH-Zd45HgxfILZlNWkHNNjuKKMSuer0l3Q0BgZ_k,10201
4
+ src/config.py,sha256=LHLQWWWvPzdQfCEUCbCMqHpEOnXJhmPFmk9bSVGnUyY,9443
5
+ src/events.py,sha256=rBH7rnaSWZ7GIC8zyBTwpcvIKWmKYCki-DNGgJhxPow,5001
6
+ src/oauth_manager.py,sha256=qcQa5BDRZr4bjqiXNflCnrXOh9mo9JVjvP2Caseg2Uc,9943
7
+ src/permissions.py,sha256=NGAnlG_z59HEiVA-k3cYvwmmiuHzxuNb5Tbd5umbL00,10483
8
+ src/server.py,sha256=0sdBz7VLWy5moO1GtecljuXFK7AV6mg6UOi6At4MPEo,45166
9
+ src/single_user_mcp.py,sha256=P-D6_B_l7fJ2OzMjVeg56OkzRkCsODqKpky4ecHRj6I,17356
10
+ src/telemetry.py,sha256=-RZPIjpI53zbsKmp-63REeZ1JirWHV5WvpSRa2nqZEk,11321
11
+ src/frontend_dist/index.html,sha256=skY5DFiRUpVBaVjXzqGkoDhHZM99qeuf08WrcEh-0EU,673
12
+ src/frontend_dist/sw.js,sha256=rihX1es-vWwjmtnXyaksJjs2dio6MVAOTAWwQPeJUYw,2164
13
+ src/frontend_dist/assets/index-DOR5YaNc.js,sha256=EhRRS_nAymGQDyv3GA4a7XuBVcNkIdckaKYLmruBZKk,254616
14
+ src/frontend_dist/assets/index-o6_8mdM8.css,sha256=nwmX_6q55mB9463XN2JM8BdeihjkALpQK83Fc3_iGvE,15936
15
+ src/middleware/data_access_tracker.py,sha256=bArBffWgYmvxOx9z_pgXQhogvnWQcc1m6WvEblDD4gw,15039
16
+ src/middleware/session_tracking.py,sha256=5W1VH9HNqIZeX0HNxDEm41U4GY6SqKSXtApDEeZK2qo,23084
17
+ open_edison-0.1.30.dist-info/METADATA,sha256=9AiZvDoQLKWeIF8G2if1YE7v9u9P0wT9-xungNjqGZI,12136
18
+ open_edison-0.1.30.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
+ open_edison-0.1.30.dist-info/entry_points.txt,sha256=qNAkJcnoTXRhj8J--3PDmXz_TQKdB8H_0C9wiCtDIyA,72
20
+ open_edison-0.1.30.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
21
+ open_edison-0.1.30.dist-info/RECORD,,
src/cli.py CHANGED
@@ -125,6 +125,12 @@ def _spawn_frontend_dev( # noqa: C901 - pragmatic complexity for env probing
125
125
  )
126
126
  # No separate website process needed. Return sentinel port (-1) so caller knows not to warn.
127
127
  return (-1, None)
128
+
129
+ if static_dir is None:
130
+ raise RuntimeError(
131
+ "No packaged dashboard detected. The website will be served from the frontend directory."
132
+ )
133
+
128
134
  pkg_frontend_candidates = [
129
135
  Path(__file__).parent / "frontend", # inside package dir
130
136
  Path(__file__).parent.parent / "frontend", # site-packages root
src/config.py CHANGED
@@ -10,6 +10,7 @@ import os
10
10
  import sys
11
11
  import tomllib
12
12
  from dataclasses import asdict, dataclass
13
+ from functools import cache
13
14
  from pathlib import Path
14
15
  from typing import Any, cast
15
16
 
@@ -37,8 +38,7 @@ def get_config_dir() -> Path:
37
38
  try:
38
39
  return Path(env_dir).expanduser().resolve()
39
40
  except Exception:
40
- # Fall through to defaults
41
- pass
41
+ log.warning(f"Failed to resolve OPEN_EDISON_CONFIG_DIR: {env_dir}")
42
42
 
43
43
  # Platform-specific defaults
44
44
  try:
@@ -58,26 +58,8 @@ def get_config_dir() -> Path:
58
58
  return (Path.home() / ".open-edison").resolve()
59
59
 
60
60
 
61
- def _default_config_path() -> Path:
62
- """Determine default config.json path.
63
-
64
- In development (editable or source checkout), prefer repository root
65
- `config.json` when present. In an installed package (site-packages),
66
- use the resolved user config dir.
67
- """
68
- repo_pyproject = root_dir / "pyproject.toml"
69
- repo_config = root_dir / "config.json"
70
-
71
- # If pyproject.toml exists next to src/, we are likely in a repo checkout
72
- # Prefer the user config directory if a config already exists there (runtime source of truth),
73
- # otherwise fall back to the repo copy.
74
- if repo_pyproject.exists():
75
- user_cfg = get_config_dir() / "config.json"
76
- if user_cfg.exists():
77
- return user_cfg
78
- return repo_config
79
-
80
- # Installed package: prefer user config directory
61
+ def get_config_json_path() -> Path:
62
+ """Get the path to the config.json file"""
81
63
  return get_config_dir() / "config.json"
82
64
 
83
65
 
@@ -156,6 +138,15 @@ class TelemetryConfig:
156
138
  export_interval_ms: int = 60000
157
139
 
158
140
 
141
+ @cache
142
+ def load_json_file(path: Path) -> dict[str, Any]:
143
+ """Load a JSON file from the given path.
144
+ Kept as a separate function because we want to manually clear cache sometimes (update in config)"""
145
+ log.info(f"Loading configuration from {path}")
146
+ with open(path) as f:
147
+ return json.load(f)
148
+
149
+
159
150
  @dataclass
160
151
  class Config:
161
152
  """Main configuration class"""
@@ -188,21 +179,18 @@ class Config:
188
179
  If no path is provided, uses OPEN_EDISON_CONFIG_DIR or project root.
189
180
  """
190
181
  if config_path is None:
191
- config_path = _default_config_path()
182
+ config_path = get_config_json_path()
192
183
  else:
193
184
  # If a directory was passed, use config.json inside it
194
185
  if config_path.is_dir():
195
186
  config_path = config_path / "config.json"
196
187
 
197
- log.info(f"Loading configuration from {config_path}")
198
-
199
188
  if not config_path.exists():
200
189
  log.warning(f"Config file not found at {config_path}, creating default config")
201
190
  self.create_default()
202
191
  self.save(config_path)
203
192
 
204
- with open(config_path) as f:
205
- data: dict[str, Any] = json.load(f)
193
+ data = load_json_file(config_path)
206
194
 
207
195
  mcp_servers_data = data.get("mcp_servers", []) # type: ignore
208
196
  server_data = data.get("server", {}) # type: ignore
@@ -248,7 +236,7 @@ class Config:
248
236
  def save(self, config_path: Path | None = None) -> None:
249
237
  """Save configuration to JSON file"""
250
238
  if config_path is None:
251
- config_path = _default_config_path()
239
+ config_path = get_config_json_path()
252
240
  else:
253
241
  # If a directory was passed, save to config.json inside it
254
242
  if config_path.is_dir():