Skip to main content

Overview

Root contexts are containers that hold the history and state for a series of related interactions in MCP. They enable coherent multi-turn conversations, state tracking across complex workflows, and context sharing across clients.

Conversation persistence

Maintain coherent multi-turn conversations without re-sending history

Memory management

Store and retrieve information across interactions

State management

Track progress in complex multi-step workflows

Context sharing

Allow multiple clients to access the same conversation state

Root context lifecycle

Create Root Context


Initialize with Metadata


Send Requests with Context ID ◄──┐
       │                          │
       ▼                          │
Update Context with Results ──────┘


Archive Context When Complete

C# implementation

// .NET Example: Root Context Management
using Microsoft.Mcp.Client;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;

public class RootContextExample
{
    private readonly IMcpClient _client;
    private readonly IRootContextManager _contextManager;

    public RootContextExample(IMcpClient client, IRootContextManager contextManager)
    {
        _client = client;
        _contextManager = contextManager;
    }

    public async Task DemonstrateRootContextAsync()
    {
        // 1. Create a new root context
        var contextResult = await _contextManager.CreateRootContextAsync(
            new RootContextCreateOptions
            {
                Name = "Customer Support Session",
                Metadata = new Dictionary<string, string>
                {
                    ["CustomerName"]  = "Acme Corporation",
                    ["PriorityLevel"] = "High",
                    ["Domain"]        = "Cloud Services"
                }
            });

        string contextId = contextResult.ContextId;
        Console.WriteLine($"Created root context: {contextId}");

        // 2. First interaction
        var response1 = await _client.SendPromptAsync(
            "I'm having issues scaling my web service deployment in the cloud.",
            new SendPromptOptions { RootContextId = contextId }
        );
        Console.WriteLine($"Response 1: {response1.GeneratedText}");

        // 3. Second interaction — model retains previous context
        var response2 = await _client.SendPromptAsync(
            "Yes, we're using containerized deployments with Kubernetes.",
            new SendPromptOptions { RootContextId = contextId }
        );
        Console.WriteLine($"Response 2: {response2.GeneratedText}");

        // 4. Enrich context metadata from conversation
        await _contextManager.UpdateContextMetadataAsync(contextId,
            new Dictionary<string, string>
            {
                ["TechnicalEnvironment"] = "Kubernetes",
                ["IssueType"]            = "Scaling"
            });

        // 5. Inspect context
        var contextInfo = await _contextManager.GetRootContextInfoAsync(contextId);
        Console.WriteLine($"Messages: {contextInfo.MessageCount}");

        // 6. Archive when done
        await _contextManager.ArchiveRootContextAsync(contextId);
    }
}

Java implementation — financial analysis

// Java Example: Root Context for Financial Analysis
package com.example.mcp.contexts;

import com.mcp.client.McpClient;
import com.mcp.client.ContextManager;
import com.mcp.models.RootContext;
import com.mcp.models.McpResponse;
import java.util.HashMap;
import java.util.Map;

public class RootContextsDemo {
    private final McpClient client;
    private final ContextManager contextManager;

    public RootContextsDemo(String serverUrl) {
        this.client = new McpClient.Builder()
            .setServerUrl(serverUrl)
            .build();
        this.contextManager = new ContextManager(client);
    }

    public void demonstrateRootContext() throws Exception {
        Map<String, String> metadata = new HashMap<>();
        metadata.put("projectName", "Financial Analysis");
        metadata.put("userRole",    "Financial Analyst");
        metadata.put("dataSource",  "Q1 2025 Financial Reports");

        // 1. Create context
        RootContext context = contextManager.createRootContext(
            "Financial Analysis Session", metadata);
        String contextId = context.getId();

        // 2. First query
        McpResponse response1 = client.sendPrompt(
            "Analyze the trends in Q1 financial data for our technology division",
            contextId
        );

        // 3. Enrich context with insights
        contextManager.addContextMetadata(contextId,
            Map.of("identifiedTrend", "Increasing cloud infrastructure costs"));

        // 4. Follow-up query — uses enriched context
        McpResponse response2 = client.sendPrompt(
            "What's driving the increase in cloud infrastructure costs?",
            contextId
        );

        // 5. Generate and store summary
        McpResponse summaryResponse = client.sendPrompt(
            "Summarize our analysis in 3-5 key points",
            contextId
        );

        contextManager.addContextMetadata(contextId,
            Map.of("analysisSummary", summaryResponse.getGeneratedText()));

        // 6. Archive
        contextManager.archiveContext(contextId);
    }
}

