imcp 0.0.13 → 0.0.15

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 (157) hide show
  1. package/dist/core/ConfigurationProvider.d.ts +1 -0
  2. package/dist/core/ConfigurationProvider.js +15 -0
  3. package/dist/core/InstallationService.js +2 -7
  4. package/dist/core/MCPManager.d.ts +11 -2
  5. package/dist/core/MCPManager.js +24 -1
  6. package/dist/core/RequirementService.js +2 -8
  7. package/dist/core/installers/clients/BaseClientInstaller.d.ts +51 -0
  8. package/dist/core/installers/clients/BaseClientInstaller.js +160 -0
  9. package/dist/core/installers/clients/ClientInstaller.d.ts +16 -8
  10. package/dist/core/installers/clients/ClientInstaller.js +77 -504
  11. package/dist/core/installers/clients/ClientInstallerFactory.d.ts +19 -0
  12. package/dist/core/installers/clients/ClientInstallerFactory.js +41 -0
  13. package/dist/core/installers/clients/ClineInstaller.d.ts +18 -0
  14. package/dist/core/installers/clients/ClineInstaller.js +124 -0
  15. package/dist/core/installers/clients/GithubCopilotInstaller.d.ts +34 -0
  16. package/dist/core/installers/clients/GithubCopilotInstaller.js +162 -0
  17. package/dist/core/installers/clients/MSRooCodeInstaller.d.ts +15 -0
  18. package/dist/core/installers/clients/MSRooCodeInstaller.js +122 -0
  19. package/dist/core/installers/requirements/BaseInstaller.d.ts +11 -34
  20. package/dist/core/installers/requirements/BaseInstaller.js +5 -116
  21. package/dist/core/installers/requirements/CommandInstaller.d.ts +6 -1
  22. package/dist/core/installers/requirements/CommandInstaller.js +7 -0
  23. package/dist/core/installers/requirements/GeneralInstaller.d.ts +6 -1
  24. package/dist/core/installers/requirements/GeneralInstaller.js +9 -4
  25. package/dist/core/installers/requirements/NpmInstaller.d.ts +46 -7
  26. package/dist/core/installers/requirements/NpmInstaller.js +150 -58
  27. package/dist/core/installers/requirements/PipInstaller.d.ts +9 -0
  28. package/dist/core/installers/requirements/PipInstaller.js +66 -28
  29. package/dist/core/onboard/FeedOnboardService.d.ts +72 -0
  30. package/dist/core/onboard/FeedOnboardService.js +312 -0
  31. package/dist/core/onboard/OnboardProcessor.d.ts +79 -0
  32. package/dist/core/onboard/OnboardProcessor.js +290 -0
  33. package/dist/core/onboard/OnboardStatus.d.ts +49 -0
  34. package/dist/core/onboard/OnboardStatus.js +10 -0
  35. package/dist/core/onboard/OnboardStatusManager.d.ts +57 -0
  36. package/dist/core/onboard/OnboardStatusManager.js +176 -0
  37. package/dist/core/types.d.ts +6 -6
  38. package/dist/core/validators/FeedValidator.d.ts +20 -0
  39. package/dist/core/validators/FeedValidator.js +80 -0
  40. package/dist/core/validators/IServerValidator.d.ts +19 -0
  41. package/dist/core/validators/IServerValidator.js +2 -0
  42. package/dist/core/validators/SSEServerValidator.d.ts +15 -0
  43. package/dist/core/validators/SSEServerValidator.js +39 -0
  44. package/dist/core/validators/ServerValidatorFactory.d.ts +24 -0
  45. package/dist/core/validators/ServerValidatorFactory.js +45 -0
  46. package/dist/core/validators/StdioServerValidator.d.ts +46 -0
  47. package/dist/core/validators/StdioServerValidator.js +229 -0
  48. package/dist/services/InstallRequestValidator.d.ts +1 -1
  49. package/dist/services/ServerService.d.ts +9 -6
  50. package/dist/services/ServerService.js +18 -7
  51. package/dist/utils/adoUtils.d.ts +29 -0
  52. package/dist/utils/adoUtils.js +252 -0
  53. package/dist/utils/clientUtils.d.ts +0 -7
  54. package/dist/utils/clientUtils.js +0 -42
  55. package/dist/utils/githubUtils.d.ts +10 -0
  56. package/dist/utils/githubUtils.js +22 -0
  57. package/dist/utils/macroExpressionUtils.d.ts +38 -0
  58. package/dist/utils/macroExpressionUtils.js +116 -0
  59. package/dist/utils/osUtils.d.ts +4 -20
  60. package/dist/utils/osUtils.js +78 -23
  61. package/dist/web/contract/serverContract.d.ts +66 -0
  62. package/dist/web/contract/serverContract.js +2 -0
  63. package/dist/web/public/css/notifications.css +48 -17
  64. package/dist/web/public/css/onboard.css +107 -0
  65. package/dist/web/public/index.html +90 -18
  66. package/dist/web/public/js/api.js +3 -6
  67. package/dist/web/public/js/flights/flights.js +127 -0
  68. package/dist/web/public/js/modal/index.js +58 -0
  69. package/dist/web/public/js/modal/installHandler.js +227 -0
  70. package/dist/web/public/js/modal/installModal.js +163 -0
  71. package/dist/web/public/js/modal/installation.js +281 -0
  72. package/dist/web/public/js/modal/loadingModal.js +52 -0
  73. package/dist/web/public/js/modal/loadingUI.js +74 -0
  74. package/dist/web/public/js/modal/messageQueue.js +112 -0
  75. package/dist/web/public/js/modal/modalSetup.js +513 -0
  76. package/dist/web/public/js/modal/modalUI.js +214 -0
  77. package/dist/web/public/js/modal/modalUtils.js +49 -0
  78. package/dist/web/public/js/modal/version.js +20 -0
  79. package/dist/web/public/js/modal/versionUtils.js +20 -0
  80. package/dist/web/public/js/modal.js +25 -1041
  81. package/dist/web/public/js/notifications.js +66 -27
  82. package/dist/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +338 -0
  83. package/dist/web/public/js/onboard/formProcessor.js +864 -0
  84. package/dist/web/public/js/onboard/index.js +374 -0
  85. package/dist/web/public/js/onboard/publishHandler.js +132 -0
  86. package/dist/web/public/js/onboard/state.js +76 -0
  87. package/dist/web/public/js/onboard/templates.js +343 -0
  88. package/dist/web/public/js/onboard/uiHandlers.js +758 -0
  89. package/dist/web/public/js/onboard/validationHandlers.js +378 -0
  90. package/dist/web/public/js/serverCategoryDetails.js +43 -17
  91. package/dist/web/public/js/serverCategoryList.js +15 -2
  92. package/dist/web/public/onboard.html +296 -0
  93. package/dist/web/public/styles.css +91 -1
  94. package/dist/web/server.d.ts +0 -10
  95. package/dist/web/server.js +131 -22
  96. package/package.json +2 -2
  97. package/src/core/ConfigurationProvider.ts +15 -0
  98. package/src/core/InstallationService.ts +2 -7
  99. package/src/core/MCPManager.ts +26 -1
  100. package/src/core/RequirementService.ts +2 -9
  101. package/src/core/installers/clients/BaseClientInstaller.ts +196 -0
  102. package/src/core/installers/clients/ClientInstaller.ts +97 -589
  103. package/src/core/installers/clients/ClientInstallerFactory.ts +46 -0
  104. package/src/core/installers/clients/ClineInstaller.ts +135 -0
  105. package/src/core/installers/clients/GithubCopilotInstaller.ts +179 -0
  106. package/src/core/installers/clients/MSRooCodeInstaller.ts +133 -0
  107. package/src/core/installers/requirements/BaseInstaller.ts +13 -136
  108. package/src/core/installers/requirements/CommandInstaller.ts +9 -1
  109. package/src/core/installers/requirements/GeneralInstaller.ts +11 -4
  110. package/src/core/installers/requirements/NpmInstaller.ts +178 -61
  111. package/src/core/installers/requirements/PipInstaller.ts +68 -29
  112. package/src/core/onboard/FeedOnboardService.ts +346 -0
  113. package/src/core/onboard/OnboardProcessor.ts +305 -0
  114. package/src/core/onboard/OnboardStatus.ts +55 -0
  115. package/src/core/onboard/OnboardStatusManager.ts +188 -0
  116. package/src/core/types.ts +6 -6
  117. package/src/core/validators/FeedValidator.ts +79 -0
  118. package/src/core/validators/IServerValidator.ts +21 -0
  119. package/src/core/validators/SSEServerValidator.ts +43 -0
  120. package/src/core/validators/ServerValidatorFactory.ts +51 -0
  121. package/src/core/validators/StdioServerValidator.ts +259 -0
  122. package/src/services/InstallRequestValidator.ts +1 -1
  123. package/src/services/ServerService.ts +22 -7
  124. package/src/utils/adoUtils.ts +291 -0
  125. package/src/utils/clientUtils.ts +0 -44
  126. package/src/utils/githubUtils.ts +24 -0
  127. package/src/utils/macroExpressionUtils.ts +121 -0
  128. package/src/utils/osUtils.ts +89 -24
  129. package/src/web/contract/serverContract.ts +74 -0
  130. package/src/web/public/css/notifications.css +48 -17
  131. package/src/web/public/css/onboard.css +107 -0
  132. package/src/web/public/index.html +90 -18
  133. package/src/web/public/js/api.js +3 -6
  134. package/src/web/public/js/flights/flights.js +127 -0
  135. package/src/web/public/js/modal/index.js +58 -0
  136. package/src/web/public/js/modal/installModal.js +163 -0
  137. package/src/web/public/js/modal/installation.js +281 -0
  138. package/src/web/public/js/modal/loadingModal.js +52 -0
  139. package/src/web/public/js/modal/messageQueue.js +112 -0
  140. package/src/web/public/js/modal/modalSetup.js +513 -0
  141. package/src/web/public/js/modal/modalUtils.js +49 -0
  142. package/src/web/public/js/modal/versionUtils.js +20 -0
  143. package/src/web/public/js/modal.js +25 -1041
  144. package/src/web/public/js/notifications.js +66 -27
  145. package/src/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +338 -0
  146. package/src/web/public/js/onboard/formProcessor.js +864 -0
  147. package/src/web/public/js/onboard/index.js +374 -0
  148. package/src/web/public/js/onboard/publishHandler.js +132 -0
  149. package/src/web/public/js/onboard/state.js +76 -0
  150. package/src/web/public/js/onboard/templates.js +343 -0
  151. package/src/web/public/js/onboard/uiHandlers.js +758 -0
  152. package/src/web/public/js/onboard/validationHandlers.js +378 -0
  153. package/src/web/public/js/serverCategoryDetails.js +43 -17
  154. package/src/web/public/js/serverCategoryList.js +15 -2
  155. package/src/web/public/onboard.html +296 -0
  156. package/src/web/public/styles.css +91 -1
  157. package/src/web/server.ts +167 -58
