open-edison 0.1.38__tar.gz → 0.1.39__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 (54) hide show
  1. {open_edison-0.1.38 → open_edison-0.1.39}/PKG-INFO +1 -1
  2. {open_edison-0.1.38 → open_edison-0.1.39}/config.json +38 -5
  3. {open_edison-0.1.38 → open_edison-0.1.39}/pyproject.toml +1 -1
  4. {open_edison-0.1.38 → open_edison-0.1.39}/src/mcp_importer/api.py +86 -18
  5. {open_edison-0.1.38 → open_edison-0.1.39}/src/mcp_importer/quick_cli.py +12 -15
  6. {open_edison-0.1.38 → open_edison-0.1.39}/src/oauth_manager.py +1 -1
  7. {open_edison-0.1.38 → open_edison-0.1.39}/.gitignore +0 -0
  8. {open_edison-0.1.38 → open_edison-0.1.39}/LICENSE +0 -0
  9. {open_edison-0.1.38 → open_edison-0.1.39}/README.md +0 -0
  10. {open_edison-0.1.38 → open_edison-0.1.39}/desktop_ext/README.md +0 -0
  11. {open_edison-0.1.38 → open_edison-0.1.39}/docs/README.md +0 -0
  12. {open_edison-0.1.38 → open_edison-0.1.39}/docs/architecture/single_user_design.md +0 -0
  13. {open_edison-0.1.38 → open_edison-0.1.39}/docs/core/configuration.md +0 -0
  14. {open_edison-0.1.38 → open_edison-0.1.39}/docs/core/project_structure.md +0 -0
  15. {open_edison-0.1.38 → open_edison-0.1.39}/docs/core/proxy_usage.md +0 -0
  16. {open_edison-0.1.38 → open_edison-0.1.39}/docs/deployment/docker.md +0 -0
  17. {open_edison-0.1.38 → open_edison-0.1.39}/docs/deployment/local.md +0 -0
  18. {open_edison-0.1.38 → open_edison-0.1.39}/docs/development/contributing.md +0 -0
  19. {open_edison-0.1.38 → open_edison-0.1.39}/docs/development/development_guide.md +0 -0
  20. {open_edison-0.1.38 → open_edison-0.1.39}/docs/development/testing.md +0 -0
  21. {open_edison-0.1.38 → open_edison-0.1.39}/docs/quick-reference/api_reference.md +0 -0
  22. {open_edison-0.1.38 → open_edison-0.1.39}/docs/quick-reference/config_quick_start.md +0 -0
  23. {open_edison-0.1.38 → open_edison-0.1.39}/hatch_build.py +0 -0
  24. {open_edison-0.1.38 → open_edison-0.1.39}/installation_test/README.md +0 -0
  25. {open_edison-0.1.38 → open_edison-0.1.39}/prompt_permissions.json +0 -0
  26. {open_edison-0.1.38 → open_edison-0.1.39}/resource_permissions.json +0 -0
  27. {open_edison-0.1.38 → open_edison-0.1.39}/src/__init__.py +0 -0
  28. {open_edison-0.1.38 → open_edison-0.1.39}/src/__main__.py +0 -0
  29. {open_edison-0.1.38 → open_edison-0.1.39}/src/cli.py +0 -0
  30. {open_edison-0.1.38 → open_edison-0.1.39}/src/config.py +0 -0
  31. {open_edison-0.1.38 → open_edison-0.1.39}/src/config.pyi +0 -0
  32. {open_edison-0.1.38 → open_edison-0.1.39}/src/events.py +0 -0
  33. {open_edison-0.1.38 → open_edison-0.1.39}/src/frontend_dist/assets/index-BUUcUfTt.js +0 -0
  34. {open_edison-0.1.38 → open_edison-0.1.39}/src/frontend_dist/assets/index-o6_8mdM8.css +0 -0
  35. {open_edison-0.1.38 → open_edison-0.1.39}/src/frontend_dist/index.html +0 -0
  36. {open_edison-0.1.38 → open_edison-0.1.39}/src/frontend_dist/sw.js +0 -0
  37. {open_edison-0.1.38 → open_edison-0.1.39}/src/mcp_importer/__init__.py +0 -0
  38. {open_edison-0.1.38 → open_edison-0.1.39}/src/mcp_importer/__main__.py +0 -0
  39. {open_edison-0.1.38 → open_edison-0.1.39}/src/mcp_importer/cli.py +0 -0
  40. {open_edison-0.1.38 → open_edison-0.1.39}/src/mcp_importer/export_cli.py +0 -0
  41. {open_edison-0.1.38 → open_edison-0.1.39}/src/mcp_importer/exporters.py +0 -0
  42. {open_edison-0.1.38 → open_edison-0.1.39}/src/mcp_importer/import_api.py +0 -0
  43. {open_edison-0.1.38 → open_edison-0.1.39}/src/mcp_importer/importers.py +0 -0
  44. {open_edison-0.1.38 → open_edison-0.1.39}/src/mcp_importer/merge.py +0 -0
  45. {open_edison-0.1.38 → open_edison-0.1.39}/src/mcp_importer/parsers.py +0 -0
  46. {open_edison-0.1.38 → open_edison-0.1.39}/src/mcp_importer/paths.py +0 -0
  47. {open_edison-0.1.38 → open_edison-0.1.39}/src/mcp_importer/types.py +0 -0
  48. {open_edison-0.1.38 → open_edison-0.1.39}/src/middleware/data_access_tracker.py +0 -0
  49. {open_edison-0.1.38 → open_edison-0.1.39}/src/middleware/session_tracking.py +0 -0
  50. {open_edison-0.1.38 → open_edison-0.1.39}/src/permissions.py +0 -0
  51. {open_edison-0.1.38 → open_edison-0.1.39}/src/server.py +0 -0
  52. {open_edison-0.1.38 → open_edison-0.1.39}/src/single_user_mcp.py +0 -0
  53. {open_edison-0.1.38 → open_edison-0.1.39}/src/telemetry.py +0 -0
  54. {open_edison-0.1.38 → open_edison-0.1.39}/tool_permissions.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: open-edison
