Skip to content
Open
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
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ ENV PORT=3663
# ENV MCP_PROXY_SSE_ALLOWED_KEYS=""
# Optional: Enable Admin Web UI (set to "true" to enable)
ENV ENABLE_ADMIN_UI=false
# Optional: Enable Admin terminal routes (/admin/terminal). Keep disabled unless needed.
ENV ENABLE_ADMIN_TERMINAL=false

# Optional: Admin UI Credentials (required if ENABLE_ADMIN_UI=true)
# It's recommended to set these via `docker run -e` instead of hardcoding here
Expand All @@ -146,4 +148,4 @@ EXPOSE 3663
# By not specifying ENTRYPOINT or CMD here, we rely on the base image's defaults when built as an addon.
# For standalone builds, users will need to specify the command when running the container,
# e.g., docker run <image_name> tini -- node build/sse.js
# Or, a multi-stage build could define a specific entrypoint/cmd for the standalone target.
# Or, a multi-stage build could define a specific entrypoint/cmd for the standalone target.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ Example `config/tool_config.json`:
```bash
export ENABLE_ADMIN_UI=true
```
- **`ENABLE_ADMIN_TERMINAL`**: (Optional, recommended) Set to `true` to enable `/admin/terminal` routes in the Admin UI. Default: `false`.
- Keep this disabled unless you explicitly need web terminal access.
```bash
export ENABLE_ADMIN_TERMINAL=false
```
- **`ADMIN_USERNAME`**: (Required if Admin UI enabled) Username for Admin UI login. Default: `admin`.
- **`ADMIN_PASSWORD`**: (Required if Admin UI enabled) Password for Admin UI login. Default: `password` (**Change this!**).
```bash
Expand Down Expand Up @@ -580,4 +585,4 @@ This script wraps the execution of the built server (`build/index.js`) with the

## Reference

This project was originally inspired by and refactored from [adamwattis/mcp-proxy-server](https://github.com/adamwattis/mcp-proxy-server).
This project was originally inspired by and refactored from [adamwattis/mcp-proxy-server](https://github.com/adamwattis/mcp-proxy-server).
25 changes: 23 additions & 2 deletions public/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var discoveredTools = [];
var toolDataLoaded = false;
var adminEventSource = null; // This is the local variable for the current EventSource instance
var effectiveToolsFolder = 'tools'; // Default value if not fetched or empty
var enableAdminTerminal = false;
window.effectiveToolsFolder = effectiveToolsFolder; // Expose globally
window.adminEventSource = null; // Expose adminEventSource globally from the start and keep it as a data property
window.isServerConfigDirty = false; // Initialize and expose globally
Expand Down Expand Up @@ -152,6 +153,17 @@ window.connectAdminSSE = connectAdminSSE;
window.appendToInstallOutput = appendToInstallOutput;
window.getInstallOutputElement = getInstallOutputElement;

function applyTerminalNavState() {
if (!navTerminalButton) return;
if (enableAdminTerminal) {
navTerminalButton.disabled = false;
navTerminalButton.title = '';
} else {
navTerminalButton.disabled = true;
navTerminalButton.title = 'Terminal is disabled (set ENABLE_ADMIN_TERMINAL=true).';
}
}

const showSection = (sectionId) => {
document.querySelectorAll('.admin-section').forEach(section => {
section.style.display = 'none';
Expand Down Expand Up @@ -200,15 +212,19 @@ const handleLoginSuccess = async () => {
if (envResponse.ok) {
const envData = await envResponse.json();
window.effectiveToolsFolder = (envData.toolsFolder && envData.toolsFolder.trim() !== '') ? envData.toolsFolder.trim() : 'tools';
enableAdminTerminal = envData.enableAdminTerminal === true;
console.log("Effective TOOLS_FOLDER set to:", window.effectiveToolsFolder);
} else {
console.warn("Failed to fetch environment info, defaulting effectiveToolsFolder to 'tools'.");
window.effectiveToolsFolder = 'tools';
enableAdminTerminal = false;
}
} catch (err) {
console.error("Error fetching environment info (TOOLS_FOLDER):", err);
window.effectiveToolsFolder = 'tools';
enableAdminTerminal = false;
}
applyTerminalNavState();

showSection('servers-section');
if (typeof loadServerConfig === 'function') {
Expand Down Expand Up @@ -238,6 +254,8 @@ const handleLogoutSuccess = () => {
const serverList = document.getElementById('server-list'); if (serverList) serverList.innerHTML = '';
const toolList = document.getElementById('tool-list'); if (toolList) toolList.innerHTML = '';
currentServerConfig = {}; currentToolConfig = { tools: {} }; discoveredTools = []; toolDataLoaded = false;
enableAdminTerminal = false;
applyTerminalNavState();
loginError.textContent = '';
if (adminEventSource) {
adminEventSource.close();
Expand Down Expand Up @@ -338,7 +356,10 @@ document.addEventListener('DOMContentLoaded', () => {
else if (typeof loadToolData !== 'function') console.error("loadToolData not found.");
});
}
if (navTerminalButton) navTerminalButton.addEventListener('click', () => window.location.href = 'terminal.html');
if (navTerminalButton) navTerminalButton.addEventListener('click', () => {
if (!enableAdminTerminal) return;
window.location.href = 'terminal.html';
});
if (logoutButton) {
logoutButton.addEventListener('click', async () => {
try {
Expand Down Expand Up @@ -434,4 +455,4 @@ document.addEventListener('DOMContentLoaded', () => {

});

console.log("script.js loaded and initialized.");
console.log("script.js loaded and initialized.");
15 changes: 12 additions & 3 deletions src/sse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ const rawEnableAdminUI = process.env.ENABLE_ADMIN_UI;
// Enable Admin UI if ENABLE_ADMIN_UI is 'true' (case-insensitive), '1', or 'yes' (case-insensitive).
// Defaults to false if not set, empty, or any other value.
const enableAdminUI = typeof rawEnableAdminUI === 'string' && (rawEnableAdminUI.toLowerCase() === 'true' || rawEnableAdminUI === '1' || rawEnableAdminUI.toLowerCase() === 'yes');
// Separately control terminal exposure inside Admin UI.
const rawEnableAdminTerminal = process.env.ENABLE_ADMIN_TERMINAL;
const enableAdminTerminal = typeof rawEnableAdminTerminal === 'string' && (rawEnableAdminTerminal.toLowerCase() === 'true' || rawEnableAdminTerminal === '1' || rawEnableAdminTerminal.toLowerCase() === 'yes');

async function getSessionSecret(): Promise<string> {
if (SESSION_SECRET_ENV && SESSION_SECRET_ENV !== 'unsafe-default-secret' && SESSION_SECRET_ENV.trim() !== '') {
Expand Down Expand Up @@ -283,7 +286,8 @@ if (enableAdminUI) {
const config = await loadConfig();
res.json({
toolsFolder: process.env.TOOLS_FOLDER || "",
serverToolnameSeparator: config.serverToolnameSeparator // Expose the separator
serverToolnameSeparator: config.serverToolnameSeparator, // Expose the separator
enableAdminTerminal: enableAdminTerminal
});
} catch (error: any) {
logger.error("Error fetching environment info for admin UI:", error);
Expand Down Expand Up @@ -511,8 +515,13 @@ if (enableAdminUI) {
});
});

// Mount the terminal router under /admin/terminal, protected by authentication
app.use('/admin/terminal', isAuthenticated, terminalRouter);
if (enableAdminTerminal) {
logger.log("Admin terminal is ENABLED.");
// Mount the terminal router under /admin/terminal, protected by authentication
app.use('/admin/terminal', isAuthenticated, terminalRouter);
} else {
logger.log("Admin terminal is DISABLED. Set ENABLE_ADMIN_TERMINAL=true to enable.");
}


// Static file serving for admin UI should also be inside the if block
Expand Down