@@ -0,0 +1,296 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Onboard MCP Category - IMCP</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link href="https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css" rel="stylesheet">
9
+ <!-- Bootstrap CSS is likely not needed if primarily using Tailwind -->
10
+ <!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> -->
11
+ <link rel="stylesheet" href="styles.css">
12
+ <link rel="stylesheet" href="css/onboard.css">
13
+ <!-- Highlight.js for JSON syntax highlighting -->
14
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
15
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
16
+ <link rel="stylesheet" href="css/modal.css">
17
+ <link rel="stylesheet" href="css/notifications.css">
18
+ <script type="module">
19
+ import flights, { getFlightQueryParameters } from './js/flights/flights.js';
20
+ if (!flights.enableOnboard) {
21
+ // Preserve flight params when redirecting
22
+ window.location.href = `index.html${getFlightQueryParameters()}`;
23
+ }
24
+
25
+ document.addEventListener('DOMContentLoaded', () => {
26
+ const flightQueryString = getFlightQueryParameters();
27
+
28
+ const backToServerLink = document.querySelector('a[href="index.html"]');
29
+ if (backToServerLink) {
30
+ backToServerLink.href = `index.html${flightQueryString}`;
31
+ }
32
+
33
+ // Update cancel buttons in forms
34
+ const cancelButtons = document.querySelectorAll('button[onclick="window.location.href=\'index.html\'"]');
35
+ cancelButtons.forEach(button => {
36
+ button.onclick = () => { window.location.href = `index.html${flightQueryString}`; };
37
+ });
38
+
39
+ const jsonEditorCancelButton = document.querySelector('#jsonEditorActionsContainer button[onclick="window.location.href=\'index.html\'"]');
40
+ if (jsonEditorCancelButton) {
41
+ jsonEditorCancelButton.onclick = () => { window.location.href = `index.html${flightQueryString}`; };
42
+ }
43
+ });
44
+ </script>
45
+ </head>
46
+ <body class="bg-gray-50 min-h-screen">
47
+ <div class="alert-container"></div>
48
+ <div class="container mx-auto px-4 py-6">
49
+ <div class="flex items-center justify-between mb-8">
50
+ <h1 class="text-3xl font-bold text-gray-900 flex items-center">
51
+ <i class='bx bx-server mr-3 text-blue-600'></i>
52
+ Onboard MCP Category
53
+ </h1>
54
+ <a href="index.html" class="text-gray-600 hover:text-gray-900 flex items-center">
55
+ <i class='bx bx-arrow-back mr-2'></i>
56
+ Back to Servers
57
+ </a>
58
+ </div>
59
+
60
+ <div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6 max-w-6xl mx-auto">
61
+ <!-- Tab Navigation -->
62
+ <div class="mb-6 border-b border-gray-200">
63
+ <nav class="flex space-x-8" aria-label="Tabs">
64
+ <button id="tab-create-category" class="py-2 px-4 text-blue-600 border-b-2 border-blue-600 font-semibold focus:outline-none" type="button">
65
+ Create New Server Category
66
+ </button>
67
+ <button id="tab-create-server" class="py-2 px-4 text-gray-600 border-b-2 border-transparent hover:text-blue-600 hover:border-blue-600 font-semibold focus:outline-none" type="button">
68
+ Create New Server in Existing Category
69
+ </button>
70
+ </nav>
71
+ <!-- View Mode Toggle Switch -->
72
+ <div class="flex justify-end mt-2 mb-4">
73
+ <label for="viewModeToggle" class="flex items-center cursor-pointer">
74
+ <span class="mr-3 text-sm font-medium text-gray-700">Form View</span>
75
+ <div class="relative">
76
+ <input type="checkbox" id="viewModeToggle" class="sr-only peer">
77
+ <div class="w-10 h-6 bg-gray-200 rounded-full peer peer-focus:ring-2 peer-focus:ring-blue-300 peer-checked:bg-blue-600 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all"></div>
78
+ </div>
79
+ <span class="ml-3 text-sm font-medium text-gray-700">JSON View</span>
80
+ </label>
81
+ </div>
82
+ </div>
83
+ <!-- Tab Panels -->
84
+ <div id="panel-create-category">
85
+ <form id="onboardForm" class="space-y-6">
86
+ <!-- Basic Information -->
87
+ <div class="space-y-4">
88
+ <h2 onclick="toggleSectionContent('basicInformationContent', this.querySelector('.toggle-icon'))" class="text-xl font-semibold text-gray-900 flex items-center justify-between mb-4 cursor-pointer">
89
+ <span class="flex items-center">
90
+ <i class='bx bx-info-circle mr-2 text-blue-600'></i>
91
+ Basic Information
92
+ </span>
93
+ <i class='bx bx-chevron-down toggle-icon text-xl text-gray-500'></i>
94
+ </h2>
95
+ <div id="basicInformationContent" class="grid grid-cols-1 md:grid-cols-2 gap-4 p-6 bg-white rounded-lg border border-gray-200 shadow-sm">
96
+ <div>
97
+ <label class="block text-sm font-medium text-gray-700 mb-1">Category Name*</label>
98
+ <input type="text" name="name" required
99
+ class="w-full px-3 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
100
+ placeholder="e.g., ai-coder-tools">
101
+ </div>
102
+ <div>
103
+ <label class="block text-sm font-medium text-gray-700 mb-1">Display Name*</label>
104
+ <input type="text" name="displayName" required
105
+ class="w-full px-3 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
106
+ placeholder="e.g., Coder Tools">
107
+ </div>
108
+ <div class="md:col-span-2">
109
+ <label class="block text-sm font-medium text-gray-700 mb-1">Description</label>
110
+ <textarea name="description" rows="3"
111
+ class="w-full px-3 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
112
+ placeholder="Describe the purpose and capabilities of this server category"></textarea>
113
+ </div>
114
+ <div class="md:col-span-2">
115
+ <label class="block text-sm font-medium text-gray-700 mb-1">Repository URL</label>
116
+ <input type="url" name="repository"
117
+ class="w-full px-3 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
118
+ placeholder="e.g., https://github.com/organization/repository">
119
+ </div>
120
+ </div>
121
+ </div>
122
+ <!-- MCP Servers -->
123
+ <div class="space-y-4">
124
+ <div class="flex justify-between items-center mb-4">
125
+ <h2 class="text-xl font-semibold text-gray-900 flex items-center">
126
+ <i class='bx bx-server mr-2 text-blue-600'></i>
127
+ MCP Servers
128
+ </h2>
129
+ <button type="button" onclick="addServer()"
130
+ class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 flex items-center text-sm">
131
+ <i class='bx bx-plus mr-2'></i>
132
+ Add MCP Server
133
+ </button>
134
+ </div>
135
+ <div id="serversList" class="space-y-4">
136
+ <!-- Server configurations will be added here dynamically by serverTemplate -->
137
+ </div>
138
+ </div>
139
+ <div class="flex justify-end space-x-4 pt-6 border-t">
140
+ <button type="button" onclick="window.location.href='index.html'"
141
+ class="px-6 py-2.5 border border-gray-300 rounded-lg text-gray-600 hover:bg-gray-50">
142
+ Cancel
143
+ </button>
144
+ <button type="button" id="validateButtonNewCategory"
145
+ class="px-6 py-2.5 bg-green-500 text-white rounded-lg hover:bg-green-600 flex items-center">
146
+ <i class='bx bx-check-shield mr-2'></i>
147
+ Validate
148
+ </button>
149
+ <button type="submit" id="publishButtonNewCategory"
150
+ class="px-6 py-2.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 flex items-center">
151
+ <i class='bx bxs-cloud-upload mr-2'></i>
152
+ Publish
153
+ </button>
154
+ </div>
155
+ <!-- Validation Status Panel -->
156
+ <div id="validationStatusPanelNewCategory" class="hidden p-6 bg-gray-50 rounded-lg border border-gray-200">
157
+ <h3 onclick="toggleSectionContent('validationStatusContentNewCategory', this.querySelector('.toggle-icon'))" class="cursor-pointer text-lg font-semibold text-gray-800 mb-3 flex items-center justify-between">
158
+ <span class="flex items-center">
159
+ <i class='bx bx-analyse mr-2 text-blue-600'></i>Validation Status
160
+ </span>
161
+ <i class='bx bx-chevron-down toggle-icon text-xl text-gray-500'></i>
162
+ </h3>
163
+ <div id="validationStatusContentNewCategory" class="text-sm text-gray-700 space-y-2">
164
+ <!-- Validation messages will be displayed here -->
165
+ </div>
166
+ </div>
167
+ </form>
168
+ </div>
169
+ <div id="panel-create-server" class="hidden">
170
+ <form id="onboardServerForm" class="space-y-6">
171
+ <!-- Select Existing Category -->
172
+ <div class="space-y-4">
173
+ <h2 class="text-xl font-semibold text-gray-900 flex items-center mb-4">
174
+ <i class='bx bx-category mr-2 text-blue-600'></i>
175
+ Select Server Category
176
+ </h2>
177
+ <select id="existingCategorySelect" name="categoryName" required
178
+ class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
179
+ <option value="">Loading categories...</option>
180
+ </select>
181
+ </div>
182
+
183
+ <!-- Basic Information (Read-only after category selection) -->
184
+ <div id="existingCategoryBasicInfoContainer" class="hidden space-y-4">
185
+ <h2 onclick="toggleSectionContent('existingCategoryBasicInformationContent', this.querySelector('.toggle-icon'))" class="text-xl font-semibold text-gray-900 flex items-center justify-between mb-4 cursor-pointer">
186
+ <span class="flex items-center">
187
+ <i class='bx bx-info-circle mr-2 text-blue-600'></i>
188
+ Basic Information (Read-only)
189
+ </span>
190
+ <i class='bx bx-chevron-down toggle-icon text-xl text-gray-500'></i>
191
+ </h2>
192
+ <div id="existingCategoryBasicInformationContent" class="grid grid-cols-1 md:grid-cols-2 gap-4 p-6 bg-white rounded-lg border border-gray-200 shadow-sm">
193
+ <div>
194
+ <label class="block text-sm font-medium text-gray-700 mb-1">Category Name</label>
195
+ <input type="text" name="name" readonly class="w-full px-3 py-1.5 border border-gray-300 rounded-lg bg-gray-100 cursor-not-allowed">
196
+ </div>
197
+ <div>
198
+ <label class="block text-sm font-medium text-gray-700 mb-1">Display Name</label>
199
+ <input type="text" name="displayName" readonly class="w-full px-3 py-1.5 border border-gray-300 rounded-lg bg-gray-100 cursor-not-allowed">
200
+ </div>
201
+ <div class="md:col-span-2">
202
+ <label class="block text-sm font-medium text-gray-700 mb-1">Description</label>
203
+ <textarea name="description" rows="3" readonly class="w-full px-3 py-1.5 border border-gray-300 rounded-lg bg-gray-100 cursor-not-allowed"></textarea>
204
+ </div>
205
+ <div class="md:col-span-2">
206
+ <label class="block text-sm font-medium text-gray-700 mb-1">Repository URL</label>
207
+ <input type="url" name="repository" readonly class="w-full px-3 py-1.5 border border-gray-300 rounded-lg bg-gray-100 cursor-not-allowed">
208
+ </div>
209
+ </div>
210
+ </div>
211
+
212
+ <!-- MCP Servers (Existing read-only, new ones editable) -->
213
+ <div id="existingCategoryMcpServersContainer" class="hidden space-y-4">
214
+ <div class="flex justify-between items-center mb-4">
215
+ <h2 class="text-xl font-semibold text-gray-900 flex items-center">
216
+ <i class='bx bx-server mr-2 text-blue-600'></i>
217
+ MCP Servers
218
+ </h2>
219
+ <button type="button" id="addServerToExistingCategoryBtn"
220
+ class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 flex items-center text-sm">
221
+ <i class='bx bx-plus mr-2'></i>
222
+ Add MCP Server
223
+ </button>
224
+ </div>
225
+ <div id="existingCategoryServersList" class="space-y-4">
226
+ <!-- Server configurations will be added here: existing ones read-only, new ones editable -->
227
+ </div>
228
+ </div>
229
+
230
+ <!-- Action Buttons -->
231
+ <div class="flex justify-end space-x-4 pt-6 border-t">
232
+ <button type="button" onclick="window.location.href='index.html'"
233
+ class="px-6 py-2.5 border border-gray-300 rounded-lg text-gray-600 hover:bg-gray-50">
234
+ Cancel
235
+ </button>
236
+ <button type="button" id="validateButtonExistingCategory"
237
+ class="px-6 py-2.5 bg-green-500 text-white rounded-lg hover:bg-green-600 flex items-center">
238
+ <i class='bx bx-check-shield mr-2'></i>
239
+ Validate
240
+ </button>
241
+ <button type="submit" id="publishButtonExistingCategory"
242
+ class="px-6 py-2.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 flex items-center">
243
+ <i class='bx bxs-cloud-upload mr-2'></i>
244
+ Publish
245
+ </button>
246
+ </div>
247
+
248
+ <!-- Validation Status Panel -->
249
+ <div id="validationStatusPanelExistingCategory" class="hidden p-6 bg-gray-50 rounded-lg border border-gray-200">
250
+ <h3 onclick="toggleSectionContent('validationStatusContentExistingCategoryTab', this.querySelector('.toggle-icon'))" class="cursor-pointer text-lg font-semibold text-gray-800 mb-3 flex items-center justify-between">
251
+ <span class="flex items-center">
252
+ <i class='bx bx-analyse mr-2 text-blue-600'></i>Validation Status
253
+ </span>
254
+ <i class='bx bx-chevron-down toggle-icon text-xl text-gray-500'></i>
255
+ </h3>
256
+ <div id="validationStatusContentExistingCategoryTab" class="text-sm text-gray-700 space-y-2">
257
+ <!-- Validation messages will be displayed here -->
258
+ </div>
259
+ </div>
260
+ </form>
261
+ </div>
262
+
263
+ <!-- JSON Editor Panel -->
264
+ <div id="panel-json-editor" class="hidden">
265
+ <h2 class="text-xl font-semibold text-gray-900 flex items-center mb-4">
266
+ <i class='bx bx-code-alt mr-2 text-blue-600'></i>
267
+ Edit Configuration as JSON
268
+ </h2>
269
+ <div class="relative">
270
+ <button type="button" onclick="copyJsonToClipboard()" title="Copy JSON to clipboard"
271
+ class="absolute top-2 right-2 z-10 px-3 py-1.5 text-xs bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-md flex items-center">
272
+ <i class='bx bx-copy mr-1'></i> Copy
273
+ </button>
274
+ <textarea id="jsonEditorTextarea" rows="20"
275
+ class="w-full px-3 py-2 font-mono text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
276
+ placeholder="JSON configuration will appear here..."></textarea>
277
+ </div>
278
+ <div id="jsonEditorActionsContainer" class="flex justify-end space-x-4 pt-6 border-t mt-6">
279
+ <!-- "Apply JSON to Form" button removed -->
280
+ <button type="button" onclick="window.location.href='index.html'"
281
+ class="px-6 py-2.5 border border-gray-300 rounded-lg text-gray-600 hover:bg-gray-50">
282
+ Cancel
283
+ </button>
284
+ <button type="button" id="saveJsonButton"
285
+ class="px-6 py-2.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 flex items-center">
286
+ <i class='bx bxs-cloud-upload mr-2'></i>
287
+ Publish JSON
288
+ </button>
289
+ </div>
290
+ </div>
291
+ </div>
292
+
293
+ <script src="js/onboard/index.js" type="module"></script>
294
+ <!-- Highlight.js script can be removed if not using <pre><code> for JSON -->
295
+ </body>
296
+ </html>
@@ -152,4 +152,94 @@ body {
152
152
  outline: none;
153
153
  border-color: #2563eb;
154
154
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
155
- }
155
+ }
156
+
157
+ /* Onboarding form styles */
158
+
159
+ /* Form field styles */
160
+ input[type="text"],
161
+ input[type="url"],
162
+ select,
163
+ textarea {
164
+ transition: all 0.2s ease;
165
+ }
166
+
167
+ input[type="text"]:focus,
168
+ input[type="url"]:focus,
169
+ select:focus,
170
+ textarea:focus {
171
+ outline: none;
172
+ border-color: #2563eb;
173
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
174
+ }
175
+
176
+ /* Radio button styles */
177
+ input[type="radio"] {
178
+ width: 1rem;
179
+ height: 1rem;
180
+ border: 2px solid #d1d5db;
181
+ border-radius: 50%;
182
+ transition: all 0.2s ease;
183
+ }
184
+
185
+ input[type="radio"]:checked {
186
+ border-color: #2563eb;
187
+ background-color: #2563eb;
188
+ }
189
+
190
+ /* Remove button styles */
191
+ .remove-button {
192
+ color: #dc2626;
193
+ transition: all 0.2s ease;
194
+ }
195
+
196
+ .remove-button:hover {
197
+ color: #b91c1c;
198
+ background-color: #fee2e2;
199
+ }
200
+
201
+ /* Add button styles */
202
+ .add-button {
203
+ background-color: #f3f4f6;
204
+ transition: all 0.2s ease;
205
+ }
206
+
207
+ .add-button:hover {
208
+ background-color: #e5e7eb;
209
+ transform: translateY(-1px);
210
+ }
211
+
212
+ /* Form section headings */
213
+ .section-heading {
214
+ color: #1f2937;
215
+ font-size: 1.25rem;
216
+ font-weight: 600;
217
+ margin-bottom: 1rem;
218
+ display: flex;
219
+ align-items: center;
220
+ }
221
+
222
+ .section-heading i {
223
+ color: #2563eb;
224
+ margin-right: 0.5rem;
225
+ }
226
+
227
+ /* Required field indicator */
228
+ .required-field::after {
229
+ content: "*";
230
+ color: #dc2626;
231
+ margin-left: 0.25rem;
232
+ }
233
+
234
+ /* Form grid layouts */
235
+ .form-grid {
236
+ display: grid;
237
+ gap: 1rem;
238
+ margin-bottom: 1.5rem;
239
+ }
240
+
241
+ @media (min-width: 768px) {
242
+ .form-grid {
243
+ grid-template-columns: repeat(2, 1fr);
244
+ }
245
+ }
@@ -1,11 +1 @@
1
- import { ServerInstallOptions } from '../core/types.js';
2
- export interface UpdateRequirementsRequestBody {
3
- requirements: {
4
- name: string;
5
- updateVersion: string;
6
- }[];
7
- }
8
- export interface InstallServersRequestBody {
9
- serverList: Record<string, ServerInstallOptions>;
10
- }
11
1
  export declare function startWebServer(port?: number): Promise<void>;
