open-edison 0.1.40__tar.gz → 0.1.42__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. {open_edison-0.1.40 → open_edison-0.1.42}/PKG-INFO +10 -10
  2. {open_edison-0.1.40 → open_edison-0.1.42}/README.md +9 -9
  3. {open_edison-0.1.40 → open_edison-0.1.42}/config.json +5 -38
  4. {open_edison-0.1.40 → open_edison-0.1.42}/hatch_build.py +19 -14
  5. {open_edison-0.1.40 → open_edison-0.1.42}/pyproject.toml +2 -2
  6. open_edison-0.1.42/src/mcp_importer/api.py +106 -0
  7. {open_edison-0.1.40 → open_edison-0.1.42}/src/mcp_importer/quick_cli.py +15 -12
  8. {open_edison-0.1.40 → open_edison-0.1.42}/src/oauth_manager.py +1 -1
  9. open_edison-0.1.40/src/frontend_dist/assets/index-BUUcUfTt.js +0 -51
  10. open_edison-0.1.40/src/frontend_dist/assets/index-o6_8mdM8.css +0 -1
  11. open_edison-0.1.40/src/frontend_dist/index.html +0 -21
  12. open_edison-0.1.40/src/frontend_dist/sw.js +0 -71
  13. open_edison-0.1.40/src/mcp_importer/api.py +0 -196
  14. {open_edison-0.1.40 → open_edison-0.1.42}/.gitignore +0 -0
  15. {open_edison-0.1.40 → open_edison-0.1.42}/LICENSE +0 -0
  16. {open_edison-0.1.40 → open_edison-0.1.42}/desktop_ext/README.md +0 -0
  17. {open_edison-0.1.40 → open_edison-0.1.42}/docs/README.md +0 -0
  18. {open_edison-0.1.40 → open_edison-0.1.42}/docs/architecture/single_user_design.md +0 -0
  19. {open_edison-0.1.40 → open_edison-0.1.42}/docs/core/configuration.md +0 -0
  20. {open_edison-0.1.40 → open_edison-0.1.42}/docs/core/project_structure.md +0 -0
  21. {open_edison-0.1.40 → open_edison-0.1.42}/docs/core/proxy_usage.md +0 -0
  22. {open_edison-0.1.40 → open_edison-0.1.42}/docs/deployment/docker.md +0 -0
  23. {open_edison-0.1.40 → open_edison-0.1.42}/docs/deployment/local.md +0 -0
  24. {open_edison-0.1.40 → open_edison-0.1.42}/docs/development/contributing.md +0 -0
  25. {open_edison-0.1.40 → open_edison-0.1.42}/docs/development/development_guide.md +0 -0
  26. {open_edison-0.1.40 → open_edison-0.1.42}/docs/development/testing.md +0 -0
  27. {open_edison-0.1.40 → open_edison-0.1.42}/docs/quick-reference/api_reference.md +0 -0
  28. {open_edison-0.1.40 → open_edison-0.1.42}/docs/quick-reference/config_quick_start.md +0 -0
  29. {open_edison-0.1.40 → open_edison-0.1.42}/installation_test/README.md +0 -0
  30. {open_edison-0.1.40 → open_edison-0.1.42}/prompt_permissions.json +0 -0
  31. {open_edison-0.1.40 → open_edison-0.1.42}/resource_permissions.json +0 -0
  32. {open_edison-0.1.40 → open_edison-0.1.42}/src/__init__.py +0 -0
  33. {open_edison-0.1.40 → open_edison-0.1.42}/src/__main__.py +0 -0
  34. {open_edison-0.1.40 → open_edison-0.1.42}/src/cli.py +0 -0
  35. {open_edison-0.1.40 → open_edison-0.1.42}/src/config.py +0 -0
  36. {open_edison-0.1.40 → open_edison-0.1.42}/src/config.pyi +0 -0
  37. {open_edison-0.1.40 → open_edison-0.1.42}/src/events.py +0 -0
  38. {open_edison-0.1.40 → open_edison-0.1.42}/src/mcp_importer/__init__.py +0 -0
  39. {open_edison-0.1.40 → open_edison-0.1.42}/src/mcp_importer/__main__.py +0 -0
  40. {open_edison-0.1.40 → open_edison-0.1.42}/src/mcp_importer/cli.py +0 -0
  41. {open_edison-0.1.40 → open_edison-0.1.42}/src/mcp_importer/export_cli.py +0 -0
  42. {open_edison-0.1.40 → open_edison-0.1.42}/src/mcp_importer/exporters.py +0 -0
  43. {open_edison-0.1.40 → open_edison-0.1.42}/src/mcp_importer/import_api.py +0 -0
  44. {open_edison-0.1.40 → open_edison-0.1.42}/src/mcp_importer/importers.py +0 -0
  45. {open_edison-0.1.40 → open_edison-0.1.42}/src/mcp_importer/merge.py +0 -0
  46. {open_edison-0.1.40 → open_edison-0.1.42}/src/mcp_importer/parsers.py +0 -0
  47. {open_edison-0.1.40 → open_edison-0.1.42}/src/mcp_importer/paths.py +0 -0
  48. {open_edison-0.1.40 → open_edison-0.1.42}/src/mcp_importer/types.py +0 -0
  49. {open_edison-0.1.40 → open_edison-0.1.42}/src/middleware/data_access_tracker.py +0 -0
  50. {open_edison-0.1.40 → open_edison-0.1.42}/src/middleware/session_tracking.py +0 -0
  51. {open_edison-0.1.40 → open_edison-0.1.42}/src/permissions.py +0 -0
  52. {open_edison-0.1.40 → open_edison-0.1.42}/src/server.py +0 -0
  53. {open_edison-0.1.40 → open_edison-0.1.42}/src/single_user_mcp.py +0 -0
  54. {open_edison-0.1.40 → open_edison-0.1.42}/src/telemetry.py +0 -0
  55. {open_edison-0.1.40 → open_edison-0.1.42}/tool_permissions.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: open-edison
