main
html 357 lines 12.3 KB
Raw
1 <html>
2
3 <head>
4 <title>MCP Server Detail</title>
5
6 <script type="module">
7 import { store } from "/components/settings/mcp/client/mcp-servers-store.js";
8 </script>
9 </head>
10
11 <body>
12 <div x-data>
13 <template x-if="$store.mcpServersStore">
14 <div class="mcp-tools-modal">
15 <div class="mcp-tools-header">
16 <div class="mcp-tools-heading">
17 <h3 x-text="$store.mcpServersStore.serverDetail?.name || 'MCP tools'"></h3>
18 <p x-text="$store.mcpServersStore.serverDetail?.description || 'Tools exposed by this MCP server.'"></p>
19 </div>
20 <div class="mcp-tools-actions">
21 <span class="mcp-tools-count" x-text="$store.mcpServersStore.serverDetailToolsCountLabel"></span>
22 <button type="button" class="button confirm" :disabled="$store.mcpServersStore.applying" @click="$store.mcpServersStore.applyNow()">
23 <x-icon aria-hidden="true" name="check"></x-icon>
24 <span x-text="$store.mcpServersStore.applying ? 'Applying' : 'Apply'"></span>
25 </button>
26 </div>
27 </div>
28
29 <label class="mcp-tool-search">
30 <x-icon aria-hidden="true" name="search"></x-icon>
31 <input type="search" x-model.debounce.150ms="$store.mcpServersStore.toolSearch" placeholder="Search tools" />
32 <button type="button" class="mcp-tool-search-clear"
33 x-show="$store.mcpServersStore.toolSearch"
34 @click="$store.mcpServersStore.clearToolSearch()"
35 title="Clear search">
36 <x-icon aria-hidden="true" name="close"></x-icon>
37 </button>
38 </label>
39
40 <div class="tools-container">
41 <template x-if="$store.mcpServersStore.serverDetailTools.length === 0">
42 <div class="mcp-tools-empty">No tools exposed by this server.</div>
43 </template>
44 <template x-if="$store.mcpServersStore.serverDetailTools.length > 0 && $store.mcpServersStore.filteredServerDetailTools.length === 0">
45 <div class="mcp-tools-empty">No tools match this search.</div>
46 </template>
47 <template x-for="tool in $store.mcpServersStore.filteredServerDetailTools" :key="tool.name">
48 <div class="tool-item">
49 <div class="tool-header">
50 <div>
51 <h4 x-text="tool.name"></h4>
52 <p class="tool-description" x-text="tool.description || 'No description provided.'"></p>
53 </div>
54 <div class="mcp-tool-toggle-group">
55 <label class="toggle plugin-status-toggle mcp-tool-toggle"
56 :class="{ 'disabled-appearance': !$store.mcpServersStore.canConfigureServerTools($store.mcpServersStore.serverDetail?.name) }"
57 :title="!$store.mcpServersStore.canConfigureServerTools($store.mcpServersStore.serverDetail?.name) ? 'Add this inherited server to the current config before changing tools' : ($store.mcpServersStore.isServerToolEnabled($store.mcpServersStore.serverDetail?.name, tool.name) ? 'Disable tool' : 'Enable tool')">
58 <input type="checkbox"
59 :checked="$store.mcpServersStore.isServerToolEnabled($store.mcpServersStore.serverDetail?.name, tool.name)"
60 :disabled="!$store.mcpServersStore.canConfigureServerTools($store.mcpServersStore.serverDetail?.name)"
61 @change="$store.mcpServersStore.toggleServerTool($store.mcpServersStore.serverDetail?.name, tool.name, $event.target.checked)"
62 @click.stop>
63 <span class="toggler"></span>
64 </label>
65 <span class="plugin-status-text"
66 x-text="$store.mcpServersStore.isServerToolEnabled($store.mcpServersStore.serverDetail?.name, tool.name) ? 'ON' : 'OFF'"></span>
67 </div>
68 </div>
69
70 <template x-if="tool.input_schema?.properties">
71 <div class="tool-properties">
72 <p class="properties-title">Properties:</p>
73 <ul>
74 <template x-for="(prop, propName) in tool.input_schema.properties"
75 :key="propName">
76 <li>
77 <span class="prop-name" x-text="propName"></span>:
78 <span class="prop-type" x-text="prop.type || 'any'"></span>
79 <template x-if="prop.description">
80 <span class="prop-desc" x-text="' - ' + prop.description"></span>
81 </template>
82 </li>
83 </template>
84 </ul>
85 </div>
86 </template>
87 </div>
88 </template>
89 </div>
90 </div>
91 </template>
92 </div>
93
94 <style>
95 .mcp-tools-modal {
96 display: flex;
97 flex-direction: column;
98 gap: 1rem;
99 color: var(--color-text);
100 font-family: var(--font-family-main);
101 }
102
103 .mcp-tools-header {
104 display: flex;
105 justify-content: space-between;
106 align-items: flex-start;
107 gap: 1rem;
108 }
109
110 .mcp-tools-heading {
111 min-width: 0;
112 }
113
114 .mcp-tools-heading h3,
115 .mcp-tools-heading p,
116 .tool-item h4,
117 .tool-description,
118 .properties-title {
119 margin: 0;
120 }
121
122 .mcp-tools-heading h3 {
123 font-size: 1.15rem;
124 line-height: 1.2;
125 }
126
127 .mcp-tools-heading p,
128 .tool-description,
129 .mcp-tools-empty {
130 color: var(--color-text-muted);
131 font-size: 0.88rem;
132 line-height: 1.4;
133 }
134
135 .mcp-tools-actions {
136 display: flex;
137 align-items: center;
138 justify-content: flex-end;
139 gap: 0.5rem;
140 flex-wrap: wrap;
141 }
142
143 .mcp-tools-modal .button {
144 display: inline-flex;
145 align-items: center;
146 justify-content: center;
147 gap: 0.35rem;
148 min-height: 2rem;
149 padding: 0.35rem 0.65rem;
150 border: 1px solid var(--color-border);
151 border-radius: 6px;
152 background: var(--color-panel);
153 color: var(--color-text);
154 cursor: pointer;
155 }
156
157 .mcp-tools-modal .button.confirm {
158 border-color: color-mix(in srgb, var(--color-highlight) 65%, var(--color-border));
159 background: var(--color-highlight);
160 color: #fff;
161 }
162
163 .mcp-tools-modal .button:disabled {
164 opacity: 0.55;
165 cursor: not-allowed;
166 }
167
168 .mcp-tools-count {
169 display: inline-flex;
170 align-items: center;
171 min-height: 1.45rem;
172 padding: 0.15rem 0.45rem;
173 border: 1px solid var(--color-border);
174 border-radius: 999px;
175 color: var(--color-text-muted);
176 font-size: 0.72rem;
177 font-weight: 600;
178 }
179
180 .mcp-tool-search {
181 display: flex;
182 align-items: center;
183 gap: 0.35rem;
184 min-height: 2.25rem;
185 padding: 0.25rem 0.5rem;
186 border: 1px solid var(--color-border);
187 border-radius: 7px;
188 background: var(--color-input);
189 color: var(--color-text-muted);
190 }
191
192 .mcp-tool-search input {
193 width: 100%;
194 min-width: 0;
195 border: 0;
196 background: transparent;
197 color: var(--color-text);
198 outline: none;
199 padding: 0.2rem;
200 }
201
202 .mcp-tool-search input::-webkit-search-cancel-button,
203 .mcp-tool-search input::-webkit-search-decoration {
204 -webkit-appearance: none;
205 appearance: none;
206 display: none;
207 }
208
209 .mcp-tool-search-clear {
210 display: inline-flex;
211 align-items: center;
212 justify-content: center;
213 width: 1.45rem;
214 height: 1.45rem;
215 border: 0;
216 border-radius: 5px;
217 background: transparent;
218 color: var(--color-text-muted);
219 cursor: pointer;
220 }
221
222 .mcp-tools-modal .material-symbols-outlined {
223 font-size: 1.05rem;
224 line-height: 1;
225 }
226
227 .tools-container {
228 display: flex;
229 flex-direction: column;
230 gap: 0.75rem;
231 }
232
233 .tool-item {
234 padding: 0.85rem;
235 border: 1px solid var(--color-border);
236 border-radius: 8px;
237 background: color-mix(in srgb, var(--color-panel) 88%, transparent);
238 }
239
240 .tool-header {
241 display: flex;
242 align-items: flex-start;
243 justify-content: space-between;
244 gap: 1rem;
245 }
246
247 .tool-item h4 {
248 font-size: 1rem;
249 line-height: 1.25;
250 overflow-wrap: anywhere;
251 }
252
253 .tool-description {
254 margin-top: 0.25rem;
255 overflow-wrap: anywhere;
256 }
257
258 .mcp-tool-toggle-group {
259 display: inline-flex;
260 align-items: center;
261 gap: 0.45rem;
262 min-width: 5.2rem;
263 flex: 0 0 auto;
264 }
265
266 .mcp-tool-toggle {
267 width: 48px;
268 height: 28px;
269 flex: 0 0 48px;
270 }
271
272 .mcp-tool-toggle .toggler {
273 border-radius: 999px;
274 }
275
276 .mcp-tool-toggle .toggler:before {
277 width: 20px;
278 height: 20px;
279 left: 4px;
280 bottom: 4px;
281 }
282
283 .mcp-tool-toggle input:checked + .toggler:before {
284 transform: translateX(20px);
285 }
286
287 .mcp-tool-toggle.disabled-appearance {
288 opacity: 0.6;
289 }
290
291 .mcp-tool-toggle-group .plugin-status-text {
292 color: var(--color-text-muted);
293 font-size: 0.76rem;
294 font-weight: 700;
295 min-width: 1.7rem;
296 }
297
298 .tool-properties {
299 margin-top: 0.8rem;
300 padding: 0.75rem;
301 border: 1px solid var(--color-border);
302 background: var(--color-input);
303 border-radius: 6px;
304 }
305
306 .properties-title {
307 font-weight: 600;
308 margin-bottom: 0.5rem;
309 }
310
311 .tool-properties ul {
312 margin: 0;
313 padding-left: 1.5em;
314 }
315
316 .tool-properties li {
317 margin-bottom: 0.3em;
318 }
319
320 .prop-name {
321 font-weight: 600;
322 color: var(--color-highlight);
323 }
324
325 .prop-type {
326 color: var(--color-text-muted);
327 font-style: italic;
328 }
329
330 .prop-desc {
331 color: var(--color-text);
332 }
333
334 .mcp-tools-empty {
335 padding: 0.9rem;
336 text-align: center;
337 border: 1px solid var(--color-border);
338 border-radius: 8px;
339 background: color-mix(in srgb, var(--color-panel) 88%, transparent);
340 }
341
342 @media (max-width: 760px) {
343 .mcp-tools-header,
344 .tool-header {
345 flex-direction: column;
346 align-items: stretch;
347 }
348
349 .mcp-tools-actions {
350 justify-content: flex-start;
351 }
352 }
353 </style>
354
355 </body>
356
357 </html>