3
- Version: 0.1.38
3
+ Version: 0.1.39
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
@@ -38,10 +38,10 @@
38
38
  "command": "npx",
39
39
  "args": [
40
40
  "-y",
41
- "@modelcontextprotocol/server-github"
41
+ "@github/mcp-server-github"
42
42
  ],
43
43
  "env": {
44
- "GITHUB_PERSONAL_ACCESS_TOKEN": "your-github-token-here"
44
+ "GITHUB_TOKEN": "your-github-token-here"
45
45
  },
46
46
  "enabled": false
47
47
  },
@@ -62,11 +62,10 @@
62
62
  "command": "npx",
63
63
  "args": [
64
64
  "-y",
65
- "@supabase/mcp-server"
65
+ "@supabase/mcp-server-supabase@latest"
66
66
  ],
67
67
  "env": {
68
- "SUPABASE_URL": "https://YOUR_PROJECT.supabase.co",
69
- "SUPABASE_ANON_KEY": "your-supabase-anon-key"
68
+ "SUPABASE_ACCESS_TOKEN": "your-supabase-access-token"
70
69
  },
71
70
  "enabled": false
72
71
  },
@@ -81,6 +80,40 @@
81
80
  "env": {},
82
81
  "enabled": false
83
82
  },
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
+ },
84
117
  {
85
118
  "name": "google_drive",
86
119
  "command": "npx",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "open-edison"
3
- version = "0.1.38"
3
+ version = "0.1.39"
4
4
  description = "Open-source MCP security, aggregation, and monitoring. Single-user, self-hosted MCP proxy."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1,21 +1,18 @@
1
1
  # pyright: reportMissingImports=false, reportUnknownVariableType=false, reportUnknownMemberType=false, reportUnknownArgumentType=false, reportUnknownParameterType=false
2
+ import asyncio
3
+ from collections.abc import Awaitable
2
4
  from enum import Enum
3
5
  from pathlib import Path
4
6
  from typing import Any
5
7
 
8
+ from fastmcp import FastMCP
9
+
6
10
  import mcp_importer.paths as _paths
7
11
  from mcp_importer.exporters import export_to_claude_code, export_to_cursor, export_to_vscode
8
- from mcp_importer.importers import (
9
- import_from_claude_code as _import_from_claude_code,
10
- )
11
- from mcp_importer.importers import (
12
- import_from_cursor as _import_from_cursor,
13
- )
14
- from mcp_importer.importers import (
15
- import_from_vscode as _import_from_vscode,
16
- )
12
+ from mcp_importer.importers import import_from_claude_code, import_from_cursor, import_from_vscode
17
13
  from mcp_importer.merge import MergePolicy, merge_servers
18
14
  from src.config import Config, MCPServerConfig, get_config_json_path
15
+ from src.oauth_manager import OAuthStatus, get_oauth_manager
19
16
 
20
17
 
21
18
  class CLIENT(str, Enum):
@@ -23,6 +20,12 @@ class CLIENT(str, Enum):
23
20
  VSCODE = "vscode"
24
21
  CLAUDE_CODE = "claude-code"
25
22
 
23
+ def __str__(self) -> str:
24
+ return self.value.capitalize()
25
+
26
+ def __repr__(self) -> str:
27
+ return str(self)
28
+
26
29
 
27
30
  def detect_clients() -> set[CLIENT]:
28
31
  detected: set[CLIENT] = set()
@@ -35,30 +38,31 @@ def detect_clients() -> set[CLIENT]:
35
38
  return detected
36
39
 
37
40
 
38
- import_cursor = _import_from_cursor
39
- import_vscode = _import_from_vscode
40
- import_claude_code = _import_from_claude_code
41
-
42
-
43
41
  def import_from(client: CLIENT) -> list[MCPServerConfig]:
44
42
  if client == CLIENT.CURSOR:
45
- return import_cursor()
43
+ return import_from_cursor()
46
44
  if client == CLIENT.VSCODE:
47
- return import_vscode()
45
+ return import_from_vscode()
48
46
  if client == CLIENT.CLAUDE_CODE:
49
- return import_claude_code()
47
+ return import_from_claude_code()
50
48
  raise ValueError(f"Unsupported client: {client}")
51
49
 
52
50
 
53
51
  def save_imported_servers(
54
52
  servers: list[MCPServerConfig],
55
53
  *,
54
+ dry_run: bool = False,
56
55
  merge_policy: str = MergePolicy.SKIP,
57
56
  config_dir: Path | None = None,
58
- ) -> Path:
57
+ ) -> Path | None:
59
58
  target_path: Path = (
60
59
  get_config_json_path() if config_dir is None else (Path(config_dir) / "config.json")
61
60
  )
