presentations-test / 2026 /mcp-connect /animations /http-multinode-shared-storage.html
evalstate's picture
evalstate HF Staff
Scratch test presentation publishing pipeline
5949d89 verified
Raw
History Blame Contribute Delete
16.4 kB
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="shared/http-diagram.css">
<style>
/* Make RequestId box much larger and prominent */
.message-session.request-id {
padding: 12px 16px;
border-radius: 12px;
min-width: 180px;
background: #fff3e0;
border: 3px solid #ffa726;
box-shadow: 0 6px 20px rgba(255, 152, 0, 0.35);
}
.message-session.request-id .request-label {
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #e65100;
margin-bottom: 6px;
}
.message-session.request-id .request-value {
font-family: 'SF Mono', Monaco, monospace;
font-size: 14px;
font-weight: 700;
background: white;
padding: 6px 14px;
border-radius: 8px;
border: 2px solid #ffb74d;
color: #e65100;
display: inline-block;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
/* Client response variation - blue themed */
.message.client-response .message-session.request-id {
background: #e3f2fd;
border-color: #42a5f5;
box-shadow: 0 6px 20px rgba(33, 150, 243, 0.35);
}
.message.client-response .message-session.request-id .request-label {
color: #1565c0;
}
.message.client-response .message-session.request-id .request-value {
border-color: #90caf9;
color: #1565c0;
}
/* Error variation - red themed */
.message.error-response .message-session.request-id {
background: #ffebee;
border-color: #ef5350;
box-shadow: 0 6px 20px rgba(244, 67, 54, 0.35);
}
.message.error-response .message-session.request-id .request-label {
color: #c62828;
}
.message.error-response .message-session.request-id .request-value {
border-color: #ef9a9a;
color: #c62828;
}
</style>
</head>
<body>
<div class="canvas-root">
<div class="diagram">
<!-- Client -->
<div class="entity client">
<div class="entity-box" id="client-box">
<span>MCP Client</span>
<span class="entity-status" id="client-status"></span>
</div>
<div class="state-badge" id="client-state">
<div class="badge-title">Connection State</div>
<div class="empty-state">Mcp-Session-Id: β€”</div>
<div class="session-id" id="client-session"></div>
<div class="caps-row" id="client-caps"></div>
</div>
</div>
<!-- Load Balancer -->
<div class="entity lb">
<div class="entity-box">LB</div>
</div>
<!-- Connection lines -->
<div class="connection conn-client-lb"></div>
<div class="connection conn-lb-servers"></div>
<!-- Server Cluster -->
<div class="server-cluster">
<div class="cluster-label">Streamable HTTP (Shared Init State)</div>
</div>
<!-- Server Nodes with session badges to the right -->
<div class="entity server server-1">
<div class="entity-box" id="server-1-box">
<span>Node 1</span>
<span class="entity-status" id="server-1-status"></span>
</div>
<div class="state-badge" id="server-1-state">
<div class="badge-title">Connection State</div>
<div class="empty-state">Mcp-Session-Id: β€”</div>
<div class="session-id" id="server-1-session"></div>
<div class="caps-row" id="server-1-caps"></div>
</div>
</div>
<div class="entity server server-2">
<div class="entity-box" id="server-2-box">
<span>Node 2</span>
<span class="entity-status" id="server-2-status"></span>
</div>
<div class="state-badge" id="server-2-state">
<div class="badge-title">Connection State</div>
<div class="empty-state">Mcp-Session-Id: β€”</div>
<div class="session-id" id="server-2-session"></div>
<div class="caps-row" id="server-2-caps"></div>
</div>
</div>
<div class="entity server server-3">
<div class="entity-box" id="server-3-box">
<span>Node 3</span>
<span class="entity-status" id="server-3-status"></span>
</div>
<div class="state-badge" id="server-3-state">
<div class="badge-title">Connection State</div>
<div class="empty-state">Mcp-Session-Id: β€”</div>
<div class="session-id" id="server-3-session"></div>
<div class="caps-row" id="server-3-caps"></div>
</div>
</div>
<!-- Message -->
<div class="message" id="message">
<div class="message-header">
<span class="message-type" id="message-type">initialize</span>
<span class="message-arrow" id="message-arrow">β†’</span>
</div>
<div class="message-detail" id="message-detail">InitializeRequest</div>
<div class="message-session" id="message-session">Mcp-Session-Id: sess_abc123</div>
</div>
</div>
<div class="narration" id="narration">
<span class="step-num" id="step-num">β—‹</span>
<span id="narration-text">Shared storage syncs initialize state across servers...</span>
</div>
</div>
<script src="shared/animation-config.js"></script>
<script src="shared/diagram-helpers.js"></script>
<script src="shared/canvas-scale.js"></script>
<script>
const message = document.getElementById('message');
const messageType = document.getElementById('message-type');
const messageArrow = document.getElementById('message-arrow');
const messageDetail = document.getElementById('message-detail');
const messageSession = document.getElementById('message-session');
const narration = document.getElementById('narration');
const narrationText = document.getElementById('narration-text');
const stepNum = document.getElementById('step-num');
const clientBox = document.getElementById('client-box');
const clientState = document.getElementById('client-state');
const clientSession = document.getElementById('client-session');
const clientCaps = document.getElementById('client-caps');
const clientStatus = document.getElementById('client-status');
const server1Box = document.getElementById('server-1-box');
const server2Box = document.getElementById('server-2-box');
const server3Box = document.getElementById('server-3-box');
const server1State = document.getElementById('server-1-state');
const server2State = document.getElementById('server-2-state');
const server3State = document.getElementById('server-3-state');
const server1Status = document.getElementById('server-1-status');
const server2Status = document.getElementById('server-2-status');
const server3Status = document.getElementById('server-3-status');
const server1Session = document.getElementById('server-1-session');
const server1Caps = document.getElementById('server-1-caps');
const server2Session = document.getElementById('server-2-session');
const server2Caps = document.getElementById('server-2-caps');
const server3Session = document.getElementById('server-3-session');
const server3Caps = document.getElementById('server-3-caps');
const messageElements = {
container: message,
header: message.querySelector('.message-header'),
type: messageType,
arrow: messageArrow,
detail: messageDetail,
session: messageSession,
};
// Positions (x centers)
const pos = {
client: 65,
lb: 240,
server1: 470,
server2: 470,
server3: 470
};
const yClient = 115;
const yServer1 = 38;
const yServer2 = 143;
const yServer3 = 248;
function setMessagePos(x, y) {
setMessagePosition(message, x, y);
}
function animateMessage(fromX, fromY, toX, toY, duration, callback) {
animateMessageBetween(message, fromX, fromY, toX, toY, duration, callback);
}
function animatePath(waypoints, totalDuration, callback) {
animatePathBetween(message, waypoints, totalDuration, callback);
}
function configMessage(type, arrow, method, detail, sessionText, sessionError, direction) {
configureMessage(messageElements, {
type,
arrow,
method,
detail,
sessionText,
sessionError,
direction,
});
}
function buildRequestIdMarkup(requestId) {
return `<div class="request-label">Must route to original server</div><div class="request-value">${requestId}</div>`;
}
function setStatus(element, text, className) {
element.textContent = text;
element.className = 'entity-status' + (text ? ' visible' : '') + (className ? ' ' + className : '');
}
function resetAll() {
clientBox.className = 'entity-box';
server1Box.className = 'entity-box';
server2Box.className = 'entity-box';
server3Box.className = 'entity-box';
clientState.className = 'state-badge';
server1State.className = 'state-badge';
server2State.className = 'state-badge';
server3State.className = 'state-badge';
// Clear dynamic content
clientSession.textContent = '';
clientCaps.innerHTML = '';
server1Session.textContent = '';
server1Caps.innerHTML = '';
server2Session.textContent = '';
server2Caps.innerHTML = '';
server3Session.textContent = '';
server3Caps.innerHTML = '';
setStatus(clientStatus, '', '');
setStatus(server1Status, '', '');
setStatus(server2Status, '', '');
setStatus(server3Status, '', '');
message.className = 'message';
}
const steps = [
// Step 0: Initial state
{
setup: () => {
resetAll();
stepNum.textContent = 'β—‹';
narrationText.textContent = 'Shared storage syncs initialize state across servers';
narration.className = 'narration';
},
animate: (done) => setTimeout(done, ANIMATION.INITIAL_PAUSE),
after: () => {}
},
// Step 1: Initialize request β†’ LB β†’ Node 1
{
setup: () => {
configMessage('request', 'β†’', 'initialize', 'InitializeRequest', null, false, 'right');
setMessagePos(pos.client, yClient);
stepNum.textContent = '1';
narrationText.textContent = 'Client initializes with capabilities';
narration.className = 'narration request-state';
clientBox.classList.add('alive', 'active');
},
animate: (done) => animatePath(
[{x: pos.client, y: yClient}, {x: pos.lb, y: yClient}, {x: pos.server1, y: yServer1}],
ANIMATION.MSG_DURATION,
done
),
after: () => {
clientBox.classList.remove('active');
server1Box.classList.add('alive', 'active');
}
},
// Step 2: Initialize result ← shared to all nodes
{
setup: () => {
configMessage('response', '←', 'initialize', 'InitializeResult', 'Mcp-Session-Id: sess_shared', false, 'left');
setMessagePos(pos.server1, yServer1);
stepNum.textContent = '2';
narrationText.textContent = 'Shared store syncs session to all servers';
narration.className = 'narration response-state';
server1Session.textContent = 'sess_shared';
server1Caps.innerHTML = '<span class="cap">sampling βœ“</span>';
server2Session.textContent = 'sess_shared';
server2Caps.innerHTML = '<span class="cap">sampling βœ“</span>';
server3Session.textContent = 'sess_shared';
server3Caps.innerHTML = '<span class="cap">sampling βœ“</span>';
server1State.classList.add('session');
server2State.classList.add('session');
server3State.classList.add('session');
},
animate: (done) => animatePath(
[{x: pos.server1, y: yServer1}, {x: pos.lb, y: yClient}, {x: pos.client, y: yClient}],
ANIMATION.MSG_DURATION,
done
),
after: () => {
server1Box.classList.remove('active');
clientSession.textContent = 'sess_shared';
clientCaps.innerHTML = '<span class="cap">tools βœ“</span><span class="cap">prompts βœ“</span>';
clientState.classList.add('session');
}
},
// Step 3: notifications/initialized β†’ LB β†’ Node 1
{
setup: () => {
configMessage('notification', 'β†’', 'initialized', 'notification', 'Mcp-Session-Id: sess_shared', false, 'right');
setMessagePos(pos.client, yClient);
stepNum.textContent = '3';
narrationText.textContent = 'Client sends initialized notification';
narration.className = 'narration notification-state';
clientBox.classList.add('active');
server2Box.classList.add('alive');
server3Box.classList.add('alive');
setStatus(server2Status, 'SYNCED', '');
setStatus(server3Status, 'SYNCED', '');
},
animate: (done) => animatePath(
[{x: pos.client, y: yClient}, {x: pos.lb, y: yClient}, {x: pos.server1, y: yServer1}],
ANIMATION.MSG_DURATION,
done
),
after: () => {
clientBox.classList.remove('active');
clientState.classList.add('active');
server1State.classList.add('active');
server2State.classList.add('active');
server3State.classList.add('active');
setStatus(server2Status, '', '');
setStatus(server3Status, '', '');
}
},
// Step 4: Server sends elicitation request (waiting)
{
setup: () => {
configMessage('server-request', '←', 'elicitation/request', 'ElicitRequest', buildRequestIdMarkup('req_41'), false, 'left');
messageSession.classList.add('request-id');
setMessagePos(pos.server1, yServer1);
stepNum.textContent = '4';
narrationText.textContent = 'Node 1 asks client to confirm (WAITING)';
narration.className = 'narration waiting-state';
server1Box.classList.add('waiting');
setStatus(server1Status, 'WAITING', 'waiting');
},
animate: (done) => animatePath(
[{x: pos.server1, y: yServer1}, {x: pos.lb, y: yClient}, {x: pos.client, y: yClient}],
ANIMATION.MSG_DURATION,
done
),
after: () => {
clientBox.classList.add('active');
}
},
// Step 5: Client responds but routed to Node 3
{
setup: () => {
configMessage('client-response', 'β†’', 'elicitation/response', 'ElicitResult', buildRequestIdMarkup('req_41'), false, 'right');
messageSession.classList.add('request-id');
setMessagePos(pos.client, yClient);
stepNum.textContent = '5';
narrationText.textContent = 'Client response routed to the wrong server';
narration.className = 'narration request-state';
},
animate: (done) => animatePath(
[{x: pos.client, y: yClient}, {x: pos.lb, y: yClient}, {x: pos.server3, y: yServer3}],
ANIMATION.MSG_DURATION,
done
),
after: () => {
clientBox.classList.remove('active');
server3Box.classList.add('active');
}
},
// Step 6: Error ← Node 3 doesn't know pending request
{
setup: () => {
configMessage('error-response', '←', 'ERROR', 'Unknown request', buildRequestIdMarkup('req_41'), true, 'left');
messageSession.classList.add('request-id');
setMessagePos(pos.server3, yServer3);
stepNum.textContent = 'βœ—';
narrationText.textContent = 'Shared init state is not enough for server requests';
narration.className = 'narration error-state';
server3Box.classList.add('error');
},
animate: (done) => animatePath(
[{x: pos.server3, y: yServer3}, {x: pos.lb, y: yClient}, {x: pos.client, y: yClient}],
ANIMATION.MSG_DURATION,
done
),
after: () => {
clientBox.classList.add('error');
}
},
// Step 7: Pause on error
{
setup: () => {
message.className = 'message';
},
animate: (done) => setTimeout(done, ANIMATION.ERROR_PAUSE),
after: () => {}
}
];
runStepSequence(steps);
scaleCanvas();
</script>
</body>
</html>