3
- Version: 0.1.40
3
+ Version: 0.1.42
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
@@ -76,7 +76,7 @@ Optionally, import your existing MCP configs from Cursor, VS Code, or Claude Cod
76
76
 
77
77
  ```bash
78
78
  # From source (no install) — quick one-liner (add --dry-run to preview)
79
- uv run python -m src.mcp_importer.quick_cli --yes
79
+ uv run python -m mcp_importer.quick_cli --yes
80
80
  ```
81
81
 
82
82
  <details>
@@ -139,29 +139,29 @@ OPEN_EDISON_CONFIG_DIR=~/edison-config open-edison run
139
139
 
140
140
  ```bash
141
141
  # From source (no install)
142
- uv run python -m src.mcp_importer.quick_cli --yes
142
+ uv run python -m mcp_importer.quick_cli --yes
143
143
  ```
144
144
 
145
145
  - Preview what will be imported (no writes):
146
146
 
147
147
  ```bash
148
- uv run python -m src.mcp_importer --source cursor --dry-run
148
+ uv run python -m mcp_importer --source cursor --dry-run
149
149
  ```
150
150
 
151
151
  - Import servers into Open Edison `config.json` (merge policy defaults to `skip`):
152
152
 
153
153
  ```bash
154
- uv run python -m src.mcp_importer --source cursor
155
- uv run python -m src.mcp_importer --source vscode
156
- uv run python -m src.mcp_importer --source claude-code
154
+ uv run python -m mcp_importer --source cursor
155
+ uv run python -m mcp_importer --source vscode
156
+ uv run python -m mcp_importer --source claude-code
157
157
  ```
158
158
 
159
159
  - Point your editor to Open Edison (backup original config and replace with a single Open Edison server):
160
160
 
161
161
  ```bash
162
- uv run python -m src.mcp_importer export --target cursor --yes
163
- uv run python -m src.mcp_importer export --target vscode --yes
164
- uv run python -m src.mcp_importer export --target claude-code --yes
162
+ uv run python -m mcp_importer export --target cursor --yes
163
+ uv run python -m mcp_importer export --target vscode --yes
164
+ uv run python -m mcp_importer export --target claude-code --yes
165
165
  ```
166
166
 
167
167
  </details>
@@ -52,7 +52,7 @@ Optionally, import your existing MCP configs from Cursor, VS Code, or Claude Cod
52
52
 
53
53
  ```bash
54
54
  # From source (no install) — quick one-liner (add --dry-run to preview)
55
- uv run python -m src.mcp_importer.quick_cli --yes
55
+ uv run python -m mcp_importer.quick_cli --yes
56
56
  ```
57
57
 
58
58
  <details>
@@ -115,29 +115,29 @@ OPEN_EDISON_CONFIG_DIR=~/edison-config open-edison run
115
115
 
116
116
  ```bash
117
117
  # From source (no install)
118
- uv run python -m src.mcp_importer.quick_cli --yes
118
+ uv run python -m mcp_importer.quick_cli --yes
119
119
  ```