@@ -1,11 +1,14 @@
1
1
  import express from 'express';
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
+ import { OnboardingProcessStatus } from '../core/onboard/OnboardStatus.js';
4
5
  import { SUPPORTED_CLIENT_NAMES } from '../core/constants.js';
5
6
  import { serverService } from '../services/ServerService.js';
7
+ import { feedOnboardService } from '../core/onboard/FeedOnboardService.js';
6
8
  import { openBrowser } from '../utils/osUtils.js';
7
9
  import { Logger } from '../utils/logger.js';
8
10
  import { configProvider } from '../core/ConfigurationProvider.js';
11
+ import { onboardStatusManager } from '../core/onboard/OnboardStatusManager.js';
9
12
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
13
  const app = express();
11
14
  // Middleware
@@ -52,31 +55,31 @@ app.get('/api/categories', async (req, res) => {
52
55
  error: message
53
56
  });
54
57
  }
55
- // Get server schema
56
- app.get('/api/categories/:categoryName/servers/:serverName/schema', async (req, res) => {
57
- try {
58
- const { categoryName, serverName } = req.params;
59
- const schema = await serverService.getServerSchema(categoryName, serverName);
60
- if (!schema) {
61
- return res.status(404).json({
62
- success: false,
63
- error: `Schema not found for server ${serverName} in category ${categoryName}`
64
- });
65
- }
66
- const response = {
67
- success: true,
68
- data: schema
69
- };
70
- res.json(response);
71
- }
72
- catch (error) {
73
- const message = error instanceof Error ? error.message : 'Unknown error';
74
- res.status(500).json({
58
+ });
59
+ // Get server schema
60
+ app.get('/api/categories/:categoryName/servers/:serverName/schema', async (req, res) => {
61
+ try {
62
+ const { categoryName, serverName } = req.params;
63
+ const schema = await serverService.getServerSchema(categoryName, serverName);
64
+ if (!schema) {
65
+ return res.status(404).json({
75
66
  success: false,
76
- error: `Failed to get schema for server ${req.params.serverName} in category ${req.params.categoryName}: ${message}`
67
+ error: `Schema not found for server ${serverName} in category ${categoryName}`
77
68
  });
78
69
  }
79
- });
70
+ const response = {
71
+ success: true,
72
+ data: schema
73
+ };
74
+ res.json(response);
75
+ }
76
+ catch (error) {
77
+ const message = error instanceof Error ? error.message : 'Unknown error';
78
+ res.status(500).json({
79
+ success: false,
80
+ error: `Failed to get schema for server ${req.params.serverName} in category ${req.params.categoryName}: ${message}`
81
+ });
82
+ }
80
83
  });
81
84
  // Get categories data (including feed configuration)
82
85
  app.get('/api/categories/:categoryName', async (req, res) => {
@@ -103,6 +106,7 @@ app.get('/api/categories/:categoryName', async (req, res) => {
103
106
  });
104
107
  }
105
108
  });
