#
tokens: 44728/50000 2/128 files (page 5/9)
lines: off (toggle) GitHub
raw markdown copy
This is page 5 of 9. Use http://codebase.md/tejpalvirk/contextmanager?lines=false&page={x} to view the full context.

# Directory Structure

```
├── .gitattributes
├── .gitignore
├── build-all-domains.sh
├── developer
│   ├── .gitattributes
│   ├── developer_advancedcontext.txt
│   ├── developer_buildcontext.txt
│   ├── developer_deletecontext.txt
│   ├── developer_endsession_examples.txt
│   ├── developer_endsession.txt
│   ├── developer_loadcontext.txt
│   ├── developer_startsession.txt
│   ├── Dockerfile
│   ├── index.d.ts
│   ├── index.js
│   ├── index.ts
│   ├── package.json
│   ├── README.md
│   └── tsconfig.json
├── dist
│   ├── developer
│   │   ├── index.d.ts
│   │   └── index.js
│   ├── main
│   │   ├── descriptions
│   │   │   ├── common_advancedcontext.txt
│   │   │   ├── common_buildcontext.txt
│   │   │   ├── common_deletecontext.txt
│   │   │   ├── common_endsession.txt
│   │   │   ├── common_loadcontext.txt
│   │   │   ├── common_startsession.txt
│   │   │   ├── developer_advancedcontext.txt
│   │   │   ├── developer_buildcontext.txt
│   │   │   ├── developer_deletecontext.txt
│   │   │   ├── developer_endsession_examples.txt
│   │   │   ├── developer_endsession.txt
│   │   │   ├── developer_loadcontext.txt
│   │   │   ├── developer_startsession.txt
│   │   │   ├── project_advancedcontext.txt
│   │   │   ├── project_buildcontext.txt
│   │   │   ├── project_deletecontext.txt
│   │   │   ├── project_endsession_examples.txt
│   │   │   ├── project_endsession.txt
│   │   │   ├── project_loadcontext.txt
│   │   │   ├── project_startsession.txt
│   │   │   ├── qualitativeresearch_advancedcontext.txt
│   │   │   ├── qualitativeresearch_buildcontext.txt
│   │   │   ├── qualitativeresearch_deletecontext.txt
│   │   │   ├── qualitativeresearch_endsession_examples.txt
│   │   │   ├── qualitativeresearch_endsession.txt
│   │   │   ├── qualitativeresearch_loadcontext.txt
│   │   │   ├── qualitativeresearch_startsession.txt
│   │   │   ├── quantitativeresearch_advancedcontext.txt
│   │   │   ├── quantitativeresearch_buildcontext.txt
│   │   │   ├── quantitativeresearch_deletecontext.txt
│   │   │   ├── quantitativeresearch_endsession_examples.txt
│   │   │   ├── quantitativeresearch_endsession.txt
│   │   │   ├── quantitativeresearch_loadcontext.txt
│   │   │   ├── quantitativeresearch_startsession.txt
│   │   │   ├── student_advancedcontext.txt
│   │   │   ├── student_buildcontext.txt
│   │   │   ├── student_deletecontext.txt
│   │   │   ├── student_endsession_examples.txt
│   │   │   ├── student_endsession.txt
│   │   │   ├── student_loadcontext.txt
│   │   │   └── student_startsession.txt
│   │   ├── index.d.ts
│   │   ├── index.js
│   │   ├── mcp.d.ts
│   │   └── mcp.js
│   ├── project
│   │   ├── index.d.ts
│   │   └── index.js
│   ├── qualitativeresearch
│   │   ├── index.d.ts
│   │   └── index.js
│   ├── quantitativeresearch
│   │   ├── index.d.ts
│   │   └── index.js
│   └── student
│       ├── index.d.ts
│       └── index.js
├── main
│   ├── descriptions
│   │   ├── common_advancedcontext.txt
│   │   ├── common_buildcontext.txt
│   │   ├── common_deletecontext.txt
│   │   ├── common_endsession.txt
│   │   ├── common_loadcontext.txt
│   │   ├── common_startsession.txt
│   │   ├── developer_advancedcontext.txt
│   │   ├── developer_buildcontext.txt
│   │   ├── developer_deletecontext.txt
│   │   ├── developer_endsession_examples.txt
│   │   ├── developer_endsession.txt
│   │   ├── developer_loadcontext.txt
│   │   ├── developer_startsession.txt
│   │   ├── project_advancedcontext.txt
│   │   ├── project_buildcontext.txt
│   │   ├── project_deletecontext.txt
│   │   ├── project_endsession_examples.txt
│   │   ├── project_endsession.txt
│   │   ├── project_loadcontext.txt
│   │   ├── project_startsession.txt
│   │   ├── qualitativeresearch_advancedcontext.txt
│   │   ├── qualitativeresearch_buildcontext.txt
│   │   ├── qualitativeresearch_deletecontext.txt
│   │   ├── qualitativeresearch_endsession_examples.txt
│   │   ├── qualitativeresearch_endsession.txt
│   │   ├── qualitativeresearch_loadcontext.txt
│   │   ├── qualitativeresearch_startsession.txt
│   │   ├── quantitativeresearch_advancedcontext.txt
│   │   ├── quantitativeresearch_buildcontext.txt
│   │   ├── quantitativeresearch_deletecontext.txt
│   │   ├── quantitativeresearch_endsession_examples.txt
│   │   ├── quantitativeresearch_endsession.txt
│   │   ├── quantitativeresearch_loadcontext.txt
│   │   ├── quantitativeresearch_startsession.txt
│   │   ├── student_advancedcontext.txt
│   │   ├── student_buildcontext.txt
│   │   ├── student_deletecontext.txt
│   │   ├── student_endsession_examples.txt
│   │   ├── student_endsession.txt
│   │   ├── student_loadcontext.txt
│   │   └── student_startsession.txt
│   ├── index.js
│   ├── index.ts
│   ├── mcp.ts
│   ├── package.json
│   ├── README.md
│   └── tsconfig.json
├── package-lock.json
├── package.json
├── project
│   ├── .gitattributes
│   ├── Dockerfile
│   ├── index.d.ts
│   ├── index.js
│   ├── index.ts
│   ├── package.json
│   ├── project_advancedcontext.txt
│   ├── project_buildcontext.txt
│   ├── project_deletecontext.txt
│   ├── project_endsession_examples.txt
│   ├── project_endsession.txt
│   ├── project_loadcontext.txt
│   ├── project_startsession.txt
│   ├── README.md
│   └── tsconfig.json
├── qualitativeresearch
│   ├── .gitattributes
│   ├── Dockerfile
│   ├── index.d.ts
│   ├── index.js
│   ├── index.ts
│   ├── package.json
│   ├── qualitativeresearch_advancedcontext.txt
│   ├── qualitativeresearch_buildcontext.txt
│   ├── qualitativeresearch_deletecontext.txt
│   ├── qualitativeresearch_endsession_examples.txt
│   ├── qualitativeresearch_endsession.txt
│   ├── qualitativeresearch_loadcontext.txt
│   ├── qualitativeresearch_startsession.txt
│   ├── README.md
│   └── tsconfig.json
├── quantitativeresearch
│   ├── .gitattributes
│   ├── Dockerfile
│   ├── index.d.ts
│   ├── index.js
│   ├── index.ts
│   ├── package.json
│   ├── quantitativeresearch_advancedcontext.txt
│   ├── quantitativeresearch_buildcontext.txt
│   ├── quantitativeresearch_deletecontext.txt
│   ├── quantitativeresearch_endsession_examples.txt
│   ├── quantitativeresearch_endsession.txt
│   ├── quantitativeresearch_loadcontext.txt
│   ├── quantitativeresearch_startsession.txt
│   ├── README.md
│   └── tsconfig.json
├── README.md
├── student
│   ├── .gitattributes
│   ├── Dockerfile
│   ├── index.d.ts
│   ├── index.js
│   ├── index.ts
│   ├── package.json
│   ├── README.md
│   ├── student_advancedcontext.txt
│   ├── student_buildcontext.txt
│   ├── student_deletecontext.txt
│   ├── student_endsession_examples.txt
│   ├── student_endsession.txt
│   ├── student_loadcontext.txt
│   ├── student_startsession.txt
│   └── tsconfig.json
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/qualitativeresearch/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { promises as fs } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { z } from "zod";
import { readFileSync, existsSync } from "fs";

// Define memory file path using environment variable with fallback
const parentPath = path.dirname(fileURLToPath(import.meta.url));
const defaultMemoryPath = path.join(parentPath, 'memory.json');
const defaultSessionsPath = path.join(parentPath, 'sessions.json');

// Properly handle absolute and relative paths for MEMORY_FILE_PATH
const MEMORY_FILE_PATH = process.env.MEMORY_FILE_PATH
  ? path.isAbsolute(process.env.MEMORY_FILE_PATH)
    ? process.env.MEMORY_FILE_PATH  // Use absolute path as is
    : path.join(process.cwd(), process.env.MEMORY_FILE_PATH)  // Relative to current working directory
  : defaultMemoryPath;  // Default fallback

// Properly handle absolute and relative paths for SESSIONS_FILE_PATH
const SESSIONS_FILE_PATH = process.env.SESSIONS_FILE_PATH
  ? path.isAbsolute(process.env.SESSIONS_FILE_PATH)
    ? process.env.SESSIONS_FILE_PATH  // Use absolute path as is
    : path.join(process.cwd(), process.env.SESSIONS_FILE_PATH)  // Relative to current working directory
  : defaultSessionsPath;  // Default fallback

// Qualitative Research specific entity types
const VALID_ENTITY_TYPES = [
  'project',         // Overall research study
  'participant',     // Research subjects
  'interview',       // Formal conversation with participants
  'observation',     // Field notes from observational research
  'document',        // External materials being analyzed
  'code',            // Labels applied to data segments
  'codeGroup',       // Categories or families of related codes
  'memo',            // Researcher's analytical notes
  'theme',           // Emergent patterns across data
  'quote',           // Notable excerpts from data sources
  'literature',      // Academic sources
  'researchQuestion', // Formal questions guiding the study
  'finding',         // Results or conclusions
  'status',          // Status entity type
  'priority'         // Priority entity type
];

// Qualitative Research specific relation types
const VALID_RELATION_TYPES = [
  'participated_in',  // Links participants to interviews/observations
  'codes',            // Shows which codes apply to which data
  'contains',         // Hierarchical relationship (e.g., codegroup contains codes)
  'supports',         // Data supporting a theme or finding
  'contradicts',      // Data contradicting a theme or finding
  'answers',          // Data addressing a research question
  'cites',            // References to literature
  'followed_by',      // Temporal sequence
  'related_to',       // General connection
  'reflects_on',      // Memo reflecting on data/code/theme
  'compares',         // Comparative relationship
  'conducted_by',     // Person who conducted data collection
  'transcribed_by',   // Person who transcribed data
  'part_of',          // Entity is part of another entity
  'derived_from',     // Entity is derived from another entity
  'collected_on',     // Data collection date
  'analyzes',         // Analysis relationship
  'triangulates_with', // Triangulation between data sources
  'has_status',       // Entity has a specific status
  'has_priority',     // Entity has a specific priority
  'precedes'          // Entity comes before another entity in sequence
];

// Status values for different entity types in qualitative research
const STATUS_VALUES = {
  project: ['planning', 'data_collection', 'analysis', 'writing', 'complete'],
  interview: ['scheduled', 'conducted', 'transcribed', 'coded', 'analyzed'],
  observation: ['planned', 'conducted', 'documented', 'coded', 'analyzed'],
  code: ['initial', 'revised', 'final'],
  theme: ['emerging', 'developing', 'established'],
  finding: ['preliminary', 'draft', 'final']
};

// Define valid status values for all entity types
const VALID_STATUS_VALUES = [
  'planning', 'data_collection', 'analysis', 'writing', 'complete',
  'scheduled', 'conducted', 'transcribed', 'coded', 'analyzed',
  'planned', 'documented',
  'initial', 'revised', 'final',
  'emerging', 'developing', 'established',
  'preliminary', 'draft',
  'active', 'in_progress', 'not_started'
];

// Define valid priority values
const VALID_PRIORITY_VALUES = [
  'high', 'low'
];

// Basic validation functions
function validateEntityType(entityType: string): boolean {
  return VALID_ENTITY_TYPES.includes(entityType);
}

function validateRelationType(relationType: string): boolean {
  return VALID_RELATION_TYPES.includes(relationType);
}

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// Collect tool descriptions from text files
const toolDescriptions: Record<string, string> = {
  'startsession': '',
  'loadcontext': '',
  'deletecontext': '',
  'buildcontext': '',
  'advancedcontext': '',
  'endsession': '',
};
for (const tool of Object.keys(toolDescriptions)) {
  const descriptionFilePath = path.resolve(
    __dirname,
    `qualitativeresearch_${tool}.txt`
  );
  if (existsSync(descriptionFilePath)) {
    toolDescriptions[tool] = readFileSync(descriptionFilePath, 'utf-8');
  }
}

// Session management functions
async function loadSessionStates(): Promise<Map<string, any[]>> {
  try {
    const fileContent = await fs.readFile(SESSIONS_FILE_PATH, 'utf-8');
    const sessions = JSON.parse(fileContent);
    // Convert from object to Map
    const sessionsMap = new Map<string, any[]>();
    for (const [key, value] of Object.entries(sessions)) {
      sessionsMap.set(key, value as any[]);
    }
    return sessionsMap;
  } catch (error) {
    if (error instanceof Error && 'code' in error && (error as any).code === "ENOENT") {
      return new Map<string, any[]>();
    }
    throw error;
  }
}

async function saveSessionStates(sessionsMap: Map<string, any[]>): Promise<void> {
  // Convert from Map to object
  const sessions: Record<string, any[]> = {};
  for (const [key, value] of sessionsMap.entries()) {
    sessions[key] = value;
  }
  await fs.writeFile(SESSIONS_FILE_PATH, JSON.stringify(sessions, null, 2), 'utf-8');
}

// Generate a unique session ID
function generateSessionId(): string {
  return `qual_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
}

// We are storing our memory using entities, relations, and observations in a graph structure
interface Entity {
  name: string;
  entityType: string;
  observations: string[];
}

interface Relation {
  from: string;
  to: string;
  relationType: string;
}

interface KnowledgeGraph {
  entities: Entity[];
  relations: Relation[];
}

// The KnowledgeGraphManager class contains all operations to interact with the knowledge graph
class KnowledgeGraphManager {
  private async loadGraph(): Promise<KnowledgeGraph> {
    try {
      const fileContent = await fs.readFile(MEMORY_FILE_PATH, 'utf-8');
      return JSON.parse(fileContent);
    } catch (error) {
      // If the file doesn't exist, return an empty graph
      return {
        entities: [],
        relations: []
      };
    }
  }

  private async saveGraph(graph: KnowledgeGraph): Promise<void> {
    await fs.writeFile(MEMORY_FILE_PATH, JSON.stringify(graph, null, 2), 'utf-8');
  }

  // Initialize status and priority entities
  async initializeStatusAndPriority(): Promise<void> {
    const graph = await this.loadGraph();
    
    // Create status entities if they don't exist
    for (const statusValue of VALID_STATUS_VALUES) {
      const statusName = `status:${statusValue}`;
      if (!graph.entities.some(e => e.name === statusName && e.entityType === 'status')) {
        graph.entities.push({
          name: statusName,
          entityType: 'status',
          observations: [`A ${statusValue} status value`]
        });
      }
    }
    
    // Create priority entities if they don't exist
    for (const priorityValue of VALID_PRIORITY_VALUES) {
      const priorityName = `priority:${priorityValue}`;
      if (!graph.entities.some(e => e.name === priorityName && e.entityType === 'priority')) {
        graph.entities.push({
          name: priorityName,
          entityType: 'priority',
          observations: [`A ${priorityValue} priority value`]
        });
      }
    }
    
    await this.saveGraph(graph);
  }

  // Helper method to get status of an entity
  async getEntityStatus(entityName: string): Promise<string | null> {
    const graph = await this.loadGraph();
    
    // Find status relation for this entity
    const statusRelation = graph.relations.find(r => 
      r.from === entityName && 
      r.relationType === 'has_status'
    );
    
    if (statusRelation) {
      // Extract status value from the status entity name (status:value)
      return statusRelation.to.split(':')[1];
    }
    
    return null;
  }
  
  // Helper method to get priority of an entity
  async getEntityPriority(entityName: string): Promise<string | null> {
    const graph = await this.loadGraph();
    
    // Find priority relation for this entity
    const priorityRelation = graph.relations.find(r => 
      r.from === entityName && 
      r.relationType === 'has_priority'
    );
    
    if (priorityRelation) {
      // Extract priority value from the priority entity name (priority:value)
      return priorityRelation.to.split(':')[1];
    }
    
    return null;
  }
  
  // Helper method to set status of an entity
  async setEntityStatus(entityName: string, statusValue: string): Promise<void> {
    if (!VALID_STATUS_VALUES.includes(statusValue)) {
      throw new Error(`Invalid status value: ${statusValue}. Valid values are: ${VALID_STATUS_VALUES.join(', ')}`);
    }
    
    const graph = await this.loadGraph();
    
    // Remove any existing status relations for this entity
    graph.relations = graph.relations.filter(r => 
      !(r.from === entityName && r.relationType === 'has_status')
    );
    
    // Add new status relation
    graph.relations.push({
      from: entityName,
      to: `status:${statusValue}`,
      relationType: 'has_status'
    });
    
    await this.saveGraph(graph);
  }
  
  // Helper method to set priority of an entity
  async setEntityPriority(entityName: string, priorityValue: string): Promise<void> {
    if (!VALID_PRIORITY_VALUES.includes(priorityValue)) {
      throw new Error(`Invalid priority value: ${priorityValue}. Valid values are: ${VALID_PRIORITY_VALUES.join(', ')}`);
    }
    
    const graph = await this.loadGraph();
    
    // Remove any existing priority relations for this entity
    graph.relations = graph.relations.filter(r => 
      !(r.from === entityName && r.relationType === 'has_priority')
    );
    
    // Add new priority relation
    graph.relations.push({
      from: entityName,
      to: `priority:${priorityValue}`,
      relationType: 'has_priority'
    });
    
    await this.saveGraph(graph);
  }

  async createEntities(entities: Entity[]): Promise<Entity[]> {
    const graph = await this.loadGraph();
    const existingEntityNames = new Set(graph.entities.map(e => e.name));
    
    // Validate entity types
    entities.forEach(entity => {
      if (!validateEntityType(entity.entityType)) {
        throw new Error(`Invalid entity type: ${entity.entityType}. Valid types are: ${VALID_ENTITY_TYPES.join(', ')}`);
      }
    });
    
    const newEntities = entities.filter(entity => !existingEntityNames.has(entity.name));
    graph.entities.push(...newEntities);
    
    await this.saveGraph(graph);
    return newEntities;
  }

  async createRelations(relations: Relation[]): Promise<Relation[]> {
    const graph = await this.loadGraph();
    const existingEntityNames = new Set(graph.entities.map(e => e.name));
    
    // Check that entities exist and validate relation types
    relations.forEach(relation => {
      if (!existingEntityNames.has(relation.from)) {
        throw new Error(`Entity '${relation.from}' not found`);
      }
      if (!existingEntityNames.has(relation.to)) {
        throw new Error(`Entity '${relation.to}' not found`);
      }
      if (!validateRelationType(relation.relationType)) {
        throw new Error(`Invalid relation type: ${relation.relationType}. Valid types are: ${VALID_RELATION_TYPES.join(', ')}`);
      }
    });
    
    // Filter out duplicate relations
    const existingRelations = new Set(
      graph.relations.map(r => `${r.from}:${r.to}:${r.relationType}`)
    );
    
    const newRelations = relations.filter(
      r => !existingRelations.has(`${r.from}:${r.to}:${r.relationType}`)
    );
    
    graph.relations.push(...newRelations);
    
    await this.saveGraph(graph);
    return newRelations;
  }

  async addObservations(observations: { entityName: string; contents: string[] }[]): Promise<{ entityName: string; addedObservations: string[] }[]> {
    const graph = await this.loadGraph();
    const results: { entityName: string; addedObservations: string[] }[] = [];
    
    for (const observation of observations) {
      const entity = graph.entities.find(e => e.name === observation.entityName);
      if (!entity) {
        throw new Error(`Entity '${observation.entityName}' not found`);
      }
      
      // Filter out duplicate observations
      const existingObservations = new Set(entity.observations);
      const newObservations = observation.contents.filter(o => !existingObservations.has(o));
      
      entity.observations.push(...newObservations);
      results.push({
        entityName: observation.entityName,
        addedObservations: newObservations
      });
    }
    
    await this.saveGraph(graph);
    return results;
  }

  async deleteEntities(entityNames: string[]): Promise<void> {
    const graph = await this.loadGraph();
    
    // Remove the entities
    graph.entities = graph.entities.filter(e => !entityNames.includes(e.name));
    
    // Remove relations that involve the deleted entities
    graph.relations = graph.relations.filter(
      r => !entityNames.includes(r.from) && !entityNames.includes(r.to)
    );
    
    await this.saveGraph(graph);
  }

  async deleteObservations(deletions: { entityName: string; observations: string[] }[]): Promise<void> {
    const graph = await this.loadGraph();
    
    for (const deletion of deletions) {
      const entity = graph.entities.find(e => e.name === deletion.entityName);
      if (entity) {
        // Remove the specified observations
        entity.observations = entity.observations.filter(
          o => !deletion.observations.includes(o)
        );
      }
    }
    
    await this.saveGraph(graph);
  }