JavaScript implementation

// JavaScript Example: Managing MCP Root Contexts
const { McpClient, RootContextManager } = require('@mcp/client');

class ContextSession {
    constructor(serverUrl, apiKey = null) {
        this.client = new McpClient({ serverUrl, apiKey });
        this.contextManager = new RootContextManager(this.client);
    }

    async createConversationContext(sessionName, metadata = {}) {
        const contextResult = await this.contextManager.createRootContext({
            name: sessionName,
            metadata: {
                ...metadata,
                createdAt: new Date().toISOString(),
                status: 'active'
            }
        });
        console.log(`Created context '${sessionName}': ${contextResult.id}`);
        return contextResult.id;
    }

    async sendMessage(contextId, message, options = {}) {
        const response = await this.client.sendPrompt(message, {
            rootContextId: contextId,
            temperature: options.temperature || 0.7,
            allowedTools: options.allowedTools || []
        });

        if (options.storeInsights) {
            await this.storeConversationInsights(
                contextId, message, response.generatedText);
        }

        return { message: response.generatedText, contextId };
    }

    async generateContextSummary(contextId) {
        const response = await this.client.sendPrompt(
            "Summarize our conversation in 3-4 sentences, highlighting main points.",
            { rootContextId: contextId, temperature: 0.3 }
        );

        await this.contextManager.updateContextMetadata(contextId, {
            conversationSummary: response.generatedText,
            summarizedAt: new Date().toISOString()
        });

        return response.generatedText;
    }

    async archiveContext(contextId) {
        const summary = await this.generateContextSummary(contextId);
        await this.contextManager.archiveContext(contextId);
        return { status: "archived", contextId, summary };
    }
}

// Usage
async function demonstrateContextSession() {
    const session = new ContextSession('https://mcp-server-example.com');

    const contextId = await session.createConversationContext(
        'Product Support - Database Performance',
        {
            customer:     'Globex Corporation',
            product:      'Enterprise Database',
            severity:     'Medium',
            supportAgent: 'AI Assistant'
        }
    );

    const response1 = await session.sendMessage(
        contextId,
        "I'm experiencing slow query performance after the latest update.",
        { storeInsights: true }
    );

    const response2 = await session.sendMessage(
        contextId,
        "Yes, we've already checked the indexes — they're properly configured.",
        { storeInsights: true }
    );

    const archiveResult = await session.archiveContext(contextId);
    console.log('Summary:', archiveResult.summary);
}

Python implementation

# Python Example: Root Context for Multi-Turn Assistance
import asyncio
from datetime import datetime
from mcp_client import McpClient, RootContextManager

class AssistantSession:
    def __init__(self, server_url, api_key=None):
        self.client = McpClient(server_url=server_url, api_key=api_key)
        self.context_manager = RootContextManager(self.client)

    async def create_session(self, name, user_info=None):
        metadata = {
            "session_type": "assistant",
            "created_at": datetime.now().isoformat(),
        }
        if user_info:
            metadata.update({f"user_{k}": v for k, v in user_info.items()})

        context = await self.context_manager.create_root_context(name, metadata)
        return context.id

    async def send_message(self, context_id, message, tools=None):
        options = {"root_context_id": context_id}
        if tools:
            options["allowed_tools"] = tools

        response = await self.client.send_prompt(message, options)

        await self.context_manager.update_context_metadata(
            context_id,
            {
                f"message_{datetime.now().timestamp()}": message[:50] + "...",
                "last_interaction": datetime.now().isoformat()
            }
        )
        return response

    async def end_session(self, context_id):
        summary_response = await self.client.send_prompt(
            "Summarize our conversation and any key decisions made.",
            {"root_context_id": context_id}
        )

        await self.context_manager.update_context_metadata(
            context_id,
            {
                "summary":    summary_response.generated_text,
                "ended_at":   datetime.now().isoformat(),
                "status":     "completed"
            }
        )
        await self.context_manager.archive_context(context_id)

        return {"status": "completed", "summary": summary_response.generated_text}

Best practices

Create focused contexts

Use separate root contexts for different conversation purposes to maintain clarity

Set expiration policies

Archive or delete old contexts to manage storage and comply with data retention policies

Store relevant metadata

Enrich contexts with domain-specific metadata as conversations reveal new information

Generate summaries

When contexts grow large, summarize them to manage token size while preserving key insights
When a context grows very long, use the model itself to generate a summary, store it in context metadata, and optionally start a fresh context that begins with the summary. This prevents context window exhaustion in long-running workflows.

Build docs developers (and LLMs) love