open-edison 0.1.37__py3-none-any.whl → 0.1.39__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.
- src/oauth_manager.py → oauth_manager.py +1 -1
- {open_edison-0.1.37.dist-info → open_edison-0.1.39.dist-info}/METADATA +1 -2
- open_edison-0.1.39.dist-info/RECORD +22 -0
- open_edison-0.1.37.dist-info/RECORD +0 -35
- src/mcp_importer/__init__.py +0 -15
- src/mcp_importer/__main__.py +0 -19
- src/mcp_importer/api.py +0 -106
- src/mcp_importer/cli.py +0 -113
- src/mcp_importer/export_cli.py +0 -201
- src/mcp_importer/exporters.py +0 -393
- src/mcp_importer/import_api.py +0 -3
- src/mcp_importer/importers.py +0 -63
- src/mcp_importer/merge.py +0 -47
- src/mcp_importer/parsers.py +0 -148
- src/mcp_importer/paths.py +0 -92
- src/mcp_importer/quick_cli.py +0 -65
- src/mcp_importer/types.py +0 -5
- /src/__init__.py → /__init__.py +0 -0
- /src/__main__.py → /__main__.py +0 -0
- /src/cli.py → /cli.py +0 -0
- /src/config.py → /config.py +0 -0
- /src/config.pyi → /config.pyi +0 -0
- /src/events.py → /events.py +0 -0
- {src/frontend_dist → frontend_dist}/assets/index-BUUcUfTt.js +0 -0
- {src/frontend_dist → frontend_dist}/assets/index-o6_8mdM8.css +0 -0
- {src/frontend_dist → frontend_dist}/index.html +0 -0
- {src/frontend_dist → frontend_dist}/sw.js +0 -0
- {src/middleware → middleware}/data_access_tracker.py +0 -0
- {src/middleware → middleware}/session_tracking.py +0 -0
- {open_edison-0.1.37.dist-info → open_edison-0.1.39.dist-info}/WHEEL +0 -0
- {open_edison-0.1.37.dist-info → open_edison-0.1.39.dist-info}/entry_points.txt +0 -0
- {open_edison-0.1.37.dist-info → open_edison-0.1.39.dist-info}/licenses/LICENSE +0 -0
- /src/permissions.py → /permissions.py +0 -0
- /src/server.py → /server.py +0 -0
- /src/single_user_mcp.py → /single_user_mcp.py +0 -0
- /src/telemetry.py → /telemetry.py +0 -0
@@ -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()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: open-edison
|
3
|
-
Version: 0.1.
|
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
|
@@ -140,7 +140,6 @@ OPEN_EDISON_CONFIG_DIR=~/edison-config open-edison run
|
|
140
140
|
```bash
|
141
141
|
# From source (no install)
|
142
142
|
uv run python -m mcp_importer.quick_cli --yes
|
143
|
-
# After install: mcp-importer-quick --yes
|
144
143
|
```
|
145
144
|
|
146
145
|
- Preview what will be imported (no writes):
|
@@ -0,0 +1,22 @@
|
|
1
|
+
__init__.py,sha256=bEYMwBiuW9jzF07iWhas4Vb30EcpnqfpNfz_Q6yO1jU,209
|
2
|
+
__main__.py,sha256=kQsaVyzRa_ESC57JpKDSQJAHExuXme0rM5beJsYxFeA,161
|
3
|
+
cli.py,sha256=P38IWER41S5oAfbd_7p89hBpnjClsNHpmE5pSsJc6uU,9733
|
4
|
+
config.py,sha256=RSsAYzl8cj6eaDN1RORMcfKKWBcp4bKTQp2BdhAL9mg,10258
|
5
|
+
config.pyi,sha256=FgehEGli8ZXSjGlANBgMGv5497q4XskQciOc1fUcxqM,2033
|
6
|
+
events.py,sha256=aFQrVXDIZwt55Dz6OtyoXu2yi9evqo-8jZzo3CR2Tto,4965
|
7
|
+
oauth_manager.py,sha256=W9QSo0vfGDQ_i-QWCngkv7YLSL3Rk5jfPmqjU1J2rnU,9911
|
8
|
+
permissions.py,sha256=NGAnlG_z59HEiVA-k3cYvwmmiuHzxuNb5Tbd5umbL00,10483
|
9
|
+
server.py,sha256=cnO5bgxT-lrfuwk9AIvB_HBV8SWOtFClfGUn5_zFWyo,45652
|
10
|
+
single_user_mcp.py,sha256=rJrlqHcIubGkos_24ux5rb3OoKYDzvagCHghhfDeXTI,18535
|
11
|
+
telemetry.py,sha256=-RZPIjpI53zbsKmp-63REeZ1JirWHV5WvpSRa2nqZEk,11321
|
12
|
+
frontend_dist/index.html,sha256=s95FMkH8VLisvawLH7bZxbLzRUFvMhHkH6ZMzpVBngs,673
|
13
|
+
frontend_dist/sw.js,sha256=rihX1es-vWwjmtnXyaksJjs2dio6MVAOTAWwQPeJUYw,2164
|
14
|
+
frontend_dist/assets/index-BUUcUfTt.js,sha256=awoyPI6u0v6ao2iarZdSkrSDUvyU8aNkMLqHMvgVgyY,257666
|
15
|
+
frontend_dist/assets/index-o6_8mdM8.css,sha256=nwmX_6q55mB9463XN2JM8BdeihjkALpQK83Fc3_iGvE,15936
|
16
|
+
middleware/data_access_tracker.py,sha256=bArBffWgYmvxOx9z_pgXQhogvnWQcc1m6WvEblDD4gw,15039
|
17
|
+
middleware/session_tracking.py,sha256=5W1VH9HNqIZeX0HNxDEm41U4GY6SqKSXtApDEeZK2qo,23084
|
18
|
+
open_edison-0.1.39.dist-info/METADATA,sha256=xCXSMsOP0Tn_T1x3QzU4YVgOodONGsr4U1JyCE7F6FI,13152
|
19
|
+
open_edison-0.1.39.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
20
|
+
open_edison-0.1.39.dist-info/entry_points.txt,sha256=qUjYGPEfqSQyra9dTe1aRvHVAAzLzoNvZqNDk1t75IA,163
|
21
|
+
open_edison-0.1.39.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
22
|
+
open_edison-0.1.39.dist-info/RECORD,,
|
@@ -1,35 +0,0 @@
|
|
1
|
-
src/__init__.py,sha256=bEYMwBiuW9jzF07iWhas4Vb30EcpnqfpNfz_Q6yO1jU,209
|
2
|
-
src/__main__.py,sha256=kQsaVyzRa_ESC57JpKDSQJAHExuXme0rM5beJsYxFeA,161
|
3
|
-
src/cli.py,sha256=P38IWER41S5oAfbd_7p89hBpnjClsNHpmE5pSsJc6uU,9733
|
4
|
-
src/config.py,sha256=RSsAYzl8cj6eaDN1RORMcfKKWBcp4bKTQp2BdhAL9mg,10258
|
5
|
-
src/config.pyi,sha256=FgehEGli8ZXSjGlANBgMGv5497q4XskQciOc1fUcxqM,2033
|
6
|
-
src/events.py,sha256=aFQrVXDIZwt55Dz6OtyoXu2yi9evqo-8jZzo3CR2Tto,4965
|
7
|
-
src/oauth_manager.py,sha256=qcQa5BDRZr4bjqiXNflCnrXOh9mo9JVjvP2Caseg2Uc,9943
|
8
|
-
src/permissions.py,sha256=NGAnlG_z59HEiVA-k3cYvwmmiuHzxuNb5Tbd5umbL00,10483
|
9
|
-
src/server.py,sha256=cnO5bgxT-lrfuwk9AIvB_HBV8SWOtFClfGUn5_zFWyo,45652
|
10
|
-
src/single_user_mcp.py,sha256=rJrlqHcIubGkos_24ux5rb3OoKYDzvagCHghhfDeXTI,18535
|
11
|
-
src/telemetry.py,sha256=-RZPIjpI53zbsKmp-63REeZ1JirWHV5WvpSRa2nqZEk,11321
|
12
|
-
src/frontend_dist/index.html,sha256=s95FMkH8VLisvawLH7bZxbLzRUFvMhHkH6ZMzpVBngs,673
|
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
|
16
|
-
src/mcp_importer/__init__.py,sha256=Yqr4NVAbKRVIuDzOj-yXzyB8HWLl-I4KmP5pVIRxs1o,271
|
17
|
-
src/mcp_importer/__main__.py,sha256=_2LUxAFFGJ9ECg5OoUqUZMxE6QBzhYPrfpLfKvCQM7k,507
|
18
|
-
src/mcp_importer/api.py,sha256=8BwPCeve-rY6T9Xhn-9FptBR2K_v_UBxe7m6CaOUMnw,3354
|
19
|
-
src/mcp_importer/cli.py,sha256=Pe0GLWm1nMd1VuNXOSkxIrFZuGNFc9dNvfBsvf-bdBI,3487
|
20
|
-
src/mcp_importer/export_cli.py,sha256=daEadB6nL8P4OpEGFx0GshuN1a091L7BhiitpV1bPqA,6294
|
21
|
-
src/mcp_importer/exporters.py,sha256=fSgl6seduoXFp7YnKH26UEaC1sFBnd4whSut7CJLBQs,11348
|
22
|
-
src/mcp_importer/import_api.py,sha256=xWaKoE3vibSWpA5roVL7qEMS73vcmAC0tcHP6CsZw6E,95
|
23
|
-
src/mcp_importer/importers.py,sha256=zGN8lT7qQJ95jDTd-ck09j_w5PSvH-uj33TILoHfHbs,2191
|
24
|
-
src/mcp_importer/merge.py,sha256=KIGT7UgbAm07-LdyoUXEJ7ABSIiPTFlj_qjz669yFxg,1569
|
25
|
-
src/mcp_importer/parsers.py,sha256=JRE7y_Gg-QmlAARvZdrI9CmUyy-ODvDPbS695pb3Aw8,4856
|
26
|
-
src/mcp_importer/paths.py,sha256=4L-cPr7KCM9X9gAUP7Da6ictLNrPWuQ_IM419zqY-2I,2700
|
27
|
-
src/mcp_importer/quick_cli.py,sha256=MCkHr_ljyUPS0pzTwJf4bW-UpknQP8NPzIWMxjUu5Nc,1907
|
28
|
-
src/mcp_importer/types.py,sha256=h03TbAnJbap6OWWd0dT0QcFWNvSaiVFWH9V9PD6x4s0,138
|
29
|
-
src/middleware/data_access_tracker.py,sha256=bArBffWgYmvxOx9z_pgXQhogvnWQcc1m6WvEblDD4gw,15039
|
30
|
-
src/middleware/session_tracking.py,sha256=5W1VH9HNqIZeX0HNxDEm41U4GY6SqKSXtApDEeZK2qo,23084
|
31
|
-
open_edison-0.1.37.dist-info/METADATA,sha256=41uTPLASX7MIufzvxIz_MQ7zAVkTfmNrrmz3afWqAu4,13198
|
32
|
-
open_edison-0.1.37.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
33
|
-
open_edison-0.1.37.dist-info/entry_points.txt,sha256=qUjYGPEfqSQyra9dTe1aRvHVAAzLzoNvZqNDk1t75IA,163
|
34
|
-
open_edison-0.1.37.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
35
|
-
open_edison-0.1.37.dist-info/RECORD,,
|
src/mcp_importer/__init__.py
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
"""MCP importer package for Open Edison scripts.
|
2
|
-
|
3
|
-
Import submodules explicitly as needed, e.g. `from mcp_importer import cli`.
|
4
|
-
"""
|
5
|
-
|
6
|
-
# pyright: reportUnsupportedDunderAll=false
|
7
|
-
|
8
|
-
__all__ = [
|
9
|
-
"paths",
|
10
|
-
"parsers",
|
11
|
-
"importers",
|
12
|
-
"merge",
|
13
|
-
"cli",
|
14
|
-
"api",
|
15
|
-
]
|
src/mcp_importer/__main__.py
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import sys
|
4
|
-
|
5
|
-
from mcp_importer.cli import run_cli as import_run_cli
|
6
|
-
from mcp_importer.export_cli import run_cli as export_run_cli
|
7
|
-
|
8
|
-
|
9
|
-
def main() -> int:
|
10
|
-
# Usage:
|
11
|
-
# python -m mcp_importer -> import CLI
|
12
|
-
# python -m mcp_importer export ... -> export CLI
|
13
|
-
if len(sys.argv) > 1 and sys.argv[1] == "export":
|
14
|
-
return export_run_cli(sys.argv[2:])
|
15
|
-
return import_run_cli(sys.argv[1:])
|
16
|
-
|
17
|
-
|
18
|
-
if __name__ == "__main__":
|
19
|
-
raise SystemExit(main())
|
src/mcp_importer/api.py
DELETED
@@ -1,106 +0,0 @@
|
|
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
|
-
import mcp_importer.paths as _paths
|
7
|
-
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
|
-
)
|
17
|
-
from mcp_importer.merge import MergePolicy, merge_servers
|
18
|
-
from src.config import Config, MCPServerConfig, get_config_json_path
|
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
|
-
)
|
src/mcp_importer/cli.py
DELETED
@@ -1,113 +0,0 @@
|
|
1
|
-
# pyright: reportMissingImports=false, reportUnknownVariableType=false, reportUnknownArgumentType=false, reportUnknownMemberType=false, reportUnknownParameterType=false
|
2
|
-
import argparse
|
3
|
-
import sys
|
4
|
-
from pathlib import Path
|
5
|
-
|
6
|
-
from loguru import logger as log
|
7
|
-
|
8
|
-
from src.config import Config, get_config_dir
|
9
|
-
|
10
|
-
from .importers import IMPORTERS
|
11
|
-
from .merge import MergePolicy, merge_servers
|
12
|
-
|
13
|
-
## Ensure import of src config (place src on sys.path before import)
|
14
|
-
# THIS_FILE = Path(__file__).resolve()
|
15
|
-
# REPO_ROOT = THIS_FILE.parents[2]
|
16
|
-
# SRC_DIR = REPO_ROOT / "src"
|
17
|
-
# if str(SRC_DIR) not in sys.path:
|
18
|
-
# sys.path.insert(0, str(SRC_DIR))
|
19
|
-
|
20
|
-
|
21
|
-
def build_arg_parser() -> argparse.ArgumentParser:
|
22
|
-
p = argparse.ArgumentParser(
|
23
|
-
description="Import MCP servers from other tools into Open Edison config.json"
|
24
|
-
)
|
25
|
-
p.add_argument(
|
26
|
-
"--source",
|
27
|
-
choices=["cursor", "vscode", "claude-code"],
|
28
|
-
required=True,
|
29
|
-
)
|
30
|
-
p.add_argument(
|
31
|
-
"--config-dir",
|
32
|
-
type=Path,
|
33
|
-
help="Directory containing target config.json (default: OPEN_EDISON_CONFIG_DIR or repo root)",
|
34
|
-
)
|
35
|
-
p.add_argument(
|
36
|
-
"--merge",
|
37
|
-
choices=[MergePolicy.SKIP, MergePolicy.OVERWRITE, MergePolicy.RENAME],
|
38
|
-
default=MergePolicy.SKIP,
|
39
|
-
)
|
40
|
-
p.add_argument(
|
41
|
-
"--dry-run", action="store_true", help="Show changes without writing to config.json"
|
42
|
-
)
|
43
|
-
return p
|
44
|
-
|
45
|
-
|
46
|
-
def run_cli(argv: list[str] | None = None) -> int: # noqa: C901
|
47
|
-
parser = build_arg_parser()
|
48
|
-
args = parser.parse_args(argv)
|
49
|
-
|
50
|
-
source: str = args.source
|
51
|
-
|
52
|
-
importer = IMPORTERS.get(source)
|
53
|
-
if not importer:
|
54
|
-
print(f"Unsupported source: {source}", file=sys.stderr)
|
55
|
-
return 2
|
56
|
-
|
57
|
-
# Resolve target config path
|
58
|
-
target_dir: Path = args.config_dir or get_config_dir()
|
59
|
-
target_path = target_dir / "config.json"
|
60
|
-
|
61
|
-
# Load existing config (auto-creates default if missing via Config.load)
|
62
|
-
config_obj: Config = Config(target_dir)
|
63
|
-
|
64
|
-
# Import
|
65
|
-
imported_servers = importer()
|
66
|
-
|
67
|
-
if not imported_servers:
|
68
|
-
log.warning("No servers found to import from source '{}'", source)
|
69
|
-
return 0
|
70
|
-
|
71
|
-
# Merge
|
72
|
-
merged = merge_servers(
|
73
|
-
existing=config_obj.mcp_servers,
|
74
|
-
imported=imported_servers,
|
75
|
-
policy=args.merge,
|
76
|
-
)
|
77
|
-
|
78
|
-
existing_names: set[str] = {str(getattr(s, "name", "")) for s in config_obj.mcp_servers}
|
79
|
-
merged_names: set[str] = {str(getattr(s, "name", "")) for s in merged}
|
80
|
-
added = merged_names - existing_names
|
81
|
-
replaced: set[str] = set()
|
82
|
-
if args.merge == MergePolicy.OVERWRITE:
|
83
|
-
replaced = existing_names & {s.name for s in imported_servers}
|
84
|
-
|
85
|
-
log.info("Imported {} server(s) from '{}'", len(imported_servers), source)
|
86
|
-
try:
|
87
|
-
names_preview = ", ".join(sorted(getattr(s, "name", "") for s in imported_servers))
|
88
|
-
if names_preview:
|
89
|
-
log.info("Detected servers: {}", names_preview)
|
90
|
-
except Exception:
|
91
|
-
pass
|
92
|
-
if added:
|
93
|
-
log.info("Added: {}", ", ".join(sorted(added)))
|
94
|
-
if replaced:
|
95
|
-
log.info("Overwrote: {}", ", ".join(sorted(replaced)))
|
96
|
-
|
97
|
-
if args.dry_run:
|
98
|
-
log.info("Dry-run enabled; not writing changes to {}", target_path)
|
99
|
-
log.debug("Merged servers: {}", merged)
|
100
|
-
return 0
|
101
|
-
|
102
|
-
config_obj.mcp_servers = merged
|
103
|
-
config_obj.save(target_path)
|
104
|
-
log.info("Configuration updated: {}", target_path)
|
105
|
-
return 0
|
106
|
-
|
107
|
-
|
108
|
-
def main() -> int:
|
109
|
-
return run_cli()
|
110
|
-
|
111
|
-
|
112
|
-
if __name__ == "__main__":
|
113
|
-
raise SystemExit(main())
|
src/mcp_importer/export_cli.py
DELETED
@@ -1,201 +0,0 @@
|
|
1
|
-
import argparse
|
2
|
-
from pathlib import Path
|
3
|
-
|
4
|
-
from loguru import logger as log
|
5
|
-
|
6
|
-
from .exporters import ExportError, export_to_claude_code, export_to_cursor, export_to_vscode
|
7
|
-
from .paths import (
|
8
|
-
detect_cursor_config_path,
|
9
|
-
detect_vscode_config_path,
|
10
|
-
get_default_cursor_config_path,
|
11
|
-
get_default_vscode_config_path,
|
12
|
-
)
|
13
|
-
|
14
|
-
|
15
|
-
def _prompt_yes_no(message: str, *, default_no: bool = True) -> bool:
|
16
|
-
suffix = "[y/N]" if default_no else "[Y/n]"
|
17
|
-
while True:
|
18
|
-
resp = input(f"{message} {suffix} ").strip().lower()
|
19
|
-
if resp == "y" or resp == "yes":
|
20
|
-
return True
|
21
|
-
if resp == "n" or resp == "no":
|
22
|
-
return False
|
23
|
-
if resp == "" and default_no:
|
24
|
-
return False
|
25
|
-
if resp == "" and not default_no:
|
26
|
-
return True
|
27
|
-
|
28
|
-
|
29
|
-
def build_arg_parser() -> argparse.ArgumentParser:
|
30
|
-
p = argparse.ArgumentParser(
|
31
|
-
description="Export editor MCP config to use Open Edison (Cursor support)",
|
32
|
-
)
|
33
|
-
p.add_argument("--target", choices=["cursor", "vscode", "claude-code"], default="cursor")
|
34
|
-
p.add_argument("--dry-run", action="store_true", help="Show actions without writing")
|
35
|
-
p.add_argument("--force", action="store_true", help="Rewrite even if already configured")
|
36
|
-
p.add_argument(
|
37
|
-
"--yes",
|
38
|
-
action="store_true",
|
39
|
-
help="Automatic yes to prompts (create missing files without confirmation)",
|
40
|
-
)
|
41
|
-
p.add_argument("--url", default="http://localhost:3000/mcp/", help="MCP URL")
|
42
|
-
p.add_argument(
|
43
|
-
"--api-key",
|
44
|
-
default="dev-api-key-change-me",
|
45
|
-
help="API key for Authorization header",
|
46
|
-
)
|
47
|
-
p.add_argument("--name", default="open-edison", help="Name of the server entry")
|
48
|
-
return p
|
49
|
-
|
50
|
-
|
51
|
-
def _handle_cursor(args: argparse.Namespace) -> int:
|
52
|
-
detected = detect_cursor_config_path()
|
53
|
-
target_path: Path = detected if detected else get_default_cursor_config_path()
|
54
|
-
|
55
|
-
create_if_missing = False
|
56
|
-
if not target_path.exists():
|
57
|
-
if args.yes:
|
58
|
-
create_if_missing = True
|
59
|
-
else:
|
60
|
-
confirmed = _prompt_yes_no(
|
61
|
-
f"Cursor config not found at {target_path}. Create it?", default_no=False
|
62
|
-
)
|
63
|
-
if not confirmed:
|
64
|
-
log.info("Aborted: user declined to create missing file")
|
65
|
-
return 0
|
66
|
-
create_if_missing = True
|
67
|
-
|
68
|
-
try:
|
69
|
-
result = export_to_cursor(
|
70
|
-
url=args.url,
|
71
|
-
api_key=args.api_key,
|
72
|
-
server_name=args.name,
|
73
|
-
dry_run=args.dry_run,
|
74
|
-
force=args.force,
|
75
|
-
create_if_missing=create_if_missing,
|
76
|
-
)
|
77
|
-
except ExportError as e:
|
78
|
-
log.error(str(e))
|
79
|
-
return 1
|
80
|
-
|
81
|
-
if result.dry_run:
|
82
|
-
log.info("Dry-run complete. No changes written.")
|
83
|
-
return 0
|
84
|
-
|
85
|
-
if result.wrote_changes:
|
86
|
-
if result.backup_path is not None:
|
87
|
-
log.info("Backup created at {}", result.backup_path)
|
88
|
-
log.info("Updated {}", result.target_path)
|
89
|
-
else:
|
90
|
-
log.info("No changes were necessary.")
|
91
|
-
return 0
|
92
|
-
|
93
|
-
|
94
|
-
def _handle_vscode(args: argparse.Namespace) -> int:
|
95
|
-
detected = detect_vscode_config_path()
|
96
|
-
target_path: Path = detected if detected else get_default_vscode_config_path()
|
97
|
-
|
98
|
-
create_if_missing = False
|
99
|
-
if not target_path.exists():
|
100
|
-
if args.yes:
|
101
|
-
create_if_missing = True
|
102
|
-
else:
|
103
|
-
confirmed = _prompt_yes_no(
|
104
|
-
f"VS Code MCP config not found at {target_path}. Create it?", default_no=False
|
105
|
-
)
|
106
|
-
if not confirmed:
|
107
|
-
log.info("Aborted: user declined to create missing file")
|
108
|
-
return 0
|
109
|
-
create_if_missing = True
|
110
|
-
|
111
|
-
try:
|
112
|
-
result = export_to_vscode(
|
113
|
-
url=args.url,
|
114
|
-
api_key=args.api_key,
|
115
|
-
server_name=args.name,
|
116
|
-
dry_run=args.dry_run,
|
117
|
-
force=args.force,
|
118
|
-
create_if_missing=create_if_missing,
|
119
|
-
)
|
120
|
-
except ExportError as e:
|
121
|
-
log.error(str(e))
|
122
|
-
return 1
|
123
|
-
|
124
|
-
if result.dry_run:
|
125
|
-
log.info("Dry-run complete. No changes written.")
|
126
|
-
return 0
|
127
|
-
|
128
|
-
if result.wrote_changes:
|
129
|
-
if result.backup_path is not None:
|
130
|
-
log.info("Backup created at {}", result.backup_path)
|
131
|
-
log.info("Updated {}", result.target_path)
|
132
|
-
else:
|
133
|
-
log.info("No changes were necessary.")
|
134
|
-
return 0
|
135
|
-
|
136
|
-
|
137
|
-
def _handle_claude_code(args: argparse.Namespace) -> int:
|
138
|
-
from .paths import detect_claude_code_config_path, get_default_claude_code_config_path
|
139
|
-
|
140
|
-
detected = detect_claude_code_config_path()
|
141
|
-
target_path: Path = detected if detected else get_default_claude_code_config_path()
|
142
|
-
|
143
|
-
create_if_missing = False
|
144
|
-
if not target_path.exists():
|
145
|
-
if args.yes:
|
146
|
-
create_if_missing = True
|
147
|
-
else:
|
148
|
-
confirmed = _prompt_yes_no(
|
149
|
-
f"Claude Code config not found at {target_path}. Create it?", default_no=False
|
150
|
-
)
|
151
|
-
if not confirmed:
|
152
|
-
log.info("Aborted: user declined to create missing file")
|
153
|
-
return 0
|
154
|
-
create_if_missing = True
|
155
|
-
|
156
|
-
try:
|
157
|
-
result = export_to_claude_code(
|
158
|
-
url=args.url,
|
159
|
-
api_key=args.api_key,
|
160
|
-
server_name=args.name,
|
161
|
-
dry_run=args.dry_run,
|
162
|
-
force=args.force,
|
163
|
-
create_if_missing=create_if_missing,
|
164
|
-
)
|
165
|
-
except ExportError as e:
|
166
|
-
log.error(str(e))
|
167
|
-
return 1
|
168
|
-
|
169
|
-
if result.dry_run:
|
170
|
-
log.info("Dry-run complete. No changes written.")
|
171
|
-
return 0
|
172
|
-
|
173
|
-
if result.wrote_changes:
|
174
|
-
if result.backup_path is not None:
|
175
|
-
log.info("Backup created at {}", result.backup_path)
|
176
|
-
log.info("Updated {}", result.target_path)
|
177
|
-
else:
|
178
|
-
log.info("No changes were necessary.")
|
179
|
-
return 0
|
180
|
-
|
181
|
-
|
182
|
-
def run_cli(argv: list[str] | None = None) -> int:
|
183
|
-
parser = build_arg_parser()
|
184
|
-
args = parser.parse_args(argv)
|
185
|
-
|
186
|
-
if args.target == "cursor":
|
187
|
-
return _handle_cursor(args)
|
188
|
-
if args.target == "vscode":
|
189
|
-
return _handle_vscode(args)
|
190
|
-
if args.target == "claude-code":
|
191
|
-
return _handle_claude_code(args)
|
192
|
-
log.error("Unsupported target: {}", args.target)
|
193
|
-
return 2
|
194
|
-
|
195
|
-
|
196
|
-
def main() -> int:
|
197
|
-
return run_cli()
|
198
|
-
|
199
|
-
|
200
|
-
if __name__ == "__main__":
|
201
|
-
raise SystemExit(main())
|