  async deleteRelations(relations: Relation[]): Promise<void> {
    const graph = await this.loadGraph();
    
    // Remove specified relations
    graph.relations = graph.relations.filter(r => 
      !relations.some(toDelete => 
        r.from === toDelete.from && 
        r.to === toDelete.to && 
        r.relationType === toDelete.relationType
      )
    );
    
    await this.saveGraph(graph);
  }

  async readGraph(): Promise<KnowledgeGraph> {
    return this.loadGraph();
  }

  async searchNodes(query: string): Promise<KnowledgeGraph> {
    const graph = await this.loadGraph();
    
    // Split query into search terms
    const terms = query.toLowerCase().split(/\s+/);
    
    // Find matching entities
    const matchingEntityNames = new Set<string>();
    
    for (const entity of graph.entities) {
      // Check if all terms match
      const matchesAllTerms = terms.every(term => {
        // Check entity name
        if (entity.name.toLowerCase().includes(term)) {
          return true;
        }
        
        // Check entity type
        if (entity.entityType.toLowerCase().includes(term)) {
          return true;
        }
        
        // Check observations
        for (const observation of entity.observations) {
          if (observation.toLowerCase().includes(term)) {
            return true;
          }
        }
        
        return false;
      });
      
      if (matchesAllTerms) {
        matchingEntityNames.add(entity.name);
      }
    }
    
    // Find relations between matching entities
    const matchingRelations = graph.relations.filter(r => 
      matchingEntityNames.has(r.from) && matchingEntityNames.has(r.to)
    );
    
    // Return matching entities and their relations
    return {
      entities: graph.entities.filter(e => matchingEntityNames.has(e.name)),
      relations: matchingRelations
    };
  }

  async openNodes(names: string[]): Promise<KnowledgeGraph> {
    const graph = await this.loadGraph();
    
    // Find the specified entities
    const entities = graph.entities.filter(e => names.includes(e.name));
    
    // Find relations between the specified entities
    const relations = graph.relations.filter(r => 
      names.includes(r.from) && names.includes(r.to)
    );
    
    return {
      entities,
      relations
    };
  }

  // Get project overview including research questions, methodology, participants, data sources
  async getProjectOverview(projectName: string): Promise<any> {
    const graph = await this.loadGraph();
    
    // Find the project
    const project = graph.entities.find(e => e.name === projectName && e.entityType === 'project');
    if (!project) {
      throw new Error(`Project '${projectName}' not found`);
    }
    
    // Find research questions
    const researchQuestions: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'part_of' && relation.to === projectName) {
        const question = graph.entities.find(
          e => e.name === relation.from && e.entityType === 'researchQuestion'
        );
        if (question) {
          researchQuestions.push(question);
        }
      }
    }
    
    // Find participants
    const participants: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'part_of' && relation.to === projectName) {
        const participant = graph.entities.find(
          e => e.name === relation.from && e.entityType === 'participant'
        );
        if (participant) {
          participants.push(participant);
        }
      }
    }
    
    // Find data sources (interviews, observations, documents)
    const interviews: Entity[] = [];
    const observations: Entity[] = [];
    const documents: Entity[] = [];
    
    for (const relation of graph.relations) {
      if (relation.relationType === 'part_of' && relation.to === projectName) {
        const entity = graph.entities.find(e => e.name === relation.from);
        
        if (entity) {
          if (entity.entityType === 'interview') {
            interviews.push(entity);
          } else if (entity.entityType === 'observation') {
            observations.push(entity);
          } else if (entity.entityType === 'document') {
            documents.push(entity);
          }
        }
      }
    }
    
    // Find findings
    const findings: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'part_of' && relation.to === projectName) {
        const finding = graph.entities.find(
          e => e.name === relation.from && e.entityType === 'finding'
        );
        if (finding) {
          findings.push(finding);
        }
      }
    }
    
    // Find themes
    const themes: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'part_of' && relation.to === projectName) {
        const theme = graph.entities.find(
          e => e.name === relation.from && e.entityType === 'theme'
        );
        if (theme) {
          themes.push(theme);
        }
      }
    }
    
    // Get methodology info from project observations
    const methodologyObs = project.observations.filter(
      o => o.toLowerCase().includes('method') || o.toLowerCase().includes('approach')
    );
    
    return {
      project,
      researchQuestions,
      methodology: methodologyObs,
      dataCollection: {
        participants: participants.length,
        interviews: interviews.length,
        observations: observations.length,
        documents: documents.length,
        participantsList: participants,
        interviewsList: interviews,
        observationsList: observations,
        documentsList: documents
      },
      analysis: {
        themes: themes.length,
        themesList: themes
      },
      findings
    };
  }

  // Get all data related to a specific participant
  async getParticipantProfile(participantName: string): Promise<any> {
    const graph = await this.loadGraph();
    
    // Find the participant
    const participant = graph.entities.find(e => e.name === participantName && e.entityType === 'participant');
    if (!participant) {
      throw new Error(`Participant '${participantName}' not found`);
    }
    
    // Find interviews with this participant
    const interviews: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'participated_in' && relation.from === participantName) {
        const interview = graph.entities.find(e => e.name === relation.to && e.entityType === 'interview');
        if (interview) {
          interviews.push(interview);
        }
      }
    }
    
    // Find observations including this participant
    const observations: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'participated_in' && relation.from === participantName) {
        const observation = graph.entities.find(e => e.name === relation.to && e.entityType === 'observation');
        if (observation) {
          observations.push(observation);
        }
      }
    }
    
    // Find quotes from this participant
    const quotes: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'contains' && relation.to === participantName) {
        const quote = graph.entities.find(e => e.name === relation.from && e.entityType === 'quote');
        if (quote) {
          quotes.push(quote);
        }
      }
    }
    
    // Find any memos about this participant
    const memos: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'reflects_on' && relation.to === participantName) {
        const memo = graph.entities.find(e => e.name === relation.from && e.entityType === 'memo');
        if (memo) {
          memos.push(memo);
        }
      }
    }
    
    // Extract demographic information from observations
    const demographicObs = participant.observations.filter(
      o => o.toLowerCase().includes('age') || 
           o.toLowerCase().includes('gender') || 
           o.toLowerCase().includes('occupation') ||
           o.toLowerCase().includes('education')
    );
    
    return {
      participant,
      demographics: demographicObs,
      interviews,
      observations,
      quotes,
      memos
    };
  }

  // Get themes with supporting codes and data
  async getThematicAnalysis(projectName: string): Promise<any> {
    const graph = await this.loadGraph();
    
    // Find the project
    const project = graph.entities.find(e => e.name === projectName && e.entityType === 'project');
    if (!project) {
      throw new Error(`Project '${projectName}' not found`);
    }
    
    // Find all themes related to this project
    const themes: Entity[] = [];
    
    for (const relation of graph.relations) {
      if (relation.relationType === 'part_of' && relation.to === projectName) {
        const theme = graph.entities.find(e => e.name === relation.from && e.entityType === 'theme');
        if (theme) {
          themes.push(theme);
        }
      }
    }
    
    // For each theme, find supporting data
    const thematicAnalysis = themes.map(theme => {
      // Find codes supporting this theme
      const supportingCodes: Entity[] = [];
      for (const relation of graph.relations) {
        if (relation.relationType === 'supports' && relation.to === theme.name) {
          const code = graph.entities.find(e => e.name === relation.from && e.entityType === 'code');
          if (code) {
            supportingCodes.push(code);
          }
        }
      }
      
      // For each code, find supporting quotes
      const codeData = supportingCodes.map(code => {
        const quotes: Entity[] = [];
        for (const relation of graph.relations) {
          if (relation.relationType === 'codes' && relation.from === code.name) {
            const quote = graph.entities.find(e => e.name === relation.to && e.entityType === 'quote');
            if (quote) {
              quotes.push(quote);
            }
          }
        }
        
        return {
          code,
          quotes
        };
      });
      
      // Find any memos reflecting on this theme
      const memos: Entity[] = [];
      for (const relation of graph.relations) {
        if (relation.relationType === 'reflects_on' && relation.to === theme.name) {
          const memo = graph.entities.find(e => e.name === relation.from && e.entityType === 'memo');
          if (memo) {
            memos.push(memo);
          }
        }
      }
      
      // Find status of the theme
      const statusObs = theme.observations.find(o => o.startsWith('Status:'));
      const status = statusObs ? statusObs.split(':')[1].trim() : 'unknown';
      
      return {
        theme,
        status,
        supportingData: codeData,
        codes: supportingCodes,
        memos
      };
    });
    
    return {
      project,
      themes: thematicAnalysis
    };
  }

  // Get all data segments tagged with a specific code
  async getCodedData(codeName: string): Promise<any> {
    const graph = await this.loadGraph();
    
    // Find the code
    const code = graph.entities.find(e => e.name === codeName && e.entityType === 'code');
    if (!code) {
      throw new Error(`Code '${codeName}' not found`);
    }
    
    // Find which code group this code belongs to, if any
    const codeGroups: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'contains' && relation.to === codeName) {
        const codeGroup = graph.entities.find(e => e.name === relation.from && e.entityType === 'codeGroup');
        if (codeGroup) {
          codeGroups.push(codeGroup);
        }
      }
    }
    
    // Find all quotes tagged with this code
    const quotes: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'codes' && relation.from === codeName) {
        const quote = graph.entities.find(e => e.name === relation.to && e.entityType === 'quote');
        if (quote) {
          quotes.push(quote);
        }
      }
    }
    
    // Find which sources (interviews, observations, documents) these quotes come from
    const sources = new Map<string, Entity>();
    
    for (const quote of quotes) {
      for (const relation of graph.relations) {
        if (relation.relationType === 'contains' && relation.from !== codeName && relation.to === quote.name) {
          const source = graph.entities.find(e => e.name === relation.from);
          if (source) {
            sources.set(source.name, source);
          }
        }
      }
    }
    
    // Find themes this code supports
    const themes: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'supports' && relation.from === codeName) {
        const theme = graph.entities.find(e => e.name === relation.to && e.entityType === 'theme');
        if (theme) {
          themes.push(theme);
        }
      }
    }
    
    // Find any memos reflecting on this code
    const memos: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'reflects_on' && relation.to === codeName) {
        const memo = graph.entities.find(e => e.name === relation.from && e.entityType === 'memo');
        if (memo) {
          memos.push(memo);
        }
      }
    }
    
    return {
      code,
      codeGroups,
      quotes,
      sourceCount: sources.size,
      sources: Array.from(sources.values()),
      themes,
      memos
    };
  }

  // Shows data organized by research questions with findings
  async getResearchQuestionAnalysis(projectName: string): Promise<any> {
    const graph = await this.loadGraph();
    
    // Find the project
    const project = graph.entities.find(e => e.name === projectName && e.entityType === 'project');
    if (!project) {
      throw new Error(`Project '${projectName}' not found`);
    }
    
    // Find all research questions for this project
    const researchQuestions: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'part_of' && relation.to === projectName) {
        const question = graph.entities.find(e => e.name === relation.from && e.entityType === 'researchQuestion');
        if (question) {
          researchQuestions.push(question);
        }
      }
    }
    
    // For each research question, find related data
    const questionAnalysis = researchQuestions.map(question => {
      // Find findings that answer this question
      const findings: Entity[] = [];
      for (const relation of graph.relations) {
        if (relation.relationType === 'answers' && relation.to === question.name) {
          const finding = graph.entities.find(e => e.name === relation.from && e.entityType === 'finding');
          if (finding) {
            findings.push(finding);
          }
        }
      }
      
      // Find themes related to this question
      const themes: Entity[] = [];
      for (const relation of graph.relations) {
        if (relation.relationType === 'answers' && relation.to === question.name) {
          const theme = graph.entities.find(e => e.name === relation.from && e.entityType === 'theme');
          if (theme) {
            themes.push(theme);
          }
        }
      }
      
      // Find data directly addressing this question
      const quotes: Entity[] = [];
      for (const relation of graph.relations) {
        if (relation.relationType === 'answers' && relation.to === question.name) {
          const quote = graph.entities.find(e => e.name === relation.from && e.entityType === 'quote');
          if (quote) {
            quotes.push(quote);
          }
        }
      }
      
      return {
        question,
        findings,
        themes,
        quotes
      };
    });
    
    return {
      project,
      researchQuestions: questionAnalysis
    };
  }

  // Returns data in temporal sequence
  async getChronologicalData(projectName: string, dataType?: string): Promise<any> {
    const graph = await this.loadGraph();
    
    // Find the project
    const project = graph.entities.find(e => e.name === projectName && e.entityType === 'project');
    if (!project) {
      throw new Error(`Project '${projectName}' not found`);
    }
    
    // Find all data collection entities for this project
    let dataEntities: Entity[] = [];
    
    for (const relation of graph.relations) {
      if (relation.relationType === 'part_of' && relation.to === projectName) {
        let entity;
        
        // Filter by data type if specified
        if (dataType) {
          entity = graph.entities.find(
            e => e.name === relation.from && e.entityType === dataType
          );
        } else {
          entity = graph.entities.find(
            e => e.name === relation.from && 
               (e.entityType === 'interview' || 
                e.entityType === 'observation' || 
                e.entityType === 'document')
          );
        }
        
        if (entity) {
          dataEntities.push(entity);
        }
      }
    }
    
    // Extract date information for each entity
    const dataWithDates = dataEntities.map(entity => {
      const dateObs = entity.observations.find(o => 
        o.startsWith('Date:') || o.startsWith('Collected on:') || o.startsWith('Created:')
      );
      
      let date = new Date(0);
      if (dateObs) {
        // Extract date string and try to parse it
        const dateString = dateObs.split(':')[1].trim();
        const parsedDate = new Date(dateString);
        if (!isNaN(parsedDate.getTime())) {
          date = parsedDate;
        }
      }
      
      return {
        entity,
        date
      };
    });
    
    // Sort by date
    dataWithDates.sort((a, b) => a.date.getTime() - b.date.getTime());
    
    // Create a timeline of data
    const timeline = dataWithDates.map(item => {
      // For each entity, find related quotes
      const quotes: Entity[] = [];
      for (const relation of graph.relations) {
        if (relation.relationType === 'contains' && relation.from === item.entity.name) {
          const quote = graph.entities.find(e => e.name === relation.to && e.entityType === 'quote');
          if (quote) {
            quotes.push(quote);
          }
        }
      }
      
      return {
        date: item.date,
        entity: item.entity,
        quotes
      };
    });
    
    return {
      project,
      timeline
    };
  }

  // Finds where multiple codes appear together
  async getCodeCooccurrence(codeName: string): Promise<any> {
    const graph = await this.loadGraph();
    
    // Find the code
    const code = graph.entities.find(e => e.name === codeName && e.entityType === 'code');
    if (!code) {
      throw new Error(`Code '${codeName}' not found`);
    }
    
    // Find all quotes tagged with this code
    const quotes: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'codes' && relation.from === codeName) {
        const quote = graph.entities.find(e => e.name === relation.to && e.entityType === 'quote');
        if (quote) {
          quotes.push(quote);
        }
      }
    }
    
    // For each quote, find other codes that also tag it
    const codeOccurrences = new Map<string, { code: Entity; count: number; quotes: Entity[] }>();
    
    for (const quote of quotes) {
      for (const relation of graph.relations) {
        if (relation.relationType === 'codes' && relation.from !== codeName && relation.to === quote.name) {
          const otherCode = graph.entities.find(e => e.name === relation.from && e.entityType === 'code');
          if (otherCode) {
            if (codeOccurrences.has(otherCode.name)) {
              const occurrence = codeOccurrences.get(otherCode.name)!;
              occurrence.count++;
              occurrence.quotes.push(quote);
            } else {
              codeOccurrences.set(otherCode.name, {
                code: otherCode,
                count: 1,
                quotes: [quote]
              });
            }
          }
        }
      }
    }
    
    // Sort codes by co-occurrence frequency
    const cooccurringCodes = Array.from(codeOccurrences.values())
      .sort((a, b) => b.count - a.count);
    
    return {
      code,
      quotesCount: quotes.length,
      cooccurringCodes
    };
  }

  // Gets all memos related to a specific entity
  async getMemosByFocus(entityName: string): Promise<any> {
    const graph = await this.loadGraph();
    
    // Find the entity
    const entity = graph.entities.find(e => e.name === entityName);
    if (!entity) {
      throw new Error(`Entity '${entityName}' not found`);
    }
    
    // Find all memos reflecting on this entity
    const memos: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'reflects_on' && relation.to === entityName) {
        const memo = graph.entities.find(e => e.name === relation.from && e.entityType === 'memo');
        if (memo) {
          memos.push(memo);
        }
      }
    }
    
    // Sort memos by date if possible
    const memosWithDates = memos.map(memo => {
      const dateObs = memo.observations.find(o => o.startsWith('Date:') || o.startsWith('Created:'));
      
      let date = new Date(0);
      if (dateObs) {
        const dateString = dateObs.split(':')[1].trim();
        const parsedDate = new Date(dateString);
        if (!isNaN(parsedDate.getTime())) {
          date = parsedDate;
        }
      }
      
      return {
        memo,
        date
      };
    });
    
    // Sort by date, most recent first
    memosWithDates.sort((a, b) => b.date.getTime() - a.date.getTime());
    
    return {
      entity,
      memos: memosWithDates.map(m => m.memo)
    };
  }

  // Returns information about methods, sampling, analysis approach
  async getMethodologyDetails(projectName: string): Promise<any> {
    const graph = await this.loadGraph();
    
    // Find the project
    const project = graph.entities.find(e => e.name === projectName && e.entityType === 'project');
    if (!project) {
      throw new Error(`Project '${projectName}' not found`);
    }
    
    // Extract methodology information from project observations
    const methodologyObs = project.observations.filter(o => 
      o.toLowerCase().includes('method') || 
      o.toLowerCase().includes('approach') || 
      o.toLowerCase().includes('sampling') || 
      o.toLowerCase().includes('analysis') ||
      o.toLowerCase().includes('validity') ||
      o.toLowerCase().includes('reliability')
    );
    
    // Find methodology-related memos
    const memos: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'reflects_on' && relation.to === projectName) {
        const memo = graph.entities.find(e => e.name === relation.from && e.entityType === 'memo');
        if (memo && memo.observations.some(o => 
          o.toLowerCase().includes('method') || 
          o.toLowerCase().includes('approach') || 
          o.toLowerCase().includes('sampling') || 
          o.toLowerCase().includes('analysis')
        )) {
          memos.push(memo);
        }
      }
    }
    
    // Calculate data collection statistics
    // 1. Get all interviews
    const interviews: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'part_of' && relation.to === projectName) {
        const interview = graph.entities.find(e => e.name === relation.from && e.entityType === 'interview');
        if (interview) {
          interviews.push(interview);
        }
      }
    }
    
    // 2. Get all observations
    const observations: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'part_of' && relation.to === projectName) {
        const observation = graph.entities.find(e => e.name === relation.from && e.entityType === 'observation');
        if (observation) {
          observations.push(observation);
        }
      }
    }
    
    // 3. Get all documents
    const documents: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'part_of' && relation.to === projectName) {
        const document = graph.entities.find(e => e.name === relation.from && e.entityType === 'document');
        if (document) {
          documents.push(document);
        }
      }
    }
    
    // 4. Get all participants
    const participants: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'part_of' && relation.to === projectName) {
        const participant = graph.entities.find(e => e.name === relation.from && e.entityType === 'participant');
        if (participant) {
          participants.push(participant);
        }
      }
    }
    
    // Find literature cited in this project
    const literature: Entity[] = [];
    for (const relation of graph.relations) {
      if (relation.relationType === 'cites' && relation.from === projectName) {
        const source = graph.entities.find(e => e.name === relation.to && e.entityType === 'literature');
        if (source) {
          literature.push(source);
        }
      }
    }
    
    return {
      project,
      methodology: methodologyObs,
      dataCollection: {
        participants: participants.length,
        interviews: interviews.length,
        observations: observations.length,
        documents: documents.length
      },
      memos,
      literature
    };
  }

  // First, let's add the missing getRelatedEntities method to the KnowledgeGraphManager class
  // Add this before the async main() function

  async getRelatedEntities(entityName: string, relationTypes?: string[]): Promise<any> {
    const graph = await this.loadGraph();
    
    // Find the entity
    const entity = graph.entities.find(e => e.name === entityName);
    if (!entity) {
      throw new Error(`Entity '${entityName}' not found`);
    }
    
    // Find all relations involving this entity
    let relevantRelations = graph.relations.filter(r => r.from === entityName || r.to === entityName);
    
    // Filter by relation types if specified
    if (relationTypes && relationTypes.length > 0) {
      relevantRelations = relevantRelations.filter(r => relationTypes.includes(r.relationType));
    }
    
    // Get all related entities grouped by relation type
    const related: Record<string, Entity[]> = {};
    
    for (const relation of relevantRelations) {
      const relationType = relation.relationType;
      if (!related[relationType]) {
        related[relationType] = [];
      }
      
      if (relation.from === entityName) {
        const target = graph.entities.find(e => e.name === relation.to);
        if (target) {
          related[relationType].push(target);
        }
      } else {
        const source = graph.entities.find(e => e.name === relation.from);
        if (source) {
          related[relationType].push(source);
        }
      }
    }
    
    return {
      entity,
      related
    };
  }
}