109
+ // Handle server category onboarding
106
110
  // Install servers for a category
107
111
  app.post('/api/categories/:categoryName/install', async (req, res) => {
108
112
  try {
@@ -130,6 +134,111 @@ app.post('/api/categories/:categoryName/install', async (req, res) => {
130
134
  });
131
135
  }
132
136
  });
137
+ // Handle server category onboarding
138
+ app.post('/api/categories/onboard', async (req, res) => {
139
+ try {
140
+ const { categoryData, forExistingCategory } = req.body; // Extract forExistingCategory from request body
141
+ // Basic validation for categoryData presence and essential fields for this endpoint
142
+ if (!categoryData || !categoryData.name || !categoryData.displayName) {
143
+ return res.status(400).json({
144
+ success: false,
145
+ error: 'Category data, including name and display name, is required.'
146
+ });
147
+ }
148
+ // categoryData is now expected to be a FeedConfiguration object directly from the client
149
+ const feedConfiguration = categoryData;
150
+ // Structural validation is now primarily handled by FeedOnboardService.validateStaticConfig
151
+ // The service will throw an error if the structure is invalid, which will be caught by the catch block below.
152
+ const operationResult = await feedOnboardService.onboardFeed(feedConfiguration, forExistingCategory);
153
+ // The response now directly reflects the OperationStatus returned by the service.
154
+ // The client will use data.onboardingId (which is the categoryName) to poll for status updates.
155
+ const response = {
156
+ success: operationResult.status !== OnboardingProcessStatus.FAILED, // Or a more nuanced success check
157
+ // Ensure data.onboardingId is the categoryName for polling
158
+ data: { ...operationResult, onboardingId: feedConfiguration.name }
159
+ };
160
+ res.status(operationResult.status === OnboardingProcessStatus.FAILED ? 500 : 200).json(response);
161
+ }
162
+ catch (error) {
163
+ const message = error instanceof Error ? error.message : 'Unknown error';
164
+ res.status(500).json({
165
+ success: false,
166
+ error: `Failed to ${req.body.isUpdate ? 'update' : 'create'} server category: ${message}`
167
+ });
168
+ }
169
+ });
170
+ // Validate feed configuration
171
+ app.post('/api/categories/onboard/validate', async (req, res) => {
172
+ try {
173
+ const { categoryData, forExistingCategory } = req.body;
174
+ if (!categoryData || !categoryData.name) {
175
+ return res.status(400).json({
176
+ success: false,
177
+ error: 'Category name is required in categoryData for validation.'
178
+ });
179
+ }
180
+ // categoryData is now expected to be a FeedConfiguration object directly from the client
181
+ const feedConfigurationToValidate = categoryData;
182
+ // Structural validation is now primarily handled by FeedOnboardService.validateStaticConfig
183
+ // The service will throw an error if the structure is invalid, which will be caught by the catch block below.
184
+ // Call the service, which now returns OperationStatus & { feedConfiguration? }
185
+ // serverName parameter has been removed from validateFeed
186
+ const validationOperationResult = await feedOnboardService.validateFeed(feedConfigurationToValidate, forExistingCategory);
187
+ // The response includes the operation status and the feed configuration that was validated.
188
+ // The client will use data.onboardingId (which is the categoryName) to poll for status updates.
189
+ const response = {
190
+ success: validationOperationResult.status !== OnboardingProcessStatus.FAILED,
191
+ // Ensure data.onboardingId is the categoryName for polling
192
+ data: { ...validationOperationResult, onboardingId: feedConfigurationToValidate.name }
193
+ };
194
+ res.status(validationOperationResult.status === OnboardingProcessStatus.FAILED ? 500 : 200).json(response);
195
+ }
196
+ catch (error) {
197
+ const message = error instanceof Error ? error.message : 'Unknown error';
198
+ res.status(500).json({
199
+ success: false,
200
+ error: `Failed to validate server category: ${message}`
201
+ });
202
+ }
203
+ });
204
+ // Get category onboarding/validation operation status
205
+ app.get('/api/categories/:categoryName/onboard/status', async (req, res) => {
206
+ try {
207
+ const { categoryName } = req.params;
208
+ // categoryName is now the identifier for the operation status
209
+ const status = await onboardStatusManager.getStatus(categoryName);
210
+ if (!status) {
211
+ return res.status(404).json({
212
+ success: false,
213
+ error: `No active operation found for category ${categoryName}`
214
+ });
215
+ }
216
+ // Construct the response data based on the retrieved OnboardStatus
217
+ const responseData = {
218
+ onboardingId: status.onboardingId, // This will be the categoryName
219
+ status: status.status,
220
+ message: status.currentStep || status.errorMessage || 'Processing...',
221
+ lastQueried: new Date().toISOString(),
222
+ ...(status.validationStatus && { validationStatus: status.validationStatus }),
223
+ ...(status.prInfo && { prInfo: status.prInfo }),
224
+ ...(status.result && { result: status.result }),
225
+ ...(status.errorMessage && { errorMessage: status.errorMessage }),
226
+ ...(status.operationType && { operationType: status.operationType }),
227
+ };
228
+ const response = {
229
+ success: true,
230
+ data: responseData
231
+ };
232
+ res.json(response);
233
+ }
234
+ catch (error) {
235
+ const message = error instanceof Error ? error.message : 'Unknown error';
236
+ res.status(500).json({
237
+ success: false,
238
+ error: `Failed to get operation status for category ${req.params.categoryName}: ${message}`
239
+ });
240
+ }
241
+ });
133
242
  // Uninstall tools from a server
