open-edison 0.1.42__py3-none-any.whl → 0.1.43__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.
- {open_edison-0.1.42.dist-info → open_edison-0.1.43.dist-info}/METADATA +10 -10
- {open_edison-0.1.42.dist-info → open_edison-0.1.43.dist-info}/RECORD +13 -11
- src/cli.py +8 -41
- src/frontend_dist/assets/index-BUUcUfTt.js +51 -0
- src/frontend_dist/assets/index-o6_8mdM8.css +1 -0
- src/frontend_dist/index.html +16 -6
- src/frontend_dist/sw.js +71 -0
- src/mcp_importer/api.py +115 -17
- src/mcp_importer/quick_cli.py +12 -15
- src/oauth_manager.py +1 -1
- frontend_dist/index.html +0 -11
- {open_edison-0.1.42.dist-info → open_edison-0.1.43.dist-info}/WHEEL +0 -0
- {open_edison-0.1.42.dist-info → open_edison-0.1.43.dist-info}/entry_points.txt +0 -0
- {open_edison-0.1.42.dist-info → open_edison-0.1.43.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.\!container{width:100%!important}.container{width:100%}@media (min-width: 640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width: 768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width: 1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width: 1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width: 1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.fixed{position:fixed}.relative{position:relative}.bottom-4{bottom:1rem}.right-4{right:1rem}.z-50{z-index:50}.m-0{margin:0}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-auto{margin-left:auto}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-2{height:.5rem}.h-3{height:.75rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-\[580px\]{height:580px}.w-10{width:2.5rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-\[260px\]{width:260px}.w-full{width:100%}.min-w-\[240px\]{min-width:240px}.max-w-\[1400px\]{max-width:1400px}.max-w-\[260px\]{max-width:260px}.max-w-sm{max-width:24rem}.border-collapse{border-collapse:collapse}.translate-x-1{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-5{--tw-translate-x: 1.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-amber-400\/30{border-color:#fbbf244d}.border-app-accent{border-color:var(--accent)}.border-app-border{border-color:var(--border)}.border-blue-400\/30{border-color:#60a5fa4d}.border-rose-400\/30{border-color:#fb71854d}.bg-amber-400{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / var(--tw-bg-opacity, 1))}.bg-app-accent{background-color:var(--accent)}.bg-app-bg{background-color:var(--bg)}.bg-app-border{background-color:var(--border)}.bg-blue-400{--tw-bg-opacity: 1;background-color:rgb(96 165 250 / var(--tw-bg-opacity, 1))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-rose-400{--tw-bg-opacity: 1;background-color:rgb(251 113 133 / var(--tw-bg-opacity, 1))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.\!px-2{padding-left:.5rem!important;padding-right:.5rem!important}.\!px-3{padding-left:.75rem!important;padding-right:.75rem!important}.\!py-1\.5{padding-top:.375rem!important;padding-bottom:.375rem!important}.\!py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-2{padding-bottom:.5rem}.text-left{text-align:left}.text-center{text-align:center}.align-bottom{vertical-align:bottom}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-app-accent{color:var(--accent)}.text-app-muted{color:var(--muted)}.text-app-text{color:var(--text)}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-rose-400{--tw-text-opacity: 1;color:rgb(251 113 133 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.accent-blue-500{accent-color:#3b82f6}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}:root{--bg: #0b0c10;--card: #111318;--border: #1f2430;--text: #e6e6e6;--muted: #a0a7b4;--accent: #7c3aed;--success: #10b981;--warning: #f59e0b;--danger: #ef4444}[data-theme=dark]{--bg: #0b0c10;--card: #111318;--border: #1f2430;--text: #e6e6e6;--muted: #a0a7b4}[data-theme=light]{--bg: #f8fafc;--card: #ffffff;--border: #e5e7eb;--text: #0f172a;--muted: #475569}@media (prefers-color-scheme: light){:root{--bg: #f8fafc;--card: #ffffff;--border: #e5e7eb;--text: #0f172a;--muted: #475569}}html,body,#root{height:100%}body{margin:0;background:var(--bg);color:var(--text)}.container{margin:0 auto;padding:24px;max-width:1100px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:16px}.card{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:16px;box-shadow:0 1px 2px #0000000a,0 2px 12px #00000014}.stat{display:flex;align-items:center;gap:12px}.badge{display:inline-block;font-size:12px;padding:2px 8px;border-radius:999px;border:1px solid var(--border);background:#7c3aed14;color:var(--text)}.table{width:100%;border-collapse:collapse}.table th,.table td{border-bottom:1px solid var(--border);padding:8px 4px;text-align:left}.muted{color:var(--muted)}.accent{color:var(--accent)}.success{color:var(--success)}.warning{color:var(--warning)}.danger{color:var(--danger)}.toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px}.button{border:1px solid var(--border);background:var(--card);color:var(--text);padding:6px 10px;border-radius:8px;cursor:pointer}.button:hover{filter:brightness(1.05)}.hover\:text-gray-200:hover{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-blue-400:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(96 165 250 / var(--tw-ring-opacity, 1))}@media (min-width: 640px){.sm\:flex{display:flex}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-end{align-items:flex-end}}@media (min-width: 1024px){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-\[220px_1fr\]{grid-template-columns:220px 1fr}}
|
src/frontend_dist/index.html
CHANGED
@@ -1,11 +1,21 @@
|
|
1
|
-
<!
|
2
|
-
<html>
|
1
|
+
<!doctype html>
|
2
|
+
<html lang="en">
|
3
|
+
|
3
4
|
<head>
|
4
|
-
<
|
5
|
-
<meta
|
5
|
+
<meta charset="UTF-8" />
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
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">
|
6
15
|
</head>
|
16
|
+
|
7
17
|
<body>
|
8
|
-
<
|
9
|
-
<p>Frontend assets not available. Run 'make build_package' to build the full dashboard.</p>
|
18
|
+
<div id="root"></div>
|
10
19
|
</body>
|
20
|
+
|
11
21
|
</html>
|
src/frontend_dist/sw.js
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
// Minimal service worker for actionable notifications
|
2
|
+
|
3
|
+
self.addEventListener('install', (event) => {
|
4
|
+
self.skipWaiting();
|
5
|
+
});
|
6
|
+
|
7
|
+
self.addEventListener('activate', (event) => {
|
8
|
+
event.waitUntil(self.clients.claim());
|
9
|
+
});
|
10
|
+
|
11
|
+
// Receive messages from pages to show notifications with actions
|
12
|
+
self.addEventListener('message', (event) => {
|
13
|
+
try {
|
14
|
+
const data = event.data || {};
|
15
|
+
if (data && data.type === 'SHOW_MCP_BLOCK_NOTIFICATION') {
|
16
|
+
const title = data.title || 'Action required';
|
17
|
+
const body = data.body || 'Approve or deny the request';
|
18
|
+
const payload = data.data || {};
|
19
|
+
event.waitUntil(
|
20
|
+
self.registration.showNotification(title, {
|
21
|
+
body,
|
22
|
+
requireInteraction: true,
|
23
|
+
data: payload,
|
24
|
+
actions: [
|
25
|
+
{ action: 'approve', title: 'Approve' },
|
26
|
+
{ action: 'deny', title: 'Deny' }
|
27
|
+
]
|
28
|
+
})
|
29
|
+
);
|
30
|
+
}
|
31
|
+
} catch (e) {
|
32
|
+
// swallow
|
33
|
+
}
|
34
|
+
});
|
35
|
+
|
36
|
+
// Handle action button clicks and generic clicks
|
37
|
+
self.addEventListener('notificationclick', (event) => {
|
38
|
+
try {
|
39
|
+
const payload = (event.notification && event.notification.data) || {};
|
40
|
+
const action = event.action;
|
41
|
+
event.notification.close();
|
42
|
+
|
43
|
+
if (action === 'approve') {
|
44
|
+
const body = {
|
45
|
+
session_id: payload.sessionId,
|
46
|
+
kind: payload.kind,
|
47
|
+
name: payload.name
|
48
|
+
};
|
49
|
+
event.waitUntil(
|
50
|
+
fetch('/api/approve', {
|
51
|
+
method: 'POST',
|
52
|
+
headers: { 'Content-Type': 'application/json' },
|
53
|
+
body: JSON.stringify(body)
|
54
|
+
}).catch(() => { })
|
55
|
+
);
|
56
|
+
return;
|
57
|
+
}
|
58
|
+
|
59
|
+
if (action === 'deny') {
|
60
|
+
// No-op; could notify page if desired
|
61
|
+
return;
|
62
|
+
}
|
63
|
+
|
64
|
+
// Generic click: open dashboard as a safe fallback
|
65
|
+
event.waitUntil(self.clients.openWindow('/dashboard').catch(() => { }));
|
66
|
+
} catch (e) {
|
67
|
+
// swallow
|
68
|
+
}
|
69
|
+
});
|
70
|
+
|
71
|
+
|
src/mcp_importer/api.py
CHANGED
@@ -1,21 +1,28 @@
|
|
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
|
+
from loguru import logger as log
|
10
|
+
|
6
11
|
from src.config import Config, MCPServerConfig, get_config_json_path
|
7
12
|
from src.mcp_importer import paths as _paths
|
8
|
-
from src.mcp_importer.exporters import
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
import_from_cursor as _import_from_cursor,
|
13
|
+
from src.mcp_importer.exporters import (
|
14
|
+
ExportResult,
|
15
|
+
export_to_claude_code,
|
16
|
+
export_to_cursor,
|
17
|
+
export_to_vscode,
|
14
18
|
)
|
15
19
|
from src.mcp_importer.importers import (
|
16
|
-
|
20
|
+
import_from_claude_code,
|
21
|
+
import_from_cursor,
|
22
|
+
import_from_vscode,
|
17
23
|
)
|
18
24
|
from src.mcp_importer.merge import MergePolicy, merge_servers
|
25
|
+
from src.oauth_manager import OAuthStatus, get_oauth_manager
|
19
26
|
|
20
27
|
|
21
28
|
class CLIENT(str, Enum):
|
@@ -23,6 +30,12 @@ class CLIENT(str, Enum):
|
|
23
30
|
VSCODE = "vscode"
|
24
31
|
CLAUDE_CODE = "claude-code"
|
25
32
|
|
33
|
+
def __str__(self) -> str:
|
34
|
+
return self.value.capitalize()
|
35
|
+
|
36
|
+
def __repr__(self) -> str:
|
37
|
+
return str(self)
|
38
|
+
|
26
39
|
|
27
40
|
def detect_clients() -> set[CLIENT]:
|
28
41
|
detected: set[CLIENT] = set()
|
@@ -35,30 +48,31 @@ def detect_clients() -> set[CLIENT]:
|
|
35
48
|
return detected
|
36
49
|
|
37
50
|
|
38
|
-
import_cursor = _import_from_cursor
|
39
|
-
import_vscode = _import_from_vscode
|
40
|
-
import_claude_code = _import_from_claude_code
|
41
|
-
|
42
|
-
|
43
51
|
def import_from(client: CLIENT) -> list[MCPServerConfig]:
|
44
52
|
if client == CLIENT.CURSOR:
|
45
|
-
return
|
53
|
+
return import_from_cursor()
|
46
54
|
if client == CLIENT.VSCODE:
|
47
|
-
return
|
55
|
+
return import_from_vscode()
|
48
56
|
if client == CLIENT.CLAUDE_CODE:
|
49
|
-
return
|
57
|
+
return import_from_claude_code()
|
50
58
|
raise ValueError(f"Unsupported client: {client}")
|
51
59
|
|
52
60
|
|
53
61
|
def save_imported_servers(
|
54
62
|
servers: list[MCPServerConfig],
|
55
63
|
*,
|
64
|
+
dry_run: bool = False,
|
56
65
|
merge_policy: str = MergePolicy.SKIP,
|
57
66
|
config_dir: Path | None = None,
|
58
|
-
) -> Path:
|
67
|
+
) -> Path | None:
|
59
68
|
target_path: Path = (
|
60
69
|
get_config_json_path() if config_dir is None else (Path(config_dir) / "config.json")
|
61
70
|
)
|
71
|
+
if dry_run:
|
72
|
+
print(
|
73
|
+
f"[dry-run] Would import {len(servers)} server(s) and save to config.json (at {target_path})"
|
74
|
+
)
|
75
|
+
return None
|
62
76
|
cfg: Config = Config(target_path)
|
63
77
|
merged = merge_servers(existing=cfg.mcp_servers, imported=servers, policy=merge_policy)
|
64
78
|
cfg.mcp_servers = merged
|
@@ -75,7 +89,17 @@ def export_edison_to(
|
|
75
89
|
dry_run: bool = False,
|
76
90
|
force: bool = False,
|
77
91
|
create_if_missing: bool = False,
|
78
|
-
) ->
|
92
|
+
) -> ExportResult:
|
93
|
+
if dry_run:
|
94
|
+
print(
|
95
|
+
f"[dry-run] Would export Open Edison to '{client}' (backup and replace editor MCP config)"
|
96
|
+
)
|
97
|
+
return ExportResult(
|
98
|
+
target_path=Path(""),
|
99
|
+
backup_path=None,
|
100
|
+
wrote_changes=False,
|
101
|
+
dry_run=True,
|
102
|
+
)
|
79
103
|
match client:
|
80
104
|
case CLIENT.CURSOR:
|
81
105
|
return export_to_cursor(
|
@@ -104,3 +128,77 @@ def export_edison_to(
|
|
104
128
|
force=force,
|
105
129
|
create_if_missing=create_if_missing,
|
106
130
|
)
|
131
|
+
|
132
|
+
|
133
|
+
def verify_mcp_server(server: MCPServerConfig) -> bool: # noqa
|
134
|
+
"""Minimal validation: try listing tools/resources/prompts via FastMCP within a timeout."""
|
135
|
+
|
136
|
+
async def _verify_async() -> bool:
|
137
|
+
if not server.command.strip():
|
138
|
+
return False
|
139
|
+
|
140
|
+
# Inline backend config and capability listing (no extra helpers)
|
141
|
+
backend_cfg: dict[str, Any] = {
|
142
|
+
"mcpServers": {
|
143
|
+
server.name: {
|
144
|
+
"command": server.command,
|
145
|
+
"args": server.args,
|
146
|
+
"env": server.env or {},
|
147
|
+
**({"roots": server.roots} if server.roots else {}),
|
148
|
+
}
|
149
|
+
}
|
150
|
+
}
|
151
|
+
|
152
|
+
proxy: FastMCP[Any] | None = None
|
153
|
+
host: FastMCP[Any] | None = None
|
154
|
+
try:
|
155
|
+
proxy = FastMCP.as_proxy(backend_cfg)
|
156
|
+
host = FastMCP(name=f"open-edison-verify-host-{server.name}")
|
157
|
+
host.mount(proxy, prefix=server.name)
|
158
|
+
|
159
|
+
async def _call_list(kind: str) -> Any:
|
160
|
+
manager_name = {
|
161
|
+
"tools": "_tool_manager",
|
162
|
+
"resources": "_resource_manager",
|
163
|
+
"prompts": "_prompt_manager",
|
164
|
+
}[kind]
|
165
|
+
manager = getattr(host, manager_name)
|
166
|
+
return await getattr(manager, f"list_{kind}")()
|
167
|
+
|
168
|
+
await asyncio.wait_for(
|
169
|
+
asyncio.gather(
|
170
|
+
_call_list("tools"),
|
171
|
+
_call_list("resources"),
|
172
|
+
_call_list("prompts"),
|
173
|
+
),
|
174
|
+
timeout=30.0,
|
175
|
+
)
|
176
|
+
return True
|
177
|
+
except Exception as e:
|
178
|
+
log.error("MCP verification failed for '{}': {}", server.name, e)
|
179
|
+
return False
|
180
|
+
finally:
|
181
|
+
try:
|
182
|
+
for obj in (host, proxy):
|
183
|
+
if isinstance(obj, FastMCP):
|
184
|
+
result = obj.shutdown() # type: ignore[attr-defined]
|
185
|
+
if isinstance(result, Awaitable):
|
186
|
+
await result # type: ignore[func-returns-value]
|
187
|
+
except Exception:
|
188
|
+
pass
|
189
|
+
|
190
|
+
return asyncio.run(_verify_async())
|
191
|
+
|
192
|
+
|
193
|
+
def server_needs_oauth(server: MCPServerConfig) -> bool: # noqa
|
194
|
+
"""Return True if the remote server currently needs OAuth; False otherwise."""
|
195
|
+
|
196
|
+
async def _needs_oauth_async() -> bool:
|
197
|
+
if not server.is_remote_server():
|
198
|
+
return False
|
199
|
+
info = await get_oauth_manager().check_oauth_requirement(
|
200
|
+
server.name, server.get_remote_url()
|
201
|
+
)
|
202
|
+
return info.status == OAuthStatus.NEEDS_AUTH
|
203
|
+
|
204
|
+
return asyncio.run(_needs_oauth_async())
|
src/mcp_importer/quick_cli.py
CHANGED
@@ -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
|
|
src/oauth_manager.py
CHANGED
@@ -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()
|
frontend_dist/index.html
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html>
|
3
|
-
<head>
|
4
|
-
<title>Open Edison Dashboard</title>
|
5
|
-
<meta charset="utf-8">
|
6
|
-
</head>
|
7
|
-
<body>
|
8
|
-
<h1>Open Edison Dashboard</h1>
|
9
|
-
<p>Frontend assets not available. Run 'make build_package' to build the full dashboard.</p>
|
10
|
-
</body>
|
11
|
-
</html>
|
File without changes
|
File without changes
|
File without changes
|