> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rippletide.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> Give your AI agents persistent memory with a context graph

## What is the Context Graph?

The Context Graph is a persistent context graph that gives your AI agents memory across conversations. Without it, your agent forgets everything between sessions. With it, your agent can:

* **Remember** facts, decisions, preferences, and context across sessions
* **Recall** past information by searching the context graph
* **Relate** entities to each other (e.g. "John works at Acme Corp")
* **Invalidate** outdated information when things change

Each agent gets its own **isolated context graph**, so memories from one project don't leak into another.

## When to use it

* Your agent needs to recall past interactions or user preferences
* You want structured knowledge (entities + relationships) instead of flat text
* You need cross-session memory for coding assistants, support agents, or project tools
* You want to plug memory into any MCP-compatible client (Cursor, Claude, VS Code)

## How it works

<Steps>
  <Step title="Your AI assistant (Cursor, Claude, etc.)">
    Calls tools like `remember`, `recall`, `relate` automatically during conversations.
  </Step>

  <Step title="MCP (Model Context Protocol)">
    The open transport protocol that connects AI assistants to the Context Graph. Think of MCP as the pipe. The Context Graph is what flows through it.
  </Step>

  <Step title="Context Graph API">
    Reads and writes to the agent's context graph.
  </Step>

  <Step title="Agent's Context Graph">
    Stores entities, memories, and relations. Each agent gets its own isolated graph.
  </Step>
</Steps>

You don't need to do anything. The assistant decides when to store or retrieve information based on context.

## Example

Here is an interactive **live** environment where you can test the comparison. On the left, a standard agent without memory. On the right, the same agent with a real Context Graph via the playground proxy.

For this Overview demo, the right side is preloaded with **code best practices**. Try asking: "How should I format a React component?"