134
243
  app.post('/api/categories/:categoryName/uninstall', async (req, res) => {
135
244
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imcp",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "description": "Node.js SDK for Model Context Protocol (MCP)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -15,7 +15,7 @@
15
15
  "test": "jest",
16
16
  "lint": "eslint src --ext .ts",
17
17
  "format": "prettier --write \"src/**/*.ts\"",
18
- "copy-public": "xcopy /E /I /Y src\\web\\public dist\\web\\public"
18
+ "copy-public": "node -e \"const fs=require('fs'); const path=require('path'); const cp=require('child_process'); const cmd=process.platform==='win32' ? 'xcopy /E /I /Y src\\\\web\\\\public dist\\\\web\\\\public' : 'cp -r src/web/public dist/web/'; cp.execSync(cmd)\""
19
19
  },
20
20
  "keywords": [
21
21
  "mcp",
@@ -273,6 +273,21 @@ export class ConfigurationProvider {
273
273
  });
274
274
  }
275
275
 
276
+ async GetServerRequirementStatus(categoryName: string, serverName: string): Promise<RequirementStatus[]> {
277
+ return await this.withLock(async () => {
278
+ // Inline getServerCategory logic
279
+ const category = this.configuration.localServerCategories.find(s => s.name === categoryName);
280
+ if (!category?.feedConfiguration) return [];
281
+ const serverConfig = category.feedConfiguration.mcpServers.find(s => s.name === serverName);
282
+ if (!serverConfig?.dependencies?.requirements) return [];
283
+ const requirementNames = serverConfig.dependencies.requirements.map(r => r.name);
284
+
285
+ return requirementNames
286
+ .map(name => category?.installationStatus?.requirementsStatus[name])
287
+ .filter(x => x !== undefined) as RequirementStatus[];
288
+ });
289
+ }
290
+
276
291
  async isServerReady(categoryName: string, serverName: string, clients: string[]): Promise<boolean> {
277
292
  return await this.withLock(async () => {
278
293
  // Inline the logic from getServerStatus and getInstallationStatus to avoid nested lock
@@ -149,14 +149,9 @@ export class InstallationService {
149
149
  }
150
150
  });
151
151
 
152
- // Create updated requirement config with new version
153
- const updatedReqConfig: RequirementConfig = {
154
- ...reqConfig,
155
- version: reqToUpdate.version
156
- };
157
152
 
158
153
  // For pip requirements, check if we have a stored pythonEnv
159
- if (updatedReqConfig.type === 'pip' && currentStatus.pythonEnv && !options?.settings?.pythonEnv) {
154
+ if (reqConfig.type === 'pip' && currentStatus.pythonEnv && !options?.settings?.pythonEnv) {
160
155
  options = {
161
156
  ...options,
162
157
  settings: { ...options?.settings, pythonEnv: currentStatus.pythonEnv }
@@ -164,7 +159,7 @@ export class InstallationService {
164
159
  }
165
160
 
166
161
  // Update the requirement with options for pip environment
167
- const updatedStatus = await requirementService.updateRequirement(updatedReqConfig, reqToUpdate.version, options);
162
+ const updatedStatus = await requirementService.updateRequirement(reqConfig, reqToUpdate.version, options);
168
163
 
169
164
  // Update requirement status
170
165
  await configProvider.updateRequirementStatus(categoryName, reqToUpdate.name, {