120
120
 
121
121
  - Preview what will be imported (no writes):
122
122
 
123
123
  ```bash
124
- uv run python -m src.mcp_importer --source cursor --dry-run
124
+ uv run python -m mcp_importer --source cursor --dry-run
125
125
  ```
126
126
 
127
127
  - Import servers into Open Edison `config.json` (merge policy defaults to `skip`):
128
128
 
129
129
  ```bash
130
- uv run python -m src.mcp_importer --source cursor
131
- uv run python -m src.mcp_importer --source vscode
132
- uv run python -m src.mcp_importer --source claude-code
130
+ uv run python -m mcp_importer --source cursor
131
+ uv run python -m mcp_importer --source vscode
132
+ uv run python -m mcp_importer --source claude-code
133
133
  ```
134
134
 
135
135
  - Point your editor to Open Edison (backup original config and replace with a single Open Edison server):
136
136
 
137
137
  ```bash
138
- uv run python -m src.mcp_importer export --target cursor --yes
139
- uv run python -m src.mcp_importer export --target vscode --yes
140
- uv run python -m src.mcp_importer export --target claude-code --yes
138
+ uv run python -m mcp_importer export --target cursor --yes
139
+ uv run python -m mcp_importer export --target vscode --yes
140
+ uv run python -m mcp_importer export --target claude-code --yes
141
141
  ```
142
142
 
143
143
  </details>
@@ -38,10 +38,10 @@
38
38
  "command": "npx",
39
39
  "args": [
40
40
  "-y",
41
- "@github/mcp-server-github"
41
+ "@modelcontextprotocol/server-github"
42
42
  ],
43
43
  "env": {
44
- "GITHUB_TOKEN": "your-github-token-here"
44
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "your-github-token-here"
45
45
  },
46
46
  "enabled": false
47
47
  },
@@ -62,10 +62,11 @@
62
62
  "command": "npx",
63
63
  "args": [
64
64
  "-y",
65
- "@supabase/mcp-server-supabase@latest"
65
+ "@supabase/mcp-server"
66
66
  ],
67
67
  "env": {
68
- "SUPABASE_ACCESS_TOKEN": "your-supabase-access-token"
68
+ "SUPABASE_URL": "https://YOUR_PROJECT.supabase.co",
69
+ "SUPABASE_ANON_KEY": "your-supabase-anon-key"
69
70
  },
70
71
  "enabled": false
71
72
  },
@@ -80,40 +81,6 @@
80
81
  "env": {},
81
82
  "enabled": false
82
83
  },