// Main function to set up the MCP server
async function main() {
  try {
    const knowledgeGraphManager = new KnowledgeGraphManager();
    
    // Initialize status and priority entities
    await knowledgeGraphManager.initializeStatusAndPriority();
    
    // Create the MCP server with a name and version
    const server = new McpServer({
      name: "Context Manager",
      version: "1.0.0"
    });
    
    // Define a resource that exposes the entire graph
    server.resource(
      "graph",
      "graph://researcher/qualitative",
      async (uri) => ({
        contents: [{
          uri: uri.href,
          text: JSON.stringify(await knowledgeGraphManager.readGraph(), null, 2)
        }]
      })
    );
    
    // Define tools using zod for parameter validation

    /**
     * Load context for a specific entity
     */
    server.tool(
      "loadcontext",
      toolDescriptions["loadcontext"],
      {
        entityName: z.string(),
        entityType: z.string().optional(),
        sessionId: z.string().optional() // Optional to maintain backward compatibility
      },
      async ({ entityName, entityType = "project", sessionId }) => {
        try {
          // Validate session if ID is provided
          if (sessionId) {
            const sessionStates = await loadSessionStates();
            if (!sessionStates.has(sessionId)) {
              console.warn(`Warning: Session ${sessionId} not found, but proceeding with context load`);
              // Initialize it anyway for more robustness
              sessionStates.set(sessionId, []);
              await saveSessionStates(sessionStates);
            }
            
            // Track that this entity was loaded in this session
            const sessionState = sessionStates.get(sessionId) || [];
            const loadEvent = {
              type: 'context_loaded',
              timestamp: new Date().toISOString(),
              entityName,
              entityType
            };
            sessionState.push(loadEvent);
            sessionStates.set(sessionId, sessionState);
            await saveSessionStates(sessionStates);
          }
          
          // Get the entity
          // Changed from using 'name:' prefix to directly searching by the entity name
          const entityGraph = await knowledgeGraphManager.searchNodes(entityName);
          if (entityGraph.entities.length === 0) {
            throw new Error(`Entity ${entityName} not found`);
          }
          
          // Find the exact entity by name (case-sensitive match)
          const entity = entityGraph.entities.find(e => e.name === entityName);
          if (!entity) {
            throw new Error(`Entity ${entityName} not found`);
          }
          
          // Different context loading based on entity type
          let contextMessage = "";
          
          if (entityType === "project") {
            // Get project overview
            const projectOverview = await knowledgeGraphManager.getProjectOverview(entityName);
            
            // Get thematic analysis
            let thematicAnalysis;
            try {
              thematicAnalysis = await knowledgeGraphManager.getThematicAnalysis(entityName);
            } catch (error) {
              thematicAnalysis = { themes: [] };
            }
            
            // Get research question analysis
            let researchQuestions;
            try {
              researchQuestions = await knowledgeGraphManager.getResearchQuestionAnalysis(entityName);
            } catch (error) {
              researchQuestions = { researchQuestions: [] };
            }
            
            // Get methodology details
            let methodology;
            try {
              methodology = await knowledgeGraphManager.getMethodologyDetails(entityName);
            } catch (error) {
              methodology = { methodology: [] };
            }
            
            // Get status and priority using the relation-based approach
            const status = await knowledgeGraphManager.getEntityStatus(entityName) || "Unknown";
            const priority = await knowledgeGraphManager.getEntityPriority(entityName);
            const priorityText = priority ? `- **Priority**: ${priority}` : "";
            
            // Format observations
            const observationsList = entity.observations.length > 0 
              ? entity.observations.map(obs => `- ${obs}`).join("\n")
              : "No observations";
            
            // Extract methodology information
            const methodologyText = methodology.methodology?.map((m: string) => `- ${m}`).join("\n") || "No methodology details available";
            
            // Extract research questions
            const questionsText = researchQuestions.researchQuestions?.map((q: any) => {
              const findings = q.findings?.map((f: Entity) => `  - ${f.name}`).join("\n") || "  - No findings yet";
              return `- **${q.question.name}**\n${findings}`;
            }).join("\n") || "No research questions found";
            
            // Format data collection stats
            const participantCount = projectOverview.dataCollection?.participants || 0;
            const interviewCount = projectOverview.dataCollection?.interviews || 0;
            const observationCount = projectOverview.dataCollection?.observations || 0;
            const documentCount = projectOverview.dataCollection?.documents || 0;
            
            // Format theme analysis
            const themesText = thematicAnalysis.themes?.map(async (t: any) => {
              const codeCount = t.supportingData?.length || 0;
              const themeStatus = await knowledgeGraphManager.getEntityStatus(t.theme.name) || "unknown";
              return `- **${t.theme.name}** (Status: ${themeStatus}): ${codeCount} codes`;
            });
            
            const resolvedThemesText = themesText ? 
              await Promise.all(themesText).then(texts => texts.join("\n")) : 
              "No themes identified yet";
            
            // Get recent data collection - without date references
            const recentInterviews = projectOverview.dataCollection?.interviewsList?.slice(0, 5).map(async (i: Entity) => {
              const participant = i.observations.find(o => o.startsWith("participant:"))?.substring(12) || "Unknown";
              const interviewStatus = await knowledgeGraphManager.getEntityStatus(i.name) || "unknown";
              return `- **${i.name}** with ${participant} (Status: ${interviewStatus})`;
            });
            
            const resolvedInterviewsText = recentInterviews ? 
              await Promise.all(recentInterviews).then(texts => texts.join("\n")) : 
              "No recent interviews";
            
            // Get findings with status from relations
            const findingsText = projectOverview.findings?.map(async (f: Entity) => {
              const findingStatus = await knowledgeGraphManager.getEntityStatus(f.name) || "preliminary";
              const findingObs = f.observations.length > 0 ? f.observations[0] : "No description";
              return `- **${f.name}** (Status: ${findingStatus}): ${findingObs}`;
            });
            
            const resolvedFindingsText = findingsText ? 
              await Promise.all(findingsText).then(texts => texts.join("\n")) : 
              "No findings recorded yet";
            
            contextMessage = `# Qualitative Research Project Context: ${entityName}

## Project Details
- **Status**: ${status}
${priorityText}

## Observations
${observationsList}

## Research Design
${methodologyText}

## Research Questions
${questionsText}

## Data Collection Stats
- **Participants**: ${participantCount}
- **Interviews**: ${interviewCount}
- **Observations**: ${observationCount}
- **Documents**: ${documentCount}

## Recent Interviews
${resolvedInterviewsText}

## Analysis Progress
### Themes
${resolvedThemesText}

## Findings
${resolvedFindingsText}`;
          } 
          else if (entityType === "participant") {
            // Get participant profile
            const participantProfile = await knowledgeGraphManager.getParticipantProfile(entityName);
            
            // Get status and priority using the relation-based approach
            const status = await knowledgeGraphManager.getEntityStatus(entityName) || "Unknown";
            const priority = await knowledgeGraphManager.getEntityPriority(entityName);
            const priorityText = priority ? `- **Priority**: ${priority}` : "";
            
            // Format observations
            const observationsList = entity.observations.length > 0 
              ? entity.observations.map(obs => `- ${obs}`).join("\n")
              : "No observations";
            
            // Format demographics without relying on patterns
            const demographics = participantProfile.demographics?.map((d: string) => `- ${d}`).join("\n") || "No demographic information available";
            
            // Format interviews with status from relations
            const interviewsText = participantProfile.interviews?.map(async (i: Entity) => {
              const interviewStatus = await knowledgeGraphManager.getEntityStatus(i.name) || "unknown";
              return `- **${i.name}** (Status: ${interviewStatus})`;
            });
            
            const resolvedInterviewsText = interviewsText ? 
              await Promise.all(interviewsText).then(texts => texts.join("\n")) : 
              "No interviews recorded";
            
            // Format observations with status from relations
            const observationsText = participantProfile.observations?.map(async (o: Entity) => {
              const observationStatus = await knowledgeGraphManager.getEntityStatus(o.name) || "unknown";
              return `- **${o.name}** (Status: ${observationStatus})`;
            });
            
            const resolvedObservationsText = observationsText ? 
              await Promise.all(observationsText).then(texts => texts.join("\n")) : 
              "No observations recorded";
            
            // Format quotes
            const quotesText = participantProfile.quotes?.map((q: Entity) => {
              // Show the full quote
              const quote = q.observations.find(o => !o.startsWith("source:") && !o.startsWith("context:"));
              const source = q.observations.find(o => o.startsWith("source:"))?.substring(7) || "Unknown source";
              return `- "${quote || "No text available"}" (Source: ${source})`;
            }).join("\n") || "No quotes recorded";
            
            // Format memos
            const memosText = participantProfile.memos?.map(async (m: Entity) => {
              const memoStatus = await knowledgeGraphManager.getEntityStatus(m.name) || "unknown";
              const topic = m.observations.find(o => o.startsWith("topic:"))?.substring(6) || "Untitled";
              return `- **${topic}** (Status: ${memoStatus})`;
            });
            
            const resolvedMemosText = memosText ? 
              await Promise.all(memosText).then(texts => texts.join("\n")) : 
              "No memos about this participant";
            
            contextMessage = `# Participant Context: ${entityName}

## Status and Priority
- **Status**: ${status}
${priorityText}

## Observations
${observationsList}

## Demographics
${demographics}

## Interviews
${resolvedInterviewsText}

## Observations
${resolvedObservationsText}

## Quotes
${quotesText}

## Research Memos
${resolvedMemosText}`;
          }
          else if (entityType === "interview") {
            // Find which project this interview belongs to
            let projectName = 'Unknown project';
            
            for (const relation of entityGraph.relations) {
              if (relation.relationType === 'part_of' && relation.from === entityName) {
                const project = entityGraph.entities.find(e => e.name === relation.to && e.entityType === 'project');
                if (project) {
                  projectName = project.name;
                  break;
                }
              }
            }
            
            // Get status and priority using the relation-based approach
            const status = await knowledgeGraphManager.getEntityStatus(entityName) || "Unknown";
            const priority = await knowledgeGraphManager.getEntityPriority(entityName);
            const priorityText = priority ? `- **Priority**: ${priority}` : "";
            
            // Format observations
            const observationsList = entity.observations.length > 0 
              ? entity.observations.map(obs => `- ${obs}`).join("\n")
              : "No observations";
            
            // Get interview details without parsing date
            const participant = entity.observations.find(o => o.startsWith("participant:"))?.substring(12) || "Unknown";
            
            // Find codes applied to this interview and include their status
            const codesWithStatus = [];
            
            for (const relation of entityGraph.relations) {
              if (relation.relationType === 'codes' && relation.to === entityName) {
                const code = entityGraph.entities.find(e => e.name === relation.from && e.entityType === 'code');
                if (code) {
                  const codeStatus = await knowledgeGraphManager.getEntityStatus(code.name) || "unknown";
                  codesWithStatus.push({
                    code,
                    status: codeStatus
                  });
                }
              }
            }
            
            const codesText = codesWithStatus.map(c => 
              `- **${c.code.name}** (Status: ${c.status}): ${c.code.observations[0] || "No description"}`
            ).join("\n") || "No codes applied yet";
            
            // Find quotes from this interview
            const quotes = [];
            for (const relation of entityGraph.relations) {
              if (relation.relationType === 'contains' && relation.from === entityName) {
                const quote = entityGraph.entities.find(e => e.name === relation.to && e.entityType === 'quote');
                if (quote) {
                  quotes.push(quote);
                }
              }
            }
            
            const quotesText = quotes.map(q => {
              // Get the full quote text
              const quoteText = q.observations.find(o => !o.startsWith("context:") && !o.startsWith("speaker:")) || "No text";
              return `- "${quoteText}"`;
            }).join("\n") || "No notable quotes recorded";
            
            contextMessage = `# Interview Context: ${entityName}

## Overview
- **Project**: ${projectName}
- **Participant**: ${participant}
- **Status**: ${status}
${priorityText}

## Observations
${observationsList}

## Applied Codes
${codesText}

## Notable Quotes
${quotesText}`;
          }
          else if (entityType === "code") {
            // Get coded data for this code
            const codedData = await knowledgeGraphManager.getCodedData(entityName);
            
            // Format code context
            const definition = entity.observations.find(o => !o.startsWith("status:") && !o.startsWith("created:"));
            const created = entity.observations.find(o => o.startsWith("created:"))?.substring(8) || "Unknown";
            const status = entity.observations.find(o => o.startsWith("status:"))?.substring(7) || "active";
            
            // Format code groups
            const codeGroupsText = codedData.codeGroups?.map((group: Entity) => {
              const description = group.observations.find(o => !o.startsWith("created:"));
              return `- **${group.name}**: ${description || "No description"}`;
            }).join("\n") || "Not part of any code groups";
            
            // Format quotes
            const quotesText = codedData.quotes?.map((quote: Entity) => {
              const source = quote.observations.find(o => o.startsWith("source:"))?.substring(7) || "Unknown source";
              const text = quote.observations.find(o => !o.startsWith("source:") && !o.startsWith("context:"));
              return `- "${text || "No text"}" (Source: ${source})`;
            }).join("\n") || "No quotes tagged with this code";
            
            // Format sources
            const sourcesText = codedData.sources?.map((source: Entity) => {
              return `- **${source.name}** (${source.entityType})`;
            }).join("\n") || "No sources found";
            
            // Format themes
            const themesText = codedData.themes?.map((theme: Entity) => {
              const description = theme.observations.find(o => !o.startsWith("status:") && !o.startsWith("created:"));
              return `- **${theme.name}**: ${description || "No description"}`;
            }).join("\n") || "Not associated with any themes";
            
            // Get co-occurrence data
            let cooccurrenceData;
            try {
              cooccurrenceData = await knowledgeGraphManager.getCodeCooccurrence(entityName);
              
              // Format co-occurrence
              const cooccurrenceText = cooccurrenceData.cooccurringCodes?.map((c: any) => {
                return `- **${c.code.name}** (${c.count} co-occurrences)`;
              }).slice(0, 5).join("\n") || "No code co-occurrence data";
              
              contextMessage = `# Code Context: ${entityName}

## Code Details
- **Definition**: ${definition || "No definition provided"}
- **Created**: ${created}
- **Status**: ${status}
- **Items Coded**: ${codedData.quotes?.length || 0}

## Part of Code Groups
${codeGroupsText}

## Supporting Themes
${themesText}

## Top Co-occurring Codes
${cooccurrenceText}

## Example Quotes
${quotesText}

## Used in These Sources
${sourcesText}`;
            } catch (error) {
              contextMessage = `# Code Context: ${entityName}

## Code Details
- **Definition**: ${definition || "No definition provided"}
- **Created**: ${created}
- **Status**: ${status}
- **Items Coded**: ${codedData.quotes?.length || 0}

## Part of Code Groups
${codeGroupsText}

## Supporting Themes
${themesText}

## Example Quotes
${quotesText}

## Used in These Sources
${sourcesText}`;
            }
          }
          else if (entityType === "theme") {
            // Get thematic analysis data
            let projectName = "";
            
            // Find which project this theme belongs to
            for (const relation of entityGraph.relations) {
              if (relation.relationType === 'part_of' && relation.from === entityName) {
                const project = entityGraph.entities.find(e => e.name === relation.to && e.entityType === 'project');
                if (project) {
                  projectName = project.name;
                  break;
                }
              }
            }
            
            let thematicAnalysis;
            try {
              thematicAnalysis = await knowledgeGraphManager.getThematicAnalysis(projectName);
              
              // Find this theme in the analysis
              const themeAnalysis = thematicAnalysis.themes?.find((t: any) => t.theme.name === entityName);
              
              if (themeAnalysis) {
                const description = entity.observations.find(o => !o.startsWith("created:") && !o.startsWith("status:"));
                const status = entity.observations.find(o => o.startsWith("status:"))?.substring(7) || "emerging";
                const created = entity.observations.find(o => o.startsWith("created:"))?.substring(8) || "Unknown";
                
                // Format codes
                const codesText = themeAnalysis.codes?.map((code: Entity) => {
                  const definition = code.observations.find(o => !o.startsWith("status:") && !o.startsWith("created:"));
                  return `- **${code.name}**: ${definition || "No definition"}`;
                }).join("\n") || "No supporting codes";
                
                // Format supporting quotes
                const quotesText = themeAnalysis.supportingData?.flatMap((codeData: any) => 
                  codeData.quotes.map((quote: Entity) => {
                    const text = quote.observations.find(o => !o.startsWith("source:") && !o.startsWith("context:"));
                    return `- "${text || "No text"}" [Code: ${codeData.code.name}]`;
                  })
                ).slice(0, 10).join("\n") || "No supporting quotes";
                
                // Format memos
                const memosText = themeAnalysis.memos?.map((memo: Entity) => {
                  const date = memo.observations.find(o => o.startsWith("date:"))?.substring(5) || "Unknown date";
                  const topic = memo.observations.find(o => o.startsWith("topic:"))?.substring(6) || "Untitled";
                  const content = memo.observations.find(o => !o.startsWith("date:") && !o.startsWith("topic:"));
                  return `- **${topic}** (${date}): ${content ? (content.length > 100 ? content.substring(0, 100) + "..." : content) : "No content"}`;
                }).join("\n") || "No analytical memos about this theme";
                
                contextMessage = `# Theme Context: ${entityName}

## Theme Details
- **Description**: ${description || "No description provided"}
- **Status**: ${status}
- **Created**: ${created}
- **Project**: ${projectName || "Not associated with a specific project"}

## Supporting Codes
${codesText}

## Example Supporting Quotes
${quotesText}

## Analytical Memos
${memosText}`;
              } else {
                const description = entity.observations.find(o => !o.startsWith("created:") && !o.startsWith("status:"));
                const status = entity.observations.find(o => o.startsWith("status:"))?.substring(7) || "emerging";
                
                contextMessage = `# Theme Context: ${entityName}

## Theme Details
- **Description**: ${description || "No description provided"}
- **Status**: ${status}
- **Project**: ${projectName || "Not associated with a specific project"}

No detailed analysis available for this theme.`;
              }
            } catch (error) {
              const description = entity.observations.find(o => !o.startsWith("created:") && !o.startsWith("status:"));
              const status = entity.observations.find(o => o.startsWith("status:"))?.substring(7) || "emerging";
              
              contextMessage = `# Theme Context: ${entityName}

## Theme Details
- **Description**: ${description || "No description provided"}
- **Status**: ${status}
- **Project**: ${projectName || "Not associated with a specific project"}

No detailed analysis available for this theme.`;
            }
          }
          else if (entityType === "memo") {
            // Get memo details
            const topic = entity.observations.find(o => o.startsWith("topic:"))?.substring(6) || "Untitled";
            const date = entity.observations.find(o => o.startsWith("date:"))?.substring(5) || "Unknown date";
            const content = entity.observations.find(o => !o.startsWith("topic:") && !o.startsWith("date:"));
            
            // Find what this memo reflects on
            const relatedEntities: Entity[] = [];
            for (const relation of entityGraph.relations) {
              if (relation.relationType === 'reflects_on' && relation.from === entityName) {
                const relatedEntity = entityGraph.entities.find(e => e.name === relation.to);
                if (relatedEntity) {
                  relatedEntities.push(relatedEntity);
                }
              }
            }
            
            // Find which project this memo belongs to
            let projectName = 'Unknown project';
            for (const relation of entityGraph.relations) {
              if (relation.relationType === 'part_of' && relation.from === entityName) {
                const project = entityGraph.entities.find(e => e.name === relation.to && e.entityType === 'project');
                if (project) {
                  projectName = project.name;
                  break;
                }
              }
            }
            
            // Format related entities
            const relatedText = relatedEntities.map((e: Entity) => `- **${e.name}** (${e.entityType})`).join("\n") || "Not specifically linked to any entities";
            
            contextMessage = `# Memo Context: ${entityName}

## Memo Details
- **Topic**: ${topic}
- **Date**: ${date}
- **Project**: ${projectName}

## Content
${content || "No content available"}

## Related Entities
${relatedText}`;
          }
          else if (entityType === "researchQuestion") {
            // Find which project this research question belongs to
            let projectName = 'Unknown project';
            for (const relation of entityGraph.relations) {
              if (relation.relationType === 'part_of' && relation.from === entityName) {
                const project = entityGraph.entities.find(e => e.name === relation.to && e.entityType === 'project');
                if (project) {
                  projectName = project.name;
                  break;
                }
              }
            }
            
            // Get research question analysis
            let analysisData;
            try {
              analysisData = await knowledgeGraphManager.getResearchQuestionAnalysis(projectName);
              
              // Find this question in the analysis
              const questionAnalysis = analysisData.researchQuestions?.find((q: any) => q.question.name === entityName);
              
              if (questionAnalysis) {
                // Format findings
                const findingsText = questionAnalysis.findings?.map((finding: Entity) => {
                  const status = finding.observations.find(o => o.startsWith("status:"))?.substring(7) || "preliminary";
                  const description = finding.observations.find(o => !o.startsWith("status:") && !o.startsWith("created:"));
                  return `- **${finding.name}** (${status}): ${description || "No description"}`;
                }).join("\n") || "No findings recorded yet";
                
                // Format themes
                const themesText = questionAnalysis.themes?.map((theme: Entity) => {
                  const description = theme.observations.find(o => !o.startsWith("status:") && !o.startsWith("created:"));
                  return `- **${theme.name}**: ${description || "No description"}`;
                }).join("\n") || "No themes associated with this question";
                
                // Format quotes
                const quotesText = questionAnalysis.quotes?.map((quote: Entity) => {
                  const source = quote.observations.find(o => o.startsWith("source:"))?.substring(7) || "Unknown source";
                  const text = quote.observations.find(o => !o.startsWith("source:") && !o.startsWith("context:"));
                  return `- "${text || "No text"}" (Source: ${source})`;
                }).slice(0, 5).join("\n") || "No direct quotes addressing this question";
                
                contextMessage = `# Research Question Context: ${entityName}

## Question
${entity.observations.find(o => !o.startsWith("created:")) || entityName}

## Project
${projectName}

## Findings
${findingsText}

## Related Themes
${themesText}

## Supporting Quotes
${quotesText}`;
              } else {
                contextMessage = `# Research Question Context: ${entityName}

## Question
${entity.observations.find(o => !o.startsWith("created:")) || entityName}

## Project
${projectName}

No analysis data available for this research question.`;
              }
            } catch (error) {
              contextMessage = `# Research Question Context: ${entityName}

## Question
${entity.observations.find(o => !o.startsWith("created:")) || entityName}

## Project
${projectName}

No analysis data available for this research question.`;
            }
          }
          else {
            // Generic entity context for other entity types
            // Get related entities
            const relatedEntitiesData = await knowledgeGraphManager.getRelatedEntities(entityName);
            
            // Format observations
            const observationsText = entity.observations.map((obs: string) => `- ${obs}`).join("\n") || "No observations";
            
            // Format related entities
            const relatedText = Object.entries(relatedEntitiesData.related || {}).map(([relation, entities]) => {
              const entitiesList = (entities as any[]).map(e => `- **${e.name}** (${e.entityType})`).join("\n");
              return `### ${relation} (${(entities as any[]).length})\n${entitiesList}`;
            }).join("\n\n") || "No related entities found";
            
            contextMessage = `# Entity Context: ${entityName} (${entityType})

## Observations
${observationsText}

## Related Entities
${relatedText}`;
          }
          
          return {
            content: [{
              type: "text",
              text: contextMessage
            }]
          };
        } catch (error) {
          return {
            content: [{
              type: "text",
              text: JSON.stringify({ 
                success: false,
                error: error instanceof Error ? error.message : String(error)
              }, null, 2)
            }]
          };
        }
      }
    );

    // Helper function to process each stage of endsession
    async function processStage(params: {
      sessionId: string;
      stage: string;
      stageNumber: number;
      totalStages: number;
      analysis?: string;
      stageData?: any;
      nextStageNeeded: boolean;
      isRevision?: boolean;
      revisesStage?: number;
    }, previousStages: any[]): Promise<any> {
      // Process based on the stage
      switch (params.stage) {
        case "summary":
          // Process summary stage
          return {
            stage: "summary",
            stageNumber: params.stageNumber,
            analysis: params.analysis || "",
            stageData: params.stageData || { 
              summary: "",
              duration: "",
              project: ""
            },
            completed: !params.nextStageNeeded
          };
          
        case "interviewData":
          // Process interview data stage
          return {
            stage: "interviewData",
            stageNumber: params.stageNumber,
            analysis: params.analysis || "",
            stageData: params.stageData || { interviews: [] },
            completed: !params.nextStageNeeded
          };
          
        case "memos":
          // Process memos stage
          return {
            stage: "memos",
            stageNumber: params.stageNumber,
            analysis: params.analysis || "",
            stageData: params.stageData || { memos: [] },
            completed: !params.nextStageNeeded
          };
          
        case "codingActivity":
          // Process coding activity stage
          return {
            stage: "codingActivity",
            stageNumber: params.stageNumber,
            analysis: params.analysis || "",
            stageData: params.stageData || { codes: [] },
            completed: !params.nextStageNeeded
          };
          
        case "themes":
          // Process themes stage
          return {
            stage: "themes",
            stageNumber: params.stageNumber,
            analysis: params.analysis || "",
            stageData: params.stageData || { themes: [] },
            completed: !params.nextStageNeeded
          };
          
        case "projectStatus":
          // Process project status stage
          return {
            stage: "projectStatus",
            stageNumber: params.stageNumber,
            analysis: params.analysis || "",
            stageData: params.stageData || { 
              projectStatus: "",
              projectObservation: ""
            },
            completed: !params.nextStageNeeded
          };
          
        case "assembly":
          // Final assembly stage - compile all arguments for end-session
          return {
            stage: "assembly",
            stageNumber: params.stageNumber,
            analysis: "Final assembly of end-session arguments",
            stageData: assembleEndSessionArgs(previousStages),
            completed: true
          };
          
        default:
          throw new Error(`Unknown stage: ${params.stage}`);
      }
    }

    // Helper function to assemble the final end-session arguments
    function assembleEndSessionArgs(stages: any[]): any {
      const summaryStage = stages.find(s => s.stage === "summary");
      const interviewDataStage = stages.find(s => s.stage === "interviewData");
      const memosStage = stages.find(s => s.stage === "memos");
      const codingActivityStage = stages.find(s => s.stage === "codingActivity");
      const themesStage = stages.find(s => s.stage === "themes");
      const projectStatusStage = stages.find(s => s.stage === "projectStatus");
      
      return {
        summary: summaryStage?.stageData?.summary || "",
        duration: summaryStage?.stageData?.duration || "unknown",
        project: summaryStage?.stageData?.project || "",
        interviewData: JSON.stringify(interviewDataStage?.stageData?.interviews || []),
        newMemos: JSON.stringify(memosStage?.stageData?.memos || []),
        codingActivity: JSON.stringify(codingActivityStage?.stageData?.codes || []),
        newThemes: JSON.stringify(themesStage?.stageData?.themes || []),
        projectStatus: projectStatusStage?.stageData?.projectStatus || "",
        projectObservation: projectStatusStage?.stageData?.projectObservation || ""
      };
    }

    /**
     * End session by processing all stages and recording the final results.
     * Only use this tool if the user asks for it.
     * 
     * Usage examples:
     * 
     * 1. Starting the end session process with the summary stage:
     * {
     *   "sessionId": "qual_1234567890_abc123",  // From startsession
     *   "stage": "summary",
     *   "stageNumber": 1,
     *   "totalStages": 6, 
     *   "analysis": "Analyzed progress on the interview data coding",
     *   "stageData": {
     *     "summary": "Completed initial coding of participant interviews",
     *     "duration": "3 hours",
     *     "project": "Health Behavior Study"  // Project name
     *   },
     *   "nextStageNeeded": true,  // More stages coming
     *   "isRevision": false
     * }
     * 
     * 2. Middle stage for themes:
     * {
     *   "sessionId": "qual_1234567890_abc123",
     *   "stage": "themes",
     *   "stageNumber": 2,
     *   "totalStages": 6,
     *   "analysis": "Identified emerging themes",
     *   "stageData": {
     *     "themes": [
     *       { "name": "Perceived Barriers", "codes": ["time_constraints", "financial_concerns"], "description": "Factors preventing healthy behaviors" },
     *       { "name": "Social Support", "codes": ["family_influence", "peer_encouragement"], "description": "External motivation from relationships" }
     *     ]
     *   },
     *   "nextStageNeeded": true,
     *   "isRevision": false
     * }
     * 
     * 3. Final assembly stage:
     * {
     *   "sessionId": "qual_1234567890_abc123",
     *   "stage": "assembly",
     *   "stageNumber": 6,
     *   "totalStages": 6,
     *   "nextStageNeeded": false,  // This completes the session
     *   "isRevision": false
     * }
     */
    server.tool(
      "endsession",
      toolDescriptions["endsession"],
      {
        sessionId: z.string().describe("The unique session identifier obtained from startsession"),
        stage: z.string().describe("Current stage of analysis: 'summary', 'themes', 'codes', 'memos', 'participantInsights', or 'assembly'"),
        stageNumber: z.number().int().positive().describe("The sequence number of the current stage (starts at 1)"),
        totalStages: z.number().int().positive().describe("Total number of stages in the workflow (typically 6 for standard workflow)"),
        analysis: z.string().optional().describe("Text analysis or observations for the current stage"),
        stageData: z.record(z.string(), z.any()).optional().describe(`Stage-specific data structure - format depends on the stage type:
        - For 'summary' stage: { summary: "Session summary text", duration: "3 hours", project: "Project Name" }
        - For 'themes' stage: { themes: [{ name: "Theme1", codes: ["code1", "code2"], description: "Theme description" }] }
        - For 'codes' stage: { codes: [{ name: "Code1", description: "Code meaning", quotes: ["Quote text"] }] }
        - For 'memos' stage: { memos: [{ title: "Memo title", content: "Detailed memo text", tags: ["tag1", "tag2"] }] }
        - For 'participantInsights' stage: { insights: [{ participant: "P1", observation: "Key insight about participant" }] }
        - For 'assembly' stage: no stageData needed - automatic assembly of previous stages`),
        nextStageNeeded: z.boolean().describe("Whether additional stages are needed after this one (false for final stage)"),
        isRevision: z.boolean().optional().describe("Whether this is revising a previous stage"),
        revisesStage: z.number().int().positive().optional().describe("If revising, which stage number is being revised")
      },
      async (params) => {
        try {
          // Load session states from persistent storage
          const sessionStates = await loadSessionStates();
          
          // Validate session ID
          if (!sessionStates.has(params.sessionId)) {
            return {
              content: [{
                type: "text",
                text: JSON.stringify({ 
                  success: false,
                  error: `Session with ID ${params.sessionId} not found. Please start a new session with startsession.`
                }, null, 2)
              }]
            };
          }
          
          // Get or initialize session state
          let sessionState = sessionStates.get(params.sessionId) || [];
          
          // Process the current stage
          const stageResult = await processStage(params, sessionState);
          
          // Store updated state
          if (params.isRevision && params.revisesStage) {
            // Find the analysis stages in the session state
            const analysisStages = sessionState.filter(item => item.type === 'analysis_stage') || [];
            
            if (params.revisesStage <= analysisStages.length) {
              // Replace the revised stage
              analysisStages[params.revisesStage - 1] = {
                type: 'analysis_stage',
                ...stageResult
              };
            } else {
              // Add as a new stage
              analysisStages.push({
                type: 'analysis_stage',
                ...stageResult
              });
            }
            
            // Update the session state with the modified analysis stages
            sessionState = [
              ...sessionState.filter(item => item.type !== 'analysis_stage'),
              ...analysisStages
            ];
          } else {
            // Add new stage
            sessionState.push({
              type: 'analysis_stage',
              ...stageResult
            });
          }
          
          // Update in persistent storage
          sessionStates.set(params.sessionId, sessionState);
          await saveSessionStates(sessionStates);
          
          // Check if this is the final assembly stage and no more stages are needed
          if (params.stage === "assembly" && !params.nextStageNeeded) {
            // Get the assembled arguments
            const args = stageResult.stageData;
            
            try {
              // Parse arguments
              const summary = args.summary;
              const duration = args.duration;
              const project = args.project;
              const interviewData = args.interviewData ? JSON.parse(args.interviewData) : [];
              const newMemos = args.newMemos ? JSON.parse(args.newMemos) : [];
              const codingActivity = args.codingActivity ? JSON.parse(args.codingActivity) : [];
              const newThemes = args.newThemes ? JSON.parse(args.newThemes) : [];
              const projectStatus = args.projectStatus;
              const projectObservation = args.projectObservation;
              
              // Update project status using the relation-based approach
              try {
                // Set the project status using our helper method
                if (projectStatus) {
                  await knowledgeGraphManager.setEntityStatus(project, projectStatus);
                }
                
                // Add observation if provided
                if (projectObservation) {
                  await knowledgeGraphManager.addObservations([{
                    entityName: project,
                    contents: [projectObservation]
                  }]);
                }
              } catch (error) {
                console.error(`Error updating status for project ${project}:`, error);
              }
              
              // Record session completion in persistent storage
              sessionState.push({
                type: 'session_completed',
                timestamp: new Date().toISOString(),
                project
              });
              
              sessionStates.set(params.sessionId, sessionState);
              await saveSessionStates(sessionStates);
              
              // Prepare the summary message
              const summaryMessage = `# Qualitative Research Session Recorded

I've recorded your research session focusing on the ${project} project.

## Session Summary
${summary}

${interviewData.length > 0 ? `## Interviews Conducted
${interviewData.map((i: {participant: string, notes: string}) => 
  `- Interview with ${i.participant}`
).join('\n')}` : "No interviews were recorded."}

${newMemos.length > 0 ? `## Research Memos Created
${newMemos.map((m: {topic: string, content: string}) => `- ${m.topic}`).join('\n')}` : "No memos were created."}

${codingActivity.length > 0 ? `## Coding Activity
${codingActivity.map((c: {code: string, dataItem: string, note?: string}) => 
  `- Coded ${c.dataItem} with "${c.code}"${c.note ? `: ${c.note}` : ''}`
).join('\n')}` : "No coding was performed."}

${newThemes.length > 0 ? `## Themes Identified
${newThemes.map((t: {name: string, description: string}) => `- ${t.name}: ${t.description}`).join('\n')}` : "No themes were identified."}

## Project Status
Project ${project} has been updated to: ${projectStatus}

Would you like me to perform any additional updates to your qualitative research knowledge graph?`;
              
              // Return the final result with the session recorded message
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({
                    success: true,
                    stageCompleted: params.stage,
                    nextStageNeeded: false,
                    stageResult: stageResult,
                    sessionRecorded: true,
                    summaryMessage: summaryMessage
                  }, null, 2)
                }]
              };
            } catch (error) {
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({
                    success: false,
                    error: `Error assembling end-session arguments: ${error instanceof Error ? error.message : String(error)}`
                  }, null, 2)
                }]
              };
            }
          } else {
            // This is not the final stage or more stages are needed
            // Return intermediate result
            return {
              content: [{
                type: "text",
                text: JSON.stringify({
                  success: true,
                  stageCompleted: params.stage,
                  nextStageNeeded: params.nextStageNeeded,
                  stageResult: stageResult,
                  endSessionArgs: params.stage === "assembly" ? stageResult.stageData : null
                }, null, 2)
              }]
            };
          }
        } catch (error) {
          return {
            content: [{
              type: "text",
              text: JSON.stringify({
                success: false,
                error: `Error recording qualitative research session: ${error instanceof Error ? error.message : String(error)}`
              }, null, 2)
            }]
          };
        }
      }
    );

    /**
     * Start a new session for qualitative research. Returns session ID, recent sessions, active projects, sample participants, top codes, and recent memos.
     * The output allows the user to easily choose what to focus on and which specific context to load.
     */
    server.tool(
      "startsession",
      toolDescriptions["startsession"],
      {},
      async () => {
        try {
          // Generate a unique session ID
          const sessionId = generateSessionId();
          
          // Get recent sessions from persistent storage
          const sessionStates = await loadSessionStates();

          // Initialize the session state
          sessionStates.set(sessionId, []);
          await saveSessionStates(sessionStates);
          
          // Convert sessions map to array and retrieve the most recent sessions
          const recentSessions = Array.from(sessionStates.entries())
            .map(([id, stages]) => {
              // Extract summary data from the first stage (if it exists)
              const summaryStage = stages.find(s => s.stage === "summary");
              return {
                id,
                project: summaryStage?.stageData?.project || "Unknown project",
                summary: summaryStage?.stageData?.summary || "No summary available"
              };
            })
            .slice(0, 3); // Default to showing 3 recent sessions
          
          // Query for all research projects and filter by status
          const projectsQuery = await knowledgeGraphManager.searchNodes("entityType:project");
          const projects = [];
          
          // Filter for active projects based on has_status relation
          for (const project of projectsQuery.entities) {
            const status = await knowledgeGraphManager.getEntityStatus(project.name);
            if (status === "active" || status === "in_progress" || status === "data_collection" || status === "analysis") {
              projects.push(project);
            }
          }
          
          // Query for a sample of participants
          const participantsQuery = await knowledgeGraphManager.searchNodes("entityType:participant");
          const participants = participantsQuery.entities.slice(0, 5); // Limit to 5 participants for initial display
          
          // Get all codes
          const codesQuery = await knowledgeGraphManager.searchNodes("entityType:code");
          const codes = codesQuery.entities.slice(0, 10); // Top 10 codes
          
          // Get recent memos
          const memosQuery = await knowledgeGraphManager.searchNodes("entityType:memo");
          const memos = memosQuery.entities.slice(0, 3); // Most recent 3 memos
          
          // Format the context information using entity-relation approach
          const projectsText = await Promise.all(projects.map(async p => {
            const status = await knowledgeGraphManager.getEntityStatus(p.name) || "Unknown";
            const priority = await knowledgeGraphManager.getEntityPriority(p.name);
            const priorityText = priority ? `, Priority: ${priority}` : "";
            
            // Show truncated preview of first observation
            const preview = p.observations.length > 0 
              ? `${p.observations[0].substring(0, 60)}${p.observations[0].length > 60 ? '...' : ''}`
              : "No description";
              
            return `- **${p.name}** (Status: ${status}${priorityText}): ${preview}`;
          }));
          
          const participantsText = await Promise.all(participants.map(async p => {
            const status = await knowledgeGraphManager.getEntityStatus(p.name) || "Active";
            
            // Show truncated preview of first observation for demographics
            const preview = p.observations.length > 0 
              ? `${p.observations[0].substring(0, 60)}${p.observations[0].length > 60 ? '...' : ''}`
              : "No demographics";
              
            return `- **${p.name}** (Status: ${status}): ${preview}`;
          }));
          
          const codesText = await Promise.all(codes.map(async c => {
            const status = await knowledgeGraphManager.getEntityStatus(c.name) || "initial";
            
            // Show truncated preview of first observation for description
            const preview = c.observations.length > 0 
              ? `${c.observations[0].substring(0, 60)}${c.observations[0].length > 60 ? '...' : ''}`
              : "No description";
              
            return `- **${c.name}** (Status: ${status}): ${preview}`;
          }));
          
          const memosText = await Promise.all(memos.map(async m => {
            const status = await knowledgeGraphManager.getEntityStatus(m.name) || "draft";
            
            // Show truncated preview of first observation for content
            const preview = m.observations.length > 0 
              ? `${m.observations[0].substring(0, 60)}${m.observations[0].length > 60 ? '...' : ''}`
              : "No content";
              
            return `- **${m.name}** (Status: ${status}): ${preview}`;
          }));
          
          const sessionsText = recentSessions.map(s => {
            return `- ${s.project} - ${s.summary.substring(0, 60)}${s.summary.length > 60 ? '...' : ''}`;
          }).join("\n");
          
          return {
            content: [{
              type: "text",
              text: `# Choose what to focus on in this session

## Session ID
\`${sessionId}\`

## Recent Research Sessions
${sessionsText || "No recent sessions found."}

## Active Research Projects
${projectsText.join("\n") || "No active projects found."}

## Sample Participants
${participantsText.join("\n") || "No participants found."}

## Top Codes
${codesText.join("\n") || "No codes found."}

## Recent Memos
${memosText.join("\n") || "No memos found."}

To load specific context, use the \`loadcontext\` tool with the entity name and session ID - ${sessionId}`
            }]
          };
        } catch (error) {
          return {
            content: [{
              type: "text",
              text: JSON.stringify({ 
                success: false,
                error: error instanceof Error ? error.message : String(error)
              }, null, 2)
            }]
          };
        }
      }
    );

    /**
     * Create new entities, relations, and observations.
     */
    server.tool(
      "buildcontext",
      toolDescriptions["buildcontext"],
      {
        type: z.enum(["entities", "relations", "observations"]).describe("Type of creation operation: 'entities', 'relations', or 'observations'"),
        data: z.array(z.any()).describe("Data for the creation operation, structure varies by type but must be an array")
      },
      async ({ type, data }) => {
        try {
          let result;
          
          switch (type) {
            case "entities":
              // Validate entity types
              for (const entity of data) {
                if (!validateEntityType(entity.entityType)) {
                  throw new Error(`Invalid entity type: ${entity.entityType}`);
                }
              }
              
              // Ensure entities match the Entity interface
              const typedEntities: Entity[] = data.map((e: any) => ({
                name: e.name,
                entityType: e.entityType,
                observations: e.observations
              }));
              result = await knowledgeGraphManager.createEntities(typedEntities);
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, created: result }, null, 2)
                }]
              };
              
            case "relations":
              // Validate relation types
              for (const relation of data) {
                if (!validateRelationType(relation.relationType)) {
                  throw new Error(`Invalid relation type: ${relation.relationType}`);
                }
              }
              
              // Ensure relations match the Relation interface
              const typedRelations: Relation[] = data.map((r: any) => ({
                from: r.from,
                to: r.to,
                relationType: r.relationType
              }));
              result = await knowledgeGraphManager.createRelations(typedRelations);
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, created: result }, null, 2)
                }]
              };
              
            case "observations":
              // For qualitative researcher domain, addObservations takes an array
              // Ensure observations match the required interface
              const typedObservations: { entityName: string; contents: string[] }[] = 
                Array.isArray(data) ? data.map((o: any) => ({
                  entityName: o.entityName,
                  contents: Array.isArray(o.contents) ? o.contents : 
                           Array.isArray(o.observations) ? o.observations : []
                })) : [data];
              
              result = await knowledgeGraphManager.addObservations(typedObservations);
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, added: result }, null, 2)
                }]
              };
              
            default:
              throw new Error(`Invalid type: ${type}. Must be 'entities', 'relations', or 'observations'.`);
          }
        } catch (error) {
          return {
            content: [{
              type: "text",
              text: JSON.stringify({ 
                success: false,
                error: error instanceof Error ? error.message : String(error)
              }, null, 2)
            }]
          };
        }
      }
    );
    
    /**
     * Delete entities, relations, or observations.
     */
    server.tool(
      "deletecontext",
      toolDescriptions["deletecontext"],
      {
        type: z.enum(["entities", "relations", "observations"]).describe("Type of deletion operation: 'entities', 'relations', or 'observations'"),
        data: z.array(z.any()).describe("Data for the deletion operation, structure varies by type but must be an array")
      },
      async ({ type, data }) => {
        try {
          switch (type) {
            case "entities":
              await knowledgeGraphManager.deleteEntities(data);
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, message: `Deleted ${data.length} entities` }, null, 2)
                }]
              };
              
            case "relations":
              // Ensure relations match the Relation interface
              const typedRelations: Relation[] = data.map((r: any) => ({
                from: r.from,
                to: r.to,
                relationType: r.relationType
              }));
              await knowledgeGraphManager.deleteRelations(typedRelations);
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, message: `Deleted ${data.length} relations` }, null, 2)
                }]
              };
              
            case "observations":
              // Ensure deletions match the required interface
              const typedDeletions: { entityName: string; observations: string[] }[] = data.map((d: any) => ({
                entityName: d.entityName,
                observations: d.observations
              }));
              await knowledgeGraphManager.deleteObservations(typedDeletions);
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, message: `Deleted observations from ${data.length} entities` }, null, 2)
                }]
              };
              
            default:
              throw new Error(`Invalid type: ${type}. Must be 'entities', 'relations', or 'observations'.`);
          }
        } catch (error) {
          return {
            content: [{
              type: "text",
              text: JSON.stringify({ 
                success: false,
                error: error instanceof Error ? error.message : String(error)
              }, null, 2)
            }]
          };
        }
      }
    );
    
    /**
     * Get information about the graph, search for nodes, open nodes, get project overview, get participant profile, get codes, get themes, get transcript, get memo, get analysis, get codebook, or get related entities.
     */
    server.tool(
      "advancedcontext",
      toolDescriptions["advancedcontext"],
      {
        type: z.enum([
          "graph", 
          "search", 
          "nodes", 
          "project", 
          "participant", 
          "codes", 
          "themes", 
          "transcript", 
          "memo", 
          "analysis", 
          "codebook", 
          "related"
        ]).describe("Type of get operation"),
        params: z.record(z.string(), z.any()).describe("Parameters for the get operation, structure varies by type")
      },
      async ({ type, params }) => {
        try {
          let result;
          
          switch (type) {
            case "graph":
              result = await knowledgeGraphManager.readGraph();
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, graph: result }, null, 2)
                }]
              };
              
            case "search":
              result = await knowledgeGraphManager.searchNodes(params.query);
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, results: result }, null, 2)
                }]
              };
              
            case "nodes":
              result = await knowledgeGraphManager.openNodes(params.names);
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, nodes: result }, null, 2)
                }]
              };
              
            case "project":
              result = await knowledgeGraphManager.getProjectOverview(params.projectName);
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, project: result }, null, 2)
                }]
              };
              
            case "participant":
              result = await knowledgeGraphManager.getParticipantProfile(params.participantName);
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, participant: result }, null, 2)
                }]
              };
              
            case "codes":
              // Use searchNodes for codes instead of a specialized method
              result = await knowledgeGraphManager.searchNodes(`entityType:code ${params.projectName ? `project:${params.projectName}` : ""}`);
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, codes: result }, null, 2)
                }]
              };
              
            case "themes":
              // Use searchNodes for themes
              result = await knowledgeGraphManager.searchNodes(`entityType:theme ${params.projectName ? `project:${params.projectName}` : ""}`);
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, themes: result }, null, 2)
                }]
              };
              
            case "transcript":
              // Use searchNodes to find the transcript
              const transcriptQuery = await knowledgeGraphManager.searchNodes(
                `entityType:transcript participant:${params.participantName} ${params.interviewId ? `interview:${params.interviewId}` : ""}`
              );
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, transcript: transcriptQuery }, null, 2)
                }]
              };
              
            case "memo":
              // Use openNodes to get the specific memo
              const memoResult = await knowledgeGraphManager.openNodes([params.memoName]);
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, memo: memoResult }, null, 2)
                }]
              };
              
            case "analysis":
              // Use searchNodes to get analysis artifacts
              const analysisQuery = await knowledgeGraphManager.searchNodes(`entityType:analysis project:${params.projectName}`);
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, analysis: analysisQuery }, null, 2)
                }]
              };
              
            case "codebook":
              // Use searchNodes to get codebook entries
              const codebookQuery = await knowledgeGraphManager.searchNodes(`entityType:code project:${params.projectName}`);
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({ success: true, codebook: codebookQuery }, null, 2)
                }]
              };
              
            case "related":
              // For the related case, we don't have a specialized method in the manager
              // So we'll use the generic KnowledgeGraph search capabilities
              const entityGraph = await knowledgeGraphManager.searchNodes(params.entityName);
              const entity = entityGraph.entities.find(e => e.name === params.entityName);
              
              if (!entity) {
                throw new Error(`Entity "${params.entityName}" not found`);
              }
              
              // Find related entities
              const relations = entityGraph.relations.filter(r => 
                r.from === params.entityName || r.to === params.entityName
              );
              
              const relatedNames = relations.map(r => 
                r.from === params.entityName ? r.to : r.from
              );
              
              if (relatedNames.length === 0) {
                return {
                  content: [{
                    type: "text",
                    text: JSON.stringify({ success: true, related: { entity, relatedEntities: [] } }, null, 2)
                  }]
                };
              }
              
              const relatedEntitiesGraph = await knowledgeGraphManager.openNodes(relatedNames);
              
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({
                    success: true,
                    related: {
                      entity,
                      relations,
                      relatedEntities: relatedEntitiesGraph.entities
                    }
                  }, null, 2)
                }]
              };
              
            default:
              throw new Error(`Invalid type: ${type}. Must be one of the supported get operation types.`);
          }
        } catch (error) {
          return {
            content: [{
              type: "text",
              text: JSON.stringify({ 
                success: false,
                error: error instanceof Error ? error.message : String(error)
              }, null, 2)
            }]
          };
        }
      }
    );
  
    // Connect the server to the transport
    const transport = new StdioServerTransport();
    await server.connect(transport);
  } catch (error) {
    console.error("Error starting server:", error);
    process.exit(1);
  }
}

