zerochat 7.3.0__py3-none-any.whl
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.
- zerochat-7.3.0.dist-info/METADATA +70 -0
- zerochat-7.3.0.dist-info/RECORD +130 -0
- zerochat-7.3.0.dist-info/WHEEL +5 -0
- zerochat-7.3.0.dist-info/entry_points.txt +2 -0
- zerochat-7.3.0.dist-info/licenses/LICENSE +34 -0
- zerochat-7.3.0.dist-info/top_level.txt +2 -0
- zerochat.py +2004 -0
- zerochat_runtime/__init__.py +1 -0
- zerochat_runtime/assets/css/base.css +199 -0
- zerochat_runtime/assets/css/components/composer.css +1258 -0
- zerochat_runtime/assets/css/components/debug.css +777 -0
- zerochat_runtime/assets/css/components/header.css +97 -0
- zerochat_runtime/assets/css/components/icons.css +39 -0
- zerochat_runtime/assets/css/components/markdown.css +685 -0
- zerochat_runtime/assets/css/components/messages.css +473 -0
- zerochat_runtime/assets/css/components/modals.css +1319 -0
- zerochat_runtime/assets/css/components/sidebar.css +622 -0
- zerochat_runtime/assets/css/components/tools.css +1223 -0
- zerochat_runtime/assets/css/layout.css +171 -0
- zerochat_runtime/assets/css/print.css +120 -0
- zerochat_runtime/assets/css/styles.css +18 -0
- zerochat_runtime/assets/css/theme-overrides.css +555 -0
- zerochat_runtime/assets/css/tokens.css +165 -0
- zerochat_runtime/assets/help/architecture.html +247 -0
- zerochat_runtime/assets/help/composer.html +134 -0
- zerochat_runtime/assets/help/conversations.html +105 -0
- zerochat_runtime/assets/help/debug.html +108 -0
- zerochat_runtime/assets/help/en/architecture.html +247 -0
- zerochat_runtime/assets/help/en/composer.html +134 -0
- zerochat_runtime/assets/help/en/conversations.html +104 -0
- zerochat_runtime/assets/help/en/debug.html +105 -0
- zerochat_runtime/assets/help/en/gemini-free.html +39 -0
- zerochat_runtime/assets/help/en/index.html +191 -0
- zerochat_runtime/assets/help/en/learning.html +71 -0
- zerochat_runtime/assets/help/en/mcp.html +143 -0
- zerochat_runtime/assets/help/en/openrouter-free.html +39 -0
- zerochat_runtime/assets/help/en/profiles.html +173 -0
- zerochat_runtime/assets/help/en/rag.html +130 -0
- zerochat_runtime/assets/help/en/reasoning-telemetry.html +108 -0
- zerochat_runtime/assets/help/en/tools-agent.html +133 -0
- zerochat_runtime/assets/help/en/webllm.html +355 -0
- zerochat_runtime/assets/help/gemini-free.html +39 -0
- zerochat_runtime/assets/help/help.css +604 -0
- zerochat_runtime/assets/help/help.js +51 -0
- zerochat_runtime/assets/help/index.html +194 -0
- zerochat_runtime/assets/help/learning.html +71 -0
- zerochat_runtime/assets/help/mcp.html +143 -0
- zerochat_runtime/assets/help/openrouter-free.html +39 -0
- zerochat_runtime/assets/help/profiles.html +174 -0
- zerochat_runtime/assets/help/rag.html +130 -0
- zerochat_runtime/assets/help/reasoning-telemetry.html +111 -0
- zerochat_runtime/assets/help/tools-agent.html +133 -0
- zerochat_runtime/assets/help/webllm.html +359 -0
- zerochat_runtime/assets/js/agent-core.js +1553 -0
- zerochat_runtime/assets/js/api.js +844 -0
- zerochat_runtime/assets/js/app.js +2558 -0
- zerochat_runtime/assets/js/attachments.js +220 -0
- zerochat_runtime/assets/js/charts.js +338 -0
- zerochat_runtime/assets/js/chat-engine.js +566 -0
- zerochat_runtime/assets/js/config-store.js +207 -0
- zerochat_runtime/assets/js/context-manager.js +584 -0
- zerochat_runtime/assets/js/conversation-service.js +484 -0
- zerochat_runtime/assets/js/cookies.js +688 -0
- zerochat_runtime/assets/js/data-reset-service.js +136 -0
- zerochat_runtime/assets/js/debug.js +508 -0
- zerochat_runtime/assets/js/defaults.js +16 -0
- zerochat_runtime/assets/js/export.js +179 -0
- zerochat_runtime/assets/js/file-parser.js +2088 -0
- zerochat_runtime/assets/js/generation-controller.js +417 -0
- zerochat_runtime/assets/js/i18n.js +1483 -0
- zerochat_runtime/assets/js/icons.js +156 -0
- zerochat_runtime/assets/js/ingestionEngine.js +436 -0
- zerochat_runtime/assets/js/markdown.js +588 -0
- zerochat_runtime/assets/js/mcp.js +1271 -0
- zerochat_runtime/assets/js/message-turns.js +62 -0
- zerochat_runtime/assets/js/profile-backup.js +118 -0
- zerochat_runtime/assets/js/profile-export-bundle.js +481 -0
- zerochat_runtime/assets/js/profile-repository.js +213 -0
- zerochat_runtime/assets/js/providers-webllm.js +486 -0
- zerochat_runtime/assets/js/providers.js +1560 -0
- zerochat_runtime/assets/js/rag-index.js +295 -0
- zerochat_runtime/assets/js/rag-service.js +530 -0
- zerochat_runtime/assets/js/rag-ui.js +997 -0
- zerochat_runtime/assets/js/ragStorage.js +676 -0
- zerochat_runtime/assets/js/sandbox.js +449 -0
- zerochat_runtime/assets/js/state.js +704 -0
- zerochat_runtime/assets/js/storage-db.js +144 -0
- zerochat_runtime/assets/js/tool-cards.js +330 -0
- zerochat_runtime/assets/js/tool-security.js +653 -0
- zerochat_runtime/assets/js/tools/README.md +50 -0
- zerochat_runtime/assets/js/tools/builtin/agent-checkpoint.tool.js +212 -0
- zerochat_runtime/assets/js/tools/builtin/download-pdf.tool.js +111 -0
- zerochat_runtime/assets/js/tools/builtin/execute-javascript.tool.js +219 -0
- zerochat_runtime/assets/js/tools/builtin/fetch-web-page.tool.js +112 -0
- zerochat_runtime/assets/js/tools/builtin/list-documents.tool.js +117 -0
- zerochat_runtime/assets/js/tools/builtin/read-knowledge-chunk.tool.js +129 -0
- zerochat_runtime/assets/js/tools/builtin/read-knowledge-image.tool.js +95 -0
- zerochat_runtime/assets/js/tools/builtin/render-chart.tool.js +90 -0
- zerochat_runtime/assets/js/tools/builtin/search-knowledge-base.tool.js +124 -0
- zerochat_runtime/assets/js/tools/builtin/search-web.tool.js +125 -0
- zerochat_runtime/assets/js/tools/tool-manifest.js +53 -0
- zerochat_runtime/assets/js/tools/tool-runtime.js +77 -0
- zerochat_runtime/assets/js/ui-composer.js +319 -0
- zerochat_runtime/assets/js/ui-conversation.js +655 -0
- zerochat_runtime/assets/js/ui-dialogs.js +132 -0
- zerochat_runtime/assets/js/ui-generation-status.js +119 -0
- zerochat_runtime/assets/js/ui-inspector.js +804 -0
- zerochat_runtime/assets/js/ui-mcp.js +604 -0
- zerochat_runtime/assets/js/ui-profiles.js +824 -0
- zerochat_runtime/assets/js/ui-reasoning.js +146 -0
- zerochat_runtime/assets/js/ui-settings.js +825 -0
- zerochat_runtime/assets/js/ui-shell.js +215 -0
- zerochat_runtime/assets/js/ui-sidebar.js +429 -0
- zerochat_runtime/assets/js/ui-telemetry.js +404 -0
- zerochat_runtime/assets/js/ui-transfer.js +262 -0
- zerochat_runtime/assets/js/utils.js +216 -0
- zerochat_runtime/assets/js/vendor/orama.browser.js +11 -0
- zerochat_runtime/assets/js/web-browser.js +569 -0
- zerochat_runtime/assets/js/web-search.js +519 -0
- zerochat_runtime/assets/manifest.webmanifest +20 -0
- zerochat_runtime/assets/services/dummy_mcp/dummy_mcp_server.py +47 -0
- zerochat_runtime/assets/services/dummy_mcp/service.json +22 -0
- zerochat_runtime/assets/services/lsp/installer.json +9 -0
- zerochat_runtime/assets/services/lsp/service.json +22 -0
- zerochat_runtime/assets/services/memory/installer.json +9 -0
- zerochat_runtime/assets/services/memory/service.json +24 -0
- zerochat_runtime/assets/services/playwright/installer.json +10 -0
- zerochat_runtime/assets/services/playwright/service.json +39 -0
- zerochat_runtime/assets/sw.js +164 -0
- zerochat_runtime/assets/zerochat.html +671 -0
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI for the local ZeroChat MCP host. External MCP products are discovered
|
|
3
|
+
* through the downloaded host; this bundle contains no product recipe or
|
|
4
|
+
* process implementation.
|
|
5
|
+
*/
|
|
6
|
+
(function (root, factory) {
|
|
7
|
+
if (typeof exports === 'object' && typeof module !== 'undefined') module.exports = factory();
|
|
8
|
+
else root.ChatUIMcp = factory();
|
|
9
|
+
})(typeof self !== 'undefined' ? self : this, function () {
|
|
10
|
+
'use strict';
|
|
11
|
+
const DEFAULT_HOST = '127.0.0.1';
|
|
12
|
+
const DEFAULT_PORT = 6388;
|
|
13
|
+
const resolveDep = (name, path) => (typeof window !== 'undefined' && window.ChatUtils?.resolveDep
|
|
14
|
+
? window.ChatUtils.resolveDep(name, path)
|
|
15
|
+
: ((typeof window !== 'undefined' && window[name]) || (typeof require !== 'undefined'
|
|
16
|
+
? (() => { try { return require(path); } catch (_) { return null; } })() : null)));
|
|
17
|
+
const getUtils = () => resolveDep('ChatUtils', './utils.js');
|
|
18
|
+
const getI18n = () => resolveDep('ChatI18n', './i18n.js');
|
|
19
|
+
const getIcons = () => resolveDep('ChatIcons', './icons.js');
|
|
20
|
+
const getState = () => resolveDep('ChatState', './state.js');
|
|
21
|
+
const getMCP = () => resolveDep('ChatMCP', './mcp.js');
|
|
22
|
+
const getConfig = () => resolveDep('ChatConfig', './config-store.js');
|
|
23
|
+
const getSecurity = () => resolveDep('ChatToolSecurity', './tool-security.js');
|
|
24
|
+
const t = (key, params) => getI18n()?.t ? getI18n().t(key, params) : key;
|
|
25
|
+
const escapeHtml = value => getUtils()?.escapeHtml ? getUtils().escapeHtml(value) : '';
|
|
26
|
+
|
|
27
|
+
function clearSafeContent(element) {
|
|
28
|
+
const utils = getUtils();
|
|
29
|
+
if (utils?.clearElement) return utils.clearElement(element);
|
|
30
|
+
if (element) element.textContent = '';
|
|
31
|
+
return element;
|
|
32
|
+
}
|
|
33
|
+
function appendSafeText(parent, tagName, value, options) {
|
|
34
|
+
const utils = getUtils();
|
|
35
|
+
const child = utils?.appendTextElement?.(parent, tagName, value, options);
|
|
36
|
+
if (!child && parent) parent.textContent = `${parent.textContent || ''}${value == null ? '' : String(value)}`;
|
|
37
|
+
return child;
|
|
38
|
+
}
|
|
39
|
+
function appendTrustedIcon(parent, icon) {
|
|
40
|
+
if (!parent || !icon || typeof document === 'undefined') return;
|
|
41
|
+
const host = document.createElement('span');
|
|
42
|
+
host.setAttribute('aria-hidden', 'true');
|
|
43
|
+
const utils = getUtils();
|
|
44
|
+
if (utils?.setTrustedHtml) utils.setTrustedHtml(host, icon);
|
|
45
|
+
else host.innerHTML = icon;
|
|
46
|
+
parent.appendChild(host);
|
|
47
|
+
}
|
|
48
|
+
function sanitizePort(port) {
|
|
49
|
+
const parsed = parseInt(port, 10);
|
|
50
|
+
return Number.isInteger(parsed) && parsed >= 1024 && parsed <= 65535 ? parsed : DEFAULT_PORT;
|
|
51
|
+
}
|
|
52
|
+
function sanitizeHost(host) { return String(host || '').trim() || DEFAULT_HOST; }
|
|
53
|
+
function buildMcpEndpoint(host, port, path = '/sse') {
|
|
54
|
+
const suffix = path ? (path.startsWith('/') ? path : `/${path}`) : '/sse';
|
|
55
|
+
return `http://${sanitizeHost(host)}:${sanitizePort(port)}${suffix}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function generateTerminalCommand() {
|
|
59
|
+
return 'curl -sL https://albalday.github.io/zerochat/zerochat.py -o zerochat.py && python3 zerochat.py';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function copyCommandToClipboard(text, btnElement, translator = t) {
|
|
63
|
+
if (!text) return false;
|
|
64
|
+
let ok = false;
|
|
65
|
+
if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
|
|
66
|
+
try { await navigator.clipboard.writeText(text); ok = true; } catch (e) {}
|
|
67
|
+
}
|
|
68
|
+
if (!ok && typeof document !== 'undefined' && document.body) {
|
|
69
|
+
const textarea = document.createElement('textarea');
|
|
70
|
+
textarea.value = text;
|
|
71
|
+
textarea.setAttribute('readonly', '');
|
|
72
|
+
textarea.style.position = 'fixed';
|
|
73
|
+
textarea.style.opacity = '0';
|
|
74
|
+
document.body.appendChild(textarea);
|
|
75
|
+
textarea.select();
|
|
76
|
+
try { ok = document.execCommand('copy'); } catch (e) {}
|
|
77
|
+
textarea.remove();
|
|
78
|
+
}
|
|
79
|
+
if (btnElement && ok) {
|
|
80
|
+
const orig = btnElement.innerHTML;
|
|
81
|
+
const check = getIcons()?.get ? getIcons().get('check', { size: 14 }) : '';
|
|
82
|
+
btnElement.innerHTML = `${check} <span>${translator('mcp_cmd_copied')}</span>`;
|
|
83
|
+
btnElement.classList.add('btn-copied');
|
|
84
|
+
const timer = setTimeout(() => { btnElement.innerHTML = orig; btnElement.classList.remove('btn-copied'); }, 2000);
|
|
85
|
+
if (typeof timer?.unref === 'function') timer.unref();
|
|
86
|
+
}
|
|
87
|
+
return ok;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function renderToolsList(container, tools, currentEnabledTools = {}, translator = t) {
|
|
91
|
+
if (!container) return;
|
|
92
|
+
const Icons = getIcons();
|
|
93
|
+
container.style.display = 'block';
|
|
94
|
+
|
|
95
|
+
if (!Array.isArray(tools) || tools.length === 0) {
|
|
96
|
+
const isConnected = getState()?.get ? getState().get('mcp')?.status === 'connected' : false;
|
|
97
|
+
const msg = isConnected ? translator('mcp_tools_empty_connected') : translator('mcp_tools_empty_disconnected');
|
|
98
|
+
const plug = Icons?.get ? Icons.get('plug', { size: 24 }) : '<svg class="ui-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22v-5"></path><path d="M9 8V2"></path><path d="M15 8V2"></path><path d="M18 8v5a6 6 0 0 1-12 0V8z"></path></svg>';
|
|
99
|
+
container.innerHTML = `<div class="mcp-tools-empty">${plug}<p>${msg}</p></div>`;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const plug16 = Icons?.get ? Icons.get('plug', { size: 16 }) : '';
|
|
104
|
+
const term16 = Icons?.get ? Icons.get('terminal', { size: 16 }) : '';
|
|
105
|
+
const badgeText = escapeHtml(translator('mcp_tool_badge'));
|
|
106
|
+
|
|
107
|
+
const Security = getSecurity();
|
|
108
|
+
const globalPolicy = Security?.manager?.getGlobalMcpPolicy ? Security.manager.getGlobalMcpPolicy() : 'ask';
|
|
109
|
+
|
|
110
|
+
const items = tools.map(tool => {
|
|
111
|
+
const id = tool.id || tool.name;
|
|
112
|
+
const isChecked = currentEnabledTools[id] !== undefined ? currentEnabledTools[id] !== false : (currentEnabledTools[tool.name] !== undefined ? currentEnabledTools[tool.name] !== false : true);
|
|
113
|
+
const desc = escapeHtml(tool.descFallback || tool.description || '');
|
|
114
|
+
|
|
115
|
+
let paramsHint = '';
|
|
116
|
+
const props = tool.inputSchema?.properties || tool.parameters?.properties;
|
|
117
|
+
if (props && typeof props === 'object') {
|
|
118
|
+
const req = Array.isArray(tool.inputSchema?.required || tool.parameters?.required) ? (tool.inputSchema?.required || tool.parameters?.required) : [];
|
|
119
|
+
paramsHint = Object.entries(props).map(([k, v]) => `${k}${req.includes(k) ? '' : '?'}: ${v.type || 'any'}`).join(', ');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const serverName = tool.metadata?.mcpServerName || '';
|
|
123
|
+
const originalName = tool.metadata?.originalName || tool.name;
|
|
124
|
+
const toolRule = Security?.manager?.getToolPolicy ? Security.manager.getToolPolicy(id) : null;
|
|
125
|
+
const toolConstraints = Security?.manager?.getToolConstraints ? Security.manager.getToolConstraints(id) : null;
|
|
126
|
+
const hasConstraints = !!(toolConstraints && (toolConstraints.command || toolConstraints.path));
|
|
127
|
+
const effectivePolicy = toolRule || 'ask';
|
|
128
|
+
|
|
129
|
+
let authTagHtml = '';
|
|
130
|
+
if (globalPolicy === 'allow_all') {
|
|
131
|
+
authTagHtml = `<span class="mcp-auth-badge status-allowed" title="${escapeHtml(translator('mcp_security_policy_allow_all'))}">${escapeHtml(translator('mcp_security_badge_allowed'))} (Global)</span>`;
|
|
132
|
+
} else {
|
|
133
|
+
const selectTitle = escapeHtml(translator('mcp_security_select_title'));
|
|
134
|
+
const optAsk = escapeHtml(translator('mcp_security_badge_ask'));
|
|
135
|
+
let optAllowed = escapeHtml(translator('mcp_security_badge_allowed'));
|
|
136
|
+
if (hasConstraints && effectivePolicy === 'allow') {
|
|
137
|
+
optAllowed += ` (${escapeHtml(translator('mcp_security_constrained') || 'Acotada')})`;
|
|
138
|
+
}
|
|
139
|
+
const optDenied = escapeHtml(translator('tool_auth_denied_badge'));
|
|
140
|
+
const statusClass = effectivePolicy === 'allow' ? 'status-allowed' : (effectivePolicy === 'deny' ? 'status-denied' : 'status-ask');
|
|
141
|
+
|
|
142
|
+
authTagHtml = `
|
|
143
|
+
<select class="mcp-auth-select ${statusClass}" data-tool-id="${escapeHtml(id)}" data-server-name="${escapeHtml(serverName)}" data-orig-name="${escapeHtml(originalName)}" title="${selectTitle}" aria-label="${selectTitle}">
|
|
144
|
+
<option value="ask" ${effectivePolicy === 'ask' ? 'selected' : ''}>${optAsk}</option>
|
|
145
|
+
<option value="allow" ${effectivePolicy === 'allow' ? 'selected' : ''}>${optAllowed}</option>
|
|
146
|
+
<option value="deny" ${effectivePolicy === 'deny' ? 'selected' : ''}>${optDenied}</option>
|
|
147
|
+
</select>`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return `
|
|
151
|
+
<div class="mcp-tool-card" data-tool-id="${escapeHtml(id)}">
|
|
152
|
+
<div class="mcp-tool-info">
|
|
153
|
+
<div class="mcp-tool-title-row">
|
|
154
|
+
<span class="mcp-tool-icon">${plug16}</span>
|
|
155
|
+
<span class="mcp-tool-name">${escapeHtml(tool.name)}</span>
|
|
156
|
+
<span class="mcp-tool-badge">${badgeText}</span>
|
|
157
|
+
${authTagHtml}
|
|
158
|
+
</div>
|
|
159
|
+
<p class="mcp-tool-desc">${desc}</p>
|
|
160
|
+
${paramsHint ? `<div class="mcp-tool-params"><code>${escapeHtml(paramsHint)}</code></div>` : ''}
|
|
161
|
+
</div>
|
|
162
|
+
<div class="mcp-tool-action">
|
|
163
|
+
<label class="switch">
|
|
164
|
+
<input type="checkbox" class="agent-tool-checkbox mcp-tool-checkbox" data-tool-id="${escapeHtml(id)}" ${isChecked ? 'checked' : ''}>
|
|
165
|
+
<span class="slider"></span>
|
|
166
|
+
</label>
|
|
167
|
+
</div>
|
|
168
|
+
</div>`;
|
|
169
|
+
}).join('');
|
|
170
|
+
|
|
171
|
+
container.innerHTML = `
|
|
172
|
+
<div class="mcp-tools-header">
|
|
173
|
+
<div class="mcp-tools-title">${term16}<span data-i18n="mcp_tools_section_title">${translator('mcp_tools_section_title')}</span></div>
|
|
174
|
+
<span class="mcp-tools-counter">${translator('mcp_tools_count', { count: tools.length })}</span>
|
|
175
|
+
</div>
|
|
176
|
+
<div class="mcp-tools-list">${items}</div>`;
|
|
177
|
+
|
|
178
|
+
container.querySelectorAll?.('.mcp-auth-select').forEach(sel => {
|
|
179
|
+
sel.addEventListener?.('change', () => {
|
|
180
|
+
const tid = sel.getAttribute?.('data-tool-id');
|
|
181
|
+
if (!tid) return;
|
|
182
|
+
const newPolicy = sel.value;
|
|
183
|
+
const sName = sel.getAttribute?.('data-server-name') || '';
|
|
184
|
+
const oName = sel.getAttribute?.('data-orig-name') || tid;
|
|
185
|
+
|
|
186
|
+
if (Security?.manager) {
|
|
187
|
+
if (newPolicy === 'ask') {
|
|
188
|
+
Security.manager.revokeToolPolicy(tid);
|
|
189
|
+
} else {
|
|
190
|
+
Security.manager.setToolPolicy(tid, newPolicy, {
|
|
191
|
+
serverName: sName,
|
|
192
|
+
originalName: oName
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Actualizar clase visual del select según la nueva opción
|
|
198
|
+
sel.className = `mcp-auth-select status-${newPolicy === 'allow' ? 'allowed' : (newPolicy === 'deny' ? 'denied' : 'ask')}`;
|
|
199
|
+
|
|
200
|
+
// Sincronizar lista de autorizaciones guardadas en el modal si está disponible
|
|
201
|
+
const savedListEl = typeof document !== 'undefined' ? document.getElementById('mcp-saved-auths-list') : null;
|
|
202
|
+
if (savedListEl && typeof renderSavedAuthorizations === 'function') {
|
|
203
|
+
renderSavedAuthorizations({
|
|
204
|
+
savedAuthsList: savedListEl,
|
|
205
|
+
btnClearAuths: document.getElementById('btn-mcp-clear-auths')
|
|
206
|
+
}, translator);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
container.querySelectorAll?.('.mcp-tool-checkbox').forEach(cb => {
|
|
212
|
+
cb.addEventListener?.('change', () => {
|
|
213
|
+
const tid = cb.getAttribute?.('data-tool-id');
|
|
214
|
+
if (!tid) return;
|
|
215
|
+
const Config = getConfig();
|
|
216
|
+
if (Config) {
|
|
217
|
+
const curr = (Config.get?.() || Config.getActive?.())?.enabledTools || {};
|
|
218
|
+
(Config.updateRuntime || Config.update)?.call(Config, { enabledTools: { ...curr, [tid]: cb.checked } });
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function renderExternalServers(container, servers, translator = t) {
|
|
225
|
+
if (!container) return;
|
|
226
|
+
if (!Array.isArray(servers) || servers.length === 0) {
|
|
227
|
+
container.innerHTML = `<div class="mcp-servers-empty label-hint">${escapeHtml(translator('mcp_servers_empty'))}</div>`;
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const language = getI18n()?.getLanguage?.() || 'es';
|
|
231
|
+
container.innerHTML = servers.map(server => {
|
|
232
|
+
const isRunning = server.status === 'running';
|
|
233
|
+
const isStarting = server.status === 'starting';
|
|
234
|
+
const isInstalling = server.status === 'installing';
|
|
235
|
+
const isBusy = isStarting || isInstalling;
|
|
236
|
+
const statusClass = isRunning ? 'status-running' : (isBusy ? 'status-starting' : (server.status === 'error' ? 'status-error' : 'status-stopped'));
|
|
237
|
+
const statusLabel = translator(`mcp_external_status_${server.status || 'stopped'}`);
|
|
238
|
+
const desc = escapeHtml(server.description?.[language] || server.description?.es || server.description || '');
|
|
239
|
+
const err = server.error ? `<p class="mcp-server-error">${escapeHtml(server.error)}</p>` : '';
|
|
240
|
+
let optionsHtml = '';
|
|
241
|
+
if (Array.isArray(server.options) && server.options.length > 0) {
|
|
242
|
+
optionsHtml = `<div class="mcp-server-options">` + server.options.map(opt => {
|
|
243
|
+
const optLabel = escapeHtml(opt.label?.[language] || opt.label?.es || opt.label || opt.id);
|
|
244
|
+
const optDesc = opt.description ? `<span class="label-hint">${escapeHtml(opt.description?.[language] || opt.description?.es || opt.description || '')}</span>` : '';
|
|
245
|
+
const userVal = server.userOptions && opt.id in server.userOptions ? server.userOptions[opt.id] : opt.default;
|
|
246
|
+
if (opt.type === 'boolean') {
|
|
247
|
+
const isChecked = Boolean(userVal);
|
|
248
|
+
return `
|
|
249
|
+
<div class="mcp-server-option-row">
|
|
250
|
+
<label class="switch switch-sm">
|
|
251
|
+
<input type="checkbox" class="mcp-server-option-checkbox" data-server-id="${escapeHtml(server.id)}" data-option-id="${escapeHtml(opt.id)}" ${isChecked ? 'checked' : ''}>
|
|
252
|
+
<span class="slider round"></span>
|
|
253
|
+
</label>
|
|
254
|
+
<div class="mcp-server-option-meta">
|
|
255
|
+
<span class="mcp-server-option-label">${optLabel}</span>
|
|
256
|
+
${optDesc}
|
|
257
|
+
</div>
|
|
258
|
+
</div>`;
|
|
259
|
+
}
|
|
260
|
+
return '';
|
|
261
|
+
}).join('') + `</div>`;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return `
|
|
265
|
+
<div class="mcp-server-item" data-server-id="${escapeHtml(server.id)}">
|
|
266
|
+
<div class="mcp-server-info">
|
|
267
|
+
<div class="mcp-server-title-row">
|
|
268
|
+
<strong class="mcp-server-name">${escapeHtml(server.displayName?.[language] || server.displayName?.es || server.displayName || server.id)}</strong>
|
|
269
|
+
<span class="mcp-server-badge ${statusClass}">${escapeHtml(statusLabel)}</span>
|
|
270
|
+
${server.toolCount ? `<span class="mcp-server-tool-count">${escapeHtml(translator('mcp_servers_count_tools', { count: server.toolCount }))}</span>` : ''}
|
|
271
|
+
</div>
|
|
272
|
+
${desc ? `<p class="mcp-server-desc">${desc}</p>` : ''}
|
|
273
|
+
${err}
|
|
274
|
+
${optionsHtml}
|
|
275
|
+
</div>
|
|
276
|
+
<div class="mcp-server-actions">
|
|
277
|
+
<button type="button" class="btn-mcp-server-toggle ${isRunning ? 'btn-danger' : 'btn-secondary'}" data-server-id="${escapeHtml(server.id)}" data-action="${isRunning ? 'stop' : 'start'}" ${isBusy ? 'disabled' : ''}>
|
|
278
|
+
${escapeHtml(isRunning ? translator('mcp_btn_stop_server') : translator('mcp_btn_start_server'))}
|
|
279
|
+
</button>
|
|
280
|
+
</div>
|
|
281
|
+
</div>`;
|
|
282
|
+
}).join('');
|
|
283
|
+
|
|
284
|
+
container.querySelectorAll?.('.mcp-server-option-checkbox').forEach(cb => {
|
|
285
|
+
cb.addEventListener?.('change', async () => {
|
|
286
|
+
const sid = cb.getAttribute?.('data-server-id');
|
|
287
|
+
const optId = cb.getAttribute?.('data-option-id');
|
|
288
|
+
if (!sid || !optId) return;
|
|
289
|
+
cb.disabled = true;
|
|
290
|
+
const MCP = getMCP();
|
|
291
|
+
try {
|
|
292
|
+
await MCP?.manager?.configureExternalServer?.(sid, {
|
|
293
|
+
options: { [optId]: cb.checked }
|
|
294
|
+
});
|
|
295
|
+
const updated = await MCP?.manager?.fetchExternalServers?.();
|
|
296
|
+
const State = getState();
|
|
297
|
+
if (updated && State?.set) {
|
|
298
|
+
const current = State.get('mcp') || {};
|
|
299
|
+
State.set('mcp', {
|
|
300
|
+
...current,
|
|
301
|
+
externalHost: updated.host || 'running',
|
|
302
|
+
externalServers: updated.servers || []
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
} catch (e) {
|
|
306
|
+
console.error('[MCP UI] Error configuring server option:', e);
|
|
307
|
+
cb.checked = !cb.checked;
|
|
308
|
+
} finally {
|
|
309
|
+
cb.disabled = false;
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
container.querySelectorAll?.('.btn-mcp-server-toggle').forEach(btn => {
|
|
315
|
+
btn.addEventListener?.('click', async () => {
|
|
316
|
+
const sid = btn.getAttribute?.('data-server-id');
|
|
317
|
+
const action = btn.getAttribute?.('data-action');
|
|
318
|
+
if (!sid) return;
|
|
319
|
+
btn.disabled = true;
|
|
320
|
+
const MCP = getMCP();
|
|
321
|
+
try {
|
|
322
|
+
if (action === 'start') {
|
|
323
|
+
await MCP?.manager?.startExternalServer?.(sid);
|
|
324
|
+
} else {
|
|
325
|
+
await MCP?.manager?.stopExternalServer?.(sid);
|
|
326
|
+
}
|
|
327
|
+
} finally {
|
|
328
|
+
btn.disabled = false;
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function renderConnectionStatus(elements, mcpState, translator = t) {
|
|
335
|
+
if (!elements) return;
|
|
336
|
+
const State = getState();
|
|
337
|
+
const state = mcpState || State?.get?.('mcp') || { status: 'disconnected', host: DEFAULT_HOST, port: DEFAULT_PORT, tools: [] };
|
|
338
|
+
const status = state.status || 'disconnected';
|
|
339
|
+
const isConn = status === 'connected';
|
|
340
|
+
|
|
341
|
+
if (elements.statusBadge) elements.statusBadge.className = `mcp-status-badge mcp-status-${status}`;
|
|
342
|
+
if (elements.statusText) {
|
|
343
|
+
if (typeof elements.statusText.setAttribute === 'function') {
|
|
344
|
+
elements.statusText.setAttribute('data-i18n', `mcp_status_${status}`);
|
|
345
|
+
}
|
|
346
|
+
elements.statusText.textContent = translator(`mcp_status_${status}`);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (elements.serverDetails) {
|
|
350
|
+
elements.serverDetails.style.display = isConn ? 'inline-flex' : 'none';
|
|
351
|
+
clearSafeContent(elements.serverDetails);
|
|
352
|
+
if (isConn) {
|
|
353
|
+
const name = state.serverInfo?.name || 'mcp-proxy';
|
|
354
|
+
const ver = state.serverInfo?.version ? ` v${state.serverInfo.version}` : '';
|
|
355
|
+
const latency = Number(state.latencyMs);
|
|
356
|
+
const lat = Number.isFinite(latency) && latency > 0 ? ` · ${latency}ms` : '';
|
|
357
|
+
const count = Array.isArray(state.tools) ? state.tools.length : 0;
|
|
358
|
+
const toolLabel = count > 0 ? translator('mcp_tools_discovered', { count }) : translator('mcp_no_tools');
|
|
359
|
+
const titleStr = `${name}${ver}${lat}`.trim();
|
|
360
|
+
if (typeof elements.serverDetails.setAttribute === 'function') {
|
|
361
|
+
elements.serverDetails.setAttribute('title', titleStr);
|
|
362
|
+
}
|
|
363
|
+
elements.serverDetails.title = titleStr;
|
|
364
|
+
appendSafeText(elements.serverDetails, 'span', toolLabel, { className: 'mcp-detail-item' });
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (elements.bootstrapCard) elements.bootstrapCard.style.display = isConn ? 'none' : 'block';
|
|
369
|
+
if (elements.reconnectHint) {
|
|
370
|
+
elements.reconnectHint.style.display = status === 'disconnected' ? 'block' : 'none';
|
|
371
|
+
if (typeof elements.reconnectHint.setAttribute === 'function') {
|
|
372
|
+
elements.reconnectHint.setAttribute('data-i18n', 'mcp_reconnect_after_restart');
|
|
373
|
+
}
|
|
374
|
+
elements.reconnectHint.textContent = translator('mcp_reconnect_after_restart');
|
|
375
|
+
}
|
|
376
|
+
if (elements.commandSnippet) elements.commandSnippet.textContent = generateTerminalCommand();
|
|
377
|
+
|
|
378
|
+
if (elements.toolsContainer) {
|
|
379
|
+
const currentConfig = getConfig()?.get?.() || {};
|
|
380
|
+
const allTools = isConn ? (state.tools || []) : [];
|
|
381
|
+
renderToolsList(elements.toolsContainer, allTools, currentConfig.enabledTools || {}, translator);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (elements.serversList) {
|
|
385
|
+
const servers = isConn ? (state.externalServers || []) : [];
|
|
386
|
+
renderExternalServers(elements.serversList, servers, translator);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function renderSavedAuthorizations(elements, translator = t) {
|
|
391
|
+
if (!elements && typeof document === 'undefined') return;
|
|
392
|
+
const container = elements?.savedAuthsList || (typeof document !== 'undefined' ? document.getElementById('mcp-saved-auths-list') : null);
|
|
393
|
+
const btnClear = elements?.btnClearAuths || (typeof document !== 'undefined' ? document.getElementById('btn-mcp-clear-auths') : null);
|
|
394
|
+
if (!container) return;
|
|
395
|
+
|
|
396
|
+
const Security = getSecurity();
|
|
397
|
+
const authorized = Security?.manager?.listAuthorizedTools ? Security.manager.listAuthorizedTools() : [];
|
|
398
|
+
|
|
399
|
+
if (!authorized || authorized.length === 0) {
|
|
400
|
+
container.innerHTML = `<div class="mcp-no-auths-msg label-hint">${escapeHtml(translator('mcp_security_no_saved_auths'))}</div>`;
|
|
401
|
+
if (btnClear) btnClear.style.display = 'none';
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (btnClear) btnClear.style.display = 'inline-flex';
|
|
406
|
+
|
|
407
|
+
const Icons = getIcons();
|
|
408
|
+
const trashIcon = Icons?.get ? Icons.get('trash', { size: 12 }) : '';
|
|
409
|
+
|
|
410
|
+
container.innerHTML = authorized.map(item => {
|
|
411
|
+
const toolId = escapeHtml(item.toolId);
|
|
412
|
+
const origName = escapeHtml(item.originalName || item.toolId);
|
|
413
|
+
const isAllowed = item.policy === 'allow';
|
|
414
|
+
const badgeClass = isAllowed ? 'status-allowed' : 'status-denied';
|
|
415
|
+
let badgeText = escapeHtml(isAllowed ? translator('mcp_security_badge_allowed') : translator('tool_auth_denied_badge'));
|
|
416
|
+
const revokeLabel = escapeHtml(translator('mcp_security_btn_revoke'));
|
|
417
|
+
|
|
418
|
+
let constraintTag = '';
|
|
419
|
+
if (item.constraints) {
|
|
420
|
+
const parts = [];
|
|
421
|
+
if (item.constraints.command?.allowedPrefixes) {
|
|
422
|
+
parts.push(item.constraints.command.allowedPrefixes.join(', '));
|
|
423
|
+
}
|
|
424
|
+
if (item.constraints.path?.allowedDirectories) {
|
|
425
|
+
parts.push(item.constraints.path.allowedDirectories.join(', '));
|
|
426
|
+
}
|
|
427
|
+
if (parts.length > 0) {
|
|
428
|
+
badgeText += ` (${escapeHtml(translator('mcp_security_constrained') || 'Acotada')})`;
|
|
429
|
+
constraintTag = `<span class="mcp-auth-constraint-tag" title="${escapeHtml(parts.join(' | '))}">${escapeHtml(parts.join(' | '))}</span>`;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return `
|
|
434
|
+
<div class="mcp-auth-item" data-tool-id="${toolId}">
|
|
435
|
+
<div class="mcp-auth-item-info">
|
|
436
|
+
<strong class="mcp-auth-item-name">${origName}</strong>
|
|
437
|
+
<span class="mcp-auth-badge ${badgeClass}">${badgeText}</span>
|
|
438
|
+
${constraintTag}
|
|
439
|
+
</div>
|
|
440
|
+
<button type="button" class="btn-revoke-auth" data-tool-id="${toolId}" title="${revokeLabel}">
|
|
441
|
+
${trashIcon} <span>${revokeLabel}</span>
|
|
442
|
+
</button>
|
|
443
|
+
</div>`;
|
|
444
|
+
}).join('');
|
|
445
|
+
|
|
446
|
+
container.querySelectorAll('.btn-revoke-auth').forEach(btn => {
|
|
447
|
+
btn.addEventListener('click', () => {
|
|
448
|
+
const tid = btn.getAttribute('data-tool-id');
|
|
449
|
+
if (tid && Security?.manager?.revokeToolPolicy) {
|
|
450
|
+
Security.manager.revokeToolPolicy(tid);
|
|
451
|
+
renderSavedAuthorizations(elements, translator);
|
|
452
|
+
if (elements?.toolsContainer) {
|
|
453
|
+
const currentConfig = getConfig()?.get?.() || {};
|
|
454
|
+
const state = getState()?.get?.('mcp') || {};
|
|
455
|
+
const allTools = state.tools || [];
|
|
456
|
+
renderToolsList(elements.toolsContainer, allTools, currentConfig.enabledTools || {}, translator);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function initMcpUI(elements, options = {}) {
|
|
464
|
+
if (!elements) return null;
|
|
465
|
+
const State = getState();
|
|
466
|
+
const Security = getSecurity();
|
|
467
|
+
|
|
468
|
+
// Controles de Seguridad MCP
|
|
469
|
+
const radioAsk = elements.mcpSetupDialog?.querySelector?.('#mcp-policy-ask') || (typeof document !== 'undefined' ? document.getElementById('mcp-policy-ask') : null);
|
|
470
|
+
const radioAllowAll = elements.mcpSetupDialog?.querySelector?.('#mcp-policy-allow-all') || (typeof document !== 'undefined' ? document.getElementById('mcp-policy-allow-all') : null);
|
|
471
|
+
const btnClearAuths = elements.mcpSetupDialog?.querySelector?.('#btn-mcp-clear-auths') || (typeof document !== 'undefined' ? document.getElementById('btn-mcp-clear-auths') : null);
|
|
472
|
+
|
|
473
|
+
function syncSecurityControls() {
|
|
474
|
+
const currentGlobalPolicy = Security?.manager?.getGlobalMcpPolicy ? Security.manager.getGlobalMcpPolicy() : 'ask';
|
|
475
|
+
if (radioAsk) radioAsk.checked = (currentGlobalPolicy === 'ask');
|
|
476
|
+
if (radioAllowAll) radioAllowAll.checked = (currentGlobalPolicy === 'allow_all');
|
|
477
|
+
renderSavedAuthorizations(elements, t);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function renderCurrentToolsList() {
|
|
481
|
+
if (elements.toolsContainer) {
|
|
482
|
+
const currentCfg = getConfig()?.get?.() || {};
|
|
483
|
+
const st = State?.get?.('mcp') || {};
|
|
484
|
+
const allTools = st.tools || [];
|
|
485
|
+
renderToolsList(elements.toolsContainer, allTools, currentCfg.enabledTools || {}, t);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
radioAsk?.addEventListener?.('change', () => {
|
|
490
|
+
if (radioAsk.checked && Security?.manager?.setGlobalMcpPolicy) {
|
|
491
|
+
Security.manager.setGlobalMcpPolicy('ask');
|
|
492
|
+
renderCurrentToolsList();
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
radioAllowAll?.addEventListener?.('change', () => {
|
|
497
|
+
if (radioAllowAll.checked && Security?.manager?.setGlobalMcpPolicy) {
|
|
498
|
+
Security.manager.setGlobalMcpPolicy('allow_all');
|
|
499
|
+
renderCurrentToolsList();
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
btnClearAuths?.addEventListener?.('click', () => {
|
|
504
|
+
if (Security?.manager?.clearAllAuthorizations) {
|
|
505
|
+
Security.manager.clearAllAuthorizations();
|
|
506
|
+
renderSavedAuthorizations(elements, t);
|
|
507
|
+
renderCurrentToolsList();
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
const unsubscribeSecurity = Security?.manager?.subscribe ? Security.manager.subscribe(() => {
|
|
512
|
+
syncSecurityControls();
|
|
513
|
+
renderCurrentToolsList();
|
|
514
|
+
}) : null;
|
|
515
|
+
|
|
516
|
+
elements.btnCopyCmd?.addEventListener?.('click', () => {
|
|
517
|
+
copyCommandToClipboard(generateTerminalCommand(), elements.btnCopyCmd, t);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
const unsubscribe = State?.subscribe?.('mcp', (newState) => {
|
|
522
|
+
renderConnectionStatus(elements, newState, t);
|
|
523
|
+
});
|
|
524
|
+
renderConnectionStatus(elements, State?.get?.('mcp'), t);
|
|
525
|
+
syncSecurityControls();
|
|
526
|
+
|
|
527
|
+
async function syncExternalServers() {
|
|
528
|
+
if (!elements.serversList) return;
|
|
529
|
+
const MCP = getMCP();
|
|
530
|
+
const State = getState();
|
|
531
|
+
if (State?.get?.('mcp')?.status !== 'connected') {
|
|
532
|
+
renderExternalServers(elements.serversList, [], t);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
try {
|
|
536
|
+
await MCP?.manager?.syncExternalServers?.();
|
|
537
|
+
} catch (e) {
|
|
538
|
+
console.warn('[MCP UI] syncExternalServers failed:', e);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const handleLanguageChange = () => {
|
|
543
|
+
renderConnectionStatus(elements, State?.get?.('mcp'), t);
|
|
544
|
+
renderSavedAuthorizations(elements, t);
|
|
545
|
+
syncExternalServers();
|
|
546
|
+
};
|
|
547
|
+
const I18n = getI18n();
|
|
548
|
+
let unsubscribeLang = null;
|
|
549
|
+
if (I18n?.onChange) {
|
|
550
|
+
unsubscribeLang = I18n.onChange(handleLanguageChange);
|
|
551
|
+
} else if (typeof window !== 'undefined' && typeof window.addEventListener === 'function') {
|
|
552
|
+
window.addEventListener('zerochat:languagechange', handleLanguageChange);
|
|
553
|
+
unsubscribeLang = () => window.removeEventListener('zerochat:languagechange', handleLanguageChange);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return {
|
|
557
|
+
syncSecurityControls,
|
|
558
|
+
syncExternalServers,
|
|
559
|
+
render: () => renderConnectionStatus(elements, State?.get?.('mcp'), t),
|
|
560
|
+
destroy: () => {
|
|
561
|
+
if (typeof unsubscribe === 'function') unsubscribe();
|
|
562
|
+
if (typeof unsubscribeSecurity === 'function') unsubscribeSecurity();
|
|
563
|
+
if (typeof unsubscribeLang === 'function') unsubscribeLang();
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
async function autoConnectIfAvailable(options = {}) {
|
|
569
|
+
const Config = getConfig();
|
|
570
|
+
const MCP = getMCP();
|
|
571
|
+
const currentConfig = Config?.get?.() || Config?.getActive?.() || {};
|
|
572
|
+
const host = options.host || currentConfig.mcpHost || DEFAULT_HOST;
|
|
573
|
+
const port = sanitizePort(options.port || currentConfig.mcpPort || DEFAULT_PORT);
|
|
574
|
+
const timeoutMs = options.timeoutMs || 1500;
|
|
575
|
+
|
|
576
|
+
if (MCP?.manager?.autoConnectIfAvailable) {
|
|
577
|
+
return await MCP.manager.autoConnectIfAvailable({ host, port, timeoutMs });
|
|
578
|
+
}
|
|
579
|
+
return { success: false, available: false };
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
async function verifyActiveConnection(options = {}) {
|
|
583
|
+
const MCP = getMCP();
|
|
584
|
+
return MCP?.manager?.verifyProxyConnection
|
|
585
|
+
? MCP.manager.verifyProxyConnection({ timeoutMs: options.timeoutMs || 1500 })
|
|
586
|
+
: { success: false, skipped: true };
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
return {
|
|
590
|
+
DEFAULT_HOST,
|
|
591
|
+
DEFAULT_PORT,
|
|
592
|
+
sanitizePort,
|
|
593
|
+
sanitizeHost,
|
|
594
|
+
buildMcpEndpoint,
|
|
595
|
+
generateTerminalCommand,
|
|
596
|
+
copyCommandToClipboard,
|
|
597
|
+
renderConnectionStatus,
|
|
598
|
+
renderToolsList,
|
|
599
|
+
renderExternalServers,
|
|
600
|
+
initMcpUI,
|
|
601
|
+
autoConnectIfAvailable,
|
|
602
|
+
verifyActiveConnection
|
|
603
|
+
};
|
|
604
|
+
});
|