83
- {
84
- "name": "figma",
85
- "command": "npx",
86
- "args": [
87
- "-y",
88
- "figma-developer-mcp",
89
- "--stdio"
90
- ],
91
- "env": {
92
- "FIGMA_API_KEY": "your-figma-api-key"
93
- },
94
- "enabled": false
95
- },
96
- {
97
- "name": "figma_dev_mode",
98
- "command": "npx",
99
- "args": [
100
- "-y",
101
- "mcp-remote",
102
- "http://127.0.0.1:3845/sse"
103
- ],
104
- "env": {},
105
- "enabled": false
106
- },
107
- {
108
- "name": "playwright",
109
- "command": "npx",
110
- "args": [
111
- "-y",
112
- "@automatalabs/mcp-server-playwright"
113
- ],
114
- "env": {},
115
- "enabled": false
116
- },
117
84
  {
118
85
  "name": "google_drive",
119
86
  "command": "npx",
@@ -1,4 +1,3 @@
1
- import os
2
1
  import shutil
3
2
  from pathlib import Path
4
3
 
@@ -21,14 +20,7 @@ class BuildHook(BuildHookInterface): # type: ignore
21
20
  src_frontend_dist = project_root / "src" / "frontend_dist"
22
21
  repo_frontend_dist = project_root / "frontend" / "dist"
23
22
 
24
- # Opt-in enforcement: only enforce when OPEN_EDISON_REQUIRE_FRONTEND=1/true/yes
25
- enforce = os.environ.get("OPEN_EDISON_REQUIRE_FRONTEND", "").lower() in {"1", "true", "yes"}
26
- if not enforce:
27
- self.app.display_info(
28
- "Skipping frontend_dist enforcement (set OPEN_EDISON_REQUIRE_FRONTEND=1 to enforce)"
29
- )
30
- return
31
-
23
+ # Always ensure frontend assets are available for packaging
32
24
  # Fast path: already present in src/
33
25
  if (src_frontend_dist / "index.html").exists():
34
26
  self.app.display_info("frontend_dist already present; skipping build/copy")
@@ -42,8 +34,21 @@ class BuildHook(BuildHookInterface): # type: ignore
42
34
  self.app.display_info("Copied frontend/dist -> src/frontend_dist for packaging")
43
35
  return
44
36
 
45
- # No assets available; fail fast with guidance
46
- raise RuntimeError(
47
- "Packaged dashboard (src/frontend_dist) missing and frontend/dist not found. "
48
- "Run 'make build_package' to generate assets before packaging/uvx."
49
- )
37
+ # If no frontend assets are available, create a minimal placeholder
38
+ # This prevents build failures while still allowing the package to be built
39
+ if not src_frontend_dist.exists():
40
+ src_frontend_dist.mkdir(parents=True, exist_ok=True)
41
+ # Create a minimal index.html placeholder
42
+ placeholder_html = """<!DOCTYPE html>
43
+ <html>
44
+ <head>
45
+ <title>Open Edison Dashboard</title>
46
+ <meta charset="utf-8">
47
+ </head>
48
+ <body>
49
+ <h1>Open Edison Dashboard</h1>
50
+ <p>Frontend assets not available. Run 'make build_package' to build the full dashboard.</p>
51
+ </body>
52
+ </html>"""
53
+ (src_frontend_dist / "index.html").write_text(placeholder_html)
54
+ self.app.display_info("Created minimal frontend placeholder for packaging")
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "open-edison"
3
- version = "0.1.40"
3
+ version = "0.1.42"
4
4
  description = "Open-source MCP security, aggregation, and monitoring. Single-user, self-hosted MCP proxy."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -82,7 +82,7 @@ include = [
82
82
  "src/frontend_dist/**",
83
83
  "docs/**",
84
84
  ]
85
- force-include = { "src/frontend_dist" = "src/frontend_dist" }
85
+
86
86
 
87
87
  [tool.hatch.build.targets.sdist.hooks.custom]
88
88
  path = "hatch_build.py"
@@ -0,0 +1,106 @@
1
+ # pyright: reportMissingImports=false, reportUnknownVariableType=false, reportUnknownMemberType=false, reportUnknownArgumentType=false, reportUnknownParameterType=false
2
+ from enum import Enum
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ from src.config import Config, MCPServerConfig, get_config_json_path
7
+ from src.mcp_importer import paths as _paths
8
+ from src.mcp_importer.exporters import export_to_claude_code, export_to_cursor, export_to_vscode
9
+ from src.mcp_importer.importers import (
10
+ import_from_claude_code as _import_from_claude_code,
11
+ )
12
+ from src.mcp_importer.importers import (
13
+ import_from_cursor as _import_from_cursor,
14
+ )
15
+ from src.mcp_importer.importers import (
16
+ import_from_vscode as _import_from_vscode,
17
+ )
18
+ from src.mcp_importer.merge import MergePolicy, merge_servers
19
+
20
+
21
+ class CLIENT(str, Enum):
22
+ CURSOR = "cursor"
23
+ VSCODE = "vscode"
24
+ CLAUDE_CODE = "claude-code"
25
+
26
+
27
+ def detect_clients() -> set[CLIENT]:
28
+ detected: set[CLIENT] = set()
29
+ if _paths.detect_cursor_config_path() is not None:
30
+ detected.add(CLIENT.CURSOR)
31
+ if _paths.detect_vscode_config_path() is not None:
32
+ detected.add(CLIENT.VSCODE)
33
+ if _paths.detect_claude_code_config_path() is not None:
34
+ detected.add(CLIENT.CLAUDE_CODE)
35
+ return detected
36
+
37
+
38
+ import_cursor = _import_from_cursor
39
+ import_vscode = _import_from_vscode
40
+ import_claude_code = _import_from_claude_code
41
+
42
+
43
+ def import_from(client: CLIENT) -> list[MCPServerConfig]:
44
+ if client == CLIENT.CURSOR:
45
+ return import_cursor()
46
+ if client == CLIENT.VSCODE:
47
+ return import_vscode()
48
+ if client == CLIENT.CLAUDE_CODE:
49
+ return import_claude_code()
50
+ raise ValueError(f"Unsupported client: {client}")
51
+
52
+
53
+ def save_imported_servers(
54
+ servers: list[MCPServerConfig],
55
+ *,
56
+ merge_policy: str = MergePolicy.SKIP,
57
+ config_dir: Path | None = None,
58
+ ) -> Path:
59
+ target_path: Path = (
60
+ get_config_json_path() if config_dir is None else (Path(config_dir) / "config.json")
61
+ )
62
+ cfg: Config = Config(target_path)
63
+ merged = merge_servers(existing=cfg.mcp_servers, imported=servers, policy=merge_policy)
64
+ cfg.mcp_servers = merged
65
+ cfg.save(target_path)
66
+ return target_path
67
+
68
+
69
+ def export_edison_to(
70
+ client: CLIENT,
71
+ *,
72
+ url: str = "http://localhost:3000/mcp/",
73
+ api_key: str = "dev-api-key-change-me",
74
+ server_name: str = "open-edison",
75
+ dry_run: bool = False,
76
+ force: bool = False,
77
+ create_if_missing: bool = False,
78
+ ) -> Any:
79
+ match client:
80
+ case CLIENT.CURSOR:
81
+ return export_to_cursor(
82
+ url=url,
83
+ api_key=api_key,
84
+ server_name=server_name,
85
+ dry_run=dry_run,
86
+ force=force,
87
+ create_if_missing=create_if_missing,
88
+ )
89
+ case CLIENT.VSCODE:
90
+ return export_to_vscode(
91
+ url=url,
92
+ api_key=api_key,
93
+ server_name=server_name,
94
+ dry_run=dry_run,
95
+ force=force,
96
+ create_if_missing=create_if_missing,
97
+ )
98
+ case CLIENT.CLAUDE_CODE:
99
+ return export_to_claude_code(
100
+ url=url,
101
+ api_key=api_key,
102
+ server_name=server_name,
103
+ dry_run=dry_run,
104
+ force=force,
105
+ create_if_missing=create_if_missing,
106
+ )
@@ -26,19 +26,10 @@ def run_cli(argv: list[str] | None = None) -> int:
26
26
  )
