Skip to content

Instantly share code, notes, and snippets.

@artydev
Last active June 17, 2026 15:01
Show Gist options
  • Select an option

  • Save artydev/e5271161177c266e07c05514db72ac68 to your computer and use it in GitHub Desktop.

Select an option

Save artydev/e5271161177c266e07c05514db72ac68 to your computer and use it in GitHub Desktop.
OLLAMA TERMINAL - WTH - MCP SERVER
import { get, set, del } from 'https://cdn.jsdelivr.net/npm/idb-keyval@6/+esm';
/* ──────────────────────────────────────────────────────────────
DOM REFS
────────────────────────────────────────────────────────────── */
const log = document.getElementById("log");
const input = document.getElementById("input");
const sessionBar = document.getElementById("sessionBar");
const terminalPrompt = document.getElementById("terminalPrompt");
const mcpDot = document.getElementById("mcpDot");
const mcpStatusEl = document.getElementById("mcpStatus");
const statusModelEl = document.getElementById("statusModel");
const toolCountEl = document.getElementById("toolCount");
const msgCountEl = document.getElementById("msgCount");
const stopBtn = document.getElementById("stopBtn");
/* ──────────────────────────────────────────────────────────────
CONSTANTS & ENDPOINTS
────────────────────────────────────────────────────────────── */
const OLLAMA_URL = "http://localhost:11434/api/chat";
const ALBERT_URL = "http://localhost:3000/proxy/albert";
const MCP_SERVER_URL = "http://localhost:3000";
const REGISTRY_KEY = "multiplexer_registry_config";
const SESSION_PREFIX = "multiplexer_session_";
const AVAILABLE_MODELS = [
"gemma4:e4b",
"gpt-oss:120b-cloud",
"AgentPublic/llama3-instruct-8b",
"albert-small",
"albert-large"
];
const SYSTEM_PROMPT = {
role: "system",
content: "You are a helpful local terminal intelligence agent. You have real-time access to advanced local host platform systems tools. Invoke them whenever required to answer questions accurately."
};
/* ──────────────────────────────────────────────────────────────
STATE
────────────────────────────────────────────────────────────── */
let registry = { currentActiveId: null, activeModel: "gemma4:e4b", list: [] };
let activeMessages = [SYSTEM_PROMPT];
let fullPersistentHistory = [];
let RUNTIME_AVAILABLE_TOOLS = [];
let ALBERT_API_KEY = "";
const CMD_HISTORY_MAX = 50;
let cmdHistory = [];
let historyIndex = -1;
let historyDraft = "";
let currentAbortController = null;
// Smart Auto-Scroll State Tracking Indicator
let shouldAutoScroll = true;
function setGenerating(on) {
if (on) {
stopBtn.classList.add("visible");
input.disabled = true;
input.placeholder = "generating… (Esc to stop)";
} else {
stopBtn.classList.remove("visible");
input.disabled = false;
input.placeholder = "enter a prompt or /help…";
input.focus();
currentAbortController = null;
}
}
/* ──────────────────────────────────────────────────────────────
SCROLL POSITION DETECTOR
────────────────────────────────────────────────────────────── */
log.addEventListener("scroll", () => {
// A threshold buffer window of 15px handles elastic scroll bouncing on mobile/trackpads
const threshold = 15;
const isAtBottom = log.scrollHeight - log.scrollTop - log.clientHeight <= threshold;
if (isAtBottom) {
shouldAutoScroll = true;
} else {
// User explicitly scrolled away from the floor margin threshold
shouldAutoScroll = false;
}
});
function executeAutoScroll() {
if (shouldAutoScroll) {
log.scrollTop = log.scrollHeight;
}
}
/* ──────────────────────────────────────────────────────────────
STATUS BAR HELPERS
────────────────────────────────────────────────────────────── */
function setMcpStatus(online, toolCount = 0) {
if (online) {
mcpDot.className = "status-dot online";
mcpStatusEl.className = "";
mcpStatusEl.style.color = "var(--green)";
mcpStatusEl.textContent = "MCP online";
} else {
mcpDot.className = "status-dot offline";
mcpStatusEl.className = "offline";
mcpStatusEl.style.color = "var(--red)";
mcpStatusEl.textContent = "MCP offline";
}
toolCountEl.textContent = `${toolCount} tool${toolCount !== 1 ? 's' : ''}`;
}
function updateStatusBar() {
statusModelEl.textContent = registry.activeModel || "—";
const userMsgs = activeMessages.filter(m => m.role === "user").length;
msgCountEl.textContent = `${userMsgs} msg${userMsgs !== 1 ? 's' : ''}`;
}
/* ──────────────────────────────────────────────────────────────
MCP CLIENT
────────────────────────────────────────────────────────────── */
async function fetchToolsFromMcpServer() {
try {
print(`connecting to MCP gateway at ${MCP_SERVER_URL}…`, "sys");
const response = await fetch(MCP_SERVER_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ jsonrpc: "2.0", method: "tools/list", params: {}, id: 1 })
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
if (data.result?.tools) {
RUNTIME_AVAILABLE_TOOLS = data.result.tools.map(t => ({
type: "function",
function: {
name: t.name,
description: t.description,
parameters: {
type: t.inputSchema?.type || "object",
properties: t.inputSchema?.properties || {},
required: t.inputSchema?.required || []
}
}
}));
setMcpStatus(true, RUNTIME_AVAILABLE_TOOLS.length);
print(`🟢 MCP connected — ${RUNTIME_AVAILABLE_TOOLS.length} tools loaded`, "sys");
}
} catch (err) {
setMcpStatus(false, 0);
print(`🔴 MCP offline (${err.message}) — text-only mode`, "sys");
RUNTIME_AVAILABLE_TOOLS = [];
}
}
async function executeMcpToolCall(name, args, signal = null) {
try {
const response = await fetch(MCP_SERVER_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ jsonrpc: "2.0", method: "tools/call", params: { name, arguments: args }, id: Date.now() }),
signal
});
if (!response.ok) {
const errData = await response.json().catch(() => ({}));
throw new Error(errData.error?.message || `HTTP ${response.status}`);
}
const rpcResult = await response.json();
if (rpcResult.error) throw new Error(rpcResult.error.message);
const content = rpcResult.result;
if (content?.content && Array.isArray(content.content)) {
return content.content.filter(c => c.type === "text").map(c => c.text).join("\n") || "No output.";
}
return JSON.stringify(content);
} catch (err) {
if (err.name === "AbortError") return "Tool error: Execution aborted by user.";
return `Tool error: ${err.message}`;
}
}
/* ──────────────────────────────────────────────────────────────
CONTEXT PRUNING
────────────────────────────────────────────────────────────── */
function pruneActiveMemory() {
const sys = activeMessages[0];
let hist = activeMessages.slice(1);
while (hist.length > 16) hist.shift();
while (hist.length > 0 && (hist[0].role === "tool" || hist[0].content === "")) hist.shift();
activeMessages = [sys, ...hist];
updateStatusBar();
}
/* ──────────────────────────────────────────────────────────────
PERSISTENCE
────────────────────────────────────────────────────────────── */
async function saveAllToBrowser() {
try {
await set(REGISTRY_KEY, registry);
if (registry.currentActiveId) await set(SESSION_PREFIX + registry.currentActiveId, fullPersistentHistory);
} catch (err) { console.error("Storage write error:", err); }
}
async function loadSessionData(sessionId) {
try {
const saved = await get(SESSION_PREFIX + sessionId);
fullPersistentHistory = saved || [];
activeMessages = [SYSTEM_PROMPT, ...fullPersistentHistory.filter(m => !m.isToolActivityLog)];
pruneActiveMemory();
renderTerminalScreen();
renderTopMultiplexerBar();
} catch (err) { print("DB error: " + err.message, "err"); }
}
/* ──────────────────────────────────────────────────────────────
RENDERING
────────────────────────────────────────────────────────────── */
function renderTopMultiplexerBar() {
sessionBar.innerHTML = "";
registry.list.forEach((session, index) => {
const wrapper = document.createElement("div");
wrapper.className = "session-wrapper";
const tab = document.createElement("span");
const isActive = session.id === registry.currentActiveId;
tab.className = `session-tab ${isActive ? 'active' : ''}`;
tab.textContent = `${index}: ${session.name}`;
tab.addEventListener("click", () => { if (!isActive) switchSession(index); });
tab.addEventListener("dblclick", (e) => {
e.stopPropagation();
const editorInput = document.createElement("input");
editorInput.type = "text";
editorInput.className = "rename-input";
editorInput.value = session.name;
const finishRename = async () => {
const fresh = editorInput.value.trim();
if (fresh && fresh !== session.name) { session.name = fresh; await saveAllToBrowser(); renderTerminalScreen(); }
renderTopMultiplexerBar();
};
editorInput.addEventListener("keydown", (ke) => {
if (ke.key === "Enter") finishRename();
if (ke.key === "Escape") renderTopMultiplexerBar();
});
editorInput.addEventListener("blur", finishRename);
wrapper.replaceChild(editorInput, tab);
editorInput.focus(); editorInput.select();
});
wrapper.appendChild(tab);
const deleteBtn = document.createElement("span");
deleteBtn.className = "delete-btn";
deleteBtn.textContent = "✕";
deleteBtn.title = "Delete session";
deleteBtn.addEventListener("click", (e) => { e.stopPropagation(); handleDeleteSessionConfirmation(index); });
wrapper.appendChild(deleteBtn);
sessionBar.appendChild(wrapper);
});
const actions = document.createElement("div");
actions.className = "bar-actions-group";
const modelWrap = document.createElement("div");
modelWrap.className = "model-selector-wrapper";
modelWrap.innerHTML = `<span>⚙ model:</span>`;
const sel = document.createElement("select");
sel.className = "model-select-native";
const models = [...new Set([registry.activeModel, ...AVAILABLE_MODELS])];
models.forEach(m => {
const opt = document.createElement("option");
opt.value = m;
const isAlbert = isAlbertModel(m);
opt.textContent = isAlbert ? `🇫🇷 Albert — ${m}` : `🐋 Ollama — ${m}`;
if (m === registry.activeModel) opt.selected = true;
sel.appendChild(opt);
});
sel.addEventListener("change", e => switchActiveModel(e.target.value));
modelWrap.appendChild(sel);
actions.appendChild(modelWrap);
const newBtn = document.createElement("span");
newBtn.className = "bar-btn new-session-btn";
newBtn.textContent = "+ New";
newBtn.title = "Create new session";
newBtn.addEventListener("click", () => handleCreateSession());
actions.appendChild(newBtn);
const wipeBtn = document.createElement("span");
wipeBtn.className = "bar-btn wipe-workspace-btn";
wipeBtn.textContent = "Wipe All";
wipeBtn.title = "Erase all sessions";
wipeBtn.addEventListener("click", () => handleWipeAllSessionsConfirmation());
actions.appendChild(wipeBtn);
sessionBar.appendChild(actions);
updateStatusBar();
}
function renderTerminalScreen() {
log.innerHTML = "";
const cur = registry.list.find(s => s.id === registry.currentActiveId);
const name = cur ? cur.name : "none";
const promptEngine = isAlbertModel(registry.activeModel) ? "albert-proxy" : "ollama";
terminalPrompt.textContent = `user@${promptEngine}:[${name}]~$`;
if (!cur) {
printBanner();
print("No active session. Click '+ New' or type /new to begin.", "sys");
return;
}
if (fullPersistentHistory.length === 0) {
printBanner();
print(`Session [${name}] ready — model: ${registry.activeModel}`, "sys");
print("Type /help for available commands.", "sys");
return;
}
let i = 0;
while (i < fullPersistentHistory.length) {
const msg = fullPersistentHistory[i];
if (msg.role === "user") {
print(`user@${promptEngine}:[${name}]~$ ${msg.content}`, "user");
i++; continue;
}
if (msg.role === "assistant" && !msg.isInternalToolCall && msg.content?.trim()) {
print(`assistant: ${msg.content}`, "ai");
i++; continue;
}
if (msg.isToolActivityLog && msg.content?.startsWith("🔧")) {
const execMsg = msg.content;
const resultMsg = fullPersistentHistory[i + 1];
const tName = execMsg.replace("🔧 [MCP] executing → ", "").trim();
const outcome = resultMsg?.content?.replace(/^📦 \[MCP\] result: /, "") ?? "";
const isError = outcome.startsWith("Tool error:");
const block = document.createElement("div");
block.className = "tool-block" + (isError ? " tool-error" : "");
const header = document.createElement("div");
header.className = "tool-block-header";
header.innerHTML = `
<span class="tool-chevron">▶</span>
<span class="tool-block-label">🔧 ${tName}</span>
<span class="tool-block-badge">${isError ? "error" : "done"}</span>`;
block.appendChild(header);
const body = document.createElement("div");
body.className = "tool-block-body";
body.innerHTML = `
<div class="tool-block-section">
<div class="tool-block-section-label">result</div>
<div class="tool-block-code result-text"></div>
</div>`;
body.querySelector(".result-text").textContent = outcome;
block.appendChild(body);
header.addEventListener("click", () => block.classList.toggle("open"));
if (isError) block.classList.add("open");
log.appendChild(block);
i += 2;
if (fullPersistentHistory[i]?.isInternalToolCall) i++;
continue;
}
i++;
}
// Guarantee view snaps on full context history initialization loads
log.scrollTop = log.scrollHeight;
}
function printBanner() {
const lines = [
"╔══════════════════════════════════════════╗",
"║ 🧛 MULTIPLEXER TERMINAL CORE ║",
"║ MCP local tools & Albert Proxy ║",
"╚══════════════════════════════════════════╝",
];
lines.forEach(l => print(l, "banner-line"));
const hr = document.createElement("hr");
hr.className = "log-divider";
log.appendChild(hr);
}
function print(text, cls = "sys") {
const div = document.createElement("div");
div.className = "line " + cls;
div.textContent = text;
log.appendChild(div);
executeAutoScroll();
return div;
}
/* ──────────────────────────────────────────────────────────────
MULTIPLEXER OPERATIONS
────────────────────────────────────────────────────────────── */
async function switchActiveModel(name) {
if (!name?.trim()) return;
registry.activeModel = name.trim();
if (isAlbertModel(registry.activeModel) && !ALBERT_API_KEY) {
verifyAlbertAuth();
}
await saveAllToBrowser();
updateStatusBar();
print(`⚙ Model switched → ${registry.activeModel}`, "sys");
renderTopMultiplexerBar();
renderTerminalScreen();
}
function isAlbertModel(modelName) {
return modelName.startsWith("AgentPublic/") || modelName.startsWith("albert-");
}
function verifyAlbertAuth() {
if (!ALBERT_API_KEY) {
const key = prompt("🔒 Enter your ALBERT_API_KEY (from albert.api.etalab.gouv.fr):");
if (key?.trim()) {
ALBERT_API_KEY = key.trim();
print("🔑 Albert API verification token accepted in temporary memory context.", "sys");
} else {
print("⚠ Warning: No Albert API key supplied. Proxy requests will drop.", "err");
}
}
}
async function handleCreateSession(customName = null) {
if (registry.currentActiveId) {
try {
await set(SESSION_PREFIX + registry.currentActiveId, fullPersistentHistory);
} catch (e) { console.error("Failed to save current session before switching:", e); }
}
const id = "s_" + Date.now();
const name = customName?.trim() || `session-${registry.list.length}`;
registry.list.push({ id, name });
registry.currentActiveId = id;
fullPersistentHistory = [];
activeMessages = [SYSTEM_PROMPT];
await saveAllToBrowser();
renderTopMultiplexerBar();
renderTerminalScreen();
}
async function switchSession(index) {
const t = registry.list[index];
if (!t || t.id === registry.currentActiveId) return;
if (registry.currentActiveId) {
try {
await set(SESSION_PREFIX + registry.currentActiveId, fullPersistentHistory);
} catch (e) { console.error("Failed to save session before switch:", e); }
}
registry.currentActiveId = t.id;
await set(REGISTRY_KEY, registry);
await loadSessionData(t.id);
}
async function handleDeleteSessionConfirmation(index) {
const s = registry.list[index];
if (!s) return;
if (confirm(`Delete session "${s.name}"?`)) {
await del(SESSION_PREFIX + s.id);
registry.list.splice(index, 1);
if (registry.currentActiveId === s.id) {
if (registry.list.length > 0) {
registry.currentActiveId = registry.list[Math.max(0, index - 1)].id;
await set(REGISTRY_KEY, registry);
await loadSessionData(registry.currentActiveId);
} else {
registry.currentActiveId = null;
fullPersistentHistory = [];
activeMessages = [SYSTEM_PROMPT];
await set(REGISTRY_KEY, registry);
renderTopMultiplexerBar();
renderTerminalScreen();
}
} else {
await set(REGISTRY_KEY, registry);
renderTopMultiplexerBar();
}
}
}
async function handleWipeAllSessionsConfirmation() {
if (prompt("Type WIPE to erase all sessions:") === "WIPE") {
for (const s of registry.list) await del(SESSION_PREFIX + s.id);
await del(REGISTRY_KEY);
registry.list = [];
registry.currentActiveId = null;
fullPersistentHistory = [];
activeMessages = [SYSTEM_PROMPT];
await handleCreateSession("general");
}
}
async function handleSlashCommand(raw) {
const parts = raw.trim().split(" ");
const cmd = parts[0].toLowerCase();
const args = parts.slice(1).join(" ");
if (cmd === "/new") {
await handleCreateSession(args || null);
} else if (cmd === "/clear") {
log.innerHTML = "";
printBanner();
} else if (cmd === "/key") {
ALBERT_API_KEY = args.trim();
print("🔑 ALBERT_API_KEY updated successfully.", "sys");
} else if (cmd === "/help") {
const cmds = [
[" /new [name]", "create a new session tab"],
[" /clear", "clear the terminal output"],
[" /key [token]", "set/update your Albert API Key context value"],
[" /help", "show this help message"],
];
print("── commands ──────────────────────────────────", "sys");
cmds.forEach(([c, d]) => print(`${c.padEnd(18)}${d}`, "sys"));
print("──────────────────────────────────────────────", "sys");
} else {
print(`unknown command: ${cmd} (try /help)`, "err");
}
}
/* ──────────────────────────────────────────────────────────────
STREAM / AGENTIC CORE (MULTIPLEXED ROUTING)
────────────────────────────────────────────────────────────── */
async function callModel() {
currentAbortController = new AbortController();
const targetAlbert = isAlbertModel(registry.activeModel);
const url = targetAlbert ? ALBERT_URL : OLLAMA_URL;
const headers = { "Content-Type": "application/json" };
if (targetAlbert) {
if (!ALBERT_API_KEY) verifyAlbertAuth();
headers["Authorization"] = `Bearer ${ALBERT_API_KEY}`;
}
const payload = {
model: registry.activeModel,
stream: true,
messages: activeMessages.map(({role, content, tool_calls}) => ({role, content, ...(tool_calls && {tool_calls})}))
};
if (RUNTIME_AVAILABLE_TOOLS.length > 0) payload.tools = RUNTIME_AVAILABLE_TOOLS;
return await fetch(url, {
method: "POST",
headers,
body: JSON.stringify(payload),
signal: currentAbortController.signal
});
}
async function askAI(promptText) {
const cur = registry.list.find(s => s.id === registry.currentActiveId);
const label = cur ? cur.name : "~";
const promptEngine = isAlbertModel(registry.activeModel) ? "albert-proxy" : "ollama";
// New prompt input forcing snap to floor ensures tracking alignment triggers properly
shouldAutoScroll = true;
print(`user@${promptEngine}:[${label}]~$ ${promptText}`, "user");
const userMsg = { role: "user", content: promptText };
activeMessages.push(userMsg);
fullPersistentHistory.push(userMsg);
updateStatusBar();
setGenerating(true);
let guard = 6;
let isProcessing = true;
let aborted = false;
const targetAlbert = isAlbertModel(registry.activeModel);
try {
while (isProcessing && guard-- > 0) {
const thinkLine = document.createElement("div");
thinkLine.className = "line sys";
thinkLine.innerHTML = `<span style="color:var(--purple)">assistant</span> is thinking<div class="thinking-dots"><span></span><span></span><span></span></div>`;
log.appendChild(thinkLine);
executeAutoScroll();
let stream;
try {
stream = await callModel();
} catch (fetchErr) {
thinkLine.remove();
if (fetchErr.name === "AbortError") { aborted = true; break; }
throw fetchErr;
}
const reader = stream.body.getReader();
const decoder = new TextDecoder();
let displayEl = null;
let textNode = null;
let buf = "";
let fullText = "";
let rawToolCallsMap = {};
let thinkRemoved = false;
const removeThink = () => { if (!thinkRemoved) { thinkLine.remove(); thinkRemoved = true; } };
try {
while (true) {
const { value, done } = await reader.read();
buf += decoder.decode(value || new Uint8Array(), { stream: !done });
const lines = buf.split("\n");
buf = lines.pop();
for (const line of lines) {
let processedLine = line.trim();
if (!processedLine) continue;
if (targetAlbert && processedLine.startsWith("data: ")) {
processedLine = processedLine.replace(/^data:\s*/, "");
}
if (processedLine === "[DONE]") continue;
try {
const json = JSON.parse(processedLine);
const choice = targetAlbert ? json.choices?.[0] : json;
const msg = targetAlbert ? choice?.delta : choice?.message;
if (msg?.tool_calls?.length) {
removeThink();
msg.tool_calls.forEach(tc => {
const idx = tc.index ?? 0;
// FIX 1: Initialize accumulator dynamically based on incoming type
if (!rawToolCallsMap[idx]) {
const initialArgsType = typeof tc.function?.arguments === "object" ? {} : "";
rawToolCallsMap[idx] = {
id: tc.id || "",
type: "function",
function: { name: "", arguments: initialArgsType }
};
}
if (tc.id) rawToolCallsMap[idx].id = tc.id;
if (tc.function?.name) rawToolCallsMap[idx].function.name += tc.function.name;
// FIX 2: Prevent [object Object] concatenation strings
if (tc.function?.arguments) {
const incomingArgs = tc.function.arguments;
if (typeof incomingArgs === "object" && incomingArgs !== null) {
// Ollama format: Merge properties natively
if (typeof rawToolCallsMap[idx].function.arguments !== "object") {
rawToolCallsMap[idx].function.arguments = {};
}
rawToolCallsMap[idx].function.arguments = {
...rawToolCallsMap[idx].function.arguments,
...incomingArgs
};
} else {
// OpenAI/Albert Proxy format: Append string text chunks
if (typeof rawToolCallsMap[idx].function.arguments !== "string") {
rawToolCallsMap[idx].function.arguments = "";
}
rawToolCallsMap[idx].function.arguments += incomingArgs;
}
}
});
} else if (msg?.content) {
removeThink();
fullText += msg.content;
if (!displayEl) {
displayEl = document.createElement("div");
displayEl.className = "line ai";
displayEl.innerHTML = `<span>assistant: </span>`;
textNode = document.createTextNode("");
displayEl.appendChild(textNode);
log.appendChild(displayEl);
}
textNode.nodeValue += msg.content;
executeAutoScroll();
}
} catch {
// Safe evaluation pass
}
}
if (done) break;
}
} catch (readErr) {
removeThink();
if (readErr.name === "AbortError") { aborted = true; break; }
throw readErr;
}
removeThink();
if (aborted) break;
const toolCalls = Object.values(rawToolCallsMap);
if (toolCalls.length > 0) {
if (displayEl) displayEl.remove();
displayEl = null;
fullText = "";
activeMessages.push({ role: "assistant", content: null, tool_calls: toolCalls });
for (const tc of toolCalls) {
const tName = tc.function.name;
// FIX 3: Safe adaptive evaluation pass (Handles object context vs payload strings)
let tArgs = {};
try {
tArgs = typeof tc.function.arguments === "string"
? JSON.parse(tc.function.arguments)
: (tc.function.arguments || {});
} catch (pe) {
console.error("Args evaluation error:", pe);
tArgs = {};
}
const block = document.createElement("div");
block.className = "tool-block";
const header = document.createElement("div");
header.className = "tool-block-header";
header.innerHTML = `
<span class="tool-chevron">▶</span>
<span class="tool-block-label">🔧 ${tName}</span>
<span class="tool-block-badge">executing…</span>`;
block.appendChild(header);
const body = document.createElement("div");
body.className = "tool-block-body";
const argsSection = document.createElement("div");
argsSection.className = "tool-block-section";
argsSection.innerHTML = `<div class="tool-block-section-label">arguments</div>
<div class="tool-block-code"></div>`;
argsSection.querySelector(".tool-block-code").textContent = JSON.stringify(tArgs, null, 2);
body.appendChild(argsSection);
const resultSection = document.createElement("div");
resultSection.className = "tool-block-section";
resultSection.innerHTML = `<div class="tool-block-section-label">result</div>
<div class="tool-block-code result-text">running…</div>`;
body.appendChild(resultSection);
block.appendChild(body);
log.appendChild(block);
executeAutoScroll();
header.addEventListener("click", () => {
block.classList.toggle("open");
executeAutoScroll();
});
const logMsg = `🔧 [MCP] executing → ${tName}`;
fullPersistentHistory.push({ role: "system", content: logMsg, isToolActivityLog: true });
fullPersistentHistory.push({ role: "assistant", content: JSON.stringify(tc), isInternalToolCall: true });
const outcome = await executeMcpToolCall(tName, tArgs, currentAbortController?.signal);
const isError = outcome.startsWith("Tool error:");
const resultCode = resultSection.querySelector(".result-text");
resultCode.textContent = outcome;
const badge = header.querySelector(".tool-block-badge");
badge.textContent = isError ? "error" : "done";
if (isError) {
block.classList.add("tool-error");
block.classList.add("open");
}
const resultMsg = `📦 [MCP] result: ${outcome.substring(0, 400)}${outcome.length > 400 ? '…' : ''}`;
fullPersistentHistory.push({ role: "system", content: resultMsg, isToolActivityLog: true });
activeMessages.push({ role: "tool", name: tName, tool_call_id: tc.id, content: outcome });
executeAutoScroll();
}
} else {
isProcessing = false;
const finalMsg = { role: "assistant", content: fullText };
activeMessages.push(finalMsg);
if (fullText.trim()) {
fullPersistentHistory.push(finalMsg);
}
}
}
if (aborted) {
print("⚠ Generation stopped by user.", "sys");
}
await saveAllToBrowser();
pruneActiveMemory();
} catch (err) {
print(`runtime error: ${err.message}`, "err");
} finally {
setGenerating(false);
}
}
/* ──────────────────────────────────────────────────────────────
STOP BUTTON
────────────────────────────────────────────────────────────── */
stopBtn.addEventListener("click", () => {
if (currentAbortController) currentAbortController.abort();
});
/* ──────────────────────────────────────────────────────────────
INPUT HANDLER
────────────────────────────────────────────────────────────── */
input.addEventListener("keydown", async (e) => {
if (e.key === "Escape") {
if (currentAbortController) currentAbortController.abort();
return;
}
if (e.key === "ArrowUp") {
e.preventDefault();
if (cmdHistory.length === 0) return;
if (historyIndex === -1) {
historyDraft = input.value;
historyIndex = cmdHistory.length - 1;
} else if (historyIndex > 0) {
historyIndex--;
}
input.value = cmdHistory[historyIndex];
requestAnimationFrame(() => { input.selectionStart = input.selectionEnd = input.value.length; });
return;
}
if (e.key === "ArrowDown") {
e.preventDefault();
if (historyIndex === -1) return;
if (historyIndex < cmdHistory.length - 1) {
historyIndex++;
input.value = cmdHistory[historyIndex];
} else {
historyIndex = -1;
input.value = historyDraft;
}
requestAnimationFrame(() => { input.selectionStart = input.selectionEnd = input.value.length; });
return;
}
if (e.key !== "Enter") {
if (historyIndex !== -1) historyIndex = -1;
return;
}
const value = input.value.trim();
if (!value) return;
input.value = "";
historyIndex = -1;
historyDraft = "";
if (cmdHistory[cmdHistory.length - 1] !== value) {
cmdHistory.push(value);
if (cmdHistory.length > CMD_HISTORY_MAX) cmdHistory.shift();
}
if (value.startsWith("/")) {
shouldAutoScroll = true;
print(`client:[cmd]~$ ${value}`, "user");
await handleSlashCommand(value);
} else {
if (!registry.currentActiveId) {
shouldAutoScroll = true;
print("No active session. Type /new to start one.", "err");
return;
}
await askAI(value);
}
});
/* ──────────────────────────────────────────────────────────────
BOOT
────────────────────────────────────────────────────────────── */
async function boot() {
try {
const saved = await get(REGISTRY_KEY);
if (saved?.list?.length) {
registry = saved;
} else {
const id = "s_" + Date.now();
registry.list.push({ id, name: "general" });
registry.currentActiveId = id;
}
} catch (err) {
console.warn("Registry recovery failover window context invoked:", err);
const id = "s_" + Date.now();
registry.list.push({ id, name: "general" });
registry.currentActiveId = id;
}
renderTopMultiplexerBar();
await loadSessionData(registry.currentActiveId);
updateStatusBar();
await fetchToolsFromMcpServer();
if (isAlbertModel(registry.activeModel) && !ALBERT_API_KEY) {
verifyAlbertAuth();
}
input.focus();
}
boot();
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Text.Json.Nodes;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy => policy
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
});
var app = builder.Build();
app.UseCors();
app.Urls.Add("http://localhost:3000");
app.MapPost("/", async (HttpContext context) =>
{
using var reader = new StreamReader(context.Request.Body);
var body = await reader.ReadToEndAsync();
var json = JsonNode.Parse(body);
if (json == null) return Results.BadRequest("Invalid JSON-RPC payload format.");
string? method = json["method"]?.GetValue<string>();
var id = json["id"];
// 1. Handshake Lifecycle
if (method == "initialize")
{
return Results.Json(new
{
jsonrpc = "2.0",
id = id,
result = new { protocolVersion = "2025-11-25" }
});
}
// 2. Return tools declaration array to your frontend
if (method == "tools/list")
{
return Results.Json(new
{
jsonrpc = "2.0",
id = id,
result = new
{
tools = new[]
{
new
{
name = "list_files",
description = "Lists files and directories in a given local path.",
inputSchema = new
{
type = "object",
properties = new
{
path = new { type = "string", description = "The target directory absolute path (e.g., C:\\temp)" }
},
required = new[] { "path" }
}
}
}
}
});
}
// 3. Fix: Handle the execution invocation routing target!
if (method == "tools/call")
{
var paramsNode = json["params"];
string? toolName = paramsNode?["name"]?.GetValue<string>();
var arguments = paramsNode?["arguments"];
if (toolName == "list_files")
{
// Safely pull the path argument sent from the frontend
string? targetPath = arguments?["path"]?.GetValue<string>() ?? arguments?["directory"]?.GetValue<string>();
// Fallback strategy if your model keeps insisting on sending parameter field as "command"
if (string.IsNullOrEmpty(targetPath) && arguments?["command"] != null)
{
string cmd = arguments["command"].GetValue<string>();
// Extract clean target strings if model sent raw "dir C:\temp" phrases
targetPath = cmd.Replace("dir ", "").Replace("cmd /c", "").Replace("\"", "").Trim();
}
if (string.IsNullOrEmpty(targetPath))
{
return Results.Json(CreateRpcError(id, "Missing required argument property: path"));
}
try
{
if (!Directory.Exists(targetPath))
{
return Results.Json(CreateRpcResult(id, $"Error: Directory '{targetPath}' does not exist."));
}
// Gather directory content cleanly using standard C# runtime IO definitions
var entries = Directory.GetFileSystemEntries(targetPath)
.Select(e => Path.GetFileName(e));
string formattedTextResult = $"Contents of {targetPath}:\n" + string.Join("\n", entries);
return Results.Json(CreateRpcResult(id, formattedTextResult));
}
catch (Exception ex)
{
return Results.Json(CreateRpcResult(id, $"Tool error: {ex.Message}"));
}
}
return Results.Json(CreateRpcError(id, $"Unknown tool execution target location requested: {toolName}"));
}
return Results.Json(CreateRpcError(id, "Method execution handler not established on this target destination."));
});
// Structural helper wrappers for clean MCP response formats
object CreateRpcResult(JsonNode? id, string textContent) => new
{
jsonrpc = "2.0",
id = id,
result = new
{
content = new[] { new { type = "text", text = textContent } }
}
};
object CreateRpcError(JsonNode? id, string errorMessage) => new
{
jsonrpc = "2.0",
id = id,
error = new { code = -32601, message = errorMessage }
};
app.Run();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OllamaTerminal — Dracula</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600;700&family=Fira+Code:wght@300;400;500;600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div id="app">
<!-- Title Bar -->
<div id="titleBar">
<div class="traffic-lights">
<div class="tl tl-red" title="Close"></div>
<div class="tl tl-yellow" title="Minimize"></div>
<div class="tl tl-green" title="Maximize"></div>
</div>
<span id="titleText">ollama-terminal — dracula edition</span>
<span class="title-badge">MCP</span>
</div>
<!-- Session / Multiplexer Bar -->
<div id="sessionBar"></div>
<!-- Status Bar -->
<div id="statusBar">
<span class="status-item">
<span class="status-dot offline" id="mcpDot"></span>
<span id="mcpStatus" class="offline">MCP offline</span>
</span>
<span class="status-sep"></span>
<span class="status-item">model: <span id="statusModel" style="color:var(--cyan);margin-left:4px;"></span></span>
<span class="status-sep"></span>
<span class="status-item" id="toolCount" style="color:var(--orange);">0 tools</span>
<span class="status-sep"></span>
<span class="status-item" id="msgCount" style="color:var(--comment);">0 msgs</span>
</div>
<!-- Output Log -->
<div id="log"></div>
<!-- Input Row -->
<div id="inputRow">
<span id="terminalPrompt">user@ollama:~$</span>
<input id="input" type="text" autocomplete="off" spellcheck="false" placeholder="enter a prompt or /help…" />
<span class="hist-hint" title="↑↓ history">↑↓</span>
<span id="stopBtn" title="Stop generation (Esc)">
<span class="stop-icon"></span>Stop
</span>
<div class="input-cursor-hint"></div>
</div>
</div>
<script type="module" src="./app.js">
</script>
</body>
</html>
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment