mindroot 9.3.0__py3-none-any.whl → 9.6.0__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.
Files changed (183) hide show
  1. mindroot/coreplugins/admin/__init__.py +3 -1
  2. mindroot/coreplugins/admin/agent_router.py +250 -7
  3. mindroot/coreplugins/admin/asset_manager.py +164 -0
  4. mindroot/coreplugins/admin/command_router.py +236 -1
  5. mindroot/coreplugins/admin/mcp_catalog_routes.py +156 -0
  6. mindroot/coreplugins/admin/mcp_publish_routes.py +450 -0
  7. mindroot/coreplugins/admin/mcp_registry_routes.py +495 -0
  8. mindroot/coreplugins/admin/mcp_routes.py +216 -0
  9. mindroot/coreplugins/admin/mod.py +62 -0
  10. mindroot/coreplugins/admin/oauth_callback_router.py +84 -0
  11. mindroot/coreplugins/admin/persona_handler.py +15 -6
  12. mindroot/coreplugins/admin/persona_router.py +158 -2
  13. mindroot/coreplugins/admin/plugin_manager.py +105 -9
  14. mindroot/coreplugins/admin/plugin_router_fixed.py +23 -0
  15. mindroot/coreplugins/admin/plugin_router_new_not_working.py +145 -0
  16. mindroot/coreplugins/admin/plugin_routes.py +114 -0
  17. mindroot/coreplugins/admin/registry_settings_routes.py +140 -0
  18. mindroot/coreplugins/admin/router.py +116 -15
  19. mindroot/coreplugins/admin/service_models.py +1 -1
  20. mindroot/coreplugins/admin/settings_router.py +1 -0
  21. mindroot/coreplugins/admin/static/css/admin-custom.css +357 -2
  22. mindroot/coreplugins/admin/static/css/dark.css +1 -0
  23. mindroot/coreplugins/admin/static/css/default.css +4 -0
  24. mindroot/coreplugins/admin/static/js/about-info.js +367 -0
  25. mindroot/coreplugins/admin/static/js/agent-form.js +83 -3
  26. mindroot/coreplugins/admin/static/js/api-key-script.js +307 -0
  27. mindroot/coreplugins/admin/static/js/mcp-manager.js +348 -0
  28. mindroot/coreplugins/admin/static/js/mcp-publisher.js +780 -0
  29. mindroot/coreplugins/admin/static/js/persona-editor.js +34 -5
  30. mindroot/coreplugins/admin/static/js/plugin-toggle.js +1 -1
  31. mindroot/coreplugins/admin/static/js/recommended-plugin-install.js +63 -0
  32. mindroot/coreplugins/admin/static/js/registry-auth-section.js +132 -0
  33. mindroot/coreplugins/admin/static/js/registry-manager-base.js +613 -0
  34. mindroot/coreplugins/admin/static/js/registry-manager-publish-old-delete.js +166 -0
  35. mindroot/coreplugins/admin/static/js/registry-manager.js +351 -0
  36. mindroot/coreplugins/admin/static/js/registry-publish-section.js +377 -0
  37. mindroot/coreplugins/admin/static/js/registry-search-section.js +400 -0
  38. mindroot/coreplugins/admin/static/js/registry-search-section.js.bak +3 -0
  39. mindroot/coreplugins/admin/static/js/registry-settings.js +69 -0
  40. mindroot/coreplugins/admin/static/js/registry-shared-services.js +903 -0
  41. mindroot/coreplugins/admin/static/js/registry-simple-sections.js +85 -0
  42. mindroot/coreplugins/admin/static/js/secure-widget-manager.js +438 -0
  43. mindroot/coreplugins/admin/static/logo.png +0 -0
  44. mindroot/coreplugins/admin/templates/admin.jinja2 +275 -110
  45. mindroot/coreplugins/agent/Assistant/agent.json +27 -11
  46. mindroot/coreplugins/agent/agent.py +2 -2
  47. mindroot/coreplugins/agent/command_parser.py +25 -10
  48. mindroot/coreplugins/agent/templates/system.jinja2 +0 -12
  49. mindroot/coreplugins/chat/__init__.py +4 -1
  50. mindroot/coreplugins/chat/router.py +132 -20
  51. mindroot/coreplugins/chat/router_dedup_patch.py +20 -0
  52. mindroot/coreplugins/chat/services.py +31 -1
  53. mindroot/coreplugins/chat/static/css/action-fix.css +32 -0
  54. mindroot/coreplugins/chat/static/css/admin-custom.css +5 -3
  55. mindroot/coreplugins/chat/static/css/dark.css +24 -3
  56. mindroot/coreplugins/chat/static/css/default.css +24 -3
  57. mindroot/coreplugins/chat/static/css/main.css +1 -0
  58. mindroot/coreplugins/chat/static/js/action.js +137 -60
  59. mindroot/coreplugins/chat/static/js/chat-history.js +3 -0
  60. mindroot/coreplugins/chat/static/js/chat.js +59 -16
  61. mindroot/coreplugins/chat/static/js/chat.js.diff +221 -0
  62. mindroot/coreplugins/chat/static/js/chatform.js +2 -2
  63. mindroot/coreplugins/chat/static/site.webmanifest +1 -1
  64. mindroot/coreplugins/chat/templates/chat.jinja2 +3 -3
  65. mindroot/coreplugins/chat/widget_manager.py +139 -0
  66. mindroot/coreplugins/chat/widget_routes.py +287 -0
  67. mindroot/coreplugins/check_list/inject/admin.jinja2 +1 -1
  68. mindroot/coreplugins/email/__init__.py +2 -0
  69. mindroot/coreplugins/email/email_provider.py +2 -2
  70. mindroot/coreplugins/email/mod.py +100 -0
  71. mindroot/coreplugins/email/services.py +5 -3
  72. mindroot/coreplugins/email/smtp_handler.py +9 -3
  73. mindroot/coreplugins/email/test_email_service.py +75 -0
  74. mindroot/coreplugins/env_manager/mod.py +61 -25
  75. mindroot/coreplugins/home/router.py +37 -2
  76. mindroot/coreplugins/home/static/imgs/logo.png +0 -0
  77. mindroot/coreplugins/home/static/imgs/logo.png.bak +0 -0
  78. mindroot/coreplugins/home/static/imgs/logo_teal.png +0 -0
  79. mindroot/coreplugins/home/static/imgs/logo_teal2.png +0 -0
  80. mindroot/coreplugins/home/static/imgs/logo_teal_detailed.png +0 -0
  81. mindroot/coreplugins/home/static/imgs/logo_teal_python.png +0 -0
  82. mindroot/coreplugins/home/templates/home.jinja2 +15 -6
  83. mindroot/coreplugins/index/indices/default/index.json +39 -6
  84. mindroot/coreplugins/jwt_auth/middleware.py +47 -2
  85. mindroot/coreplugins/jwt_auth/mod.py +40 -17
  86. mindroot/coreplugins/l8n/__init__.py +6 -0
  87. mindroot/coreplugins/l8n/debug_loader.py +85 -0
  88. mindroot/coreplugins/l8n/debug_middleware.py +74 -0
  89. mindroot/coreplugins/l8n/l8n_constants.py +19 -0
  90. mindroot/coreplugins/l8n/language_detection.py +183 -0
  91. mindroot/coreplugins/l8n/middleware.py +151 -0
  92. mindroot/coreplugins/l8n/mod.py +277 -0
  93. mindroot/coreplugins/l8n/monkey_patch_to_delete.py +186 -0
  94. mindroot/coreplugins/l8n/test_enhanced.py +298 -0
  95. mindroot/coreplugins/l8n/test_l8n.py +95 -0
  96. mindroot/coreplugins/l8n/test_l8n_standalone.py +251 -0
  97. mindroot/coreplugins/l8n/test_middleware.py +272 -0
  98. mindroot/coreplugins/l8n/utils.py +232 -0
  99. mindroot/coreplugins/mcp_/__init__.py +14 -0
  100. mindroot/coreplugins/mcp_/catalog_commands.py +328 -0
  101. mindroot/coreplugins/mcp_/catalog_manager.py +263 -0
  102. mindroot/coreplugins/mcp_/dynamic_commands.py +154 -0
  103. mindroot/coreplugins/mcp_/mcp_manager.py +1031 -0
  104. mindroot/coreplugins/mcp_/mod.py +367 -0
  105. mindroot/coreplugins/mcp_/oauth_storage.py +144 -0
  106. mindroot/coreplugins/mcp_/server_installer.py +79 -0
  107. mindroot/coreplugins/mcp_/setup.py +26 -0
  108. mindroot/coreplugins/mcp_/test_dynamic_commands.py +134 -0
  109. mindroot/coreplugins/mcp_/testmcpclient.py +92 -0
  110. mindroot/coreplugins/persona/mod.py +12 -7
  111. mindroot/coreplugins/signup/templates/signup.jinja2 +1 -1
  112. mindroot/coreplugins/subscriptions/__init__.py +1 -0
  113. mindroot/coreplugins/subscriptions/mod.py +14 -3
  114. mindroot/coreplugins/subscriptions/router.py +3 -0
  115. mindroot/coreplugins/user_service/__init__.py +1 -2
  116. mindroot/coreplugins/user_service/admin_init.py +1 -0
  117. mindroot/coreplugins/user_service/email_service.py +72 -17
  118. mindroot/coreplugins/user_service/mod.py +10 -2
  119. mindroot/coreplugins/user_service/router.py +2 -0
  120. mindroot/lib/auth/api_key.py +28 -0
  121. mindroot/lib/cli/plugins.py +94 -0
  122. mindroot/lib/plugins/default_plugin_manifest.json +20 -0
  123. mindroot/lib/plugins/installation.py +5 -5
  124. mindroot/lib/plugins/l8n_static_handler.py +225 -0
  125. mindroot/lib/plugins/loader.py +33 -3
  126. mindroot/lib/plugins/loader_with_l8n.py +281 -0
  127. mindroot/lib/plugins/manifest.py +236 -24
  128. mindroot/lib/providers/commands.py +3 -1
  129. mindroot/lib/route_decorators.py +5 -5
  130. mindroot/lib/templates.py +183 -11
  131. mindroot/lib/utils/merge_arrays.py +1 -1
  132. mindroot/migrate.py +39 -20
  133. mindroot/registry/data_access.py +1 -1
  134. mindroot/server.py +42 -13
  135. mindroot/server_missing_normal_args.py +197 -0
  136. mindroot/server_prev.py +173 -0
  137. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/METADATA +7 -2
  138. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/RECORD +143 -113
  139. mindroot/coreplugins/admin/plugin_manager_backup.py +0 -615
  140. mindroot/coreplugins/admin/static/favicon/about.txt +0 -6
  141. mindroot/coreplugins/admin/static/favicon/android-chrome-512x512.png +0 -0
  142. mindroot/coreplugins/admin/static/favicon/apple-touch-icon.png +0 -0
  143. mindroot/coreplugins/admin/static/favicon/favicon-16x16.png +0 -0
  144. mindroot/coreplugins/admin/static/favicon/favicon-32x32.png +0 -0
  145. mindroot/coreplugins/admin/static/favicon/favicon.ico +0 -0
  146. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/about.txt +0 -6
  147. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
  148. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
  149. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
  150. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
  151. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
  152. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon.ico +0 -0
  153. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/site.webmanifest +0 -1
  154. mindroot/coreplugins/admin/static/favicon/logo.png +0 -0
  155. mindroot/coreplugins/admin/static/favicon/site.webmanifest +0 -1
  156. mindroot/coreplugins/admin/static/js/backup/agent-editor.js +0 -186
  157. mindroot/coreplugins/admin/static/js/backup/agent-form.js +0 -1133
  158. mindroot/coreplugins/admin/static/js/backup/agent-list.js +0 -94
  159. mindroot/coreplugins/chat/static/favicon/about.txt +0 -6
  160. mindroot/coreplugins/chat/static/favicon/android-chrome-192x192.png +0 -0
  161. mindroot/coreplugins/chat/static/favicon/android-chrome-512x512.png +0 -0
  162. mindroot/coreplugins/chat/static/favicon/apple-touch-icon.png +0 -0
  163. mindroot/coreplugins/chat/static/favicon/favicon-16x16.png +0 -0
  164. mindroot/coreplugins/chat/static/favicon/favicon-32x32.png +0 -0
  165. mindroot/coreplugins/chat/static/favicon/favicon.ico +0 -0
  166. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/about.txt +0 -6
  167. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
  168. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
  169. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
  170. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
  171. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
  172. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon.ico +0 -0
  173. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/site.webmanifest +0 -1
  174. mindroot/coreplugins/chat/static/favicon/logo.png +0 -0
  175. mindroot/coreplugins/chat/static/favicon/site.webmanifest +0 -1
  176. mindroot/coreplugins/index/default.json +0 -76
  177. mindroot/coreplugins/user_service/file_trigger_service.py +0 -12
  178. mindroot/coreplugins/user_service/hooks.py +0 -23
  179. /mindroot/coreplugins/{admin/static/favicon/android-chrome-192x192.png → home/static/imgs/backuplogo.png} +0 -0
  180. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/WHEEL +0 -0
  181. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/entry_points.txt +0 -0
  182. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/licenses/LICENSE +0 -0
  183. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,780 @@