// Run the main function
main().catch(error => {
  console.error("Unhandled error:", error);
  process.exit(1);
});

// Export the KnowledgeGraphManager for testing
export { KnowledgeGraphManager }; 
```

--------------------------------------------------------------------------------
/student/index.js:
--------------------------------------------------------------------------------

```javascript
#!/usr/bin/env node
// Updated imports using the modern MCP SDK API
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Node.js type declarations
import * as fs from 'fs/promises';
import * as path from 'path';
import { fileURLToPath } from 'url';
import { readFileSync, existsSync } from "fs";
// Define memory file path using environment variable with fallback
const parentPath = path.dirname(fileURLToPath(import.meta.url));
const defaultMemoryPath = path.join(parentPath, 'memory.json');
const defaultSessionsPath = path.join(parentPath, 'sessions.json');
// Properly handle absolute and relative paths for MEMORY_FILE_PATH
const MEMORY_FILE_PATH = process.env.MEMORY_FILE_PATH
    ? path.isAbsolute(process.env.MEMORY_FILE_PATH)
        ? process.env.MEMORY_FILE_PATH // Use absolute path as is
        : path.join(process.cwd(), process.env.MEMORY_FILE_PATH) // Relative to current working directory
    : defaultMemoryPath; // Default fallback
// Properly handle absolute and relative paths for SESSIONS_FILE_PATH
const SESSIONS_FILE_PATH = process.env.SESSIONS_FILE_PATH
    ? path.isAbsolute(process.env.SESSIONS_FILE_PATH)
        ? process.env.SESSIONS_FILE_PATH // Use absolute path as is
        : path.join(process.cwd(), process.env.SESSIONS_FILE_PATH) // Relative to current working directory
    : defaultSessionsPath; // Default fallback