61
+ if dry_run:
62
+ print(
63
+ f"[dry-run] Would import {len(servers)} server(s) and save to config.json (at {target_path})"
64
+ )
65
+ return None
62
66
  cfg: Config = Config(target_path)
63
67
  merged = merge_servers(existing=cfg.mcp_servers, imported=servers, policy=merge_policy)
64
68
  cfg.mcp_servers = merged
@@ -76,6 +80,11 @@ def export_edison_to(
76
80
  force: bool = False,
77
81
  create_if_missing: bool = False,
78
82
  ) -> Any:
83
+ if dry_run:
84
+ print(
85
+ f"[dry-run] Would export Open Edison to '{client}' (backup and replace editor MCP config)"
86
+ )
87
+ return None
79
88
  match client:
80
89
  case CLIENT.CURSOR:
81
90
  return export_to_cursor(
@@ -104,3 +113,62 @@ def export_edison_to(
104
113
  force=force,
105
114
  create_if_missing=create_if_missing,
106
115
  )
116
+
117
+
118
+ def verify_mcp_server(server: MCPServerConfig) -> bool:
119
+ """Minimal validation: try listing tools/resources/prompts via FastMCP within a timeout."""
120
+
121
+ async def _verify_async() -> bool:
122
+ if not server.command.strip():
123
+ return False
124
+
125
+ backend_cfg: dict[str, Any] = {
126
+ "mcpServers": {
127
+ server.name: {
128
+ "command": server.command,
129
+ "args": server.args,
130
+ "env": server.env or {},
131
+ **({"roots": server.roots} if server.roots else {}),
132
+ }
133
+ }
134
+ }
135
+
136
+ proxy: FastMCP[Any] | None = None
137
+ try:
138
+ proxy = FastMCP.as_proxy(backend=backend_cfg, name=f"open-edison-verify-{server.name}")
139
+ s: Any = proxy
140
+ await asyncio.wait_for(
141
+ asyncio.gather(
142
+ s.list_tools(),
143
+ s.list_resources(),
144
+ s.list_prompts(),
145
+ ),
146
+ timeout=15.0,
147
+ )
148
+ return True
149
+ except Exception:
150
+ return False
151
+ finally:
152
+ try:
153
+ if isinstance(proxy, FastMCP):
154
+ result = proxy.shutdown() # type: ignore[attr-defined]
155
+ if isinstance(result, Awaitable):
156
+ await result # type: ignore[func-returns-value]
157
+ except Exception:
158
+ pass
159
+
160
+ return asyncio.run(_verify_async())
161
+
162
+
163
+ def server_needs_oauth(server: MCPServerConfig) -> bool:
164
+ """Return True if the remote server currently needs OAuth; False otherwise."""
165
+
166
+ async def _needs_oauth_async() -> bool:
167
+ if not server.is_remote_server():
168
+ return False
169
+ info = await get_oauth_manager().check_oauth_requirement(
170
+ server.name, server.get_remote_url()
171
+ )
172
+ return info.status == OAuthStatus.NEEDS_AUTH
173
+
174
+ return asyncio.run(_needs_oauth_async())
@@ -26,10 +26,19 @@ 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
+ )
29
32
  args = parser.parse_args(argv)
30
33
 
31
34
  detected = detect_clients()
32
- client = _pick_first(detected)
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}")
33
42
  if client is None:
34
43
  print("No supported clients detected.")
35
44
  return 2
@@ -39,20 +48,8 @@ def run_cli(argv: list[str] | None = None) -> int:
39
48
  print(f"No servers found to import from '{client.value}'.")
40
49
  return 0
41
50
 
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)
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)
56
53
  print(f"Completed quick import/export for {client.value}.")
57
54
  return 0
58
55
 
@@ -7,6 +7,7 @@ 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
10
11
  from enum import Enum
11
12
  from pathlib import Path
12
13
 
@@ -137,7 +138,6 @@ class OAuthManager:
137
138
  expires_in = getattr(existing_tokens, "expires_in", None)
138
139
  if expires_in:
139
140
  # 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()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes