imcp 0.0.17 → 0.0.18
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.
- package/dist/cli/commands/serve.js +2 -1
- package/dist/core/installers/clients/BaseClientInstaller.d.ts +25 -2
- package/dist/core/installers/clients/BaseClientInstaller.js +121 -0
- package/dist/core/installers/clients/ClineInstaller.d.ts +1 -6
- package/dist/core/installers/clients/ClineInstaller.js +1 -94
- package/dist/core/installers/clients/GithubCopilotInstaller.d.ts +1 -6
- package/dist/core/installers/clients/GithubCopilotInstaller.js +1 -94
- package/dist/core/installers/clients/MSRooCodeInstaller.d.ts +1 -5
- package/dist/core/installers/clients/MSRooCodeInstaller.js +1 -94
- package/dist/core/loaders/ConfigurationLoader.d.ts +4 -1
- package/dist/core/loaders/ConfigurationLoader.js +24 -3
- package/dist/core/loaders/ConfigurationProvider.d.ts +4 -1
- package/dist/core/loaders/ConfigurationProvider.js +13 -4
- package/dist/core/loaders/ServerSchemaLoader.d.ts +15 -4
- package/dist/core/loaders/ServerSchemaLoader.js +86 -20
- package/dist/core/loaders/ServerSchemaProvider.d.ts +2 -5
- package/dist/core/loaders/ServerSchemaProvider.js +32 -62
- package/dist/core/metadatas/types.d.ts +3 -2
- package/dist/core/onboard/FeedOnboardService.d.ts +14 -7
- package/dist/core/onboard/FeedOnboardService.js +214 -129
- package/dist/core/onboard/OnboardProcessor.d.ts +7 -1
- package/dist/core/onboard/OnboardProcessor.js +52 -8
- package/dist/core/onboard/OnboardStatus.d.ts +6 -1
- package/dist/core/onboard/OnboardStatusManager.d.ts +70 -24
- package/dist/core/onboard/OnboardStatusManager.js +230 -46
- package/dist/core/validators/FeedValidator.d.ts +7 -2
- package/dist/core/validators/FeedValidator.js +61 -7
- package/dist/core/validators/StdioServerValidator.js +84 -32
- package/dist/services/MCPManager.d.ts +2 -1
- package/dist/services/MCPManager.js +5 -1
- package/dist/services/ServerService.js +2 -2
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +10 -0
- package/dist/web/public/js/modal/installation.js +1 -1
- package/dist/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +41 -9
- package/dist/web/public/js/onboard/formProcessor.js +200 -34
- package/dist/web/public/js/onboard/index.js +2 -2
- package/dist/web/public/js/onboard/publishHandler.js +30 -22
- package/dist/web/public/js/onboard/templates.js +34 -40
- package/dist/web/public/js/onboard/uiHandlers.js +175 -84
- package/dist/web/public/js/onboard/validationHandlers.js +147 -64
- package/dist/web/public/js/serverCategoryDetails.js +19 -4
- package/dist/web/public/js/serverCategoryList.js +13 -1
- package/dist/web/public/onboard.html +1 -1
- package/dist/web/server.js +19 -6
- package/package.json +1 -1
|
@@ -172,14 +172,26 @@ export async function submitForm(event, activeTab, currentSelectedCategoryData =
|
|
|
172
172
|
showToast('No existing category selected or category data is missing.', 'error');
|
|
173
173
|
return;
|
|
174
174
|
}
|
|
175
|
-
const newServersData = formDataToFeedConfiguration(formElement, true, currentSelectedCategoryData);
|
|
176
175
|
|
|
177
|
-
|
|
176
|
+
// Get the complete state of servers and their requirements from the current form.
|
|
177
|
+
// The `true` flag for `forExistingCategoryTab` tells formDataToFeedConfiguration
|
|
178
|
+
// to only process server data and not category-level fields from this form.
|
|
179
|
+
// It should return ALL servers currently in this form, including modified adhoc and new ones.
|
|
180
|
+
const formDerivedData = formDataToFeedConfiguration(formElement, true, currentSelectedCategoryData);
|
|
178
181
|
|
|
179
|
-
|
|
182
|
+
// Start with a deep clone of the original category data (for name, description, etc.)
|
|
183
|
+
feedConfiguration = JSON.parse(JSON.stringify(currentSelectedCategoryData));
|
|
180
184
|
|
|
185
|
+
// Replace the mcpServers list entirely with what was parsed from the form.
|
|
186
|
+
// This ensures that the list reflects the current state of the form (original read-only, modified adhoc, new).
|
|
187
|
+
feedConfiguration.mcpServers = formDerivedData.mcpServers || [];
|
|
188
|
+
|
|
189
|
+
// Merge requirements: start with original requirements, then add any new ones from the form.
|
|
190
|
+
// `formDerivedData.requirements` should ideally only contain requirements introduced by
|
|
191
|
+
// servers in the current form that are not already in `currentSelectedCategoryData.requirements`.
|
|
192
|
+
// The existing logic for merging requirements seems okay if `formDerivedData.requirements` is correctly populated.
|
|
181
193
|
const existingReqKeys = new Set((feedConfiguration.requirements || []).map(r => `${r.type}|${r.name}|${r.version}`));
|
|
182
|
-
(
|
|
194
|
+
(formDerivedData.requirements || []).forEach(newReq => {
|
|
183
195
|
const reqKey = `${newReq.type}|${newReq.name}|${newReq.version}`;
|
|
184
196
|
if (!existingReqKeys.has(reqKey)) {
|
|
185
197
|
feedConfiguration.requirements.push(newReq);
|
|
@@ -193,7 +205,7 @@ export async function submitForm(event, activeTab, currentSelectedCategoryData =
|
|
|
193
205
|
}
|
|
194
206
|
|
|
195
207
|
try {
|
|
196
|
-
|
|
208
|
+
const response = await fetch('/api/categories/onboard', {
|
|
197
209
|
method: 'POST',
|
|
198
210
|
headers: { 'Content-Type': 'application/json' },
|
|
199
211
|
body: JSON.stringify({
|
|
@@ -208,7 +220,7 @@ export async function submitForm(event, activeTab, currentSelectedCategoryData =
|
|
|
208
220
|
}
|
|
209
221
|
|
|
210
222
|
const result = await response.json();
|
|
211
|
-
console.log('Form submitted successfully:', result);
|
|
223
|
+
// console.log('Form submitted successfully:', result);
|
|
212
224
|
showToast('Form submitted successfully! See console for operation details.', 'success');
|
|
213
225
|
} catch (error) {
|
|
214
226
|
console.error('Error submitting form:', error);
|
|
@@ -327,6 +339,15 @@ export function formDataToFeedConfiguration(formElement, forExistingCategoryTab
|
|
|
327
339
|
mcpServers: []
|
|
328
340
|
};
|
|
329
341
|
|
|
342
|
+
let serversListId;
|
|
343
|
+
if (formElement.id === 'onboardForm') {
|
|
344
|
+
serversListId = 'serversList';
|
|
345
|
+
} else if (formElement.id === 'onboardServerForm') {
|
|
346
|
+
serversListId = 'existingCategoryServersList';
|
|
347
|
+
} else {
|
|
348
|
+
console.warn('[formDataToFeedConfiguration] Could not determine serversListId from formElement.id:', formElement.id);
|
|
349
|
+
}
|
|
350
|
+
|
|
330
351
|
if (!forExistingCategoryTab) {
|
|
331
352
|
feedConfiguration.name = currentFormData.get('name') || '';
|
|
332
353
|
feedConfiguration.displayName = currentFormData.get('displayName') || '';
|
|
@@ -420,8 +441,13 @@ export function formDataToFeedConfiguration(formElement, forExistingCategoryTab
|
|
|
420
441
|
}
|
|
421
442
|
}
|
|
422
443
|
|
|
423
|
-
serverDataMap.
|
|
444
|
+
// console.log('[formDataToFeedConfiguration] serverDataMap keys before processing:', Array.from(serverDataMap.keys())); // DEBUG
|
|
445
|
+
// console.log('[formDataToFeedConfiguration] serverDataMap size:', serverDataMap.size); // DEBUG
|
|
446
|
+
|
|
447
|
+
// Process servers found in the form data (new/adhoc or edited original servers)
|
|
448
|
+
serverDataMap.forEach((serverRaw, serverIndex) => {
|
|
424
449
|
let mcpServer;
|
|
450
|
+
// ... (existing logic to build mcpServer from serverRaw)
|
|
425
451
|
if (serverRaw.mode === 'sse') {
|
|
426
452
|
mcpServer = {
|
|
427
453
|
name: serverRaw.name,
|
|
@@ -436,7 +462,7 @@ export function formDataToFeedConfiguration(formElement, forExistingCategoryTab
|
|
|
436
462
|
requirements: []
|
|
437
463
|
}
|
|
438
464
|
};
|
|
439
|
-
} else {
|
|
465
|
+
} else { // stdio or other modes
|
|
440
466
|
mcpServer = {
|
|
441
467
|
name: serverRaw.name,
|
|
442
468
|
description: serverRaw.description,
|
|
@@ -446,7 +472,7 @@ export function formDataToFeedConfiguration(formElement, forExistingCategoryTab
|
|
|
446
472
|
installation: {
|
|
447
473
|
command: serverRaw.installation.command,
|
|
448
474
|
args: serverRaw.installation.args || [],
|
|
449
|
-
env: {}
|
|
475
|
+
env: {} // Initialize env
|
|
450
476
|
},
|
|
451
477
|
dependencies: {
|
|
452
478
|
requirements: []
|
|
@@ -466,7 +492,7 @@ export function formDataToFeedConfiguration(formElement, forExistingCategoryTab
|
|
|
466
492
|
if (Object.keys(envVars).length > 0) {
|
|
467
493
|
mcpServer.installation.env = envVars;
|
|
468
494
|
} else {
|
|
469
|
-
delete mcpServer.installation.env;
|
|
495
|
+
delete mcpServer.installation.env; // Clean up if no env vars
|
|
470
496
|
}
|
|
471
497
|
}
|
|
472
498
|
|
|
@@ -500,6 +526,7 @@ export function formDataToFeedConfiguration(formElement, forExistingCategoryTab
|
|
|
500
526
|
assetName: reqRaw.registry.local.assetName,
|
|
501
527
|
};
|
|
502
528
|
}
|
|
529
|
+
// Clean up empty registry objects
|
|
503
530
|
if (fullRequirementConfig.registry.githubRelease && !fullRequirementConfig.registry.githubRelease.repository) delete fullRequirementConfig.registry.githubRelease;
|
|
504
531
|
if (fullRequirementConfig.registry.artifacts && !fullRequirementConfig.registry.artifacts.registryUrl) delete fullRequirementConfig.registry.artifacts;
|
|
505
532
|
if (fullRequirementConfig.registry.local && !fullRequirementConfig.registry.local.localPath) delete fullRequirementConfig.registry.local;
|
|
@@ -514,17 +541,103 @@ export function formDataToFeedConfiguration(formElement, forExistingCategoryTab
|
|
|
514
541
|
|
|
515
542
|
const reqKey = `${reqRaw.type}|${reqRaw.name}|${reqRaw.version}`;
|
|
516
543
|
if (!globalRequirementsMap.has(reqKey)) {
|
|
517
|
-
const { order, ...reqConfigForGlobal } = fullRequirementConfig;
|
|
544
|
+
const { order, ...reqConfigForGlobal } = fullRequirementConfig; // Exclude order for global list
|
|
518
545
|
globalRequirementsMap.set(reqKey, reqConfigForGlobal);
|
|
519
546
|
}
|
|
520
547
|
});
|
|
521
548
|
if (mcpServer.dependencies.requirements.length === 0) {
|
|
522
|
-
delete mcpServer.dependencies;
|
|
549
|
+
delete mcpServer.dependencies; // Clean up if no requirements
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Attempt to retrieve and add systemTags from the DOM element,
|
|
553
|
+
// ONLY if we are in the 'Create Server in Existing Category' tab context.
|
|
554
|
+
// For 'Create New Category' tab, systemTags should not be assigned from DOM.
|
|
555
|
+
if (formElement.id === 'onboardServerForm' && serversListId) {
|
|
556
|
+
const selector = `#${serversListId} .server-item[data-index="${serverIndex}"]`;
|
|
557
|
+
const serverItemElement = document.querySelector(selector);
|
|
558
|
+
if (serverItemElement && serverItemElement.dataset.systemTags) {
|
|
559
|
+
try {
|
|
560
|
+
mcpServer.systemTags = JSON.parse(serverItemElement.dataset.systemTags);
|
|
561
|
+
} catch (e) {
|
|
562
|
+
console.error(`[formDataToFeedConfiguration] Error parsing systemTags for server index ${serverIndex} ('${mcpServer.name}') on ${formElement.id}:`, e, serverItemElement.dataset.systemTags);
|
|
563
|
+
mcpServer.systemTags = { parseError: true };
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
// If no dataset.systemTags or not onboardServerForm, mcpServer.systemTags remains undefined.
|
|
567
|
+
} else {
|
|
568
|
+
// Ensure systemTags is not carried over if not in the correct context or not present in dataset
|
|
569
|
+
delete mcpServer.systemTags;
|
|
523
570
|
}
|
|
524
571
|
|
|
525
572
|
feedConfiguration.mcpServers.push(mcpServer);
|
|
526
573
|
});
|
|
527
574
|
|
|
575
|
+
|
|
576
|
+
// If processing for an existing category, ensure original non-adhoc servers are included
|
|
577
|
+
// if they weren't picked up by the form (e.g., because they were read-only and disabled).
|
|
578
|
+
if (forExistingCategoryTab && baseCategoryData && Array.isArray(baseCategoryData.mcpServers)) {
|
|
579
|
+
baseCategoryData.mcpServers.forEach(originalServer => {
|
|
580
|
+
// Check if this original server (by name) is already in our processed list.
|
|
581
|
+
// We use name as the primary identifier for existing servers.
|
|
582
|
+
const isAlreadyProcessed = feedConfiguration.mcpServers.some(
|
|
583
|
+
processedServer => processedServer.name === originalServer.name
|
|
584
|
+
);
|
|
585
|
+
|
|
586
|
+
if (!isAlreadyProcessed && (!originalServer.systemTags || originalServer.systemTags.adhoc !== 'true')) {
|
|
587
|
+
// This original server was not in the form data (likely read-only) and is not adhoc. Add it.
|
|
588
|
+
// Ensure its requirements are also added to the global list if not already present.
|
|
589
|
+
feedConfiguration.mcpServers.push(JSON.parse(JSON.stringify(originalServer))); // Add a clone
|
|
590
|
+
|
|
591
|
+
if (originalServer.dependencies && Array.isArray(originalServer.dependencies.requirements)) {
|
|
592
|
+
originalServer.dependencies.requirements.forEach(req => {
|
|
593
|
+
// We need the full requirement definition for the global map.
|
|
594
|
+
// This might require looking up the full definition from baseCategoryData.requirements
|
|
595
|
+
// if originalServer.dependencies.requirements only has name/version/order.
|
|
596
|
+
// For simplicity here, we assume baseCategoryData.requirements contains full definitions.
|
|
597
|
+
const originalGlobalReq = baseCategoryData.requirements?.find(
|
|
598
|
+
gReq => gReq.name === req.name && gReq.type && gReq.version === req.version // Type might not be in server's dep list
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
if (originalGlobalReq) {
|
|
602
|
+
const reqKey = `${originalGlobalReq.type}|${originalGlobalReq.name}|${originalGlobalReq.version}`;
|
|
603
|
+
if (!globalRequirementsMap.has(reqKey)) {
|
|
604
|
+
globalRequirementsMap.set(reqKey, JSON.parse(JSON.stringify(originalGlobalReq)));
|
|
605
|
+
}
|
|
606
|
+
} else {
|
|
607
|
+
// Fallback if full definition not found, add what we have, though type might be missing.
|
|
608
|
+
// This part might need refinement based on actual structure of baseCategoryData.requirements
|
|
609
|
+
// and how server-specific dependencies link to global ones.
|
|
610
|
+
// The current server-specific req usually has name, version, order. Type is global.
|
|
611
|
+
// We need to find the type from the global requirements list.
|
|
612
|
+
// This logic assumes that if a server has a dependency, its full definition (including type)
|
|
613
|
+
// must exist in the global `baseCategoryData.requirements`.
|
|
614
|
+
|
|
615
|
+
// Let's find the type from baseCategoryData.requirements based on name and version
|
|
616
|
+
const matchingGlobalReqForType = baseCategoryData.requirements?.find(
|
|
617
|
+
gReq => gReq.name === req.name && gReq.version === req.version
|
|
618
|
+
);
|
|
619
|
+
if (matchingGlobalReqForType && matchingGlobalReqForType.type) {
|
|
620
|
+
const reqKey = `${matchingGlobalReqForType.type}|${req.name}|${req.version}`;
|
|
621
|
+
if (!globalRequirementsMap.has(reqKey)) {
|
|
622
|
+
// Construct a basic global requirement if not found, though ideally it should exist.
|
|
623
|
+
globalRequirementsMap.set(reqKey, {
|
|
624
|
+
name: req.name,
|
|
625
|
+
version: req.version,
|
|
626
|
+
type: matchingGlobalReqForType.type
|
|
627
|
+
// Other fields like alias, registry would be missing here if not in matchingGlobalReqForType
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
} else {
|
|
631
|
+
console.warn(`Could not find full global requirement definition (or type) for ${req.name} v${req.version} from original server ${originalServer.name}`);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
|
|
528
641
|
feedConfiguration.requirements = Array.from(globalRequirementsMap.values());
|
|
529
642
|
|
|
530
643
|
// Validate the entire configuration
|
|
@@ -668,47 +781,95 @@ export function populateForm(feedConfig, formId = 'onboardForm', renderServersAs
|
|
|
668
781
|
clearEnvCountersForTab(serversListId);
|
|
669
782
|
clearServerRequirementCountersForTab(serversListId);
|
|
670
783
|
|
|
671
|
-
(feedConfig.mcpServers || []).forEach((
|
|
784
|
+
(feedConfig.mcpServers || []).forEach((serverData) => { // Renamed 'server' to 'serverData' for clarity
|
|
672
785
|
// Get the correct current index for this tab before adding the server
|
|
673
786
|
const currentServerIndex = getServerCounter(serversListId);
|
|
674
787
|
// window.addServer will increment the counter for serversListId internally
|
|
675
|
-
|
|
676
|
-
|
|
788
|
+
// It also returns the server item, but we'll query it again for safety after DOM manipulation.
|
|
789
|
+
window.addServer(serversListId, renderServersAsReadOnly, serverData);
|
|
790
|
+
|
|
791
|
+
// After addServer, the server item should exist in the DOM.
|
|
792
|
+
const serverItem = currentForm.querySelector(`#${serversListId} .server-item[data-index="${currentServerIndex}"]`);
|
|
793
|
+
|
|
794
|
+
if (serverItem) {
|
|
795
|
+
if (formId === 'onboardServerForm') {
|
|
796
|
+
if (serverData.systemTags?.adhoc === "true") {
|
|
797
|
+
// Case 1: JSON data explicitly marks it as adhoc. Respect this.
|
|
798
|
+
serverItem.dataset.systemTags = JSON.stringify(serverData.systemTags);
|
|
799
|
+
// console.log(`[populateForm] Server ${serverData.name || currentServerIndex} in ${formId} retains adhoc status from JSON.`);
|
|
800
|
+
} else {
|
|
801
|
+
// Case 2: JSON data does NOT explicitly mark it adhoc.
|
|
802
|
+
// Check if it was an original server from the category.
|
|
803
|
+
// state.originalServerNamesForFormPopulation is set when toggling from JSON to Form view for an existing category.
|
|
804
|
+
if (state.originalServerNamesForFormPopulation && serverData.name && state.originalServerNamesForFormPopulation.has(serverData.name)) {
|
|
805
|
+
// It's an original server from the category, now treated as adhoc because it passed through JSON view.
|
|
806
|
+
serverItem.dataset.systemTags = JSON.stringify({ adhoc: "true" });
|
|
807
|
+
// console.log(`[populateForm] Original server ${serverData.name} in ${formId} marked as adhoc after JSON view.`);
|
|
808
|
+
} else {
|
|
809
|
+
// It's a new server (not in original list) and JSON didn't mark it adhoc.
|
|
810
|
+
// Or, it's an original server but its JSON representation explicitly removed/lacked adhoc tag.
|
|
811
|
+
// Ensure it's NOT adhoc.
|
|
812
|
+
// If serverData.systemTags exists but doesn't have adhoc:true, preserve those other tags.
|
|
813
|
+
if (serverData.systemTags && Object.keys(serverData.systemTags).length > 0) {
|
|
814
|
+
const newTags = { ...serverData.systemTags };
|
|
815
|
+
delete newTags.adhoc; // Ensure adhoc is not true
|
|
816
|
+
if (Object.keys(newTags).length > 0) {
|
|
817
|
+
serverItem.dataset.systemTags = JSON.stringify(newTags);
|
|
818
|
+
} else {
|
|
819
|
+
delete serverItem.dataset.systemTags;
|
|
820
|
+
}
|
|
821
|
+
} else {
|
|
822
|
+
delete serverItem.dataset.systemTags;
|
|
823
|
+
}
|
|
824
|
+
// console.log(`[populateForm] Server ${serverData.name || currentServerIndex} in ${formId} is NOT marked adhoc.`);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
} else {
|
|
828
|
+
// For 'onboardForm' (Create Category tab), or if serverData had systemTags not making it adhoc.
|
|
829
|
+
// If serverData has systemTags, reflect them. Otherwise, ensure no systemTags.
|
|
830
|
+
if (serverData.systemTags) {
|
|
831
|
+
serverItem.dataset.systemTags = JSON.stringify(serverData.systemTags);
|
|
832
|
+
} else {
|
|
833
|
+
delete serverItem.dataset.systemTags;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
// Continue with populating fields using serverData
|
|
677
838
|
const serverNameInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].name"]`);
|
|
678
|
-
if (serverNameInput) serverNameInput.value =
|
|
839
|
+
if (serverNameInput) serverNameInput.value = serverData.name || '';
|
|
679
840
|
|
|
680
841
|
const modeInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].mode"]`);
|
|
681
842
|
if (modeInput) {
|
|
682
|
-
modeInput.value =
|
|
843
|
+
modeInput.value = serverData.mode || 'stdio';
|
|
683
844
|
if (typeof window.renderInstallationConfig === 'function') {
|
|
684
|
-
window.renderInstallationConfig(currentServerIndex, serversListId,
|
|
845
|
+
window.renderInstallationConfig(currentServerIndex, serversListId, serverData.mode || 'stdio', renderServersAsReadOnly, serverData.installation);
|
|
685
846
|
}
|
|
686
847
|
}
|
|
687
848
|
const descInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].description"]`);
|
|
688
|
-
if (descInput) descInput.value =
|
|
849
|
+
if (descInput) descInput.value = serverData.description || '';
|
|
689
850
|
|
|
690
|
-
if (
|
|
851
|
+
if (serverData.schemas) {
|
|
691
852
|
const schemaPathEl = document.getElementById(`schema-path-${currentServerIndex}`);
|
|
692
|
-
if (schemaPathEl) schemaPathEl.value =
|
|
853
|
+
if (schemaPathEl) schemaPathEl.value = serverData.schemas;
|
|
693
854
|
}
|
|
694
|
-
if (
|
|
855
|
+
if (serverData.repository) {
|
|
695
856
|
const repoInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].repository"]`);
|
|
696
|
-
if (repoInput) repoInput.value =
|
|
857
|
+
if (repoInput) repoInput.value = serverData.repository;
|
|
697
858
|
}
|
|
698
859
|
|
|
699
|
-
if (
|
|
700
|
-
if (
|
|
860
|
+
if (serverData.installation) {
|
|
861
|
+
if (serverData.mode === 'sse') {
|
|
701
862
|
const urlInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].installation.url"]`);
|
|
702
|
-
if (urlInput) urlInput.value =
|
|
863
|
+
if (urlInput) urlInput.value = serverData.installation.url || '';
|
|
703
864
|
} else {
|
|
704
865
|
const cmdInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].installation.command"]`);
|
|
705
|
-
if (cmdInput) cmdInput.value =
|
|
706
|
-
if (
|
|
866
|
+
if (cmdInput) cmdInput.value = serverData.installation.command || '';
|
|
867
|
+
if (serverData.installation.args && Array.isArray(serverData.installation.args)) {
|
|
707
868
|
const argsInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].installation.args"]`);
|
|
708
|
-
if (argsInput) argsInput.value =
|
|
869
|
+
if (argsInput) argsInput.value = serverData.installation.args.join(', ');
|
|
709
870
|
}
|
|
710
|
-
if (
|
|
711
|
-
Object.entries(
|
|
871
|
+
if (serverData.installation.env) {
|
|
872
|
+
Object.entries(serverData.installation.env).forEach(([envName, envConfig]) => {
|
|
712
873
|
const currentEnvIndex = window.addEnvVariable(currentServerIndex, serversListId, renderServersAsReadOnly);
|
|
713
874
|
|
|
714
875
|
const nameInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].installation.env[${currentEnvIndex}].name"]`);
|
|
@@ -730,8 +891,12 @@ export function populateForm(feedConfig, formId = 'onboardForm', renderServersAs
|
|
|
730
891
|
}
|
|
731
892
|
}
|
|
732
893
|
|
|
733
|
-
if (
|
|
734
|
-
server
|
|
894
|
+
if (serverData.dependencies && serverData.dependencies.requirements) {
|
|
895
|
+
// Determine if this server's requirements should be effectively read-only
|
|
896
|
+
const serverIsEffectivelyReadOnlyForReqs = renderServersAsReadOnly && !(serverData.systemTags?.adhoc === 'true');
|
|
897
|
+
// console.log(`[populateForm] Server: ${serverData.name}, Adhoc: ${serverData.systemTags?.adhoc === 'true'}, renderServersAsReadOnly: ${renderServersAsReadOnly}, Calculated serverIsEffectivelyReadOnlyForReqs: ${serverIsEffectivelyReadOnlyForReqs}`); // DEBUG
|
|
898
|
+
|
|
899
|
+
serverData.dependencies.requirements.forEach((depReq) => {
|
|
735
900
|
// Find requirement by name only, as requested.
|
|
736
901
|
const fullReq = (feedConfig.requirements || []).find(r => r.name === depReq.name);
|
|
737
902
|
if (!fullReq) {
|
|
@@ -739,7 +904,8 @@ export function populateForm(feedConfig, formId = 'onboardForm', renderServersAs
|
|
|
739
904
|
return;
|
|
740
905
|
}
|
|
741
906
|
|
|
742
|
-
|
|
907
|
+
// Pass serverIsEffectivelyReadOnlyForReqs to addServerRequirement
|
|
908
|
+
const currentReqIndex = window.addServerRequirement(currentServerIndex, serversListId, serverIsEffectivelyReadOnlyForReqs);
|
|
743
909
|
|
|
744
910
|
currentForm.querySelector(`[name="servers[${currentServerIndex}].requirements[${currentReqIndex}].name"]`).value = fullReq.name || '';
|
|
745
911
|
currentForm.querySelector(`[name="servers[${currentServerIndex}].requirements[${currentReqIndex}].type"]`).value = fullReq.type || '';
|
|
@@ -293,11 +293,11 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|
|
293
293
|
// addRequirementBtn.addEventListener('click', addRequirement); // addRequirement from uiHandlers.js
|
|
294
294
|
// }
|
|
295
295
|
|
|
296
|
-
const addServerBtnNewCategory = document.getElementById('
|
|
296
|
+
const addServerBtnNewCategory = document.getElementById('addServerBtnNewCategory'); // For "Create New Category" tab
|
|
297
297
|
if (addServerBtnNewCategory) {
|
|
298
298
|
// addServer in uiHandlers defaults serversListId to 'serversList', which is correct for this button.
|
|
299
299
|
// It also defaults isReadOnly to false.
|
|
300
|
-
addServerBtnNewCategory.addEventListener('click', () => addServer());
|
|
300
|
+
addServerBtnNewCategory.addEventListener('click', () => window.addServer('serversList', false, null));
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
const addServerBtnExistingCategory = document.getElementById('addServerToExistingCategoryBtn'); // For "Create Server in Existing Category" tab
|
|
@@ -22,6 +22,11 @@ export async function handlePublish(event, activeTab, currentSelectedCategoryDat
|
|
|
22
22
|
const { panelId, contentId, formId, validateButtonId, publishButtonId } = getElementIdsByTab(activeTab);
|
|
23
23
|
|
|
24
24
|
const statusContentElement = document.getElementById(contentId);
|
|
25
|
+
// Ensure the progress toggle listener is attached when handlePublish is called
|
|
26
|
+
if (typeof ensureProgressToggleListener === 'function') { // Check if imported correctly
|
|
27
|
+
ensureProgressToggleListener(statusContentElement);
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
const onboardForm = document.getElementById(formId);
|
|
26
31
|
const validateButton = document.getElementById(validateButtonId);
|
|
27
32
|
const publishButton = document.getElementById(publishButtonId);
|
|
@@ -48,21 +53,10 @@ export async function handlePublish(event, activeTab, currentSelectedCategoryDat
|
|
|
48
53
|
if (forExistingCategoryTab && currentSelectedCategoryData) {
|
|
49
54
|
finalFeedConfiguration = JSON.parse(JSON.stringify(currentSelectedCategoryData)); // Deep clone
|
|
50
55
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const existingReqKeys = new Set((finalFeedConfiguration.requirements || []).map(r => `${r.type}|${r.name}|${r.version}`));
|
|
56
|
-
(newServersData.requirements || []).forEach(newReq => {
|
|
57
|
-
const reqKey = `${newReq.type}|${newReq.name}|${newReq.version}`;
|
|
58
|
-
if (!existingReqKeys.has(reqKey)) {
|
|
59
|
-
if (!finalFeedConfiguration.requirements) {
|
|
60
|
-
finalFeedConfiguration.requirements = [];
|
|
61
|
-
}
|
|
62
|
-
finalFeedConfiguration.requirements.push(newReq);
|
|
63
|
-
existingReqKeys.add(reqKey);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
56
|
+
// Replace MCP Servers and Requirements with the current state from the form (newServersData)
|
|
57
|
+
// This aligns with the logic in handleValidation
|
|
58
|
+
finalFeedConfiguration.mcpServers = newServersData.mcpServers || [];
|
|
59
|
+
finalFeedConfiguration.requirements = newServersData.requirements || [];
|
|
66
60
|
} else {
|
|
67
61
|
finalFeedConfiguration = newServersData;
|
|
68
62
|
}
|
|
@@ -87,11 +81,18 @@ export async function handlePublish(event, activeTab, currentSelectedCategoryDat
|
|
|
87
81
|
|
|
88
82
|
if (result.success && result.data) {
|
|
89
83
|
updateOperationDisplay(result.data, statusContentElement);
|
|
90
|
-
const categoryName =
|
|
84
|
+
const categoryName = finalFeedConfiguration.name; // Use the original category name for polling
|
|
85
|
+
const operationTypeForPolling = 'FULL_ONBOARDING'; // Publish always initiates 'FULL_ONBOARDING'
|
|
91
86
|
const initialStatus = result.data.status;
|
|
92
87
|
|
|
93
|
-
if (categoryName && initialStatus !== 'COMPLETED' && initialStatus !== 'FAILED' && initialStatus !== '
|
|
94
|
-
pollingIntervalId = setInterval(() =>
|
|
88
|
+
if (categoryName && typeof initialStatus === 'string' && initialStatus.toUpperCase() !== 'COMPLETED' && initialStatus.toUpperCase() !== 'FAILED' && initialStatus.toUpperCase() !== 'SUCCEEDED') {
|
|
89
|
+
pollingIntervalId = setInterval(async () => {
|
|
90
|
+
const shouldContinue = await pollOperationStatus(categoryName, contentId, validateButtonId, publishButtonId, 'publish', operationTypeForPolling);
|
|
91
|
+
if (!shouldContinue) {
|
|
92
|
+
clearInterval(pollingIntervalId);
|
|
93
|
+
pollingIntervalId = null;
|
|
94
|
+
}
|
|
95
|
+
}, POLLING_INTERVAL);
|
|
95
96
|
} else {
|
|
96
97
|
// Restore buttons to their original state fully
|
|
97
98
|
publishButton.disabled = false;
|
|
@@ -100,15 +101,22 @@ export async function handlePublish(event, activeTab, currentSelectedCategoryDat
|
|
|
100
101
|
validateButton.disabled = false;
|
|
101
102
|
validateButton.innerHTML = "<i class='bx bx-check-shield mr-2'></i>Validate";
|
|
102
103
|
validateButton.classList.remove('opacity-50');
|
|
103
|
-
if (
|
|
104
|
+
if (initialStatus.toUpperCase() === 'COMPLETED' || initialStatus.toUpperCase() === 'SUCCEEDED') {
|
|
104
105
|
showToast(result.data.message || 'Publish successful!', 'success');
|
|
105
|
-
} else if (
|
|
106
|
+
} else if (initialStatus.toUpperCase() === 'FAILED') {
|
|
106
107
|
showToast(result.data.errorMessage || result.data.message || 'Publish failed.', 'error');
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
} else {
|
|
110
|
-
|
|
111
|
-
|
|
111
|
+
// Handle cases where result.success is false or result.data is missing
|
|
112
|
+
const errorMessage = (result.data && result.data.message) || result.error || result.message || 'Publish request failed: Unknown error';
|
|
113
|
+
if (result.data) { // If data is present (even if success is false), update display
|
|
114
|
+
updateOperationDisplay(result.data, statusContentElement);
|
|
115
|
+
} else {
|
|
116
|
+
statusContentElement.innerHTML = `<p class="text-red-500">${errorMessage}</p>`;
|
|
117
|
+
}
|
|
118
|
+
showToast(errorMessage, 'error');
|
|
119
|
+
|
|
112
120
|
// Restore buttons to their original state fully
|
|
113
121
|
publishButton.disabled = false;
|
|
114
122
|
publishButton.innerHTML = "<i class='bx bx-cloud-upload mr-2'></i>Publish";
|
|
@@ -10,31 +10,24 @@
|
|
|
10
10
|
* @returns {string} HTML string for the server item.
|
|
11
11
|
*/
|
|
12
12
|
export const serverTemplate = (serverIndex, isReadOnly = false, serverData = null, serversListId = 'serversList') => {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
const
|
|
13
|
+
const isServerAdhoc = serverData?.systemTags?.adhoc === 'true';
|
|
14
|
+
// Server is fully editable if the category isn't read-only OR if the server itself is adhoc.
|
|
15
|
+
const makeServerFullyEditable = !isReadOnly || isServerAdhoc;
|
|
16
|
+
// DEBUG: console.log(`[serverTemplate] ServerIndex: ${serverIndex}, isContextReadOnly(isReadOnly): ${isReadOnly}, isServerAdhoc: ${isServerAdhoc}, makeServerFullyEditable: ${makeServerFullyEditable}, flag for addReq/addEnv: ${!makeServerFullyEditable}`);
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
// e.g. onclick="removeServer(${serverIndex}, 'serversList')" - this needs to be dynamic.
|
|
26
|
-
// For simplicity, let's assume the global functions in uiHandlers.js will determine the active list.
|
|
27
|
-
// Or, the functions like removeServer are already modified to accept serversListId.
|
|
28
|
-
// The `addServer` in `uiHandlers.js` is called with `serversListId`.
|
|
29
|
-
// The buttons here call global functions. These global functions (removeServer, addEnvVariable, etc.)
|
|
30
|
-
// will need to be aware of the active serversListId.
|
|
31
|
-
// This can be done by having uiHandlers.js set a global 'activeServersListId' or by modifying
|
|
32
|
-
// each function to accept it and ensuring the HTML onclicks pass it.
|
|
33
|
-
// The latter is cleaner. The `onclick` in HTML should be `removeServer(${serverIndex}, 'CURRENT_LIST_ID')`.
|
|
34
|
-
// This means `uiHandlers.addServer` needs to inject the correct `serversListId` into these onclicks.
|
|
18
|
+
const disabledAttr = makeServerFullyEditable ? '' : 'disabled';
|
|
19
|
+
const readOnlyClasses = makeServerFullyEditable ? '' : 'bg-gray-100 cursor-not-allowed opacity-70';
|
|
20
|
+
|
|
21
|
+
// "Remove Server" button should be hidden if the server is NOT fully editable.
|
|
22
|
+
// A server is fully editable if the context isn't read-only OR if it's an adhoc server.
|
|
23
|
+
const hideRemoveServerButtonClass = !makeServerFullyEditable ? 'hidden' : '';
|
|
24
|
+
// Other action buttons (add dependency, add env var) also respect makeServerFullyEditable.
|
|
25
|
+
const hideActionButtonsClass = !makeServerFullyEditable ? 'hidden' : '';
|
|
35
26
|
|
|
36
|
-
|
|
37
|
-
// The
|
|
27
|
+
|
|
28
|
+
// Note: The 'serversListId' parameter is crucial for ensuring that actions (like removeServer, addEnvVariable)
|
|
29
|
+
// are correctly scoped to the server list in the current tab (e.g., 'serversList' or 'existingCategoryServersList').
|
|
30
|
+
// The onclick handlers in this template dynamically include this 'serversListId'.
|
|
38
31
|
|
|
39
32
|
return `
|
|
40
33
|
<div class="server-item p-4 border border-gray-300 rounded-lg mb-6 bg-white shadow" data-index="${serverIndex}">
|
|
@@ -43,13 +36,13 @@ export const serverTemplate = (serverIndex, isReadOnly = false, serverData = nul
|
|
|
43
36
|
aria-expanded="false" aria-controls="${serversListId}-server-content-${serverIndex}"
|
|
44
37
|
onclick="window.toggleSectionContent('${serversListId}-server-content-${serverIndex}', this.querySelector('i.toggle-icon'), this)"
|
|
45
38
|
onkeydown="if(event.key==='Enter' || event.key===' ') { window.toggleSectionContent('${serversListId}-server-content-${serverIndex}', this.querySelector('i.toggle-icon'), this); event.preventDefault(); }">
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
39
|
+
<h3 id="${serversListId}-server-title-${serverIndex}" class="text-lg font-semibold text-gray-800">MCP Server #${serverIndex + 1}${isServerAdhoc ? ' <span class="text-sm text-blue-600 ml-1">(Adhoc - Editable)</span>' : ''}</h3>
|
|
40
|
+
<div class="flex items-center">
|
|
41
|
+
<button type="button" onclick="event.stopPropagation(); removeServer(${serverIndex}, '${serversListId}')"
|
|
42
|
+
class="action-button-in-server p-1.5 text-sm text-red-600 hover:text-red-800 hover:bg-red-50 rounded-md flex items-center mr-2 ${hideRemoveServerButtonClass}" title="Remove Server">
|
|
43
|
+
<i class='bx bx-trash text-lg'></i>
|
|
44
|
+
</button>
|
|
45
|
+
<i class='bx bxs-chevron-down text-xl toggle-icon'></i>
|
|
53
46
|
</div>
|
|
54
47
|
</div>
|
|
55
48
|
<div id="${serversListId}-server-content-${serverIndex}" class="collapsible-content server-content-scrollable hidden" role="region" aria-labelledby="${serversListId}-server-title-${serverIndex}">
|
|
@@ -77,15 +70,15 @@ export const serverTemplate = (serverIndex, isReadOnly = false, serverData = nul
|
|
|
77
70
|
</div>
|
|
78
71
|
<div class="md:col-span-2 flex gap-x-4 items-end">
|
|
79
72
|
<div class="flex-grow">
|
|
80
|
-
<label class="block text-sm font-medium text-gray-700 mb-1">Schema File Path
|
|
73
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">Schema File Path
|
|
74
|
+
<span class="text-xs text-gray-500 ml-1" title="Optional for listing available tools, please fill full path of the schema file">
|
|
75
|
+
<i class='bx bx-info-circle'></i> Optional
|
|
76
|
+
</span>
|
|
77
|
+
</label>
|
|
81
78
|
<input type="text" name="servers[${serverIndex}].schemas" id="schema-path-${serverIndex}" ${disabledAttr}
|
|
82
79
|
class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${readOnlyClasses}"
|
|
83
|
-
placeholder="e.g.,
|
|
80
|
+
placeholder="e.g., /Users/xxx/schemas/my_server_schema.json">
|
|
84
81
|
</div>
|
|
85
|
-
<button type="button" onclick="browseLocalSchema(${serverIndex}, '${serversListId}')" ${disabledAttr}
|
|
86
|
-
class="action-button-in-server px-4 py-2 text-sm bg-gray-100 hover:bg-gray-200 border border-gray-300 rounded-lg whitespace-nowrap ${hideButtonClass} ${isReadOnly ? 'opacity-50 cursor-not-allowed' : ''}">
|
|
87
|
-
Browse Local
|
|
88
|
-
</button>
|
|
89
82
|
</div>
|
|
90
83
|
<div class="md:col-span-2">
|
|
91
84
|
<label class="block text-sm font-medium text-gray-700 mb-1">Repository URL</label>
|
|
@@ -109,8 +102,8 @@ export const serverTemplate = (serverIndex, isReadOnly = false, serverData = nul
|
|
|
109
102
|
<div id="server-requirements-list-${serverIndex}" class="space-y-4">
|
|
110
103
|
<!-- Server requirements will be populated here by serverRequirementTemplate -->
|
|
111
104
|
</div>
|
|
112
|
-
<button type="button" onclick="addServerRequirement(${serverIndex}, '${serversListId}', ${
|
|
113
|
-
class="action-button-in-server mt-3 px-3 py-1.5 text-sm border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-100 flex items-center ${
|
|
105
|
+
<button type="button" onclick="addServerRequirement(${serverIndex}, '${serversListId}', ${!makeServerFullyEditable})"
|
|
106
|
+
class="action-button-in-server mt-3 px-3 py-1.5 text-sm border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-100 flex items-center ${hideActionButtonsClass}">
|
|
114
107
|
<i class='bx bx-plus mr-1'></i> Add Dependency
|
|
115
108
|
</button>
|
|
116
109
|
</div>
|
|
@@ -147,8 +140,8 @@ export const serverTemplate = (serverIndex, isReadOnly = false, serverData = nul
|
|
|
147
140
|
<div id="envVarsContainer_${serverIndex}" class="space-y-3">
|
|
148
141
|
<!-- Environment variables will be added here by envVariableTemplate -->
|
|
149
142
|
</div>
|
|
150
|
-
<button type="button" onclick="addEnvVariable(${serverIndex}, '${serversListId}', ${
|
|
151
|
-
class="action-button-in-server mt-3 px-3 py-1.5 text-sm border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-100 flex items-center ${
|
|
143
|
+
<button type="button" onclick="addEnvVariable(${serverIndex}, '${serversListId}', ${!makeServerFullyEditable})"
|
|
144
|
+
class="action-button-in-server mt-3 px-3 py-1.5 text-sm border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-100 flex items-center ${hideActionButtonsClass}">
|
|
152
145
|
<i class='bx bx-plus mr-1'></i> Add Environment Variable
|
|
153
146
|
</button>
|
|
154
147
|
</div>
|
|
@@ -217,6 +210,7 @@ export const envVariableTemplate = (serverIndex, envIndex, isReadOnly = false, s
|
|
|
217
210
|
* @returns {string} HTML string for the server requirement item.
|
|
218
211
|
*/
|
|
219
212
|
export const serverRequirementTemplate = (serverIndex, reqIndex, isReadOnly = false, serversListId = 'serversList') => {
|
|
213
|
+
// DEBUG: console.log(`[serverRequirementTemplate] ServerIndex: ${serverIndex}, ReqIndex: ${reqIndex}, isReadOnly received: ${isReadOnly}`);
|
|
220
214
|
const disabledAttr = isReadOnly ? 'disabled' : '';
|
|
221
215
|
const readOnlyClasses = isReadOnly ? 'bg-gray-100 cursor-not-allowed opacity-70' : '';
|
|
222
216
|
const hideButtonClass = isReadOnly ? 'hidden' : '';
|