// Student education specific entity types
const validEntityTypes = [
    'course',
    'assignment',
    'exam',
    'concept',
    'resource',
    'note',
    'lecture',
    'project',
    'question',
    'term',
    'goal',
    'professor',
    'status', // Entity status
    'priority' // Entity priority
];
// Function to validate entity type
function isValidEntityType(type) {
    return validEntityTypes.includes(type);
}
// Explicit validation function for TypeScript
function validateEntityType(type) {
    if (!isValidEntityType(type)) {
        throw new Error(`Invalid entity type: ${type}. Valid types are: ${validEntityTypes.join(', ')}`);
    }
}
// Student education specific relation types
const VALID_RELATION_TYPES = [
    'enrolled_in', // Student is taking a course
    'assigned_in', // Assignment is part of a course
    'due_on', // Assignment/exam has specific due date
    'covers', // Lecture/resource covers concept
    'references', // Note references concept
    'prerequisite_for', // Concept is foundation for another
    'taught_by', // Course taught by professor
    'scheduled_for', // Lecture/exam scheduled for specific time
    'contains', // Course contains lectures/assignments
    'requires', // Assignment requires specific concepts
    'related_to', // Concept related to another concept
    'created_for', // Note created for specific lecture
    'studies', // Study session focuses on concept/exam
    'helps_with', // Resource helps with assignment/concept
    'submitted', // Assignment submitted on date
    'part_of', // Entity is part of another entity
    'included_in', // Included in a larger component
    'follows', // Entity follows another in sequence
    'attends', // Student attends lecture
    'graded_with', // Assignment/exam graded with specific criteria
    'has_status', // Entity has a specific status
    'has_priority' // Entity has a specific priority
];
// Define valid status values for education entities
const VALID_STATUS_VALUES = ['not_started', 'in_progress', 'complete'];
// Define valid priority values for education entities
const VALID_PRIORITY_VALUES = ['low', 'high'];
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Collect tool descriptions from text files
const toolDescriptions = {
    'startsession': '',
    'loadcontext': '',
    'deletecontext': '',
    'buildcontext': '',
    'advancedcontext': '',
    'endsession': '',
};
for (const tool of Object.keys(toolDescriptions)) {
    const descriptionFilePath = path.resolve(__dirname, `student_${tool}.txt`);
    if (existsSync(descriptionFilePath)) {
        toolDescriptions[tool] = readFileSync(descriptionFilePath, 'utf-8');
    }
}
// The KnowledgeGraphManager class contains all operations to interact with the knowledge graph
class KnowledgeGraphManager {
    async loadGraph() {
        try {
            const fileContent = await fs.readFile(MEMORY_FILE_PATH, 'utf-8');
            return JSON.parse(fileContent);
        }
        catch (error) {
            // If the file doesn't exist, return an empty graph
            return {
                entities: [],
                relations: []
            };
        }
    }
    async saveGraph(graph) {
        await fs.writeFile(MEMORY_FILE_PATH, JSON.stringify(graph, null, 2), 'utf-8');
    }
    // Initialize status and priority entities
    async initializeStatusAndPriority() {
        const graph = await this.loadGraph();
        // Create status entities if they don't exist
        for (const statusValue of VALID_STATUS_VALUES) {
            const statusName = `status:${statusValue}`;
            if (!graph.entities.some(e => e.name === statusName && e.entityType === 'status')) {
                graph.entities.push({
                    name: statusName,
                    entityType: 'status',
                    observations: [`A ${statusValue} status value`]
                });
            }
        }
        // Create priority entities if they don't exist
        for (const priorityValue of VALID_PRIORITY_VALUES) {
            const priorityName = `priority:${priorityValue}`;
            if (!graph.entities.some(e => e.name === priorityName && e.entityType === 'priority')) {
                graph.entities.push({
                    name: priorityName,
                    entityType: 'priority',
                    observations: [`A ${priorityValue} priority value`]
                });
            }
        }
        await this.saveGraph(graph);
    }
    // Helper method to get status of an entity
    async getEntityStatus(entityName) {
        const graph = await this.loadGraph();
        // Find status relation for this entity
        const statusRelation = graph.relations.find(r => r.from === entityName &&
            r.relationType === 'has_status');
        if (statusRelation) {
            // Extract status value from the status entity name (status:value)
            return statusRelation.to.split(':')[1];
        }
        return null;
    }
    // Helper method to get priority of an entity
    async getEntityPriority(entityName) {
        const graph = await this.loadGraph();
        // Find priority relation for this entity
        const priorityRelation = graph.relations.find(r => r.from === entityName &&
            r.relationType === 'has_priority');
        if (priorityRelation) {
            // Extract priority value from the priority entity name (priority:value)
            return priorityRelation.to.split(':')[1];
        }
        return null;
    }
    // Helper method to set status of an entity
    async setEntityStatus(entityName, statusValue) {
        if (!VALID_STATUS_VALUES.includes(statusValue)) {
            throw new Error(`Invalid status value: ${statusValue}. Valid values are: ${VALID_STATUS_VALUES.join(', ')}`);
        }
        const graph = await this.loadGraph();
        // Remove any existing status relations for this entity
        graph.relations = graph.relations.filter(r => !(r.from === entityName && r.relationType === 'has_status'));
        // Add new status relation
        graph.relations.push({
            from: entityName,
            to: `status:${statusValue}`,
            relationType: 'has_status'
        });
        await this.saveGraph(graph);
    }
    // Helper method to set priority of an entity
    async setEntityPriority(entityName, priorityValue) {
        if (!VALID_PRIORITY_VALUES.includes(priorityValue)) {
            throw new Error(`Invalid priority value: ${priorityValue}. Valid values are: ${VALID_PRIORITY_VALUES.join(', ')}`);
        }
        const graph = await this.loadGraph();
        // Remove any existing priority relations for this entity
        graph.relations = graph.relations.filter(r => !(r.from === entityName && r.relationType === 'has_priority'));
        // Add new priority relation
        graph.relations.push({
            from: entityName,
            to: `priority:${priorityValue}`,
            relationType: 'has_priority'
        });
        await this.saveGraph(graph);
    }
    async createEntities(entities) {
        const graph = await this.loadGraph();
        // Validate entity names don't already exist
        for (const entity of entities) {
            if (graph.entities.some(e => e.name === entity.name)) {
                throw new Error(`Entity with name ${entity.name} already exists`);
            }
            validateEntityType(entity.entityType);
        }
        // Add new entities
        graph.entities.push(...entities);
        // Save updated graph
        await this.saveGraph(graph);
        return graph;
    }
    async createRelations(relations) {
        const graph = await this.loadGraph();
        // Validate relations
        for (const relation of relations) {
            // Check if entities exist
            if (!graph.entities.some(e => e.name === relation.from)) {
                throw new Error(`Entity '${relation.from}' not found`);
            }
            if (!graph.entities.some(e => e.name === relation.to)) {
                throw new Error(`Entity '${relation.to}' not found`);
            }
            if (!VALID_RELATION_TYPES.includes(relation.relationType)) {
                throw new Error(`Invalid relation type: ${relation.relationType}. Valid types are: ${VALID_RELATION_TYPES.join(', ')}`);
            }
            // Check if relation already exists
            if (graph.relations.some(r => r.from === relation.from &&
                r.to === relation.to &&
                r.relationType === relation.relationType)) {
                throw new Error(`Relation from '${relation.from}' to '${relation.to}' with type '${relation.relationType}' already exists`);
            }
        }
        // Add relations
        graph.relations.push(...relations);
        // Save updated graph
        await this.saveGraph(graph);
        return graph;
    }
    async addObservations(entityName, observations) {
        const graph = await this.loadGraph();
        // Find the entity
        const entity = graph.entities.find(e => e.name === entityName);
        if (!entity) {
            throw new Error(`Entity '${entityName}' not found`);
        }
        // Add observations
        entity.observations.push(...observations);
        // Save updated graph
        await this.saveGraph(graph);
        return graph;
    }
    async deleteEntities(entityNames) {
        const graph = await this.loadGraph();
        // Remove the entities
        graph.entities = graph.entities.filter(e => !entityNames.includes(e.name));
        // Remove relations that involve the deleted entities
        graph.relations = graph.relations.filter(r => !entityNames.includes(r.from) && !entityNames.includes(r.to));
        await this.saveGraph(graph);
    }
    async deleteObservations(deletions) {
        const graph = await this.loadGraph();
        for (const deletion of deletions) {
            const entity = graph.entities.find(e => e.name === deletion.entityName);
            if (entity) {
                // Remove the specified observations
                entity.observations = entity.observations.filter(o => !deletion.observations.includes(o));
            }
        }
        await this.saveGraph(graph);
    }
    async deleteRelations(relations) {
        const graph = await this.loadGraph();
        // Remove specified relations
        graph.relations = graph.relations.filter(r => !relations.some(toDelete => r.from === toDelete.from &&
            r.to === toDelete.to &&
            r.relationType === toDelete.relationType));
        await this.saveGraph(graph);
    }
    async readGraph() {
        return this.loadGraph();
    }
    async searchNodes(query) {
        const graph = await this.loadGraph();
        // Split query into search terms
        const terms = query.toLowerCase().split(/\s+/);
        // Find matching entities
        const matchingEntityNames = new Set();
        for (const entity of graph.entities) {
            // Check if all terms match
            const matchesAllTerms = terms.every(term => {
                // Check entity name
                if (entity.name.toLowerCase().includes(term)) {
                    return true;
                }
                // Check entity type
                if (entity.entityType.toLowerCase().includes(term)) {
                    return true;
                }
                // Check observations
                for (const observation of entity.observations) {
                    if (observation.toLowerCase().includes(term)) {
                        return true;
                    }
                }
                return false;
            });
            if (matchesAllTerms) {
                matchingEntityNames.add(entity.name);
            }
        }
        // Find relations between matching entities
        const matchingRelations = graph.relations.filter(r => matchingEntityNames.has(r.from) && matchingEntityNames.has(r.to));
        // Return matching entities and their relations
        return {
            entities: graph.entities.filter(e => matchingEntityNames.has(e.name)),
            relations: matchingRelations
        };
    }
    async openNodes(names) {
        const graph = await this.loadGraph();
        // Find the specified entities
        const entities = graph.entities.filter(e => names.includes(e.name));
        // Find relations between the specified entities
        const relations = graph.relations.filter(r => names.includes(r.from) && names.includes(r.to));
        return {
            entities,
            relations
        };
    }
    // Get summary of course including lectures, assignments, exams, textbooks
    async getCourseOverview(courseName) {
        const graph = await this.loadGraph();
        // Find the course
        const course = graph.entities.find(e => e.name === courseName && e.entityType === 'course');
        if (!course) {
            throw new Error(`Course '${courseName}' not found`);
        }
        // Find term this course belongs to
        let term;
        for (const relation of graph.relations) {
            if (relation.relationType === 'part_of' && relation.from === courseName) {
                const potentialTerm = graph.entities.find(e => e.name === relation.to && e.entityType === 'term');
                if (potentialTerm) {
                    term = potentialTerm;
                    break;
                }
            }
        }
        // Find professor who teaches this course
        let professor;
        for (const relation of graph.relations) {
            if (relation.relationType === 'taught_by' && relation.from === courseName) {
                professor = graph.entities.find(e => e.name === relation.to && e.entityType === 'professor');
                if (professor) {
                    break;
                }
            }
        }
        // Find lectures for this course
        const lectures = [];
        for (const relation of graph.relations) {
            if (relation.relationType === 'part_of' && relation.to === courseName) {
                const lecture = graph.entities.find(e => e.name === relation.from && e.entityType === 'lecture');
                if (lecture) {
                    lectures.push(lecture);
                }
            }
        }
        // Sort lectures by date if available
        lectures.sort((a, b) => {
            const aDateObs = a.observations.find(o => o.startsWith('Date:'));
            const bDateObs = b.observations.find(o => o.startsWith('Date:'));
            if (aDateObs && bDateObs) {
                const aDate = new Date(aDateObs.split(':')[1].trim());
                const bDate = new Date(bDateObs.split(':')[1].trim());
                return aDate.getTime() - bDate.getTime();
            }
            return 0;
        });
        // Find assignments for this course
        const assignments = [];
        for (const relation of graph.relations) {
            if (relation.relationType === 'assigned_in' && relation.to === courseName) {
                const assignment = graph.entities.find(e => e.name === relation.from && e.entityType === 'assignment');
                if (assignment) {
                    assignments.push(assignment);
                }
            }
        }
        // Sort assignments by due date if available
        assignments.sort((a, b) => {
            const aDueDateObs = a.observations.find(o => o.startsWith('Due:'));
            const bDueDateObs = b.observations.find(o => o.startsWith('Due:'));
            if (aDueDateObs && bDueDateObs) {
                const aDate = new Date(aDueDateObs.split(':')[1].trim());
                const bDate = new Date(bDueDateObs.split(':')[1].trim());
                return aDate.getTime() - bDate.getTime();
            }
            return 0;
        });
        // Find exams for this course
        const exams = [];
        for (const relation of graph.relations) {
            if (relation.relationType === 'scheduled_for' && relation.from === courseName) {
                const exam = graph.entities.find(e => e.name === relation.to && e.entityType === 'exam');
                if (exam) {
                    exams.push(exam);
                }
            }
        }
        // Sort exams by date if available
        exams.sort((a, b) => {
            const aDateObs = a.observations.find(o => o.startsWith('Date:'));
            const bDateObs = b.observations.find(o => o.startsWith('Date:'));
            if (aDateObs && bDateObs) {
                const aDate = new Date(aDateObs.split(':')[1].trim());
                const bDate = new Date(bDateObs.split(':')[1].trim());
                return aDate.getTime() - bDate.getTime();
            }
            return 0;
        });
        // Find concepts covered in this course
        const concepts = [];
        for (const relation of graph.relations) {
            if (relation.relationType === 'covers' && relation.from === courseName) {
                const concept = graph.entities.find(e => e.name === relation.to && e.entityType === 'concept');
                if (concept) {
                    concepts.push(concept);
                }
            }
        }
        // Find resources for this course (textbooks, articles, etc.)
        const resources = [];
        for (const relation of graph.relations) {
            if (relation.relationType === 'helps_with' && relation.to === courseName) {
                const resource = graph.entities.find(e => e.name === relation.from && e.entityType === 'resource');
                if (resource) {
                    resources.push(resource);
                }
            }
        }
        // Find notes for this course
        const notes = [];
        for (const relation of graph.relations) {
            if (relation.relationType === 'created_for' && relation.to === courseName) {
                const note = graph.entities.find(e => e.name === relation.from && e.entityType === 'note');
                if (note) {
                    notes.push(note);
                }
            }
        }
        // Extract course info from observations
        const courseCode = course.observations.find(o => o.startsWith('Code:'))?.split(':')[1].trim() || 'N/A';
        const courseLocation = course.observations.find(o => o.startsWith('Location:'))?.split(':')[1].trim() || 'N/A';
        const courseSchedule = course.observations.find(o => o.startsWith('Schedule:'))?.split(':')[1].trim() || 'N/A';
        const courseStatus = course.observations.find(o => o.startsWith('Status:'))?.split(':')[1].trim() || 'N/A';
        return {
            course,
            term,
            professor,
            info: {
                code: courseCode,
                location: courseLocation,
                schedule: courseSchedule,
                status: courseStatus
            },
            summary: {
                lectureCount: lectures.length,
                assignmentCount: assignments.length,
                examCount: exams.length,
                conceptCount: concepts.length,
                resourceCount: resources.length,
                noteCount: notes.length
            },
            lectures,
            assignments,
            exams,
            concepts,
            resources,
            notes
        };
    }
    // Returns assignments and exams with approaching due dates
    async getUpcomingDeadlines(termName, courseName, daysAhead = 14) {
        const graph = await this.loadGraph();
        const today = new Date();
        const endDate = new Date();
        endDate.setDate(today.getDate() + daysAhead);
        // Filter for specific term if provided
        let relevantCourses = [];
        if (termName) {
            // Find the specific term
            const term = graph.entities.find(e => e.name === termName && e.entityType === 'term');
            if (!term) {
                throw new Error(`Term '${termName}' not found`);
            }
            // Find courses in this term
            for (const relation of graph.relations) {
                if (relation.relationType === 'part_of' && relation.from === relation.to && relation.to === termName) {
                    const course = graph.entities.find(e => e.name === relation.from && e.entityType === 'course');
                    if (course) {
                        relevantCourses.push(course);
                    }
                }
            }
        }
        else {
            // Get all courses if no term specified
            relevantCourses = graph.entities.filter(e => e.entityType === 'course');
        }
        // Filter for specific course if provided
        if (courseName) {
            relevantCourses = relevantCourses.filter(c => c.name === courseName);
            if (relevantCourses.length === 0) {
                throw new Error(`Course '${courseName}' not found`);
            }
        }
        // Find all assignments and exams for these courses
        const deadlines = [];
        for (const course of relevantCourses) {
            // Find assignments for this course
            for (const relation of graph.relations) {
                if (relation.relationType === 'assigned_in' && relation.to === course.name) {
                    const assignment = graph.entities.find(e => e.name === relation.from && e.entityType === 'assignment');
                    if (assignment) {
                        // Check due date
                        const dueDateObs = assignment.observations.find(o => o.startsWith('Due:'));
                        if (dueDateObs) {
                            const dueDateStr = dueDateObs.split(':')[1].trim();
                            const dueDate = new Date(dueDateStr);
                            // Check if it's in our date range
                            if (dueDate >= today && dueDate <= endDate) {
                                const daysRemaining = Math.ceil((dueDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
                                deadlines.push({
                                    entity: assignment,
                                    dueDate,
                                    course,
                                    daysRemaining
                                });
                            }
                        }
                    }
                }
            }
            // Find exams for this course
            for (const relation of graph.relations) {
                if (relation.relationType === 'scheduled_for' && relation.from === course.name) {
                    const exam = graph.entities.find(e => e.name === relation.to && e.entityType === 'exam');
                    if (exam) {
                        // Check exam date
                        const dateObs = exam.observations.find(o => o.startsWith('Date:'));
                        if (dateObs) {
                            const dateStr = dateObs.split(':')[1].trim();
                            const examDate = new Date(dateStr);
                            // Check if it's in our date range
                            if (examDate >= today && examDate <= endDate) {
                                const daysRemaining = Math.ceil((examDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
                                deadlines.push({
                                    entity: exam,
                                    dueDate: examDate,
                                    course,
                                    daysRemaining
                                });
                            }
                        }
                    }
                }
            }
        }
        // Sort by due date
        deadlines.sort((a, b) => a.dueDate.getTime() - b.dueDate.getTime());
        return {
            deadlines,
            startDate: today.toISOString().slice(0, 10),
            endDate: endDate.toISOString().slice(0, 10),
            courseFilter: courseName,
            termFilter: termName,
            count: deadlines.length
        };
    }
    // Get detailed information about assignment status, including progress, related concepts, and resources
    async getAssignmentStatus(assignmentName) {
        const graph = await this.loadGraph();
        // Find the assignment
        const assignment = graph.entities.find(e => e.name === assignmentName && e.entityType === 'assignment');
        if (!assignment) {
            throw new Error(`Assignment '${assignmentName}' not found`);
        }
        // Find the course this assignment belongs to
        let course;
        for (const relation of graph.relations) {
            if (relation.relationType === 'assigned_in' && relation.from === assignmentName) {
                course = graph.entities.find(e => e.name === relation.to && e.entityType === 'course');
                if (course) {
                    break;
                }
            }
        }
        // Get status using the relation-based approach
        const status = await this.getEntityStatus(assignmentName) || 'not_started';
        // Get priority using the relation-based approach
        const priority = await this.getEntityPriority(assignmentName);
        const dueDate = assignment.observations.find(o => o.startsWith('Due:'))?.split(':')[1].trim();
        const pointsWorth = assignment.observations.find(o => o.startsWith('Points:'))?.split(':')[1].trim();
        const instructions = assignment.observations.find(o => o.startsWith('Instructions:'))?.split(':')[1].trim();
        // Calculate time remaining if due date exists
        let timeRemaining = null;
        let daysRemaining = null;
        let isOverdue = false;
        if (dueDate) {
            const dueDateTime = new Date(dueDate).getTime();
            const now = new Date().getTime();
            timeRemaining = dueDateTime - now;
            daysRemaining = Math.ceil(timeRemaining / (1000 * 60 * 60 * 24));
            isOverdue = timeRemaining < 0;
        }
        // Find concepts related to this assignment
        const concepts = [];
        for (const relation of graph.relations) {
            if (relation.relationType === 'covers' && relation.from === assignmentName) {
                const concept = graph.entities.find(e => e.name === relation.to && e.entityType === 'concept');
                if (concept) {
                    concepts.push(concept);
                }
            }
        }
        // Find resources that might help with this assignment
        const resources = [];
        // Direct resources for the assignment
        for (const relation of graph.relations) {
            if (relation.relationType === 'helps_with' && relation.to === assignmentName) {
                const resource = graph.entities.find(e => e.name === relation.from && e.entityType === 'resource');
                if (resource) {
                    resources.push(resource);
                }
            }
        }
        // Resources for concepts related to the assignment
        for (const concept of concepts) {
            for (const relation of graph.relations) {
                if (relation.relationType === 'helps_with' && relation.to === concept.name) {
                    const resource = graph.entities.find(e => e.name === relation.from && e.entityType === 'resource');
                    if (resource && !resources.some(r => r.name === resource.name)) {
                        resources.push(resource);
                    }
                }
            }
        }
        // Find notes related to this assignment
        const notes = [];
        for (const relation of graph.relations) {
            if (relation.relationType === 'created_for' && relation.to === assignmentName) {
                const note = graph.entities.find(e => e.name === relation.from && e.entityType === 'note');
                if (note) {
                    notes.push(note);
                }
            }
        }
        // Add notes related to concepts covered by the assignment
        for (const concept of concepts) {
            for (const relation of graph.relations) {
                if (relation.relationType === 'references' && relation.to === concept.name) {
                    const note = graph.entities.find(e => e.name === relation.from && e.entityType === 'note');
                    if (note && !notes.some(n => n.name === note.name)) {
                        notes.push(note);
                    }
                }
            }
        }
        return {
            assignment,
            course,
            info: {
                status,
                dueDate,
                pointsWorth,
                instructions,
                timeRemaining,
                daysRemaining,
                isOverdue
            },
            concepts,
            resources,
            notes
        };
    }
    // Get exam preparation resources, related concepts, and study plan
    async getExamPrep(examName) {
        const graph = await this.loadGraph();
        // Find the exam
        const exam = graph.entities.find(e => e.name === examName && e.entityType === 'exam');
        if (!exam) {
            throw new Error(`Exam '${examName}' not found`);
        }
        // Find the course this exam is for
        let course;
        for (const relation of graph.relations) {
            if (relation.relationType === 'scheduled_for' && relation.to === examName) {
                course = graph.entities.find(e => e.name === relation.from && e.entityType === 'course');
                if (course) {
                    break;
                }
            }
        }
        // Get exam info from observations
        const examDate = exam.observations.find(o => o.startsWith('Date:'))?.split(':')[1].trim();
        const examLocation = exam.observations.find(o => o.startsWith('Location:'))?.split(':')[1].trim();
        const examFormat = exam.observations.find(o => o.startsWith('Format:'))?.split(':')[1].trim();
        const examDuration = exam.observations.find(o => o.startsWith('Duration:'))?.split(':')[1].trim();
        // Calculate time remaining if exam date exists
        let timeRemaining = null;
        let daysRemaining = null;
        if (examDate) {
            const examDateTime = new Date(examDate).getTime();
            const now = new Date().getTime();
            timeRemaining = examDateTime - now;
            daysRemaining = Math.ceil(timeRemaining / (1000 * 60 * 60 * 24));
        }
        // Find concepts covered in the exam
        const concepts = [];
        for (const relation of graph.relations) {
            if (relation.relationType === 'covers' && relation.from === examName) {
                const concept = graph.entities.find(e => e.name === relation.to && e.entityType === 'concept');
                if (concept) {
                    concepts.push(concept);
                }
            }
        }
        // If no concepts directly related to exam, get concepts from the course
        if (concepts.length === 0 && course) {
            for (const relation of graph.relations) {
                if (relation.relationType === 'covers' && relation.from === course.name) {
                    const concept = graph.entities.find(e => e.name === relation.to && e.entityType === 'concept');
                    if (concept) {
                        concepts.push(concept);
                    }
                }
            }
        }
        // Find resources helpful for the exam
        const resources = [];
        // Direct resources for the exam
        for (const relation of graph.relations) {
            if (relation.relationType === 'helps_with' && relation.to === examName) {
                const resource = graph.entities.find(e => e.name === relation.from && e.entityType === 'resource');
                if (resource) {
                    resources.push(resource);
                }
            }
        }
        // Resources for concepts covered by the exam
        for (const concept of concepts) {
            for (const relation of graph.relations) {
                if (relation.relationType === 'helps_with' && relation.to === concept.name) {
                    const resource = graph.entities.find(e => e.name === relation.from && e.entityType === 'resource');
                    if (resource && !resources.some(r => r.name === resource.name)) {
                        resources.push(resource);
                    }
                }
            }
        }
        // Resources for the course
        if (course) {
            for (const relation of graph.relations) {
                if (relation.relationType === 'helps_with' && relation.to === course.name) {
                    const resource = graph.entities.find(e => e.name === relation.from && e.entityType === 'resource');
                    if (resource && !resources.some(r => r.name === resource.name)) {
                        resources.push(resource);
                    }
                }
            }
        }
        // Find notes related to the exam
        const notes = [];
        // Direct notes for the exam
        for (const relation of graph.relations) {
            if (relation.relationType === 'created_for' && relation.to === examName) {
                const note = graph.entities.find(e => e.name === relation.from && e.entityType === 'note');
                if (note) {
                    notes.push(note);
                }
            }
        }
        // Notes for concepts covered in the exam
        for (const concept of concepts) {
            for (const relation of graph.relations) {
                if (relation.relationType === 'references' && relation.to === concept.name) {
                    const note = graph.entities.find(e => e.name === relation.from && e.entityType === 'note');
                    if (note && !notes.some(n => n.name === note.name)) {
                        notes.push(note);
                    }
                }
            }
        }
        // Find previous exams for the course
        const previousExams = [];
        if (course) {
            for (const relation of graph.relations) {
                if (relation.relationType === 'scheduled_for' && relation.from === course.name && relation.to !== examName) {
                    const prevExam = graph.entities.find(e => e.name === relation.to && e.entityType === 'exam');
                    if (prevExam) {
                        const prevExamDate = prevExam.observations.find(o => o.startsWith('Date:'))?.split(':')[1].trim();
                        if (prevExamDate && new Date(prevExamDate) < new Date()) {
                            previousExams.push(prevExam);
                        }
                    }
                }
            }
        }
        // Find study sessions scheduled for this exam
        // Note: We no longer use studySession entities, this section is removed
        const studySessions = [];
        // Get concepts covered in this exam
        const conceptsCovered = [];
        return {
            exam,
            course,
            info: {
                examDate,
                examLocation,
                examFormat,
                examDuration,
                timeRemaining,
                daysRemaining
            },
            concepts,
            resources,
            notes,
            previousExams,
            studySessions,
            summary: {
                conceptCount: concepts.length,
                resourceCount: resources.length,
                noteCount: notes.length,
                previousExamCount: previousExams.length,
                studySessionCount: studySessions.length
            }
        };
    }
    // Find concepts related to a given concept and how they're connected
    async findRelatedConcepts(conceptName, depth = 1) {
        const graph = await this.loadGraph();
        // Find the concept
        const concept = graph.entities.find(e => e.name === conceptName && e.entityType === 'concept');
        if (!concept) {
            throw new Error(`Concept '${conceptName}' not found`);
        }
        // Initialize results
        const relatedConcepts = [];
        // Set to track processed concepts to avoid duplicates
        const processedConcepts = new Set();
        processedConcepts.add(conceptName);
        // Queue of concepts to process with their current depth and path
        const queue = [{ name: conceptName, currentDepth: 0, path: [] }];
        // Process the queue
        while (queue.length > 0) {
            const { name, currentDepth, path } = queue.shift();
            // Skip if we've reached max depth (except for the initial concept)
            if (currentDepth > depth && name !== conceptName)
                continue;
            // Find the concept entity
            const currentConcept = graph.entities.find(e => e.name === name && e.entityType === 'concept');
            if (!currentConcept)
                continue;
            // Skip the initial concept for the results
            if (name !== conceptName) {
                // Find courses that cover this concept
                const courses = [];
                for (const relation of graph.relations) {
                    if (relation.relationType === 'covers' && relation.to === name) {
                        const course = graph.entities.find(e => e.name === relation.from && e.entityType === 'course');
                        if (course) {
                            courses.push(course);
                        }
                    }
                }
                // Find resources that help with this concept
                const resources = [];
                for (const relation of graph.relations) {
                    if (relation.relationType === 'helps_with' && relation.to === name) {
                        const resource = graph.entities.find(e => e.name === relation.from && e.entityType === 'resource');
                        if (resource) {
                            resources.push(resource);
                        }
                    }
                }
                relatedConcepts.push({
                    concept: currentConcept,
                    relationPath: [...path],
                    depth: currentDepth,
                    courses,
                    resources
                });
            }
            // Find directly related concepts through 'related_to'
            for (const relation of graph.relations) {
                if (relation.relationType === 'related_to') {
                    let nextConcept = null;
                    // Check bidirectional relation
                    if (relation.from === name) {
                        nextConcept = relation.to;
                    }
                    else if (relation.to === name) {
                        nextConcept = relation.from;
                    }
                    if (nextConcept && !processedConcepts.has(nextConcept)) {
                        processedConcepts.add(nextConcept);
                        queue.push({
                            name: nextConcept,
                            currentDepth: currentDepth + 1,
                            path: [...path, `related_to ${nextConcept}`]
                        });
                    }
                }
            }
            // Find prerequisites
            for (const relation of graph.relations) {
                if (relation.relationType === 'prerequisite_for') {
                    let nextConcept = null;
                    let relationDescription = '';
                    if (relation.from === name) {
                        nextConcept = relation.to;
                        relationDescription = `prerequisite_for ${nextConcept}`;
                    }
                    else if (relation.to === name) {
                        nextConcept = relation.from;
                        relationDescription = `${nextConcept} is_prerequisite_for this`;
                    }
                    if (nextConcept && !processedConcepts.has(nextConcept)) {
                        processedConcepts.add(nextConcept);
                        queue.push({
                            name: nextConcept,
                            currentDepth: currentDepth + 1,
                            path: [...path, relationDescription]
                        });
                    }
                }
            }
        }
        // Sort related concepts by depth
        relatedConcepts.sort((a, b) => a.depth - b.depth);
        return {
            concept,
            relatedConcepts,
            summary: {
                totalRelated: relatedConcepts.length,
                maxDepth: depth
            }
        };
    }
    // Track lecture notes and find related concepts and resources
    async trackLectureNotes(courseName) {
        const graph = await this.loadGraph();
        // Find the course
        const course = graph.entities.find(e => e.name === courseName && e.entityType === 'course');
        if (!course) {
            throw new Error(`Course '${courseName}' not found`);
        }
        // Find lectures for this course
        const lectures = [];
        for (const relation of graph.relations) {
            if (relation.relationType === 'part_of' && relation.to === courseName) {
                const lecture = graph.entities.find(e => e.name === relation.from && e.entityType === 'lecture');
                if (lecture) {
                    lectures.push(lecture);
                }
            }
        }
        // Sort lectures by date if available
        lectures.sort((a, b) => {
            const aDateObs = a.observations.find(o => o.startsWith('Date:'));
            const bDateObs = b.observations.find(o => o.startsWith('Date:'));
            if (aDateObs && bDateObs) {
                const aDate = new Date(aDateObs.split(':')[1].trim());
                const bDate = new Date(bDateObs.split(':')[1].trim());
                return aDate.getTime() - bDate.getTime();
            }
            return 0;
        });
        // Create a structure to hold lecture data with notes and concepts
        const lectureData = [];
        for (const lecture of lectures) {
            // Get details about the lecture
            const lectureDate = lecture.observations.find(o => o.startsWith('Date:'))?.split(':')[1].trim();
            const lectureTopic = lecture.observations.find(o => o.startsWith('Topic:'))?.split(':')[1].trim();
            // Find notes for this lecture
            const notes = [];
            for (const relation of graph.relations) {
                if (relation.relationType === 'created_for' && relation.to === lecture.name) {
                    const note = graph.entities.find(e => e.name === relation.from && e.entityType === 'note');
                    if (note) {
                        notes.push(note);
                    }
                }
            }
            // Find concepts covered in this lecture
            const concepts = [];
            for (const relation of graph.relations) {
                if (relation.relationType === 'covers' && relation.from === lecture.name) {
                    const concept = graph.entities.find(e => e.name === relation.to && e.entityType === 'concept');
                    if (concept) {
                        concepts.push(concept);
                    }
                }
            }
            // Get resources related to this lecture
            const resources = [];
            for (const relation of graph.relations) {
                if (relation.relationType === 'helps_with' && relation.to === lecture.name) {
                    const resource = graph.entities.find(e => e.name === relation.from && e.entityType === 'resource');
                    if (resource) {
                        resources.push(resource);
                    }
                }
            }
            // Add resources related to concepts in this lecture
            for (const concept of concepts) {
                for (const relation of graph.relations) {
                    if (relation.relationType === 'helps_with' && relation.to === concept.name) {
                        const resource = graph.entities.find(e => e.name === relation.from && e.entityType === 'resource');
                        if (resource && !resources.some(r => r.name === resource.name)) {
                            resources.push(resource);
                        }
                    }
                }
            }
            lectureData.push({
                lecture,
                info: {
                    date: lectureDate,
                    topic: lectureTopic
                },
                notes,
                concepts,
                resources,
                summary: {
                    noteCount: notes.length,
                    conceptCount: concepts.length,
                    resourceCount: resources.length
                }
            });
        }
        return {
            course,
            lectures: lectureData,
            summary: {
                lectureCount: lectures.length,
                totalNotes: lectureData.reduce((sum, ld) => sum + ld.notes.length, 0),
                totalConcepts: lectureData.reduce((sum, ld) => sum + ld.concepts.length, 0)
            }
        };
    }
    // Get term overview including courses, progress, and important dates
    async getTermOverview(termName) {
        const graph = await this.loadGraph();
        // Find the term
        const term = graph.entities.find(e => e.name === termName && e.entityType === 'term');
        if (!term) {
            throw new Error(`Term '${termName}' not found`);
        }
        // Get term info
        const startDate = term.observations.find(o => o.startsWith('StartDate:'))?.split(':')[1].trim();
        const endDate = term.observations.find(o => o.startsWith('EndDate:'))?.split(':')[1].trim();
        const status = term.observations.find(o => o.startsWith('Status:'))?.split(':')[1].trim() || 'in_progress';
        // Find courses for this term
        const courses = [];
        for (const relation of graph.relations) {
            if (relation.relationType === 'part_of' && relation.to === termName) {
                const course = graph.entities.find(e => e.name === relation.from && e.entityType === 'course');
                if (course) {
                    courses.push(course);
                }
            }
        }
        // Get detailed information for each course
        const courseData = [];
        for (const course of courses) {
            // Get course info
            const courseCode = course.observations.find(o => o.startsWith('Code:'))?.split(':')[1].trim();
            const courseSchedule = course.observations.find(o => o.startsWith('Schedule:'))?.split(':')[1].trim();
            const courseStatus = course.observations.find(o => o.startsWith('Status:'))?.split(':')[1].trim() || 'in_progress';
            // Find professor
            let professor;
            for (const relation of graph.relations) {
                if (relation.relationType === 'taught_by' && relation.from === course.name) {
                    professor = graph.entities.find(e => e.name === relation.to && e.entityType === 'professor');
                    if (professor) {
                        break;
                    }
                }
            }
            // Find assignments for this course
            const assignments = [];
            for (const relation of graph.relations) {
                if (relation.relationType === 'assigned_in' && relation.to === course.name) {
                    const assignment = graph.entities.find(e => e.name === relation.from && e.entityType === 'assignment');
                    if (assignment) {
                        assignments.push(assignment);
                    }
                }
            }
            // Count completed and total assignments
            const completedAssignments = assignments.filter(a => a.observations.find(o => o.startsWith('Status:'))?.split(':')[1].trim() === 'completed').length;
            // Find exams for this course
            const exams = [];
            for (const relation of graph.relations) {
                if (relation.relationType === 'scheduled_for' && relation.from === course.name) {
                    const exam = graph.entities.find(e => e.name === relation.to && e.entityType === 'exam');
                    if (exam) {
                        exams.push(exam);
                    }
                }
            }
            // Sort exams by date
            exams.sort((a, b) => {
                const aDateObs = a.observations.find(o => o.startsWith('Date:'));
                const bDateObs = b.observations.find(o => o.startsWith('Date:'));
                if (aDateObs && bDateObs) {
                    const aDate = new Date(aDateObs.split(':')[1].trim());
                    const bDate = new Date(bDateObs.split(':')[1].trim());
                    return aDate.getTime() - bDate.getTime();
                }
                return 0;
            });
            // Get the next upcoming exam
            const upcomingExam = exams.find(e => {
                const dateObs = e.observations.find(o => o.startsWith('Date:'));
                if (dateObs) {
                    const examDate = new Date(dateObs.split(':')[1].trim());
                    return examDate > new Date();
                }
                return false;
            });
            courseData.push({
                course,
                professor,
                info: {
                    code: courseCode,
                    schedule: courseSchedule,
                    status: courseStatus
                },
                progress: {
                    completedAssignments,
                    totalAssignments: assignments.length,
                    completionRate: assignments.length > 0 ?
                        Math.round((completedAssignments / assignments.length) * 100) : 0
                },
                upcomingExam,
                summary: {
                    assignmentCount: assignments.length,
                    examCount: exams.length
                }
            });
        }
        // Find all deadlines (assignments and exams) in this term
        const allDeadlines = [];
        // Current date for comparisons
        const today = new Date();
        // Process assignments
        for (const course of courses) {
            // Find assignments for this course
            for (const relation of graph.relations) {
                if (relation.relationType === 'assigned_in' && relation.to === course.name) {
                    const assignment = graph.entities.find(e => e.name === relation.from && e.entityType === 'assignment');
                    if (assignment) {
                        // Check due date
                        const dueDateObs = assignment.observations.find(o => o.startsWith('Due:'));
                        if (dueDateObs) {
                            const dueDateStr = dueDateObs.split(':')[1].trim();
                            const dueDate = new Date(dueDateStr);
                            // Only include future deadlines
                            if (dueDate >= today) {
                                const daysRemaining = Math.ceil((dueDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
                                allDeadlines.push({
                                    entity: assignment,
                                    type: 'assignment',
                                    course,
                                    date: dueDate,
                                    daysRemaining
                                });
                            }
                        }
                    }
                }
            }
            // Find exams for this course
            for (const relation of graph.relations) {
                if (relation.relationType === 'scheduled_for' && relation.from === course.name) {
                    const exam = graph.entities.find(e => e.name === relation.to && e.entityType === 'exam');
                    if (exam) {
                        // Check exam date
                        const dateObs = exam.observations.find(o => o.startsWith('Date:'));
                        if (dateObs) {
                            const dateStr = dateObs.split(':')[1].trim();
                            const examDate = new Date(dateStr);
                            // Only include future dates
                            if (examDate >= today) {
                                const daysRemaining = Math.ceil((examDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
                                allDeadlines.push({
                                    entity: exam,
                                    type: 'exam',
                                    course,
                                    date: examDate,
                                    daysRemaining
                                });
                            }
                        }
                    }
                }
            }
        }
        // Sort all deadlines by date
        allDeadlines.sort((a, b) => a.date.getTime() - b.date.getTime());
        return {
            term,
            info: {
                startDate,
                endDate,
                status
            },
            courses: courseData,
            upcomingDeadlines: allDeadlines.slice(0, 10), // Return the next 10 deadlines
            summary: {
                courseCount: courses.length,
                deadlineCount: allDeadlines.length
            }
        };
    }
}
// Session management functions
async function loadSessionStates() {
    try {
        const fileContent = await fs.readFile(SESSIONS_FILE_PATH, 'utf-8');
        const sessions = JSON.parse(fileContent);
        // Convert from object to Map
        const sessionsMap = new Map();
        for (const [key, value] of Object.entries(sessions)) {
            sessionsMap.set(key, value);
        }
        return sessionsMap;
    }
    catch (error) {
        if (error instanceof Error && 'code' in error && error.code === "ENOENT") {
            return new Map();
        }
        throw error;
    }
}
async function saveSessionStates(sessionsMap) {
    // Convert from Map to object
    const sessions = {};
    for (const [key, value] of sessionsMap.entries()) {
        sessions[key] = value;
    }
    await fs.writeFile(SESSIONS_FILE_PATH, JSON.stringify(sessions, null, 2), 'utf-8');
}
// Generate a unique session ID
function generateSessionId() {
    return `stud_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
}
// Setup the MCP server
async function main() {
    const knowledgeGraphManager = new KnowledgeGraphManager();
    // Initialize status and priority entities
    await knowledgeGraphManager.initializeStatusAndPriority();
    // Helper function to get current term
    async function getCurrentTerm() {
        // Find the most recent term with status "active"
        const termQuery = await knowledgeGraphManager.searchNodes("entityType:term status:active");
        if (termQuery.entities.length > 0) {
            return termQuery.entities[0].name;
        }
        return null;
    }
    // Create the MCP server using the new API
    const server = new McpServer({
        name: "Context Manager",
        version: "1.0.0"
    });
    // Define a resource that exposes the entire graph
    server.resource("graph", "graph://student", async (uri) => ({
        contents: [{
                uri: uri.href,
                text: JSON.stringify(await knowledgeGraphManager.readGraph(), null, 2)
            }]
    }));
    /**
     * Load context for a specific entity
     */
    server.tool("loadcontext", toolDescriptions["loadcontext"], {
        entityName: z.string(),
        entityType: z.enum(validEntityTypes).optional().describe("Type of entity to load, defaults to 'course'"),
        sessionId: z.string().optional().describe("Session ID from startsession to track context loading")
    }, async ({ entityName, entityType = "course", sessionId }) => {
        try {
            // Validate session if ID is provided
            if (sessionId) {
                const sessionStates = await loadSessionStates();
                if (!sessionStates.has(sessionId)) {
                    console.warn(`Warning: Session ${sessionId} not found, but proceeding with context load`);
                    // Initialize it anyway for more robustness
                    sessionStates.set(sessionId, []);
                    await saveSessionStates(sessionStates);
                }
                // Track that this entity was loaded in this session
                const sessionState = sessionStates.get(sessionId) || [];
                const loadEvent = {
                    type: 'context_loaded',
                    timestamp: new Date().toISOString(),
                    entityName,
                    entityType
                };
                sessionState.push(loadEvent);
                sessionStates.set(sessionId, sessionState);
                await saveSessionStates(sessionStates);
            }
            // Get the entity
            const entityGraph = await knowledgeGraphManager.searchNodes(entityName);
            if (entityGraph.entities.length === 0) {
                throw new Error(`Entity ${entityName} not found`);
            }
            // Find the exact entity by name (case-sensitive match)
            const entity = entityGraph.entities.find(e => e.name === entityName);
            if (!entity) {
                throw new Error(`Entity ${entityName} not found`);
            }
            // Get status and priority
            const status = await knowledgeGraphManager.getEntityStatus(entityName) || "not_started";
            const priority = await knowledgeGraphManager.getEntityPriority(entityName);
            // Format observations for display
            const observationsList = entity.observations.length > 0
                ? entity.observations.map(obs => `- ${obs}`).join("\n")
                : "No observations";
            // Different context loading based on entity type
            let contextMessage = "";
            if (entityType === "course") {
                // Get course overview
                const courseOverview = await knowledgeGraphManager.getCourseOverview(entityName);
                // Format course context message
                const code = entity.observations.find(o => o.startsWith("Code:"))?.substring(5) || "No code";
                const schedule = entity.observations.find(o => o.startsWith("Schedule:"))?.substring(9) || "No schedule";
                const location = entity.observations.find(o => o.startsWith("Location:"))?.substring(9) || "No location";
                // Format lectures
                const lecturesText = courseOverview.lectures?.map((lecture) => {
                    return `- **${lecture.name}**: ${lecture.observations.join(", ")}`;
                }).join("\n") || "No lectures found";
                // Format assignments - use status relation
                const assignmentsText = courseOverview.assignments?.map(async (assignment) => {
                    const assignmentStatus = await knowledgeGraphManager.getEntityStatus(assignment.name) || "not_started";
                    const assignmentPriority = await knowledgeGraphManager.getEntityPriority(assignment.name);
                    const priorityText = assignmentPriority ? `, Priority: ${assignmentPriority}` : "";
                    return `- **${assignment.name}** (Status: ${assignmentStatus}${priorityText}): ${assignment.observations.join(", ")}`;
                });
                const resolvedAssignmentsText = assignmentsText ?
                    await Promise.all(assignmentsText).then(texts => texts.join("\n")) :
                    "No assignments found";
                // Format exams - use status relation
                const examsText = courseOverview.exams?.map(async (exam) => {
                    const examStatus = await knowledgeGraphManager.getEntityStatus(exam.name) || "not_started";
                    return `- **${exam.name}** (Status: ${examStatus}): ${exam.observations.join(", ")}`;
                });
                const resolvedExamsText = examsText ?
                    await Promise.all(examsText).then(texts => texts.join("\n")) :
                    "No exams found";
                // Format concepts
                const conceptsText = courseOverview.concepts?.map((concept) => {
                    return `- **${concept.name}**: ${concept.observations.join(", ")}`;
                }).join("\n") || "No concepts found";
                // Format resources
                const resourcesText = courseOverview.resources?.map((resource) => {
                    return `- **${resource.name}**: ${resource.observations.join(", ")}`;
                }).join("\n") || "No resources found";
                // Add professor info if available
                const professorText = courseOverview.professor ?
                    `**Professor**: ${courseOverview.professor.name}
${courseOverview.professor.observations.join("\n")}` : "No professor information";
                // Add term info if available
                const termText = courseOverview.term ?
                    `**Term**: ${courseOverview.term.name}` : "No term information";
                contextMessage = `# Course Context: ${entityName}

## Course Overview
- **Code**: ${code}
- **Status**: ${status}
- **Priority**: ${priority || "N/A"}
- **Schedule**: ${schedule}
- **Location**: ${location}

## Observations
${observationsList}

- ${termText}
- ${professorText}

## Lectures
${lecturesText}

## Assignments
${resolvedAssignmentsText}

## Exams
${resolvedExamsText}

## Key Concepts
${conceptsText}

## Resources
${resourcesText}`;
            }
            else if (entityType === "assignment") {
                // Get assignment status
                const assignmentStatus = await knowledgeGraphManager.getAssignmentStatus(entityName);
                // Get course name
                const courseName = assignmentStatus.course?.name || "Unknown course";
                // Format assignment context using relations instead of observations
                const dueDate = entity.observations.find(o => o.startsWith("Due:"))?.substring(4) || "No due date";
                const points = entity.observations.find(o => o.startsWith("Points:"))?.substring(7) || "Not specified";
                const instructions = entity.observations.find(o => o.startsWith("Instructions:"))?.substring(13) || "No instructions provided";
                // Calculate time remaining
                let timeRemainingText = "No due date specified";
                if (assignmentStatus.timeRemaining !== null) {
                    if (assignmentStatus.isOverdue) {
                        timeRemainingText = `OVERDUE by ${Math.abs(assignmentStatus.daysRemaining)} days`;
                    }
                    else {
                        timeRemainingText = `${assignmentStatus.daysRemaining} days remaining`;
                    }
                }
                // Format related concepts
                const conceptsText = assignmentStatus.concepts?.map((concept) => {
                    return `- **${concept.name}**: ${concept.observations.join(", ")}`;
                }).join("\n") || "No related concepts found";
                // Format related resources
                const resourcesText = assignmentStatus.resources?.map((resource) => {
                    return `- **${resource.name}**: ${resource.observations.join(", ")}`;
                }).join("\n") || "No resources found";
                // Format notes
                const notesText = assignmentStatus.notes?.map((note) => {
                    return `- **${note.name}**: ${note.observations.join(", ")}`;
                }).join("\n") || "No notes found";
                contextMessage = `# Assignment Context: ${entityName}

## Assignment Overview
- **Course**: ${courseName}
- **Status**: ${status}
- **Priority**: ${priority || "N/A"}
- **Due Date**: ${dueDate}
- **Points**: ${points}
- **Time Remaining**: ${timeRemainingText}

## Observations
${observationsList}

## Instructions
${instructions}

## Related Concepts
${conceptsText}

## Helpful Resources
${resourcesText}

## Your Notes
${notesText}`;
            }
            else if (entityType === "exam") {
                // Get exam prep information
                const examPrep = await knowledgeGraphManager.getExamPrep(entityName);
                // Format exam context
                const examDate = entity.observations.find(o => o.startsWith("Date:"))?.substring(5) || "No date scheduled";
                const examLocation = entity.observations.find(o => o.startsWith("Location:"))?.substring(9) || "No location specified";
                const examFormat = entity.observations.find(o => o.startsWith("Format:"))?.substring(7) || "No format specified";
                const examDuration = entity.observations.find(o => o.startsWith("Duration:"))?.substring(9) || "No duration specified";
                // Calculate time remaining
                let timeRemainingText = "No exam date specified";
                if (examPrep.daysRemaining !== null) {
                    timeRemainingText = `${examPrep.daysRemaining} days until exam`;
                }
                // Get course name
                const courseName = examPrep.course?.name || "Unknown course";
                // Format concepts covered
                const conceptsText = examPrep.concepts?.map((concept) => {
                    return `- **${concept.name}**: ${concept.observations.join(", ")}`;
                }).join("\n") || "No concepts listed";
                // Format study resources
                const resourcesText = examPrep.resources?.map((resource) => {
                    return `- **${resource.name}**: ${resource.observations.join(", ")}`;
                }).join("\n") || "No resources found";
                // Format lectures
                const lecturesText = examPrep.lectures?.map((lecture) => {
                    return `- **${lecture.name}**: ${lecture.observations.join(", ")}`;
                }).join("\n") || "No lectures found";
                contextMessage = `# Exam Context: ${entityName}

## Exam Details
- **Course**: ${courseName}
- **Date**: ${examDate}
- **Time Remaining**: ${timeRemainingText}
- **Location**: ${examLocation}
- **Format**: ${examFormat}
- **Duration**: ${examDuration}

## Concepts to Study
${conceptsText}

## Key Lectures
${lecturesText}

## Study Resources
${resourcesText}`;
            }
            else if (entityType === "concept") {
                // Get related concepts
                const relatedConceptsData = await knowledgeGraphManager.findRelatedConcepts(entityName);
                // Format concept context
                const description = entity.observations.find(o => !o.startsWith("Level:")) || "No description available";
                const level = entity.observations.find(o => o.startsWith("Level:"))?.substring(6) || "Beginner";
                // Format related concepts
                const relatedConceptsText = relatedConceptsData.relatedConcepts?.map((related) => {
                    return `- **${related.concept.name}**: ${related.relationPath.join(' → ')}`;
                }).join("\n") || "No related concepts found";
                // Format courses that cover this concept
                const coursesText = relatedConceptsData.courses?.map((course) => {
                    return `- **${course.name}**: ${course.observations.join(", ")}`;
                }).join("\n") || "No courses found";
                // Format resources about this concept
                const resourcesText = relatedConceptsData.resources?.map((resource) => {
                    return `- **${resource.name}**: ${resource.observations.join(", ")}`;
                }).join("\n") || "No resources found";
                contextMessage = `# Concept Context: ${entityName}

## Concept Details
- **Difficulty Level**: ${level}
- **Description**: ${description}

## Related Concepts
${relatedConceptsText}

## Covered in Courses
${coursesText}

## Learning Resources
${resourcesText}`;
            }
            else if (entityType === "term") {
                // Get term overview
                const termOverview = await knowledgeGraphManager.getTermOverview(entityName);
                // Format term context
                const startDate = entity.observations.find(o => o.startsWith("StartDate:"))?.substring(10) || "No start date";
                const endDate = entity.observations.find(o => o.startsWith("EndDate:"))?.substring(8) || "No end date";
                const status = entity.observations.find(o => o.startsWith("Status:"))?.substring(7) || "Unknown status";
                // Format courses in this term
                const coursesText = termOverview.courseData?.map((courseData) => {
                    return `- **${courseData.course.name}**: ${courseData.info.code || "No code"}, ${courseData.info.status}, ${courseData.progress.completionRate || 0}% complete`;
                }).join("\n\n") || "No courses found";
                // Format upcoming deadlines
                const deadlinesText = termOverview.upcomingDeadlines?.map((deadline) => {
                    return `- **${deadline.entity.name}** (${deadline.entity.entityType})
  Course: ${deadline.course.name}
  Due: ${deadline.dueDate} (${deadline.daysRemaining} days remaining)`;
                }).join("\n\n") || "No upcoming deadlines";
                contextMessage = `# Term Context: ${entityName}

## Term Details
- **Start Date**: ${startDate}
- **End Date**: ${endDate}
- **Status**: ${status}

## Courses This Term
${coursesText}

## Upcoming Deadlines
${deadlinesText}`;
            }
            else {
                // Generic entity context for other entity types
                // Find all relations involving this entity
                const relations = await knowledgeGraphManager.openNodes([entityName]);
                // Build a text representation of related entities
                const incomingRelations = relations.relations.filter(r => r.to === entityName);
                const outgoingRelations = relations.relations.filter(r => r.from === entityName);
                const incomingText = incomingRelations.map(rel => {
                    const sourceEntity = relations.entities.find(e => e.name === rel.from);
                    if (!sourceEntity)
                        return null;
                    return `- **${sourceEntity.name}** (${sourceEntity.entityType}) → ${rel.relationType} → ${entityName}`;
                }).filter(Boolean).join("\n") || "No incoming relations";
                const outgoingText = outgoingRelations.map(rel => {
                    const targetEntity = relations.entities.find(e => e.name === rel.to);
                    if (!targetEntity)
                        return null;
                    return `- **${entityName}** → ${rel.relationType} → **${targetEntity.name}** (${targetEntity.entityType})`;
                }).filter(Boolean).join("\n") || "No outgoing relations";
                // Format observations
                const observationsText = entity.observations.map(obs => `- ${obs}`).join("\n") || "No observations";
                contextMessage = `# Entity Context: ${entityName} (${entityType})

## Observations
${observationsText}

## Incoming Relations
${incomingText}

## Outgoing Relations
${outgoingText}`;
            }
            return {
                content: [{
                        type: "text",
                        text: contextMessage
                    }]
            };
        }
        catch (error) {
            return {
                content: [{
                        type: "text",
                        text: JSON.stringify({
                            success: false,
                            error: error instanceof Error ? error.message : String(error)
                        }, null, 2)
                    }]
            };
        }
    });
    // Helper function to process each stage of endsession
    async function processStage(params, previousStages) {
        // Process based on the stage
        switch (params.stage) {
            case "summary":
                // Process summary stage
                return {
                    stage: "summary",
                    stageNumber: params.stageNumber,
                    analysis: params.analysis || "",
                    stageData: params.stageData || {
                        summary: "",
                        duration: "",
                        course: ""
                    },
                    completed: !params.nextStageNeeded
                };
            case "conceptsLearned":
                // Process concepts learned stage
                return {
                    stage: "conceptsLearned",
                    stageNumber: params.stageNumber,
                    analysis: params.analysis || "",
                    stageData: params.stageData || { concepts: [] },
                    completed: !params.nextStageNeeded
                };
            case "assignmentUpdates":
                // Process assignment updates stage
                return {
                    stage: "assignmentUpdates",
                    stageNumber: params.stageNumber,
                    analysis: params.analysis || "",
                    stageData: params.stageData || { updates: [] },
                    completed: !params.nextStageNeeded
                };
            case "newConcepts":
                // Process new concepts stage
                return {
                    stage: "newConcepts",
                    stageNumber: params.stageNumber,
                    analysis: params.analysis || "",
                    stageData: params.stageData || { concepts: [] },
                    completed: !params.nextStageNeeded
                };
            case "courseStatus":
                // Process course status stage
                return {
                    stage: "courseStatus",
                    stageNumber: params.stageNumber,
                    analysis: params.analysis || "",
                    stageData: params.stageData || {
                        courseStatus: "",
                        courseObservation: ""
                    },
                    completed: !params.nextStageNeeded
                };
            case "assembly":
                // Final assembly stage - compile all arguments for end-session
                return {
                    stage: "assembly",
                    stageNumber: params.stageNumber,
                    analysis: "Final assembly of end-session arguments",
                    stageData: assembleEndSessionArgs(previousStages),
                    completed: true
                };
            default:
                throw new Error(`Unknown stage: ${params.stage}`);
        }
    }
    // Helper function to assemble the final end-session arguments
    function assembleEndSessionArgs(stages) {
        const summaryStage = stages.find(s => s.stage === "summary");
        const conceptsLearnedStage = stages.find(s => s.stage === "conceptsLearned");
        const assignmentUpdatesStage = stages.find(s => s.stage === "assignmentUpdates");
        const newConceptsStage = stages.find(s => s.stage === "newConcepts");
        const courseStatusStage = stages.find(s => s.stage === "courseStatus");
        return {
            summary: summaryStage?.stageData?.summary || "",
            duration: summaryStage?.stageData?.duration || "unknown",
            course: summaryStage?.stageData?.course || "",
            conceptsLearned: JSON.stringify(conceptsLearnedStage?.stageData?.concepts || []),
            assignmentUpdates: JSON.stringify(assignmentUpdatesStage?.stageData?.updates || []),
            courseStatus: courseStatusStage?.stageData?.courseStatus || "",
            courseObservation: courseStatusStage?.stageData?.courseObservation || "",
            newConcepts: JSON.stringify(newConceptsStage?.stageData?.concepts || [])
        };
    }
    /**
     * End session by processing all stages and recording the final results.
     * Only use this tool if the user asks for it.
     *
     * Usage examples:
     *
     * 1. Starting the end session process with the summary stage:
     * {
     *   "sessionId": "stu_1234567890_abc123",  // From startsession
     *   "stage": "summary",
     *   "stageNumber": 1,
     *   "totalStages": 6,
     *   "analysis": "Analyzed progress on studying for the final exam",
     *   "stageData": {
     *     "summary": "Reviewed course materials and completed practice problems",
     *     "duration": "2 hours",
     *     "focus": "Data Structures"  // Course name
     *   },
     *   "nextStageNeeded": true,  // More stages coming
     *   "isRevision": false
     * }
     *
     * 2. Middle stage for concepts:
     * {
     *   "sessionId": "stu_1234567890_abc123",
     *   "stage": "conceptsLearned",
     *   "stageNumber": 2,
     *   "totalStages": 6,
     *   "analysis": "Listed key concepts studied",
     *   "stageData": {
     *     "concepts": [
     *       "Balanced binary trees",
     *       "Red-black trees",
     *       "Tree traversal algorithms"
     *     ]
     *   },
     *   "nextStageNeeded": true,
     *   "isRevision": false
     * }
     *
     * 3. Final assembly stage:
     * {
     *   "sessionId": "stu_1234567890_abc123",
     *   "stage": "assembly",
     *   "stageNumber": 6,
     *   "totalStages": 6,
     *   "nextStageNeeded": false,  // This completes the session
     *   "isRevision": false
     * }
     */
    server.tool("endsession", toolDescriptions["endsession"], {
        sessionId: z.string().describe("The unique session identifier obtained from startsession"),
        stage: z.string().describe("Current stage of analysis: 'summary', 'conceptsLearned', 'assignmentProgress', 'questions', 'nextSteps', or 'assembly'"),
        stageNumber: z.number().int().positive().describe("The sequence number of the current stage (starts at 1)"),
        totalStages: z.number().int().positive().describe("Total number of stages in the workflow (typically 5 for standard workflow)"),
        analysis: z.string().optional().describe("Text analysis or observations for the current stage"),
        stageData: z.record(z.string(), z.any()).optional().describe(`Stage-specific data structure - format depends on the stage type:
      - For 'summary' stage: { summary: "Session summary text", duration: "2 hours", focus: "CourseName" }
      - For 'conceptsLearned' stage: { concepts: ["Concept A", "Concept B", "Concept C"] }
      - For 'assignmentProgress' stage: { assignments: [{ name: "Assignment1", status: "completed" }, { name: "Assignment2", status: "in_progress" }] }
      - For 'questions' stage: { questions: ["Question about topic X", "Question about concept Y"] }
      - For 'nextSteps' stage: { nextSteps: ["Review chapter 7", "Complete practice problems", "Attend office hours"] }
      - For 'assembly' stage: no stageData needed - automatic assembly of previous stages`),
        nextStageNeeded: z.boolean().describe("Whether additional stages are needed after this one (false for final stage)"),
        isRevision: z.boolean().optional().describe("Whether this is revising a previous stage"),
        revisesStage: z.number().int().positive().optional().describe("If revising, which stage number is being revised")
    }, async (params) => {
        try {
            // Load session states from persistent storage
            const sessionStates = await loadSessionStates();
            // Validate session ID
            if (!sessionStates.has(params.sessionId)) {
                return {
                    content: [{
                            type: "text",
                            text: JSON.stringify({
                                success: false,
                                error: `Session with ID ${params.sessionId} not found. Please start a new session with startsession.`
                            }, null, 2)
                        }]
                };
            }
            // Get or initialize session state
            let sessionState = sessionStates.get(params.sessionId) || [];
            // Process the current stage
            const stageResult = await processStage(params, sessionState);
            // Store updated state
            if (params.isRevision && params.revisesStage) {
                // Find the analysis stages in the session state
                const analysisStages = sessionState.filter(item => item.type === 'analysis_stage') || [];
                if (params.revisesStage <= analysisStages.length) {
                    // Replace the revised stage
                    analysisStages[params.revisesStage - 1] = {
                        type: 'analysis_stage',
                        ...stageResult
                    };
                }
                else {
                    // Add as a new stage
                    analysisStages.push({
                        type: 'analysis_stage',
                        ...stageResult
                    });
                }
                // Update the session state with the modified analysis stages
                sessionState = [
                    ...sessionState.filter(item => item.type !== 'analysis_stage'),
                    ...analysisStages
                ];
            }
            else {
                // Add new stage
                sessionState.push({
                    type: 'analysis_stage',
                    ...stageResult
                });
            }
            // Update in persistent storage
            sessionStates.set(params.sessionId, sessionState);
            await saveSessionStates(sessionStates);
            // Check if this is the final assembly stage and no more stages are needed
            if (params.stage === "assembly" && !params.nextStageNeeded) {
                // Get the assembled arguments
                const args = stageResult.stageData;
                try {
                    // Parse arguments
                    const summary = args.summary;
                    const duration = args.duration;
                    const course = args.course;
                    const conceptsLearned = args.conceptsLearned ? JSON.parse(args.conceptsLearned) : [];
                    const assignmentUpdates = args.assignmentUpdates ? JSON.parse(args.assignmentUpdates) : [];
                    const courseStatus = args.courseStatus;
                    const courseObservation = args.courseObservation;
                    const newConcepts = args.newConcepts ? JSON.parse(args.newConcepts) : [];
                    // Create concept entities for new concepts learned
                    const timestamp = new Date().getTime();
                    const conceptEntities = conceptsLearned.map((concept, i) => ({
                        name: `Concept_${timestamp}_${i + 1}`,
                        entityType: "concept",
                        observations: [concept]
                    }));
                    if (conceptEntities.length > 0) {
                        await knowledgeGraphManager.createEntities(conceptEntities);
                        // Link concepts to course
                        const conceptRelations = conceptEntities.map((concept) => ({
                            from: course,
                            to: concept.name,
                            relationType: "contains"
                        }));
                        await knowledgeGraphManager.createRelations(conceptRelations);
                    }
                    // Update assignment statuses using the relation-based approach
                    for (const assignment of assignmentUpdates) {
                        try {
                            // Map status values to standard status values
                            let statusValue;
                            switch (assignment.status.toLowerCase()) {
                                case "completed":
                                case "complete":
                                case "done":
                                case "finished":
                                case "submitted":
                                    statusValue = "complete";
                                    break;
                                case "in_progress":
                                case "started":
                                case "working":
                                case "active":
                                    statusValue = "in_progress";
                                    break;
                                default:
                                    statusValue = "not_started";
                            }
                            // Set the status using the new method
                            await knowledgeGraphManager.setEntityStatus(assignment.name, statusValue);
                            // If completed, also create a 'resolves' relation to the course
                            if (statusValue === "complete") {
                                await knowledgeGraphManager.createRelations([{
                                        from: course,
                                        to: assignment.name,
                                        relationType: "resolves"
                                    }]);
                            }
                        }
                        catch (error) {
                            console.error(`Error updating status for assignment ${assignment.name}:`, error);
                        }
                    }
                    // Update course status using the relation-based approach
                    try {
                        // Map course status to standard values
                        let courseStatusValue;
                        switch (courseStatus.toLowerCase()) {
                            case "completed":
                            case "complete":
                            case "done":
                            case "finished":
                                courseStatusValue = "complete";
                                break;
                            case "in_progress":
                            case "active":
                            case "current":
                                courseStatusValue = "in_progress";
                                break;
                            default:
                                courseStatusValue = "not_started";
                        }
                        // Set the course status using the new method
                        await knowledgeGraphManager.setEntityStatus(course, courseStatusValue);
                        // Add observation if provided
                        if (courseObservation) {
                            await knowledgeGraphManager.addObservations(course, [courseObservation]);
                        }
                    }
                    catch (error) {
                        console.error(`Error updating status for course ${course}:`, error);
                    }
                    // Create new concept entities
                    if (newConcepts && newConcepts.length > 0) {
                        const newConceptEntities = newConcepts.map((concept) => ({
                            name: concept.name,
                            entityType: "concept",
                            observations: [concept.description]
                        }));
                        await knowledgeGraphManager.createEntities(newConceptEntities);
                        // Link concepts to course
                        const conceptRelations = newConceptEntities.map((concept) => ({
                            from: course,
                            to: concept.name,
                            relationType: "contains"
                        }));
                        await knowledgeGraphManager.createRelations(conceptRelations);
                    }
                    // Record session completion in persistent storage
                    sessionState.push({
                        type: 'session_completed',
                        timestamp: new Date().toISOString(),
                        summary: summary,
                        course: course
                    });
                    sessionStates.set(params.sessionId, sessionState);
                    await saveSessionStates(sessionStates);
                    // Prepare the summary message
                    const summaryMessage = `# Study Session Recorded

I've recorded your study session focusing on ${course}.

## Concepts Learned
${conceptsLearned.map((c) => `- ${c}`).join('\n') || "No specific concepts recorded."}

## Assignment Updates
${assignmentUpdates.map((a) => `- ${a.name}: ${a.status}`).join('\n') || "No assignment updates."}

## Course Status
Course ${course} has been updated to: ${courseStatus}

${newConcepts && newConcepts.length > 0 ? `## New Concepts Added
${newConcepts.map((c) => `- ${c.name}: ${c.description}`).join('\n')}` : "No new concepts added."}

## Session Summary
${summary}

Would you like me to perform any additional updates to your student knowledge graph?`;
                    // Return the final result with the session recorded message
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({
                                    success: true,
                                    stageCompleted: params.stage,
                                    nextStageNeeded: false,
                                    stageResult: stageResult,
                                    sessionRecorded: true,
                                    summaryMessage: summaryMessage
                                }, null, 2)
                            }]
                    };
                }
                catch (error) {
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({
                                    success: false,
                                    error: `Error recording study session: ${error instanceof Error ? error.message : String(error)}`
                                }, null, 2)
                            }]
                    };
                }
            }
            else {
                // This is not the final stage or more stages are needed
                // Return intermediate result
                return {
                    content: [{
                            type: "text",
                            text: JSON.stringify({
                                success: true,
                                stageCompleted: params.stage,
                                nextStageNeeded: params.nextStageNeeded,
                                stageResult: stageResult,
                                endSessionArgs: params.stage === "assembly" ? stageResult.stageData : null
                            }, null, 2)
                        }]
                };
            }
        }
        catch (error) {
            return {
                content: [{
                        type: "text",
                        text: JSON.stringify({
                            success: false,
                            error: error instanceof Error ? error.message : String(error)
                        }, null, 2)
                    }]
            };
        }
    });
    /**
     * Start a new study session. Returns session ID, recent study sessions, active courses, and upcoming deadlines.
     * The output allows the user to easily choose what to focus on and which specific context to load.
     */
    server.tool("startsession", toolDescriptions["startsession"], {}, async () => {
        try {
            // Generate a unique session ID
            const sessionId = generateSessionId();
            // Get the current active term
            const currentTerm = await getCurrentTerm();
            // Get recent sessions from persistent storage instead of entities
            const sessionStates = await loadSessionStates();
            // Initialize the session state
            sessionStates.set(sessionId, []);
            await saveSessionStates(sessionStates);
            // Convert sessions map to array, sort, and take most recent ones
            const recentSessions = Array.from(sessionStates.entries())
                .map(([id, stages]) => {
                // Extract summary data from the first stage (if it exists)
                const summaryStage = stages.find(s => s.stage === "summary");
                return {
                    id,
                    course: summaryStage?.stageData?.course || "Unknown course",
                    summary: summaryStage?.stageData?.summary || "No summary available"
                };
            })
                .slice(0, 3); // Default to 3 recent sessions
            // Get active courses - look for courses with "in_progress" status
            const allCoursesQuery = await knowledgeGraphManager.searchNodes("entityType:course");
            const courses = [];
            // Filter courses with active status using the relation
            for (const course of allCoursesQuery.entities) {
                const status = await knowledgeGraphManager.getEntityStatus(course.name);
                if (status === "in_progress" || status === "active" || !status) {
                    courses.push(course);
                }
            }
            // Get upcoming deadlines (default to 14 days)
            const deadlines = await knowledgeGraphManager.getUpcomingDeadlines(currentTerm || undefined, undefined, 14);
            // Get recent concepts studied
            const recentConceptsQuery = await knowledgeGraphManager.searchNodes("entityType:concept");
            const recentConcepts = recentConceptsQuery.entities.slice(0, 5);
            // Prepare message content
            const coursesText = courses.map(async (c) => {
                const status = await knowledgeGraphManager.getEntityStatus(c.name) || "not_started";
                const priority = await knowledgeGraphManager.getEntityPriority(c.name);
                const priorityText = priority ? ` (Priority: ${priority})` : "";
                const preview = c.observations.length > 0
                    ? `${c.observations[0].substring(0, 60)}${c.observations[0].length > 60 ? '...' : ''}`
                    : "No description";
                return `- **${c.name}** (Status: ${status}${priorityText}): ${preview}`;
            });
            const resolvedCoursesText = await Promise.all(coursesText);
            const sessionsText = recentSessions.map(s => {
                return `- ${s.course} - ${s.summary.substring(0, 60)}${s.summary.length > 60 ? '...' : ''}`;
            }).join("\n");
            const deadlinesText = deadlines.deadlines.map((d) => {
                const daysUntil = d.daysRemaining;
                return `- **${d.entity.name}** (${d.course.name}): Due in ${daysUntil} day${daysUntil !== 1 ? 's' : ''}`;
            }).join("\n");
            const conceptsText = recentConcepts.map(c => {
                const preview = c.observations.length > 0
                    ? `${c.observations[0].substring(0, 60)}${c.observations[0].length > 60 ? '...' : ''}`
                    : "No description";
                return `- **${c.name}**: ${preview}`;
            }).join("\n");
            return {
                content: [{
                        type: "text",
                        text: `# Choose what to focus on in this session

## Recent Study Sessions
${sessionsText || "No recent sessions found."}

## Current Courses
${resolvedCoursesText.join("\n") || "No active courses found."}

## Upcoming Deadlines (Next 14 Days)
${deadlinesText || "No upcoming deadlines in the next 14 days."}

## Recently Studied Concepts
${conceptsText || "No recently studied concepts found."}

To load the context for a specific entity, use the \`loadcontext\` tool with the entity name and session ID - ${sessionId}`
                    }]
            };
        }
        catch (error) {
            return {
                content: [{
                        type: "text",
                        text: JSON.stringify({
                            success: false,
                            error: error instanceof Error ? error.message : String(error)
                        }, null, 2)
                    }]
            };
        }
    });
    /**
     * Create new entities, relations, and observations.
     */
    server.tool("buildcontext", toolDescriptions["buildcontext"], {
        type: z.enum(["entities", "relations", "observations"]).describe("Type of creation operation: 'entities', 'relations', or 'observations'"),
        data: z.array(z.any()).describe("Data for the creation operation, structure varies by type but must be an array")
    }, async ({ type, data }) => {
        try {
            let result;
            switch (type) {
                case "entities":
                    // Validate entity types
                    for (const entity of data) {
                        validateEntityType(entity.entityType);
                    }
                    // Ensure entities match the Entity interface
                    const typedEntities = data.map((e) => ({
                        name: e.name,
                        entityType: e.entityType,
                        observations: e.observations
                    }));
                    result = await knowledgeGraphManager.createEntities(typedEntities);
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, created: result }, null, 2)
                            }]
                    };
                case "relations":
                    // Ensure relations match the Relation interface
                    const typedRelations = data.map((r) => ({
                        from: r.from,
                        to: r.to,
                        relationType: r.relationType
                    }));
                    result = await knowledgeGraphManager.createRelations(typedRelations);
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, created: result }, null, 2)
                            }]
                    };
                case "observations":
                    for (const item of data) {
                        if (item.entityName && Array.isArray(item.contents)) {
                            await knowledgeGraphManager.addObservations(item.entityName, item.contents);
                        }
                    }
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, message: "Added observations to entities" }, null, 2)
                            }]
                    };
                default:
                    throw new Error(`Invalid type: ${type}. Must be 'entities', 'relations', or 'observations'.`);
            }
        }
        catch (error) {
            return {
                content: [{
                        type: "text",
                        text: JSON.stringify({
                            success: false,
                            error: error instanceof Error ? error.message : String(error)
                        }, null, 2)
                    }]
            };
        }
    });
    /**
     * Delete entities, relations, or observations.
     */
    server.tool("deletecontext", toolDescriptions["deletecontext"], {
        type: z.enum(["entities", "relations", "observations"]).describe("Type of deletion operation: 'entities', 'relations', or 'observations'"),
        data: z.array(z.any()).describe("Data for the deletion operation, structure varies by type but must be an array")
    }, async ({ type, data }) => {
        try {
            switch (type) {
                case "entities":
                    await knowledgeGraphManager.deleteEntities(data);
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, message: `Deleted ${data.length} entities` }, null, 2)
                            }]
                    };
                case "relations":
                    // Ensure relations match the Relation interface
                    const typedRelations = data.map((r) => ({
                        from: r.from,
                        to: r.to,
                        relationType: r.relationType
                    }));
                    await knowledgeGraphManager.deleteRelations(typedRelations);
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, message: `Deleted ${data.length} relations` }, null, 2)
                            }]
                    };
                case "observations":
                    // Ensure deletions match the required interface
                    const typedDeletions = data.map((d) => ({
                        entityName: d.entityName,
                        observations: d.observations
                    }));
                    await knowledgeGraphManager.deleteObservations(typedDeletions);
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, message: `Deleted observations from ${data.length} entities` }, null, 2)
                            }]
                    };
                default:
                    throw new Error(`Invalid type: ${type}. Must be 'entities', 'relations', or 'observations'.`);
            }
        }
        catch (error) {
            return {
                content: [{
                        type: "text",
                        text: JSON.stringify({
                            success: false,
                            error: error instanceof Error ? error.message : String(error)
                        }, null, 2)
                    }]
            };
        }
    });
    /**
     * Get information about the knowledge graph, search for nodes, get course overview, get upcoming deadlines, get assignment status, get exam prep, find related concepts, track lecture notes, or get term overview.
     */
    server.tool("advancedcontext", toolDescriptions["advancedcontext"], {
        type: z.enum(["graph", "search", "nodes", "course", "deadlines", "assignment", "exam", "concepts", "lecture", "term"]).describe("Type of get operation: 'graph', 'search', 'nodes', 'course', 'deadlines', 'assignment', 'exam', 'concepts', 'lecture', or 'term'"),
        params: z.record(z.string(), z.any()).describe("Parameters for the operation, structure varies by type")
    }, async ({ type, params }) => {
        try {
            let result;
            switch (type) {
                case "graph":
                    result = await knowledgeGraphManager.readGraph();
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, graph: result }, null, 2)
                            }]
                    };
                case "search":
                    result = await knowledgeGraphManager.searchNodes(params.query);
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, results: result }, null, 2)
                            }]
                    };
                case "nodes":
                    result = await knowledgeGraphManager.openNodes(params.names);
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, nodes: result }, null, 2)
                            }]
                    };
                case "course":
                    result = await knowledgeGraphManager.getCourseOverview(params.courseName);
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, course: result }, null, 2)
                            }]
                    };
                case "deadlines":
                    result = await knowledgeGraphManager.getUpcomingDeadlines(params.termName, params.courseName, params.daysAhead || 14);
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, deadlines: result }, null, 2)
                            }]
                    };
                case "assignment":
                    result = await knowledgeGraphManager.getAssignmentStatus(params.assignmentName);
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, assignment: result }, null, 2)
                            }]
                    };
                case "exam":
                    result = await knowledgeGraphManager.getExamPrep(params.examName);
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, exam: result }, null, 2)
                            }]
                    };
                case "concepts":
                    result = await knowledgeGraphManager.findRelatedConcepts(params.conceptName, params.depth || 1);
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, concepts: result }, null, 2)
                            }]
                    };
                case "lecture":
                    result = await knowledgeGraphManager.trackLectureNotes(params.courseName);
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, lectures: result }, null, 2)
                            }]
                    };
                case "term":
                    result = await knowledgeGraphManager.getTermOverview(params.termName);
                    return {
                        content: [{
                                type: "text",
                                text: JSON.stringify({ success: true, term: result }, null, 2)
                            }]
                    };
                default:
                    throw new Error(`Invalid type: ${type}. Must be one of the supported get operation types.`);
            }
        }
        catch (error) {
            return {
                content: [{
                        type: "text",
                        text: JSON.stringify({
                            success: false,
                            error: error instanceof Error ? error.message : String(error)
                        }, null, 2)
                    }]
            };
        }
    });
    // Start the server
    try {
        const transport = new StdioServerTransport();
        await server.connect(transport);
    }
    catch (error) {
        console.error("Error starting server:", error);
        process.exit(1);
    }
}
main().catch(error => {
    console.error('Fatal error:', error);
    process.exit(1);
});
// Export the KnowledgeGraphManager class for testing
export { KnowledgeGraphManager };

```
Page 5/9FirstPrevNextLast