<iframe
  width="100%"
  height="700px"
  style={{ border: 'none', borderRadius: '10px', background: 'transparent', margin: '16px 0 32px 0' }}
  title="Interactive Context Graph Terminal (Live)"
  srcDoc={`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html,
body {
  font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;
  background: #070b14;
  color: #e0e0e0;
}
.container { max-width: 100%; }
.header-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 16px;
  background: #1a1a2e;
  border-radius: 8px 8px 0 0;
  border-bottom: 1px solid #333;
  gap: 12px;
}
.session-id {
  font-weight: bold;
  font-size: 13px;
  color: #e0e0e0;
  font-family: monospace;
}
.refresh-btn {
  background: #2d2d44;
  color: #ccc;
  border: 1px solid #444;
  padding: 6px 14px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 12px;
  white-space: nowrap;
}
.refresh-btn:hover { background: #3a3a55; color: #fff; }
.mode-banner {
  padding: 8px 12px;
  font-size: 12px;
  letter-spacing: 0.2px;
  color: #b7c9ff;
  background: #131727;
  border-left: 1px solid #2a2f45;
  border-right: 1px solid #2a2f45;
  border-bottom: 1px solid #2a2f45;
}
.term-comparison {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  padding: 12px;
  background: #0d0d1a;
}
.term-window {
  background: #1a1a2e;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid #2a2a44;
  min-width: 0;
}
.term-title-bar {
  display: flex;
  align-items: center;
  padding: 8px 12px;
  background: #252540;
  gap: 8px;
}
.term-dot { width: 10px; height: 10px; border-radius: 50%; }
.term-dot.r { background: #ff5f57; }
.term-dot.y { background: #febc2e; }
.term-dot.g { background: #28c840; }
.term-title {
  flex: 1;
  text-align: center;
  font-size: 12px;
  color: #888;
}
.term-body {
  padding: 12px;
  min-height: 480px;
  max-height: 480px;
  overflow-y: auto;
  font-size: 13px;
  line-height: 1.6;
}
.term-line {
  margin-bottom: 4px;
  word-wrap: break-word;
  white-space: pre-wrap;
}
.term-prompt { color: #5eead4; }
.term-prompt::before { content: '> '; color: #666; }
.term-response { color: #e0e0e0; }
.term-response::before { content: '>> '; color: #666; }
.term-tool { color: #a78bfa; padding-left: 16px; font-size: 12px; }
.term-tool::before { content: '[tool] '; font-weight: bold; }
.term-found { color: #fbbf24; padding-left: 24px; font-size: 12px; font-style: italic; }
.term-error { color: #f87171; }
.term-label {
  color: #555;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1px;
  margin-bottom: 8px;
}
.term-thinking { display: flex; gap: 4px; padding: 4px 0; }
.td {
  width: 6px;
  height: 6px;
  background: #5eead4;
  border-radius: 50%;
  animation: bounce 1.2s infinite ease-in-out;
}
.td:nth-child(2) { animation-delay: 0.2s; }
.td:nth-child(3) { animation-delay: 0.4s; }
@keyframes bounce {
  0%, 80%, 100% { transform: scale(0); }
  40% { transform: scale(1); }
}
.input-area {
  display: flex;
  gap: 8px;
  padding: 12px;
  background: #0d0d1a;
  border-radius: 0 0 8px 8px;
}
.input-area input {
  flex: 1;
  background: #1a1a2e;
  border: 1px solid #333;
  color: #e0e0e0;
  padding: 10px 14px;
  border-radius: 8px;
  font-size: 14px;
  outline: none;
  font-family: inherit;
}
.input-area input:focus { border-color: #5eead4; }
.input-area input::placeholder { color: #555; }
.input-area button {
  background: #238636;
  color: #fff;
  border: none;
  padding: 10px 20px;
  border-radius: 8px;
  cursor: pointer;
  font-weight: 600;
  font-size: 14px;
}
.input-area button:disabled { background: #23863688; cursor: not-allowed; }
@media (max-width: 600px) {
  .term-comparison { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="container">
<div class="header-bar">
  <span>Demo ID: <span class="session-id" id="sessionIdDisplay">overview-preloaded</span></span>
  <button class="refresh-btn" type="button" onclick="handleRefreshContext()">New Conversation</button>
</div>

<div class="mode-banner">Live demo: real proxy calls to a Context Graph-enabled agent.</div>

<div class="term-comparison">
  <div class="term-window">
    <div class="term-title-bar">
      <div class="term-dot r"></div>
      <div class="term-dot y"></div>
      <div class="term-dot g"></div>
      <div class="term-title">Without Context Graph</div>
    </div>
    <div class="term-body" id="vanillaBody"></div>
  </div>

  <div class="term-window">
    <div class="term-title-bar">
      <div class="term-dot r"></div>
      <div class="term-dot y"></div>
      <div class="term-dot g"></div>
      <div class="term-title">With Context Graph</div>
    </div>
    <div class="term-body" id="mcpBody"></div>
  </div>
</div>

<form class="input-area" onsubmit="handleSend(event)">
  <input
    type="text"
    id="chatInput"
    placeholder="Type a message... (e.g. How should I format a React component?)"
    oninput="toggleButton()"
    autocomplete="off"
  />
  <button type="submit" id="sendBtn" disabled>Send</button>
</form>
</div>

<script>
var API_BASE = 'https://agent-evalserver-production.up.railway.app';
var MCP_AGENT_CHAT_URL = 'https://agent-evalserver-production.up.railway.app/api/agents/51e43552-0cc3-4029-bc81-8e489e3d4235/chat';
var MCP_AGENT_ID = '51e43552-0cc3-4029-bc81-8e489e3d4235';
var DEMO_ID = 'overview-live-preloaded';

var sessionId = '';
var isLoading = false;
var isBootstrapping = false;

function newSessionId() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random() * 16 | 0;
    var v = c === 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}

function truncate(text, maxLen) {
  if (!text) return '';
  if (text.length <= maxLen) return text;
  return text.slice(0, maxLen - 3) + '...';
}

function addMessage(targetId, type, content) {
  var body = document.getElementById(targetId);
  var div = document.createElement('div');
  div.className = 'term-line ' + type;

  if (type === 'term-thinking') {
    div.innerHTML = "<span class='td'></span><span class='td'></span><span class='td'></span>";
  } else {
    var prefix = '';
    if (type === 'term-prompt') prefix = 'You: ';
    else if (type === 'term-response' || type === 'term-error') prefix = 'Assistant: ';
    div.innerText = prefix + content;
  }

  body.appendChild(div);
  body.scrollTop = body.scrollHeight;
  return div;
}

function clearBodies() {
  document.getElementById('vanillaBody').innerHTML = '';
  document.getElementById('mcpBody').innerHTML = '';
}

function toggleButton() {
  var input = document.getElementById('chatInput').value.trim();
  document.getElementById('sendBtn').disabled = !input || isLoading || isBootstrapping;
}

async function postJSON(path, payload) {
  var response = await fetch(API_BASE + path, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload)
  });

  var data;
  try {
    data = await response.json();
  } catch (error) {
    data = { error: 'Invalid JSON response from proxy' };
  }

  if (!response.ok || data.error) {
    throw new Error(data.error || ('HTTP ' + response.status));
  }

  return data;
}

async function callVanilla(message) {
  return postJSON('/api/chat-playground/vanilla', {
    message: message,
    history: []
  });
}

async function callMcp(message) {
  return postJSON('/api/chat-playground/mcp', {
    message: message,
    sessionId: sessionId,
    history: [],
    chatAgentUrl: MCP_AGENT_CHAT_URL,
    agentId: MCP_AGENT_ID
  });
}

function renderToolLogs(tools) {
  if (!tools || tools.length === 0) {
    addMessage('mcpBody', 'term-tool', 'no tool call emitted on this turn');
    return;
  }

  for (var i = 0; i < tools.length; i++) {
    var tool = tools[i];
    var params = tool.params ? String(tool.params) : '';
    addMessage('mcpBody', 'term-tool', tool.name + '(' + truncate(params, 72) + ')');

    var toolResult = tool.result ? String(tool.result).replace(/\s+/g, ' ').trim() : '';
    if (toolResult) {
      addMessage('mcpBody', 'term-found', truncate(toolResult, 140));
    }
  }
}

async function preloadMemories() {
  isBootstrapping = true;
  toggleButton();
  addMessage('mcpBody', 'term-label', 'Using preloaded context from agent endpoint');
  addMessage('mcpBody', 'term-tool', 'agent: ' + MCP_AGENT_ID);
  addMessage('mcpBody', 'term-label', 'Ready: ask about React component formatting');
  isBootstrapping = false;
  toggleButton();
}

async function initializeSession() {
  sessionId = newSessionId();
  clearBodies();

  document.getElementById('sessionIdDisplay').innerText = DEMO_ID + ' / ' + sessionId;
  addMessage('vanillaBody', 'term-label', 'No long-term memory (live call)');
  addMessage('mcpBody', 'term-label', 'With live Context Graph memory');
  await preloadMemories();
}

async function handleRefreshContext() {
  if (isLoading || isBootstrapping) return;
  document.getElementById('chatInput').value = '';
  toggleButton();
  await initializeSession();
}

async function handleSend(event) {
  event.preventDefault();

  var inputEl = document.getElementById('chatInput');
  var userMessage = inputEl.value.trim();
  if (!userMessage || isLoading || isBootstrapping) return;

  isLoading = true;
  toggleButton();
  inputEl.value = '';

  addMessage('vanillaBody', 'term-prompt', userMessage);
  addMessage('mcpBody', 'term-prompt', userMessage);

  var vThinking = addMessage('vanillaBody', 'term-thinking', '');
  var mThinking = addMessage('mcpBody', 'term-thinking', '');

  var results = await Promise.allSettled([
    callVanilla(userMessage),
    callMcp(userMessage)
  ]);

  vThinking.remove();
  mThinking.remove();

  if (results[0].status === 'fulfilled') {
    var vanillaData = results[0].value;
    addMessage('vanillaBody', 'term-response', vanillaData.response || 'No response');
  } else {
    addMessage('vanillaBody', 'term-error', results[0].reason ? results[0].reason.message : 'Vanilla request failed');
  }

  if (results[1].status === 'fulfilled') {
    var mcpData = results[1].value;
    renderToolLogs(mcpData.tools || []);
    addMessage('mcpBody', 'term-response', mcpData.response || 'No response');
  } else {
    addMessage('mcpBody', 'term-error', results[1].reason ? results[1].reason.message : 'MCP request failed');
  }

  isLoading = false;
  toggleButton();
}

initializeSession();
</script>
</body>
</html>`}
/>

## Next steps

<CardGroup cols={2}>
  <Card title="Quickstart" icon="rocket" href="/docs/mcp/quickstart">
    Get the Context Graph running in your MCP client in minutes
  </Card>

  <Card title="Configuration" icon="gear" href="/docs/mcp/configuration">
    Self-hosted setup, transport modes, environment variables
  </Card>

  <Card title="Tools & Resources" icon="wrench" href="/docs/mcp/tools">
    All 7 tools and 4 resources your AI assistant can use
  </Card>

  <Card title="Try it yourself" icon="play" href="/docs/mcp/try-it-yourself">
    Test the Context Graph interactively in our playground
  </Card>
</CardGroup>
