open-edison 0.1.38__tar.gz → 0.1.40__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.
- {open_edison-0.1.38 → open_edison-0.1.40}/PKG-INFO +10 -10
- {open_edison-0.1.38 → open_edison-0.1.40}/README.md +9 -9
- {open_edison-0.1.38 → open_edison-0.1.40}/config.json +38 -5
- {open_edison-0.1.38 → open_edison-0.1.40}/pyproject.toml +5 -8
- {open_edison-0.1.38 → open_edison-0.1.40}/src/mcp_importer/__init__.py +1 -1
- {open_edison-0.1.38 → open_edison-0.1.40}/src/mcp_importer/__main__.py +2 -2
- open_edison-0.1.40/src/mcp_importer/api.py +196 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/mcp_importer/quick_cli.py +13 -16
- {open_edison-0.1.38 → open_edison-0.1.40}/src/oauth_manager.py +1 -1
- open_edison-0.1.38/src/mcp_importer/api.py +0 -106
- {open_edison-0.1.38 → open_edison-0.1.40}/.gitignore +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/LICENSE +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/desktop_ext/README.md +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/docs/README.md +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/docs/architecture/single_user_design.md +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/docs/core/configuration.md +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/docs/core/project_structure.md +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/docs/core/proxy_usage.md +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/docs/deployment/docker.md +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/docs/deployment/local.md +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/docs/development/contributing.md +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/docs/development/development_guide.md +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/docs/development/testing.md +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/docs/quick-reference/api_reference.md +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/docs/quick-reference/config_quick_start.md +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/hatch_build.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/installation_test/README.md +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/prompt_permissions.json +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/resource_permissions.json +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/__init__.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/__main__.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/cli.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/config.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/config.pyi +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/events.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/frontend_dist/assets/index-BUUcUfTt.js +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/frontend_dist/assets/index-o6_8mdM8.css +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/frontend_dist/index.html +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/frontend_dist/sw.js +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/mcp_importer/cli.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/mcp_importer/export_cli.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/mcp_importer/exporters.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/mcp_importer/import_api.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/mcp_importer/importers.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/mcp_importer/merge.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/mcp_importer/parsers.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/mcp_importer/paths.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/mcp_importer/types.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/middleware/data_access_tracker.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/middleware/session_tracking.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/permissions.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/server.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/single_user_mcp.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/src/telemetry.py +0 -0
- {open_edison-0.1.38 → open_edison-0.1.40}/tool_permissions.json +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: open-edison
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.40
|
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 mcp_importer.quick_cli --yes
|
79
|
+
uv run python -m src.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 mcp_importer.quick_cli --yes
|
142
|
+
uv run python -m src.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 mcp_importer --source cursor --dry-run
|
148
|
+
uv run python -m src.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 mcp_importer --source cursor
|
155
|
-
uv run python -m mcp_importer --source vscode
|
156
|
-
uv run python -m mcp_importer --source claude-code
|
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
|
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 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
|
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
|
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 mcp_importer.quick_cli --yes
|
55
|
+
uv run python -m src.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 mcp_importer.quick_cli --yes
|
118
|
+
uv run python -m src.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 mcp_importer --source cursor --dry-run
|
124
|
+
uv run python -m src.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 mcp_importer --source cursor
|
131
|
-
uv run python -m mcp_importer --source vscode
|
132
|
-
uv run python -m mcp_importer --source claude-code
|
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
|
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 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
|
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
|
141
141
|
```
|
142
142
|
|
143
143
|
</details>
|
@@ -38,10 +38,10 @@
|
|
38
38
|
"command": "npx",
|
39
39
|
"args": [
|
40
40
|
"-y",
|
41
|
-
"@
|
41
|
+
"@github/mcp-server-github"
|
42
42
|
],
|
43
43
|
"env": {
|
44
|
-
"
|
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
|
-
"
|
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.
|
3
|
+
version = "0.1.40"
|
4
4
|
description = "Open-source MCP security, aggregation, and monitoring. Single-user, self-hosted MCP proxy."
|
5
5
|
readme = "README.md"
|
6
6
|
authors = [
|
@@ -28,8 +28,8 @@ requires-python = ">= 3.12"
|
|
28
28
|
[project.scripts]
|
29
29
|
open-edison = "src.cli:main"
|
30
30
|
open_edison = "src.cli:main"
|
31
|
-
mcp-importer = "mcp_importer.__main__:main"
|
32
|
-
mcp-importer-quick = "mcp_importer.quick_cli:main"
|
31
|
+
mcp-importer = "src.mcp_importer.__main__:main"
|
32
|
+
mcp-importer-quick = "src.mcp_importer.quick_cli:main"
|
33
33
|
|
34
34
|
[build-system]
|
35
35
|
requires = ["hatchling"]
|
@@ -54,10 +54,7 @@ dev-dependencies = [
|
|
54
54
|
allow-direct-references = true
|
55
55
|
|
56
56
|
[tool.hatch.build.targets.wheel]
|
57
|
-
packages = ["src"
|
58
|
-
exclude = [
|
59
|
-
"src/mcp_importer/**",
|
60
|
-
]
|
57
|
+
packages = ["src"]
|
61
58
|
include = [
|
62
59
|
"README.md",
|
63
60
|
"LICENSE",
|
@@ -66,8 +63,8 @@ include = [
|
|
66
63
|
"resource_permissions.json",
|
67
64
|
"prompt_permissions.json",
|
68
65
|
"docs/**",
|
69
|
-
"src/frontend_dist/**",
|
70
66
|
]
|
67
|
+
force-include = { "src/frontend_dist" = "frontend_dist" }
|
71
68
|
|
72
69
|
[tool.hatch.build.targets.wheel.hooks.custom]
|
73
70
|
path = "hatch_build.py"
|
@@ -2,8 +2,8 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import sys
|
4
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
|
5
|
+
from src.mcp_importer.cli import run_cli as import_run_cli
|
6
|
+
from src.mcp_importer.export_cli import run_cli as export_run_cli
|
7
7
|
|
8
8
|
|
9
9
|
def main() -> int:
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# pyright: reportMissingImports=false, reportUnknownVariableType=false, reportUnknownMemberType=false, reportUnknownArgumentType=false, reportUnknownParameterType=false
|
2
|
+
import asyncio
|
3
|
+
from collections.abc import Awaitable
|
4
|
+
from enum import Enum
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import Any, Protocol, cast, runtime_checkable
|
7
|
+
|
8
|
+
from fastmcp import FastMCP
|
9
|
+
|
10
|
+
from src.config import Config, MCPServerConfig, get_config_json_path
|
11
|
+
from src.mcp_importer import paths as _paths
|
12
|
+
from src.mcp_importer.exporters import (
|
13
|
+
ExportResult,
|
14
|
+
export_to_claude_code,
|
15
|
+
export_to_cursor,
|
16
|
+
export_to_vscode,
|
17
|
+
)
|
18
|
+
from src.mcp_importer.importers import (
|
19
|
+
import_from_claude_code,
|
20
|
+
import_from_cursor,
|
21
|
+
import_from_vscode,
|
22
|
+
)
|
23
|
+
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
|
+
|
34
|
+
|
35
|
+
class CLIENT(str, Enum):
|
36
|
+
CURSOR = "cursor"
|
37
|
+
VSCODE = "vscode"
|
38
|
+
CLAUDE_CODE = "claude-code"
|
39
|
+
|
40
|
+
def __str__(self) -> str:
|
41
|
+
return self.value.capitalize()
|
42
|
+
|
43
|
+
def __repr__(self) -> str:
|
44
|
+
return str(self)
|
45
|
+
|
46
|
+
|
47
|
+
def detect_clients() -> set[CLIENT]:
|
48
|
+
detected: set[CLIENT] = set()
|
49
|
+
if _paths.detect_cursor_config_path() is not None:
|
50
|
+
detected.add(CLIENT.CURSOR)
|
51
|
+
if _paths.detect_vscode_config_path() is not None:
|
52
|
+
detected.add(CLIENT.VSCODE)
|
53
|
+
if _paths.detect_claude_code_config_path() is not None:
|
54
|
+
detected.add(CLIENT.CLAUDE_CODE)
|
55
|
+
return detected
|
56
|
+
|
57
|
+
|
58
|
+
def import_from(client: CLIENT) -> list[MCPServerConfig]:
|
59
|
+
if client == CLIENT.CURSOR:
|
60
|
+
return import_from_cursor()
|
61
|
+
if client == CLIENT.VSCODE:
|
62
|
+
return import_from_vscode()
|
63
|
+
if client == CLIENT.CLAUDE_CODE:
|
64
|
+
return import_from_claude_code()
|
65
|
+
raise ValueError(f"Unsupported client: {client}")
|
66
|
+
|
67
|
+
|
68
|
+
def save_imported_servers(
|
69
|
+
servers: list[MCPServerConfig],
|
70
|
+
*,
|
71
|
+
dry_run: bool = False,
|
72
|
+
merge_policy: str = MergePolicy.SKIP,
|
73
|
+
config_dir: Path | None = None,
|
74
|
+
) -> Path | None:
|
75
|
+
target_path: Path = (
|
76
|
+
get_config_json_path() if config_dir is None else (Path(config_dir) / "config.json")
|
77
|
+
)
|
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
|
+
cfg: Config = Config(target_path)
|
84
|
+
merged = merge_servers(existing=cfg.mcp_servers, imported=servers, policy=merge_policy)
|
85
|
+
cfg.mcp_servers = merged
|
86
|
+
cfg.save(target_path)
|
87
|
+
return target_path
|
88
|
+
|
89
|
+
|
90
|
+
def export_edison_to(
|
91
|
+
client: CLIENT,
|
92
|
+
*,
|
93
|
+
url: str = "http://localhost:3000/mcp/",
|
94
|
+
api_key: str = "dev-api-key-change-me",
|
95
|
+
server_name: str = "open-edison",
|
96
|
+
dry_run: bool = False,
|
97
|
+
force: bool = False,
|
98
|
+
create_if_missing: bool = False,
|
99
|
+
) -> ExportResult:
|
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
|
+
)
|
110
|
+
match client:
|
111
|
+
case CLIENT.CURSOR:
|
112
|
+
return export_to_cursor(
|
113
|
+
url=url,
|
114
|
+
api_key=api_key,
|
115
|
+
server_name=server_name,
|
116
|
+
dry_run=dry_run,
|
117
|
+
force=force,
|
118
|
+
create_if_missing=create_if_missing,
|
119
|
+
)
|
120
|
+
case CLIENT.VSCODE:
|
121
|
+
return export_to_vscode(
|
122
|
+
url=url,
|
123
|
+
api_key=api_key,
|
124
|
+
server_name=server_name,
|
125
|
+
dry_run=dry_run,
|
126
|
+
force=force,
|
127
|
+
create_if_missing=create_if_missing,
|
128
|
+
)
|
129
|
+
case CLIENT.CLAUDE_CODE:
|
130
|
+
return export_to_claude_code(
|
131
|
+
url=url,
|
132
|
+
api_key=api_key,
|
133
|
+
server_name=server_name,
|
134
|
+
dry_run=dry_run,
|
135
|
+
force=force,
|
136
|
+
create_if_missing=create_if_missing,
|
137
|
+
)
|
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())
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
3
3
|
import argparse
|
4
4
|
from collections.abc import Iterable
|
5
5
|
|
6
|
-
from mcp_importer.api import (
|
6
|
+
from src.mcp_importer.api import (
|
7
7
|
CLIENT,
|
8
8
|
detect_clients,
|
9
9
|
export_edison_to,
|
@@ -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
|
-
|
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
|
-
|
43
|
-
|
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()
|
@@ -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
|
-
)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|