27
27
  parser.add_argument("--dry-run", action="store_true", help="Preview actions without writing")
28
28
  parser.add_argument("--yes", action="store_true", help="Skip confirmations (no effect here)")
29
- parser.add_argument(
30
- "--source-client", type=CLIENT, help="Client to import from", required=False, default=None
31
- )
32
29
  args = parser.parse_args(argv)
33
30
 
34
31
  detected = detect_clients()
35
- print(f"Detected clients: {detected}")
36
- if args.source_client:
37
- client = args.source_client
38
- assert client in detected, f"Client {client} not detected"
39
- else:
40
- client = _pick_first(detected)
41
- print(f"Going to import from client: {client}")
32
+ client = _pick_first(detected)
42
33
  if client is None:
43
34
  print("No supported clients detected.")
44
35
  return 2
@@ -48,8 +39,20 @@ def run_cli(argv: list[str] | None = None) -> int:
48
39
  print(f"No servers found to import from '{client.value}'.")
49
40
  return 0
50
41
 
51
- save_imported_servers(servers, dry_run=args.dry_run)
52
- export_edison_to(client, dry_run=args.dry_run, force=True, create_if_missing=True)
42
+ if args.dry_run:
43
+ print(
44
+ f"[dry-run] Would import {len(servers)} server(s) from '{client.value}' and save to config.json"
45
+ )
46
+ # Exercise export path safely (no writes)
47
+ export_edison_to(client, dry_run=True, force=True, create_if_missing=True)
48
+ print(
49
+ f"[dry-run] Would export Open Edison to '{client.value}' (backup and replace editor MCP config)"
50
+ )
51
+ print("Dry-run complete.")
52
+ return 0
53
+
54
+ save_imported_servers(servers)
55
+ export_edison_to(client, dry_run=False, force=True, create_if_missing=True)
53
56
  print(f"Completed quick import/export for {client.value}.")
54
57
  return 0
55
58
 
@@ -7,7 +7,6 @@ Provides detection, token management, and authentication flow coordination.
7
7
 
8
8
  import asyncio
9
9
  from dataclasses import dataclass
10
- from datetime import datetime, timedelta
11
10
  from enum import Enum
12
11
  from pathlib import Path
13
12
 
@@ -138,6 +137,7 @@ class OAuthManager:
138
137
  expires_in = getattr(existing_tokens, "expires_in", None)
139
138
  if expires_in:
140
139
  # If expires_in is available, we can calculate expiration
140
+ from datetime import datetime, timedelta
141
141
 
142
142
  expiry = datetime.now() + timedelta(seconds=expires_in)
143
143
  token_expires_at = expiry.isoformat()