Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 47 additions & 16 deletions mcpgateway/static/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -3022,50 +3022,73 @@ function updateEditToolRequestTypes(selectedMethod = null) {
// TOOL SELECT FUNCTIONALITY
// ===================================================================

function initToolSelect(selectId, pillsId, warnId, max = 6) {
const select = safeGetElement(selectId);
const pillsBox = safeGetElement(pillsId);
const warnBox = safeGetElement(warnId);
function initToolSelect(
selectId,
pillsId,
warnId,
max = 6,
selectBtnId = null,
clearBtnId = null,
) {
const container = document.getElementById(selectId);
const pillsBox = document.getElementById(pillsId);
const warnBox = document.getElementById(warnId);
const clearBtn = clearBtnId ? document.getElementById(clearBtnId) : null;
const selectBtn = selectBtnId ? document.getElementById(selectBtnId) : null;

if (!select || !pillsBox || !warnBox) {
if (!container || !pillsBox || !warnBox) {
console.warn(
`Tool select elements not found: ${selectId}, ${pillsId}, ${warnId}`,
);
return;
}

const checkboxes = container.querySelectorAll('input[type="checkbox"]');
const pillClasses =
"inline-block px-2 py-1 text-xs font-medium text-blue-800 bg-blue-100 rounded";
"inline-block px-3 py-1 text-xs font-semibold text-indigo-700 bg-indigo-100 rounded-full shadow";

function update() {
try {
const chosen = Array.from(select.selectedOptions);
const count = chosen.length;
const checked = Array.from(checkboxes).filter((cb) => cb.checked);
const count = checked.length;

// Rebuild pills safely
pillsBox.innerHTML = "";
chosen.forEach((opt) => {
checked.forEach((cb) => {
const span = document.createElement("span");
span.className = pillClasses;
span.textContent = opt.text; // Safe text content
span.textContent =
cb.nextElementSibling?.textContent?.trim() || "Unnamed";
pillsBox.appendChild(span);
});

// Warning when > max
if (count > max) {
warnBox.textContent = `Selected ${count} tools. Selecting more than ${max} tools can degrade agent performance with the server.`;
warnBox.className = "text-yellow-600 text-sm mt-2";
} else {
warnBox.textContent = "";
warnBox.className = "";
}
} catch (error) {
console.error("Error updating tool select:", error);
}
}

if (clearBtn) {
clearBtn.addEventListener("click", () => {
checkboxes.forEach((cb) => (cb.checked = false));
update();
});
}

if (selectBtn) {
selectBtn.addEventListener("click", () => {
checkboxes.forEach((cb) => (cb.checked = true));
update();
});
}

update(); // Initial render
select.addEventListener("change", update);
checkboxes.forEach((cb) => cb.addEventListener("change", update));
}

// ===================================================================
Expand Down Expand Up @@ -4415,8 +4438,12 @@ async function handleEditToolFormSubmit(event) {

// // Save CodeMirror editors' contents if present

if (window.editToolHeadersEditor) window.editToolHeadersEditor.save();
if (window.editToolSchemaEditor) window.editToolSchemaEditor.save();
if (window.editToolHeadersEditor) {
window.editToolHeadersEditor.save();
}
if (window.editToolSchemaEditor) {
window.editToolSchemaEditor.save();
}

const isInactiveCheckedBool = isInactiveChecked("tools");
formData.append("is_inactive_checked", isInactiveCheckedBool);
Expand All @@ -4428,7 +4455,7 @@ async function handleEditToolFormSubmit(event) {
headers: { "X-Requested-With": "XMLHttpRequest" },
});
console.log("response:", response);
result = await response.json();
const result = await response.json();
console.log("result edit tool form:", result);
if (!result.success) {
throw new Error(result.message || "An error occurred");
Expand Down Expand Up @@ -4838,12 +4865,16 @@ function initializeToolSelects() {
"selectedToolsPills",
"selectedToolsWarning",
6,
"selectAllToolsBtn",
"clearAllToolsBtn",
);
initToolSelect(
"edit-server-tools",
"selectedEditToolsPills",
"selectedEditToolsWarning",
6,
"selectAllEditToolsBtn",
"clearAllEditToolsBtn",
);
}

Expand Down
129 changes: 95 additions & 34 deletions mcpgateway/templates/admin.html
Original file line number Diff line number Diff line change
Expand Up @@ -401,31 +401,64 @@ <h3 class="text-lg font-bold mb-4 dark:text-gray-200">
class="mt-1 block w-full rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-900 dark:placeholder-gray-300 dark:text-gray-300"
/>
</div>
<div>
<div class="p-4 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-300 dark:border-gray-700">
<label
class="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2"
for="associatedTools"
class="block text-sm font-medium text-gray-700 dark:text-gray-300"
>
Associated Tools
</label>
<select
name="associatedTools"
<div
id="associatedTools"
multiple
class="mt-1 block w-full rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-900 dark:placeholder-gray-300 dark:text-gray-300"
class="max-h-60 overflow-y-auto rounded-md border border-gray-300 dark:border-gray-700 shadow-sm p-3 bg-gray-50 dark:bg-gray-900"
>
{% for tool in tools %}
<option value="{{ tool.id }}">{{ tool.name }}</option>
<label
class="flex items-center space-x-3 text-gray-700 dark:text-gray-300 mb-2 cursor-pointer hover:bg-indigo-50 dark:hover:bg-indigo-900 rounded-md p-1"
>
<input
type="checkbox"
name="associatedTools"
value="{{ tool.id }}"
class="tool-checkbox form-checkbox h-5 w-5 text-indigo-600 dark:bg-gray-800 dark:border-gray-600"
/>
<span class="select-none">{{ tool.name }}</span>
</label>
{% endfor %}
</select>
<span
</div>
<div class="flex justify-end mt-3 gap-2">

<button
type="button"
id="selectAllToolsBtn"
class="px-3 py-1 text-sm font-semibold text-green-600 border border-green-600 rounded-md hover:bg-green-50 focus:outline-none focus:ring-2 focus:ring-green-300 focus:ring-offset-1"
aria-label="Select all tools"
>
Select All
</button>

<button
type="button"
id="clearAllToolsBtn"
class="px-3 py-1 text-sm font-semibold text-red-600 border border-red-600 rounded-md hover:bg-red-50 focus:outline-none focus:ring-2 focus:ring-red-300 focus:ring-offset-1"
aria-label="Clear all selected tools"
>
Clear All
</button>
</div>

<!-- Selected pills -->
<div
id="selectedToolsPills"
class="inline-flex flex-wrap gap-1 mt-2"
></span>
<span
class="mt-4 flex flex-wrap gap-2"
></div>

<!-- Warning message -->
<div
id="selectedToolsWarning"
class="block text-sm font-semibold text-red-600 mt-1"
></span>
class="mt-2 min-h-[1.25rem] text-sm font-semibold text-yellow-600"
aria-live="polite"
></div>
</div>
<div>
<label
Expand Down Expand Up @@ -3132,38 +3165,66 @@ <h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">
class="mt-1 block w-full rounded-md border border-gray-600 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-900 dark:placeholder-gray-300 dark:text-gray-300"
/>
</div>
<div>

<div class="p-4 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-600">
<label
for="edit-server-tools"
class="block text-sm font-medium text-gray-700 dark:text-gray-300"
class="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2"
>
Associated Tools
</label>
<select
<div
id="edit-server-tools"
name="associatedTools"
{#
same
field
name
#}
multiple
class="mt-1 block w-full rounded-md border border-gray-600 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-900 dark:placeholder-gray-300 dark:text-gray-300"
class="max-h-60 overflow-y-auto rounded-md border border-gray-600 shadow-sm p-3 bg-gray-50 dark:bg-gray-900"
>
{% for tool in tools %}
<option value="{{ tool.id }}">{{ tool.name }}</option>
<label
class="flex items-center space-x-3 text-gray-700 dark:text-gray-300 mb-2 cursor-pointer hover:bg-indigo-50 dark:hover:bg-indigo-900 rounded-md p-1"
>
<input
type="checkbox"
name="associatedTools"
value="{{ tool.id }}"
class="tool-checkbox form-checkbox h-5 w-5 text-indigo-600 dark:bg-gray-800 dark:border-gray-600"
/>
<span class="select-none">{{ tool.name }}</span>
</label>
{% endfor %}
</select>
<span
</div>
<div class="flex justify-end gap-3 mt-3">
<button
type="button"
id="selectAllEditToolsBtn"
class="px-3 py-1 text-sm font-semibold text-green-600 border border-green-600 rounded-md hover:bg-green-50 focus:outline-none focus:ring-2 focus:ring-green-300 focus:ring-offset-1"
aria-label="Select all tools"
>
Select All
</button>

<button
type="button"
id="clearAllEditToolsBtn"
class="px-3 py-1 text-sm font-semibold text-red-600 border border-red-600 rounded-md hover:bg-red-50 focus:outline-none focus:ring-2 focus:ring-red-300 focus:ring-offset-1"
aria-label="Clear all selected tools"
>
Clear All
</button>
</div>

<!-- Selected pills -->
<div
id="selectedEditToolsPills"
class="inline-flex flex-wrap gap-1 mt-2"
></span>
<!-- container -->
<span
class="mt-4 flex flex-wrap gap-2"
></div>

<!-- Warning message -->
<div
id="selectedEditToolsWarning"
class="block text-sm font-semibold text-red-600 mt-1"
></span>
class="mt-2 min-h-[1.25rem] text-sm font-semibold text-yellow-600"
aria-live="polite"
></div>
</div>

<div>
<label
class="block text-sm font-medium text-gray-700 dark:text-gray-300"
Expand Down
Loading