Spaces:
Running
Running
Add MCP Connect 2026 presentation with latest updates
Browse files- .gitattributes +6 -0
- 2026/mcp-connect/animations/chat-api-view.html +196 -0
- 2026/mcp-connect/animations/chat-demo.html +123 -0
- 2026/mcp-connect/animations/http-multinode-cookie.html +387 -0
- 2026/mcp-connect/animations/http-multinode-shared-storage.html +444 -0
- 2026/mcp-connect/animations/http-multinode-stateless.html +392 -0
- 2026/mcp-connect/animations/http-multinode.html +368 -0
- 2026/mcp-connect/animations/mcp-mrtr-flow.html +221 -0
- 2026/mcp-connect/animations/mcp-mrtr-request.html +233 -0
- 2026/mcp-connect/animations/mcp-stateful-request.html +318 -0
- 2026/mcp-connect/animations/shared/animation-config.js +32 -0
- 2026/mcp-connect/animations/shared/canvas-scale.js +44 -0
- 2026/mcp-connect/animations/shared/diagram-helpers.js +152 -0
- 2026/mcp-connect/animations/shared/http-diagram.css +505 -0
- 2026/mcp-connect/animations/shared/sequence-helpers.js +83 -0
- 2026/mcp-connect/animations/stdio-simple.html +722 -0
- 2026/mcp-connect/images/2026-02-03-mcp-server-stats.png +3 -0
- 2026/mcp-connect/images/2026-02-03-mcpremote1.png +3 -0
- 2026/mcp-connect/images/2026-02-03-mcpremote2.png +3 -0
- 2026/mcp-connect/images/2026-02-04-client-dataset.png +3 -0
- 2026/mcp-connect/images/2026-02-04-efficient.png +0 -0
- 2026/mcp-connect/images/github-mark.svg +1 -0
- 2026/mcp-connect/images/hf_logo.svg +8 -0
- 2026/mcp-connect/images/huggingface-mark-logo.svg +15 -0
- 2026/mcp-connect/images/intro-spaces.webm +3 -0
- 2026/mcp-connect/images/mcp-icon.svg +12 -0
- 2026/mcp-connect/images/xcom-logo-black.png +3 -0
- 2026/mcp-connect/presentation.html +0 -0
- index.html +16 -0
.gitattributes
CHANGED
|
@@ -53,3 +53,9 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 53 |
2025/secure-se-sep/images/local_context.png filter=lfs diff=lfs merge=lfs -text
|
| 54 |
2025/secure-se-sep/images/model_parameters.png filter=lfs diff=lfs merge=lfs -text
|
| 55 |
2025/secure-se-sep/images/xcom-logo-black.png filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
2025/secure-se-sep/images/local_context.png filter=lfs diff=lfs merge=lfs -text
|
| 54 |
2025/secure-se-sep/images/model_parameters.png filter=lfs diff=lfs merge=lfs -text
|
| 55 |
2025/secure-se-sep/images/xcom-logo-black.png filter=lfs diff=lfs merge=lfs -text
|
| 56 |
+
2026/mcp-connect/images/2026-02-03-mcp-server-stats.png filter=lfs diff=lfs merge=lfs -text
|
| 57 |
+
2026/mcp-connect/images/2026-02-03-mcpremote1.png filter=lfs diff=lfs merge=lfs -text
|
| 58 |
+
2026/mcp-connect/images/2026-02-03-mcpremote2.png filter=lfs diff=lfs merge=lfs -text
|
| 59 |
+
2026/mcp-connect/images/2026-02-04-client-dataset.png filter=lfs diff=lfs merge=lfs -text
|
| 60 |
+
2026/mcp-connect/images/intro-spaces.webm filter=lfs diff=lfs merge=lfs -text
|
| 61 |
+
2026/mcp-connect/images/xcom-logo-black.png filter=lfs diff=lfs merge=lfs -text
|
2026/mcp-connect/animations/chat-api-view.html
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<style>
|
| 6 |
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 7 |
+
html, body {
|
| 8 |
+
height: 100%;
|
| 9 |
+
overflow: hidden;
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
body {
|
| 13 |
+
background: transparent;
|
| 14 |
+
display: flex;
|
| 15 |
+
justify-content: center;
|
| 16 |
+
align-items: center;
|
| 17 |
+
width: 100vw;
|
| 18 |
+
height: 100vh;
|
| 19 |
+
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
.api-window {
|
| 23 |
+
background: linear-gradient(135deg, #1e1e1e 0%, #252526 100%);
|
| 24 |
+
border-radius: 16px;
|
| 25 |
+
padding: 16px;
|
| 26 |
+
width: 480px;
|
| 27 |
+
height: 460px;
|
| 28 |
+
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
| 29 |
+
overflow: hidden;
|
| 30 |
+
display: flex;
|
| 31 |
+
flex-direction: column;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
.api-header {
|
| 35 |
+
color: #888;
|
| 36 |
+
font-size: 11px;
|
| 37 |
+
margin-bottom: 8px;
|
| 38 |
+
padding-bottom: 6px;
|
| 39 |
+
border-bottom: 1px solid #333;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
.api-content {
|
| 43 |
+
font-size: 12px;
|
| 44 |
+
line-height: 1.5;
|
| 45 |
+
color: #d4d4d4;
|
| 46 |
+
flex: 1;
|
| 47 |
+
overflow: auto;
|
| 48 |
+
scrollbar-width: none;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
.api-content::-webkit-scrollbar {
|
| 52 |
+
display: none;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
.json-brace { color: #d4d4d4; }
|
| 56 |
+
.json-key { color: #9cdcfe; }
|
| 57 |
+
.json-string { color: #ce9178; }
|
| 58 |
+
.json-bracket { color: #d4d4d4; }
|
| 59 |
+
.json-punct { color: #d4d4d4; }
|
| 60 |
+
|
| 61 |
+
.message-block {
|
| 62 |
+
opacity: 0;
|
| 63 |
+
transform: translateX(-10px);
|
| 64 |
+
margin-left: 20px;
|
| 65 |
+
padding: 4px 0;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
.message-block.show {
|
| 69 |
+
animation: slideInLeft 0.4s ease forwards;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
.message-block.fade-out {
|
| 73 |
+
animation: fadeOut 0.8s ease forwards;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
.role-user { border-left: 3px solid #0b93f6; padding-left: 10px; }
|
| 77 |
+
.role-assistant { border-left: 3px solid #34c759; padding-left: 10px; }
|
| 78 |
+
|
| 79 |
+
.static-content {
|
| 80 |
+
opacity: 0;
|
| 81 |
+
}
|
| 82 |
+
.static-content.show {
|
| 83 |
+
animation: fadeIn 0.3s ease forwards;
|
| 84 |
+
}
|
| 85 |
+
.static-content.fade-out {
|
| 86 |
+
animation: fadeOut 0.8s ease forwards;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
@keyframes slideInLeft {
|
| 90 |
+
from { opacity: 0; transform: translateX(-10px); }
|
| 91 |
+
to { opacity: 1; transform: translateX(0); }
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
@keyframes fadeIn {
|
| 95 |
+
from { opacity: 0; }
|
| 96 |
+
to { opacity: 1; }
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
@keyframes fadeOut {
|
| 100 |
+
from { opacity: 1; }
|
| 101 |
+
to { opacity: 0; }
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
.content-truncated {
|
| 105 |
+
color: #666;
|
| 106 |
+
font-style: italic;
|
| 107 |
+
}
|
| 108 |
+
</style>
|
| 109 |
+
</head>
|
| 110 |
+
<body>
|
| 111 |
+
<div class="api-window canvas-root">
|
| 112 |
+
<div class="api-header">POST /v1/chat/completions</div>
|
| 113 |
+
<div class="api-content">
|
| 114 |
+
<div class="static-content" data-delay="0" id="openBrace">
|
| 115 |
+
<span class="json-brace">{</span><br>
|
| 116 |
+
<span class="json-key">"messages"</span><span class="json-punct">:</span> <span class="json-bracket">[</span>
|
| 117 |
+
</div>
|
| 118 |
+
|
| 119 |
+
<div class="message-block role-user" data-delay="0">
|
| 120 |
+
<span class="json-brace">{</span><br>
|
| 121 |
+
<span class="json-key">"role"</span><span class="json-punct">:</span> <span class="json-string">"user"</span><span class="json-punct">,</span><br>
|
| 122 |
+
<span class="json-key">"content"</span><span class="json-punct">:</span> <span class="json-string">"Can you help me find information about MCP servers?"</span><br>
|
| 123 |
+
<span class="json-brace">}</span>
|
| 124 |
+
</div>
|
| 125 |
+
|
| 126 |
+
<div class="message-block role-assistant" data-delay="2000">
|
| 127 |
+
<span class="json-brace">{</span><br>
|
| 128 |
+
<span class="json-key">"role"</span><span class="json-punct">:</span> <span class="json-string">"assistant"</span><span class="json-punct">,</span><br>
|
| 129 |
+
<span class="json-key">"content"</span><span class="json-punct">:</span> <span class="json-string">"Of course! I can search for MCP server documentation..."</span><br>
|
| 130 |
+
<span class="json-brace">}</span>
|
| 131 |
+
</div>
|
| 132 |
+
|
| 133 |
+
<div class="message-block role-user" data-delay="4000">
|
| 134 |
+
<span class="json-brace">{</span><br>
|
| 135 |
+
<span class="json-key">"role"</span><span class="json-punct">:</span> <span class="json-string">"user"</span><span class="json-punct">,</span><br>
|
| 136 |
+
<span class="json-key">"content"</span><span class="json-punct">:</span> <span class="json-string">"Great, what transport options are available?"</span><br>
|
| 137 |
+
<span class="json-brace">}</span>
|
| 138 |
+
</div>
|
| 139 |
+
|
| 140 |
+
<div class="message-block role-assistant" data-delay="6000">
|
| 141 |
+
<span class="json-brace">{</span><br>
|
| 142 |
+
<span class="json-key">"role"</span><span class="json-punct">:</span> <span class="json-string">"assistant"</span><span class="json-punct">,</span><br>
|
| 143 |
+
<span class="json-key">"content"</span><span class="json-punct">:</span> <span class="json-string">"MCP supports STDIO, Streamable HTTP, and..."</span><br>
|
| 144 |
+
<span class="json-brace">}</span>
|
| 145 |
+
</div>
|
| 146 |
+
|
| 147 |
+
<div class="message-block role-user" data-delay="8000">
|
| 148 |
+
<span class="json-brace">{</span><br>
|
| 149 |
+
<span class="json-key">"role"</span><span class="json-punct">:</span> <span class="json-string">"user"</span><span class="json-punct">,</span><br>
|
| 150 |
+
<span class="json-key">"content"</span><span class="json-punct">:</span> <span class="json-string">"Can servers make requests back to clients?"</span><br>
|
| 151 |
+
<span class="json-brace">}</span>
|
| 152 |
+
</div>
|
| 153 |
+
|
| 154 |
+
<div class="message-block role-assistant" data-delay="10000">
|
| 155 |
+
<span class="json-brace">{</span><br>
|
| 156 |
+
<span class="json-key">"role"</span><span class="json-punct">:</span> <span class="json-string">"assistant"</span><span class="json-punct">,</span><br>
|
| 157 |
+
<span class="json-key">"content"</span><span class="json-punct">:</span> <span class="json-string">"Yes! Servers can request Sampling and Elicitation..."</span><br>
|
| 158 |
+
<span class="json-brace">}</span>
|
| 159 |
+
</div>
|
| 160 |
+
|
| 161 |
+
<div class="static-content" data-delay="0" id="closeBrace">
|
| 162 |
+
<span class="json-bracket">]</span><br>
|
| 163 |
+
<span class="json-brace">}</span>
|
| 164 |
+
</div>
|
| 165 |
+
</div>
|
| 166 |
+
</div>
|
| 167 |
+
|
| 168 |
+
<script src="shared/sequence-helpers.js"></script>
|
| 169 |
+
<script src="shared/canvas-scale.js"></script>
|
| 170 |
+
<script>
|
| 171 |
+
const apiContent = document.querySelector('.api-content');
|
| 172 |
+
const timelineItems = Array.from(document.querySelectorAll('.message-block, .static-content'));
|
| 173 |
+
const delays = timelineItems.map((item) => parseInt(item.dataset.delay, 10) || 0);
|
| 174 |
+
const maxDelay = delays.length ? Math.max(...delays) : 0;
|
| 175 |
+
const cycleDuration = maxDelay + 4000 + 1000 + 2000;
|
| 176 |
+
apiContent.scrollTop = 0;
|
| 177 |
+
|
| 178 |
+
setInterval(() => {
|
| 179 |
+
apiContent.scrollTop = 0;
|
| 180 |
+
}, cycleDuration);
|
| 181 |
+
|
| 182 |
+
runSequenceAnimation({
|
| 183 |
+
selectors: ['.message-block', '.static-content'],
|
| 184 |
+
cyclePause: 2000,
|
| 185 |
+
fadeOutOffset: 4000,
|
| 186 |
+
initialDelay: 500,
|
| 187 |
+
onShow: (el) => {
|
| 188 |
+
if (el.classList.contains('message-block')) {
|
| 189 |
+
el.scrollIntoView({ block: 'nearest' });
|
| 190 |
+
}
|
| 191 |
+
},
|
| 192 |
+
});
|
| 193 |
+
scaleCanvas();
|
| 194 |
+
</script>
|
| 195 |
+
</body>
|
| 196 |
+
</html>
|
2026/mcp-connect/animations/chat-demo.html
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<style>
|
| 6 |
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 7 |
+
html, body {
|
| 8 |
+
height: 100%;
|
| 9 |
+
overflow: hidden;
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
body {
|
| 13 |
+
background: transparent;
|
| 14 |
+
display: flex;
|
| 15 |
+
justify-content: center;
|
| 16 |
+
align-items: center;
|
| 17 |
+
width: 100vw;
|
| 18 |
+
height: 100vh;
|
| 19 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
.chat-window {
|
| 23 |
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
| 24 |
+
border-radius: 16px;
|
| 25 |
+
padding: 24px;
|
| 26 |
+
width: 480px;
|
| 27 |
+
height: 460px;
|
| 28 |
+
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
| 29 |
+
overflow: hidden;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
.chat-messages {
|
| 33 |
+
display: flex;
|
| 34 |
+
flex-direction: column;
|
| 35 |
+
gap: 14px;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
.message {
|
| 39 |
+
display: flex;
|
| 40 |
+
opacity: 0;
|
| 41 |
+
transform: translateY(20px);
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
.message.show {
|
| 45 |
+
animation: slideIn 0.5s ease forwards;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
.message.fade-out {
|
| 49 |
+
animation: fadeOut 0.8s ease forwards;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
.user-message { justify-content: flex-end; }
|
| 53 |
+
.assistant-message { justify-content: flex-start; }
|
| 54 |
+
|
| 55 |
+
.message-bubble {
|
| 56 |
+
max-width: 85%;
|
| 57 |
+
padding: 12px 16px;
|
| 58 |
+
border-radius: 18px;
|
| 59 |
+
font-size: 14px;
|
| 60 |
+
line-height: 1.45;
|
| 61 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
.user-message .message-bubble {
|
| 65 |
+
background: linear-gradient(135deg, #0b93f6 0%, #007AFF 100%);
|
| 66 |
+
color: white;
|
| 67 |
+
border-bottom-right-radius: 4px;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
.assistant-message .message-bubble {
|
| 71 |
+
background: linear-gradient(135deg, #34c759 0%, #30d158 100%);
|
| 72 |
+
color: white;
|
| 73 |
+
border-bottom-left-radius: 4px;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
@keyframes slideIn {
|
| 77 |
+
from { opacity: 0; transform: translateY(20px); }
|
| 78 |
+
to { opacity: 1; transform: translateY(0); }
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
@keyframes fadeOut {
|
| 82 |
+
from { opacity: 1; transform: translateY(0); }
|
| 83 |
+
to { opacity: 0; transform: translateY(-10px); }
|
| 84 |
+
}
|
| 85 |
+
</style>
|
| 86 |
+
</head>
|
| 87 |
+
<body>
|
| 88 |
+
<div class="chat-window canvas-root">
|
| 89 |
+
<div class="chat-messages" id="chatMessages">
|
| 90 |
+
<div class="message user-message" data-delay="0">
|
| 91 |
+
<div class="message-bubble">Can you help me find information about MCP servers?</div>
|
| 92 |
+
</div>
|
| 93 |
+
<div class="message assistant-message" data-delay="2000">
|
| 94 |
+
<div class="message-bubble">Of course! I can search for MCP server documentation. Let me use the search tool...</div>
|
| 95 |
+
</div>
|
| 96 |
+
<div class="message user-message" data-delay="4000">
|
| 97 |
+
<div class="message-bubble">Great, what transport options are available?</div>
|
| 98 |
+
</div>
|
| 99 |
+
<div class="message assistant-message" data-delay="6000">
|
| 100 |
+
<div class="message-bubble">MCP supports STDIO, Streamable HTTP, and the deprecated SSE transport. HTTP is recommended for remote servers.</div>
|
| 101 |
+
</div>
|
| 102 |
+
<div class="message user-message" data-delay="8000">
|
| 103 |
+
<div class="message-bubble">Can servers make requests back to clients?</div>
|
| 104 |
+
</div>
|
| 105 |
+
<div class="message assistant-message" data-delay="10000">
|
| 106 |
+
<div class="message-bubble">Yes! Servers can request Sampling and Elicitation from clients. This enables interactive workflows.</div>
|
| 107 |
+
</div>
|
| 108 |
+
</div>
|
| 109 |
+
</div>
|
| 110 |
+
|
| 111 |
+
<script src="shared/sequence-helpers.js"></script>
|
| 112 |
+
<script src="shared/canvas-scale.js"></script>
|
| 113 |
+
<script>
|
| 114 |
+
runSequenceAnimation({
|
| 115 |
+
selectors: ['.message'],
|
| 116 |
+
cyclePause: 2000,
|
| 117 |
+
fadeOutOffset: 4000,
|
| 118 |
+
initialDelay: 500,
|
| 119 |
+
});
|
| 120 |
+
scaleCanvas();
|
| 121 |
+
</script>
|
| 122 |
+
</body>
|
| 123 |
+
</html>
|
2026/mcp-connect/animations/http-multinode-cookie.html
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<link rel="stylesheet" href="shared/http-diagram.css">
|
| 6 |
+
<style>
|
| 7 |
+
/* Make MCP-Cookie box much larger and prominent */
|
| 8 |
+
.message-session.mcp-cookie {
|
| 9 |
+
padding: 12px 16px;
|
| 10 |
+
border-radius: 12px;
|
| 11 |
+
min-width: 200px;
|
| 12 |
+
background: #f3e5f5;
|
| 13 |
+
border: 3px solid #ab47bc;
|
| 14 |
+
box-shadow: 0 6px 20px rgba(156, 39, 176, 0.35);
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
.message-session.mcp-cookie .cookie-label {
|
| 18 |
+
font-size: 10px;
|
| 19 |
+
font-weight: 600;
|
| 20 |
+
text-transform: uppercase;
|
| 21 |
+
letter-spacing: 0.5px;
|
| 22 |
+
color: #7b1fa2;
|
| 23 |
+
margin-bottom: 6px;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
.message-session.mcp-cookie .cookie-value {
|
| 27 |
+
font-family: 'SF Mono', Monaco, monospace;
|
| 28 |
+
font-size: 14px;
|
| 29 |
+
font-weight: 700;
|
| 30 |
+
background: white;
|
| 31 |
+
padding: 6px 14px;
|
| 32 |
+
border-radius: 8px;
|
| 33 |
+
border: 2px solid #ce93d8;
|
| 34 |
+
color: #7b1fa2;
|
| 35 |
+
display: inline-block;
|
| 36 |
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
.message-session.mcp-cookie .cookie-value.empty {
|
| 40 |
+
color: #9e9e9e;
|
| 41 |
+
border-color: #e0e0e0;
|
| 42 |
+
font-style: italic;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
/* Set-Cookie variation - green themed (server issuing) */
|
| 46 |
+
.message-session.mcp-cookie.set-cookie {
|
| 47 |
+
background: #e8f5e9;
|
| 48 |
+
border-color: #66bb6a;
|
| 49 |
+
box-shadow: 0 6px 20px rgba(76, 175, 80, 0.35);
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
.message-session.mcp-cookie.set-cookie .cookie-label {
|
| 53 |
+
color: #2e7d32;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
.message-session.mcp-cookie.set-cookie .cookie-value {
|
| 57 |
+
border-color: #a5d6a7;
|
| 58 |
+
color: #2e7d32;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
/* Request with cookie - blue themed */
|
| 62 |
+
.message.request .message-session.mcp-cookie:not(.set-cookie) {
|
| 63 |
+
background: #e3f2fd;
|
| 64 |
+
border-color: #42a5f5;
|
| 65 |
+
box-shadow: 0 6px 20px rgba(33, 150, 243, 0.35);
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
.message.request .message-session.mcp-cookie:not(.set-cookie) .cookie-label {
|
| 69 |
+
color: #1565c0;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
.message.request .message-session.mcp-cookie:not(.set-cookie) .cookie-value {
|
| 73 |
+
border-color: #90caf9;
|
| 74 |
+
color: #1565c0;
|
| 75 |
+
}
|
| 76 |
+
</style>
|
| 77 |
+
</head>
|
| 78 |
+
<body>
|
| 79 |
+
<div class="canvas-root">
|
| 80 |
+
<div class="diagram">
|
| 81 |
+
<!-- Client -->
|
| 82 |
+
<div class="entity client">
|
| 83 |
+
<div class="entity-box" id="client-box">
|
| 84 |
+
<span>MCP Client</span>
|
| 85 |
+
<span class="entity-status" id="client-status"></span>
|
| 86 |
+
</div>
|
| 87 |
+
<div class="state-badge" id="client-state">
|
| 88 |
+
<div class="badge-title">Cookie Jar</div>
|
| 89 |
+
<div class="empty-state">empty</div>
|
| 90 |
+
<div class="session-id" id="client-session"></div>
|
| 91 |
+
<div class="caps-row" id="client-caps"></div>
|
| 92 |
+
</div>
|
| 93 |
+
</div>
|
| 94 |
+
|
| 95 |
+
<!-- Load Balancer -->
|
| 96 |
+
<div class="entity lb">
|
| 97 |
+
<div class="entity-box">LB</div>
|
| 98 |
+
</div>
|
| 99 |
+
|
| 100 |
+
<!-- Connection lines -->
|
| 101 |
+
<div class="connection conn-client-lb"></div>
|
| 102 |
+
<div class="connection conn-lb-servers"></div>
|
| 103 |
+
|
| 104 |
+
<!-- Server Cluster -->
|
| 105 |
+
<div class="server-cluster">
|
| 106 |
+
<div class="cluster-label">Streamable HTTP (Cookies)</div>
|
| 107 |
+
</div>
|
| 108 |
+
|
| 109 |
+
<!-- Server Nodes with session badges to the right -->
|
| 110 |
+
<div class="entity server server-1">
|
| 111 |
+
<div class="entity-box" id="server-1-box">
|
| 112 |
+
<span>Node 1</span>
|
| 113 |
+
<span class="entity-status" id="server-1-status"></span>
|
| 114 |
+
</div>
|
| 115 |
+
<div class="state-badge" id="server-1-state">
|
| 116 |
+
<div class="badge-title">Known Cookie</div>
|
| 117 |
+
<div class="empty-state">none</div>
|
| 118 |
+
<div class="session-id" id="server-1-session"></div>
|
| 119 |
+
<div class="caps-row" id="server-1-caps"></div>
|
| 120 |
+
</div>
|
| 121 |
+
</div>
|
| 122 |
+
|
| 123 |
+
<div class="entity server server-2">
|
| 124 |
+
<div class="entity-box" id="server-2-box">
|
| 125 |
+
<span>Node 2</span>
|
| 126 |
+
<span class="entity-status" id="server-2-status"></span>
|
| 127 |
+
</div>
|
| 128 |
+
<div class="state-badge" id="server-2-state">
|
| 129 |
+
<div class="badge-title">Known Cookie</div>
|
| 130 |
+
<div class="empty-state">none</div>
|
| 131 |
+
<div class="session-id" id="server-2-session"></div>
|
| 132 |
+
<div class="caps-row" id="server-2-caps"></div>
|
| 133 |
+
</div>
|
| 134 |
+
</div>
|
| 135 |
+
|
| 136 |
+
<div class="entity server server-3">
|
| 137 |
+
<div class="entity-box" id="server-3-box">
|
| 138 |
+
<span>Node 3</span>
|
| 139 |
+
<span class="entity-status" id="server-3-status"></span>
|
| 140 |
+
</div>
|
| 141 |
+
<div class="state-badge" id="server-3-state">
|
| 142 |
+
<div class="badge-title">Known Cookie</div>
|
| 143 |
+
<div class="empty-state">none</div>
|
| 144 |
+
<div class="session-id" id="server-3-session"></div>
|
| 145 |
+
<div class="caps-row" id="server-3-caps"></div>
|
| 146 |
+
</div>
|
| 147 |
+
</div>
|
| 148 |
+
|
| 149 |
+
<!-- Message -->
|
| 150 |
+
<div class="message" id="message">
|
| 151 |
+
<div class="message-header">
|
| 152 |
+
<span class="message-type" id="message-type">initialize</span>
|
| 153 |
+
<span class="message-arrow" id="message-arrow">→</span>
|
| 154 |
+
</div>
|
| 155 |
+
<div class="message-detail" id="message-detail">CallToolRequest</div>
|
| 156 |
+
<div class="message-session" id="message-session">MCP-Cookie: (none)</div>
|
| 157 |
+
</div>
|
| 158 |
+
</div>
|
| 159 |
+
|
| 160 |
+
<div class="narration" id="narration">
|
| 161 |
+
<span class="step-num" id="step-num">○</span>
|
| 162 |
+
<span id="narration-text">Client uses MCP cookies for session semantics...</span>
|
| 163 |
+
</div>
|
| 164 |
+
|
| 165 |
+
</div>
|
| 166 |
+
|
| 167 |
+
<script src="shared/animation-config.js"></script>
|
| 168 |
+
<script src="shared/diagram-helpers.js"></script>
|
| 169 |
+
<script src="shared/canvas-scale.js"></script>
|
| 170 |
+
<script>
|
| 171 |
+
const message = document.getElementById('message');
|
| 172 |
+
const messageType = document.getElementById('message-type');
|
| 173 |
+
const messageArrow = document.getElementById('message-arrow');
|
| 174 |
+
const messageDetail = document.getElementById('message-detail');
|
| 175 |
+
const messageSession = document.getElementById('message-session');
|
| 176 |
+
const narration = document.getElementById('narration');
|
| 177 |
+
const narrationText = document.getElementById('narration-text');
|
| 178 |
+
const stepNum = document.getElementById('step-num');
|
| 179 |
+
|
| 180 |
+
const clientBox = document.getElementById('client-box');
|
| 181 |
+
const clientState = document.getElementById('client-state');
|
| 182 |
+
const clientSession = document.getElementById('client-session');
|
| 183 |
+
const clientCaps = document.getElementById('client-caps');
|
| 184 |
+
|
| 185 |
+
const server1Box = document.getElementById('server-1-box');
|
| 186 |
+
const server2Box = document.getElementById('server-2-box');
|
| 187 |
+
const server3Box = document.getElementById('server-3-box');
|
| 188 |
+
const server1State = document.getElementById('server-1-state');
|
| 189 |
+
const server2State = document.getElementById('server-2-state');
|
| 190 |
+
const server3State = document.getElementById('server-3-state');
|
| 191 |
+
const server1Session = document.getElementById('server-1-session');
|
| 192 |
+
const server1Caps = document.getElementById('server-1-caps');
|
| 193 |
+
const server2Session = document.getElementById('server-2-session');
|
| 194 |
+
const server2Caps = document.getElementById('server-2-caps');
|
| 195 |
+
const server3Session = document.getElementById('server-3-session');
|
| 196 |
+
const server3Caps = document.getElementById('server-3-caps');
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
const messageElements = {
|
| 200 |
+
container: message,
|
| 201 |
+
header: message.querySelector('.message-header'),
|
| 202 |
+
type: messageType,
|
| 203 |
+
arrow: messageArrow,
|
| 204 |
+
detail: messageDetail,
|
| 205 |
+
session: messageSession,
|
| 206 |
+
};
|
| 207 |
+
|
| 208 |
+
// Positions (x centers)
|
| 209 |
+
const pos = {
|
| 210 |
+
client: 65,
|
| 211 |
+
lb: 240,
|
| 212 |
+
server1: 470,
|
| 213 |
+
server2: 470,
|
| 214 |
+
server3: 470
|
| 215 |
+
};
|
| 216 |
+
const yClient = 115;
|
| 217 |
+
const yServer1 = 38;
|
| 218 |
+
const yServer2 = 143;
|
| 219 |
+
const yServer3 = 248;
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
function setMessagePos(x, y) {
|
| 223 |
+
setMessagePosition(message, x, y);
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
function animateMessage(fromX, fromY, toX, toY, duration, callback) {
|
| 227 |
+
animateMessageBetween(message, fromX, fromY, toX, toY, duration, callback);
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
function animatePath(waypoints, totalDuration, callback) {
|
| 231 |
+
animatePathBetween(message, waypoints, totalDuration, callback);
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
function configMessage(type, arrow, method, detail, sessionText, sessionError, direction) {
|
| 235 |
+
configureMessage(messageElements, {
|
| 236 |
+
type,
|
| 237 |
+
arrow,
|
| 238 |
+
method,
|
| 239 |
+
detail,
|
| 240 |
+
sessionText,
|
| 241 |
+
sessionError,
|
| 242 |
+
direction,
|
| 243 |
+
});
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
function buildCookieMarkup(label, value, isEmpty) {
|
| 247 |
+
const valueClass = isEmpty ? 'cookie-value empty' : 'cookie-value';
|
| 248 |
+
return `<div class="cookie-label">${label}</div><div class="${valueClass}">${value}</div>`;
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
function resetAll() {
|
| 252 |
+
clientBox.className = 'entity-box';
|
| 253 |
+
server1Box.className = 'entity-box';
|
| 254 |
+
server2Box.className = 'entity-box';
|
| 255 |
+
server3Box.className = 'entity-box';
|
| 256 |
+
clientState.className = 'state-badge';
|
| 257 |
+
server1State.className = 'state-badge';
|
| 258 |
+
server2State.className = 'state-badge';
|
| 259 |
+
server3State.className = 'state-badge';
|
| 260 |
+
// Clear dynamic content
|
| 261 |
+
clientSession.textContent = '';
|
| 262 |
+
clientCaps.innerHTML = '';
|
| 263 |
+
server1Session.textContent = '';
|
| 264 |
+
server1Caps.innerHTML = '';
|
| 265 |
+
server2Session.textContent = '';
|
| 266 |
+
server2Caps.innerHTML = '';
|
| 267 |
+
server3Session.textContent = '';
|
| 268 |
+
server3Caps.innerHTML = '';
|
| 269 |
+
message.className = 'message';
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
const steps = [
|
| 273 |
+
// Step 0: Initial state
|
| 274 |
+
{
|
| 275 |
+
setup: () => {
|
| 276 |
+
resetAll();
|
| 277 |
+
stepNum.textContent = '○';
|
| 278 |
+
narrationText.textContent = 'Client and server exchange cookies at the MCP layer';
|
| 279 |
+
narration.className = 'narration';
|
| 280 |
+
},
|
| 281 |
+
animate: (done) => setTimeout(done, ANIMATION.INITIAL_PAUSE),
|
| 282 |
+
after: () => {}
|
| 283 |
+
},
|
| 284 |
+
// Step 1: tools/call → LB → Node 1 (no cookie yet)
|
| 285 |
+
{
|
| 286 |
+
setup: () => {
|
| 287 |
+
configMessage('request', '→', 'tools/call', 'CallToolRequest', buildCookieMarkup('MCP-Cookie', '(none)', true), false, 'right');
|
| 288 |
+
messageSession.classList.add('mcp-cookie');
|
| 289 |
+
setMessagePos(pos.client, yClient);
|
| 290 |
+
stepNum.textContent = '1';
|
| 291 |
+
narrationText.textContent = 'Client sends request without cookie (MCP JSON-RPC layer)';
|
| 292 |
+
narration.className = 'narration request-state';
|
| 293 |
+
clientBox.classList.add('alive', 'active');
|
| 294 |
+
},
|
| 295 |
+
animate: (done) => animatePath(
|
| 296 |
+
[{x: pos.client, y: yClient}, {x: pos.lb, y: yClient}, {x: pos.server1, y: yServer1}],
|
| 297 |
+
ANIMATION.MSG_DURATION,
|
| 298 |
+
done
|
| 299 |
+
),
|
| 300 |
+
after: () => {
|
| 301 |
+
clientBox.classList.remove('active');
|
| 302 |
+
server1Box.classList.add('alive', 'active');
|
| 303 |
+
}
|
| 304 |
+
},
|
| 305 |
+
// Step 2: tools/call result ← Node 1 issues cookie
|
| 306 |
+
{
|
| 307 |
+
setup: () => {
|
| 308 |
+
configMessage('response', '←', 'tools/call', 'CallToolResult', buildCookieMarkup('Set-MCP-Cookie', 'cookie_4f2', false), false, 'left');
|
| 309 |
+
messageSession.classList.add('mcp-cookie', 'set-cookie');
|
| 310 |
+
setMessagePos(pos.server1, yServer1);
|
| 311 |
+
stepNum.textContent = '2';
|
| 312 |
+
narrationText.textContent = 'Server issues MCP cookie (can be copied to HTTP headers)';
|
| 313 |
+
narration.className = 'narration response-state';
|
| 314 |
+
server1Session.textContent = 'cookie_4f2';
|
| 315 |
+
server1State.classList.add('active');
|
| 316 |
+
},
|
| 317 |
+
animate: (done) => animatePath(
|
| 318 |
+
[{x: pos.server1, y: yServer1}, {x: pos.lb, y: yClient}, {x: pos.client, y: yClient}],
|
| 319 |
+
ANIMATION.MSG_DURATION,
|
| 320 |
+
done
|
| 321 |
+
),
|
| 322 |
+
after: () => {
|
| 323 |
+
server1Box.classList.remove('active');
|
| 324 |
+
clientSession.textContent = 'cookie_4f2';
|
| 325 |
+
clientState.classList.add('active');
|
| 326 |
+
}
|
| 327 |
+
},
|
| 328 |
+
// Step 3: tools/call → LB → Node 2 (cookie included)
|
| 329 |
+
{
|
| 330 |
+
setup: () => {
|
| 331 |
+
configMessage('request', '→', 'tools/call', 'CallToolRequest', buildCookieMarkup('MCP-Cookie', 'cookie_4f2', false), false, 'right');
|
| 332 |
+
messageSession.classList.add('mcp-cookie');
|
| 333 |
+
setMessagePos(pos.client, yClient);
|
| 334 |
+
stepNum.textContent = '3';
|
| 335 |
+
narrationText.textContent = 'Client returns cookie on the next request';
|
| 336 |
+
narration.className = 'narration request-state';
|
| 337 |
+
clientBox.classList.add('active');
|
| 338 |
+
},
|
| 339 |
+
animate: (done) => animatePath(
|
| 340 |
+
[{x: pos.client, y: yClient}, {x: pos.lb, y: yClient}, {x: pos.server2, y: yServer2}],
|
| 341 |
+
ANIMATION.MSG_DURATION,
|
| 342 |
+
done
|
| 343 |
+
),
|
| 344 |
+
after: () => {
|
| 345 |
+
clientBox.classList.remove('active');
|
| 346 |
+
server2Box.classList.add('alive', 'active');
|
| 347 |
+
}
|
| 348 |
+
},
|
| 349 |
+
// Step 4: tools/call result ← Node 2 recognizes cookie
|
| 350 |
+
{
|
| 351 |
+
setup: () => {
|
| 352 |
+
configMessage('response', '←', 'tools/call', 'CallToolResult', buildCookieMarkup('MCP-Cookie ✓', 'cookie_4f2', false), false, 'left');
|
| 353 |
+
messageSession.classList.add('mcp-cookie');
|
| 354 |
+
setMessagePos(pos.server2, yServer2);
|
| 355 |
+
stepNum.textContent = '4';
|
| 356 |
+
narrationText.textContent = 'Server uses cookie to maintain session semantics';
|
| 357 |
+
narration.className = 'narration response-state';
|
| 358 |
+
server2Session.textContent = 'cookie_4f2';
|
| 359 |
+
server2State.classList.add('active');
|
| 360 |
+
},
|
| 361 |
+
animate: (done) => animatePath(
|
| 362 |
+
[{x: pos.server2, y: yServer2}, {x: pos.lb, y: yClient}, {x: pos.client, y: yClient}],
|
| 363 |
+
ANIMATION.MSG_DURATION,
|
| 364 |
+
done
|
| 365 |
+
),
|
| 366 |
+
after: () => {
|
| 367 |
+
server2Box.classList.remove('active');
|
| 368 |
+
}
|
| 369 |
+
},
|
| 370 |
+
// Step 5: Pause before restart
|
| 371 |
+
{
|
| 372 |
+
setup: () => {
|
| 373 |
+
message.className = 'message';
|
| 374 |
+
stepNum.textContent = '✓';
|
| 375 |
+
narrationText.textContent = 'MCP cookies preserve client/server context across requests';
|
| 376 |
+
narration.className = 'narration success-state';
|
| 377 |
+
},
|
| 378 |
+
animate: (done) => setTimeout(done, ANIMATION.ERROR_PAUSE),
|
| 379 |
+
after: () => {}
|
| 380 |
+
}
|
| 381 |
+
];
|
| 382 |
+
|
| 383 |
+
runStepSequence(steps);
|
| 384 |
+
scaleCanvas();
|
| 385 |
+
</script>
|
| 386 |
+
</body>
|
| 387 |
+
</html>
|
2026/mcp-connect/animations/http-multinode-shared-storage.html
ADDED
|
@@ -0,0 +1,444 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<link rel="stylesheet" href="shared/http-diagram.css">
|
| 6 |
+
<style>
|
| 7 |
+
/* Make RequestId box much larger and prominent */
|
| 8 |
+
.message-session.request-id {
|
| 9 |
+
padding: 12px 16px;
|
| 10 |
+
border-radius: 12px;
|
| 11 |
+
min-width: 180px;
|
| 12 |
+
background: #fff3e0;
|
| 13 |
+
border: 3px solid #ffa726;
|
| 14 |
+
box-shadow: 0 6px 20px rgba(255, 152, 0, 0.35);
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
.message-session.request-id .request-label {
|
| 18 |
+
font-size: 10px;
|
| 19 |
+
font-weight: 600;
|
| 20 |
+
text-transform: uppercase;
|
| 21 |
+
letter-spacing: 0.5px;
|
| 22 |
+
color: #e65100;
|
| 23 |
+
margin-bottom: 6px;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
.message-session.request-id .request-value {
|
| 27 |
+
font-family: 'SF Mono', Monaco, monospace;
|
| 28 |
+
font-size: 14px;
|
| 29 |
+
font-weight: 700;
|
| 30 |
+
background: white;
|
| 31 |
+
padding: 6px 14px;
|
| 32 |
+
border-radius: 8px;
|
| 33 |
+
border: 2px solid #ffb74d;
|
| 34 |
+
color: #e65100;
|
| 35 |
+
display: inline-block;
|
| 36 |
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
/* Client response variation - blue themed */
|
| 40 |
+
.message.client-response .message-session.request-id {
|
| 41 |
+
background: #e3f2fd;
|
| 42 |
+
border-color: #42a5f5;
|
| 43 |
+
box-shadow: 0 6px 20px rgba(33, 150, 243, 0.35);
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
.message.client-response .message-session.request-id .request-label {
|
| 47 |
+
color: #1565c0;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
.message.client-response .message-session.request-id .request-value {
|
| 51 |
+
border-color: #90caf9;
|
| 52 |
+
color: #1565c0;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
/* Error variation - red themed */
|
| 56 |
+
.message.error-response .message-session.request-id {
|
| 57 |
+
background: #ffebee;
|
| 58 |
+
border-color: #ef5350;
|
| 59 |
+
box-shadow: 0 6px 20px rgba(244, 67, 54, 0.35);
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
.message.error-response .message-session.request-id .request-label {
|
| 63 |
+
color: #c62828;
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
.message.error-response .message-session.request-id .request-value {
|
| 67 |
+
border-color: #ef9a9a;
|
| 68 |
+
color: #c62828;
|
| 69 |
+
}
|
| 70 |
+
</style>
|
| 71 |
+
</head>
|
| 72 |
+
<body>
|
| 73 |
+
<div class="canvas-root">
|
| 74 |
+
<div class="diagram">
|
| 75 |
+
<!-- Client -->
|
| 76 |
+
<div class="entity client">
|
| 77 |
+
<div class="entity-box" id="client-box">
|
| 78 |
+
<span>MCP Client</span>
|
| 79 |
+
<span class="entity-status" id="client-status"></span>
|
| 80 |
+
</div>
|
| 81 |
+
<div class="state-badge" id="client-state">
|
| 82 |
+
<div class="badge-title">Connection State</div>
|
| 83 |
+
<div class="empty-state">Mcp-Session-Id: —</div>
|
| 84 |
+
<div class="session-id" id="client-session"></div>
|
| 85 |
+
<div class="caps-row" id="client-caps"></div>
|
| 86 |
+
</div>
|
| 87 |
+
</div>
|
| 88 |
+
|
| 89 |
+
<!-- Load Balancer -->
|
| 90 |
+
<div class="entity lb">
|
| 91 |
+
<div class="entity-box">LB</div>
|
| 92 |
+
</div>
|
| 93 |
+
|
| 94 |
+
<!-- Connection lines -->
|
| 95 |
+
<div class="connection conn-client-lb"></div>
|
| 96 |
+
<div class="connection conn-lb-servers"></div>
|
| 97 |
+
|
| 98 |
+
<!-- Server Cluster -->
|
| 99 |
+
<div class="server-cluster">
|
| 100 |
+
<div class="cluster-label">Streamable HTTP (Shared Init State)</div>
|
| 101 |
+
</div>
|
| 102 |
+
|
| 103 |
+
<!-- Server Nodes with session badges to the right -->
|
| 104 |
+
<div class="entity server server-1">
|
| 105 |
+
<div class="entity-box" id="server-1-box">
|
| 106 |
+
<span>Node 1</span>
|
| 107 |
+
<span class="entity-status" id="server-1-status"></span>
|
| 108 |
+
</div>
|
| 109 |
+
<div class="state-badge" id="server-1-state">
|
| 110 |
+
<div class="badge-title">Connection State</div>
|
| 111 |
+
<div class="empty-state">Mcp-Session-Id: —</div>
|
| 112 |
+
<div class="session-id" id="server-1-session"></div>
|
| 113 |
+
<div class="caps-row" id="server-1-caps"></div>
|
| 114 |
+
</div>
|
| 115 |
+
</div>
|
| 116 |
+
|
| 117 |
+
<div class="entity server server-2">
|
| 118 |
+
<div class="entity-box" id="server-2-box">
|
| 119 |
+
<span>Node 2</span>
|
| 120 |
+
<span class="entity-status" id="server-2-status"></span>
|
| 121 |
+
</div>
|
| 122 |
+
<div class="state-badge" id="server-2-state">
|
| 123 |
+
<div class="badge-title">Connection State</div>
|
| 124 |
+
<div class="empty-state">Mcp-Session-Id: —</div>
|
| 125 |
+
<div class="session-id" id="server-2-session"></div>
|
| 126 |
+
<div class="caps-row" id="server-2-caps"></div>
|
| 127 |
+
</div>
|
| 128 |
+
</div>
|
| 129 |
+
|
| 130 |
+
<div class="entity server server-3">
|
| 131 |
+
<div class="entity-box" id="server-3-box">
|
| 132 |
+
<span>Node 3</span>
|
| 133 |
+
<span class="entity-status" id="server-3-status"></span>
|
| 134 |
+
</div>
|
| 135 |
+
<div class="state-badge" id="server-3-state">
|
| 136 |
+
<div class="badge-title">Connection State</div>
|
| 137 |
+
<div class="empty-state">Mcp-Session-Id: —</div>
|
| 138 |
+
<div class="session-id" id="server-3-session"></div>
|
| 139 |
+
<div class="caps-row" id="server-3-caps"></div>
|
| 140 |
+
</div>
|
| 141 |
+
</div>
|
| 142 |
+
|
| 143 |
+
<!-- Message -->
|
| 144 |
+
<div class="message" id="message">
|
| 145 |
+
<div class="message-header">
|
| 146 |
+
<span class="message-type" id="message-type">initialize</span>
|
| 147 |
+
<span class="message-arrow" id="message-arrow">→</span>
|
| 148 |
+
</div>
|
| 149 |
+
<div class="message-detail" id="message-detail">InitializeRequest</div>
|
| 150 |
+
<div class="message-session" id="message-session">Mcp-Session-Id: sess_abc123</div>
|
| 151 |
+
</div>
|
| 152 |
+
</div>
|
| 153 |
+
|
| 154 |
+
<div class="narration" id="narration">
|
| 155 |
+
<span class="step-num" id="step-num">○</span>
|
| 156 |
+
<span id="narration-text">Shared storage syncs initialize state across servers...</span>
|
| 157 |
+
</div>
|
| 158 |
+
|
| 159 |
+
</div>
|
| 160 |
+
|
| 161 |
+
<script src="shared/animation-config.js"></script>
|
| 162 |
+
<script src="shared/diagram-helpers.js"></script>
|
| 163 |
+
<script src="shared/canvas-scale.js"></script>
|
| 164 |
+
<script>
|
| 165 |
+
const message = document.getElementById('message');
|
| 166 |
+
const messageType = document.getElementById('message-type');
|
| 167 |
+
const messageArrow = document.getElementById('message-arrow');
|
| 168 |
+
const messageDetail = document.getElementById('message-detail');
|
| 169 |
+
const messageSession = document.getElementById('message-session');
|
| 170 |
+
const narration = document.getElementById('narration');
|
| 171 |
+
const narrationText = document.getElementById('narration-text');
|
| 172 |
+
const stepNum = document.getElementById('step-num');
|
| 173 |
+
|
| 174 |
+
const clientBox = document.getElementById('client-box');
|
| 175 |
+
const clientState = document.getElementById('client-state');
|
| 176 |
+
const clientSession = document.getElementById('client-session');
|
| 177 |
+
const clientCaps = document.getElementById('client-caps');
|
| 178 |
+
const clientStatus = document.getElementById('client-status');
|
| 179 |
+
|
| 180 |
+
const server1Box = document.getElementById('server-1-box');
|
| 181 |
+
const server2Box = document.getElementById('server-2-box');
|
| 182 |
+
const server3Box = document.getElementById('server-3-box');
|
| 183 |
+
const server1State = document.getElementById('server-1-state');
|
| 184 |
+
const server2State = document.getElementById('server-2-state');
|
| 185 |
+
const server3State = document.getElementById('server-3-state');
|
| 186 |
+
const server1Status = document.getElementById('server-1-status');
|
| 187 |
+
const server2Status = document.getElementById('server-2-status');
|
| 188 |
+
const server3Status = document.getElementById('server-3-status');
|
| 189 |
+
const server1Session = document.getElementById('server-1-session');
|
| 190 |
+
const server1Caps = document.getElementById('server-1-caps');
|
| 191 |
+
const server2Session = document.getElementById('server-2-session');
|
| 192 |
+
const server2Caps = document.getElementById('server-2-caps');
|
| 193 |
+
const server3Session = document.getElementById('server-3-session');
|
| 194 |
+
const server3Caps = document.getElementById('server-3-caps');
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
const messageElements = {
|
| 198 |
+
container: message,
|
| 199 |
+
header: message.querySelector('.message-header'),
|
| 200 |
+
type: messageType,
|
| 201 |
+
arrow: messageArrow,
|
| 202 |
+
detail: messageDetail,
|
| 203 |
+
session: messageSession,
|
| 204 |
+
};
|
| 205 |
+
|
| 206 |
+
// Positions (x centers)
|
| 207 |
+
const pos = {
|
| 208 |
+
client: 65,
|
| 209 |
+
lb: 240,
|
| 210 |
+
server1: 470,
|
| 211 |
+
server2: 470,
|
| 212 |
+
server3: 470
|
| 213 |
+
};
|
| 214 |
+
const yClient = 115;
|
| 215 |
+
const yServer1 = 38;
|
| 216 |
+
const yServer2 = 143;
|
| 217 |
+
const yServer3 = 248;
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
function setMessagePos(x, y) {
|
| 221 |
+
setMessagePosition(message, x, y);
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
function animateMessage(fromX, fromY, toX, toY, duration, callback) {
|
| 225 |
+
animateMessageBetween(message, fromX, fromY, toX, toY, duration, callback);
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
function animatePath(waypoints, totalDuration, callback) {
|
| 229 |
+
animatePathBetween(message, waypoints, totalDuration, callback);
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
function configMessage(type, arrow, method, detail, sessionText, sessionError, direction) {
|
| 233 |
+
configureMessage(messageElements, {
|
| 234 |
+
type,
|
| 235 |
+
arrow,
|
| 236 |
+
method,
|
| 237 |
+
detail,
|
| 238 |
+
sessionText,
|
| 239 |
+
sessionError,
|
| 240 |
+
direction,
|
| 241 |
+
});
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
function buildRequestIdMarkup(requestId) {
|
| 245 |
+
return `<div class="request-label">Must route to original server</div><div class="request-value">${requestId}</div>`;
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
function setStatus(element, text, className) {
|
| 249 |
+
element.textContent = text;
|
| 250 |
+
element.className = 'entity-status' + (text ? ' visible' : '') + (className ? ' ' + className : '');
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
function resetAll() {
|
| 254 |
+
clientBox.className = 'entity-box';
|
| 255 |
+
server1Box.className = 'entity-box';
|
| 256 |
+
server2Box.className = 'entity-box';
|
| 257 |
+
server3Box.className = 'entity-box';
|
| 258 |
+
clientState.className = 'state-badge';
|
| 259 |
+
server1State.className = 'state-badge';
|
| 260 |
+
server2State.className = 'state-badge';
|
| 261 |
+
server3State.className = 'state-badge';
|
| 262 |
+
// Clear dynamic content
|
| 263 |
+
clientSession.textContent = '';
|
| 264 |
+
clientCaps.innerHTML = '';
|
| 265 |
+
server1Session.textContent = '';
|
| 266 |
+
server1Caps.innerHTML = '';
|
| 267 |
+
server2Session.textContent = '';
|
| 268 |
+
server2Caps.innerHTML = '';
|
| 269 |
+
server3Session.textContent = '';
|
| 270 |
+
server3Caps.innerHTML = '';
|
| 271 |
+
setStatus(clientStatus, '', '');
|
| 272 |
+
setStatus(server1Status, '', '');
|
| 273 |
+
setStatus(server2Status, '', '');
|
| 274 |
+
setStatus(server3Status, '', '');
|
| 275 |
+
message.className = 'message';
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
const steps = [
|
| 279 |
+
// Step 0: Initial state
|
| 280 |
+
{
|
| 281 |
+
setup: () => {
|
| 282 |
+
resetAll();
|
| 283 |
+
stepNum.textContent = '○';
|
| 284 |
+
narrationText.textContent = 'Shared storage syncs initialize state across servers';
|
| 285 |
+
narration.className = 'narration';
|
| 286 |
+
},
|
| 287 |
+
animate: (done) => setTimeout(done, ANIMATION.INITIAL_PAUSE),
|
| 288 |
+
after: () => {}
|
| 289 |
+
},
|
| 290 |
+
// Step 1: Initialize request → LB → Node 1
|
| 291 |
+
{
|
| 292 |
+
setup: () => {
|
| 293 |
+
configMessage('request', '→', 'initialize', 'InitializeRequest', null, false, 'right');
|
| 294 |
+
setMessagePos(pos.client, yClient);
|
| 295 |
+
stepNum.textContent = '1';
|
| 296 |
+
narrationText.textContent = 'Client initializes with capabilities';
|
| 297 |
+
narration.className = 'narration request-state';
|
| 298 |
+
clientBox.classList.add('alive', 'active');
|
| 299 |
+
},
|
| 300 |
+
animate: (done) => animatePath(
|
| 301 |
+
[{x: pos.client, y: yClient}, {x: pos.lb, y: yClient}, {x: pos.server1, y: yServer1}],
|
| 302 |
+
ANIMATION.MSG_DURATION,
|
| 303 |
+
done
|
| 304 |
+
),
|
| 305 |
+
after: () => {
|
| 306 |
+
clientBox.classList.remove('active');
|
| 307 |
+
server1Box.classList.add('alive', 'active');
|
| 308 |
+
}
|
| 309 |
+
},
|
| 310 |
+
// Step 2: Initialize result ← shared to all nodes
|
| 311 |
+
{
|
| 312 |
+
setup: () => {
|
| 313 |
+
configMessage('response', '←', 'initialize', 'InitializeResult', 'Mcp-Session-Id: sess_shared', false, 'left');
|
| 314 |
+
setMessagePos(pos.server1, yServer1);
|
| 315 |
+
stepNum.textContent = '2';
|
| 316 |
+
narrationText.textContent = 'Shared store syncs session to all servers';
|
| 317 |
+
narration.className = 'narration response-state';
|
| 318 |
+
server1Session.textContent = 'sess_shared';
|
| 319 |
+
server1Caps.innerHTML = '<span class="cap">sampling ✓</span>';
|
| 320 |
+
server2Session.textContent = 'sess_shared';
|
| 321 |
+
server2Caps.innerHTML = '<span class="cap">sampling ✓</span>';
|
| 322 |
+
server3Session.textContent = 'sess_shared';
|
| 323 |
+
server3Caps.innerHTML = '<span class="cap">sampling ✓</span>';
|
| 324 |
+
server1State.classList.add('session');
|
| 325 |
+
server2State.classList.add('session');
|
| 326 |
+
server3State.classList.add('session');
|
| 327 |
+
},
|
| 328 |
+
animate: (done) => animatePath(
|
| 329 |
+
[{x: pos.server1, y: yServer1}, {x: pos.lb, y: yClient}, {x: pos.client, y: yClient}],
|
| 330 |
+
ANIMATION.MSG_DURATION,
|
| 331 |
+
done
|
| 332 |
+
),
|
| 333 |
+
after: () => {
|
| 334 |
+
server1Box.classList.remove('active');
|
| 335 |
+
clientSession.textContent = 'sess_shared';
|
| 336 |
+
clientCaps.innerHTML = '<span class="cap">tools ✓</span><span class="cap">prompts ✓</span>';
|
| 337 |
+
clientState.classList.add('session');
|
| 338 |
+
}
|
| 339 |
+
},
|
| 340 |
+
// Step 3: notifications/initialized → LB → Node 1
|
| 341 |
+
{
|
| 342 |
+
setup: () => {
|
| 343 |
+
configMessage('notification', '→', 'initialized', 'notification', 'Mcp-Session-Id: sess_shared', false, 'right');
|
| 344 |
+
setMessagePos(pos.client, yClient);
|
| 345 |
+
stepNum.textContent = '3';
|
| 346 |
+
narrationText.textContent = 'Client sends initialized notification';
|
| 347 |
+
narration.className = 'narration notification-state';
|
| 348 |
+
clientBox.classList.add('active');
|
| 349 |
+
server2Box.classList.add('alive');
|
| 350 |
+
server3Box.classList.add('alive');
|
| 351 |
+
setStatus(server2Status, 'SYNCED', '');
|
| 352 |
+
setStatus(server3Status, 'SYNCED', '');
|
| 353 |
+
},
|
| 354 |
+
animate: (done) => animatePath(
|
| 355 |
+
[{x: pos.client, y: yClient}, {x: pos.lb, y: yClient}, {x: pos.server1, y: yServer1}],
|
| 356 |
+
ANIMATION.MSG_DURATION,
|
| 357 |
+
done
|
| 358 |
+
),
|
| 359 |
+
after: () => {
|
| 360 |
+
clientBox.classList.remove('active');
|
| 361 |
+
clientState.classList.add('active');
|
| 362 |
+
server1State.classList.add('active');
|
| 363 |
+
server2State.classList.add('active');
|
| 364 |
+
server3State.classList.add('active');
|
| 365 |
+
setStatus(server2Status, '', '');
|
| 366 |
+
setStatus(server3Status, '', '');
|
| 367 |
+
}
|
| 368 |
+
},
|
| 369 |
+
// Step 4: Server sends elicitation request (waiting)
|
| 370 |
+
{
|
| 371 |
+
setup: () => {
|
| 372 |
+
configMessage('server-request', '←', 'elicitation/request', 'ElicitRequest', buildRequestIdMarkup('req_41'), false, 'left');
|
| 373 |
+
messageSession.classList.add('request-id');
|
| 374 |
+
setMessagePos(pos.server1, yServer1);
|
| 375 |
+
stepNum.textContent = '4';
|
| 376 |
+
narrationText.textContent = 'Node 1 asks client to confirm (WAITING)';
|
| 377 |
+
narration.className = 'narration waiting-state';
|
| 378 |
+
server1Box.classList.add('waiting');
|
| 379 |
+
setStatus(server1Status, 'WAITING', 'waiting');
|
| 380 |
+
},
|
| 381 |
+
animate: (done) => animatePath(
|
| 382 |
+
[{x: pos.server1, y: yServer1}, {x: pos.lb, y: yClient}, {x: pos.client, y: yClient}],
|
| 383 |
+
ANIMATION.MSG_DURATION,
|
| 384 |
+
done
|
| 385 |
+
),
|
| 386 |
+
after: () => {
|
| 387 |
+
clientBox.classList.add('active');
|
| 388 |
+
}
|
| 389 |
+
},
|
| 390 |
+
// Step 5: Client responds but routed to Node 3
|
| 391 |
+
{
|
| 392 |
+
setup: () => {
|
| 393 |
+
configMessage('client-response', '→', 'elicitation/response', 'ElicitResult', buildRequestIdMarkup('req_41'), false, 'right');
|
| 394 |
+
messageSession.classList.add('request-id');
|
| 395 |
+
setMessagePos(pos.client, yClient);
|
| 396 |
+
stepNum.textContent = '5';
|
| 397 |
+
narrationText.textContent = 'Client response routed to the wrong server';
|
| 398 |
+
narration.className = 'narration request-state';
|
| 399 |
+
},
|
| 400 |
+
animate: (done) => animatePath(
|
| 401 |
+
[{x: pos.client, y: yClient}, {x: pos.lb, y: yClient}, {x: pos.server3, y: yServer3}],
|
| 402 |
+
ANIMATION.MSG_DURATION,
|
| 403 |
+
done
|
| 404 |
+
),
|
| 405 |
+
after: () => {
|
| 406 |
+
clientBox.classList.remove('active');
|
| 407 |
+
server3Box.classList.add('active');
|
| 408 |
+
}
|
| 409 |
+
},
|
| 410 |
+
// Step 6: Error ← Node 3 doesn't know pending request
|
| 411 |
+
{
|
| 412 |
+
setup: () => {
|
| 413 |
+
configMessage('error-response', '←', 'ERROR', 'Unknown request', buildRequestIdMarkup('req_41'), true, 'left');
|
| 414 |
+
messageSession.classList.add('request-id');
|
| 415 |
+
setMessagePos(pos.server3, yServer3);
|
| 416 |
+
stepNum.textContent = '✗';
|
| 417 |
+
narrationText.textContent = 'Shared init state is not enough for server requests';
|
| 418 |
+
narration.className = 'narration error-state';
|
| 419 |
+
server3Box.classList.add('error');
|
| 420 |
+
},
|
| 421 |
+
animate: (done) => animatePath(
|
| 422 |
+
[{x: pos.server3, y: yServer3}, {x: pos.lb, y: yClient}, {x: pos.client, y: yClient}],
|
| 423 |
+
ANIMATION.MSG_DURATION,
|
| 424 |
+
done
|
| 425 |
+
),
|
| 426 |
+
after: () => {
|
| 427 |
+
clientBox.classList.add('error');
|
| 428 |
+
}
|
| 429 |
+
},
|
| 430 |
+
// Step 7: Pause on error
|
| 431 |
+
{
|
| 432 |
+
setup: () => {
|
| 433 |
+
message.className = 'message';
|
| 434 |
+
},
|
| 435 |
+
animate: (done) => setTimeout(done, ANIMATION.ERROR_PAUSE),
|
| 436 |
+
after: () => {}
|
| 437 |
+
}
|
| 438 |
+
];
|
| 439 |
+
|
| 440 |
+
runStepSequence(steps);
|
| 441 |
+
scaleCanvas();
|
| 442 |
+
</script>
|
| 443 |
+
</body>
|
| 444 |
+
</html>
|
2026/mcp-connect/animations/http-multinode-stateless.html
ADDED
|
@@ -0,0 +1,392 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<link rel="stylesheet" href="shared/http-diagram.css">
|
| 6 |
+
<style>
|
| 7 |
+
/* Override message-session to be much larger for stateless demo */
|
| 8 |
+
.message-session.caps {
|
| 9 |
+
padding: 12px 16px;
|
| 10 |
+
border-radius: 12px;
|
| 11 |
+
min-width: 180px;
|
| 12 |
+
background: #e8f5e9;
|
| 13 |
+
border: 3px solid #66bb6a;
|
| 14 |
+
box-shadow: 0 6px 20px rgba(76, 175, 80, 0.35);
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
.message-session .session-label {
|
| 18 |
+
font-size: 11px;
|
| 19 |
+
font-weight: 600;
|
| 20 |
+
text-transform: uppercase;
|
| 21 |
+
letter-spacing: 0.5px;
|
| 22 |
+
color: #2e7d32;
|
| 23 |
+
margin-bottom: 8px;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
.message-session .cap-row {
|
| 27 |
+
display: flex;
|
| 28 |
+
gap: 8px;
|
| 29 |
+
flex-wrap: wrap;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
.message-session .cap-chip {
|
| 33 |
+
font-family: 'SF Mono', Monaco, monospace;
|
| 34 |
+
font-size: 12px;
|
| 35 |
+
font-weight: 600;
|
| 36 |
+
background: white;
|
| 37 |
+
padding: 6px 12px;
|
| 38 |
+
border-radius: 8px;
|
| 39 |
+
border: 2px solid #a5d6a7;
|
| 40 |
+
color: #2e7d32;
|
| 41 |
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
/* Make server response caps orange themed */
|
| 45 |
+
.message.response .message-session.caps {
|
| 46 |
+
background: #fff3e0;
|
| 47 |
+
border-color: #ffa726;
|
| 48 |
+
box-shadow: 0 6px 20px rgba(255, 152, 0, 0.35);
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
.message.response .message-session .session-label {
|
| 52 |
+
color: #e65100;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
.message.response .message-session .cap-chip {
|
| 56 |
+
border-color: #ffb74d;
|
| 57 |
+
color: #e65100;
|
| 58 |
+
}
|
| 59 |
+
</style>
|
| 60 |
+
</head>
|
| 61 |
+
<body>
|
| 62 |
+
<div class="canvas-root">
|
| 63 |
+
<div class="diagram">
|
| 64 |
+
<!-- Client -->
|
| 65 |
+
<div class="entity client">
|
| 66 |
+
<div class="entity-box" id="client-box">
|
| 67 |
+
<span>MCP Client</span>
|
| 68 |
+
<span class="entity-status" id="client-status"></span>
|
| 69 |
+
</div>
|
| 70 |
+
<div class="state-badge" id="client-state">
|
| 71 |
+
<div class="badge-title">Connection State</div>
|
| 72 |
+
<div class="empty-state">stateless</div>
|
| 73 |
+
<div class="session-id" id="client-session"></div>
|
| 74 |
+
<div class="caps-row" id="client-caps"></div>
|
| 75 |
+
</div>
|
| 76 |
+
</div>
|
| 77 |
+
|
| 78 |
+
<!-- Load Balancer -->
|
| 79 |
+
<div class="entity lb">
|
| 80 |
+
<div class="entity-box">LB</div>
|
| 81 |
+
</div>
|
| 82 |
+
|
| 83 |
+
<!-- Connection lines -->
|
| 84 |
+
<div class="connection conn-client-lb"></div>
|
| 85 |
+
<div class="connection conn-lb-servers"></div>
|
| 86 |
+
|
| 87 |
+
<!-- Server Cluster -->
|
| 88 |
+
<div class="server-cluster">
|
| 89 |
+
<div class="cluster-label">Streamable HTTP (Stateless)</div>
|
| 90 |
+
</div>
|
| 91 |
+
|
| 92 |
+
<!-- Server Nodes with session badges to the right -->
|
| 93 |
+
<div class="entity server server-1">
|
| 94 |
+
<div class="entity-box" id="server-1-box">
|
| 95 |
+
<span>Node 1</span>
|
| 96 |
+
<span class="entity-status" id="server-1-status"></span>
|
| 97 |
+
</div>
|
| 98 |
+
<div class="state-badge" id="server-1-state">
|
| 99 |
+
<div class="badge-title">Connection State</div>
|
| 100 |
+
<div class="empty-state">stateless</div>
|
| 101 |
+
<div class="session-id" id="server-1-session"></div>
|
| 102 |
+
<div class="caps-row" id="server-1-caps"></div>
|
| 103 |
+
</div>
|
| 104 |
+
</div>
|
| 105 |
+
|
| 106 |
+
<div class="entity server server-2">
|
| 107 |
+
<div class="entity-box" id="server-2-box">
|
| 108 |
+
<span>Node 2</span>
|
| 109 |
+
<span class="entity-status" id="server-2-status"></span>
|
| 110 |
+
</div>
|
| 111 |
+
<div class="state-badge" id="server-2-state">
|
| 112 |
+
<div class="badge-title">Connection State</div>
|
| 113 |
+
<div class="empty-state">stateless</div>
|
| 114 |
+
<div class="session-id"></div>
|
| 115 |
+
<div class="caps-row"></div>
|
| 116 |
+
</div>
|
| 117 |
+
</div>
|
| 118 |
+
|
| 119 |
+
<div class="entity server server-3">
|
| 120 |
+
<div class="entity-box" id="server-3-box">
|
| 121 |
+
<span>Node 3</span>
|
| 122 |
+
<span class="entity-status" id="server-3-status"></span>
|
| 123 |
+
</div>
|
| 124 |
+
<div class="state-badge" id="server-3-state">
|
| 125 |
+
<div class="badge-title">Connection State</div>
|
| 126 |
+
<div class="empty-state">stateless</div>
|
| 127 |
+
<div class="session-id" id="server-3-session"></div>
|
| 128 |
+
<div class="caps-row" id="server-3-caps"></div>
|
| 129 |
+
</div>
|
| 130 |
+
</div>
|
| 131 |
+
|
| 132 |
+
<!-- Message -->
|
| 133 |
+
<div class="message" id="message">
|
| 134 |
+
<div class="message-header">
|
| 135 |
+
<span class="message-type" id="message-type">initialize</span>
|
| 136 |
+
<span class="message-arrow" id="message-arrow">→</span>
|
| 137 |
+
</div>
|
| 138 |
+
<div class="message-detail" id="message-detail">InitializeRequest</div>
|
| 139 |
+
<div class="message-session" id="message-session">Client capabilities: tools, prompts</div>
|
| 140 |
+
</div>
|
| 141 |
+
</div>
|
| 142 |
+
|
| 143 |
+
<div class="narration" id="narration">
|
| 144 |
+
<span class="step-num" id="step-num">○</span>
|
| 145 |
+
<span id="narration-text">Client sends stateless requests via HTTP...</span>
|
| 146 |
+
</div>
|
| 147 |
+
|
| 148 |
+
</div>
|
| 149 |
+
|
| 150 |
+
<script src="shared/animation-config.js"></script>
|
| 151 |
+
<script src="shared/diagram-helpers.js"></script>
|
| 152 |
+
<script src="shared/canvas-scale.js"></script>
|
| 153 |
+
<script>
|
| 154 |
+
const message = document.getElementById('message');
|
| 155 |
+
const messageType = document.getElementById('message-type');
|
| 156 |
+
const messageArrow = document.getElementById('message-arrow');
|
| 157 |
+
const messageDetail = document.getElementById('message-detail');
|
| 158 |
+
const messageSession = document.getElementById('message-session');
|
| 159 |
+
const narration = document.getElementById('narration');
|
| 160 |
+
const narrationText = document.getElementById('narration-text');
|
| 161 |
+
const stepNum = document.getElementById('step-num');
|
| 162 |
+
|
| 163 |
+
const clientBox = document.getElementById('client-box');
|
| 164 |
+
const clientState = document.getElementById('client-state');
|
| 165 |
+
const clientSession = document.getElementById('client-session');
|
| 166 |
+
const clientCaps = document.getElementById('client-caps');
|
| 167 |
+
|
| 168 |
+
const server1Box = document.getElementById('server-1-box');
|
| 169 |
+
const server2Box = document.getElementById('server-2-box');
|
| 170 |
+
const server3Box = document.getElementById('server-3-box');
|
| 171 |
+
const server1State = document.getElementById('server-1-state');
|
| 172 |
+
const server2State = document.getElementById('server-2-state');
|
| 173 |
+
const server3State = document.getElementById('server-3-state');
|
| 174 |
+
const server1Session = document.getElementById('server-1-session');
|
| 175 |
+
const server1Caps = document.getElementById('server-1-caps');
|
| 176 |
+
const server3Session = document.getElementById('server-3-session');
|
| 177 |
+
const server3Caps = document.getElementById('server-3-caps');
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
const messageElements = {
|
| 181 |
+
container: message,
|
| 182 |
+
header: message.querySelector('.message-header'),
|
| 183 |
+
type: messageType,
|
| 184 |
+
arrow: messageArrow,
|
| 185 |
+
detail: messageDetail,
|
| 186 |
+
session: messageSession,
|
| 187 |
+
};
|
| 188 |
+
|
| 189 |
+
// Positions (x centers)
|
| 190 |
+
const pos = {
|
| 191 |
+
client: 65,
|
| 192 |
+
lb: 240,
|
| 193 |
+
server1: 470,
|
| 194 |
+
server2: 470,
|
| 195 |
+
server3: 470
|
| 196 |
+
};
|
| 197 |
+
const yClient = 115;
|
| 198 |
+
const yServer1 = 38;
|
| 199 |
+
const yServer2 = 143;
|
| 200 |
+
const yServer3 = 248;
|
| 201 |
+
|
| 202 |
+
function buildCapsMarkup(label, caps) {
|
| 203 |
+
const chips = caps.map((cap) => `<span class="cap-chip">${cap} ✓</span>`).join('');
|
| 204 |
+
return `<div class="session-label">${label}</div><div class="cap-row">${chips}</div>`;
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
function setMessagePos(x, y) {
|
| 209 |
+
setMessagePosition(message, x, y);
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
function animateMessage(fromX, fromY, toX, toY, duration, callback) {
|
| 213 |
+
animateMessageBetween(message, fromX, fromY, toX, toY, duration, callback);
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
function animatePath(waypoints, totalDuration, callback) {
|
| 217 |
+
animatePathBetween(message, waypoints, totalDuration, callback);
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
function configMessage(type, arrow, method, detail, sessionText, sessionError, direction) {
|
| 221 |
+
configureMessage(messageElements, {
|
| 222 |
+
type,
|
| 223 |
+
arrow,
|
| 224 |
+
method,
|
| 225 |
+
detail,
|
| 226 |
+
sessionText,
|
| 227 |
+
sessionError,
|
| 228 |
+
direction,
|
| 229 |
+
});
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
function resetAll() {
|
| 233 |
+
clientBox.className = 'entity-box';
|
| 234 |
+
server1Box.className = 'entity-box';
|
| 235 |
+
server2Box.className = 'entity-box';
|
| 236 |
+
server3Box.className = 'entity-box';
|
| 237 |
+
clientState.className = 'state-badge';
|
| 238 |
+
server1State.className = 'state-badge';
|
| 239 |
+
server2State.className = 'state-badge';
|
| 240 |
+
server3State.className = 'state-badge';
|
| 241 |
+
// Clear dynamic content
|
| 242 |
+
clientSession.textContent = '';
|
| 243 |
+
clientCaps.innerHTML = '';
|
| 244 |
+
server1Session.textContent = '';
|
| 245 |
+
server1Caps.innerHTML = '';
|
| 246 |
+
server3Session.textContent = '';
|
| 247 |
+
server3Caps.innerHTML = '';
|
| 248 |
+
message.className = 'message';
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
const steps = [
|
| 252 |
+
// Step 0: Initial state
|
| 253 |
+
{
|
| 254 |
+
setup: () => {
|
| 255 |
+
resetAll();
|
| 256 |
+
stepNum.textContent = '○';
|
| 257 |
+
narrationText.textContent = 'Client sends stateless requests via load balancer...';
|
| 258 |
+
narration.className = 'narration';
|
| 259 |
+
},
|
| 260 |
+
animate: (done) => setTimeout(done, ANIMATION.INITIAL_PAUSE),
|
| 261 |
+
after: () => {}
|
| 262 |
+
},
|
| 263 |
+
// Step 1: tools/call → LB → Node 2 (client caps included)
|
| 264 |
+
{
|
| 265 |
+
setup: () => {
|
| 266 |
+
configMessage(
|
| 267 |
+
'request',
|
| 268 |
+
'→',
|
| 269 |
+
'tools/call',
|
| 270 |
+
'CallToolRequest',
|
| 271 |
+
buildCapsMarkup('Client capabilities', ['tools', 'prompts']),
|
| 272 |
+
false,
|
| 273 |
+
'right'
|
| 274 |
+
);
|
| 275 |
+
messageSession.classList.add('caps');
|
| 276 |
+
setMessagePos(pos.client, yClient);
|
| 277 |
+
stepNum.textContent = '1';
|
| 278 |
+
narrationText.textContent = 'Client sends tool call with capabilities on every request';
|
| 279 |
+
narration.className = 'narration request-state';
|
| 280 |
+
clientBox.classList.add('alive', 'active');
|
| 281 |
+
},
|
| 282 |
+
animate: (done) => animatePath(
|
| 283 |
+
[{x: pos.client, y: yClient}, {x: pos.lb, y: yClient}, {x: pos.server2, y: yServer2}],
|
| 284 |
+
ANIMATION.MSG_DURATION,
|
| 285 |
+
done
|
| 286 |
+
),
|
| 287 |
+
after: () => {
|
| 288 |
+
clientBox.classList.remove('active');
|
| 289 |
+
server2Box.classList.add('alive', 'active');
|
| 290 |
+
}
|
| 291 |
+
},
|
| 292 |
+
// Step 2: tools/call result ← Node 2 (server caps included)
|
| 293 |
+
{
|
| 294 |
+
setup: () => {
|
| 295 |
+
configMessage(
|
| 296 |
+
'response',
|
| 297 |
+
'←',
|
| 298 |
+
'tools/call',
|
| 299 |
+
'CallToolResult',
|
| 300 |
+
buildCapsMarkup('Server capabilities', ['sampling']),
|
| 301 |
+
false,
|
| 302 |
+
'left'
|
| 303 |
+
);
|
| 304 |
+
messageSession.classList.add('caps');
|
| 305 |
+
setMessagePos(pos.server2, yServer2);
|
| 306 |
+
stepNum.textContent = '2';
|
| 307 |
+
narrationText.textContent = 'Node 2 responds with result and server capabilities';
|
| 308 |
+
narration.className = 'narration response-state';
|
| 309 |
+
},
|
| 310 |
+
animate: (done) => animatePath(
|
| 311 |
+
[{x: pos.server2, y: yServer2}, {x: pos.lb, y: yClient}, {x: pos.client, y: yClient}],
|
| 312 |
+
ANIMATION.MSG_DURATION,
|
| 313 |
+
done
|
| 314 |
+
),
|
| 315 |
+
after: () => {
|
| 316 |
+
server2Box.classList.remove('active');
|
| 317 |
+
}
|
| 318 |
+
},
|
| 319 |
+
// Step 3: prompts/list → LB → Node 3
|
| 320 |
+
{
|
| 321 |
+
setup: () => {
|
| 322 |
+
configMessage(
|
| 323 |
+
'request',
|
| 324 |
+
'→',
|
| 325 |
+
'prompts/list',
|
| 326 |
+
'ListPromptsRequest',
|
| 327 |
+
buildCapsMarkup('Client capabilities', ['tools', 'prompts']),
|
| 328 |
+
false,
|
| 329 |
+
'right'
|
| 330 |
+
);
|
| 331 |
+
messageSession.classList.add('caps');
|
| 332 |
+
setMessagePos(pos.client, yClient);
|
| 333 |
+
stepNum.textContent = '3';
|
| 334 |
+
narrationText.textContent = 'Next request can hit any node in the cluster';
|
| 335 |
+
narration.className = 'narration request-state';
|
| 336 |
+
clientBox.classList.add('active');
|
| 337 |
+
},
|
| 338 |
+
animate: (done) => animatePath(
|
| 339 |
+
[{x: pos.client, y: yClient}, {x: pos.lb, y: yClient}, {x: pos.server3, y: yServer3}],
|
| 340 |
+
ANIMATION.MSG_DURATION,
|
| 341 |
+
done
|
| 342 |
+
),
|
| 343 |
+
after: () => {
|
| 344 |
+
clientBox.classList.remove('active');
|
| 345 |
+
server3Box.classList.add('alive', 'active');
|
| 346 |
+
}
|
| 347 |
+
},
|
| 348 |
+
// Step 4: prompts/list result ← Node 3
|
| 349 |
+
{
|
| 350 |
+
setup: () => {
|
| 351 |
+
configMessage(
|
| 352 |
+
'response',
|
| 353 |
+
'←',
|
| 354 |
+
'prompts/list',
|
| 355 |
+
'ListPromptsResult',
|
| 356 |
+
buildCapsMarkup('Server capabilities', ['sampling']),
|
| 357 |
+
false,
|
| 358 |
+
'left'
|
| 359 |
+
);
|
| 360 |
+
messageSession.classList.add('caps');
|
| 361 |
+
setMessagePos(pos.server3, yServer3);
|
| 362 |
+
stepNum.textContent = '4';
|
| 363 |
+
narrationText.textContent = 'Node 3 replies with its capabilities and result';
|
| 364 |
+
narration.className = 'narration response-state';
|
| 365 |
+
},
|
| 366 |
+
animate: (done) => animatePath(
|
| 367 |
+
[{x: pos.server3, y: yServer3}, {x: pos.lb, y: yClient}, {x: pos.client, y: yClient}],
|
| 368 |
+
ANIMATION.MSG_DURATION,
|
| 369 |
+
done
|
| 370 |
+
),
|
| 371 |
+
after: () => {
|
| 372 |
+
server3Box.classList.remove('active');
|
| 373 |
+
}
|
| 374 |
+
},
|
| 375 |
+
// Step 5: Pause before restart
|
| 376 |
+
{
|
| 377 |
+
setup: () => {
|
| 378 |
+
message.className = 'message';
|
| 379 |
+
stepNum.textContent = '✓';
|
| 380 |
+
narrationText.textContent = 'Stateless flow — capabilities travel with each message';
|
| 381 |
+
narration.className = 'narration success-state';
|
| 382 |
+
},
|
| 383 |
+
animate: (done) => setTimeout(done, ANIMATION.ERROR_PAUSE),
|
| 384 |
+
after: () => {}
|
| 385 |
+
}
|
| 386 |
+
];
|
| 387 |
+
|
| 388 |
+
runStepSequence(steps);
|
| 389 |
+
scaleCanvas();
|
| 390 |
+
</script>
|
| 391 |
+
</body>
|
| 392 |
+
</html>
|
2026/mcp-connect/animations/http-multinode.html
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<link rel="stylesheet" href="shared/http-diagram.css">
|
| 6 |
+
|
| 7 |
+
</head>
|
| 8 |
+
<body>
|
| 9 |
+
<div class="canvas-root">
|
| 10 |
+
<div class="diagram">
|
| 11 |
+
<!-- Client -->
|
| 12 |
+
<div class="entity client">
|
| 13 |
+
<div class="entity-box" id="client-box">
|
| 14 |
+
<span>MCP Client</span>
|
| 15 |
+
<span class="entity-status" id="client-status"></span>
|
| 16 |
+
</div>
|
| 17 |
+
<div class="state-badge" id="client-state">
|
| 18 |
+
<div class="badge-title">Connection State</div>
|
| 19 |
+
<div class="empty-state">Mcp-Session-Id: —</div>
|
| 20 |
+
<div class="session-id" id="client-session"></div>
|
| 21 |
+
<div class="caps-row" id="client-caps"></div>
|
| 22 |
+
</div>
|
| 23 |
+
</div>
|
| 24 |
+
|
| 25 |
+
<!-- Load Balancer -->
|
| 26 |
+
<div class="entity lb">
|
| 27 |
+
<div class="entity-box">LB</div>
|
| 28 |
+
</div>
|
| 29 |
+
|
| 30 |
+
<!-- Connection lines -->
|
| 31 |
+
<div class="connection conn-client-lb"></div>
|
| 32 |
+
<div class="connection conn-lb-servers"></div>
|
| 33 |
+
|
| 34 |
+
<!-- Server Cluster -->
|
| 35 |
+
<div class="server-cluster">
|
| 36 |
+
<div class="cluster-label">Streamable HTTP (MCP Servers)</div>
|
| 37 |
+
</div>
|
| 38 |
+
|
| 39 |
+
<!-- Server Nodes with session badges to the right -->
|
| 40 |
+
<div class="entity server server-1">
|
| 41 |
+
<div class="entity-box" id="server-1-box">
|
| 42 |
+
<span>Node 1</span>
|
| 43 |
+
<span class="entity-status" id="server-1-status"></span>
|
| 44 |
+
</div>
|
| 45 |
+
<div class="state-badge" id="server-1-state">
|
| 46 |
+
<div class="badge-title">Connection State</div>
|
| 47 |
+
<div class="empty-state">Mcp-Session-Id: —</div>
|
| 48 |
+
<div class="session-id" id="server-1-session"></div>
|
| 49 |
+
<div class="caps-row" id="server-1-caps"></div>
|
| 50 |
+
</div>
|
| 51 |
+
</div>
|
| 52 |
+
|
| 53 |
+
<div class="entity server server-2">
|
| 54 |
+
<div class="entity-box" id="server-2-box">
|
| 55 |
+
<span>Node 2</span>
|
| 56 |
+
<span class="entity-status" id="server-2-status"></span>
|
| 57 |
+
</div>
|
| 58 |
+
<div class="state-badge" id="server-2-state">
|
| 59 |
+
<div class="badge-title">Connection State</div>
|
| 60 |
+
<div class="empty-state">Mcp-Session-Id: —</div>
|
| 61 |
+
<div class="session-id"></div>
|
| 62 |
+
<div class="caps-row"></div>
|
| 63 |
+
</div>
|
| 64 |
+
</div>
|
| 65 |
+
|
| 66 |
+
<div class="entity server server-3">
|
| 67 |
+
<div class="entity-box" id="server-3-box">
|
| 68 |
+
<span>Node 3</span>
|
| 69 |
+
<span class="entity-status" id="server-3-status"></span>
|
| 70 |
+
</div>
|
| 71 |
+
<div class="state-badge" id="server-3-state">
|
| 72 |
+
<div class="badge-title">Connection State</div>
|
| 73 |
+
<div class="empty-state">Mcp-Session-Id: —</div>
|
| 74 |
+
<div class="session-id" id="server-3-session"></div>
|
| 75 |
+
<div class="caps-row" id="server-3-caps"></div>
|
| 76 |
+
</div>
|
| 77 |
+
</div>
|
| 78 |
+
|
| 79 |
+
<!-- Message -->
|
| 80 |
+
<div class="message" id="message">
|
| 81 |
+
<div class="message-header">
|
| 82 |
+
<span class="message-type" id="message-type">initialize</span>
|
| 83 |
+
<span class="message-arrow" id="message-arrow">→</span>
|
| 84 |
+
</div>
|
| 85 |
+
<div class="message-detail" id="message-detail">InitializeRequest</div>
|
| 86 |
+
<div class="message-session" id="message-session">Mcp-Session-Id: sess_abc123</div>
|
| 87 |
+
</div>
|
| 88 |
+
</div>
|
| 89 |
+
|
| 90 |
+
<div class="narration" id="narration">
|
| 91 |
+
<span class="step-num" id="step-num">○</span>
|
| 92 |
+
<span id="narration-text">Client connects via HTTP...</span>
|
| 93 |
+
</div>
|
| 94 |
+
|
| 95 |
+
</div>
|
| 96 |
+
|
| 97 |
+
<script src="shared/animation-config.js"></script>
|
| 98 |
+
<script src="shared/diagram-helpers.js"></script>
|
| 99 |
+
<script src="shared/canvas-scale.js"></script>
|
| 100 |
+
<script>
|
| 101 |
+
const message = document.getElementById('message');
|
| 102 |
+
const messageType = document.getElementById('message-type');
|
| 103 |
+
const messageArrow = document.getElementById('message-arrow');
|
| 104 |
+
const messageDetail = document.getElementById('message-detail');
|
| 105 |
+
const messageSession = document.getElementById('message-session');
|
| 106 |
+
const narration = document.getElementById('narration');
|
| 107 |
+
const narrationText = document.getElementById('narration-text');
|
| 108 |
+
const stepNum = document.getElementById('step-num');
|
| 109 |
+
|
| 110 |
+
const clientBox = document.getElementById('client-box');
|
| 111 |
+
const clientState = document.getElementById('client-state');
|
| 112 |
+
const clientSession = document.getElementById('client-session');
|
| 113 |
+
const clientCaps = document.getElementById('client-caps');
|
| 114 |
+
|
| 115 |
+
const server1Box = document.getElementById('server-1-box');
|
| 116 |
+
const server2Box = document.getElementById('server-2-box');
|
| 117 |
+
const server3Box = document.getElementById('server-3-box');
|
| 118 |
+
const server1State = document.getElementById('server-1-state');
|
| 119 |
+
const server2State = document.getElementById('server-2-state');
|
| 120 |
+
const server3State = document.getElementById('server-3-state');
|
| 121 |
+
const server1Session = document.getElementById('server-1-session');
|
| 122 |
+
const server1Caps = document.getElementById('server-1-caps');
|
| 123 |
+
const server3Session = document.getElementById('server-3-session');
|
| 124 |
+
const server3Caps = document.getElementById('server-3-caps');
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
const messageElements = {
|
| 128 |
+
container: message,
|
| 129 |
+
header: message.querySelector('.message-header'),
|
| 130 |
+
type: messageType,
|
| 131 |
+
arrow: messageArrow,
|
| 132 |
+
detail: messageDetail,
|
| 133 |
+
session: messageSession,
|
| 134 |
+
};
|
| 135 |
+
|
| 136 |
+
// Positions (x centers)
|
| 137 |
+
const pos = {
|
| 138 |
+
client: 65,
|
| 139 |
+
lb: 240,
|
| 140 |
+
server1: 470,
|
| 141 |
+
server2: 470,
|
| 142 |
+
server3: 470
|
| 143 |
+
};
|
| 144 |
+
const yClient = 115;
|
| 145 |
+
const yServer1 = 38;
|
| 146 |
+
const yServer2 = 143;
|
| 147 |
+
const yServer3 = 248;
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
function setMessagePos(x, y) {
|
| 151 |
+
setMessagePosition(message, x, y);
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
function animateMessage(fromX, fromY, toX, toY, duration, callback) {
|
| 155 |
+
animateMessageBetween(message, fromX, fromY, toX, toY, duration, callback);
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
function animatePath(waypoints, totalDuration, callback) {
|
| 159 |
+
animatePathBetween(message, waypoints, totalDuration, callback);
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
function configMessage(type, arrow, method, detail, sessionText, sessionError, direction) {
|
| 163 |
+
configureMessage(messageElements, {
|
| 164 |
+
type,
|
| 165 |
+
arrow,
|
| 166 |
+
method,
|
| 167 |
+
detail,
|
| 168 |
+
sessionText,
|
| 169 |
+
sessionError,
|
| 170 |
+
direction,
|
| 171 |
+
});
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
function resetAll() {
|
| 175 |
+
clientBox.className = 'entity-box';
|
| 176 |
+
server1Box.className = 'entity-box';
|
| 177 |
+
server2Box.className = 'entity-box';
|
| 178 |
+
server3Box.className = 'entity-box';
|
| 179 |
+
clientState.className = 'state-badge';
|
| 180 |
+
server1State.className = 'state-badge';
|
| 181 |
+
server2State.className = 'state-badge';
|
| 182 |
+
server3State.className = 'state-badge';
|
| 183 |
+
// Clear dynamic content
|
| 184 |
+
clientSession.textContent = '';
|
| 185 |
+
clientCaps.innerHTML = '';
|
| 186 |
+
server1Session.textContent = '';
|
| 187 |
+
server1Caps.innerHTML = '';
|
| 188 |
+
server3Session.textContent = '';
|
| 189 |
+
server3Caps.innerHTML = '';
|
| 190 |
+
message.className = 'message';
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
const steps = [
|
| 194 |
+
// Step 0: Initial state
|
| 195 |
+
{
|
| 196 |
+
setup: () => {
|
| 197 |
+
resetAll();
|
| 198 |
+
stepNum.textContent = '○';
|
| 199 |
+
narrationText.textContent = 'Client connects to server cluster via load balancer...';
|
| 200 |
+
narration.className = 'narration';
|
| 201 |
+
},
|
| 202 |
+
animate: (done) => setTimeout(done, ANIMATION.INITIAL_PAUSE),
|
| 203 |
+
after: () => {}
|
| 204 |
+
},
|
| 205 |
+
// Step 1: Initialize request → LB → Node 1
|
| 206 |
+
{
|
| 207 |
+
setup: () => {
|
| 208 |
+
configMessage('request', '→', 'initialize', 'InitializeRequest', null, false, 'right');
|
| 209 |
+
setMessagePos(pos.client, yClient);
|
| 210 |
+
stepNum.textContent = '1';
|
| 211 |
+
narrationText.textContent = 'Client sends InitializeRequest with capabilities';
|
| 212 |
+
narration.className = 'narration request-state';
|
| 213 |
+
clientBox.classList.add('alive', 'active');
|
| 214 |
+
},
|
| 215 |
+
animate: (done) => animatePath(
|
| 216 |
+
[{x: pos.client, y: yClient}, {x: pos.lb, y: yClient}, {x: pos.server1, y: yServer1}],
|
| 217 |
+
ANIMATION.MSG_DURATION,
|
| 218 |
+
done
|
| 219 |
+
),
|
| 220 |
+
after: () => {
|
| 221 |
+
clientBox.classList.remove('active');
|
| 222 |
+
server1Box.classList.add('alive', 'active');
|
| 223 |
+
}
|
| 224 |
+
},
|
| 225 |
+
// Step 2: Initialize result ← with session ID
|
| 226 |
+
{
|
| 227 |
+
setup: () => {
|
| 228 |
+
configMessage('response', '←', 'initialize', 'InitializeResult', 'Mcp-Session-Id: sess_abc123', false, 'left');
|
| 229 |
+
setMessagePos(pos.server1, yServer1);
|
| 230 |
+
stepNum.textContent = '2';
|
| 231 |
+
narrationText.textContent = 'Node 1 creates session, stores client capabilities';
|
| 232 |
+
narration.className = 'narration response-state';
|
| 233 |
+
// Populate server 1 state
|
| 234 |
+
server1Session.textContent = 'sess_abc123';
|
| 235 |
+
server1Caps.innerHTML = '<span class="cap">sampling ✓</span>';
|
| 236 |
+
server1State.classList.add('session');
|
| 237 |
+
},
|
| 238 |
+
animate: (done) => animatePath(
|
| 239 |
+
[{x: pos.server1, y: yServer1}, {x: pos.lb, y: yClient}, {x: pos.client, y: yClient}],
|
| 240 |
+
ANIMATION.MSG_DURATION,
|
| 241 |
+
done
|
| 242 |
+
),
|
| 243 |
+
after: () => {
|
| 244 |
+
server1Box.classList.remove('active');
|
| 245 |
+
clientSession.textContent = 'sess_abc123';
|
| 246 |
+
clientState.classList.add('session');
|
| 247 |
+
}
|
| 248 |
+
},
|
| 249 |
+
// Step 3: notifications/initialized → LB → Node 1
|
| 250 |
+
{
|
| 251 |
+
setup: () => {
|
| 252 |
+
configMessage('notification', '→', 'initialized', 'notification', 'Mcp-Session-Id: sess_abc123', false, 'right');
|
| 253 |
+
setMessagePos(pos.client, yClient);
|
| 254 |
+
stepNum.textContent = '3';
|
| 255 |
+
narrationText.textContent = 'Client sends initialized notification';
|
| 256 |
+
narration.className = 'narration notification-state';
|
| 257 |
+
clientBox.classList.add('active');
|
| 258 |
+
},
|
| 259 |
+
animate: (done) => animatePath(
|
| 260 |
+
[{x: pos.client, y: yClient}, {x: pos.lb, y: yClient}, {x: pos.server1, y: yServer1}],
|
| 261 |
+
ANIMATION.MSG_DURATION,
|
| 262 |
+
done
|
| 263 |
+
),
|
| 264 |
+
after: () => {
|
| 265 |
+
clientBox.classList.remove('active');
|
| 266 |
+
// Populate client state
|
| 267 |
+
clientSession.textContent = 'sess_abc123';
|
| 268 |
+
clientCaps.innerHTML = '<span class="cap">tools ✓</span><span class="cap">prompts ✓</span>';
|
| 269 |
+
clientState.classList.add('active');
|
| 270 |
+
server1State.classList.add('active');
|
| 271 |
+
}
|
| 272 |
+
},
|
| 273 |
+
// Step 4: tools/call → LB → Node 1 (correct)
|
| 274 |
+
{
|
| 275 |
+
setup: () => {
|
| 276 |
+
configMessage('request', '→', 'tools/call', 'CallToolRequest', 'Mcp-Session-Id: sess_abc123', false, 'right');
|
| 277 |
+
setMessagePos(pos.client, yClient);
|
| 278 |
+
stepNum.textContent = '4';
|
| 279 |
+
narrationText.textContent = 'Client calls tool with session ID';
|
| 280 |
+
narration.className = 'narration request-state';
|
| 281 |
+
clientBox.classList.add('active');
|
| 282 |
+
},
|
| 283 |
+
animate: (done) => animatePath(
|
| 284 |
+
[{x: pos.client, y: yClient}, {x: pos.lb, y: yClient}, {x: pos.server1, y: yServer1}],
|
| 285 |
+
ANIMATION.MSG_DURATION,
|
| 286 |
+
done
|
| 287 |
+
),
|
| 288 |
+
after: () => {
|
| 289 |
+
clientBox.classList.remove('active');
|
| 290 |
+
server1Box.classList.add('active');
|
| 291 |
+
}
|
| 292 |
+
},
|
| 293 |
+
// Step 5: tools/call result ←
|
| 294 |
+
{
|
| 295 |
+
setup: () => {
|
| 296 |
+
configMessage('response', '←', 'tools/call', 'CallToolResult', 'Mcp-Session-Id: sess_abc123', false, 'left');
|
| 297 |
+
setMessagePos(pos.server1, yServer1);
|
| 298 |
+
stepNum.textContent = '5';
|
| 299 |
+
narrationText.textContent = 'Node 1 has session + capabilities — success ✓';
|
| 300 |
+
narration.className = 'narration response-state';
|
| 301 |
+
},
|
| 302 |
+
animate: (done) => animatePath(
|
| 303 |
+
[{x: pos.server1, y: yServer1}, {x: pos.lb, y: yClient}, {x: pos.client, y: yClient}],
|
| 304 |
+
ANIMATION.MSG_DURATION,
|
| 305 |
+
done
|
| 306 |
+
),
|
| 307 |
+
after: () => {
|
| 308 |
+
server1Box.classList.remove('active');
|
| 309 |
+
}
|
| 310 |
+
},
|
| 311 |
+
// Step 6: Another request → LB → Node 3 (WRONG!)
|
| 312 |
+
{
|
| 313 |
+
setup: () => {
|
| 314 |
+
configMessage('request', '→', 'tools/call', 'CallToolRequest', 'Mcp-Session-Id: sess_abc123', false, 'right');
|
| 315 |
+
setMessagePos(pos.client, yClient);
|
| 316 |
+
stepNum.textContent = '6';
|
| 317 |
+
narrationText.textContent = 'Next request... LB routes to Node 3!';
|
| 318 |
+
narration.className = 'narration request-state';
|
| 319 |
+
clientBox.classList.add('active');
|
| 320 |
+
},
|
| 321 |
+
animate: (done) => animatePath(
|
| 322 |
+
[{x: pos.client, y: yClient}, {x: pos.lb, y: yClient}, {x: pos.server3, y: yServer3}],
|
| 323 |
+
ANIMATION.MSG_DURATION,
|
| 324 |
+
done
|
| 325 |
+
),
|
| 326 |
+
after: () => {
|
| 327 |
+
clientBox.classList.remove('active');
|
| 328 |
+
server3Box.classList.add('alive');
|
| 329 |
+
}
|
| 330 |
+
},
|
| 331 |
+
// Step 7: Error! Node 3 doesn't know this session
|
| 332 |
+
{
|
| 333 |
+
setup: () => {
|
| 334 |
+
configMessage('error-response', '←', '404 Not Found', 'Unknown session', 'Mcp-Session-Id: sess_abc123', true, 'left');
|
| 335 |
+
setMessagePos(pos.server3, yServer3);
|
| 336 |
+
stepNum.textContent = '✗';
|
| 337 |
+
narrationText.textContent = 'Node 3 has no session or capabilities — fails!';
|
| 338 |
+
narration.className = 'narration error-state';
|
| 339 |
+
server3Box.classList.add('error');
|
| 340 |
+
// Show error state with question marks
|
| 341 |
+
server3Session.textContent = 'sess_abc123 ?';
|
| 342 |
+
server3Caps.innerHTML = '<span class="cap">sampling ?</span>';
|
| 343 |
+
server3State.classList.add('error');
|
| 344 |
+
},
|
| 345 |
+
animate: (done) => animatePath(
|
| 346 |
+
[{x: pos.server3, y: yServer3}, {x: pos.lb, y: yClient}, {x: pos.client, y: yClient}],
|
| 347 |
+
ANIMATION.MSG_DURATION,
|
| 348 |
+
done
|
| 349 |
+
),
|
| 350 |
+
after: () => {
|
| 351 |
+
clientBox.classList.add('error');
|
| 352 |
+
}
|
| 353 |
+
},
|
| 354 |
+
// Step 8: Pause on error
|
| 355 |
+
{
|
| 356 |
+
setup: () => {
|
| 357 |
+
message.className = 'message';
|
| 358 |
+
},
|
| 359 |
+
animate: (done) => setTimeout(done, ANIMATION.ERROR_PAUSE),
|
| 360 |
+
after: () => {}
|
| 361 |
+
}
|
| 362 |
+
];
|
| 363 |
+
|
| 364 |
+
runStepSequence(steps);
|
| 365 |
+
scaleCanvas();
|
| 366 |
+
</script>
|
| 367 |
+
</body>
|
| 368 |
+
</html>
|
2026/mcp-connect/animations/mcp-mrtr-flow.html
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<style>
|
| 6 |
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 7 |
+
body {
|
| 8 |
+
background: transparent;
|
| 9 |
+
display: flex;
|
| 10 |
+
justify-content: center;
|
| 11 |
+
align-items: center;
|
| 12 |
+
width: 100vw;
|
| 13 |
+
height: 100vh;
|
| 14 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 15 |
+
padding: 0;
|
| 16 |
+
overflow: hidden;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
.flow-window {
|
| 20 |
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
| 21 |
+
border-radius: 16px;
|
| 22 |
+
padding: 20px;
|
| 23 |
+
width: 480px;
|
| 24 |
+
height: 460px;
|
| 25 |
+
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
| 26 |
+
overflow: hidden;
|
| 27 |
+
display: flex;
|
| 28 |
+
flex-direction: column;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
.flow-header {
|
| 32 |
+
display: flex;
|
| 33 |
+
justify-content: space-between;
|
| 34 |
+
margin-bottom: 16px;
|
| 35 |
+
padding-bottom: 10px;
|
| 36 |
+
border-bottom: 1px solid #333;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
.flow-header span {
|
| 40 |
+
font-size: 14px;
|
| 41 |
+
font-weight: 600;
|
| 42 |
+
text-transform: uppercase;
|
| 43 |
+
letter-spacing: 1px;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
.client-label { color: #0b93f6; }
|
| 47 |
+
.server-label { color: #f5a623; }
|
| 48 |
+
|
| 49 |
+
.flow-messages {
|
| 50 |
+
display: flex;
|
| 51 |
+
flex-direction: column;
|
| 52 |
+
gap: 10px;
|
| 53 |
+
flex: 1;
|
| 54 |
+
overflow-y: auto;
|
| 55 |
+
padding-right: 6px;
|
| 56 |
+
scrollbar-width: none;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
.flow-messages::-webkit-scrollbar {
|
| 60 |
+
width: 0;
|
| 61 |
+
height: 0;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
.flow-msg {
|
| 65 |
+
display: flex;
|
| 66 |
+
align-items: center;
|
| 67 |
+
gap: 10px;
|
| 68 |
+
opacity: 0;
|
| 69 |
+
transform: translateY(10px);
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
.flow-msg.show {
|
| 73 |
+
animation: slideIn 0.4s ease forwards;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
.flow-msg.fade-out {
|
| 77 |
+
animation: fadeOut 0.8s ease forwards;
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
.flow-msg.client-msg { flex-direction: row; }
|
| 81 |
+
.flow-msg.server-msg { flex-direction: row-reverse; }
|
| 82 |
+
|
| 83 |
+
.arrow {
|
| 84 |
+
font-size: 20px;
|
| 85 |
+
flex-shrink: 0;
|
| 86 |
+
width: 30px;
|
| 87 |
+
text-align: center;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
.client-msg .arrow { color: #0b93f6; }
|
| 91 |
+
.server-msg .arrow { color: #f5a623; }
|
| 92 |
+
|
| 93 |
+
.msg-content {
|
| 94 |
+
flex: 1;
|
| 95 |
+
padding: 12px 16px;
|
| 96 |
+
border-radius: 10px;
|
| 97 |
+
font-size: 14px;
|
| 98 |
+
line-height: 1.5;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
.client-msg .msg-content {
|
| 102 |
+
background: linear-gradient(135deg, #0b93f6 0%, #007AFF 100%);
|
| 103 |
+
color: white;
|
| 104 |
+
border-bottom-left-radius: 3px;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
.server-msg .msg-content {
|
| 108 |
+
background: linear-gradient(135deg, #f5a623 0%, #f09819 100%);
|
| 109 |
+
color: white;
|
| 110 |
+
border-bottom-right-radius: 3px;
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
.msg-type {
|
| 114 |
+
font-weight: 600;
|
| 115 |
+
font-size: 12px;
|
| 116 |
+
opacity: 0.9;
|
| 117 |
+
margin-bottom: 4px;
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
.msg-detail {
|
| 121 |
+
font-size: 12px;
|
| 122 |
+
opacity: 0.85;
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
@keyframes slideIn {
|
| 126 |
+
from { opacity: 0; transform: translateY(10px); }
|
| 127 |
+
to { opacity: 1; transform: translateY(0); }
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
@keyframes fadeOut {
|
| 131 |
+
from { opacity: 1; }
|
| 132 |
+
to { opacity: 0; }
|
| 133 |
+
}
|
| 134 |
+
</style>
|
| 135 |
+
</head>
|
| 136 |
+
<body>
|
| 137 |
+
<div class="flow-window canvas-root">
|
| 138 |
+
<div class="flow-header">
|
| 139 |
+
<span class="client-label">Client</span>
|
| 140 |
+
<span class="server-label">Server</span>
|
| 141 |
+
</div>
|
| 142 |
+
<div class="flow-messages">
|
| 143 |
+
<div class="flow-msg client-msg" data-delay="0">
|
| 144 |
+
<div class="msg-content">
|
| 145 |
+
<div class="msg-type">CallToolRequest</div>
|
| 146 |
+
<div class="msg-detail">tool: "deploy_app", env: "production"</div>
|
| 147 |
+
</div>
|
| 148 |
+
<div class="arrow">→</div>
|
| 149 |
+
</div>
|
| 150 |
+
|
| 151 |
+
<div class="flow-msg server-msg" data-delay="2500">
|
| 152 |
+
<div class="msg-content">
|
| 153 |
+
<div class="msg-type">elicitation/create</div>
|
| 154 |
+
<div class="msg-detail">server waits for client reply</div>
|
| 155 |
+
</div>
|
| 156 |
+
<div class="arrow">←</div>
|
| 157 |
+
</div>
|
| 158 |
+
|
| 159 |
+
<div class="flow-msg client-msg" data-delay="5000">
|
| 160 |
+
<div class="msg-content">
|
| 161 |
+
<div class="msg-type">elicitation/response</div>
|
| 162 |
+
<div class="msg-detail">client must retain request id</div>
|
| 163 |
+
</div>
|
| 164 |
+
<div class="arrow">→</div>
|
| 165 |
+
</div>
|
| 166 |
+
|
| 167 |
+
<div class="flow-msg server-msg" data-delay="7500">
|
| 168 |
+
<div class="msg-content">
|
| 169 |
+
<div class="msg-type">sampling/createMessage</div>
|
| 170 |
+
<div class="msg-detail">server keeps call context</div>
|
| 171 |
+
</div>
|
| 172 |
+
<div class="arrow">←</div>
|
| 173 |
+
</div>
|
| 174 |
+
|
| 175 |
+
<div class="flow-msg client-msg" data-delay="10000">
|
| 176 |
+
<div class="msg-content">
|
| 177 |
+
<div class="msg-type">sampling/response</div>
|
| 178 |
+
<div class="msg-detail">client responds on new request</div>
|
| 179 |
+
</div>
|
| 180 |
+
<div class="arrow">→</div>
|
| 181 |
+
</div>
|
| 182 |
+
|
| 183 |
+
<div class="flow-msg server-msg" data-delay="12500">
|
| 184 |
+
<div class="msg-content">
|
| 185 |
+
<div class="msg-type">CallToolResult</div>
|
| 186 |
+
<div class="msg-detail">status: "success" ✓</div>
|
| 187 |
+
</div>
|
| 188 |
+
<div class="arrow">←</div>
|
| 189 |
+
</div>
|
| 190 |
+
</div>
|
| 191 |
+
</div>
|
| 192 |
+
|
| 193 |
+
<script src="shared/sequence-helpers.js"></script>
|
| 194 |
+
<script src="shared/canvas-scale.js"></script>
|
| 195 |
+
<script>
|
| 196 |
+
const flowMessages = document.querySelector('.flow-messages');
|
| 197 |
+
const flowItems = Array.from(document.querySelectorAll('.flow-msg'));
|
| 198 |
+
const delays = flowItems.map((item) => parseInt(item.dataset.delay, 10) || 0);
|
| 199 |
+
const maxDelay = delays.length ? Math.max(...delays) : 0;
|
| 200 |
+
const FADE_OUT_OFFSET = 2500;
|
| 201 |
+
const CYCLE_PAUSE = 3500;
|
| 202 |
+
const cycleDuration = maxDelay + FADE_OUT_OFFSET + 1000 + CYCLE_PAUSE;
|
| 203 |
+
|
| 204 |
+
flowMessages.scrollTop = 0;
|
| 205 |
+
setInterval(() => {
|
| 206 |
+
flowMessages.scrollTop = 0;
|
| 207 |
+
}, cycleDuration);
|
| 208 |
+
|
| 209 |
+
runSequenceAnimation({
|
| 210 |
+
selectors: ['.flow-msg'],
|
| 211 |
+
cyclePause: CYCLE_PAUSE,
|
| 212 |
+
fadeOutOffset: FADE_OUT_OFFSET,
|
| 213 |
+
initialDelay: 500,
|
| 214 |
+
onShow: (el) => {
|
| 215 |
+
el.scrollIntoView({ block: 'nearest' });
|
| 216 |
+
},
|
| 217 |
+
});
|
| 218 |
+
scaleCanvas();
|
| 219 |
+
</script>
|
| 220 |
+
</body>
|
| 221 |
+
</html>
|
2026/mcp-connect/animations/mcp-mrtr-request.html
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<style>
|
| 6 |
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 7 |
+
body {
|
| 8 |
+
background: transparent;
|
| 9 |
+
display: flex;
|
| 10 |
+
justify-content: center;
|
| 11 |
+
align-items: center;
|
| 12 |
+
width: 100vw;
|
| 13 |
+
height: 100vh;
|
| 14 |
+
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
|
| 15 |
+
padding: 0;
|
| 16 |
+
overflow: hidden;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
.api-window {
|
| 20 |
+
background: linear-gradient(135deg, #1e1e1e 0%, #252526 100%);
|
| 21 |
+
border-radius: 16px;
|
| 22 |
+
padding: 12px;
|
| 23 |
+
width: 480px;
|
| 24 |
+
height: 460px;
|
| 25 |
+
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
| 26 |
+
overflow: hidden;
|
| 27 |
+
display: flex;
|
| 28 |
+
flex-direction: column;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
.api-header {
|
| 32 |
+
color: #888;
|
| 33 |
+
font-size: 11px;
|
| 34 |
+
margin-bottom: 4px;
|
| 35 |
+
padding-bottom: 3px;
|
| 36 |
+
border-bottom: 1px solid #333;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
.request-label {
|
| 40 |
+
color: #569cd6;
|
| 41 |
+
font-size: 10px;
|
| 42 |
+
margin-bottom: 2px;
|
| 43 |
+
opacity: 0;
|
| 44 |
+
}
|
| 45 |
+
.request-label.show { animation: fadeIn 0.3s ease forwards; }
|
| 46 |
+
.request-label.fade-out { animation: fadeOut 0.8s ease forwards; }
|
| 47 |
+
|
| 48 |
+
.api-content {
|
| 49 |
+
font-size: 10px;
|
| 50 |
+
line-height: 1.3;
|
| 51 |
+
flex: 1;
|
| 52 |
+
overflow: auto;
|
| 53 |
+
padding-right: 6px;
|
| 54 |
+
scrollbar-width: none;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
.api-content::-webkit-scrollbar {
|
| 58 |
+
display: none;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
.json-brace { color: #d4d4d4; }
|
| 62 |
+
.json-key { color: #9cdcfe; }
|
| 63 |
+
.json-string { color: #ce9178; }
|
| 64 |
+
.json-bracket { color: #d4d4d4; }
|
| 65 |
+
.json-bool { color: #569cd6; }
|
| 66 |
+
|
| 67 |
+
.msg-block {
|
| 68 |
+
opacity: 0;
|
| 69 |
+
transform: translateX(-10px);
|
| 70 |
+
margin: 1px 0;
|
| 71 |
+
padding: 1px 0 1px 6px;
|
| 72 |
+
color: #d4d4d4;
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
.msg-block.show {
|
| 76 |
+
animation: slideInLeft 0.4s ease forwards;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
.msg-block.fade-out {
|
| 80 |
+
animation: fadeOut 0.8s ease forwards;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
.msg-block.client-req { border-left: 2px solid #0b93f6; }
|
| 84 |
+
.msg-block.server-req { border-left: 2px solid #f5a623; }
|
| 85 |
+
.msg-block.client-resp { border-left: 2px solid #34c759; }
|
| 86 |
+
|
| 87 |
+
.static-content {
|
| 88 |
+
opacity: 0;
|
| 89 |
+
}
|
| 90 |
+
.static-content.show { animation: fadeIn 0.3s ease forwards; }
|
| 91 |
+
.static-content.fade-out { animation: fadeOut 0.8s ease forwards; }
|
| 92 |
+
|
| 93 |
+
.request-group {
|
| 94 |
+
margin-bottom: 2px;
|
| 95 |
+
padding-bottom: 2px;
|
| 96 |
+
border-bottom: 1px dashed #333;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
.request-group:last-child {
|
| 100 |
+
border-bottom: none;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
@keyframes slideInLeft {
|
| 104 |
+
from { opacity: 0; transform: translateX(-10px); }
|
| 105 |
+
to { opacity: 1; transform: translateX(0); }
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
@keyframes fadeIn {
|
| 109 |
+
from { opacity: 0; }
|
| 110 |
+
to { opacity: 1; }
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
@keyframes fadeOut {
|
| 114 |
+
from { opacity: 1; }
|
| 115 |
+
to { opacity: 0; }
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
.dim { opacity: 0.5; }
|
| 119 |
+
</style>
|
| 120 |
+
</head>
|
| 121 |
+
<body>
|
| 122 |
+
<div class="api-window canvas-root">
|
| 123 |
+
<div class="api-header">POST /mcp (Client → Server)</div>
|
| 124 |
+
<div class="api-content">
|
| 125 |
+
|
| 126 |
+
<!-- Request 1: Initial CallToolRequest -->
|
| 127 |
+
<div class="request-group">
|
| 128 |
+
<div class="request-label" data-delay="0">Request #1 — tools/call</div>
|
| 129 |
+
<div class="msg-block client-req" data-delay="0">
|
| 130 |
+
<span class="json-brace">{</span><br>
|
| 131 |
+
<span class="json-key">"method"</span>: <span class="json-string">"tools/call"</span>,<br>
|
| 132 |
+
<span class="json-key">"params"</span>: <span class="json-brace">{</span> <span class="json-key">"name"</span>: <span class="json-string">"deploy_app"</span>, <span class="json-key">"env"</span>: <span class="json-string">"production"</span> <span class="json-brace">}</span><br>
|
| 133 |
+
<span class="json-brace">}</span>
|
| 134 |
+
</div>
|
| 135 |
+
</div>
|
| 136 |
+
|
| 137 |
+
<!-- Response 1: Incomplete result with dependent requests -->
|
| 138 |
+
<div class="request-group">
|
| 139 |
+
<div class="request-label" data-delay="2500">Response #1 — incomplete (dependent_requests)</div>
|
| 140 |
+
<div class="msg-block server-req" data-delay="2500">
|
| 141 |
+
<span class="json-brace">{</span><br>
|
| 142 |
+
<span class="json-key">"dependent_requests"</span>: <span class="json-brace">{</span><br>
|
| 143 |
+
<span class="json-key">"confirm"</span>: <span class="json-string">"elicitation/create"</span><br>
|
| 144 |
+
<span class="json-brace">}</span><br>
|
| 145 |
+
<span class="json-brace">}</span>
|
| 146 |
+
</div>
|
| 147 |
+
</div>
|
| 148 |
+
|
| 149 |
+
<!-- Request 2: Retry with dependent responses -->
|
| 150 |
+
<div class="request-group">
|
| 151 |
+
<div class="request-label" data-delay="5000">Request #2 — retry (elicitation response)</div>
|
| 152 |
+
<div class="msg-block client-req" data-delay="5000">
|
| 153 |
+
<span class="json-brace">{</span><br>
|
| 154 |
+
<span class="json-key">"method"</span>: <span class="json-string">"tools/call"</span>,<br>
|
| 155 |
+
<span class="json-key">"params"</span>: <span class="json-brace">{</span> <span class="json-key">"name"</span>: <span class="json-string">"deploy_app"</span> <span class="json-brace">}</span>,<br>
|
| 156 |
+
<span class="json-key">"dependent_responses"</span>: <span class="json-brace">{</span><br>
|
| 157 |
+
<span class="json-key">"confirm"</span>: <span class="json-brace">{</span> <span class="json-key">"result"</span>: <span class="json-brace">{</span> <span class="json-key">"action"</span>: <span class="json-string">"accept"</span> <span class="json-brace">}</span> <span class="json-brace">}</span><br>
|
| 158 |
+
<span class="json-brace">}</span><br>
|
| 159 |
+
<span class="json-brace">}</span>
|
| 160 |
+
</div>
|
| 161 |
+
</div>
|
| 162 |
+
|
| 163 |
+
<!-- Response 2: Incomplete result with next dependent request -->
|
| 164 |
+
<div class="request-group">
|
| 165 |
+
<div class="request-label" data-delay="7500">Response #2 — sampling request (dependent_requests)</div>
|
| 166 |
+
<div class="msg-block server-req" data-delay="7500">
|
| 167 |
+
<span class="json-brace">{</span><br>
|
| 168 |
+
<span class="json-key">"dependent_requests"</span>: <span class="json-brace">{</span><br>
|
| 169 |
+
<span class="json-key">"summary"</span>: <span class="json-string">"sampling/createMessage"</span><br>
|
| 170 |
+
<span class="json-brace">}</span><br>
|
| 171 |
+
<span class="json-brace">}</span>
|
| 172 |
+
</div>
|
| 173 |
+
</div>
|
| 174 |
+
|
| 175 |
+
<div class="request-group">
|
| 176 |
+
<div class="request-label" data-delay="10000">Request #3 — retry (sampling response added)</div>
|
| 177 |
+
<div class="msg-block client-req" data-delay="10000">
|
| 178 |
+
<span class="json-brace">{</span><br>
|
| 179 |
+
<span class="json-key">"method"</span>: <span class="json-string">"tools/call"</span>,<br>
|
| 180 |
+
<span class="json-key">"params"</span>: <span class="json-brace">{</span> <span class="json-key">"name"</span>: <span class="json-string">"deploy_app"</span> <span class="json-brace">}</span>,<br>
|
| 181 |
+
<span class="json-key">"dependent_responses"</span>: <span class="json-brace">{</span><br>
|
| 182 |
+
<span class="json-key">"confirm"</span>: <span class="json-brace">{</span> <span class="json-key">"result"</span>: <span class="json-brace">{</span> <span class="json-key">"action"</span>: <span class="json-string">"accept"</span> <span class="json-brace">}</span> <span class="json-brace">}</span>,<br>
|
| 183 |
+
<span class="json-key">"summary"</span>: <span class="json-brace">{</span> <span class="json-key">"result"</span>: <span class="json-brace">{</span> <span class="json-key">"content"</span>: <span class="json-string">"..."</span> <span class="json-brace">}</span> <span class="json-brace">}</span><br>
|
| 184 |
+
<span class="json-brace">}</span><br>
|
| 185 |
+
<span class="json-brace">}</span>
|
| 186 |
+
</div>
|
| 187 |
+
</div>
|
| 188 |
+
|
| 189 |
+
<!-- Response 3: Final result -->
|
| 190 |
+
<div class="request-group">
|
| 191 |
+
<div class="request-label" data-delay="12500">Response #3 — CallToolResult</div>
|
| 192 |
+
<div class="msg-block server-req" data-delay="12500">
|
| 193 |
+
<span class="json-brace">{</span><br>
|
| 194 |
+
<span class="json-key">"result"</span>: <span class="json-brace">{</span> <span class="json-key">"status"</span>: <span class="json-string">"success"</span> <span class="json-brace">}</span><br>
|
| 195 |
+
<span class="json-brace">}</span>
|
| 196 |
+
</div>
|
| 197 |
+
</div>
|
| 198 |
+
|
| 199 |
+
</div>
|
| 200 |
+
</div>
|
| 201 |
+
|
| 202 |
+
<script src="shared/sequence-helpers.js"></script>
|
| 203 |
+
<script src="shared/canvas-scale.js"></script>
|
| 204 |
+
<script>
|
| 205 |
+
const apiContent = document.querySelector('.api-content');
|
| 206 |
+
const timelineItems = Array.from(document.querySelectorAll('.msg-block, .request-label'));
|
| 207 |
+
const delays = timelineItems.map((item) => parseInt(item.dataset.delay, 10) || 0);
|
| 208 |
+
const maxDelay = delays.length ? Math.max(...delays) : 0;
|
| 209 |
+
const FADE_OUT_OFFSET = 2500;
|
| 210 |
+
const CYCLE_PAUSE = 3500;
|
| 211 |
+
const cycleDuration = maxDelay + FADE_OUT_OFFSET + 1000 + CYCLE_PAUSE;
|
| 212 |
+
|
| 213 |
+
apiContent.scrollTop = 0;
|
| 214 |
+
setInterval(() => {
|
| 215 |
+
apiContent.scrollTop = 0;
|
| 216 |
+
}, cycleDuration);
|
| 217 |
+
|
| 218 |
+
runSequenceAnimation({
|
| 219 |
+
selectors: ['.msg-block', '.request-label'],
|
| 220 |
+
cyclePause: CYCLE_PAUSE,
|
| 221 |
+
fadeOutOffset: FADE_OUT_OFFSET,
|
| 222 |
+
initialDelay: 500,
|
| 223 |
+
lastDelayOverride: 12500,
|
| 224 |
+
onShow: (el) => {
|
| 225 |
+
if (el.classList.contains('msg-block')) {
|
| 226 |
+
el.scrollIntoView({ block: 'nearest' });
|
| 227 |
+
}
|
| 228 |
+
},
|
| 229 |
+
});
|
| 230 |
+
scaleCanvas();
|
| 231 |
+
</script>
|
| 232 |
+
</body>
|
| 233 |
+
</html>
|
2026/mcp-connect/animations/mcp-stateful-request.html
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<style>
|
| 6 |
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 7 |
+
body {
|
| 8 |
+
background: transparent;
|
| 9 |
+
display: flex;
|
| 10 |
+
justify-content: center;
|
| 11 |
+
align-items: center;
|
| 12 |
+
width: 100vw;
|
| 13 |
+
height: 100vh;
|
| 14 |
+
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
|
| 15 |
+
padding: 0;
|
| 16 |
+
overflow: hidden;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
.api-window {
|
| 20 |
+
background: linear-gradient(135deg, #1e1e1e 0%, #252526 100%);
|
| 21 |
+
border-radius: 16px;
|
| 22 |
+
padding: 20px;
|
| 23 |
+
width: 480px;
|
| 24 |
+
height: 460px;
|
| 25 |
+
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
| 26 |
+
overflow: hidden;
|
| 27 |
+
position: relative;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
.api-header {
|
| 31 |
+
color: #888;
|
| 32 |
+
font-size: 11px;
|
| 33 |
+
margin-bottom: 12px;
|
| 34 |
+
padding-bottom: 8px;
|
| 35 |
+
border-bottom: 1px solid #333;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
.direction-label {
|
| 39 |
+
font-size: 10px;
|
| 40 |
+
margin-bottom: 10px;
|
| 41 |
+
padding: 4px 8px;
|
| 42 |
+
border-radius: 4px;
|
| 43 |
+
display: inline-block;
|
| 44 |
+
}
|
| 45 |
+
.to-server { background: #0b93f620; color: #0b93f6; }
|
| 46 |
+
.to-client { background: #f5a62320; color: #f5a623; }
|
| 47 |
+
|
| 48 |
+
.single-message {
|
| 49 |
+
position: absolute;
|
| 50 |
+
top: 80px;
|
| 51 |
+
left: 20px;
|
| 52 |
+
right: 20px;
|
| 53 |
+
opacity: 0;
|
| 54 |
+
transform: scale(0.95);
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
.single-message.show {
|
| 58 |
+
animation: popIn 0.3s ease forwards;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
.single-message.fade-out {
|
| 62 |
+
animation: popOut 0.25s ease forwards;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
.msg-box {
|
| 66 |
+
padding: 16px;
|
| 67 |
+
border-radius: 10px;
|
| 68 |
+
font-size: 11px;
|
| 69 |
+
line-height: 1.5;
|
| 70 |
+
color: #d4d4d4;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
.client-msg .msg-box {
|
| 74 |
+
background: linear-gradient(135deg, #0b93f615 0%, #007AFF15 100%);
|
| 75 |
+
border: 1px solid #0b93f640;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
.server-msg .msg-box {
|
| 79 |
+
background: linear-gradient(135deg, #f5a62315 0%, #f0981915 100%);
|
| 80 |
+
border: 1px solid #f5a62340;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
.json-brace { color: #d4d4d4; }
|
| 84 |
+
.json-key { color: #9cdcfe; }
|
| 85 |
+
.json-string { color: #ce9178; }
|
| 86 |
+
.json-bool { color: #569cd6; }
|
| 87 |
+
|
| 88 |
+
.state-indicator {
|
| 89 |
+
position: absolute;
|
| 90 |
+
bottom: 20px;
|
| 91 |
+
left: 20px;
|
| 92 |
+
right: 20px;
|
| 93 |
+
padding: 12px;
|
| 94 |
+
background: #2d2d3a;
|
| 95 |
+
border-radius: 8px;
|
| 96 |
+
font-size: 10px;
|
| 97 |
+
color: #888;
|
| 98 |
+
opacity: 0;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
.state-indicator.show {
|
| 102 |
+
animation: fadeIn 0.3s ease forwards;
|
| 103 |
+
}
|
| 104 |
+
.state-indicator.fade-out {
|
| 105 |
+
animation: fadeOut 0.5s ease forwards;
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
.state-indicator .label {
|
| 109 |
+
color: #f5a623;
|
| 110 |
+
font-weight: 600;
|
| 111 |
+
margin-bottom: 4px;
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
.state-dots {
|
| 115 |
+
display: flex;
|
| 116 |
+
gap: 6px;
|
| 117 |
+
margin-top: 8px;
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
.state-dot {
|
| 121 |
+
width: 8px;
|
| 122 |
+
height: 8px;
|
| 123 |
+
border-radius: 50%;
|
| 124 |
+
background: #444;
|
| 125 |
+
}
|
| 126 |
+
.state-dot.active { background: #f5a623; }
|
| 127 |
+
|
| 128 |
+
@keyframes popIn {
|
| 129 |
+
from { opacity: 0; transform: scale(0.95); }
|
| 130 |
+
to { opacity: 1; transform: scale(1); }
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
@keyframes popOut {
|
| 134 |
+
from { opacity: 1; transform: scale(1); }
|
| 135 |
+
to { opacity: 0; transform: scale(0.95); }
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
@keyframes fadeIn {
|
| 139 |
+
from { opacity: 0; }
|
| 140 |
+
to { opacity: 1; }
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
@keyframes fadeOut {
|
| 144 |
+
from { opacity: 1; }
|
| 145 |
+
to { opacity: 0; }
|
| 146 |
+
}
|
| 147 |
+
</style>
|
| 148 |
+
</head>
|
| 149 |
+
<body>
|
| 150 |
+
<div class="api-window canvas-root">
|
| 151 |
+
<div class="api-header">Current: Individual Messages (Stateful)</div>
|
| 152 |
+
|
| 153 |
+
<!-- Message 1: CallToolRequest -->
|
| 154 |
+
<div class="single-message client-msg" id="msg1">
|
| 155 |
+
<div class="direction-label to-server">→ Client to Server</div>
|
| 156 |
+
<div class="msg-box">
|
| 157 |
+
<span class="json-brace">{</span><br>
|
| 158 |
+
<span class="json-key">"method"</span>: <span class="json-string">"tools/call"</span>,<br>
|
| 159 |
+
<span class="json-key">"params"</span>: <span class="json-brace">{</span><br>
|
| 160 |
+
<span class="json-key">"name"</span>: <span class="json-string">"deploy_app"</span>,<br>
|
| 161 |
+
<span class="json-key">"env"</span>: <span class="json-string">"production"</span><br>
|
| 162 |
+
<span class="json-brace">}</span><br>
|
| 163 |
+
<span class="json-brace">}</span>
|
| 164 |
+
</div>
|
| 165 |
+
</div>
|
| 166 |
+
|
| 167 |
+
<!-- Message 2: ElicitationRequest -->
|
| 168 |
+
<div class="single-message server-msg" id="msg2">
|
| 169 |
+
<div class="direction-label to-client">← Server to Client</div>
|
| 170 |
+
<div class="msg-box">
|
| 171 |
+
<span class="json-brace">{</span><br>
|
| 172 |
+
<span class="json-key">"method"</span>: <span class="json-string">"elicitation/create"</span>,<br>
|
| 173 |
+
<span class="json-key">"params"</span>: <span class="json-brace">{</span><br>
|
| 174 |
+
<span class="json-key">"message"</span>: <span class="json-string">"Confirm deployment to production?"</span><br>
|
| 175 |
+
<span class="json-brace">}</span><br>
|
| 176 |
+
<span class="json-brace">}</span>
|
| 177 |
+
</div>
|
| 178 |
+
</div>
|
| 179 |
+
|
| 180 |
+
<!-- Message 3: ElicitationResponse -->
|
| 181 |
+
<div class="single-message client-msg" id="msg3">
|
| 182 |
+
<div class="direction-label to-server">→ Client to Server</div>
|
| 183 |
+
<div class="msg-box">
|
| 184 |
+
<span class="json-brace">{</span><br>
|
| 185 |
+
<span class="json-key">"result"</span>: <span class="json-brace">{</span><br>
|
| 186 |
+
<span class="json-key">"confirmed"</span>: <span class="json-bool">true</span><br>
|
| 187 |
+
<span class="json-brace">}</span><br>
|
| 188 |
+
<span class="json-brace">}</span>
|
| 189 |
+
</div>
|
| 190 |
+
</div>
|
| 191 |
+
|
| 192 |
+
<!-- Message 4: SamplingRequest -->
|
| 193 |
+
<div class="single-message server-msg" id="msg4">
|
| 194 |
+
<div class="direction-label to-client">← Server to Client</div>
|
| 195 |
+
<div class="msg-box">
|
| 196 |
+
<span class="json-brace">{</span><br>
|
| 197 |
+
<span class="json-key">"method"</span>: <span class="json-string">"sampling/createMessage"</span>,<br>
|
| 198 |
+
<span class="json-key">"params"</span>: <span class="json-brace">{</span><br>
|
| 199 |
+
<span class="json-key">"prompt"</span>: <span class="json-string">"Generate deployment summary"</span><br>
|
| 200 |
+
<span class="json-brace">}</span><br>
|
| 201 |
+
<span class="json-brace">}</span>
|
| 202 |
+
</div>
|
| 203 |
+
</div>
|
| 204 |
+
|
| 205 |
+
<!-- Message 5: SamplingResponse -->
|
| 206 |
+
<div class="single-message client-msg" id="msg5">
|
| 207 |
+
<div class="direction-label to-server">→ Client to Server</div>
|
| 208 |
+
<div class="msg-box">
|
| 209 |
+
<span class="json-brace">{</span><br>
|
| 210 |
+
<span class="json-key">"result"</span>: <span class="json-brace">{</span><br>
|
| 211 |
+
<span class="json-key">"content"</span>: <span class="json-string">"Deployed v2.1.0 to prod..."</span><br>
|
| 212 |
+
<span class="json-brace">}</span><br>
|
| 213 |
+
<span class="json-brace">}</span>
|
| 214 |
+
</div>
|
| 215 |
+
</div>
|
| 216 |
+
|
| 217 |
+
<!-- Message 6: CallToolResult -->
|
| 218 |
+
<div class="single-message server-msg" id="msg6">
|
| 219 |
+
<div class="direction-label to-client">← Server to Client</div>
|
| 220 |
+
<div class="msg-box">
|
| 221 |
+
<span class="json-brace">{</span><br>
|
| 222 |
+
<span class="json-key">"result"</span>: <span class="json-brace">{</span><br>
|
| 223 |
+
<span class="json-key">"status"</span>: <span class="json-string">"success"</span><br>
|
| 224 |
+
<span class="json-brace">}</span><br>
|
| 225 |
+
<span class="json-brace">}</span>
|
| 226 |
+
</div>
|
| 227 |
+
</div>
|
| 228 |
+
|
| 229 |
+
<!-- State indicator -->
|
| 230 |
+
<div class="state-indicator" id="stateBox">
|
| 231 |
+
<div class="label">⚠ Server must maintain state</div>
|
| 232 |
+
<div>Remembering: tool call, elicitation, sampling context...</div>
|
| 233 |
+
<div class="state-dots">
|
| 234 |
+
<div class="state-dot" id="dot1"></div>
|
| 235 |
+
<div class="state-dot" id="dot2"></div>
|
| 236 |
+
<div class="state-dot" id="dot3"></div>
|
| 237 |
+
<div class="state-dot" id="dot4"></div>
|
| 238 |
+
<div class="state-dot" id="dot5"></div>
|
| 239 |
+
<div class="state-dot" id="dot6"></div>
|
| 240 |
+
</div>
|
| 241 |
+
</div>
|
| 242 |
+
</div>
|
| 243 |
+
|
| 244 |
+
<script src="shared/canvas-scale.js"></script>
|
| 245 |
+
<script>
|
| 246 |
+
const messages = [
|
| 247 |
+
document.getElementById('msg1'),
|
| 248 |
+
document.getElementById('msg2'),
|
| 249 |
+
document.getElementById('msg3'),
|
| 250 |
+
document.getElementById('msg4'),
|
| 251 |
+
document.getElementById('msg5'),
|
| 252 |
+
document.getElementById('msg6')
|
| 253 |
+
];
|
| 254 |
+
const dots = [
|
| 255 |
+
document.getElementById('dot1'),
|
| 256 |
+
document.getElementById('dot2'),
|
| 257 |
+
document.getElementById('dot3'),
|
| 258 |
+
document.getElementById('dot4'),
|
| 259 |
+
document.getElementById('dot5'),
|
| 260 |
+
document.getElementById('dot6')
|
| 261 |
+
];
|
| 262 |
+
const stateBox = document.getElementById('stateBox');
|
| 263 |
+
const CYCLE_PAUSE = 3000;
|
| 264 |
+
const MSG_DURATION = 2500;
|
| 265 |
+
|
| 266 |
+
function runAnimation() {
|
| 267 |
+
// Reset
|
| 268 |
+
messages.forEach(m => m.classList.remove('show', 'fade-out'));
|
| 269 |
+
dots.forEach(d => d.classList.remove('active'));
|
| 270 |
+
stateBox.classList.remove('show', 'fade-out');
|
| 271 |
+
|
| 272 |
+
// Force reflow to ensure class removal is processed
|
| 273 |
+
void document.body.offsetHeight;
|
| 274 |
+
|
| 275 |
+
// Show state box
|
| 276 |
+
setTimeout(() => stateBox.classList.add('show'), 200);
|
| 277 |
+
|
| 278 |
+
// Show each message one at a time
|
| 279 |
+
messages.forEach((msg, i) => {
|
| 280 |
+
const showTime = i * MSG_DURATION;
|
| 281 |
+
const hideTime = showTime + MSG_DURATION - 300;
|
| 282 |
+
|
| 283 |
+
setTimeout(() => {
|
| 284 |
+
// Hide previous
|
| 285 |
+
if (i > 0) messages[i-1].classList.add('fade-out');
|
| 286 |
+
// Show current
|
| 287 |
+
msg.classList.add('show');
|
| 288 |
+
// Activate dot
|
| 289 |
+
dots[i].classList.add('active');
|
| 290 |
+
}, showTime);
|
| 291 |
+
});
|
| 292 |
+
|
| 293 |
+
// Fade out last message and state
|
| 294 |
+
const totalTime = messages.length * MSG_DURATION;
|
| 295 |
+
setTimeout(() => {
|
| 296 |
+
messages[messages.length - 1].classList.add('fade-out');
|
| 297 |
+
stateBox.classList.add('fade-out');
|
| 298 |
+
}, totalTime);
|
| 299 |
+
|
| 300 |
+
// Restart
|
| 301 |
+
setTimeout(runAnimation, totalTime + 1500 + CYCLE_PAUSE);
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
function startAnimation() {
|
| 305 |
+
setTimeout(runAnimation, 500);
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
if (document.readyState === 'complete') {
|
| 309 |
+
startAnimation();
|
| 310 |
+
} else {
|
| 311 |
+
window.addEventListener('load', startAnimation, { once: true });
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
window.addEventListener('pageshow', startAnimation);
|
| 315 |
+
scaleCanvas();
|
| 316 |
+
</script>
|
| 317 |
+
</body>
|
| 318 |
+
</html>
|
2026/mcp-connect/animations/shared/animation-config.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Shared animation timing constants
|
| 2 |
+
const ANIMATION = {
|
| 3 |
+
MSG_DURATION: 1200, // message travel time (ms)
|
| 4 |
+
STEP_PAUSE: 1500, // pause between steps (ms)
|
| 5 |
+
INITIAL_PAUSE: 3000, // pause at start before animation begins (ms)
|
| 6 |
+
ERROR_PAUSE: 4000, // pause on error state (ms)
|
| 7 |
+
};
|
| 8 |
+
|
| 9 |
+
// Shared color palette
|
| 10 |
+
const COLORS = {
|
| 11 |
+
client: '#1976d2',
|
| 12 |
+
clientLight: '#e3f2fd',
|
| 13 |
+
clientActive: '#bbdefb',
|
| 14 |
+
|
| 15 |
+
server: '#ef6c00',
|
| 16 |
+
serverLight: '#fff3e0',
|
| 17 |
+
serverActive: '#ffe0b2',
|
| 18 |
+
|
| 19 |
+
notification: '#7b1fa2',
|
| 20 |
+
|
| 21 |
+
success: '#4caf50',
|
| 22 |
+
successLight: '#e8f5e9',
|
| 23 |
+
successBorder: '#a5d6a7',
|
| 24 |
+
|
| 25 |
+
error: '#c62828',
|
| 26 |
+
errorLight: '#ffebee',
|
| 27 |
+
errorBorder: '#ef9a9a',
|
| 28 |
+
|
| 29 |
+
inactive: '#9e9e9e',
|
| 30 |
+
inactiveLight: '#f5f5f5',
|
| 31 |
+
inactiveBorder: '#e0e0e0',
|
| 32 |
+
};
|
2026/mcp-connect/animations/shared/canvas-scale.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
function scaleCanvas({ selector = '.canvas-root', minScale = 0.25, maxScale = 4 } = {}) {
|
| 2 |
+
const canvases = Array.from(document.querySelectorAll(selector));
|
| 3 |
+
if (canvases.length === 0) {
|
| 4 |
+
return;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
function updateBaseSize(canvas) {
|
| 8 |
+
if (canvas.dataset.baseWidth && canvas.dataset.baseHeight) {
|
| 9 |
+
return;
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
const { width, height } = canvas.getBoundingClientRect();
|
| 13 |
+
if (width && height) {
|
| 14 |
+
canvas.dataset.baseWidth = width;
|
| 15 |
+
canvas.dataset.baseHeight = height;
|
| 16 |
+
}
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
function updateScale() {
|
| 20 |
+
canvases.forEach((canvas) => {
|
| 21 |
+
updateBaseSize(canvas);
|
| 22 |
+
const baseWidth = parseFloat(canvas.dataset.baseWidth || '0');
|
| 23 |
+
const baseHeight = parseFloat(canvas.dataset.baseHeight || '0');
|
| 24 |
+
if (!baseWidth || !baseHeight) {
|
| 25 |
+
return;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
const scale = Math.min(window.innerWidth / baseWidth, window.innerHeight / baseHeight);
|
| 29 |
+
const clamped = Math.max(minScale, Math.min(scale, maxScale));
|
| 30 |
+
canvas.style.transform = `scale(${clamped})`;
|
| 31 |
+
canvas.style.transformOrigin = 'center';
|
| 32 |
+
});
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
const observer = new ResizeObserver(() => updateScale());
|
| 36 |
+
canvases.forEach((canvas) => {
|
| 37 |
+
observer.observe(canvas);
|
| 38 |
+
updateBaseSize(canvas);
|
| 39 |
+
});
|
| 40 |
+
|
| 41 |
+
window.addEventListener('resize', updateScale);
|
| 42 |
+
window.addEventListener('load', () => requestAnimationFrame(updateScale));
|
| 43 |
+
requestAnimationFrame(updateScale);
|
| 44 |
+
}
|
2026/mcp-connect/animations/shared/diagram-helpers.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
function setMessagePosition(message, x, y) {
|
| 2 |
+
message.style.left = `${x}px`;
|
| 3 |
+
message.style.top = `${y}px`;
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
function animateMessageBetween(message, fromX, fromY, toX, toY, duration, callback) {
|
| 7 |
+
const startTime = performance.now();
|
| 8 |
+
|
| 9 |
+
function step(currentTime) {
|
| 10 |
+
const elapsed = currentTime - startTime;
|
| 11 |
+
const progress = Math.min(elapsed / duration, 1);
|
| 12 |
+
const eased = 1 - Math.pow(1 - progress, 3);
|
| 13 |
+
setMessagePosition(
|
| 14 |
+
message,
|
| 15 |
+
fromX + (toX - fromX) * eased,
|
| 16 |
+
fromY + (toY - fromY) * eased
|
| 17 |
+
);
|
| 18 |
+
|
| 19 |
+
if (progress < 1) {
|
| 20 |
+
requestAnimationFrame(step);
|
| 21 |
+
} else if (callback) {
|
| 22 |
+
callback();
|
| 23 |
+
}
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
requestAnimationFrame(step);
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
function animatePathBetween(message, waypoints, totalDuration, callback) {
|
| 30 |
+
if (waypoints.length < 2) {
|
| 31 |
+
if (callback) {
|
| 32 |
+
callback();
|
| 33 |
+
}
|
| 34 |
+
return;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
let totalDist = 0;
|
| 38 |
+
for (let i = 0; i < waypoints.length - 1; i++) {
|
| 39 |
+
const dx = waypoints[i + 1].x - waypoints[i].x;
|
| 40 |
+
const dy = waypoints[i + 1].y - waypoints[i].y;
|
| 41 |
+
totalDist += Math.sqrt(dx * dx + dy * dy);
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
const durations = [];
|
| 45 |
+
for (let i = 0; i < waypoints.length - 1; i++) {
|
| 46 |
+
const dx = waypoints[i + 1].x - waypoints[i].x;
|
| 47 |
+
const dy = waypoints[i + 1].y - waypoints[i].y;
|
| 48 |
+
const legDist = Math.sqrt(dx * dx + dy * dy);
|
| 49 |
+
durations.push((legDist / totalDist) * totalDuration);
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
let i = 0;
|
| 53 |
+
function nextLeg() {
|
| 54 |
+
if (i < waypoints.length - 1) {
|
| 55 |
+
animateMessageBetween(
|
| 56 |
+
message,
|
| 57 |
+
waypoints[i].x, waypoints[i].y,
|
| 58 |
+
waypoints[i + 1].x, waypoints[i + 1].y,
|
| 59 |
+
durations[i],
|
| 60 |
+
() => {
|
| 61 |
+
i++;
|
| 62 |
+
nextLeg();
|
| 63 |
+
}
|
| 64 |
+
);
|
| 65 |
+
} else if (callback) {
|
| 66 |
+
callback();
|
| 67 |
+
}
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
nextLeg();
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
function configureMessage(elements, {
|
| 74 |
+
type,
|
| 75 |
+
arrow,
|
| 76 |
+
method,
|
| 77 |
+
detail,
|
| 78 |
+
sessionText,
|
| 79 |
+
sessionError = false,
|
| 80 |
+
direction = 'right',
|
| 81 |
+
} = {}) {
|
| 82 |
+
const container = elements.container;
|
| 83 |
+
const header = elements.header || container.querySelector('.message-header');
|
| 84 |
+
|
| 85 |
+
container.className = `message ${type} visible`;
|
| 86 |
+
elements.type.textContent = method;
|
| 87 |
+
elements.arrow.textContent = arrow;
|
| 88 |
+
elements.detail.textContent = detail;
|
| 89 |
+
|
| 90 |
+
if (elements.session) {
|
| 91 |
+
if (sessionText) {
|
| 92 |
+
elements.session.innerHTML = sessionText;
|
| 93 |
+
elements.session.style.display = 'block';
|
| 94 |
+
elements.session.className = 'message-session' + (sessionError ? ' error' : '');
|
| 95 |
+
} else {
|
| 96 |
+
elements.session.style.display = 'none';
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
if (header) {
|
| 101 |
+
header.style.flexDirection = direction === 'left' ? 'row-reverse' : 'row';
|
| 102 |
+
}
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
function runStepSequence(steps, { initialDelay = 100, stepPause, startOnLoad = true } = {}) {
|
| 106 |
+
if (!steps || steps.length === 0) {
|
| 107 |
+
return;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
const pause = stepPause ?? (typeof ANIMATION !== 'undefined' ? ANIMATION.STEP_PAUSE : 1500);
|
| 111 |
+
let currentStep = 0;
|
| 112 |
+
let animationTimer = null;
|
| 113 |
+
|
| 114 |
+
function runStep() {
|
| 115 |
+
if (currentStep >= steps.length) {
|
| 116 |
+
currentStep = 0;
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
const step = steps[currentStep];
|
| 120 |
+
step.setup();
|
| 121 |
+
|
| 122 |
+
animationTimer = setTimeout(() => {
|
| 123 |
+
step.animate(() => {
|
| 124 |
+
if (step.after) {
|
| 125 |
+
step.after();
|
| 126 |
+
}
|
| 127 |
+
currentStep++;
|
| 128 |
+
animationTimer = setTimeout(runStep, pause);
|
| 129 |
+
});
|
| 130 |
+
}, initialDelay);
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
const start = () => {
|
| 134 |
+
if (animationTimer) {
|
| 135 |
+
clearTimeout(animationTimer);
|
| 136 |
+
}
|
| 137 |
+
currentStep = 0;
|
| 138 |
+
runStep();
|
| 139 |
+
};
|
| 140 |
+
|
| 141 |
+
if (startOnLoad) {
|
| 142 |
+
if (document.readyState === 'complete') {
|
| 143 |
+
start();
|
| 144 |
+
} else {
|
| 145 |
+
window.addEventListener('load', start, { once: true });
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
window.addEventListener('pageshow', start);
|
| 149 |
+
} else {
|
| 150 |
+
start();
|
| 151 |
+
}
|
| 152 |
+
}
|
2026/mcp-connect/animations/shared/http-diagram.css
ADDED
|
@@ -0,0 +1,505 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 2 |
+
body {
|
| 3 |
+
background: #fafafa;
|
| 4 |
+
display: flex;
|
| 5 |
+
align-items: center;
|
| 6 |
+
justify-content: center;
|
| 7 |
+
width: 100vw;
|
| 8 |
+
height: 100vh;
|
| 9 |
+
overflow: hidden;
|
| 10 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 11 |
+
padding: 0;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
.canvas-root {
|
| 15 |
+
display: inline-flex;
|
| 16 |
+
flex-direction: column;
|
| 17 |
+
align-items: center;
|
| 18 |
+
justify-content: center;
|
| 19 |
+
padding: 30px 20px;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
.diagram {
|
| 23 |
+
position: relative;
|
| 24 |
+
width: 780px;
|
| 25 |
+
height: 320px;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
/* Entity base styles */
|
| 29 |
+
.entity {
|
| 30 |
+
position: absolute;
|
| 31 |
+
display: flex;
|
| 32 |
+
flex-direction: column;
|
| 33 |
+
align-items: center;
|
| 34 |
+
gap: 8px;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
/* Client - starts grey, becomes blue */
|
| 38 |
+
.client { left: 0; top: 100px; }
|
| 39 |
+
.client .entity-box {
|
| 40 |
+
width: 130px;
|
| 41 |
+
height: 80px;
|
| 42 |
+
border-radius: 16px;
|
| 43 |
+
background: #f5f5f5;
|
| 44 |
+
border: 3px solid #9e9e9e;
|
| 45 |
+
color: #9e9e9e;
|
| 46 |
+
display: flex;
|
| 47 |
+
flex-direction: column;
|
| 48 |
+
align-items: center;
|
| 49 |
+
justify-content: center;
|
| 50 |
+
font-weight: 600;
|
| 51 |
+
font-size: 14px;
|
| 52 |
+
transition: all 0.5s ease;
|
| 53 |
+
text-align: center;
|
| 54 |
+
line-height: 1.3;
|
| 55 |
+
gap: 2px;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
.client .entity-box.alive {
|
| 59 |
+
background: #e3f2fd;
|
| 60 |
+
border-color: #1976d2;
|
| 61 |
+
color: #1976d2;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
.client .entity-box.active {
|
| 65 |
+
background: #bbdefb;
|
| 66 |
+
border-color: #1976d2;
|
| 67 |
+
color: #1976d2;
|
| 68 |
+
box-shadow: 0 4px 20px rgba(25, 118, 210, 0.3);
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
/* Load Balancer */
|
| 72 |
+
.lb { left: 200px; top: 105px; }
|
| 73 |
+
.lb .entity-box {
|
| 74 |
+
width: 80px;
|
| 75 |
+
height: 70px;
|
| 76 |
+
background: linear-gradient(180deg, #fafafa 0%, #e0e0e0 100%);
|
| 77 |
+
border: 2px solid #9e9e9e;
|
| 78 |
+
border-radius: 8px;
|
| 79 |
+
display: flex;
|
| 80 |
+
align-items: center;
|
| 81 |
+
justify-content: center;
|
| 82 |
+
color: #616161;
|
| 83 |
+
font-weight: 600;
|
| 84 |
+
font-size: 13px;
|
| 85 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
/* Server cluster bounding box */
|
| 89 |
+
.server-cluster {
|
| 90 |
+
position: absolute;
|
| 91 |
+
left: 400px;
|
| 92 |
+
top: 0;
|
| 93 |
+
width: 300px;
|
| 94 |
+
height: 320px;
|
| 95 |
+
border: 3px solid #ef6c00;
|
| 96 |
+
border-radius: 20px;
|
| 97 |
+
background: rgba(255, 243, 224, 0.3);
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
.cluster-label {
|
| 101 |
+
position: absolute;
|
| 102 |
+
top: -14px;
|
| 103 |
+
left: 24px;
|
| 104 |
+
background: #fafafa;
|
| 105 |
+
padding: 4px 16px;
|
| 106 |
+
font-size: 16px;
|
| 107 |
+
font-weight: 600;
|
| 108 |
+
color: #ef6c00;
|
| 109 |
+
white-space: nowrap;
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
/* Server nodes - horizontal layout with badge to right */
|
| 113 |
+
.server {
|
| 114 |
+
flex-direction: row;
|
| 115 |
+
align-items: center;
|
| 116 |
+
gap: 12px;
|
| 117 |
+
left: 420px;
|
| 118 |
+
}
|
| 119 |
+
.server-1 { top: 25px; }
|
| 120 |
+
.server-2 { top: 130px; }
|
| 121 |
+
.server-3 { top: 235px; }
|
| 122 |
+
|
| 123 |
+
.server .entity-box {
|
| 124 |
+
width: 100px;
|
| 125 |
+
height: 55px;
|
| 126 |
+
border-radius: 12px;
|
| 127 |
+
background: #f5f5f5;
|
| 128 |
+
border: 3px solid #bdbdbd;
|
| 129 |
+
color: #9e9e9e;
|
| 130 |
+
display: flex;
|
| 131 |
+
flex-direction: column;
|
| 132 |
+
align-items: center;
|
| 133 |
+
justify-content: center;
|
| 134 |
+
font-weight: 600;
|
| 135 |
+
font-size: 13px;
|
| 136 |
+
transition: all 0.5s ease;
|
| 137 |
+
text-align: center;
|
| 138 |
+
gap: 2px;
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
.server .entity-box.alive {
|
| 142 |
+
background: #fff3e0;
|
| 143 |
+
border-color: #ef6c00;
|
| 144 |
+
color: #ef6c00;
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
.server .entity-box.active {
|
| 148 |
+
background: #ffe0b2;
|
| 149 |
+
border-color: #ef6c00;
|
| 150 |
+
color: #ef6c00;
|
| 151 |
+
box-shadow: 0 4px 20px rgba(239, 108, 0, 0.3);
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
.server .entity-box.error {
|
| 155 |
+
background: #ffebee;
|
| 156 |
+
border-color: #c62828;
|
| 157 |
+
color: #c62828;
|
| 158 |
+
animation: shake 0.4s ease;
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
.server .entity-box.waiting {
|
| 162 |
+
animation: waitingPulse 1s ease-in-out infinite;
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
@keyframes waitingPulse {
|
| 166 |
+
0%, 100% {
|
| 167 |
+
background: #fff3e0;
|
| 168 |
+
border-color: #ef6c00;
|
| 169 |
+
box-shadow: 0 4px 20px rgba(239, 108, 0, 0.2);
|
| 170 |
+
}
|
| 171 |
+
50% {
|
| 172 |
+
background: #ffe0b2;
|
| 173 |
+
border-color: #e65100;
|
| 174 |
+
box-shadow: 0 4px 30px rgba(239, 108, 0, 0.5);
|
| 175 |
+
}
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
@keyframes shake {
|
| 179 |
+
0%, 100% { transform: translateX(0); }
|
| 180 |
+
25% { transform: translateX(-6px); }
|
| 181 |
+
75% { transform: translateX(6px); }
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
.entity-status {
|
| 185 |
+
font-size: 9px;
|
| 186 |
+
font-weight: 600;
|
| 187 |
+
text-transform: uppercase;
|
| 188 |
+
letter-spacing: 0.3px;
|
| 189 |
+
opacity: 0;
|
| 190 |
+
transition: opacity 0.3s ease;
|
| 191 |
+
padding: 2px 6px;
|
| 192 |
+
border-radius: 3px;
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
.entity-status.visible {
|
| 196 |
+
opacity: 1;
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
.entity-status.waiting {
|
| 200 |
+
background: #e65100;
|
| 201 |
+
color: white;
|
| 202 |
+
animation: statusBlink 0.8s ease-in-out infinite;
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
@keyframes statusBlink {
|
| 206 |
+
0%, 100% { opacity: 1; transform: scale(1); }
|
| 207 |
+
50% { opacity: 0.7; transform: scale(1.05); }
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
/* State badge - shows session + capabilities */
|
| 211 |
+
.state-badge {
|
| 212 |
+
display: flex;
|
| 213 |
+
flex-direction: column;
|
| 214 |
+
align-items: flex-start;
|
| 215 |
+
gap: 4px;
|
| 216 |
+
padding: 8px 10px;
|
| 217 |
+
border-radius: 8px;
|
| 218 |
+
font-size: 10px;
|
| 219 |
+
font-weight: 600;
|
| 220 |
+
transition: all 0.4s ease;
|
| 221 |
+
/* Default: gray/inactive state */
|
| 222 |
+
background: #f5f5f5;
|
| 223 |
+
border: 1px solid #e0e0e0;
|
| 224 |
+
color: #9e9e9e;
|
| 225 |
+
min-width: 140px;
|
| 226 |
+
min-height: 64px;
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
.state-badge.active {
|
| 230 |
+
background: #e8f5e9;
|
| 231 |
+
border: 1px solid #a5d6a7;
|
| 232 |
+
color: #2e7d32;
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
.state-badge.error {
|
| 236 |
+
background: #ffebee;
|
| 237 |
+
border-color: #ef9a9a;
|
| 238 |
+
color: #c62828;
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
.state-badge .badge-title {
|
| 242 |
+
font-size: 8px;
|
| 243 |
+
text-transform: uppercase;
|
| 244 |
+
letter-spacing: 0.5px;
|
| 245 |
+
color: #888;
|
| 246 |
+
font-weight: 500;
|
| 247 |
+
}
|
| 248 |
+
|
| 249 |
+
.state-badge .session-id {
|
| 250 |
+
font-family: 'SF Mono', Monaco, monospace;
|
| 251 |
+
font-size: 10px;
|
| 252 |
+
display: none;
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
.state-badge.active .session-id,
|
| 256 |
+
.state-badge.error .session-id {
|
| 257 |
+
display: block;
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
.state-badge.session .session-id {
|
| 261 |
+
display: block;
|
| 262 |
+
}
|
| 263 |
+
|
| 264 |
+
.state-badge.session .empty-state {
|
| 265 |
+
display: none;
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
.state-badge .caps-row {
|
| 269 |
+
display: none;
|
| 270 |
+
gap: 4px;
|
| 271 |
+
flex-wrap: wrap;
|
| 272 |
+
}
|
| 273 |
+
|
| 274 |
+
.state-badge.active .caps-row,
|
| 275 |
+
.state-badge.error .caps-row {
|
| 276 |
+
display: flex;
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
.state-badge .cap {
|
| 280 |
+
font-family: 'SF Mono', Monaco, monospace;
|
| 281 |
+
font-size: 8px;
|
| 282 |
+
background: white;
|
| 283 |
+
padding: 1px 5px;
|
| 284 |
+
border-radius: 3px;
|
| 285 |
+
border: 1px solid #e0e0e0;
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
.state-badge.active .cap {
|
| 289 |
+
border-color: #c8e6c9;
|
| 290 |
+
}
|
| 291 |
+
|
| 292 |
+
.state-badge.error .cap {
|
| 293 |
+
border-color: #ef9a9a;
|
| 294 |
+
}
|
| 295 |
+
|
| 296 |
+
.state-badge .empty-state {
|
| 297 |
+
font-size: 9px;
|
| 298 |
+
color: #bdbdbd;
|
| 299 |
+
font-style: italic;
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
.state-badge.active .empty-state,
|
| 303 |
+
.state-badge.error .empty-state {
|
| 304 |
+
display: none;
|
| 305 |
+
}
|
| 306 |
+
|
| 307 |
+
/* Client state badge stays below */
|
| 308 |
+
.client .state-badge {
|
| 309 |
+
align-items: center;
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
.client .state-badge .caps-row {
|
| 313 |
+
justify-content: center;
|
| 314 |
+
}
|
| 315 |
+
|
| 316 |
+
/* Connection lines */
|
| 317 |
+
.connection {
|
| 318 |
+
position: absolute;
|
| 319 |
+
height: 2px;
|
| 320 |
+
background: #bdbdbd;
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
.conn-client-lb {
|
| 324 |
+
left: 140px;
|
| 325 |
+
width: 60px;
|
| 326 |
+
top: 140px;
|
| 327 |
+
}
|
| 328 |
+
|
| 329 |
+
.conn-lb-servers {
|
| 330 |
+
left: 290px;
|
| 331 |
+
width: 110px;
|
| 332 |
+
top: 140px;
|
| 333 |
+
}
|
| 334 |
+
|
| 335 |
+
/* The moving message */
|
| 336 |
+
.message {
|
| 337 |
+
position: absolute;
|
| 338 |
+
display: flex;
|
| 339 |
+
flex-direction: column;
|
| 340 |
+
align-items: center;
|
| 341 |
+
gap: 3px;
|
| 342 |
+
opacity: 0;
|
| 343 |
+
transition: opacity 0.2s ease;
|
| 344 |
+
pointer-events: none;
|
| 345 |
+
z-index: 100;
|
| 346 |
+
transform: translateX(-50%);
|
| 347 |
+
}
|
| 348 |
+
|
| 349 |
+
.message.visible {
|
| 350 |
+
opacity: 1;
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
.message-header {
|
| 354 |
+
display: flex;
|
| 355 |
+
align-items: center;
|
| 356 |
+
gap: 5px;
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
.message-arrow {
|
| 360 |
+
font-size: 24px;
|
| 361 |
+
font-weight: bold;
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
.message-type {
|
| 365 |
+
padding: 5px 10px;
|
| 366 |
+
border-radius: 6px;
|
| 367 |
+
font-size: 11px;
|
| 368 |
+
font-weight: 600;
|
| 369 |
+
white-space: nowrap;
|
| 370 |
+
box-shadow: 0 2px 10px rgba(0,0,0,0.15);
|
| 371 |
+
}
|
| 372 |
+
|
| 373 |
+
.message-detail {
|
| 374 |
+
padding: 3px 8px;
|
| 375 |
+
border-radius: 4px;
|
| 376 |
+
font-size: 9px;
|
| 377 |
+
font-family: 'SF Mono', Monaco, monospace;
|
| 378 |
+
background: rgba(255,255,255,0.95);
|
| 379 |
+
color: #666;
|
| 380 |
+
border: 1px solid #e0e0e0;
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
.message-session {
|
| 384 |
+
padding: 3px 8px;
|
| 385 |
+
border-radius: 4px;
|
| 386 |
+
font-size: 9px;
|
| 387 |
+
font-family: 'SF Mono', Monaco, monospace;
|
| 388 |
+
background: #e8f5e9;
|
| 389 |
+
color: #2e7d32;
|
| 390 |
+
border: 1px solid #a5d6a7;
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
.message-session.caps {
|
| 394 |
+
display: flex;
|
| 395 |
+
flex-direction: column;
|
| 396 |
+
gap: 3px;
|
| 397 |
+
align-items: flex-start;
|
| 398 |
+
}
|
| 399 |
+
|
| 400 |
+
.message-session .session-label {
|
| 401 |
+
font-size: 8px;
|
| 402 |
+
text-transform: uppercase;
|
| 403 |
+
letter-spacing: 0.4px;
|
| 404 |
+
color: #2e7d32;
|
| 405 |
+
}
|
| 406 |
+
|
| 407 |
+
.message-session .cap-row {
|
| 408 |
+
display: flex;
|
| 409 |
+
gap: 4px;
|
| 410 |
+
flex-wrap: wrap;
|
| 411 |
+
}
|
| 412 |
+
|
| 413 |
+
.message-session .cap-chip {
|
| 414 |
+
font-family: 'SF Mono', Monaco, monospace;
|
| 415 |
+
font-size: 8px;
|
| 416 |
+
background: white;
|
| 417 |
+
padding: 1px 5px;
|
| 418 |
+
border-radius: 3px;
|
| 419 |
+
border: 1px solid #c8e6c9;
|
| 420 |
+
color: #2e7d32;
|
| 421 |
+
}
|
| 422 |
+
|
| 423 |
+
.message-session.error {
|
| 424 |
+
background: #ffebee;
|
| 425 |
+
color: #c62828;
|
| 426 |
+
border-color: #ef9a9a;
|
| 427 |
+
}
|
| 428 |
+
|
| 429 |
+
.message.request .message-arrow { color: #1976d2; }
|
| 430 |
+
.message.request .message-type {
|
| 431 |
+
background: #1976d2;
|
| 432 |
+
color: white;
|
| 433 |
+
}
|
| 434 |
+
|
| 435 |
+
.message.response .message-arrow { color: #ef6c00; }
|
| 436 |
+
.message.response .message-type {
|
| 437 |
+
background: #ef6c00;
|
| 438 |
+
color: white;
|
| 439 |
+
}
|
| 440 |
+
|
| 441 |
+
.message.notification .message-arrow { color: #7b1fa2; }
|
| 442 |
+
.message.notification .message-type {
|
| 443 |
+
background: #7b1fa2;
|
| 444 |
+
color: white;
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
+
.message.server-request .message-arrow { color: #ef6c00; }
|
| 448 |
+
.message.server-request .message-type {
|
| 449 |
+
background: #ef6c00;
|
| 450 |
+
color: white;
|
| 451 |
+
}
|
| 452 |
+
|
| 453 |
+
.message.client-response .message-arrow { color: #1976d2; }
|
| 454 |
+
.message.client-response .message-type {
|
| 455 |
+
background: #1976d2;
|
| 456 |
+
color: white;
|
| 457 |
+
}
|
| 458 |
+
|
| 459 |
+
.message.error-response .message-arrow { color: #c62828; }
|
| 460 |
+
.message.error-response .message-type {
|
| 461 |
+
background: #c62828;
|
| 462 |
+
color: white;
|
| 463 |
+
}
|
| 464 |
+
|
| 465 |
+
/* Narration */
|
| 466 |
+
.narration {
|
| 467 |
+
margin-top: 25px;
|
| 468 |
+
padding: 14px 20px;
|
| 469 |
+
background: white;
|
| 470 |
+
border: 2px solid #e0e0e0;
|
| 471 |
+
border-radius: 12px;
|
| 472 |
+
font-size: 14px;
|
| 473 |
+
color: #333;
|
| 474 |
+
max-width: 700px;
|
| 475 |
+
text-align: center;
|
| 476 |
+
min-height: 52px;
|
| 477 |
+
display: flex;
|
| 478 |
+
align-items: center;
|
| 479 |
+
justify-content: center;
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
.narration .step-num {
|
| 483 |
+
display: inline-block;
|
| 484 |
+
width: 26px;
|
| 485 |
+
height: 26px;
|
| 486 |
+
background: #9e9e9e;
|
| 487 |
+
color: white;
|
| 488 |
+
border-radius: 50%;
|
| 489 |
+
font-size: 13px;
|
| 490 |
+
font-weight: 600;
|
| 491 |
+
line-height: 26px;
|
| 492 |
+
text-align: center;
|
| 493 |
+
margin-right: 12px;
|
| 494 |
+
flex-shrink: 0;
|
| 495 |
+
transition: background 0.3s ease;
|
| 496 |
+
}
|
| 497 |
+
|
| 498 |
+
.narration.request-state .step-num { background: #1976d2; }
|
| 499 |
+
.narration.response-state .step-num { background: #ef6c00; }
|
| 500 |
+
.narration.notification-state .step-num { background: #7b1fa2; }
|
| 501 |
+
.narration.waiting-state .step-num { background: #e65100; }
|
| 502 |
+
.narration.error-state .step-num { background: #c62828; }
|
| 503 |
+
.narration.error-state { border-color: #ef9a9a; background: #fff8f8; }
|
| 504 |
+
.narration.success-state .step-num { background: #4caf50; }
|
| 505 |
+
|
2026/mcp-connect/animations/shared/sequence-helpers.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
function runSequenceAnimation({
|
| 2 |
+
selectors,
|
| 3 |
+
cyclePause = 2000,
|
| 4 |
+
fadeOutOffset = 4000,
|
| 5 |
+
initialDelay = 500,
|
| 6 |
+
lastDelayOverride,
|
| 7 |
+
onShow,
|
| 8 |
+
scrollContainerSelector,
|
| 9 |
+
} = {}) {
|
| 10 |
+
const groups = (selectors || []).flatMap((selector) =>
|
| 11 |
+
Array.from(document.querySelectorAll(selector))
|
| 12 |
+
);
|
| 13 |
+
|
| 14 |
+
if (groups.length === 0) {
|
| 15 |
+
return;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
const delays = groups.map((el) => parseInt(el.dataset.delay, 10) || 0);
|
| 19 |
+
const maxDelay = Math.max(...delays, 0);
|
| 20 |
+
const finalDelay = Math.max(maxDelay, lastDelayOverride || 0);
|
| 21 |
+
const scrollContainer = scrollContainerSelector
|
| 22 |
+
? document.querySelector(scrollContainerSelector)
|
| 23 |
+
: null;
|
| 24 |
+
|
| 25 |
+
let timers = [];
|
| 26 |
+
|
| 27 |
+
function clearTimers() {
|
| 28 |
+
timers.forEach((id) => clearTimeout(id));
|
| 29 |
+
timers = [];
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
function schedule(fn, delay) {
|
| 33 |
+
const id = setTimeout(fn, delay);
|
| 34 |
+
timers.push(id);
|
| 35 |
+
return id;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
function runAnimation() {
|
| 39 |
+
clearTimers();
|
| 40 |
+
groups.forEach((el) => el.classList.remove('show', 'fade-out'));
|
| 41 |
+
if (scrollContainer) {
|
| 42 |
+
scrollContainer.scrollTop = 0;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
// Force reflow to ensure class removal is processed before re-adding
|
| 46 |
+
void document.body.offsetHeight;
|
| 47 |
+
|
| 48 |
+
groups.forEach((el) => {
|
| 49 |
+
const delay = parseInt(el.dataset.delay, 10) || 0;
|
| 50 |
+
schedule(() => {
|
| 51 |
+
el.classList.add('show');
|
| 52 |
+
if (onShow) {
|
| 53 |
+
onShow(el);
|
| 54 |
+
}
|
| 55 |
+
if (scrollContainer) {
|
| 56 |
+
scrollContainer.scrollTop = scrollContainer.scrollHeight;
|
| 57 |
+
}
|
| 58 |
+
}, delay);
|
| 59 |
+
});
|
| 60 |
+
|
| 61 |
+
const fadeOutTime = finalDelay + fadeOutOffset;
|
| 62 |
+
schedule(() => {
|
| 63 |
+
groups.forEach((el) => el.classList.add('fade-out'));
|
| 64 |
+
}, fadeOutTime);
|
| 65 |
+
|
| 66 |
+
schedule(runAnimation, fadeOutTime + 1000 + cyclePause);
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
function start() {
|
| 70 |
+
clearTimers();
|
| 71 |
+
schedule(runAnimation, initialDelay);
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
if (document.readyState === 'complete') {
|
| 75 |
+
start();
|
| 76 |
+
} else {
|
| 77 |
+
window.addEventListener('load', start, { once: true });
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
window.addEventListener('pageshow', () => {
|
| 81 |
+
start();
|
| 82 |
+
});
|
| 83 |
+
}
|
2026/mcp-connect/animations/stdio-simple.html
ADDED
|
@@ -0,0 +1,722 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<style>
|
| 6 |
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 7 |
+
body {
|
| 8 |
+
background: #fafafa;
|
| 9 |
+
display: flex;
|
| 10 |
+
align-items: center;
|
| 11 |
+
justify-content: center;
|
| 12 |
+
width: 100vw;
|
| 13 |
+
height: 100vh;
|
| 14 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 15 |
+
padding: 0;
|
| 16 |
+
overflow: hidden;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
.canvas-root {
|
| 20 |
+
display: inline-flex;
|
| 21 |
+
flex-direction: column;
|
| 22 |
+
align-items: center;
|
| 23 |
+
justify-content: center;
|
| 24 |
+
padding: 24px 20px;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
/* Process boundary container */
|
| 28 |
+
.process-boundary {
|
| 29 |
+
border: 2px dashed #9e9e9e;
|
| 30 |
+
border-radius: 20px;
|
| 31 |
+
padding: 30px 30px;
|
| 32 |
+
background: rgba(255, 255, 255, 0.5);
|
| 33 |
+
position: relative;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
.process-label {
|
| 37 |
+
position: absolute;
|
| 38 |
+
top: -14px;
|
| 39 |
+
left: 50%;
|
| 40 |
+
transform: translateX(-50%);
|
| 41 |
+
background: #fafafa;
|
| 42 |
+
padding: 4px 20px;
|
| 43 |
+
font-size: 16px;
|
| 44 |
+
font-weight: 600;
|
| 45 |
+
color: #616161;
|
| 46 |
+
white-space: nowrap;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
.diagram {
|
| 50 |
+
position: relative;
|
| 51 |
+
width: 500px;
|
| 52 |
+
height: 220px;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
/* Entity base styles */
|
| 56 |
+
.entity {
|
| 57 |
+
position: absolute;
|
| 58 |
+
display: flex;
|
| 59 |
+
flex-direction: column;
|
| 60 |
+
align-items: center;
|
| 61 |
+
gap: 10px;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
/* Client - starts grey, becomes blue */
|
| 65 |
+
.client { left: 0; top: 30px; }
|
| 66 |
+
.client .entity-box {
|
| 67 |
+
width: 130px;
|
| 68 |
+
min-height: 75px;
|
| 69 |
+
height: 80px;
|
| 70 |
+
border-radius: 16px;
|
| 71 |
+
background: #f5f5f5;
|
| 72 |
+
border: 3px solid #9e9e9e;
|
| 73 |
+
color: #9e9e9e;
|
| 74 |
+
display: flex;
|
| 75 |
+
flex-direction: column;
|
| 76 |
+
align-items: center;
|
| 77 |
+
justify-content: center;
|
| 78 |
+
font-weight: 600;
|
| 79 |
+
font-size: 14px;
|
| 80 |
+
transition: all 0.5s ease;
|
| 81 |
+
text-align: center;
|
| 82 |
+
line-height: 1.3;
|
| 83 |
+
gap: 4px;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
.client .entity-box.alive {
|
| 87 |
+
background: #e3f2fd;
|
| 88 |
+
border-color: #1976d2;
|
| 89 |
+
color: #1976d2;
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
.client .entity-box.active {
|
| 93 |
+
background: #bbdefb;
|
| 94 |
+
border-color: #1976d2;
|
| 95 |
+
color: #1976d2;
|
| 96 |
+
box-shadow: 0 4px 20px rgba(25, 118, 210, 0.3);
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
/* Server - starts grey, becomes orange */
|
| 100 |
+
.server { right: 0; top: 30px; }
|
| 101 |
+
.server .entity-box {
|
| 102 |
+
width: 130px;
|
| 103 |
+
min-height: 75px;
|
| 104 |
+
height: 80px;
|
| 105 |
+
border-radius: 16px;
|
| 106 |
+
background: #f5f5f5;
|
| 107 |
+
border: 3px solid #9e9e9e;
|
| 108 |
+
color: #9e9e9e;
|
| 109 |
+
display: flex;
|
| 110 |
+
flex-direction: column;
|
| 111 |
+
align-items: center;
|
| 112 |
+
justify-content: center;
|
| 113 |
+
font-weight: 600;
|
| 114 |
+
font-size: 14px;
|
| 115 |
+
transition: all 0.5s ease;
|
| 116 |
+
text-align: center;
|
| 117 |
+
line-height: 1.3;
|
| 118 |
+
gap: 4px;
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
.server .entity-box.alive {
|
| 122 |
+
background: #fff3e0;
|
| 123 |
+
border-color: #ef6c00;
|
| 124 |
+
color: #ef6c00;
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
.server .entity-box.active {
|
| 128 |
+
background: #ffe0b2;
|
| 129 |
+
border-color: #ef6c00;
|
| 130 |
+
color: #ef6c00;
|
| 131 |
+
box-shadow: 0 4px 20px rgba(239, 108, 0, 0.3);
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
.server .entity-box.waiting {
|
| 135 |
+
animation: waitingPulse 1s ease-in-out infinite;
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
@keyframes waitingPulse {
|
| 139 |
+
0%, 100% {
|
| 140 |
+
background: #fff3e0;
|
| 141 |
+
border-color: #ef6c00;
|
| 142 |
+
box-shadow: 0 4px 20px rgba(239, 108, 0, 0.2);
|
| 143 |
+
}
|
| 144 |
+
50% {
|
| 145 |
+
background: #ffe0b2;
|
| 146 |
+
border-color: #e65100;
|
| 147 |
+
box-shadow: 0 4px 30px rgba(239, 108, 0, 0.5);
|
| 148 |
+
}
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
.entity-status {
|
| 152 |
+
font-size: 10px;
|
| 153 |
+
font-weight: 600;
|
| 154 |
+
text-transform: uppercase;
|
| 155 |
+
letter-spacing: 0.5px;
|
| 156 |
+
opacity: 0;
|
| 157 |
+
transition: opacity 0.3s ease;
|
| 158 |
+
padding: 2px 8px;
|
| 159 |
+
border-radius: 4px;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
.entity-status.visible {
|
| 163 |
+
opacity: 1;
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
.entity-status.waiting {
|
| 167 |
+
background: #e65100;
|
| 168 |
+
color: white;
|
| 169 |
+
animation: statusBlink 0.8s ease-in-out infinite;
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
@keyframes statusBlink {
|
| 173 |
+
0%, 100% { opacity: 1; transform: scale(1); }
|
| 174 |
+
50% { opacity: 0.7; transform: scale(1.05); }
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
/* State indicator badge - shows capabilities learned */
|
| 178 |
+
.state-badge {
|
| 179 |
+
display: flex;
|
| 180 |
+
flex-direction: column;
|
| 181 |
+
align-items: center;
|
| 182 |
+
gap: 4px;
|
| 183 |
+
padding: 8px 12px;
|
| 184 |
+
border-radius: 10px;
|
| 185 |
+
font-size: 11px;
|
| 186 |
+
font-weight: 600;
|
| 187 |
+
transition: all 0.4s ease;
|
| 188 |
+
width: 130px;
|
| 189 |
+
min-height: 75px;
|
| 190 |
+
/* Default: gray/inactive state */
|
| 191 |
+
background: #f5f5f5;
|
| 192 |
+
border: 1px solid #e0e0e0;
|
| 193 |
+
color: #9e9e9e;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
.state-badge.active {
|
| 197 |
+
background: #e8f5e9;
|
| 198 |
+
border: 1px solid #a5d6a7;
|
| 199 |
+
color: #2e7d32;
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
.state-badge .badge-title {
|
| 203 |
+
font-size: 9px;
|
| 204 |
+
text-transform: uppercase;
|
| 205 |
+
letter-spacing: 0.5px;
|
| 206 |
+
color: #999;
|
| 207 |
+
font-weight: 500;
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
.state-badge.active .badge-title {
|
| 211 |
+
color: #666;
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
.state-badge .caps-list {
|
| 215 |
+
display: flex;
|
| 216 |
+
flex-wrap: wrap;
|
| 217 |
+
gap: 4px;
|
| 218 |
+
justify-content: center;
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
.state-badge .cap {
|
| 222 |
+
display: flex;
|
| 223 |
+
align-items: center;
|
| 224 |
+
gap: 3px;
|
| 225 |
+
font-family: 'SF Mono', Monaco, monospace;
|
| 226 |
+
font-size: 9px;
|
| 227 |
+
background: white;
|
| 228 |
+
padding: 2px 6px;
|
| 229 |
+
border-radius: 4px;
|
| 230 |
+
border: 1px solid #e0e0e0;
|
| 231 |
+
opacity: 0;
|
| 232 |
+
transition: opacity 0.3s ease;
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
.state-badge.active .cap {
|
| 236 |
+
border-color: #c8e6c9;
|
| 237 |
+
opacity: 1;
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
.state-badge .cap .check {
|
| 241 |
+
color: #4caf50;
|
| 242 |
+
font-weight: bold;
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
.state-badge .empty-state {
|
| 246 |
+
font-size: 9px;
|
| 247 |
+
color: #bdbdbd;
|
| 248 |
+
font-style: italic;
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
.state-badge.active .empty-state {
|
| 252 |
+
display: none;
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
/* Connection line */
|
| 256 |
+
.connection {
|
| 257 |
+
position: absolute;
|
| 258 |
+
left: 145px;
|
| 259 |
+
right: 145px;
|
| 260 |
+
top: 70px;
|
| 261 |
+
height: 2px;
|
| 262 |
+
background: #bdbdbd;
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
.connection-label {
|
| 266 |
+
position: absolute;
|
| 267 |
+
left: 50%;
|
| 268 |
+
top: 50%;
|
| 269 |
+
transform: translate(-50%, -50%);
|
| 270 |
+
background: rgba(255, 255, 255, 0.9);
|
| 271 |
+
padding: 4px 12px;
|
| 272 |
+
font-size: 11px;
|
| 273 |
+
color: #757575;
|
| 274 |
+
font-weight: 500;
|
| 275 |
+
white-space: nowrap;
|
| 276 |
+
border-radius: 4px;
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
/* The moving message - centered around arrow */
|
| 280 |
+
.message {
|
| 281 |
+
position: absolute;
|
| 282 |
+
display: flex;
|
| 283 |
+
flex-direction: column;
|
| 284 |
+
align-items: center;
|
| 285 |
+
gap: 4px;
|
| 286 |
+
opacity: 0;
|
| 287 |
+
transition: opacity 0.2s ease;
|
| 288 |
+
pointer-events: none;
|
| 289 |
+
z-index: 100;
|
| 290 |
+
transform: translateX(-50%);
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
.message.visible {
|
| 294 |
+
opacity: 1;
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
.message-header {
|
| 298 |
+
display: flex;
|
| 299 |
+
align-items: center;
|
| 300 |
+
gap: 6px;
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
.message-arrow {
|
| 304 |
+
font-size: 28px;
|
| 305 |
+
font-weight: bold;
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
.message-type {
|
| 309 |
+
padding: 6px 12px;
|
| 310 |
+
border-radius: 8px;
|
| 311 |
+
font-size: 12px;
|
| 312 |
+
font-weight: 600;
|
| 313 |
+
white-space: nowrap;
|
| 314 |
+
box-shadow: 0 3px 12px rgba(0,0,0,0.15);
|
| 315 |
+
}
|
| 316 |
+
|
| 317 |
+
.message-detail {
|
| 318 |
+
padding: 3px 8px;
|
| 319 |
+
border-radius: 4px;
|
| 320 |
+
font-size: 10px;
|
| 321 |
+
font-family: 'SF Mono', Monaco, monospace;
|
| 322 |
+
background: rgba(255,255,255,0.95);
|
| 323 |
+
color: #666;
|
| 324 |
+
border: 1px solid #e0e0e0;
|
| 325 |
+
}
|
| 326 |
+
|
| 327 |
+
.message.request .message-arrow { color: #1976d2; }
|
| 328 |
+
.message.request .message-type {
|
| 329 |
+
background: #1976d2;
|
| 330 |
+
color: white;
|
| 331 |
+
}
|
| 332 |
+
|
| 333 |
+
.message.response .message-arrow { color: #ef6c00; }
|
| 334 |
+
.message.response .message-type {
|
| 335 |
+
background: #ef6c00;
|
| 336 |
+
color: white;
|
| 337 |
+
}
|
| 338 |
+
|
| 339 |
+
.message.notification .message-arrow { color: #7b1fa2; }
|
| 340 |
+
.message.notification .message-type {
|
| 341 |
+
background: #7b1fa2;
|
| 342 |
+
color: white;
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
.message.server-request .message-arrow { color: #ef6c00; }
|
| 346 |
+
.message.server-request .message-type {
|
| 347 |
+
background: #ef6c00;
|
| 348 |
+
color: white;
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
.message.client-response .message-arrow { color: #1976d2; }
|
| 352 |
+
.message.client-response .message-type {
|
| 353 |
+
background: #1976d2;
|
| 354 |
+
color: white;
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
/* Narration */
|
| 358 |
+
.narration {
|
| 359 |
+
margin-top: 30px;
|
| 360 |
+
padding: 16px 24px;
|
| 361 |
+
background: white;
|
| 362 |
+
border: 2px solid #e0e0e0;
|
| 363 |
+
border-radius: 12px;
|
| 364 |
+
font-size: 15px;
|
| 365 |
+
color: #333;
|
| 366 |
+
max-width: 650px;
|
| 367 |
+
text-align: center;
|
| 368 |
+
min-height: 56px;
|
| 369 |
+
display: flex;
|
| 370 |
+
align-items: center;
|
| 371 |
+
justify-content: center;
|
| 372 |
+
}
|
| 373 |
+
|
| 374 |
+
.narration .step-num {
|
| 375 |
+
display: inline-block;
|
| 376 |
+
width: 28px;
|
| 377 |
+
height: 28px;
|
| 378 |
+
background: #9e9e9e;
|
| 379 |
+
color: white;
|
| 380 |
+
border-radius: 50%;
|
| 381 |
+
font-size: 14px;
|
| 382 |
+
font-weight: 600;
|
| 383 |
+
line-height: 28px;
|
| 384 |
+
text-align: center;
|
| 385 |
+
margin-right: 14px;
|
| 386 |
+
flex-shrink: 0;
|
| 387 |
+
transition: background 0.3s ease;
|
| 388 |
+
}
|
| 389 |
+
|
| 390 |
+
.narration.request-state .step-num { background: #1976d2; }
|
| 391 |
+
.narration.response-state .step-num { background: #ef6c00; }
|
| 392 |
+
.narration.notification-state .step-num { background: #7b1fa2; }
|
| 393 |
+
.narration.success-state .step-num { background: #4caf50; }
|
| 394 |
+
.narration.waiting-state .step-num { background: #e65100; }
|
| 395 |
+
</style>
|
| 396 |
+
</head>
|
| 397 |
+
<body>
|
| 398 |
+
<div class="canvas-root">
|
| 399 |
+
<div class="process-boundary">
|
| 400 |
+
<div class="process-label">STDIO (Same Process)</div>
|
| 401 |
+
|
| 402 |
+
<div class="diagram">
|
| 403 |
+
<!-- Client -->
|
| 404 |
+
<div class="entity client">
|
| 405 |
+
<div class="entity-box" id="client-box">
|
| 406 |
+
<span>MCP Client</span>
|
| 407 |
+
<span class="entity-status" id="client-status"></span>
|
| 408 |
+
</div>
|
| 409 |
+
<div class="state-badge" id="client-state">
|
| 410 |
+
<div class="badge-title">Connection State</div>
|
| 411 |
+
<div class="empty-state">not connected</div>
|
| 412 |
+
<div class="caps-list">
|
| 413 |
+
<span class="cap"><span class="check">✓</span> tools</span>
|
| 414 |
+
<span class="cap"><span class="check">✓</span> prompts</span>
|
| 415 |
+
</div>
|
| 416 |
+
</div>
|
| 417 |
+
</div>
|
| 418 |
+
|
| 419 |
+
<!-- Connection line -->
|
| 420 |
+
<div class="connection">
|
| 421 |
+
<div class="connection-label">stdin / stdout</div>
|
| 422 |
+
</div>
|
| 423 |
+
|
| 424 |
+
<!-- Server -->
|
| 425 |
+
<div class="entity server">
|
| 426 |
+
<div class="entity-box" id="server-box">
|
| 427 |
+
<span>MCP Server</span>
|
| 428 |
+
<span class="entity-status" id="server-status"></span>
|
| 429 |
+
</div>
|
| 430 |
+
<div class="state-badge" id="server-state">
|
| 431 |
+
<div class="badge-title">Connection State</div>
|
| 432 |
+
<div class="empty-state">not connected</div>
|
| 433 |
+
<div class="caps-list">
|
| 434 |
+
<span class="cap"><span class="check">✓</span> sampling</span>
|
| 435 |
+
</div>
|
| 436 |
+
</div>
|
| 437 |
+
</div>
|
| 438 |
+
|
| 439 |
+
<!-- Message -->
|
| 440 |
+
<div class="message" id="message">
|
| 441 |
+
<div class="message-header">
|
| 442 |
+
<span class="message-type" id="message-type">initialize</span>
|
| 443 |
+
<span class="message-arrow" id="message-arrow">→</span>
|
| 444 |
+
</div>
|
| 445 |
+
<div class="message-detail" id="message-detail">request</div>
|
| 446 |
+
</div>
|
| 447 |
+
</div>
|
| 448 |
+
</div>
|
| 449 |
+
|
| 450 |
+
<div class="narration" id="narration">
|
| 451 |
+
<span class="step-num" id="step-num">○</span>
|
| 452 |
+
<span id="narration-text">Client spawns server as subprocess...</span>
|
| 453 |
+
</div>
|
| 454 |
+
|
| 455 |
+
</div>
|
| 456 |
+
|
| 457 |
+
<script src="shared/animation-config.js"></script>
|
| 458 |
+
<script src="shared/diagram-helpers.js"></script>
|
| 459 |
+
<script src="shared/canvas-scale.js"></script>
|
| 460 |
+
<script>
|
| 461 |
+
const message = document.getElementById('message');
|
| 462 |
+
const messageType = document.getElementById('message-type');
|
| 463 |
+
const messageArrow = document.getElementById('message-arrow');
|
| 464 |
+
const messageDetail = document.getElementById('message-detail');
|
| 465 |
+
const narration = document.getElementById('narration');
|
| 466 |
+
const narrationText = document.getElementById('narration-text');
|
| 467 |
+
const stepNum = document.getElementById('step-num');
|
| 468 |
+
const clientBox = document.getElementById('client-box');
|
| 469 |
+
const serverBox = document.getElementById('server-box');
|
| 470 |
+
const clientState = document.getElementById('client-state');
|
| 471 |
+
const serverState = document.getElementById('server-state');
|
| 472 |
+
const clientStatus = document.getElementById('client-status');
|
| 473 |
+
const serverStatus = document.getElementById('server-status');
|
| 474 |
+
|
| 475 |
+
|
| 476 |
+
const messageElements = {
|
| 477 |
+
container: message,
|
| 478 |
+
header: message.querySelector('.message-header'),
|
| 479 |
+
type: messageType,
|
| 480 |
+
arrow: messageArrow,
|
| 481 |
+
detail: messageDetail,
|
| 482 |
+
};
|
| 483 |
+
|
| 484 |
+
const clientCenter = 65;
|
| 485 |
+
const serverCenter = 435;
|
| 486 |
+
const msgY = 45;
|
| 487 |
+
|
| 488 |
+
|
| 489 |
+
function setMessagePos(x, y) {
|
| 490 |
+
setMessagePosition(message, x, y);
|
| 491 |
+
}
|
| 492 |
+
|
| 493 |
+
function animateMessage(fromX, fromY, toX, toY, duration, callback) {
|
| 494 |
+
animateMessageBetween(message, fromX, fromY, toX, toY, duration, callback);
|
| 495 |
+
}
|
| 496 |
+
|
| 497 |
+
function configMessage(type, arrow, method, detail, direction) {
|
| 498 |
+
configureMessage(messageElements, {
|
| 499 |
+
type,
|
| 500 |
+
arrow,
|
| 501 |
+
method,
|
| 502 |
+
detail,
|
| 503 |
+
direction,
|
| 504 |
+
});
|
| 505 |
+
}
|
| 506 |
+
|
| 507 |
+
function setStatus(element, text, className) {
|
| 508 |
+
element.textContent = text;
|
| 509 |
+
element.className = 'entity-status' + (text ? ' visible' : '') + (className ? ' ' + className : '');
|
| 510 |
+
}
|
| 511 |
+
|
| 512 |
+
const steps = [
|
| 513 |
+
// Step 0: Initial grey state
|
| 514 |
+
{
|
| 515 |
+
setup: () => {
|
| 516 |
+
message.className = 'message';
|
| 517 |
+
stepNum.textContent = '○';
|
| 518 |
+
narrationText.textContent = 'Client spawns server as subprocess...';
|
| 519 |
+
narration.className = 'narration';
|
| 520 |
+
clientBox.className = 'entity-box';
|
| 521 |
+
serverBox.className = 'entity-box';
|
| 522 |
+
clientState.classList.remove('active');
|
| 523 |
+
serverState.classList.remove('active');
|
| 524 |
+
setStatus(clientStatus, '', '');
|
| 525 |
+
setStatus(serverStatus, '', '');
|
| 526 |
+
},
|
| 527 |
+
animate: (done) => setTimeout(done, ANIMATION.INITIAL_PAUSE),
|
| 528 |
+
after: () => {}
|
| 529 |
+
},
|
| 530 |
+
// Step 1: Initialize request Client → Server
|
| 531 |
+
{
|
| 532 |
+
setup: () => {
|
| 533 |
+
configMessage('request', '→', 'initialize', 'InitializeRequest', 'right');
|
| 534 |
+
setMessagePos(clientCenter, msgY);
|
| 535 |
+
stepNum.textContent = '1';
|
| 536 |
+
narrationText.textContent = 'Client sends InitializeRequest with capabilities';
|
| 537 |
+
narration.className = 'narration request-state';
|
| 538 |
+
clientBox.classList.add('alive', 'active');
|
| 539 |
+
},
|
| 540 |
+
animate: (done) => animateMessage(clientCenter, msgY, serverCenter, msgY, ANIMATION.MSG_DURATION, done),
|
| 541 |
+
after: () => {
|
| 542 |
+
clientBox.classList.remove('active');
|
| 543 |
+
serverBox.classList.add('alive', 'active');
|
| 544 |
+
}
|
| 545 |
+
},
|
| 546 |
+
// Step 2: Initialize result Server → Client
|
| 547 |
+
{
|
| 548 |
+
setup: () => {
|
| 549 |
+
configMessage('response', '←', 'initialize', 'InitializeResult', 'left');
|
| 550 |
+
setMessagePos(serverCenter, msgY);
|
| 551 |
+
stepNum.textContent = '2';
|
| 552 |
+
narrationText.textContent = 'Server responds with InitializeResult';
|
| 553 |
+
narration.className = 'narration response-state';
|
| 554 |
+
},
|
| 555 |
+
animate: (done) => animateMessage(serverCenter, msgY, clientCenter, msgY, ANIMATION.MSG_DURATION, done),
|
| 556 |
+
after: () => {
|
| 557 |
+
serverBox.classList.remove('active');
|
| 558 |
+
clientBox.classList.add('active');
|
| 559 |
+
}
|
| 560 |
+
},
|
| 561 |
+
// Step 3: notifications/initialized Client → Server
|
| 562 |
+
{
|
| 563 |
+
setup: () => {
|
| 564 |
+
clientBox.classList.remove('active');
|
| 565 |
+
configMessage('notification', '→', 'initialized', 'notification', 'right');
|
| 566 |
+
setMessagePos(clientCenter, msgY);
|
| 567 |
+
stepNum.textContent = '3';
|
| 568 |
+
narrationText.textContent = 'Client sends initialized notification';
|
| 569 |
+
narration.className = 'narration notification-state';
|
| 570 |
+
clientBox.classList.add('active');
|
| 571 |
+
},
|
| 572 |
+
animate: (done) => animateMessage(clientCenter, msgY, serverCenter, msgY, ANIMATION.MSG_DURATION, done),
|
| 573 |
+
after: () => {
|
| 574 |
+
clientBox.classList.remove('active');
|
| 575 |
+
clientState.classList.add('active');
|
| 576 |
+
serverState.classList.add('active');
|
| 577 |
+
}
|
| 578 |
+
},
|
| 579 |
+
// Step 4: tools/list request
|
| 580 |
+
{
|
| 581 |
+
setup: () => {
|
| 582 |
+
configMessage('request', '→', 'tools/list', 'ListToolsRequest', 'right');
|
| 583 |
+
setMessagePos(clientCenter, msgY);
|
| 584 |
+
stepNum.textContent = '4';
|
| 585 |
+
narrationText.textContent = 'Client requests tool list';
|
| 586 |
+
narration.className = 'narration request-state';
|
| 587 |
+
clientBox.classList.add('active');
|
| 588 |
+
},
|
| 589 |
+
animate: (done) => animateMessage(clientCenter, msgY, serverCenter, msgY, ANIMATION.MSG_DURATION, done),
|
| 590 |
+
after: () => {
|
| 591 |
+
clientBox.classList.remove('active');
|
| 592 |
+
serverBox.classList.add('active');
|
| 593 |
+
}
|
| 594 |
+
},
|
| 595 |
+
// Step 5: tools/list response
|
| 596 |
+
{
|
| 597 |
+
setup: () => {
|
| 598 |
+
configMessage('response', '←', 'tools/list', 'ListToolsResult', 'left');
|
| 599 |
+
setMessagePos(serverCenter, msgY);
|
| 600 |
+
stepNum.textContent = '5';
|
| 601 |
+
narrationText.textContent = 'Server returns ListToolsResult';
|
| 602 |
+
narration.className = 'narration response-state';
|
| 603 |
+
},
|
| 604 |
+
animate: (done) => animateMessage(serverCenter, msgY, clientCenter, msgY, ANIMATION.MSG_DURATION, done),
|
| 605 |
+
after: () => {
|
| 606 |
+
serverBox.classList.remove('active');
|
| 607 |
+
}
|
| 608 |
+
},
|
| 609 |
+
// Step 6: prompts/list request
|
| 610 |
+
{
|
| 611 |
+
setup: () => {
|
| 612 |
+
configMessage('request', '→', 'prompts/list', 'ListPromptsRequest', 'right');
|
| 613 |
+
setMessagePos(clientCenter, msgY);
|
| 614 |
+
stepNum.textContent = '6';
|
| 615 |
+
narrationText.textContent = 'Client requests prompt list';
|
| 616 |
+
narration.className = 'narration request-state';
|
| 617 |
+
clientBox.classList.add('active');
|
| 618 |
+
},
|
| 619 |
+
animate: (done) => animateMessage(clientCenter, msgY, serverCenter, msgY, ANIMATION.MSG_DURATION, done),
|
| 620 |
+
after: () => {
|
| 621 |
+
clientBox.classList.remove('active');
|
| 622 |
+
serverBox.classList.add('active');
|
| 623 |
+
}
|
| 624 |
+
},
|
| 625 |
+
// Step 7: prompts/list response
|
| 626 |
+
{
|
| 627 |
+
setup: () => {
|
| 628 |
+
configMessage('response', '←', 'prompts/list', 'ListPromptsResult', 'left');
|
| 629 |
+
setMessagePos(serverCenter, msgY);
|
| 630 |
+
stepNum.textContent = '7';
|
| 631 |
+
narrationText.textContent = 'Server returns ListPromptsResult';
|
| 632 |
+
narration.className = 'narration response-state';
|
| 633 |
+
},
|
| 634 |
+
animate: (done) => animateMessage(serverCenter, msgY, clientCenter, msgY, ANIMATION.MSG_DURATION, done),
|
| 635 |
+
after: () => {
|
| 636 |
+
serverBox.classList.remove('active');
|
| 637 |
+
}
|
| 638 |
+
},
|
| 639 |
+
// Step 8: tools/call request
|
| 640 |
+
{
|
| 641 |
+
setup: () => {
|
| 642 |
+
configMessage('request', '→', 'tools/call', 'CallToolRequest', 'right');
|
| 643 |
+
setMessagePos(clientCenter, msgY);
|
| 644 |
+
stepNum.textContent = '8';
|
| 645 |
+
narrationText.textContent = 'Client calls a tool';
|
| 646 |
+
narration.className = 'narration request-state';
|
| 647 |
+
clientBox.classList.add('active');
|
| 648 |
+
},
|
| 649 |
+
animate: (done) => animateMessage(clientCenter, msgY, serverCenter, msgY, ANIMATION.MSG_DURATION, done),
|
| 650 |
+
after: () => {
|
| 651 |
+
clientBox.classList.remove('active');
|
| 652 |
+
serverBox.classList.add('active');
|
| 653 |
+
setStatus(serverStatus, 'processing', '');
|
| 654 |
+
}
|
| 655 |
+
},
|
| 656 |
+
// Step 9: Server sends sampling request BACK to client (server waits)
|
| 657 |
+
{
|
| 658 |
+
setup: () => {
|
| 659 |
+
configMessage('server-request', '←', 'sampling/createMessage', 'CreateMessageRequest', 'left');
|
| 660 |
+
setMessagePos(serverCenter, msgY);
|
| 661 |
+
stepNum.textContent = '9';
|
| 662 |
+
narrationText.textContent = 'Server needs LLM — sends sampling request to client';
|
| 663 |
+
narration.className = 'narration waiting-state';
|
| 664 |
+
serverBox.classList.remove('active');
|
| 665 |
+
serverBox.classList.add('waiting');
|
| 666 |
+
setStatus(serverStatus, 'WAITING', 'waiting');
|
| 667 |
+
},
|
| 668 |
+
animate: (done) => animateMessage(serverCenter, msgY, clientCenter, msgY, ANIMATION.MSG_DURATION, done),
|
| 669 |
+
after: () => {
|
| 670 |
+
clientBox.classList.add('active');
|
| 671 |
+
}
|
| 672 |
+
},
|
| 673 |
+
// Step 10: Client responds with sampling result
|
| 674 |
+
{
|
| 675 |
+
setup: () => {
|
| 676 |
+
configMessage('client-response', '→', 'sampling/createMessage', 'CreateMessageResult', 'right');
|
| 677 |
+
setMessagePos(clientCenter, msgY);
|
| 678 |
+
stepNum.textContent = '10';
|
| 679 |
+
narrationText.textContent = 'Client returns LLM response — server resumes';
|
| 680 |
+
narration.className = 'narration response-state';
|
| 681 |
+
},
|
| 682 |
+
animate: (done) => animateMessage(clientCenter, msgY, serverCenter, msgY, ANIMATION.MSG_DURATION, done),
|
| 683 |
+
after: () => {
|
| 684 |
+
clientBox.classList.remove('active');
|
| 685 |
+
serverBox.classList.remove('waiting');
|
| 686 |
+
serverBox.classList.add('active');
|
| 687 |
+
setStatus(serverStatus, 'processing', '');
|
| 688 |
+
}
|
| 689 |
+
},
|
| 690 |
+
// Step 11: tools/call response
|
| 691 |
+
{
|
| 692 |
+
setup: () => {
|
| 693 |
+
configMessage('response', '←', 'tools/call', 'CallToolResult', 'left');
|
| 694 |
+
setMessagePos(serverCenter, msgY);
|
| 695 |
+
stepNum.textContent = '11';
|
| 696 |
+
narrationText.textContent = 'Server completes tool call';
|
| 697 |
+
narration.className = 'narration response-state';
|
| 698 |
+
},
|
| 699 |
+
animate: (done) => animateMessage(serverCenter, msgY, clientCenter, msgY, ANIMATION.MSG_DURATION, done),
|
| 700 |
+
after: () => {
|
| 701 |
+
serverBox.classList.remove('active');
|
| 702 |
+
setStatus(serverStatus, '', '');
|
| 703 |
+
}
|
| 704 |
+
},
|
| 705 |
+
// Step 12: Pause before restart
|
| 706 |
+
{
|
| 707 |
+
setup: () => {
|
| 708 |
+
message.className = 'message';
|
| 709 |
+
stepNum.textContent = '✓';
|
| 710 |
+
narrationText.textContent = 'Connection complete — both sides know capabilities';
|
| 711 |
+
narration.className = 'narration success-state';
|
| 712 |
+
},
|
| 713 |
+
animate: (done) => setTimeout(done, ANIMATION.ERROR_PAUSE),
|
| 714 |
+
after: () => {}
|
| 715 |
+
}
|
| 716 |
+
];
|
| 717 |
+
|
| 718 |
+
runStepSequence(steps);
|
| 719 |
+
scaleCanvas();
|
| 720 |
+
</script>
|
| 721 |
+
</body>
|
| 722 |
+
</html>
|
2026/mcp-connect/images/2026-02-03-mcp-server-stats.png
ADDED
|
Git LFS Details
|
2026/mcp-connect/images/2026-02-03-mcpremote1.png
ADDED
|
Git LFS Details
|
2026/mcp-connect/images/2026-02-03-mcpremote2.png
ADDED
|
Git LFS Details
|
2026/mcp-connect/images/2026-02-04-client-dataset.png
ADDED
|
Git LFS Details
|
2026/mcp-connect/images/2026-02-04-efficient.png
ADDED
|
2026/mcp-connect/images/github-mark.svg
ADDED
|
|
2026/mcp-connect/images/hf_logo.svg
ADDED
|
|
2026/mcp-connect/images/huggingface-mark-logo.svg
ADDED
|
|
2026/mcp-connect/images/intro-spaces.webm
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f0e4d819a50bd539e7dda538f1cd05ba1c33dfa565552986c96feadd8fce93c5
|
| 3 |
+
size 14977657
|
2026/mcp-connect/images/mcp-icon.svg
ADDED
|
|
2026/mcp-connect/images/xcom-logo-black.png
ADDED
|
Git LFS Details
|
2026/mcp-connect/presentation.html
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
index.html
CHANGED
|
@@ -155,6 +155,22 @@
|
|
| 155 |
<main>
|
| 156 |
<h1>Conference Presentations</h1>
|
| 157 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
<section>
|
| 159 |
<h2>2025</h2>
|
| 160 |
<ul class="conference-list">
|
|
|
|
| 155 |
<main>
|
| 156 |
<h1>Conference Presentations</h1>
|
| 157 |
|
| 158 |
+
<section>
|
| 159 |
+
<h2>2026</h2>
|
| 160 |
+
<ul class="conference-list">
|
| 161 |
+
<li class="conference">
|
| 162 |
+
<p class="conference-title">MCP Connect (February) <span class="tag">Feb 2026</span></p>
|
| 163 |
+
<div class="links">
|
| 164 |
+
<a href="https://lu.ma/mcpconnect" aria-label="Conference Site: MCP Connect">MCP Connect</a>
|
| 165 |
+
<a href="2026/mcp-connect/presentation.html">View presentation</a>
|
| 166 |
+
</div>
|
| 167 |
+
<div class="extras">
|
| 168 |
+
<p class="note">Talk covering MCP transport choices and trade-offs in Streamable HTTP, sessions and transport relationships, and analytics insights from the <a href="https://github.com/evalstate/hf-mcp-server">Hugging Face MCP Server</a> showing real-world client behavior at scale. Includes detailed experience report and data on session lengths, initialize:tool call ratios, and client patterns in production.</p>
|
| 169 |
+
</div>
|
| 170 |
+
</li>
|
| 171 |
+
</ul>
|
| 172 |
+
</section>
|
| 173 |
+
|
| 174 |
<section>
|
| 175 |
<h2>2025</h2>
|
| 176 |
<ul class="conference-list">
|