open-edison 0.1.41__py3-none-any.whl → 0.1.42__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.
- frontend_dist/index.html +6 -16
- {open_edison-0.1.41.dist-info → open_edison-0.1.42.dist-info}/METADATA +10 -10
- {open_edison-0.1.41.dist-info → open_edison-0.1.42.dist-info}/RECORD +11 -17
- src/cli.py +8 -2
- src/frontend_dist/index.html +6 -16
- src/mcp_importer/api.py +18 -108
- src/mcp_importer/quick_cli.py +15 -12
- src/oauth_manager.py +1 -1
- frontend_dist/assets/index-BUUcUfTt.js +0 -51
- frontend_dist/assets/index-o6_8mdM8.css +0 -1
- frontend_dist/sw.js +0 -71
- src/frontend_dist/assets/index-BUUcUfTt.js +0 -51
- src/frontend_dist/assets/index-o6_8mdM8.css +0 -1
- src/frontend_dist/sw.js +0 -71
- {open_edison-0.1.41.dist-info → open_edison-0.1.42.dist-info}/WHEEL +0 -0
- {open_edison-0.1.41.dist-info → open_edison-0.1.42.dist-info}/entry_points.txt +0 -0
- {open_edison-0.1.41.dist-info → open_edison-0.1.42.dist-info}/licenses/LICENSE +0 -0
frontend_dist/index.html
CHANGED
@@ -1,21 +1,11 @@
|
|
1
|
-
<!
|
2
|
-
<html
|
3
|
-
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
4
3
|
<head>
|
5
|
-
<
|
6
|
-
<meta
|
7
|
-
<title>Open Edison Sessions</title>
|
8
|
-
<script>
|
9
|
-
// Prevents FOUC by setting theme asap
|
10
|
-
const prefersLight = window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches;
|
11
|
-
document.documentElement.setAttribute('data-theme', prefersLight ? 'light' : 'dark');
|
12
|
-
</script>
|
13
|
-
<script type="module" crossorigin src="/assets/index-BUUcUfTt.js"></script>
|
14
|
-
<link rel="stylesheet" crossorigin href="/assets/index-o6_8mdM8.css">
|
4
|
+
<title>Open Edison Dashboard</title>
|
5
|
+
<meta charset="utf-8">
|
15
6
|
</head>
|
16
|
-
|
17
7
|
<body>
|
18
|
-
<
|
8
|
+
<h1>Open Edison Dashboard</h1>
|
9
|
+
<p>Frontend assets not available. Run 'make build_package' to build the full dashboard.</p>
|
19
10
|
</body>
|
20
|
-
|
21
11
|
</html>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: open-edison
|
3
|
-
Version: 0.1.
|
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
|
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
|
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
|
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
|
155
|
-
uv run python -m
|
156
|
-
uv run python -m
|
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
|
163
|
-
uv run python -m
|
164
|
-
uv run python -m
|
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>
|
@@ -1,21 +1,18 @@
|
|
1
1
|
src/__init__.py,sha256=bEYMwBiuW9jzF07iWhas4Vb30EcpnqfpNfz_Q6yO1jU,209
|
2
2
|
src/__main__.py,sha256=kQsaVyzRa_ESC57JpKDSQJAHExuXme0rM5beJsYxFeA,161
|
3
|
-
src/cli.py,sha256=
|
3
|
+
src/cli.py,sha256=P38IWER41S5oAfbd_7p89hBpnjClsNHpmE5pSsJc6uU,9733
|
4
4
|
src/config.py,sha256=RSsAYzl8cj6eaDN1RORMcfKKWBcp4bKTQp2BdhAL9mg,10258
|
5
5
|
src/config.pyi,sha256=FgehEGli8ZXSjGlANBgMGv5497q4XskQciOc1fUcxqM,2033
|
6
6
|
src/events.py,sha256=aFQrVXDIZwt55Dz6OtyoXu2yi9evqo-8jZzo3CR2Tto,4965
|
7
|
-
src/oauth_manager.py,sha256=
|
7
|
+
src/oauth_manager.py,sha256=qcQa5BDRZr4bjqiXNflCnrXOh9mo9JVjvP2Caseg2Uc,9943
|
8
8
|
src/permissions.py,sha256=NGAnlG_z59HEiVA-k3cYvwmmiuHzxuNb5Tbd5umbL00,10483
|
9
9
|
src/server.py,sha256=cnO5bgxT-lrfuwk9AIvB_HBV8SWOtFClfGUn5_zFWyo,45652
|
10
10
|
src/single_user_mcp.py,sha256=rJrlqHcIubGkos_24ux5rb3OoKYDzvagCHghhfDeXTI,18535
|
11
11
|
src/telemetry.py,sha256=-RZPIjpI53zbsKmp-63REeZ1JirWHV5WvpSRa2nqZEk,11321
|
12
|
-
src/frontend_dist/index.html,sha256=
|
13
|
-
src/frontend_dist/sw.js,sha256=rihX1es-vWwjmtnXyaksJjs2dio6MVAOTAWwQPeJUYw,2164
|
14
|
-
src/frontend_dist/assets/index-BUUcUfTt.js,sha256=awoyPI6u0v6ao2iarZdSkrSDUvyU8aNkMLqHMvgVgyY,257666
|
15
|
-
src/frontend_dist/assets/index-o6_8mdM8.css,sha256=nwmX_6q55mB9463XN2JM8BdeihjkALpQK83Fc3_iGvE,15936
|
12
|
+
src/frontend_dist/index.html,sha256=rNNHhYAvyDujYAnwut4zIQfkSKEwES491uH4fEOjkIw,259
|
16
13
|
src/mcp_importer/__init__.py,sha256=Mk59pVr7OMGfYGWeSYk8-URfhIcrs3SPLYS7fmJbMII,275
|
17
14
|
src/mcp_importer/__main__.py,sha256=0jVfxKzyr6koVu1ghhWseah5ilKIoGovE6zkEZ-u-Og,515
|
18
|
-
src/mcp_importer/api.py,sha256=
|
15
|
+
src/mcp_importer/api.py,sha256=kYhmVHBUekTXPv3xNdt8rJNH1mbWXvs2cTZZGOkVhAk,3383
|
19
16
|
src/mcp_importer/cli.py,sha256=Pe0GLWm1nMd1VuNXOSkxIrFZuGNFc9dNvfBsvf-bdBI,3487
|
20
17
|
src/mcp_importer/export_cli.py,sha256=daEadB6nL8P4OpEGFx0GshuN1a091L7BhiitpV1bPqA,6294
|
21
18
|
src/mcp_importer/exporters.py,sha256=fSgl6seduoXFp7YnKH26UEaC1sFBnd4whSut7CJLBQs,11348
|
@@ -24,16 +21,13 @@ src/mcp_importer/importers.py,sha256=zGN8lT7qQJ95jDTd-ck09j_w5PSvH-uj33TILoHfHbs
|
|
24
21
|
src/mcp_importer/merge.py,sha256=KIGT7UgbAm07-LdyoUXEJ7ABSIiPTFlj_qjz669yFxg,1569
|
25
22
|
src/mcp_importer/parsers.py,sha256=JRE7y_Gg-QmlAARvZdrI9CmUyy-ODvDPbS695pb3Aw8,4856
|
26
23
|
src/mcp_importer/paths.py,sha256=4L-cPr7KCM9X9gAUP7Da6ictLNrPWuQ_IM419zqY-2I,2700
|
27
|
-
src/mcp_importer/quick_cli.py,sha256=
|
24
|
+
src/mcp_importer/quick_cli.py,sha256=24-R77-AuKsbeag0SyADba4Vf9oYHkaOBnRXK2sZt_4,1911
|
28
25
|
src/mcp_importer/types.py,sha256=h03TbAnJbap6OWWd0dT0QcFWNvSaiVFWH9V9PD6x4s0,138
|
29
26
|
src/middleware/data_access_tracker.py,sha256=bArBffWgYmvxOx9z_pgXQhogvnWQcc1m6WvEblDD4gw,15039
|
30
27
|
src/middleware/session_tracking.py,sha256=5W1VH9HNqIZeX0HNxDEm41U4GY6SqKSXtApDEeZK2qo,23084
|
31
|
-
frontend_dist/index.html,sha256=
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
open_edison-0.1.
|
36
|
-
open_edison-0.1.
|
37
|
-
open_edison-0.1.41.dist-info/entry_points.txt,sha256=YiGNm9x2I00hgT10HDyB4gxC1LcaV_mu8bXFjolu0Yw,171
|
38
|
-
open_edison-0.1.41.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
39
|
-
open_edison-0.1.41.dist-info/RECORD,,
|
28
|
+
frontend_dist/index.html,sha256=rNNHhYAvyDujYAnwut4zIQfkSKEwES491uH4fEOjkIw,259
|
29
|
+
open_edison-0.1.42.dist-info/METADATA,sha256=ycCiWJGiiCWyiam8Du-w4YqfUbrIhBPL4mu8HYcd8l4,13152
|
30
|
+
open_edison-0.1.42.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
31
|
+
open_edison-0.1.42.dist-info/entry_points.txt,sha256=YiGNm9x2I00hgT10HDyB4gxC1LcaV_mu8bXFjolu0Yw,171
|
32
|
+
open_edison-0.1.42.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
33
|
+
open_edison-0.1.42.dist-info/RECORD,,
|
src/cli.py
CHANGED
@@ -45,7 +45,7 @@ def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
|
45
45
|
# import-mcp: import MCP servers from other tools into config.json
|
46
46
|
sp_import = subparsers.add_parser(
|
47
47
|
"import-mcp",
|
48
|
-
help="Import MCP servers from other tools (Cursor,
|
48
|
+
help="Import MCP servers from other tools (Cursor, Windsurf, Cline, Claude Desktop, etc.)",
|
49
49
|
description=(
|
50
50
|
"Import MCP server configurations from other tools into Open Edison config.json.\n"
|
51
51
|
"Use --source to choose the tool and optional flags to control merging."
|
@@ -55,10 +55,16 @@ def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
|
55
55
|
"--source",
|
56
56
|
choices=[
|
57
57
|
"cursor",
|
58
|
+
"windsurf",
|
59
|
+
"cline",
|
60
|
+
"claude-desktop",
|
58
61
|
"vscode",
|
59
62
|
"claude-code",
|
63
|
+
"gemini-cli",
|
64
|
+
"codex",
|
65
|
+
"interactive",
|
60
66
|
],
|
61
|
-
default="
|
67
|
+
default="interactive",
|
62
68
|
help="Source application to import from",
|
63
69
|
)
|
64
70
|
sp_import.add_argument(
|
src/frontend_dist/index.html
CHANGED
@@ -1,21 +1,11 @@
|
|
1
|
-
<!
|
2
|
-
<html
|
3
|
-
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
4
3
|
<head>
|
5
|
-
<
|
6
|
-
<meta
|
7
|
-
<title>Open Edison Sessions</title>
|
8
|
-
<script>
|
9
|
-
// Prevents FOUC by setting theme asap
|
10
|
-
const prefersLight = window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches;
|
11
|
-
document.documentElement.setAttribute('data-theme', prefersLight ? 'light' : 'dark');
|
12
|
-
</script>
|
13
|
-
<script type="module" crossorigin src="/assets/index-BUUcUfTt.js"></script>
|
14
|
-
<link rel="stylesheet" crossorigin href="/assets/index-o6_8mdM8.css">
|
4
|
+
<title>Open Edison Dashboard</title>
|
5
|
+
<meta charset="utf-8">
|
15
6
|
</head>
|
16
|
-
|
17
7
|
<body>
|
18
|
-
<
|
8
|
+
<h1>Open Edison Dashboard</h1>
|
9
|
+
<p>Frontend assets not available. Run 'make build_package' to build the full dashboard.</p>
|
19
10
|
</body>
|
20
|
-
|
21
11
|
</html>
|
src/mcp_importer/api.py
CHANGED
@@ -1,35 +1,21 @@
|
|
1
1
|
# pyright: reportMissingImports=false, reportUnknownVariableType=false, reportUnknownMemberType=false, reportUnknownArgumentType=false, reportUnknownParameterType=false
|
2
|
-
import asyncio
|
3
|
-
from collections.abc import Awaitable
|
4
2
|
from enum import Enum
|
5
3
|
from pathlib import Path
|
6
|
-
from typing import Any
|
7
|
-
|
8
|
-
from fastmcp import FastMCP
|
4
|
+
from typing import Any
|
9
5
|
|
10
6
|
from src.config import Config, MCPServerConfig, get_config_json_path
|
11
7
|
from src.mcp_importer import paths as _paths
|
12
|
-
from src.mcp_importer.exporters import
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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,
|
17
14
|
)
|
18
15
|
from src.mcp_importer.importers import (
|
19
|
-
|
20
|
-
import_from_cursor,
|
21
|
-
import_from_vscode,
|
16
|
+
import_from_vscode as _import_from_vscode,
|
22
17
|
)
|
23
18
|
from src.mcp_importer.merge import MergePolicy, merge_servers
|
24
|
-
from src.oauth_manager import OAuthStatus, get_oauth_manager
|
25
|
-
|
26
|
-
|
27
|
-
@runtime_checkable
|
28
|
-
class _MCPClientLike(Protocol):
|
29
|
-
async def list_tools(self) -> Any: ...
|
30
|
-
async def list_resources(self) -> Any: ...
|
31
|
-
async def list_prompts(self) -> Any: ...
|
32
|
-
def shutdown(self) -> Awaitable[Any] | Any: ...
|
33
19
|
|
34
20
|
|
35
21
|
class CLIENT(str, Enum):
|
@@ -37,12 +23,6 @@ class CLIENT(str, Enum):
|
|
37
23
|
VSCODE = "vscode"
|
38
24
|
CLAUDE_CODE = "claude-code"
|
39
25
|
|
40
|
-
def __str__(self) -> str:
|
41
|
-
return self.value.capitalize()
|
42
|
-
|
43
|
-
def __repr__(self) -> str:
|
44
|
-
return str(self)
|
45
|
-
|
46
26
|
|
47
27
|
def detect_clients() -> set[CLIENT]:
|
48
28
|
detected: set[CLIENT] = set()
|
@@ -55,31 +35,30 @@ def detect_clients() -> set[CLIENT]:
|
|
55
35
|
return detected
|
56
36
|
|
57
37
|
|
38
|
+
import_cursor = _import_from_cursor
|
39
|
+
import_vscode = _import_from_vscode
|
40
|
+
import_claude_code = _import_from_claude_code
|
41
|
+
|
42
|
+
|
58
43
|
def import_from(client: CLIENT) -> list[MCPServerConfig]:
|
59
44
|
if client == CLIENT.CURSOR:
|
60
|
-
return
|
45
|
+
return import_cursor()
|
61
46
|
if client == CLIENT.VSCODE:
|
62
|
-
return
|
47
|
+
return import_vscode()
|
63
48
|
if client == CLIENT.CLAUDE_CODE:
|
64
|
-
return
|
49
|
+
return import_claude_code()
|
65
50
|
raise ValueError(f"Unsupported client: {client}")
|
66
51
|
|
67
52
|
|
68
53
|
def save_imported_servers(
|
69
54
|
servers: list[MCPServerConfig],
|
70
55
|
*,
|
71
|
-
dry_run: bool = False,
|
72
56
|
merge_policy: str = MergePolicy.SKIP,
|
73
57
|
config_dir: Path | None = None,
|
74
|
-
) -> Path
|
58
|
+
) -> Path:
|
75
59
|
target_path: Path = (
|
76
60
|
get_config_json_path() if config_dir is None else (Path(config_dir) / "config.json")
|
77
61
|
)
|
78
|
-
if dry_run:
|
79
|
-
print(
|
80
|
-
f"[dry-run] Would import {len(servers)} server(s) and save to config.json (at {target_path})"
|
81
|
-
)
|
82
|
-
return None
|
83
62
|
cfg: Config = Config(target_path)
|
84
63
|
merged = merge_servers(existing=cfg.mcp_servers, imported=servers, policy=merge_policy)
|
85
64
|
cfg.mcp_servers = merged
|
@@ -96,17 +75,7 @@ def export_edison_to(
|
|
96
75
|
dry_run: bool = False,
|
97
76
|
force: bool = False,
|
98
77
|
create_if_missing: bool = False,
|
99
|
-
) ->
|
100
|
-
if dry_run:
|
101
|
-
print(
|
102
|
-
f"[dry-run] Would export Open Edison to '{client}' (backup and replace editor MCP config)"
|
103
|
-
)
|
104
|
-
return ExportResult(
|
105
|
-
target_path=Path(""),
|
106
|
-
backup_path=None,
|
107
|
-
wrote_changes=False,
|
108
|
-
dry_run=True,
|
109
|
-
)
|
78
|
+
) -> Any:
|
110
79
|
match client:
|
111
80
|
case CLIENT.CURSOR:
|
112
81
|
return export_to_cursor(
|
@@ -135,62 +104,3 @@ def export_edison_to(
|
|
135
104
|
force=force,
|
136
105
|
create_if_missing=create_if_missing,
|
137
106
|
)
|
138
|
-
|
139
|
-
|
140
|
-
def verify_mcp_server(server: MCPServerConfig) -> bool: # noqa
|
141
|
-
"""Minimal validation: try listing tools/resources/prompts via FastMCP within a timeout."""
|
142
|
-
|
143
|
-
async def _verify_async() -> bool:
|
144
|
-
if not server.command.strip():
|
145
|
-
return False
|
146
|
-
|
147
|
-
backend_cfg: dict[str, Any] = {
|
148
|
-
"mcpServers": {
|
149
|
-
server.name: {
|
150
|
-
"command": server.command,
|
151
|
-
"args": server.args,
|
152
|
-
"env": server.env or {},
|
153
|
-
**({"roots": server.roots} if server.roots else {}),
|
154
|
-
}
|
155
|
-
}
|
156
|
-
}
|
157
|
-
|
158
|
-
proxy: FastMCP[Any] | None = None
|
159
|
-
try:
|
160
|
-
proxy = FastMCP.as_proxy(backend=backend_cfg, name=f"open-edison-verify-{server.name}")
|
161
|
-
s: _MCPClientLike = cast(_MCPClientLike, proxy)
|
162
|
-
await asyncio.wait_for(
|
163
|
-
asyncio.gather(
|
164
|
-
s.list_tools(),
|
165
|
-
s.list_resources(),
|
166
|
-
s.list_prompts(),
|
167
|
-
),
|
168
|
-
timeout=15.0,
|
169
|
-
)
|
170
|
-
return True
|
171
|
-
except Exception:
|
172
|
-
return False
|
173
|
-
finally:
|
174
|
-
try:
|
175
|
-
if isinstance(proxy, FastMCP):
|
176
|
-
result = proxy.shutdown() # type: ignore[attr-defined]
|
177
|
-
if isinstance(result, Awaitable):
|
178
|
-
await result # type: ignore[func-returns-value]
|
179
|
-
except Exception:
|
180
|
-
pass
|
181
|
-
|
182
|
-
return asyncio.run(_verify_async())
|
183
|
-
|
184
|
-
|
185
|
-
def server_needs_oauth(server: MCPServerConfig) -> bool: # noqa
|
186
|
-
"""Return True if the remote server currently needs OAuth; False otherwise."""
|
187
|
-
|
188
|
-
async def _needs_oauth_async() -> bool:
|
189
|
-
if not server.is_remote_server():
|
190
|
-
return False
|
191
|
-
info = await get_oauth_manager().check_oauth_requirement(
|
192
|
-
server.name, server.get_remote_url()
|
193
|
-
)
|
194
|
-
return info.status == OAuthStatus.NEEDS_AUTH
|
195
|
-
|
196
|
-
return asyncio.run(_needs_oauth_async())
|
src/mcp_importer/quick_cli.py
CHANGED
@@ -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
|
-
|
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
|
-
|
52
|
-
|
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
|
|
src/oauth_manager.py
CHANGED
@@ -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()
|