1
+ import { BaseEl } from '/admin/static/js/base.js';
2
+ import { html, css } from '/admin/static/js/lit-core.min.js';
3
+
4
+ class McpPublisher extends BaseEl {
5
+ static properties = {
6
+ serverType: { type: String },
7
+ serverName: { type: String },
8
+ serverDescription: { type: String },
9
+ localConfig: { type: Object },
10
+ remoteUrl: { type: String },
11
+ authType: { type: String },
12
+ discoveredTools: { type: Array },
13
+ loading: { type: Boolean },
14
+ error: { type: String },
15
+ success: { type: String },
16
+ oauthFlow: { type: Object },
17
+ oauthWindow: { type: Object },
18
+ registryUrl: { type: String },
19
+ authToken: { type: String },
20
+ isLoggedIn: { type: Boolean },
21
+ requiredPlaceholders: { type: Array },
22
+ placeholderValues: { type: Object }
23
+ };
24
+
25
+ static styles = css`
26
+ :host {
27
+ display: block;
28
+ width: 100%;
29
+ }
30
+
31
+ .mcp-publisher {
32
+ display: flex;
33
+ flex-direction: column;
34
+ gap: 1rem;
35
+ max-width: 800px;
36
+ }
37
+
38
+ .section {
39
+ background: rgb(10, 10, 25);
40
+ border-radius: 8px;
41
+ padding: 1rem;
42
+ border: 1px solid rgba(255, 255, 255, 0.1);
43
+ }
44
+
45
+ .form-group {
46
+ display: flex;
47
+ flex-direction: column;
48
+ gap: 0.5rem;
49
+ margin-bottom: 1rem;
50
+ }
51
+
52
+ .form-row {
53
+ display: flex;
54
+ gap: 1rem;
55
+ align-items: center;
56
+ }
57
+
58
+ label {
59
+ color: #fff;
60
+ font-weight: 500;
61
+ }
62
+
63
+ input, textarea, select {
64
+ background: #2a2a40;
65
+ color: #fff;
66
+ border: 1px solid rgba(255, 255, 255, 0.1);
67
+ padding: 0.5rem;
68
+ border-radius: 4px;
69
+ font-family: inherit;
70
+ }
71
+
72
+ input:focus, textarea:focus, select:focus {
73
+ outline: none;
74
+ border-color: #4a9eff;
75
+ }
76
+
77
+ textarea {
78
+ min-height: 100px;
79
+ font-family: 'Courier New', monospace;
80
+ resize: vertical;
81
+ }
82
+
83
+ .checkbox-group {
84
+ display: flex;
85
+ gap: 1rem;
86
+ margin: 1rem 0;
87
+ }
88
+
89
+ .checkbox-item {
90
+ display: flex;
91
+ align-items: center;
92
+ gap: 0.5rem;
93
+ cursor: pointer;
94
+ }
95
+
96
+ input[type="checkbox"] {
97
+ width: auto;
98
+ }
99
+
100
+ button {
101
+ background: #2a2a40;
102
+ color: #fff;
103
+ border: 1px solid rgba(255, 255, 255, 0.1);
104
+ padding: 0.5rem 1rem;
105
+ border-radius: 4px;
106
+ cursor: pointer;
107
+ transition: background 0.2s;
108
+ }
109
+
110
+ button:hover {
111
+ background: #3a3a50;
112
+ }
113
+
114
+ button.primary {
115
+ background: #4a9eff;
116
+ }
117
+
118
+ button.primary:hover {
119
+ background: #3a8eef;
120
+ }
121
+
122
+ button:disabled {
123
+ opacity: 0.5;
124
+ cursor: not-allowed;
125
+ }
126
+
127
+ .tools-preview {
128
+ background: rgba(0, 0, 0, 0.2);
129
+ border-radius: 4px;
130
+ padding: 1rem;
131
+ margin-top: 1rem;
132
+ }
133
+
134
+ .tool-item {
135
+ background: rgba(255, 255, 255, 0.05);
136
+ padding: 0.5rem;
137
+ border-radius: 4px;
138
+ margin-bottom: 0.5rem;
139
+ }
140
+
141
+ .tool-name {
142
+ font-weight: bold;
143
+ color: #4a9eff;
144
+ }
145
+
146
+ .tool-description {
147
+ color: #ccc;
148
+ font-size: 0.9rem;
149
+ margin-top: 0.25rem;
150
+ }
151
+
152
+ .error {
153
+ background: rgba(220, 53, 69, 0.2);
154
+ color: #dc3545;
155
+ padding: 0.5rem;
156
+ border-radius: 4px;
157
+ margin: 0.5rem 0;
158
+ }
159
+
160
+ .success {
161
+ background: rgba(40, 167, 69, 0.2);
162
+ color: #28a745;
163
+ padding: 0.5rem;
164
+ border-radius: 4px;
165
+ margin: 0.5rem 0;
166
+ }
167
+
168
+ .loading {
169
+ text-align: center;
170
+ color: #ccc;
171
+ padding: 1rem;
172
+ }
173
+
174
+ .help-text {
175
+ font-size: 0.8rem;
176
+ color: #999;
177
+ margin-top: 0.25rem;
178
+ }
179
+
180
+ .json-example {
181
+ background: rgba(0, 0, 0, 0.3);
182
+ padding: 0.5rem;
183
+ border-radius: 4px;
184
+ font-family: 'Courier New', monospace;
185
+ font-size: 0.8rem;
186
+ color: #ccc;
187
+ white-space: pre-wrap;
188
+ margin-top: 0.5rem;
189
+ }
190
+
191
+ .oauth-status {
192
+ background: rgba(74, 158, 255, 0.2);
193
+ color: #4a9eff;
194
+ padding: 0.5rem;
195
+ border-radius: 4px;
196
+ margin: 0.5rem 0;
197
+ }
198
+ `;
199
+
200
+ constructor() {
201
+ super();
202
+ this.serverType = 'local';
203
+ this.serverName = '';
204
+ this.serverDescription = '';
205
+ this.localConfig = {
206
+ command: '',
207
+ args: [],
208
+ env: {}
209
+ };
210
+ this.remoteUrl = '';
211
+ this.authType = 'none';
212
+ this.discoveredTools = [];
213
+ this.loading = false;
214
+ this.error = '';
215
+ this.success = '';
216
+ this.oauthFlow = null;
217
+ this.oauthWindow = null;
218
+ this.registryUrl = '';
219
+ this.authToken = '';
220
+ this.isLoggedIn = false;
221
+ this.requiredPlaceholders = [];
222
+ this.placeholderValues = {};
223
+
224
+ // Listen for OAuth callback messages
225
+ window.addEventListener('message', this.handleOAuthCallback.bind(this));
226
+ }
227
+
228
+ disconnectedCallback() {
229
+ super.disconnectedCallback();
230
+ // Clean up OAuth window if it exists
231
+ if (this.oauthWindow && !this.oauthWindow.closed) {
232
+ this.oauthWindow.close();
233
+ }
234
+ // Remove event listener
235
+ window.removeEventListener('message', this.handleOAuthCallback.bind(this));
236
+ }
237
+
238
+ handleServerTypeChange(e) {
239
+ this.serverType = e.target.value;
240
+ this.discoveredTools = [];
241
+ this.error = '';
242
+ this.oauthFlow = null;
243
+ }
244
+
245
+ handleLocalConfigChange(e) {
246
+ try {
247
+ const config = JSON.parse(e.target.value);
248
+ this.localConfig = {
249
+ command: config.command || '',
250
+ args: config.args || [],
251
+ env: config.env || {}
252
+ };
253
+ this.scanForRequiredSecrets();
254
+ this.error = '';
255
+ } catch (error) {
256
+ this.error = 'Invalid JSON configuration';
257
+ }
258
+ }
259
+
260
+ scanForRequiredSecrets() {
261
+ const placeholders = new Set();
262
+
263
+ // 1. Add all keys from the 'env' object automatically.
264
+ if (this.localConfig.env) {
265
+ Object.keys(this.localConfig.env).forEach(key => placeholders.add(key));
266
+ }
267
+
268
+ // 2. Scan the entire configuration for <PLACEHOLDER> syntax.
269
+ const configString = JSON.stringify(this.localConfig);
270
+ const placeholderRegex = /<([A-Z0-9_]+)>/g;
271
+ let match;
272
+ while ((match = placeholderRegex.exec(configString)) !== null) {
273
+ placeholders.add(match[1]);
274
+ }
275
+
276
+ this.requiredPlaceholders = Array.from(placeholders);
277
+ // Reset values for placeholders that no longer exist
278
+ const newValues = {};
279
+ this.requiredPlaceholders.forEach(p => {
280
+ if (this.placeholderValues[p]) {
281
+ newValues[p] = this.placeholderValues[p];
282
+ }
283
+ });
284
+ this.placeholderValues = newValues;
285
+ }
286
+
287
+ async discoverTools() {
288
+ if (!this.serverName.trim()) {
289
+ this.error = 'Please enter a server name first';
290
+ return;
291
+ }
292
+
293
+ this.loading = true;
294
+ this.error = '';
295
+ this.success = '';
296
+ this.discoveredTools = [];
297
+ this.oauthFlow = null;
298
+
299
+ try {
300
+ if (this.serverType === 'local') {
301
+ await this.discoverLocalTools();
302
+ } else {
303
+ await this.discoverRemoteTools();
304
+ }
305
+ } catch (error) {
306
+ this.error = `Failed to discover tools: ${error.message}`;
307
+ } finally {
308
+ this.loading = false;
309
+ }
310
+ }
311
+
312
+ async discoverLocalTools() {
313
+ if (!this.localConfig.command) {
314
+ throw new Error('Command is required for local servers');
315
+ }
316
+
317
+ // Use the new dedicated endpoint for testing local servers
318
+ const response = await fetch('/admin/mcp/test-local', {
319
+ method: 'POST',
320
+ headers: { 'Content-Type': 'application/json' },
321
+ body: JSON.stringify({
322
+ name: this.serverName,
323
+ command: this.localConfig.command,
324
+ args: this.localConfig.args,
325
+ env: this.localConfig.env,
326
+ secrets: this.placeholderValues
327
+ })
328
+ });
329
+
330
+ const data = await response.json();
331
+ if (data.success) {
332
+ this.discoveredTools = data.tools || [];
333
+ this.success = data.message;
334
+ } else {
335
+ throw new Error(data.detail || 'Local server test failed');
336
+ }
337
+ }
338
+
339
+ async discoverRemoteTools() {
340
+ if (!this.remoteUrl) {
341
+ throw new Error('URL is required for remote servers');
342
+ }
343
+
344
+ // Use the new backend endpoint that handles OAuth
345
+ console.log('[MCP-Publisher] discoverRemoteTools: starting test-remote', { url: this.remoteUrl, name: this.serverName });
346
+ const response = await fetch('/admin/mcp/test-remote', {
347
+ method: 'POST',
348
+ headers: { 'Content-Type': 'application/json' },
349
+ body: JSON.stringify({
350
+ url: this.remoteUrl,
351
+ name: this.serverName
352
+ })
353
+ });
354
+
355
+ const data = await response.json();
356
+ console.log('[MCP-Publisher] discoverRemoteTools: response', data);
357
+
358
+ if (data.success) {
359
+ // Successfully connected without OAuth
360
+ this.discoveredTools = data.tools || [];
361
+ console.log('[MCP-Publisher] discoverRemoteTools: tools discovered (no OAuth)', this.discoveredTools);
362
+ this.success = data.message;
363
+ // Default remote auth type to 'auto' unless specified
364
+ this.authType = 'auto';
365
+ } else if (data.requires_oauth) {
366
+ // OAuth flow required
367
+ this.oauthFlow = {
368
+ auth_url: data.auth_url,
369
+ flow_id: data.flow_id,
370
+ server_name: data.server_name
371
+ };
372
+ // Ensure we publish with remote auth type when OAuth was needed
373
+ this.authType = 'oauth2';
374
+ this.success = data.message;
375
+ console.log('[MCP-Publisher] discoverRemoteTools: OAuth required', this.oauthFlow);
376
+
377
+ // Automatically open OAuth window
378
+ await this.startOAuthFlow();
379
+ } else {
380
+ // Other error
381
+ throw new Error(data.detail || 'Connection failed');
382
+ }
383
+ }
384
+
385
+ async startOAuthFlow() {
386
+ if (!this.oauthFlow || !this.oauthFlow.auth_url) {
387
+ this.error = 'No OAuth flow available';
388
+ return;
389
+ }
390
+
391
+ this.success = 'Opening OAuth authorization window...';
392
+ console.log('[MCP-Publisher] startOAuthFlow: opening', this.oauthFlow?.auth_url);
393
+
394
+ // Open OAuth window
395
+ const width = 600;
396
+ const height = 700;
397
+ const left = (screen.width - width) / 2;
398
+ const top = (screen.height - height) / 2;
399
+
400
+ this.oauthWindow = window.open(
401
+ this.oauthFlow.auth_url,
402
+ 'oauth_window',
403
+ `width=${width},height=${height},left=${left},top=${top},scrollbars=yes,resizable=yes`
404
+ );
405
+
406
+ if (!this.oauthWindow) {
407
+ this.error = 'Failed to open OAuth window. Please allow popups and try again.';
408
+ return;
409
+ }
410
+
411
+ // Monitor window closure
412
+ const checkClosed = setInterval(() => {
413
+ // Polling for window status; discoveredTools length:', this.discoveredTools?.length
414
+ console.log('[MCP-Publisher] startOAuthFlow: polling window closed?', this.oauthWindow?.closed, 'tools:', this.discoveredTools?.length);
415
+ if (this.oauthWindow && this.oauthWindow.closed) {
416
+ clearInterval(checkClosed);
417
+ if (!this.discoveredTools.length) {
418
+ this.error = 'OAuth window was closed before completion';
419
+ this.loading = false;
420
+ }
421
+ } else {
422
+ if (!this.oauthWindow && this.discoveredTools.length) {
423
+ clearInterval(checkClosed);
424
+ this.success = 'OAuth flow completed successfully, tools discovered.';
425
+ this.loading = false;
426
+ }
427
+ }
428
+ }, 1000);
429
+ }
430
+
431
+ async handleOAuthCallback(event) {
432
+ // Only handle messages from our OAuth window
433
+ if (!this.oauthWindow || event.source !== this.oauthWindow) {
434
+ // Note: admin UI can receive unrelated messages; ignore safely
435
+ // console.debug('[MCP-Publisher] handleOAuthCallback: ignoring message from different source');
436
+ return;
437
+ }
438
+
439
+ if (event.data && event.data.type === 'oauth_callback' && event.data.code) {
440
+ try {
441
+ this.success = 'OAuth authorization received, completing flow...';
442
+ console.log('[MCP-Publisher] handleOAuthCallback: received code/state', { code: !!event.data.code, state: event.data.state });
443
+
444
+ // Close the OAuth window
445
+ this.oauthWindow.close();
446
+ this.oauthWindow = null;
447
+
448
+ // Complete the OAuth flow
449
+ const response = await fetch('/admin/mcp/complete-oauth', {
450
+ method: 'POST',
451
+ headers: { 'Content-Type': 'application/json' },
452
+ body: JSON.stringify({
453
+ server_name: this.oauthFlow.server_name,
454
+ code: event.data.code,
455
+ state: event.data.state
456
+ })
457
+ });
458
+
459
+ const data = await response.json();
460
+ console.log('[MCP-Publisher] handleOAuthCallback: complete-oauth response', data);
461
+
462
+ if (data.success) {
463
+ // Prefer tools from response; if pending, poll once for updated status
464
+ if (Array.isArray(data.tools) && data.tools.length) {
465
+ this.discoveredTools = data.tools;
466
+ console.log('[MCP-Publisher] handleOAuthCallback: tools from complete-oauth', this.discoveredTools);
467
+ } else {
468
+ // Fallback: ask backend for oauth-status and try to extract tools if connected
469
+ try {
470
+ const statusResp = await fetch(`/admin/mcp/oauth-status/${this.oauthFlow.server_name}`);
471
+ if (statusResp.ok) {
472
+ const statusData = await statusResp.json();
473
+ console.log('[MCP-Publisher] handleOAuthCallback: oauth-status', statusData);
474
+ // If server appears connected, re-trigger a lightweight test to fetch tools
475
+ if (statusData && statusData.status === 'connected') {
476
+ const recheck = await fetch('/admin/mcp/test-remote', {
477
+ method: 'POST',
478
+ headers: { 'Content-Type': 'application/json' },
479
+ body: JSON.stringify({ url: this.remoteUrl, name: this.oauthFlow.server_name })
480
+ });
481
+ const reData = await recheck.json();
482
+ console.log('[MCP-Publisher] handleOAuthCallback: recheck test-remote response', reData);
483
+ if (reData.success && Array.isArray(reData.tools)) {
484
+ this.discoveredTools = reData.tools;
485
+ }
486
+ }
487
+ // If still pending, start a short polling loop for connection then fetch tools
488
+ if (!this.discoveredTools.length) {
489
+ await this.pollForToolsAfterOAuth(6);
490
+ }
491
+ }
492
+ } catch (e) {
493
+ console.warn('Post-OAuth tool recheck failed', e);
494
+ }
495
+ }
496
+ this.success = data.message;
497
+ this.oauthFlow = null;
498
+ } else {
499
+ throw new Error(data.detail || 'OAuth completion failed');
500
+ }
501
+ } catch (error) {
502
+ this.error = `OAuth completion failed: ${error.message}`;
503
+ this.oauthFlow = null;
504
+ } finally {
505
+ this.loading = false;
506
+ }
507
+ }
508
+ }
509
+
510
+ async pollForToolsAfterOAuth(maxTries = 6, delayMs = 1000) {
511
+ console.log('[MCP-Publisher] pollForToolsAfterOAuth: start', { maxTries, delayMs, server: this.oauthFlow?.server_name });
512
+ for (let i = 0; i < maxTries && (!this.discoveredTools || this.discoveredTools.length === 0); i++) {
513
+ try {
514
+ const statusResp = await fetch(`/admin/mcp/oauth-status/${this.oauthFlow.server_name}`);
515
+ const status = statusResp.ok ? await statusResp.json() : null;
516
+ console.log(`[MCP-Publisher] pollForToolsAfterOAuth: try ${i+1} status`, status);
517
+ if (status && status.status === 'connected') {
518
+ const recheck = await fetch('/admin/mcp/test-remote', {
519
+ method: 'POST',
520
+ headers: { 'Content-Type': 'application/json' },
521
+ body: JSON.stringify({ url: this.remoteUrl, name: this.oauthFlow.server_name })
522
+ });
523
+ const reData = await recheck.json();
524
+ console.log('[MCP-Publisher] pollForToolsAfterOAuth: test-remote response', reData);
525
+ if (reData.success && Array.isArray(reData.tools) && reData.tools.length) {
526
+ this.discoveredTools = reData.tools;
527
+ console.log('[MCP-Publisher] pollForToolsAfterOAuth: tools discovered', this.discoveredTools);
528
+ return true;
529
+ }
530
+ }
531
+ } catch (e) {
532
+ console.warn('[MCP-Publisher] pollForToolsAfterOAuth: error', e);
533
+ }
534
+ await new Promise(res => setTimeout(res, delayMs));
535
+ }
536
+ console.log('[MCP-Publisher] pollForToolsAfterOAuth: end, tools found?', this.discoveredTools?.length);
537
+ return false;
538
+ }
539
+
540
+ async publishServer() {
541
+ if (!this.serverName.trim()) {
542
+ this.error = "Server name is required";
543
+ return;
544
+ }
545
+
546
+ if (!this.serverDescription.trim()) {
547
+ this.error = "Server description is required";
548
+ return;
549
+ }
550
+
551
+ if (this.discoveredTools.length === 0) {
552
+ this.error = "Please discover tools first";
553
+ return;
554
+ }
555
+
556
+ if (!this.isLoggedIn || !this.authToken) {
557
+ this.error = "Please log in to the registry first (top of page).";
558
+ return;
559
+ }
560
+
561
+ if (!this.registryUrl) {
562
+ this.error = "Registry URL is not configured.";
563
+ return;
564
+ }
565
+
566
+ this.loading = true;
567
+ this.error = "";
568
+ this.success = "";
569
+
570
+ try {
571
+ // Prepare server configuration based on type
572
+ const serverData = {
573
+ name: this.serverName,
574
+ description: this.serverDescription,
575
+ server_type: this.serverType,
576
+ tools: this.discoveredTools,
577
+ transport: this.serverType === 'local' ? 'stdio' : 'http',
578
+ auth_type: this.serverType === 'local' ? 'none' : this.authType
579
+ };
580
+
581
+ // Add type-specific configuration
582
+ if (this.serverType === "local") {
583
+ Object.assign(serverData, {
584
+ command: this.localConfig.command,
585
+ args: this.localConfig.args,
586
+ env: this.localConfig.env
587
+ });
588
+ } else {
589
+ Object.assign(serverData, {
590
+ url: this.remoteUrl
591
+ });
592
+ }
593
+
594
+ // Build registry publish payload (align with registry ContentCreate)
595
+ const publishData = {
596
+ title: this.serverName,
597
+ description: this.serverDescription,
598
+ category: "mcp_server",
599
+ content_type: "mcp_server",
600
+ version: "1.0.0",
601
+ data: serverData,
602
+ tags: ["mcp", "server", this.serverType],
603
+ dependencies: []
604
+ };
605
+
606
+ const response = await fetch(`${this.registryUrl}/publish`, {
607
+ method: "POST",
608
+ headers: {
609
+ "Content-Type": "application/json",
610
+ "Authorization": `Bearer ${this.authToken}`
611
+ },
612
+ body: JSON.stringify(publishData)
613
+ });
614
+
615
+ if (response.ok) {
616
+ const result = await response.json();
617
+ this.success = result.message || `MCP Server "${this.serverName}" published successfully!`;
618
+ this.resetForm();
619
+ } else {
620
+ const errorData = await response.json();
621
+ throw new Error(errorData.detail || "Publishing failed");
622
+ }
623
+ } catch (error) {
624
+ this.error = `Publishing failed: ${error.message}`;
625
+ } finally {
626
+ this.loading = false;
627
+ }
628
+ }
629
+
630
+ resetForm() {
631
+ this.serverName = '';
632
+ this.serverDescription = '';
633
+ this.localConfig = { command: '', args: [], env: {} };
634
+ this.remoteUrl = '';
635
+ this.discoveredTools = [];
636
+ this.oauthFlow = null;
637
+ }
638
+
639
+ _render() {
640
+ return html`
641
+ <div class="mcp-publisher">
642
+ <div class="section">
643
+ <h3>Publish MCP Server</h3>
644
+
645
+ ${this.error ? html`<div class="error">${this.error}</div>` : ''}
646
+ ${this.success ? html`<div class="success">${this.success}</div>` : ''}
647
+ ${this.oauthFlow ? html`<div class="oauth-status">OAuth flow in progress...</div>` : ''}
648
+
649
+ <div class="form-group">
650
+ <label>Server Name</label>
651
+ <input type="text"
652
+ .value=${this.serverName}
653
+ @input=${(e) => this.serverName = e.target.value}
654
+ placeholder="my-awesome-server">
655
+ </div>
656
+
657
+ <div class="form-group">
658
+ <label>Description</label>
659
+ <textarea .value=${this.serverDescription}
660
+ @input=${(e) => this.serverDescription = e.target.value}
661
+ placeholder="Describe what this MCP server does..."></textarea>
662
+ </div>
663
+
664
+ <div class="form-group">
665
+ <label>Server Type</label>
666
+ <div class="checkbox-group">
667
+ <div class="checkbox-item">
668
+ <input type="radio"
669
+ name="serverType"
670
+ value="local"
671
+ .checked=${this.serverType === 'local'}
672
+ @change=${this.handleServerTypeChange}>
673
+ <label>Local (stdio)</label>
674
+ </div>
675
+ <div class="checkbox-item">
676
+ <input type="radio"
677
+ name="serverType"
678
+ value="remote"
679
+ .checked=${this.serverType === 'remote'}
680
+ @change=${this.handleServerTypeChange}>
681
+ <label>Remote (HTTP/SSE)</label>
682
+ </div>
683
+ </div>
684
+ </div>
685
+
686
+ ${this.serverType === 'local' ? this.renderLocalConfig() : this.renderRemoteConfig()}
687
+ ${this.serverType === 'local' && this.requiredPlaceholders.length > 0 ? this.renderPlaceholderInputs() : ''}
688
+
689
+ <div class="form-row">
690
+ <button @click=${this.discoverTools} ?disabled=${this.loading} class="primary">
691
+ ${this.loading ? 'Discovering...' : 'Discover & Connect'}
692
+ </button>
693
+ </div>
694
+
695
+ ${this.discoveredTools.length > 0 ? this.renderToolsPreview() : ''}
696
+ </div>
697
+ </div>
698
+ `;
699
+ }
700
+
701
+ renderLocalConfig() {
702
+ const exampleConfig = {
703
+ command: "npx",
704
+ args: ["-y", "slack-mcp-server@latest"],
705
+ env: {
706
+ "SLACK_MCP_XOXP_TOKEN": "xoxp-YOUR-TOKEN-HERE"
707
+ }
708
+ };
709
+
710
+ return html`
711
+ <div class="form-group">
712
+ <label>Local Server Configuration (JSON)</label>
713
+ <textarea @input=${this.handleLocalConfigChange} placeholder="Enter JSON configuration...">${JSON.stringify(this.localConfig, null, 2)}</textarea>
714
+ <div class="help-text">
715
+ <strong style='color: #ffc107;'>Warning:</strong> Do not paste real secrets here. Use placeholder values. The form below will prompt for the actual secrets, which are not stored in this text block.
716
+ </div>
717
+ <div class="help-text" style="margin-top: 1rem;">
718
+ Configure the command, arguments, and environment variables for your local MCP server.
719
+ </div>
720
+ <div class="json-example">
721
+ Example:
722
+ ${JSON.stringify(exampleConfig, null, 2)}
723
+ </div>
724
+ </div>
725
+ `;
726
+ }
727
+
728
+ renderPlaceholderInputs() {
729
+ return html`
730
+ <div class="section" style="margin-top: 1rem;">
731
+ <h4>Provide Environment Variables</h4>
732
+ <p class="help-text">Provide values for the environment variables found in your configuration. These values are not published to the registry but are stored locally for convenience.</p>
733
+ ${this.requiredPlaceholders.map(placeholder => html`
734
+ <div class="form-group">
735
+ <label for="placeholder-${placeholder}">${placeholder}</label>
736
+ <input type="password"
737
+ id="placeholder-${placeholder}"
738
+ .value=${this.placeholderValues[placeholder] || ''}
739
+ @input=${e => this.placeholderValues = { ...this.placeholderValues, [placeholder]: e.target.value }} autocomplete="off">
740
+ </div>
741
+ `)}
742
+ </div>
743
+ `;
744
+ }
745
+
746
+ renderRemoteConfig() {
747
+ return html`
748
+ <div class="form-group">
749
+ <label>Remote Server URL</label>
750
+ <input type="url"
751
+ .value=${this.remoteUrl}
752
+ @input=${(e) => this.remoteUrl = e.target.value}
753
+ placeholder="https://your-mcp-server.com/mcp">
754
+ <div class="help-text">
755
+ Enter the HTTP endpoint for your remote MCP server. OAuth authentication will be handled automatically if required.
756
+ </div>
757
+ </div>
758
+ `;
759
+ }
760
+
761
+ renderToolsPreview() {
762
+ return html`
763
+ <div class="tools-preview">
764
+ <h4>Discovered Tools (${this.discoveredTools.length})</h4>
765
+ <div class="form-row">
766
+ <button class="primary" @click=${this.publishServer} ?disabled=${this.loading}>
767
+ ${this.loading ? 'Publishing...' : 'Publish Server'}
768
+ </button>
769
+ </div>
770
+ ${this.discoveredTools.map(tool => html`
771
+ <div class="tool-item">
772
+ <div class="tool-name">${tool.name}</div>
773
+ </div>
774
+ `)}
775
+ </div>
776
+ `;
777
+ }
778
+ }
779
+
780
+ customElements.define('mcp-publisher', McpPublisher);