<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MCP: An Interactive Visual Guide</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
background-color: #f8fafc; /* slate-50 */
}
.diagram-card {
background-color: white;
border-radius: 0.75rem;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
transition: all 0.3s ease-in-out;
overflow: hidden;
}
.flow-arrow {
position: relative;
padding: 0 1rem;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
color: #4b5563; /* gray-600 */
}
.flow-arrow::after {
content: '→';
font-size: 2rem;
line-height: 1;
animation: pulse-arrow 2s infinite;
}
.flow-arrow.vertical::after {
content: '↓';
}
@keyframes pulse-arrow {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.1); opacity: 0.7; }
}
.component {
border: 2px solid #e5e7eb; /* gray-200 */
border-radius: 0.5rem;
padding: 1rem;
text-align: center;
background-color: #f9fafb; /* gray-50 */
transition: all 0.2s ease-in-out;
}
.component:hover {
border-color: #3b82f6; /* blue-500 */
transform: translateY(-2px);
}
.component-title {
font-weight: 600;
color: #1f2937; /* gray-800 */
}
.component-desc {
font-size: 0.875rem;
color: #6b7280; /* gray-500 */
}
.code-block {
background-color: #1f2937; /* gray-800 */
color: #d1d5db; /* gray-300 */
padding: 0.75rem;
border-radius: 0.5rem;
font-family: monospace;
font-size: 0.8rem;
white-space: pre-wrap;
margin-top: 0.5rem;
}
.tab-button {
padding: 0.5rem 1rem;
border-radius: 0.5rem;
cursor: pointer;
font-weight: 500;
transition: all 0.2s ease-in-out;
border: 2px solid transparent;
}
.tab-button.active {
background-color: #e0e7ff; /* indigo-100 */
color: #3730a3; /* indigo-800 */
}
.tab-button:not(.active) {
background-color: #f3f4f6; /* gray-100 */
color: #4b5563; /* gray-600 */
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.hexagon {
position: relative;
width: 150px;
height: 86.60px;
background-color: #3b82f6; /* blue-500 */
margin: 43.30px auto;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 600;
text-align: center;
padding: 0 10px;
}
.hexagon:before,
.hexagon:after {
content: "";
position: absolute;
width: 0;
border-left: 75px solid transparent;
border-right: 75px solid transparent;
}
.hexagon:before {
bottom: 100%;
border-bottom: 43.30px solid #3b82f6;
}
.hexagon:after {
top: 100%;
width: 0;
border-top: 43.30px solid #3b82f6;
}
</style>
</head>
<body class="p-4 sm:p-6 md:p-8">
<div class="max-w-7xl mx-auto">
<header class="text-center mb-12">
<h1 class="text-4xl font-bold text-gray-800">Model Context Protocol</h1>
<p class="text-xl text-gray-600 mt-2">An Interactive Visual Guide</p>
</header>
<div class="space-y-12">
<!-- Section 1: Core Architecture -->
<div id="core-architecture" class="diagram-card">
<h2 class="text-2xl font-semibold text-gray-700 p-6 border-b">1. Core Architecture: Host, Client, Server</h2>
<div class="p-6 md:p-8">
<div class="flex flex-col md:flex-row items-center justify-center gap-4">
<!-- Host -->
<div class="component w-full md:w-1/3">
<div class="component-title">Host (e.g., Claude, VS Code)</div>
<div class="component-desc">The user-facing AI application. Manages security, permissions, and orchestrates connections.</div>
</div>
<div class="flow-arrow text-2xl font-bold text-gray-500"></div>
<!-- Client -->
<div class="component w-full md:w-1/3">
<div class="component-title">Client (Managed by Host)</div>
<div class="component-desc">An isolated instance managing a 1:1 stateful connection with a single server.</div>
</div>
<div class="flow-arrow text-2xl font-bold text-gray-500"></div>
<!-- Server -->
<div class="component w-full md:w-1/3">
<div class="component-title">Server (e.g., Filesystem, GitHub)</div>
<div class="component-desc">A lightweight program providing specific capabilities (tools, prompts, resources).</div>
</div>
</div>
<div class="mt-6 text-center text-gray-600 italic">
This 1:1 Client-Server relationship is key to MCP's security model, isolating servers from each other.
</div>
</div>
</div>
<!-- Section 2: Protocol Primitives -->
<div id="primitives" class="diagram-card">
<h2 class="text-2xl font-semibold text-gray-700 p-6 border-b">2. Protocol Primitives: A Layered Control Model</h2>
<div class="p-6 md:p-8">
<div id="primitive-tabs" class="flex flex-wrap gap-2 mb-6 justify-center">
<!-- Tab buttons will be injected here by JS -->
</div>
<div id="primitive-content">
<!-- Content will be injected here by JS -->
</div>
</div>
</div>
<!-- Section 3: Connection Lifecycle -->
<div id="lifecycle" class="diagram-card">
<h2 class="text-2xl font-semibold text-gray-700 p-6 border-b">3. Connection Lifecycle</h2>
<div class="p-6 md:p-8">
<div class="flex flex-col space-y-4">
<div class="flex items-center">
<div class="w-1/3 text-center font-semibold">Client</div>
<div class="w-2/3"></div>
</div>
<!-- Initialize -->
<div class="flex items-center">
<div class="w-1/3"></div>
<div class="w-2/3 relative pl-4 border-l-2 border-blue-500">
<div class="absolute -left-[11px] top-1/2 -translate-y-1/2 w-5 h-5 bg-blue-500 rounded-full border-4 border-white"></div>
<div class="font-mono text-sm bg-blue-50 p-2 rounded">initialize →</div>
<div class="text-xs text-gray-500 mt-1">Client sends capabilities and protocol version.</div>
</div>
</div>
<!-- Initialize Response -->
<div class="flex items-center">
<div class="w-2/3 relative pr-4 border-r-2 border-green-500 text-right">
<div class="absolute -right-[11px] top-1/2 -translate-y-1/2 w-5 h-5 bg-green-500 rounded-full border-4 border-white"></div>
<div class="font-mono text-sm bg-green-50 p-2 rounded inline-block">← result</div>
<div class="text-xs text-gray-500 mt-1">Server responds with its capabilities.</div>
</div>
<div class="w-1/3"></div>
</div>
<!-- Initialized -->
<div class="flex items-center">
<div class="w-1/3"></div>
<div class="w-2/3 relative pl-4 border-l-2 border-blue-500">
<div class="absolute -left-[11px] top-1/2 -translate-y-1/2 w-5 h-5 bg-blue-500 rounded-full border-4 border-white"></div>
<div class="font-mono text-sm bg-blue-50 p-2 rounded">initialized (notification) →</div>
<div class="text-xs text-gray-500 mt-1">Client confirms initialization. Connection is now operational.</div>
</div>
</div>
<div class="text-center py-4 text-gray-500 font-semibold">... Operational Phase (requests/responses) ...</div>
<!-- Shutdown -->
<div class="flex items-center">
<div class="w-1/3"></div>
<div class="w-2/3 relative pl-4 border-l-2 border-red-500">
<div class="absolute -left-[11px] top-1/2 -translate-y-1/2 w-5 h-5 bg-red-500 rounded-full border-4 border-white"></div>
<div class="font-mono text-sm bg-red-50 p-2 rounded">shutdown →</div>
<div class="text-xs text-gray-500 mt-1">Client initiates graceful shutdown.</div>
</div>
</div>
<!-- Exit -->
<div class="flex items-center">
<div class="w-1/3"></div>
<div class="w-2/3 relative pl-4 border-l-2 border-red-500">
<div class="absolute -left-[11px] top-1/2 -translate-y-1/2 w-5 h-5 bg-red-500 rounded-full border-4 border-white"></div>
<div class="font-mono text-sm bg-red-50 p-2 rounded">exit (notification) →</div>
<div class="text-xs text-gray-500 mt-1">Client terminates connection.</div>
</div>
</div>
<div class="flex items-center">
<div class="w-1/3 text-center font-semibold"></div>
<div class="w-1/3 text-center font-semibold">Server</div>
<div class="w-1/3 text-center font-semibold"></div>
</div>
</div>
</div>
</div>
<!-- Section 4: Hexagonal Architecture -->
<div id="hexagonal-architecture" class="diagram-card">
<h2 class="text-2xl font-semibold text-gray-700 p-6 border-b">4. Hexagonal Architecture in MCP Servers</h2>
<div class="p-6 md:p-8">
<p class="text-center text-gray-600 mb-8">Decouples core logic from external concerns (like transport or data storage), making the server extensible and testable. Inspired by <code class="text-sm bg-gray-200 p-1 rounded">@sparesparrow/mcp-prompts</code>.</p>
<div class="flex flex-col md:flex-row items-center justify-around gap-8">
<!-- Primary Adapters -->
<div class="w-full md:w-1/4 space-y-4">
<div class="component">
<div class="component-title">MCP Adapter</div>
<div class="component-desc">(stdio, SSE)</div>
</div>
<div class="component">
<div class="component-title">REST API Adapter</div>
<div class="component-desc">(e.g., Express.js)</div>
</div>
</div>
<div class="flow-arrow text-2xl font-bold text-gray-500 hidden md:flex"></div>
<!-- Core Logic -->
<div class="w-full md:w-1/2 relative">
<div class="hexagon">
Core Application Logic (Domain)
</div>
<div class="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-full bg-white px-2 font-semibold text-indigo-600">Primary Ports (Driving)</div>
<div class="absolute bottom-0 left-1/2 -translate-x-1/2 translate-y-full bg-white px-2 font-semibold text-purple-600">Secondary Ports (Driven)</div>
</div>
<div class="flow-arrow text-2xl font-bold text-gray-500 hidden md:flex"></div>
<!-- Secondary Adapters -->
<div class="w-full md:w-1/4 space-y-4">
<div class="component">
<div class="component-title">File Storage Adapter</div>
<div class="component-desc">(JSON files)</div>
</div>
<div class="component">
<div class="component-title">Postgres Adapter</div>
<div class="component-desc">(Database)</div>
</div>
<div class="component">
<div class="component-title">In-Memory Adapter</div>
<div class="component-desc">(for testing)</div>
</div>
</div>
</div>
</div>
</div>
<!-- Section 5: Proxy/Router Architecture -->
<div id="proxy-architecture" class="diagram-card">
<h2 class="text-2xl font-semibold text-gray-700 p-6 border-b">5. Proxy / Router Architecture</h2>
<div class="p-6 md:p-8">
<p class="text-center text-gray-600 mb-8">Aggregates multiple specialized servers into a single endpoint for the client, simplifying connection management.</p>
<div class="flex flex-col md:flex-row items-center justify-center gap-4">
<!-- Client -->
<div class="component w-full md:w-1/4">
<div class="component-title">MCP Client</div>
<div class="component-desc">(e.g. Claude Desktop)</div>
</div>
<div class="flow-arrow text-2xl font-bold text-gray-500"></div>
<!-- Proxy -->
<div class="component w-full md:w-1/3 bg-indigo-50 border-indigo-200">
<div class="component-title text-indigo-800">MCP Proxy / Router</div>
<div class="component-desc text-indigo-600">Handles aggregation, auth, logging, and routing.</div>
</div>
<div class="flow-arrow text-2xl font-bold text-gray-500"></div>
<!-- Backend Servers -->
<div class="w-full md:w-1/3 space-y-3">
<div class="component text-sm p-3">
<div class="component-title text-base">GitHub Server</div>
</div>
<div class="component text-sm p-3">
<div class="component-title text-base">Filesystem Server</div>
</div>
<div class="component text-sm p-3">
<div class="component-title text-base">Slack Server</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// --- Primitives Data ---
const primitives = [
{
id: 'prompts',
name: 'Prompts',
color: 'blue',
tagline: 'User-Controlled: Initiated by the user (e.g., via slash command).',
description: 'Pre-defined templates or workflows exposed by the server. They provide a safe and discoverable way for users to guide the LLM.',
example: `
// Client -> Server
{
"jsonrpc": "2.0",
"method": "prompts/get",
"params": { "name": "git-commit" },
"id": 1
}
// Server -> Client
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"messages": [
{ "role": "user", "content": { "type": "text", "text": "Write a git commit message for the following diff..." } }
]
}
}`
},
{
id: 'tools',
name: 'Tools',
color: 'green',
tagline: 'Model-Controlled: Initiated by the LLM to perform actions.',
description: 'Executable functions that allow the model to have side effects, like calling an API or writing a file. The user must approve the action.',
example: `
// Client -> Server
{
"jsonrpc": "2.0",
"method": "tools/execute",
"params": {
"name": "filesystem/writeFile",
"args": { "path": "./hello.txt", "content": "Hello MCP!" }
},
"id": 2
}
// Server -> Client
{
"jsonrpc": "2.0",
"id": 2,
"result": { "content": "Successfully wrote to ./hello.txt" }
}`
},
{
id: 'resources',
name: 'Resources',
color: 'purple',
tagline: 'Application-Controlled: Managed by the Host application.',
description: 'Structured, read-only data provided by the server to enrich the model\'s context. The Host decides how and when to use them.',
example: `
// Client -> Server
{
"jsonrpc": "2.0",
"method": "resources/list",
"params": { "uri": "file://." },
"id": 3
}
// Server -> Client
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"resources": [
{ "uri": "file://./src/index.js", "name": "index.js" }
]
}
}`
},
{
id: 'sampling',
name: 'Sampling',
color: 'red',
tagline: 'Server-Requested: Server asks the LLM to perform reasoning.',
description: 'An advanced primitive where a server can delegate a sub-task back to the client\'s LLM. Requires explicit user approval.',
example: `
// Server -> Client
{
"jsonrpc": "2.0",
"method": "sampling/sample",
"params": {
"prompt": "Based on this error log, what is the likely root cause?"
},
"id": 4
}
// Client -> Server
{
"jsonrpc": "2.0",
"id": 4,
"result": { "completion": "The root cause appears to be a null pointer exception..." }
}`
}
];
// --- Primitives Tab Logic ---
const tabsContainer = document.getElementById('primitive-tabs');
const contentContainer = document.getElementById('primitive-content');
primitives.forEach((p, index) => {
// Create button
const button = document.createElement('button');
button.className = `tab-button border-${p.color}-500`;
button.dataset.target = `primitive-${p.id}`;
button.textContent = p.name;
if (index === 0) {
button.classList.add('active');
}
tabsContainer.appendChild(button);
// Create content
const contentDiv = document.createElement('div');
contentDiv.id = `primitive-${p.id}`;
contentDiv.className = 'tab-content p-4 rounded-lg border-2';
contentDiv.classList.add(`border-${p.color}-200`, `bg-${p.color}-50`);
if (index === 0) {
contentDiv.classList.add('active');
}
contentDiv.innerHTML = `
<h3 class="font-semibold text-lg text-${p.color}-800">${p.name}</h3>
<p class="italic text-${p.color}-600 mb-2">${p.tagline}</p>
<p class="text-${p.color}-700 mb-4">${p.description}</p>
<div class="code-block">${p.example.trim()}</div>
`;
contentContainer.appendChild(contentDiv);
});
tabsContainer.addEventListener('click', (e) => {
if (e.target.classList.contains('tab-button')) {
const targetId = e.target.dataset.target;
// Update buttons
tabsContainer.querySelectorAll('.tab-button').forEach(btn => {
btn.classList.remove('active');
});
e.target.classList.add('active');
// Update content
contentContainer.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
document.getElementById(targetId).classList.add('active');
}
});
</script>
</body>
</html>