#
tokens: 33079/50000 1/128 files (page 7/13)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 7 of 13. Use http://codebase.md/tejpalvirk/contextmanager?lines=true&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
   1 | #!/usr/bin/env node
   2 | 
   3 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
   4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
   5 | import { promises as fs } from 'fs';
   6 | import path from 'path';
   7 | import { fileURLToPath } from 'url';
   8 | import { z } from "zod";
   9 | import { readFileSync, existsSync } from "fs";
  10 | 
  11 | // Define memory file path using environment variable with fallback
  12 | const parentPath = path.dirname(fileURLToPath(import.meta.url));
  13 | const defaultMemoryPath = path.join(parentPath, 'memory.json');
  14 | const defaultSessionsPath = path.join(parentPath, 'sessions.json');
  15 | 
  16 | // Properly handle absolute and relative paths for MEMORY_FILE_PATH
  17 | const MEMORY_FILE_PATH = process.env.MEMORY_FILE_PATH
  18 |   ? path.isAbsolute(process.env.MEMORY_FILE_PATH)
  19 |     ? process.env.MEMORY_FILE_PATH  // Use absolute path as is
  20 |     : path.join(process.cwd(), process.env.MEMORY_FILE_PATH)  // Relative to current working directory
  21 |   : defaultMemoryPath;  // Default fallback
  22 | 
  23 | // Properly handle absolute and relative paths for SESSIONS_FILE_PATH
  24 | const SESSIONS_FILE_PATH = process.env.SESSIONS_FILE_PATH
  25 |   ? path.isAbsolute(process.env.SESSIONS_FILE_PATH)
  26 |     ? process.env.SESSIONS_FILE_PATH  // Use absolute path as is
  27 |     : path.join(process.cwd(), process.env.SESSIONS_FILE_PATH)  // Relative to current working directory
  28 |   : defaultSessionsPath;  // Default fallback
  29 | 
  30 | // Qualitative Research specific entity types
  31 | const VALID_ENTITY_TYPES = [
  32 |   'project',         // Overall research study
  33 |   'participant',     // Research subjects
  34 |   'interview',       // Formal conversation with participants
  35 |   'observation',     // Field notes from observational research
  36 |   'document',        // External materials being analyzed
  37 |   'code',            // Labels applied to data segments
  38 |   'codeGroup',       // Categories or families of related codes
  39 |   'memo',            // Researcher's analytical notes
  40 |   'theme',           // Emergent patterns across data
  41 |   'quote',           // Notable excerpts from data sources
  42 |   'literature',      // Academic sources
  43 |   'researchQuestion', // Formal questions guiding the study
  44 |   'finding',         // Results or conclusions
  45 |   'status',          // Status entity type
  46 |   'priority'         // Priority entity type
  47 | ];
  48 | 
  49 | // Qualitative Research specific relation types
  50 | const VALID_RELATION_TYPES = [
  51 |   'participated_in',  // Links participants to interviews/observations
  52 |   'codes',            // Shows which codes apply to which data
  53 |   'contains',         // Hierarchical relationship (e.g., codegroup contains codes)
  54 |   'supports',         // Data supporting a theme or finding
  55 |   'contradicts',      // Data contradicting a theme or finding
  56 |   'answers',          // Data addressing a research question
  57 |   'cites',            // References to literature
  58 |   'followed_by',      // Temporal sequence
  59 |   'related_to',       // General connection
  60 |   'reflects_on',      // Memo reflecting on data/code/theme
  61 |   'compares',         // Comparative relationship
  62 |   'conducted_by',     // Person who conducted data collection
  63 |   'transcribed_by',   // Person who transcribed data
  64 |   'part_of',          // Entity is part of another entity
  65 |   'derived_from',     // Entity is derived from another entity
  66 |   'collected_on',     // Data collection date
  67 |   'analyzes',         // Analysis relationship
  68 |   'triangulates_with', // Triangulation between data sources
  69 |   'has_status',       // Entity has a specific status
  70 |   'has_priority',     // Entity has a specific priority
  71 |   'precedes'          // Entity comes before another entity in sequence
  72 | ];
  73 | 
  74 | // Status values for different entity types in qualitative research
  75 | const STATUS_VALUES = {
  76 |   project: ['planning', 'data_collection', 'analysis', 'writing', 'complete'],
  77 |   interview: ['scheduled', 'conducted', 'transcribed', 'coded', 'analyzed'],
  78 |   observation: ['planned', 'conducted', 'documented', 'coded', 'analyzed'],
  79 |   code: ['initial', 'revised', 'final'],
  80 |   theme: ['emerging', 'developing', 'established'],
  81 |   finding: ['preliminary', 'draft', 'final']
  82 | };
  83 | 
  84 | // Define valid status values for all entity types
  85 | const VALID_STATUS_VALUES = [
  86 |   'planning', 'data_collection', 'analysis', 'writing', 'complete',
  87 |   'scheduled', 'conducted', 'transcribed', 'coded', 'analyzed',
  88 |   'planned', 'documented',
  89 |   'initial', 'revised', 'final',
  90 |   'emerging', 'developing', 'established',
  91 |   'preliminary', 'draft',
  92 |   'active', 'in_progress', 'not_started'
  93 | ];
  94 | 
  95 | // Define valid priority values
  96 | const VALID_PRIORITY_VALUES = [
  97 |   'high', 'low'
  98 | ];
  99 | 
 100 | // Basic validation functions
 101 | function validateEntityType(entityType: string): boolean {
 102 |   return VALID_ENTITY_TYPES.includes(entityType);
 103 | }
 104 | 
 105 | function validateRelationType(relationType: string): boolean {
 106 |   return VALID_RELATION_TYPES.includes(relationType);
 107 | }
 108 | 
 109 | const __filename = fileURLToPath(import.meta.url);
 110 | const __dirname = path.dirname(__filename);
 111 | 
 112 | // Collect tool descriptions from text files
 113 | const toolDescriptions: Record<string, string> = {
 114 |   'startsession': '',
 115 |   'loadcontext': '',
 116 |   'deletecontext': '',
 117 |   'buildcontext': '',
 118 |   'advancedcontext': '',
 119 |   'endsession': '',
 120 | };
 121 | for (const tool of Object.keys(toolDescriptions)) {
 122 |   const descriptionFilePath = path.resolve(
 123 |     __dirname,
 124 |     `qualitativeresearch_${tool}.txt`
 125 |   );
 126 |   if (existsSync(descriptionFilePath)) {
 127 |     toolDescriptions[tool] = readFileSync(descriptionFilePath, 'utf-8');
 128 |   }
 129 | }
 130 | 
 131 | // Session management functions
 132 | async function loadSessionStates(): Promise<Map<string, any[]>> {
 133 |   try {
 134 |     const fileContent = await fs.readFile(SESSIONS_FILE_PATH, 'utf-8');
 135 |     const sessions = JSON.parse(fileContent);
 136 |     // Convert from object to Map
 137 |     const sessionsMap = new Map<string, any[]>();
 138 |     for (const [key, value] of Object.entries(sessions)) {
 139 |       sessionsMap.set(key, value as any[]);
 140 |     }
 141 |     return sessionsMap;
 142 |   } catch (error) {
 143 |     if (error instanceof Error && 'code' in error && (error as any).code === "ENOENT") {
 144 |       return new Map<string, any[]>();
 145 |     }
 146 |     throw error;
 147 |   }
 148 | }
 149 | 
 150 | async function saveSessionStates(sessionsMap: Map<string, any[]>): Promise<void> {
 151 |   // Convert from Map to object
 152 |   const sessions: Record<string, any[]> = {};
 153 |   for (const [key, value] of sessionsMap.entries()) {
 154 |     sessions[key] = value;
 155 |   }
 156 |   await fs.writeFile(SESSIONS_FILE_PATH, JSON.stringify(sessions, null, 2), 'utf-8');
 157 | }
 158 | 
 159 | // Generate a unique session ID
 160 | function generateSessionId(): string {
 161 |   return `qual_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
 162 | }
 163 | 
 164 | // We are storing our memory using entities, relations, and observations in a graph structure
 165 | interface Entity {
 166 |   name: string;
 167 |   entityType: string;
 168 |   observations: string[];
 169 | }
 170 | 
 171 | interface Relation {
 172 |   from: string;
 173 |   to: string;
 174 |   relationType: string;
 175 | }
 176 | 
 177 | interface KnowledgeGraph {
 178 |   entities: Entity[];
 179 |   relations: Relation[];
 180 | }
 181 | 
 182 | // The KnowledgeGraphManager class contains all operations to interact with the knowledge graph
 183 | class KnowledgeGraphManager {
 184 |   private async loadGraph(): Promise<KnowledgeGraph> {
 185 |     try {
 186 |       const fileContent = await fs.readFile(MEMORY_FILE_PATH, 'utf-8');
 187 |       return JSON.parse(fileContent);
 188 |     } catch (error) {
 189 |       // If the file doesn't exist, return an empty graph
 190 |       return {
 191 |         entities: [],
 192 |         relations: []
 193 |       };
 194 |     }
 195 |   }
 196 | 
 197 |   private async saveGraph(graph: KnowledgeGraph): Promise<void> {
 198 |     await fs.writeFile(MEMORY_FILE_PATH, JSON.stringify(graph, null, 2), 'utf-8');
 199 |   }
 200 | 
 201 |   // Initialize status and priority entities
 202 |   async initializeStatusAndPriority(): Promise<void> {
 203 |     const graph = await this.loadGraph();
 204 |     
 205 |     // Create status entities if they don't exist
 206 |     for (const statusValue of VALID_STATUS_VALUES) {
 207 |       const statusName = `status:${statusValue}`;
 208 |       if (!graph.entities.some(e => e.name === statusName && e.entityType === 'status')) {
 209 |         graph.entities.push({
 210 |           name: statusName,
 211 |           entityType: 'status',
 212 |           observations: [`A ${statusValue} status value`]
 213 |         });
 214 |       }
 215 |     }
 216 |     
 217 |     // Create priority entities if they don't exist
 218 |     for (const priorityValue of VALID_PRIORITY_VALUES) {
 219 |       const priorityName = `priority:${priorityValue}`;
 220 |       if (!graph.entities.some(e => e.name === priorityName && e.entityType === 'priority')) {
 221 |         graph.entities.push({
 222 |           name: priorityName,
 223 |           entityType: 'priority',
 224 |           observations: [`A ${priorityValue} priority value`]
 225 |         });
 226 |       }
 227 |     }
 228 |     
 229 |     await this.saveGraph(graph);
 230 |   }
 231 | 
 232 |   // Helper method to get status of an entity
 233 |   async getEntityStatus(entityName: string): Promise<string | null> {
 234 |     const graph = await this.loadGraph();
 235 |     
 236 |     // Find status relation for this entity
 237 |     const statusRelation = graph.relations.find(r => 
 238 |       r.from === entityName && 
 239 |       r.relationType === 'has_status'
 240 |     );
 241 |     
 242 |     if (statusRelation) {
 243 |       // Extract status value from the status entity name (status:value)
 244 |       return statusRelation.to.split(':')[1];
 245 |     }
 246 |     
 247 |     return null;
 248 |   }
 249 |   
 250 |   // Helper method to get priority of an entity
 251 |   async getEntityPriority(entityName: string): Promise<string | null> {
 252 |     const graph = await this.loadGraph();
 253 |     
 254 |     // Find priority relation for this entity
 255 |     const priorityRelation = graph.relations.find(r => 
 256 |       r.from === entityName && 
 257 |       r.relationType === 'has_priority'
 258 |     );
 259 |     
 260 |     if (priorityRelation) {
 261 |       // Extract priority value from the priority entity name (priority:value)
 262 |       return priorityRelation.to.split(':')[1];
 263 |     }
 264 |     
 265 |     return null;
 266 |   }
 267 |   
 268 |   // Helper method to set status of an entity
 269 |   async setEntityStatus(entityName: string, statusValue: string): Promise<void> {
 270 |     if (!VALID_STATUS_VALUES.includes(statusValue)) {
 271 |       throw new Error(`Invalid status value: ${statusValue}. Valid values are: ${VALID_STATUS_VALUES.join(', ')}`);
 272 |     }
 273 |     
 274 |     const graph = await this.loadGraph();
 275 |     
 276 |     // Remove any existing status relations for this entity
 277 |     graph.relations = graph.relations.filter(r => 
 278 |       !(r.from === entityName && r.relationType === 'has_status')
 279 |     );
 280 |     
 281 |     // Add new status relation
 282 |     graph.relations.push({
 283 |       from: entityName,
 284 |       to: `status:${statusValue}`,
 285 |       relationType: 'has_status'
 286 |     });
 287 |     
 288 |     await this.saveGraph(graph);
 289 |   }
 290 |   
 291 |   // Helper method to set priority of an entity
 292 |   async setEntityPriority(entityName: string, priorityValue: string): Promise<void> {
 293 |     if (!VALID_PRIORITY_VALUES.includes(priorityValue)) {
 294 |       throw new Error(`Invalid priority value: ${priorityValue}. Valid values are: ${VALID_PRIORITY_VALUES.join(', ')}`);
 295 |     }
 296 |     
 297 |     const graph = await this.loadGraph();
 298 |     
 299 |     // Remove any existing priority relations for this entity
 300 |     graph.relations = graph.relations.filter(r => 
 301 |       !(r.from === entityName && r.relationType === 'has_priority')
 302 |     );
 303 |     
 304 |     // Add new priority relation
 305 |     graph.relations.push({
 306 |       from: entityName,
 307 |       to: `priority:${priorityValue}`,
 308 |       relationType: 'has_priority'
 309 |     });
 310 |     
 311 |     await this.saveGraph(graph);
 312 |   }
 313 | 
 314 |   async createEntities(entities: Entity[]): Promise<Entity[]> {
 315 |     const graph = await this.loadGraph();
 316 |     const existingEntityNames = new Set(graph.entities.map(e => e.name));
 317 |     
 318 |     // Validate entity types
 319 |     entities.forEach(entity => {
 320 |       if (!validateEntityType(entity.entityType)) {
 321 |         throw new Error(`Invalid entity type: ${entity.entityType}. Valid types are: ${VALID_ENTITY_TYPES.join(', ')}`);
 322 |       }
 323 |     });
 324 |     
 325 |     const newEntities = entities.filter(entity => !existingEntityNames.has(entity.name));
 326 |     graph.entities.push(...newEntities);
 327 |     
 328 |     await this.saveGraph(graph);
 329 |     return newEntities;
 330 |   }
 331 | 
 332 |   async createRelations(relations: Relation[]): Promise<Relation[]> {
 333 |     const graph = await this.loadGraph();
 334 |     const existingEntityNames = new Set(graph.entities.map(e => e.name));
 335 |     
 336 |     // Check that entities exist and validate relation types
 337 |     relations.forEach(relation => {
 338 |       if (!existingEntityNames.has(relation.from)) {
 339 |         throw new Error(`Entity '${relation.from}' not found`);
 340 |       }
 341 |       if (!existingEntityNames.has(relation.to)) {
 342 |         throw new Error(`Entity '${relation.to}' not found`);
 343 |       }
 344 |       if (!validateRelationType(relation.relationType)) {
 345 |         throw new Error(`Invalid relation type: ${relation.relationType}. Valid types are: ${VALID_RELATION_TYPES.join(', ')}`);
 346 |       }
 347 |     });
 348 |     
 349 |     // Filter out duplicate relations
 350 |     const existingRelations = new Set(
 351 |       graph.relations.map(r => `${r.from}:${r.to}:${r.relationType}`)
 352 |     );
 353 |     
 354 |     const newRelations = relations.filter(
 355 |       r => !existingRelations.has(`${r.from}:${r.to}:${r.relationType}`)
 356 |     );
 357 |     
 358 |     graph.relations.push(...newRelations);
 359 |     
 360 |     await this.saveGraph(graph);
 361 |     return newRelations;
 362 |   }
 363 | 
 364 |   async addObservations(observations: { entityName: string; contents: string[] }[]): Promise<{ entityName: string; addedObservations: string[] }[]> {
 365 |     const graph = await this.loadGraph();
 366 |     const results: { entityName: string; addedObservations: string[] }[] = [];
 367 |     
 368 |     for (const observation of observations) {
 369 |       const entity = graph.entities.find(e => e.name === observation.entityName);
 370 |       if (!entity) {
 371 |         throw new Error(`Entity '${observation.entityName}' not found`);
 372 |       }
 373 |       
 374 |       // Filter out duplicate observations
 375 |       const existingObservations = new Set(entity.observations);
 376 |       const newObservations = observation.contents.filter(o => !existingObservations.has(o));
 377 |       
 378 |       entity.observations.push(...newObservations);
 379 |       results.push({
 380 |         entityName: observation.entityName,
 381 |         addedObservations: newObservations
 382 |       });
 383 |     }
 384 |     
 385 |     await this.saveGraph(graph);
 386 |     return results;
 387 |   }
 388 | 
 389 |   async deleteEntities(entityNames: string[]): Promise<void> {
 390 |     const graph = await this.loadGraph();
 391 |     
 392 |     // Remove the entities
 393 |     graph.entities = graph.entities.filter(e => !entityNames.includes(e.name));
 394 |     
 395 |     // Remove relations that involve the deleted entities
 396 |     graph.relations = graph.relations.filter(
 397 |       r => !entityNames.includes(r.from) && !entityNames.includes(r.to)
 398 |     );
 399 |     
 400 |     await this.saveGraph(graph);
 401 |   }
 402 | 
 403 |   async deleteObservations(deletions: { entityName: string; observations: string[] }[]): Promise<void> {
 404 |     const graph = await this.loadGraph();
 405 |     
 406 |     for (const deletion of deletions) {
 407 |       const entity = graph.entities.find(e => e.name === deletion.entityName);
 408 |       if (entity) {
 409 |         // Remove the specified observations
 410 |         entity.observations = entity.observations.filter(
 411 |           o => !deletion.observations.includes(o)
 412 |         );
 413 |       }
 414 |     }
 415 |     
 416 |     await this.saveGraph(graph);
 417 |   }
 418 | 
 419 |   async deleteRelations(relations: Relation[]): Promise<void> {
 420 |     const graph = await this.loadGraph();
 421 |     
 422 |     // Remove specified relations
 423 |     graph.relations = graph.relations.filter(r => 
 424 |       !relations.some(toDelete => 
 425 |         r.from === toDelete.from && 
 426 |         r.to === toDelete.to && 
 427 |         r.relationType === toDelete.relationType
 428 |       )
 429 |     );
 430 |     
 431 |     await this.saveGraph(graph);
 432 |   }
 433 | 
 434 |   async readGraph(): Promise<KnowledgeGraph> {
 435 |     return this.loadGraph();
 436 |   }
 437 | 
 438 |   async searchNodes(query: string): Promise<KnowledgeGraph> {
 439 |     const graph = await this.loadGraph();
 440 |     
 441 |     // Split query into search terms
 442 |     const terms = query.toLowerCase().split(/\s+/);
 443 |     
 444 |     // Find matching entities
 445 |     const matchingEntityNames = new Set<string>();
 446 |     
 447 |     for (const entity of graph.entities) {
 448 |       // Check if all terms match
 449 |       const matchesAllTerms = terms.every(term => {
 450 |         // Check entity name
 451 |         if (entity.name.toLowerCase().includes(term)) {
 452 |           return true;
 453 |         }
 454 |         
 455 |         // Check entity type
 456 |         if (entity.entityType.toLowerCase().includes(term)) {
 457 |           return true;
 458 |         }
 459 |         
 460 |         // Check observations
 461 |         for (const observation of entity.observations) {
 462 |           if (observation.toLowerCase().includes(term)) {
 463 |             return true;
 464 |           }
 465 |         }
 466 |         
 467 |         return false;
 468 |       });
 469 |       
 470 |       if (matchesAllTerms) {
 471 |         matchingEntityNames.add(entity.name);
 472 |       }
 473 |     }
 474 |     
 475 |     // Find relations between matching entities
 476 |     const matchingRelations = graph.relations.filter(r => 
 477 |       matchingEntityNames.has(r.from) && matchingEntityNames.has(r.to)
 478 |     );
 479 |     
 480 |     // Return matching entities and their relations
 481 |     return {
 482 |       entities: graph.entities.filter(e => matchingEntityNames.has(e.name)),
 483 |       relations: matchingRelations
 484 |     };
 485 |   }
 486 | 
 487 |   async openNodes(names: string[]): Promise<KnowledgeGraph> {
 488 |     const graph = await this.loadGraph();
 489 |     
 490 |     // Find the specified entities
 491 |     const entities = graph.entities.filter(e => names.includes(e.name));
 492 |     
 493 |     // Find relations between the specified entities
 494 |     const relations = graph.relations.filter(r => 
 495 |       names.includes(r.from) && names.includes(r.to)
 496 |     );
 497 |     
 498 |     return {
 499 |       entities,
 500 |       relations
 501 |     };
 502 |   }
 503 | 
 504 |   // Get project overview including research questions, methodology, participants, data sources
 505 |   async getProjectOverview(projectName: string): Promise<any> {
 506 |     const graph = await this.loadGraph();
 507 |     
 508 |     // Find the project
 509 |     const project = graph.entities.find(e => e.name === projectName && e.entityType === 'project');
 510 |     if (!project) {
 511 |       throw new Error(`Project '${projectName}' not found`);
 512 |     }
 513 |     
 514 |     // Find research questions
 515 |     const researchQuestions: Entity[] = [];
 516 |     for (const relation of graph.relations) {
 517 |       if (relation.relationType === 'part_of' && relation.to === projectName) {
 518 |         const question = graph.entities.find(
 519 |           e => e.name === relation.from && e.entityType === 'researchQuestion'
 520 |         );
 521 |         if (question) {
 522 |           researchQuestions.push(question);
 523 |         }
 524 |       }
 525 |     }
 526 |     
 527 |     // Find participants
 528 |     const participants: Entity[] = [];
 529 |     for (const relation of graph.relations) {
 530 |       if (relation.relationType === 'part_of' && relation.to === projectName) {
 531 |         const participant = graph.entities.find(
 532 |           e => e.name === relation.from && e.entityType === 'participant'
 533 |         );
 534 |         if (participant) {
 535 |           participants.push(participant);
 536 |         }
 537 |       }
 538 |     }
 539 |     
 540 |     // Find data sources (interviews, observations, documents)
 541 |     const interviews: Entity[] = [];
 542 |     const observations: Entity[] = [];
 543 |     const documents: Entity[] = [];
 544 |     
 545 |     for (const relation of graph.relations) {
 546 |       if (relation.relationType === 'part_of' && relation.to === projectName) {
 547 |         const entity = graph.entities.find(e => e.name === relation.from);
 548 |         
 549 |         if (entity) {
 550 |           if (entity.entityType === 'interview') {
 551 |             interviews.push(entity);
 552 |           } else if (entity.entityType === 'observation') {
 553 |             observations.push(entity);
 554 |           } else if (entity.entityType === 'document') {
 555 |             documents.push(entity);
 556 |           }
 557 |         }
 558 |       }
 559 |     }
 560 |     
 561 |     // Find findings
 562 |     const findings: Entity[] = [];
 563 |     for (const relation of graph.relations) {
 564 |       if (relation.relationType === 'part_of' && relation.to === projectName) {
 565 |         const finding = graph.entities.find(
 566 |           e => e.name === relation.from && e.entityType === 'finding'
 567 |         );
 568 |         if (finding) {
 569 |           findings.push(finding);
 570 |         }
 571 |       }
 572 |     }
 573 |     
 574 |     // Find themes
 575 |     const themes: Entity[] = [];
 576 |     for (const relation of graph.relations) {
 577 |       if (relation.relationType === 'part_of' && relation.to === projectName) {
 578 |         const theme = graph.entities.find(
 579 |           e => e.name === relation.from && e.entityType === 'theme'
 580 |         );
 581 |         if (theme) {
 582 |           themes.push(theme);
 583 |         }
 584 |       }
 585 |     }
 586 |     
 587 |     // Get methodology info from project observations
 588 |     const methodologyObs = project.observations.filter(
 589 |       o => o.toLowerCase().includes('method') || o.toLowerCase().includes('approach')
 590 |     );
 591 |     
 592 |     return {
 593 |       project,
 594 |       researchQuestions,
 595 |       methodology: methodologyObs,
 596 |       dataCollection: {
 597 |         participants: participants.length,
 598 |         interviews: interviews.length,
 599 |         observations: observations.length,
 600 |         documents: documents.length,
 601 |         participantsList: participants,
 602 |         interviewsList: interviews,
 603 |         observationsList: observations,
 604 |         documentsList: documents
 605 |       },
 606 |       analysis: {
 607 |         themes: themes.length,
 608 |         themesList: themes
 609 |       },
 610 |       findings
 611 |     };
 612 |   }
 613 | 
 614 |   // Get all data related to a specific participant
 615 |   async getParticipantProfile(participantName: string): Promise<any> {
 616 |     const graph = await this.loadGraph();
 617 |     
 618 |     // Find the participant
 619 |     const participant = graph.entities.find(e => e.name === participantName && e.entityType === 'participant');
 620 |     if (!participant) {
 621 |       throw new Error(`Participant '${participantName}' not found`);
 622 |     }
 623 |     
 624 |     // Find interviews with this participant
 625 |     const interviews: Entity[] = [];
 626 |     for (const relation of graph.relations) {
 627 |       if (relation.relationType === 'participated_in' && relation.from === participantName) {
 628 |         const interview = graph.entities.find(e => e.name === relation.to && e.entityType === 'interview');
 629 |         if (interview) {
 630 |           interviews.push(interview);
 631 |         }
 632 |       }
 633 |     }
 634 |     
 635 |     // Find observations including this participant
 636 |     const observations: Entity[] = [];
 637 |     for (const relation of graph.relations) {
 638 |       if (relation.relationType === 'participated_in' && relation.from === participantName) {
 639 |         const observation = graph.entities.find(e => e.name === relation.to && e.entityType === 'observation');
 640 |         if (observation) {
 641 |           observations.push(observation);
 642 |         }
 643 |       }
 644 |     }
 645 |     
 646 |     // Find quotes from this participant
 647 |     const quotes: Entity[] = [];
 648 |     for (const relation of graph.relations) {
 649 |       if (relation.relationType === 'contains' && relation.to === participantName) {
 650 |         const quote = graph.entities.find(e => e.name === relation.from && e.entityType === 'quote');
 651 |         if (quote) {
 652 |           quotes.push(quote);
 653 |         }
 654 |       }
 655 |     }
 656 |     
 657 |     // Find any memos about this participant
 658 |     const memos: Entity[] = [];
 659 |     for (const relation of graph.relations) {
 660 |       if (relation.relationType === 'reflects_on' && relation.to === participantName) {
 661 |         const memo = graph.entities.find(e => e.name === relation.from && e.entityType === 'memo');
 662 |         if (memo) {
 663 |           memos.push(memo);
 664 |         }
 665 |       }
 666 |     }
 667 |     
 668 |     // Extract demographic information from observations
 669 |     const demographicObs = participant.observations.filter(
 670 |       o => o.toLowerCase().includes('age') || 
 671 |            o.toLowerCase().includes('gender') || 
 672 |            o.toLowerCase().includes('occupation') ||
 673 |            o.toLowerCase().includes('education')
 674 |     );
 675 |     
 676 |     return {
 677 |       participant,
 678 |       demographics: demographicObs,
 679 |       interviews,
 680 |       observations,
 681 |       quotes,
 682 |       memos
 683 |     };
 684 |   }
 685 | 
 686 |   // Get themes with supporting codes and data
 687 |   async getThematicAnalysis(projectName: string): Promise<any> {
 688 |     const graph = await this.loadGraph();
 689 |     
 690 |     // Find the project
 691 |     const project = graph.entities.find(e => e.name === projectName && e.entityType === 'project');
 692 |     if (!project) {
 693 |       throw new Error(`Project '${projectName}' not found`);
 694 |     }
 695 |     
 696 |     // Find all themes related to this project
 697 |     const themes: Entity[] = [];
 698 |     
 699 |     for (const relation of graph.relations) {
 700 |       if (relation.relationType === 'part_of' && relation.to === projectName) {
 701 |         const theme = graph.entities.find(e => e.name === relation.from && e.entityType === 'theme');
 702 |         if (theme) {
 703 |           themes.push(theme);
 704 |         }
 705 |       }
 706 |     }
 707 |     
 708 |     // For each theme, find supporting data
 709 |     const thematicAnalysis = themes.map(theme => {
 710 |       // Find codes supporting this theme
 711 |       const supportingCodes: Entity[] = [];
 712 |       for (const relation of graph.relations) {
 713 |         if (relation.relationType === 'supports' && relation.to === theme.name) {
 714 |           const code = graph.entities.find(e => e.name === relation.from && e.entityType === 'code');
 715 |           if (code) {
 716 |             supportingCodes.push(code);
 717 |           }
 718 |         }
 719 |       }
 720 |       
 721 |       // For each code, find supporting quotes
 722 |       const codeData = supportingCodes.map(code => {
 723 |         const quotes: Entity[] = [];
 724 |         for (const relation of graph.relations) {
 725 |           if (relation.relationType === 'codes' && relation.from === code.name) {
 726 |             const quote = graph.entities.find(e => e.name === relation.to && e.entityType === 'quote');
 727 |             if (quote) {
 728 |               quotes.push(quote);
 729 |             }
 730 |           }
 731 |         }
 732 |         
 733 |         return {
 734 |           code,
 735 |           quotes
 736 |         };
 737 |       });
 738 |       
 739 |       // Find any memos reflecting on this theme
 740 |       const memos: Entity[] = [];
 741 |       for (const relation of graph.relations) {
 742 |         if (relation.relationType === 'reflects_on' && relation.to === theme.name) {
 743 |           const memo = graph.entities.find(e => e.name === relation.from && e.entityType === 'memo');
 744 |           if (memo) {
 745 |             memos.push(memo);
 746 |           }
 747 |         }
 748 |       }
 749 |       
 750 |       // Find status of the theme
 751 |       const statusObs = theme.observations.find(o => o.startsWith('Status:'));
 752 |       const status = statusObs ? statusObs.split(':')[1].trim() : 'unknown';
 753 |       
 754 |       return {
 755 |         theme,
 756 |         status,
 757 |         supportingData: codeData,
 758 |         codes: supportingCodes,
 759 |         memos
 760 |       };
 761 |     });
 762 |     
 763 |     return {
 764 |       project,
 765 |       themes: thematicAnalysis
 766 |     };
 767 |   }
 768 | 
 769 |   // Get all data segments tagged with a specific code
 770 |   async getCodedData(codeName: string): Promise<any> {
 771 |     const graph = await this.loadGraph();
 772 |     
 773 |     // Find the code
 774 |     const code = graph.entities.find(e => e.name === codeName && e.entityType === 'code');
 775 |     if (!code) {
 776 |       throw new Error(`Code '${codeName}' not found`);
 777 |     }
 778 |     
 779 |     // Find which code group this code belongs to, if any
 780 |     const codeGroups: Entity[] = [];
 781 |     for (const relation of graph.relations) {
 782 |       if (relation.relationType === 'contains' && relation.to === codeName) {
 783 |         const codeGroup = graph.entities.find(e => e.name === relation.from && e.entityType === 'codeGroup');
 784 |         if (codeGroup) {
 785 |           codeGroups.push(codeGroup);
 786 |         }
 787 |       }
 788 |     }
 789 |     
 790 |     // Find all quotes tagged with this code
 791 |     const quotes: Entity[] = [];
 792 |     for (const relation of graph.relations) {
 793 |       if (relation.relationType === 'codes' && relation.from === codeName) {
 794 |         const quote = graph.entities.find(e => e.name === relation.to && e.entityType === 'quote');
 795 |         if (quote) {
 796 |           quotes.push(quote);
 797 |         }
 798 |       }
 799 |     }
 800 |     
 801 |     // Find which sources (interviews, observations, documents) these quotes come from
 802 |     const sources = new Map<string, Entity>();
 803 |     
 804 |     for (const quote of quotes) {
 805 |       for (const relation of graph.relations) {
 806 |         if (relation.relationType === 'contains' && relation.from !== codeName && relation.to === quote.name) {
 807 |           const source = graph.entities.find(e => e.name === relation.from);
 808 |           if (source) {
 809 |             sources.set(source.name, source);
 810 |           }
 811 |         }
 812 |       }
 813 |     }
 814 |     
 815 |     // Find themes this code supports
 816 |     const themes: Entity[] = [];
 817 |     for (const relation of graph.relations) {
 818 |       if (relation.relationType === 'supports' && relation.from === codeName) {
 819 |         const theme = graph.entities.find(e => e.name === relation.to && e.entityType === 'theme');
 820 |         if (theme) {
 821 |           themes.push(theme);
 822 |         }
 823 |       }
 824 |     }
 825 |     
 826 |     // Find any memos reflecting on this code
 827 |     const memos: Entity[] = [];
 828 |     for (const relation of graph.relations) {
 829 |       if (relation.relationType === 'reflects_on' && relation.to === codeName) {
 830 |         const memo = graph.entities.find(e => e.name === relation.from && e.entityType === 'memo');
 831 |         if (memo) {
 832 |           memos.push(memo);
 833 |         }
 834 |       }
 835 |     }
 836 |     
 837 |     return {
 838 |       code,
 839 |       codeGroups,
 840 |       quotes,
 841 |       sourceCount: sources.size,
 842 |       sources: Array.from(sources.values()),
 843 |       themes,
 844 |       memos
 845 |     };
 846 |   }
 847 | 
 848 |   // Shows data organized by research questions with findings
 849 |   async getResearchQuestionAnalysis(projectName: string): Promise<any> {
 850 |     const graph = await this.loadGraph();
 851 |     
 852 |     // Find the project
 853 |     const project = graph.entities.find(e => e.name === projectName && e.entityType === 'project');
 854 |     if (!project) {
 855 |       throw new Error(`Project '${projectName}' not found`);
 856 |     }
 857 |     
 858 |     // Find all research questions for this project
 859 |     const researchQuestions: Entity[] = [];
 860 |     for (const relation of graph.relations) {
 861 |       if (relation.relationType === 'part_of' && relation.to === projectName) {
 862 |         const question = graph.entities.find(e => e.name === relation.from && e.entityType === 'researchQuestion');
 863 |         if (question) {
 864 |           researchQuestions.push(question);
 865 |         }
 866 |       }
 867 |     }
 868 |     
 869 |     // For each research question, find related data
 870 |     const questionAnalysis = researchQuestions.map(question => {
 871 |       // Find findings that answer this question
 872 |       const findings: Entity[] = [];
 873 |       for (const relation of graph.relations) {
 874 |         if (relation.relationType === 'answers' && relation.to === question.name) {
 875 |           const finding = graph.entities.find(e => e.name === relation.from && e.entityType === 'finding');
 876 |           if (finding) {
 877 |             findings.push(finding);
 878 |           }
 879 |         }
 880 |       }
 881 |       
 882 |       // Find themes related to this question
 883 |       const themes: Entity[] = [];
 884 |       for (const relation of graph.relations) {
 885 |         if (relation.relationType === 'answers' && relation.to === question.name) {
 886 |           const theme = graph.entities.find(e => e.name === relation.from && e.entityType === 'theme');
 887 |           if (theme) {
 888 |             themes.push(theme);
 889 |           }
 890 |         }
 891 |       }
 892 |       
 893 |       // Find data directly addressing this question
 894 |       const quotes: Entity[] = [];
 895 |       for (const relation of graph.relations) {
 896 |         if (relation.relationType === 'answers' && relation.to === question.name) {
 897 |           const quote = graph.entities.find(e => e.name === relation.from && e.entityType === 'quote');
 898 |           if (quote) {
 899 |             quotes.push(quote);
 900 |           }
 901 |         }
 902 |       }
 903 |       
 904 |       return {
 905 |         question,
 906 |         findings,
 907 |         themes,
 908 |         quotes
 909 |       };
 910 |     });
 911 |     
 912 |     return {
 913 |       project,
 914 |       researchQuestions: questionAnalysis
 915 |     };
 916 |   }
 917 | 
 918 |   // Returns data in temporal sequence
 919 |   async getChronologicalData(projectName: string, dataType?: string): Promise<any> {
 920 |     const graph = await this.loadGraph();
 921 |     
 922 |     // Find the project
 923 |     const project = graph.entities.find(e => e.name === projectName && e.entityType === 'project');
 924 |     if (!project) {
 925 |       throw new Error(`Project '${projectName}' not found`);
 926 |     }
 927 |     
 928 |     // Find all data collection entities for this project
 929 |     let dataEntities: Entity[] = [];
 930 |     
 931 |     for (const relation of graph.relations) {
 932 |       if (relation.relationType === 'part_of' && relation.to === projectName) {
 933 |         let entity;
 934 |         
 935 |         // Filter by data type if specified
 936 |         if (dataType) {
 937 |           entity = graph.entities.find(
 938 |             e => e.name === relation.from && e.entityType === dataType
 939 |           );
 940 |         } else {
 941 |           entity = graph.entities.find(
 942 |             e => e.name === relation.from && 
 943 |                (e.entityType === 'interview' || 
 944 |                 e.entityType === 'observation' || 
 945 |                 e.entityType === 'document')
 946 |           );
 947 |         }
 948 |         
 949 |         if (entity) {
 950 |           dataEntities.push(entity);
 951 |         }
 952 |       }
 953 |     }
 954 |     
 955 |     // Extract date information for each entity
 956 |     const dataWithDates = dataEntities.map(entity => {
 957 |       const dateObs = entity.observations.find(o => 
 958 |         o.startsWith('Date:') || o.startsWith('Collected on:') || o.startsWith('Created:')
 959 |       );
 960 |       
 961 |       let date = new Date(0);
 962 |       if (dateObs) {
 963 |         // Extract date string and try to parse it
 964 |         const dateString = dateObs.split(':')[1].trim();
 965 |         const parsedDate = new Date(dateString);
 966 |         if (!isNaN(parsedDate.getTime())) {
 967 |           date = parsedDate;
 968 |         }
 969 |       }
 970 |       
 971 |       return {
 972 |         entity,
 973 |         date
 974 |       };
 975 |     });
 976 |     
 977 |     // Sort by date
 978 |     dataWithDates.sort((a, b) => a.date.getTime() - b.date.getTime());
 979 |     
 980 |     // Create a timeline of data
 981 |     const timeline = dataWithDates.map(item => {
 982 |       // For each entity, find related quotes
 983 |       const quotes: Entity[] = [];
 984 |       for (const relation of graph.relations) {
 985 |         if (relation.relationType === 'contains' && relation.from === item.entity.name) {
 986 |           const quote = graph.entities.find(e => e.name === relation.to && e.entityType === 'quote');
 987 |           if (quote) {
 988 |             quotes.push(quote);
 989 |           }
 990 |         }
 991 |       }
 992 |       
 993 |       return {
 994 |         date: item.date,
 995 |         entity: item.entity,
 996 |         quotes
 997 |       };
 998 |     });
 999 |     
1000 |     return {
1001 |       project,
1002 |       timeline
1003 |     };
1004 |   }
1005 | 
1006 |   // Finds where multiple codes appear together
1007 |   async getCodeCooccurrence(codeName: string): Promise<any> {
1008 |     const graph = await this.loadGraph();
1009 |     
1010 |     // Find the code
1011 |     const code = graph.entities.find(e => e.name === codeName && e.entityType === 'code');
1012 |     if (!code) {
1013 |       throw new Error(`Code '${codeName}' not found`);
1014 |     }
1015 |     
1016 |     // Find all quotes tagged with this code
1017 |     const quotes: Entity[] = [];
1018 |     for (const relation of graph.relations) {
1019 |       if (relation.relationType === 'codes' && relation.from === codeName) {
1020 |         const quote = graph.entities.find(e => e.name === relation.to && e.entityType === 'quote');
1021 |         if (quote) {
1022 |           quotes.push(quote);
1023 |         }
1024 |       }
1025 |     }
1026 |     
1027 |     // For each quote, find other codes that also tag it
1028 |     const codeOccurrences = new Map<string, { code: Entity; count: number; quotes: Entity[] }>();
1029 |     
1030 |     for (const quote of quotes) {
1031 |       for (const relation of graph.relations) {
1032 |         if (relation.relationType === 'codes' && relation.from !== codeName && relation.to === quote.name) {
1033 |           const otherCode = graph.entities.find(e => e.name === relation.from && e.entityType === 'code');
1034 |           if (otherCode) {
1035 |             if (codeOccurrences.has(otherCode.name)) {
1036 |               const occurrence = codeOccurrences.get(otherCode.name)!;
1037 |               occurrence.count++;
1038 |               occurrence.quotes.push(quote);
1039 |             } else {
1040 |               codeOccurrences.set(otherCode.name, {
1041 |                 code: otherCode,
1042 |                 count: 1,
1043 |                 quotes: [quote]
1044 |               });
1045 |             }
1046 |           }
1047 |         }
1048 |       }
1049 |     }
1050 |     
1051 |     // Sort codes by co-occurrence frequency
1052 |     const cooccurringCodes = Array.from(codeOccurrences.values())
1053 |       .sort((a, b) => b.count - a.count);
1054 |     
1055 |     return {
1056 |       code,
1057 |       quotesCount: quotes.length,
1058 |       cooccurringCodes
1059 |     };
1060 |   }
1061 | 
1062 |   // Gets all memos related to a specific entity
1063 |   async getMemosByFocus(entityName: string): Promise<any> {
1064 |     const graph = await this.loadGraph();
1065 |     
1066 |     // Find the entity
1067 |     const entity = graph.entities.find(e => e.name === entityName);
1068 |     if (!entity) {
1069 |       throw new Error(`Entity '${entityName}' not found`);
1070 |     }
1071 |     
1072 |     // Find all memos reflecting on this entity
1073 |     const memos: Entity[] = [];
1074 |     for (const relation of graph.relations) {
1075 |       if (relation.relationType === 'reflects_on' && relation.to === entityName) {
1076 |         const memo = graph.entities.find(e => e.name === relation.from && e.entityType === 'memo');
1077 |         if (memo) {
1078 |           memos.push(memo);
1079 |         }
1080 |       }
1081 |     }
1082 |     
1083 |     // Sort memos by date if possible
1084 |     const memosWithDates = memos.map(memo => {
1085 |       const dateObs = memo.observations.find(o => o.startsWith('Date:') || o.startsWith('Created:'));
1086 |       
1087 |       let date = new Date(0);
1088 |       if (dateObs) {
1089 |         const dateString = dateObs.split(':')[1].trim();
1090 |         const parsedDate = new Date(dateString);
1091 |         if (!isNaN(parsedDate.getTime())) {
1092 |           date = parsedDate;
1093 |         }
1094 |       }
1095 |       
1096 |       return {
1097 |         memo,
1098 |         date
1099 |       };
1100 |     });
1101 |     
1102 |     // Sort by date, most recent first
1103 |     memosWithDates.sort((a, b) => b.date.getTime() - a.date.getTime());
1104 |     
1105 |     return {
1106 |       entity,
1107 |       memos: memosWithDates.map(m => m.memo)
1108 |     };
1109 |   }
1110 | 
1111 |   // Returns information about methods, sampling, analysis approach
1112 |   async getMethodologyDetails(projectName: string): Promise<any> {
1113 |     const graph = await this.loadGraph();
1114 |     
1115 |     // Find the project
1116 |     const project = graph.entities.find(e => e.name === projectName && e.entityType === 'project');
1117 |     if (!project) {
1118 |       throw new Error(`Project '${projectName}' not found`);
1119 |     }
1120 |     
1121 |     // Extract methodology information from project observations
1122 |     const methodologyObs = project.observations.filter(o => 
1123 |       o.toLowerCase().includes('method') || 
1124 |       o.toLowerCase().includes('approach') || 
1125 |       o.toLowerCase().includes('sampling') || 
1126 |       o.toLowerCase().includes('analysis') ||
1127 |       o.toLowerCase().includes('validity') ||
1128 |       o.toLowerCase().includes('reliability')
1129 |     );
1130 |     
1131 |     // Find methodology-related memos
1132 |     const memos: Entity[] = [];
1133 |     for (const relation of graph.relations) {
1134 |       if (relation.relationType === 'reflects_on' && relation.to === projectName) {
1135 |         const memo = graph.entities.find(e => e.name === relation.from && e.entityType === 'memo');
1136 |         if (memo && memo.observations.some(o => 
1137 |           o.toLowerCase().includes('method') || 
1138 |           o.toLowerCase().includes('approach') || 
1139 |           o.toLowerCase().includes('sampling') || 
1140 |           o.toLowerCase().includes('analysis')
1141 |         )) {
1142 |           memos.push(memo);
1143 |         }
1144 |       }
1145 |     }
1146 |     
1147 |     // Calculate data collection statistics
1148 |     // 1. Get all interviews
1149 |     const interviews: Entity[] = [];
1150 |     for (const relation of graph.relations) {
1151 |       if (relation.relationType === 'part_of' && relation.to === projectName) {
1152 |         const interview = graph.entities.find(e => e.name === relation.from && e.entityType === 'interview');
1153 |         if (interview) {
1154 |           interviews.push(interview);
1155 |         }
1156 |       }
1157 |     }
1158 |     
1159 |     // 2. Get all observations
1160 |     const observations: Entity[] = [];
1161 |     for (const relation of graph.relations) {
1162 |       if (relation.relationType === 'part_of' && relation.to === projectName) {
1163 |         const observation = graph.entities.find(e => e.name === relation.from && e.entityType === 'observation');
1164 |         if (observation) {
1165 |           observations.push(observation);
1166 |         }
1167 |       }
1168 |     }
1169 |     
1170 |     // 3. Get all documents
1171 |     const documents: Entity[] = [];
1172 |     for (const relation of graph.relations) {
1173 |       if (relation.relationType === 'part_of' && relation.to === projectName) {
1174 |         const document = graph.entities.find(e => e.name === relation.from && e.entityType === 'document');
1175 |         if (document) {
1176 |           documents.push(document);
1177 |         }
1178 |       }
1179 |     }
1180 |     
1181 |     // 4. Get all participants
1182 |     const participants: Entity[] = [];
1183 |     for (const relation of graph.relations) {
1184 |       if (relation.relationType === 'part_of' && relation.to === projectName) {
1185 |         const participant = graph.entities.find(e => e.name === relation.from && e.entityType === 'participant');
1186 |         if (participant) {
1187 |           participants.push(participant);
1188 |         }
1189 |       }
1190 |     }
1191 |     
1192 |     // Find literature cited in this project
1193 |     const literature: Entity[] = [];
1194 |     for (const relation of graph.relations) {
1195 |       if (relation.relationType === 'cites' && relation.from === projectName) {
1196 |         const source = graph.entities.find(e => e.name === relation.to && e.entityType === 'literature');
1197 |         if (source) {
1198 |           literature.push(source);
1199 |         }
1200 |       }
1201 |     }
1202 |     
1203 |     return {
1204 |       project,
1205 |       methodology: methodologyObs,
1206 |       dataCollection: {
1207 |         participants: participants.length,
1208 |         interviews: interviews.length,
1209 |         observations: observations.length,
1210 |         documents: documents.length
1211 |       },
1212 |       memos,
1213 |       literature
1214 |     };
1215 |   }
1216 | 
1217 |   // First, let's add the missing getRelatedEntities method to the KnowledgeGraphManager class
1218 |   // Add this before the async main() function
1219 | 
1220 |   async getRelatedEntities(entityName: string, relationTypes?: string[]): Promise<any> {
1221 |     const graph = await this.loadGraph();
1222 |     
1223 |     // Find the entity
1224 |     const entity = graph.entities.find(e => e.name === entityName);
1225 |     if (!entity) {
1226 |       throw new Error(`Entity '${entityName}' not found`);
1227 |     }
1228 |     
1229 |     // Find all relations involving this entity
1230 |     let relevantRelations = graph.relations.filter(r => r.from === entityName || r.to === entityName);
1231 |     
1232 |     // Filter by relation types if specified
1233 |     if (relationTypes && relationTypes.length > 0) {
1234 |       relevantRelations = relevantRelations.filter(r => relationTypes.includes(r.relationType));
1235 |     }
1236 |     
1237 |     // Get all related entities grouped by relation type
1238 |     const related: Record<string, Entity[]> = {};
1239 |     
1240 |     for (const relation of relevantRelations) {
1241 |       const relationType = relation.relationType;
1242 |       if (!related[relationType]) {
1243 |         related[relationType] = [];
1244 |       }
1245 |       
1246 |       if (relation.from === entityName) {
1247 |         const target = graph.entities.find(e => e.name === relation.to);
1248 |         if (target) {
1249 |           related[relationType].push(target);
1250 |         }
1251 |       } else {
1252 |         const source = graph.entities.find(e => e.name === relation.from);
1253 |         if (source) {
1254 |           related[relationType].push(source);
1255 |         }
1256 |       }
1257 |     }
1258 |     
1259 |     return {
1260 |       entity,
1261 |       related
1262 |     };
1263 |   }
1264 | }
1265 | 
1266 | // Main function to set up the MCP server
1267 | async function main() {
1268 |   try {
1269 |     const knowledgeGraphManager = new KnowledgeGraphManager();
1270 |     
1271 |     // Initialize status and priority entities
1272 |     await knowledgeGraphManager.initializeStatusAndPriority();
1273 |     
1274 |     // Create the MCP server with a name and version
1275 |     const server = new McpServer({
1276 |       name: "Context Manager",
1277 |       version: "1.0.0"
1278 |     });
1279 |     
1280 |     // Define a resource that exposes the entire graph
1281 |     server.resource(
1282 |       "graph",
1283 |       "graph://researcher/qualitative",
1284 |       async (uri) => ({
1285 |         contents: [{
1286 |           uri: uri.href,
1287 |           text: JSON.stringify(await knowledgeGraphManager.readGraph(), null, 2)
1288 |         }]
1289 |       })
1290 |     );
1291 |     
1292 |     // Define tools using zod for parameter validation
1293 | 
1294 |     /**
1295 |      * Load context for a specific entity
1296 |      */
1297 |     server.tool(
1298 |       "loadcontext",
1299 |       toolDescriptions["loadcontext"],
1300 |       {
1301 |         entityName: z.string(),
1302 |         entityType: z.string().optional(),
1303 |         sessionId: z.string().optional() // Optional to maintain backward compatibility
1304 |       },
1305 |       async ({ entityName, entityType = "project", sessionId }) => {
1306 |         try {
1307 |           // Validate session if ID is provided
1308 |           if (sessionId) {
1309 |             const sessionStates = await loadSessionStates();
1310 |             if (!sessionStates.has(sessionId)) {
1311 |               console.warn(`Warning: Session ${sessionId} not found, but proceeding with context load`);
1312 |               // Initialize it anyway for more robustness
1313 |               sessionStates.set(sessionId, []);
1314 |               await saveSessionStates(sessionStates);
1315 |             }
1316 |             
1317 |             // Track that this entity was loaded in this session
1318 |             const sessionState = sessionStates.get(sessionId) || [];
1319 |             const loadEvent = {
1320 |               type: 'context_loaded',
1321 |               timestamp: new Date().toISOString(),
1322 |               entityName,
1323 |               entityType
1324 |             };
1325 |             sessionState.push(loadEvent);
1326 |             sessionStates.set(sessionId, sessionState);
1327 |             await saveSessionStates(sessionStates);
1328 |           }
1329 |           
1330 |           // Get the entity
1331 |           // Changed from using 'name:' prefix to directly searching by the entity name
1332 |           const entityGraph = await knowledgeGraphManager.searchNodes(entityName);
1333 |           if (entityGraph.entities.length === 0) {
1334 |             throw new Error(`Entity ${entityName} not found`);
1335 |           }
1336 |           
1337 |           // Find the exact entity by name (case-sensitive match)
1338 |           const entity = entityGraph.entities.find(e => e.name === entityName);
1339 |           if (!entity) {
1340 |             throw new Error(`Entity ${entityName} not found`);
1341 |           }
1342 |           
1343 |           // Different context loading based on entity type
1344 |           let contextMessage = "";
1345 |           
1346 |           if (entityType === "project") {
1347 |             // Get project overview
1348 |             const projectOverview = await knowledgeGraphManager.getProjectOverview(entityName);
1349 |             
1350 |             // Get thematic analysis
1351 |             let thematicAnalysis;
1352 |             try {
1353 |               thematicAnalysis = await knowledgeGraphManager.getThematicAnalysis(entityName);
1354 |             } catch (error) {
1355 |               thematicAnalysis = { themes: [] };
1356 |             }
1357 |             
1358 |             // Get research question analysis
1359 |             let researchQuestions;
1360 |             try {
1361 |               researchQuestions = await knowledgeGraphManager.getResearchQuestionAnalysis(entityName);
1362 |             } catch (error) {
1363 |               researchQuestions = { researchQuestions: [] };
1364 |             }
1365 |             
1366 |             // Get methodology details
1367 |             let methodology;
1368 |             try {
1369 |               methodology = await knowledgeGraphManager.getMethodologyDetails(entityName);
1370 |             } catch (error) {
1371 |               methodology = { methodology: [] };
1372 |             }
1373 |             
1374 |             // Get status and priority using the relation-based approach
1375 |             const status = await knowledgeGraphManager.getEntityStatus(entityName) || "Unknown";
1376 |             const priority = await knowledgeGraphManager.getEntityPriority(entityName);
1377 |             const priorityText = priority ? `- **Priority**: ${priority}` : "";
1378 |             
1379 |             // Format observations
1380 |             const observationsList = entity.observations.length > 0 
1381 |               ? entity.observations.map(obs => `- ${obs}`).join("\n")
1382 |               : "No observations";
1383 |             
1384 |             // Extract methodology information
1385 |             const methodologyText = methodology.methodology?.map((m: string) => `- ${m}`).join("\n") || "No methodology details available";
1386 |             
1387 |             // Extract research questions
1388 |             const questionsText = researchQuestions.researchQuestions?.map((q: any) => {
1389 |               const findings = q.findings?.map((f: Entity) => `  - ${f.name}`).join("\n") || "  - No findings yet";
1390 |               return `- **${q.question.name}**\n${findings}`;
1391 |             }).join("\n") || "No research questions found";
1392 |             
1393 |             // Format data collection stats
1394 |             const participantCount = projectOverview.dataCollection?.participants || 0;
1395 |             const interviewCount = projectOverview.dataCollection?.interviews || 0;
1396 |             const observationCount = projectOverview.dataCollection?.observations || 0;
1397 |             const documentCount = projectOverview.dataCollection?.documents || 0;
1398 |             
1399 |             // Format theme analysis
1400 |             const themesText = thematicAnalysis.themes?.map(async (t: any) => {
1401 |               const codeCount = t.supportingData?.length || 0;
1402 |               const themeStatus = await knowledgeGraphManager.getEntityStatus(t.theme.name) || "unknown";
1403 |               return `- **${t.theme.name}** (Status: ${themeStatus}): ${codeCount} codes`;
1404 |             });
1405 |             
1406 |             const resolvedThemesText = themesText ? 
1407 |               await Promise.all(themesText).then(texts => texts.join("\n")) : 
1408 |               "No themes identified yet";
1409 |             
1410 |             // Get recent data collection - without date references
1411 |             const recentInterviews = projectOverview.dataCollection?.interviewsList?.slice(0, 5).map(async (i: Entity) => {
1412 |               const participant = i.observations.find(o => o.startsWith("participant:"))?.substring(12) || "Unknown";
1413 |               const interviewStatus = await knowledgeGraphManager.getEntityStatus(i.name) || "unknown";
1414 |               return `- **${i.name}** with ${participant} (Status: ${interviewStatus})`;
1415 |             });
1416 |             
1417 |             const resolvedInterviewsText = recentInterviews ? 
1418 |               await Promise.all(recentInterviews).then(texts => texts.join("\n")) : 
1419 |               "No recent interviews";
1420 |             
1421 |             // Get findings with status from relations
1422 |             const findingsText = projectOverview.findings?.map(async (f: Entity) => {
1423 |               const findingStatus = await knowledgeGraphManager.getEntityStatus(f.name) || "preliminary";
1424 |               const findingObs = f.observations.length > 0 ? f.observations[0] : "No description";
1425 |               return `- **${f.name}** (Status: ${findingStatus}): ${findingObs}`;
1426 |             });
1427 |             
1428 |             const resolvedFindingsText = findingsText ? 
1429 |               await Promise.all(findingsText).then(texts => texts.join("\n")) : 
1430 |               "No findings recorded yet";
1431 |             
1432 |             contextMessage = `# Qualitative Research Project Context: ${entityName}
1433 | 
1434 | ## Project Details
1435 | - **Status**: ${status}
1436 | ${priorityText}
1437 | 
1438 | ## Observations
1439 | ${observationsList}
1440 | 
1441 | ## Research Design
1442 | ${methodologyText}
1443 | 
1444 | ## Research Questions
1445 | ${questionsText}
1446 | 
1447 | ## Data Collection Stats
1448 | - **Participants**: ${participantCount}
1449 | - **Interviews**: ${interviewCount}
1450 | - **Observations**: ${observationCount}
1451 | - **Documents**: ${documentCount}
1452 | 
1453 | ## Recent Interviews
1454 | ${resolvedInterviewsText}
1455 | 
1456 | ## Analysis Progress
1457 | ### Themes
1458 | ${resolvedThemesText}
1459 | 
1460 | ## Findings
1461 | ${resolvedFindingsText}`;
1462 |           } 
1463 |           else if (entityType === "participant") {
1464 |             // Get participant profile
1465 |             const participantProfile = await knowledgeGraphManager.getParticipantProfile(entityName);
1466 |             
1467 |             // Get status and priority using the relation-based approach
1468 |             const status = await knowledgeGraphManager.getEntityStatus(entityName) || "Unknown";
1469 |             const priority = await knowledgeGraphManager.getEntityPriority(entityName);
1470 |             const priorityText = priority ? `- **Priority**: ${priority}` : "";
1471 |             
1472 |             // Format observations
1473 |             const observationsList = entity.observations.length > 0 
1474 |               ? entity.observations.map(obs => `- ${obs}`).join("\n")
1475 |               : "No observations";
1476 |             
1477 |             // Format demographics without relying on patterns
1478 |             const demographics = participantProfile.demographics?.map((d: string) => `- ${d}`).join("\n") || "No demographic information available";
1479 |             
1480 |             // Format interviews with status from relations
1481 |             const interviewsText = participantProfile.interviews?.map(async (i: Entity) => {
1482 |               const interviewStatus = await knowledgeGraphManager.getEntityStatus(i.name) || "unknown";
1483 |               return `- **${i.name}** (Status: ${interviewStatus})`;
1484 |             });
1485 |             
1486 |             const resolvedInterviewsText = interviewsText ? 
1487 |               await Promise.all(interviewsText).then(texts => texts.join("\n")) : 
1488 |               "No interviews recorded";
1489 |             
1490 |             // Format observations with status from relations
1491 |             const observationsText = participantProfile.observations?.map(async (o: Entity) => {
1492 |               const observationStatus = await knowledgeGraphManager.getEntityStatus(o.name) || "unknown";
1493 |               return `- **${o.name}** (Status: ${observationStatus})`;
1494 |             });
1495 |             
1496 |             const resolvedObservationsText = observationsText ? 
1497 |               await Promise.all(observationsText).then(texts => texts.join("\n")) : 
1498 |               "No observations recorded";
1499 |             
1500 |             // Format quotes
1501 |             const quotesText = participantProfile.quotes?.map((q: Entity) => {
1502 |               // Show the full quote
1503 |               const quote = q.observations.find(o => !o.startsWith("source:") && !o.startsWith("context:"));
1504 |               const source = q.observations.find(o => o.startsWith("source:"))?.substring(7) || "Unknown source";
1505 |               return `- "${quote || "No text available"}" (Source: ${source})`;
1506 |             }).join("\n") || "No quotes recorded";
1507 |             
1508 |             // Format memos
1509 |             const memosText = participantProfile.memos?.map(async (m: Entity) => {
1510 |               const memoStatus = await knowledgeGraphManager.getEntityStatus(m.name) || "unknown";
1511 |               const topic = m.observations.find(o => o.startsWith("topic:"))?.substring(6) || "Untitled";
1512 |               return `- **${topic}** (Status: ${memoStatus})`;
1513 |             });
1514 |             
1515 |             const resolvedMemosText = memosText ? 
1516 |               await Promise.all(memosText).then(texts => texts.join("\n")) : 
1517 |               "No memos about this participant";
1518 |             
1519 |             contextMessage = `# Participant Context: ${entityName}
1520 | 
1521 | ## Status and Priority
1522 | - **Status**: ${status}
1523 | ${priorityText}
1524 | 
1525 | ## Observations
1526 | ${observationsList}
1527 | 
1528 | ## Demographics
1529 | ${demographics}
1530 | 
1531 | ## Interviews
1532 | ${resolvedInterviewsText}
1533 | 
1534 | ## Observations
1535 | ${resolvedObservationsText}
1536 | 
1537 | ## Quotes
1538 | ${quotesText}
1539 | 
1540 | ## Research Memos
1541 | ${resolvedMemosText}`;
1542 |           }
1543 |           else if (entityType === "interview") {
1544 |             // Find which project this interview belongs to
1545 |             let projectName = 'Unknown project';
1546 |             
1547 |             for (const relation of entityGraph.relations) {
1548 |               if (relation.relationType === 'part_of' && relation.from === entityName) {
1549 |                 const project = entityGraph.entities.find(e => e.name === relation.to && e.entityType === 'project');
1550 |                 if (project) {
1551 |                   projectName = project.name;
1552 |                   break;
1553 |                 }
1554 |               }
1555 |             }
1556 |             
1557 |             // Get status and priority using the relation-based approach
1558 |             const status = await knowledgeGraphManager.getEntityStatus(entityName) || "Unknown";
1559 |             const priority = await knowledgeGraphManager.getEntityPriority(entityName);
1560 |             const priorityText = priority ? `- **Priority**: ${priority}` : "";
1561 |             
1562 |             // Format observations
1563 |             const observationsList = entity.observations.length > 0 
1564 |               ? entity.observations.map(obs => `- ${obs}`).join("\n")
1565 |               : "No observations";
1566 |             
1567 |             // Get interview details without parsing date
1568 |             const participant = entity.observations.find(o => o.startsWith("participant:"))?.substring(12) || "Unknown";
1569 |             
1570 |             // Find codes applied to this interview and include their status
1571 |             const codesWithStatus = [];
1572 |             
1573 |             for (const relation of entityGraph.relations) {
1574 |               if (relation.relationType === 'codes' && relation.to === entityName) {
1575 |                 const code = entityGraph.entities.find(e => e.name === relation.from && e.entityType === 'code');
1576 |                 if (code) {
1577 |                   const codeStatus = await knowledgeGraphManager.getEntityStatus(code.name) || "unknown";
1578 |                   codesWithStatus.push({
1579 |                     code,
1580 |                     status: codeStatus
1581 |                   });
1582 |                 }
1583 |               }
1584 |             }
1585 |             
1586 |             const codesText = codesWithStatus.map(c => 
1587 |               `- **${c.code.name}** (Status: ${c.status}): ${c.code.observations[0] || "No description"}`
1588 |             ).join("\n") || "No codes applied yet";
1589 |             
1590 |             // Find quotes from this interview
1591 |             const quotes = [];
1592 |             for (const relation of entityGraph.relations) {
1593 |               if (relation.relationType === 'contains' && relation.from === entityName) {
1594 |                 const quote = entityGraph.entities.find(e => e.name === relation.to && e.entityType === 'quote');
1595 |                 if (quote) {
1596 |                   quotes.push(quote);
1597 |                 }
1598 |               }
1599 |             }
1600 |             
1601 |             const quotesText = quotes.map(q => {
1602 |               // Get the full quote text
1603 |               const quoteText = q.observations.find(o => !o.startsWith("context:") && !o.startsWith("speaker:")) || "No text";
1604 |               return `- "${quoteText}"`;
1605 |             }).join("\n") || "No notable quotes recorded";
1606 |             
1607 |             contextMessage = `# Interview Context: ${entityName}
1608 | 
1609 | ## Overview
1610 | - **Project**: ${projectName}
1611 | - **Participant**: ${participant}
1612 | - **Status**: ${status}
1613 | ${priorityText}
1614 | 
1615 | ## Observations
1616 | ${observationsList}
1617 | 
1618 | ## Applied Codes
1619 | ${codesText}
1620 | 
1621 | ## Notable Quotes
1622 | ${quotesText}`;
1623 |           }
1624 |           else if (entityType === "code") {
1625 |             // Get coded data for this code
1626 |             const codedData = await knowledgeGraphManager.getCodedData(entityName);
1627 |             
1628 |             // Format code context
1629 |             const definition = entity.observations.find(o => !o.startsWith("status:") && !o.startsWith("created:"));
1630 |             const created = entity.observations.find(o => o.startsWith("created:"))?.substring(8) || "Unknown";
1631 |             const status = entity.observations.find(o => o.startsWith("status:"))?.substring(7) || "active";
1632 |             
1633 |             // Format code groups
1634 |             const codeGroupsText = codedData.codeGroups?.map((group: Entity) => {
1635 |               const description = group.observations.find(o => !o.startsWith("created:"));
1636 |               return `- **${group.name}**: ${description || "No description"}`;
1637 |             }).join("\n") || "Not part of any code groups";
1638 |             
1639 |             // Format quotes
1640 |             const quotesText = codedData.quotes?.map((quote: Entity) => {
1641 |               const source = quote.observations.find(o => o.startsWith("source:"))?.substring(7) || "Unknown source";
1642 |               const text = quote.observations.find(o => !o.startsWith("source:") && !o.startsWith("context:"));
1643 |               return `- "${text || "No text"}" (Source: ${source})`;
1644 |             }).join("\n") || "No quotes tagged with this code";
1645 |             
1646 |             // Format sources
1647 |             const sourcesText = codedData.sources?.map((source: Entity) => {
1648 |               return `- **${source.name}** (${source.entityType})`;
1649 |             }).join("\n") || "No sources found";
1650 |             
1651 |             // Format themes
1652 |             const themesText = codedData.themes?.map((theme: Entity) => {
1653 |               const description = theme.observations.find(o => !o.startsWith("status:") && !o.startsWith("created:"));
1654 |               return `- **${theme.name}**: ${description || "No description"}`;
1655 |             }).join("\n") || "Not associated with any themes";
1656 |             
1657 |             // Get co-occurrence data
1658 |             let cooccurrenceData;
1659 |             try {
1660 |               cooccurrenceData = await knowledgeGraphManager.getCodeCooccurrence(entityName);
1661 |               
1662 |               // Format co-occurrence
1663 |               const cooccurrenceText = cooccurrenceData.cooccurringCodes?.map((c: any) => {
1664 |                 return `- **${c.code.name}** (${c.count} co-occurrences)`;
1665 |               }).slice(0, 5).join("\n") || "No code co-occurrence data";
1666 |               
1667 |               contextMessage = `# Code Context: ${entityName}
1668 | 
1669 | ## Code Details
1670 | - **Definition**: ${definition || "No definition provided"}
1671 | - **Created**: ${created}
1672 | - **Status**: ${status}
1673 | - **Items Coded**: ${codedData.quotes?.length || 0}
1674 | 
1675 | ## Part of Code Groups
1676 | ${codeGroupsText}
1677 | 
1678 | ## Supporting Themes
1679 | ${themesText}
1680 | 
1681 | ## Top Co-occurring Codes
1682 | ${cooccurrenceText}
1683 | 
1684 | ## Example Quotes
1685 | ${quotesText}
1686 | 
1687 | ## Used in These Sources
1688 | ${sourcesText}`;
1689 |             } catch (error) {
1690 |               contextMessage = `# Code Context: ${entityName}
1691 | 
1692 | ## Code Details
1693 | - **Definition**: ${definition || "No definition provided"}
1694 | - **Created**: ${created}
1695 | - **Status**: ${status}
1696 | - **Items Coded**: ${codedData.quotes?.length || 0}
1697 | 
1698 | ## Part of Code Groups
1699 | ${codeGroupsText}
1700 | 
1701 | ## Supporting Themes
1702 | ${themesText}
1703 | 
1704 | ## Example Quotes
1705 | ${quotesText}
1706 | 
1707 | ## Used in These Sources
1708 | ${sourcesText}`;
1709 |             }
1710 |           }
1711 |           else if (entityType === "theme") {
1712 |             // Get thematic analysis data
1713 |             let projectName = "";
1714 |             
1715 |             // Find which project this theme belongs to
1716 |             for (const relation of entityGraph.relations) {
1717 |               if (relation.relationType === 'part_of' && relation.from === entityName) {
1718 |                 const project = entityGraph.entities.find(e => e.name === relation.to && e.entityType === 'project');
1719 |                 if (project) {
1720 |                   projectName = project.name;
1721 |                   break;
1722 |                 }
1723 |               }
1724 |             }
1725 |             
1726 |             let thematicAnalysis;
1727 |             try {
1728 |               thematicAnalysis = await knowledgeGraphManager.getThematicAnalysis(projectName);
1729 |               
1730 |               // Find this theme in the analysis
1731 |               const themeAnalysis = thematicAnalysis.themes?.find((t: any) => t.theme.name === entityName);
1732 |               
1733 |               if (themeAnalysis) {
1734 |                 const description = entity.observations.find(o => !o.startsWith("created:") && !o.startsWith("status:"));
1735 |                 const status = entity.observations.find(o => o.startsWith("status:"))?.substring(7) || "emerging";
1736 |                 const created = entity.observations.find(o => o.startsWith("created:"))?.substring(8) || "Unknown";
1737 |                 
1738 |                 // Format codes
1739 |                 const codesText = themeAnalysis.codes?.map((code: Entity) => {
1740 |                   const definition = code.observations.find(o => !o.startsWith("status:") && !o.startsWith("created:"));
1741 |                   return `- **${code.name}**: ${definition || "No definition"}`;
1742 |                 }).join("\n") || "No supporting codes";
1743 |                 
1744 |                 // Format supporting quotes
1745 |                 const quotesText = themeAnalysis.supportingData?.flatMap((codeData: any) => 
1746 |                   codeData.quotes.map((quote: Entity) => {
1747 |                     const text = quote.observations.find(o => !o.startsWith("source:") && !o.startsWith("context:"));
1748 |                     return `- "${text || "No text"}" [Code: ${codeData.code.name}]`;
1749 |                   })
1750 |                 ).slice(0, 10).join("\n") || "No supporting quotes";
1751 |                 
1752 |                 // Format memos
1753 |                 const memosText = themeAnalysis.memos?.map((memo: Entity) => {
1754 |                   const date = memo.observations.find(o => o.startsWith("date:"))?.substring(5) || "Unknown date";
1755 |                   const topic = memo.observations.find(o => o.startsWith("topic:"))?.substring(6) || "Untitled";
1756 |                   const content = memo.observations.find(o => !o.startsWith("date:") && !o.startsWith("topic:"));
1757 |                   return `- **${topic}** (${date}): ${content ? (content.length > 100 ? content.substring(0, 100) + "..." : content) : "No content"}`;
1758 |                 }).join("\n") || "No analytical memos about this theme";
1759 |                 
1760 |                 contextMessage = `# Theme Context: ${entityName}
1761 | 
1762 | ## Theme Details
1763 | - **Description**: ${description || "No description provided"}
1764 | - **Status**: ${status}
1765 | - **Created**: ${created}
1766 | - **Project**: ${projectName || "Not associated with a specific project"}
1767 | 
1768 | ## Supporting Codes
1769 | ${codesText}
1770 | 
1771 | ## Example Supporting Quotes
1772 | ${quotesText}
1773 | 
1774 | ## Analytical Memos
1775 | ${memosText}`;
1776 |               } else {
1777 |                 const description = entity.observations.find(o => !o.startsWith("created:") && !o.startsWith("status:"));
1778 |                 const status = entity.observations.find(o => o.startsWith("status:"))?.substring(7) || "emerging";
1779 |                 
1780 |                 contextMessage = `# Theme Context: ${entityName}
1781 | 
1782 | ## Theme Details
1783 | - **Description**: ${description || "No description provided"}
1784 | - **Status**: ${status}
1785 | - **Project**: ${projectName || "Not associated with a specific project"}
1786 | 
1787 | No detailed analysis available for this theme.`;
1788 |               }
1789 |             } catch (error) {
1790 |               const description = entity.observations.find(o => !o.startsWith("created:") && !o.startsWith("status:"));
1791 |               const status = entity.observations.find(o => o.startsWith("status:"))?.substring(7) || "emerging";
1792 |               
1793 |               contextMessage = `# Theme Context: ${entityName}
1794 | 
1795 | ## Theme Details
1796 | - **Description**: ${description || "No description provided"}
1797 | - **Status**: ${status}
1798 | - **Project**: ${projectName || "Not associated with a specific project"}
1799 | 
1800 | No detailed analysis available for this theme.`;
1801 |             }
1802 |           }
1803 |           else if (entityType === "memo") {
1804 |             // Get memo details
1805 |             const topic = entity.observations.find(o => o.startsWith("topic:"))?.substring(6) || "Untitled";
1806 |             const date = entity.observations.find(o => o.startsWith("date:"))?.substring(5) || "Unknown date";
1807 |             const content = entity.observations.find(o => !o.startsWith("topic:") && !o.startsWith("date:"));
1808 |             
1809 |             // Find what this memo reflects on
1810 |             const relatedEntities: Entity[] = [];
1811 |             for (const relation of entityGraph.relations) {
1812 |               if (relation.relationType === 'reflects_on' && relation.from === entityName) {
1813 |                 const relatedEntity = entityGraph.entities.find(e => e.name === relation.to);
1814 |                 if (relatedEntity) {
1815 |                   relatedEntities.push(relatedEntity);
1816 |                 }
1817 |               }
1818 |             }
1819 |             
1820 |             // Find which project this memo belongs to
1821 |             let projectName = 'Unknown project';
1822 |             for (const relation of entityGraph.relations) {
1823 |               if (relation.relationType === 'part_of' && relation.from === entityName) {
1824 |                 const project = entityGraph.entities.find(e => e.name === relation.to && e.entityType === 'project');
1825 |                 if (project) {
1826 |                   projectName = project.name;
1827 |                   break;
1828 |                 }
1829 |               }
1830 |             }
1831 |             
1832 |             // Format related entities
1833 |             const relatedText = relatedEntities.map((e: Entity) => `- **${e.name}** (${e.entityType})`).join("\n") || "Not specifically linked to any entities";
1834 |             
1835 |             contextMessage = `# Memo Context: ${entityName}
1836 | 
1837 | ## Memo Details
1838 | - **Topic**: ${topic}
1839 | - **Date**: ${date}
1840 | - **Project**: ${projectName}
1841 | 
1842 | ## Content
1843 | ${content || "No content available"}
1844 | 
1845 | ## Related Entities
1846 | ${relatedText}`;
1847 |           }
1848 |           else if (entityType === "researchQuestion") {
1849 |             // Find which project this research question belongs to
1850 |             let projectName = 'Unknown project';
1851 |             for (const relation of entityGraph.relations) {
1852 |               if (relation.relationType === 'part_of' && relation.from === entityName) {
1853 |                 const project = entityGraph.entities.find(e => e.name === relation.to && e.entityType === 'project');
1854 |                 if (project) {
1855 |                   projectName = project.name;
1856 |                   break;
1857 |                 }
1858 |               }
1859 |             }
1860 |             
1861 |             // Get research question analysis
1862 |             let analysisData;
1863 |             try {
1864 |               analysisData = await knowledgeGraphManager.getResearchQuestionAnalysis(projectName);
1865 |               
1866 |               // Find this question in the analysis
1867 |               const questionAnalysis = analysisData.researchQuestions?.find((q: any) => q.question.name === entityName);
1868 |               
1869 |               if (questionAnalysis) {
1870 |                 // Format findings
1871 |                 const findingsText = questionAnalysis.findings?.map((finding: Entity) => {
1872 |                   const status = finding.observations.find(o => o.startsWith("status:"))?.substring(7) || "preliminary";
1873 |                   const description = finding.observations.find(o => !o.startsWith("status:") && !o.startsWith("created:"));
1874 |                   return `- **${finding.name}** (${status}): ${description || "No description"}`;
1875 |                 }).join("\n") || "No findings recorded yet";
1876 |                 
1877 |                 // Format themes
1878 |                 const themesText = questionAnalysis.themes?.map((theme: Entity) => {
1879 |                   const description = theme.observations.find(o => !o.startsWith("status:") && !o.startsWith("created:"));
1880 |                   return `- **${theme.name}**: ${description || "No description"}`;
1881 |                 }).join("\n") || "No themes associated with this question";
1882 |                 
1883 |                 // Format quotes
1884 |                 const quotesText = questionAnalysis.quotes?.map((quote: Entity) => {
1885 |                   const source = quote.observations.find(o => o.startsWith("source:"))?.substring(7) || "Unknown source";
1886 |                   const text = quote.observations.find(o => !o.startsWith("source:") && !o.startsWith("context:"));
1887 |                   return `- "${text || "No text"}" (Source: ${source})`;
1888 |                 }).slice(0, 5).join("\n") || "No direct quotes addressing this question";
1889 |                 
1890 |                 contextMessage = `# Research Question Context: ${entityName}
1891 | 
1892 | ## Question
1893 | ${entity.observations.find(o => !o.startsWith("created:")) || entityName}
1894 | 
1895 | ## Project
1896 | ${projectName}
1897 | 
1898 | ## Findings
1899 | ${findingsText}
1900 | 
1901 | ## Related Themes
1902 | ${themesText}
1903 | 
1904 | ## Supporting Quotes
1905 | ${quotesText}`;
1906 |               } else {
1907 |                 contextMessage = `# Research Question Context: ${entityName}
1908 | 
1909 | ## Question
1910 | ${entity.observations.find(o => !o.startsWith("created:")) || entityName}
1911 | 
1912 | ## Project
1913 | ${projectName}
1914 | 
1915 | No analysis data available for this research question.`;
1916 |               }
1917 |             } catch (error) {
1918 |               contextMessage = `# Research Question Context: ${entityName}
1919 | 
1920 | ## Question
1921 | ${entity.observations.find(o => !o.startsWith("created:")) || entityName}
1922 | 
1923 | ## Project
1924 | ${projectName}
1925 | 
1926 | No analysis data available for this research question.`;
1927 |             }
1928 |           }
1929 |           else {
1930 |             // Generic entity context for other entity types
1931 |             // Get related entities
1932 |             const relatedEntitiesData = await knowledgeGraphManager.getRelatedEntities(entityName);
1933 |             
1934 |             // Format observations
1935 |             const observationsText = entity.observations.map((obs: string) => `- ${obs}`).join("\n") || "No observations";
1936 |             
1937 |             // Format related entities
1938 |             const relatedText = Object.entries(relatedEntitiesData.related || {}).map(([relation, entities]) => {
1939 |               const entitiesList = (entities as any[]).map(e => `- **${e.name}** (${e.entityType})`).join("\n");
1940 |               return `### ${relation} (${(entities as any[]).length})\n${entitiesList}`;
1941 |             }).join("\n\n") || "No related entities found";
1942 |             
1943 |             contextMessage = `# Entity Context: ${entityName} (${entityType})
1944 | 
1945 | ## Observations
1946 | ${observationsText}
1947 | 
1948 | ## Related Entities
1949 | ${relatedText}`;
1950 |           }
1951 |           
1952 |           return {
1953 |             content: [{
1954 |               type: "text",
1955 |               text: contextMessage
1956 |             }]
1957 |           };
1958 |         } catch (error) {
1959 |           return {
1960 |             content: [{
1961 |               type: "text",
1962 |               text: JSON.stringify({ 
1963 |                 success: false,
1964 |                 error: error instanceof Error ? error.message : String(error)
1965 |               }, null, 2)
1966 |             }]
1967 |           };
1968 |         }
1969 |       }
1970 |     );
1971 | 
1972 |     // Helper function to process each stage of endsession
1973 |     async function processStage(params: {
1974 |       sessionId: string;
1975 |       stage: string;
1976 |       stageNumber: number;
1977 |       totalStages: number;
1978 |       analysis?: string;
1979 |       stageData?: any;
1980 |       nextStageNeeded: boolean;
1981 |       isRevision?: boolean;
1982 |       revisesStage?: number;
1983 |     }, previousStages: any[]): Promise<any> {
1984 |       // Process based on the stage
1985 |       switch (params.stage) {
1986 |         case "summary":
1987 |           // Process summary stage
1988 |           return {
1989 |             stage: "summary",
1990 |             stageNumber: params.stageNumber,
1991 |             analysis: params.analysis || "",
1992 |             stageData: params.stageData || { 
1993 |               summary: "",
1994 |               duration: "",
1995 |               project: ""
1996 |             },
1997 |             completed: !params.nextStageNeeded
1998 |           };
1999 |           
2000 |         case "interviewData":
2001 |           // Process interview data stage
2002 |           return {
2003 |             stage: "interviewData",
2004 |             stageNumber: params.stageNumber,
2005 |             analysis: params.analysis || "",
2006 |             stageData: params.stageData || { interviews: [] },
2007 |             completed: !params.nextStageNeeded
2008 |           };
2009 |           
2010 |         case "memos":
2011 |           // Process memos stage
2012 |           return {
2013 |             stage: "memos",
2014 |             stageNumber: params.stageNumber,
2015 |             analysis: params.analysis || "",
2016 |             stageData: params.stageData || { memos: [] },
2017 |             completed: !params.nextStageNeeded
2018 |           };
2019 |           
2020 |         case "codingActivity":
2021 |           // Process coding activity stage
2022 |           return {
2023 |             stage: "codingActivity",
2024 |             stageNumber: params.stageNumber,
2025 |             analysis: params.analysis || "",
2026 |             stageData: params.stageData || { codes: [] },
2027 |             completed: !params.nextStageNeeded
2028 |           };
2029 |           
2030 |         case "themes":
2031 |           // Process themes stage
2032 |           return {
2033 |             stage: "themes",
2034 |             stageNumber: params.stageNumber,
2035 |             analysis: params.analysis || "",
2036 |             stageData: params.stageData || { themes: [] },
2037 |             completed: !params.nextStageNeeded
2038 |           };
2039 |           
2040 |         case "projectStatus":
2041 |           // Process project status stage
2042 |           return {
2043 |             stage: "projectStatus",
2044 |             stageNumber: params.stageNumber,
2045 |             analysis: params.analysis || "",
2046 |             stageData: params.stageData || { 
2047 |               projectStatus: "",
2048 |               projectObservation: ""
2049 |             },
2050 |             completed: !params.nextStageNeeded
2051 |           };
2052 |           
2053 |         case "assembly":
2054 |           // Final assembly stage - compile all arguments for end-session
2055 |           return {
2056 |             stage: "assembly",
2057 |             stageNumber: params.stageNumber,
2058 |             analysis: "Final assembly of end-session arguments",
2059 |             stageData: assembleEndSessionArgs(previousStages),
2060 |             completed: true
2061 |           };
2062 |           
2063 |         default:
2064 |           throw new Error(`Unknown stage: ${params.stage}`);
2065 |       }
2066 |     }
2067 | 
2068 |     // Helper function to assemble the final end-session arguments
2069 |     function assembleEndSessionArgs(stages: any[]): any {
2070 |       const summaryStage = stages.find(s => s.stage === "summary");
2071 |       const interviewDataStage = stages.find(s => s.stage === "interviewData");
2072 |       const memosStage = stages.find(s => s.stage === "memos");
2073 |       const codingActivityStage = stages.find(s => s.stage === "codingActivity");
2074 |       const themesStage = stages.find(s => s.stage === "themes");
2075 |       const projectStatusStage = stages.find(s => s.stage === "projectStatus");
2076 |       
2077 |       return {
2078 |         summary: summaryStage?.stageData?.summary || "",
2079 |         duration: summaryStage?.stageData?.duration || "unknown",
2080 |         project: summaryStage?.stageData?.project || "",
2081 |         interviewData: JSON.stringify(interviewDataStage?.stageData?.interviews || []),
2082 |         newMemos: JSON.stringify(memosStage?.stageData?.memos || []),
2083 |         codingActivity: JSON.stringify(codingActivityStage?.stageData?.codes || []),
2084 |         newThemes: JSON.stringify(themesStage?.stageData?.themes || []),
2085 |         projectStatus: projectStatusStage?.stageData?.projectStatus || "",
2086 |         projectObservation: projectStatusStage?.stageData?.projectObservation || ""
2087 |       };
2088 |     }
2089 | 
2090 |     /**
2091 |      * End session by processing all stages and recording the final results.
2092 |      * Only use this tool if the user asks for it.
2093 |      * 
2094 |      * Usage examples:
2095 |      * 
2096 |      * 1. Starting the end session process with the summary stage:
2097 |      * {
2098 |      *   "sessionId": "qual_1234567890_abc123",  // From startsession
2099 |      *   "stage": "summary",
2100 |      *   "stageNumber": 1,
2101 |      *   "totalStages": 6, 
2102 |      *   "analysis": "Analyzed progress on the interview data coding",
2103 |      *   "stageData": {
2104 |      *     "summary": "Completed initial coding of participant interviews",
2105 |      *     "duration": "3 hours",
2106 |      *     "project": "Health Behavior Study"  // Project name
2107 |      *   },
2108 |      *   "nextStageNeeded": true,  // More stages coming
2109 |      *   "isRevision": false
2110 |      * }
2111 |      * 
2112 |      * 2. Middle stage for themes:
2113 |      * {
2114 |      *   "sessionId": "qual_1234567890_abc123",
2115 |      *   "stage": "themes",
2116 |      *   "stageNumber": 2,
2117 |      *   "totalStages": 6,
2118 |      *   "analysis": "Identified emerging themes",
2119 |      *   "stageData": {
2120 |      *     "themes": [
2121 |      *       { "name": "Perceived Barriers", "codes": ["time_constraints", "financial_concerns"], "description": "Factors preventing healthy behaviors" },
2122 |      *       { "name": "Social Support", "codes": ["family_influence", "peer_encouragement"], "description": "External motivation from relationships" }
2123 |      *     ]
2124 |      *   },
2125 |      *   "nextStageNeeded": true,
2126 |      *   "isRevision": false
2127 |      * }
2128 |      * 
2129 |      * 3. Final assembly stage:
2130 |      * {
2131 |      *   "sessionId": "qual_1234567890_abc123",
2132 |      *   "stage": "assembly",
2133 |      *   "stageNumber": 6,
2134 |      *   "totalStages": 6,
2135 |      *   "nextStageNeeded": false,  // This completes the session
2136 |      *   "isRevision": false
2137 |      * }
2138 |      */
2139 |     server.tool(
2140 |       "endsession",
2141 |       toolDescriptions["endsession"],
2142 |       {
2143 |         sessionId: z.string().describe("The unique session identifier obtained from startsession"),
2144 |         stage: z.string().describe("Current stage of analysis: 'summary', 'themes', 'codes', 'memos', 'participantInsights', or 'assembly'"),
2145 |         stageNumber: z.number().int().positive().describe("The sequence number of the current stage (starts at 1)"),
2146 |         totalStages: z.number().int().positive().describe("Total number of stages in the workflow (typically 6 for standard workflow)"),
2147 |         analysis: z.string().optional().describe("Text analysis or observations for the current stage"),
2148 |         stageData: z.record(z.string(), z.any()).optional().describe(`Stage-specific data structure - format depends on the stage type:
2149 |         - For 'summary' stage: { summary: "Session summary text", duration: "3 hours", project: "Project Name" }
2150 |         - For 'themes' stage: { themes: [{ name: "Theme1", codes: ["code1", "code2"], description: "Theme description" }] }
2151 |         - For 'codes' stage: { codes: [{ name: "Code1", description: "Code meaning", quotes: ["Quote text"] }] }
2152 |         - For 'memos' stage: { memos: [{ title: "Memo title", content: "Detailed memo text", tags: ["tag1", "tag2"] }] }
2153 |         - For 'participantInsights' stage: { insights: [{ participant: "P1", observation: "Key insight about participant" }] }
2154 |         - For 'assembly' stage: no stageData needed - automatic assembly of previous stages`),
2155 |         nextStageNeeded: z.boolean().describe("Whether additional stages are needed after this one (false for final stage)"),
2156 |         isRevision: z.boolean().optional().describe("Whether this is revising a previous stage"),
2157 |         revisesStage: z.number().int().positive().optional().describe("If revising, which stage number is being revised")
2158 |       },
2159 |       async (params) => {
2160 |         try {
2161 |           // Load session states from persistent storage
2162 |           const sessionStates = await loadSessionStates();
2163 |           
2164 |           // Validate session ID
2165 |           if (!sessionStates.has(params.sessionId)) {
2166 |             return {
2167 |               content: [{
2168 |                 type: "text",
2169 |                 text: JSON.stringify({ 
2170 |                   success: false,
2171 |                   error: `Session with ID ${params.sessionId} not found. Please start a new session with startsession.`
2172 |                 }, null, 2)
2173 |               }]
2174 |             };
2175 |           }
2176 |           
2177 |           // Get or initialize session state
2178 |           let sessionState = sessionStates.get(params.sessionId) || [];
2179 |           
2180 |           // Process the current stage
2181 |           const stageResult = await processStage(params, sessionState);
2182 |           
2183 |           // Store updated state
2184 |           if (params.isRevision && params.revisesStage) {
2185 |             // Find the analysis stages in the session state
2186 |             const analysisStages = sessionState.filter(item => item.type === 'analysis_stage') || [];
2187 |             
2188 |             if (params.revisesStage <= analysisStages.length) {
2189 |               // Replace the revised stage
2190 |               analysisStages[params.revisesStage - 1] = {
2191 |                 type: 'analysis_stage',
2192 |                 ...stageResult
2193 |               };
2194 |             } else {
2195 |               // Add as a new stage
2196 |               analysisStages.push({
2197 |                 type: 'analysis_stage',
2198 |                 ...stageResult
2199 |               });
2200 |             }
2201 |             
2202 |             // Update the session state with the modified analysis stages
2203 |             sessionState = [
2204 |               ...sessionState.filter(item => item.type !== 'analysis_stage'),
2205 |               ...analysisStages
2206 |             ];
2207 |           } else {
2208 |             // Add new stage
2209 |             sessionState.push({
2210 |               type: 'analysis_stage',
2211 |               ...stageResult
2212 |             });
2213 |           }
2214 |           
2215 |           // Update in persistent storage
2216 |           sessionStates.set(params.sessionId, sessionState);
2217 |           await saveSessionStates(sessionStates);
2218 |           
2219 |           // Check if this is the final assembly stage and no more stages are needed
2220 |           if (params.stage === "assembly" && !params.nextStageNeeded) {
2221 |             // Get the assembled arguments
2222 |             const args = stageResult.stageData;
2223 |             
2224 |             try {
2225 |               // Parse arguments
2226 |               const summary = args.summary;
2227 |               const duration = args.duration;
2228 |               const project = args.project;
2229 |               const interviewData = args.interviewData ? JSON.parse(args.interviewData) : [];
2230 |               const newMemos = args.newMemos ? JSON.parse(args.newMemos) : [];
2231 |               const codingActivity = args.codingActivity ? JSON.parse(args.codingActivity) : [];
2232 |               const newThemes = args.newThemes ? JSON.parse(args.newThemes) : [];
2233 |               const projectStatus = args.projectStatus;
2234 |               const projectObservation = args.projectObservation;
2235 |               
2236 |               // Update project status using the relation-based approach
2237 |               try {
2238 |                 // Set the project status using our helper method
2239 |                 if (projectStatus) {
2240 |                   await knowledgeGraphManager.setEntityStatus(project, projectStatus);
2241 |                 }
2242 |                 
2243 |                 // Add observation if provided
2244 |                 if (projectObservation) {
2245 |                   await knowledgeGraphManager.addObservations([{
2246 |                     entityName: project,
2247 |                     contents: [projectObservation]
2248 |                   }]);
2249 |                 }
2250 |               } catch (error) {
2251 |                 console.error(`Error updating status for project ${project}:`, error);
2252 |               }
2253 |               
2254 |               // Record session completion in persistent storage
2255 |               sessionState.push({
2256 |                 type: 'session_completed',
2257 |                 timestamp: new Date().toISOString(),
2258 |                 project
2259 |               });
2260 |               
2261 |               sessionStates.set(params.sessionId, sessionState);
2262 |               await saveSessionStates(sessionStates);
2263 |               
2264 |               // Prepare the summary message
2265 |               const summaryMessage = `# Qualitative Research Session Recorded
2266 | 
2267 | I've recorded your research session focusing on the ${project} project.
2268 | 
2269 | ## Session Summary
2270 | ${summary}
2271 | 
2272 | ${interviewData.length > 0 ? `## Interviews Conducted
2273 | ${interviewData.map((i: {participant: string, notes: string}) => 
2274 |   `- Interview with ${i.participant}`
2275 | ).join('\n')}` : "No interviews were recorded."}
2276 | 
2277 | ${newMemos.length > 0 ? `## Research Memos Created
2278 | ${newMemos.map((m: {topic: string, content: string}) => `- ${m.topic}`).join('\n')}` : "No memos were created."}
2279 | 
2280 | ${codingActivity.length > 0 ? `## Coding Activity
2281 | ${codingActivity.map((c: {code: string, dataItem: string, note?: string}) => 
2282 |   `- Coded ${c.dataItem} with "${c.code}"${c.note ? `: ${c.note}` : ''}`
2283 | ).join('\n')}` : "No coding was performed."}
2284 | 
2285 | ${newThemes.length > 0 ? `## Themes Identified
2286 | ${newThemes.map((t: {name: string, description: string}) => `- ${t.name}: ${t.description}`).join('\n')}` : "No themes were identified."}
2287 | 
2288 | ## Project Status
2289 | Project ${project} has been updated to: ${projectStatus}
2290 | 
2291 | Would you like me to perform any additional updates to your qualitative research knowledge graph?`;
2292 |               
2293 |               // Return the final result with the session recorded message
2294 |               return {
2295 |                 content: [{
2296 |                   type: "text",
2297 |                   text: JSON.stringify({
2298 |                     success: true,
2299 |                     stageCompleted: params.stage,
2300 |                     nextStageNeeded: false,
2301 |                     stageResult: stageResult,
2302 |                     sessionRecorded: true,
2303 |                     summaryMessage: summaryMessage
2304 |                   }, null, 2)
2305 |                 }]
2306 |               };
2307 |             } catch (error) {
2308 |               return {
2309 |                 content: [{
2310 |                   type: "text",
2311 |                   text: JSON.stringify({
2312 |                     success: false,
2313 |                     error: `Error assembling end-session arguments: ${error instanceof Error ? error.message : String(error)}`
2314 |                   }, null, 2)
2315 |                 }]
2316 |               };
2317 |             }
2318 |           } else {
2319 |             // This is not the final stage or more stages are needed
2320 |             // Return intermediate result
2321 |             return {
2322 |               content: [{
2323 |                 type: "text",
2324 |                 text: JSON.stringify({
2325 |                   success: true,
2326 |                   stageCompleted: params.stage,
2327 |                   nextStageNeeded: params.nextStageNeeded,
2328 |                   stageResult: stageResult,
2329 |                   endSessionArgs: params.stage === "assembly" ? stageResult.stageData : null
2330 |                 }, null, 2)
2331 |               }]
2332 |             };
2333 |           }
2334 |         } catch (error) {
2335 |           return {
2336 |             content: [{
2337 |               type: "text",
2338 |               text: JSON.stringify({
2339 |                 success: false,
2340 |                 error: `Error recording qualitative research session: ${error instanceof Error ? error.message : String(error)}`
2341 |               }, null, 2)
2342 |             }]
2343 |           };
2344 |         }
2345 |       }
2346 |     );
2347 | 
2348 |     /**
2349 |      * Start a new session for qualitative research. Returns session ID, recent sessions, active projects, sample participants, top codes, and recent memos.
2350 |      * The output allows the user to easily choose what to focus on and which specific context to load.
2351 |      */
2352 |     server.tool(
2353 |       "startsession",
2354 |       toolDescriptions["startsession"],
2355 |       {},
2356 |       async () => {
2357 |         try {
2358 |           // Generate a unique session ID
2359 |           const sessionId = generateSessionId();
2360 |           
2361 |           // Get recent sessions from persistent storage
2362 |           const sessionStates = await loadSessionStates();
2363 | 
2364 |           // Initialize the session state
2365 |           sessionStates.set(sessionId, []);
2366 |           await saveSessionStates(sessionStates);
2367 |           
2368 |           // Convert sessions map to array and retrieve the most recent sessions
2369 |           const recentSessions = Array.from(sessionStates.entries())
2370 |             .map(([id, stages]) => {
2371 |               // Extract summary data from the first stage (if it exists)
2372 |               const summaryStage = stages.find(s => s.stage === "summary");
2373 |               return {
2374 |                 id,
2375 |                 project: summaryStage?.stageData?.project || "Unknown project",
2376 |                 summary: summaryStage?.stageData?.summary || "No summary available"
2377 |               };
2378 |             })
2379 |             .slice(0, 3); // Default to showing 3 recent sessions
2380 |           
2381 |           // Query for all research projects and filter by status
2382 |           const projectsQuery = await knowledgeGraphManager.searchNodes("entityType:project");
2383 |           const projects = [];
2384 |           
2385 |           // Filter for active projects based on has_status relation
2386 |           for (const project of projectsQuery.entities) {
2387 |             const status = await knowledgeGraphManager.getEntityStatus(project.name);
2388 |             if (status === "active" || status === "in_progress" || status === "data_collection" || status === "analysis") {
2389 |               projects.push(project);
2390 |             }
2391 |           }
2392 |           
2393 |           // Query for a sample of participants
2394 |           const participantsQuery = await knowledgeGraphManager.searchNodes("entityType:participant");
2395 |           const participants = participantsQuery.entities.slice(0, 5); // Limit to 5 participants for initial display
2396 |           
2397 |           // Get all codes
2398 |           const codesQuery = await knowledgeGraphManager.searchNodes("entityType:code");
2399 |           const codes = codesQuery.entities.slice(0, 10); // Top 10 codes
2400 |           
2401 |           // Get recent memos
2402 |           const memosQuery = await knowledgeGraphManager.searchNodes("entityType:memo");
2403 |           const memos = memosQuery.entities.slice(0, 3); // Most recent 3 memos
2404 |           
2405 |           // Format the context information using entity-relation approach
2406 |           const projectsText = await Promise.all(projects.map(async p => {
2407 |             const status = await knowledgeGraphManager.getEntityStatus(p.name) || "Unknown";
2408 |             const priority = await knowledgeGraphManager.getEntityPriority(p.name);
2409 |             const priorityText = priority ? `, Priority: ${priority}` : "";
2410 |             
2411 |             // Show truncated preview of first observation
2412 |             const preview = p.observations.length > 0 
2413 |               ? `${p.observations[0].substring(0, 60)}${p.observations[0].length > 60 ? '...' : ''}`
2414 |               : "No description";
2415 |               
2416 |             return `- **${p.name}** (Status: ${status}${priorityText}): ${preview}`;
2417 |           }));
2418 |           
2419 |           const participantsText = await Promise.all(participants.map(async p => {
2420 |             const status = await knowledgeGraphManager.getEntityStatus(p.name) || "Active";
2421 |             
2422 |             // Show truncated preview of first observation for demographics
2423 |             const preview = p.observations.length > 0 
2424 |               ? `${p.observations[0].substring(0, 60)}${p.observations[0].length > 60 ? '...' : ''}`
2425 |               : "No demographics";
2426 |               
2427 |             return `- **${p.name}** (Status: ${status}): ${preview}`;
2428 |           }));
2429 |           
2430 |           const codesText = await Promise.all(codes.map(async c => {
2431 |             const status = await knowledgeGraphManager.getEntityStatus(c.name) || "initial";
2432 |             
2433 |             // Show truncated preview of first observation for description
2434 |             const preview = c.observations.length > 0 
2435 |               ? `${c.observations[0].substring(0, 60)}${c.observations[0].length > 60 ? '...' : ''}`
2436 |               : "No description";
2437 |               
2438 |             return `- **${c.name}** (Status: ${status}): ${preview}`;
2439 |           }));
2440 |           
2441 |           const memosText = await Promise.all(memos.map(async m => {
2442 |             const status = await knowledgeGraphManager.getEntityStatus(m.name) || "draft";
2443 |             
2444 |             // Show truncated preview of first observation for content
2445 |             const preview = m.observations.length > 0 
2446 |               ? `${m.observations[0].substring(0, 60)}${m.observations[0].length > 60 ? '...' : ''}`
2447 |               : "No content";
2448 |               
2449 |             return `- **${m.name}** (Status: ${status}): ${preview}`;
2450 |           }));
2451 |           
2452 |           const sessionsText = recentSessions.map(s => {
2453 |             return `- ${s.project} - ${s.summary.substring(0, 60)}${s.summary.length > 60 ? '...' : ''}`;
2454 |           }).join("\n");
2455 |           
2456 |           return {
2457 |             content: [{
2458 |               type: "text",
2459 |               text: `# Choose what to focus on in this session
2460 | 
2461 | ## Session ID
2462 | \`${sessionId}\`
2463 | 
2464 | ## Recent Research Sessions
2465 | ${sessionsText || "No recent sessions found."}
2466 | 
2467 | ## Active Research Projects
2468 | ${projectsText.join("\n") || "No active projects found."}
2469 | 
2470 | ## Sample Participants
2471 | ${participantsText.join("\n") || "No participants found."}
2472 | 
2473 | ## Top Codes
2474 | ${codesText.join("\n") || "No codes found."}
2475 | 
2476 | ## Recent Memos
2477 | ${memosText.join("\n") || "No memos found."}
2478 | 
2479 | To load specific context, use the \`loadcontext\` tool with the entity name and session ID - ${sessionId}`
2480 |             }]
2481 |           };
2482 |         } catch (error) {
2483 |           return {
2484 |             content: [{
2485 |               type: "text",
2486 |               text: JSON.stringify({ 
2487 |                 success: false,
2488 |                 error: error instanceof Error ? error.message : String(error)
2489 |               }, null, 2)
2490 |             }]
2491 |           };
2492 |         }
2493 |       }
2494 |     );
2495 | 
2496 |     /**
2497 |      * Create new entities, relations, and observations.
2498 |      */
2499 |     server.tool(
2500 |       "buildcontext",
2501 |       toolDescriptions["buildcontext"],
2502 |       {
2503 |         type: z.enum(["entities", "relations", "observations"]).describe("Type of creation operation: 'entities', 'relations', or 'observations'"),
2504 |         data: z.array(z.any()).describe("Data for the creation operation, structure varies by type but must be an array")
2505 |       },
2506 |       async ({ type, data }) => {
2507 |         try {
2508 |           let result;
2509 |           
2510 |           switch (type) {
2511 |             case "entities":
2512 |               // Validate entity types
2513 |               for (const entity of data) {
2514 |                 if (!validateEntityType(entity.entityType)) {
2515 |                   throw new Error(`Invalid entity type: ${entity.entityType}`);
2516 |                 }
2517 |               }
2518 |               
2519 |               // Ensure entities match the Entity interface
2520 |               const typedEntities: Entity[] = data.map((e: any) => ({
2521 |                 name: e.name,
2522 |                 entityType: e.entityType,
2523 |                 observations: e.observations
2524 |               }));
2525 |               result = await knowledgeGraphManager.createEntities(typedEntities);
2526 |               return {
2527 |                 content: [{
2528 |                   type: "text",
2529 |                   text: JSON.stringify({ success: true, created: result }, null, 2)
2530 |                 }]
2531 |               };
2532 |               
2533 |             case "relations":
2534 |               // Validate relation types
2535 |               for (const relation of data) {
2536 |                 if (!validateRelationType(relation.relationType)) {
2537 |                   throw new Error(`Invalid relation type: ${relation.relationType}`);
2538 |                 }
2539 |               }
2540 |               
2541 |               // Ensure relations match the Relation interface
2542 |               const typedRelations: Relation[] = data.map((r: any) => ({
2543 |                 from: r.from,
2544 |                 to: r.to,
2545 |                 relationType: r.relationType
2546 |               }));
2547 |               result = await knowledgeGraphManager.createRelations(typedRelations);
2548 |               return {
2549 |                 content: [{
2550 |                   type: "text",
2551 |                   text: JSON.stringify({ success: true, created: result }, null, 2)
2552 |                 }]
2553 |               };
2554 |               
2555 |             case "observations":
2556 |               // For qualitative researcher domain, addObservations takes an array
2557 |               // Ensure observations match the required interface
2558 |               const typedObservations: { entityName: string; contents: string[] }[] = 
2559 |                 Array.isArray(data) ? data.map((o: any) => ({
2560 |                   entityName: o.entityName,
2561 |                   contents: Array.isArray(o.contents) ? o.contents : 
2562 |                            Array.isArray(o.observations) ? o.observations : []
2563 |                 })) : [data];
2564 |               
2565 |               result = await knowledgeGraphManager.addObservations(typedObservations);
2566 |               return {
2567 |                 content: [{
2568 |                   type: "text",
2569 |                   text: JSON.stringify({ success: true, added: result }, null, 2)
2570 |                 }]
2571 |               };
2572 |               
2573 |             default:
2574 |               throw new Error(`Invalid type: ${type}. Must be 'entities', 'relations', or 'observations'.`);
2575 |           }
2576 |         } catch (error) {
2577 |           return {
2578 |             content: [{
2579 |               type: "text",
2580 |               text: JSON.stringify({ 
2581 |                 success: false,
2582 |                 error: error instanceof Error ? error.message : String(error)
2583 |               }, null, 2)
2584 |             }]
2585 |           };
2586 |         }
2587 |       }
2588 |     );
2589 |     
2590 |     /**
2591 |      * Delete entities, relations, or observations.
2592 |      */
2593 |     server.tool(
2594 |       "deletecontext",
2595 |       toolDescriptions["deletecontext"],
2596 |       {
2597 |         type: z.enum(["entities", "relations", "observations"]).describe("Type of deletion operation: 'entities', 'relations', or 'observations'"),
2598 |         data: z.array(z.any()).describe("Data for the deletion operation, structure varies by type but must be an array")
2599 |       },
2600 |       async ({ type, data }) => {
2601 |         try {
2602 |           switch (type) {
2603 |             case "entities":
2604 |               await knowledgeGraphManager.deleteEntities(data);
2605 |               return {
2606 |                 content: [{
2607 |                   type: "text",
2608 |                   text: JSON.stringify({ success: true, message: `Deleted ${data.length} entities` }, null, 2)
2609 |                 }]
2610 |               };
2611 |               
2612 |             case "relations":
2613 |               // Ensure relations match the Relation interface
2614 |               const typedRelations: Relation[] = data.map((r: any) => ({
2615 |                 from: r.from,
2616 |                 to: r.to,
2617 |                 relationType: r.relationType
2618 |               }));
2619 |               await knowledgeGraphManager.deleteRelations(typedRelations);
2620 |               return {
2621 |                 content: [{
2622 |                   type: "text",
2623 |                   text: JSON.stringify({ success: true, message: `Deleted ${data.length} relations` }, null, 2)
2624 |                 }]
2625 |               };
2626 |               
2627 |             case "observations":
2628 |               // Ensure deletions match the required interface
2629 |               const typedDeletions: { entityName: string; observations: string[] }[] = data.map((d: any) => ({
2630 |                 entityName: d.entityName,
2631 |                 observations: d.observations
2632 |               }));
2633 |               await knowledgeGraphManager.deleteObservations(typedDeletions);
2634 |               return {
2635 |                 content: [{
2636 |                   type: "text",
2637 |                   text: JSON.stringify({ success: true, message: `Deleted observations from ${data.length} entities` }, null, 2)
2638 |                 }]
2639 |               };
2640 |               
2641 |             default:
2642 |               throw new Error(`Invalid type: ${type}. Must be 'entities', 'relations', or 'observations'.`);
2643 |           }
2644 |         } catch (error) {
2645 |           return {
2646 |             content: [{
2647 |               type: "text",
2648 |               text: JSON.stringify({ 
2649 |                 success: false,
2650 |                 error: error instanceof Error ? error.message : String(error)
2651 |               }, null, 2)
2652 |             }]
2653 |           };
2654 |         }
2655 |       }
2656 |     );
2657 |     
2658 |     /**
2659 |      * 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.
2660 |      */
2661 |     server.tool(
2662 |       "advancedcontext",
2663 |       toolDescriptions["advancedcontext"],
2664 |       {
2665 |         type: z.enum([
2666 |           "graph", 
2667 |           "search", 
2668 |           "nodes", 
2669 |           "project", 
2670 |           "participant", 
2671 |           "codes", 
2672 |           "themes", 
2673 |           "transcript", 
2674 |           "memo", 
2675 |           "analysis", 
2676 |           "codebook", 
2677 |           "related"
2678 |         ]).describe("Type of get operation"),
2679 |         params: z.record(z.string(), z.any()).describe("Parameters for the get operation, structure varies by type")
2680 |       },
2681 |       async ({ type, params }) => {
2682 |         try {
2683 |           let result;
2684 |           
2685 |           switch (type) {
2686 |             case "graph":
2687 |               result = await knowledgeGraphManager.readGraph();
2688 |               return {
2689 |                 content: [{
2690 |                   type: "text",
2691 |                   text: JSON.stringify({ success: true, graph: result }, null, 2)
2692 |                 }]
2693 |               };
2694 |               
2695 |             case "search":
2696 |               result = await knowledgeGraphManager.searchNodes(params.query);
2697 |               return {
2698 |                 content: [{
2699 |                   type: "text",
2700 |                   text: JSON.stringify({ success: true, results: result }, null, 2)
2701 |                 }]
2702 |               };
2703 |               
2704 |             case "nodes":
2705 |               result = await knowledgeGraphManager.openNodes(params.names);
2706 |               return {
2707 |                 content: [{
2708 |                   type: "text",
2709 |                   text: JSON.stringify({ success: true, nodes: result }, null, 2)
2710 |                 }]
2711 |               };
2712 |               
2713 |             case "project":
2714 |               result = await knowledgeGraphManager.getProjectOverview(params.projectName);
2715 |               return {
2716 |                 content: [{
2717 |                   type: "text",
2718 |                   text: JSON.stringify({ success: true, project: result }, null, 2)
2719 |                 }]
2720 |               };
2721 |               
2722 |             case "participant":
2723 |               result = await knowledgeGraphManager.getParticipantProfile(params.participantName);
2724 |               return {
2725 |                 content: [{
2726 |                   type: "text",
2727 |                   text: JSON.stringify({ success: true, participant: result }, null, 2)
2728 |                 }]
2729 |               };
2730 |               
2731 |             case "codes":
2732 |               // Use searchNodes for codes instead of a specialized method
2733 |               result = await knowledgeGraphManager.searchNodes(`entityType:code ${params.projectName ? `project:${params.projectName}` : ""}`);
2734 |               return {
2735 |                 content: [{
2736 |                   type: "text",
2737 |                   text: JSON.stringify({ success: true, codes: result }, null, 2)
2738 |                 }]
2739 |               };
2740 |               
2741 |             case "themes":
2742 |               // Use searchNodes for themes
2743 |               result = await knowledgeGraphManager.searchNodes(`entityType:theme ${params.projectName ? `project:${params.projectName}` : ""}`);
2744 |               return {
2745 |                 content: [{
2746 |                   type: "text",
2747 |                   text: JSON.stringify({ success: true, themes: result }, null, 2)
2748 |                 }]
2749 |               };
2750 |               
2751 |             case "transcript":
2752 |               // Use searchNodes to find the transcript
2753 |               const transcriptQuery = await knowledgeGraphManager.searchNodes(
2754 |                 `entityType:transcript participant:${params.participantName} ${params.interviewId ? `interview:${params.interviewId}` : ""}`
2755 |               );
2756 |               return {
2757 |                 content: [{
2758 |                   type: "text",
2759 |                   text: JSON.stringify({ success: true, transcript: transcriptQuery }, null, 2)
2760 |                 }]
2761 |               };
2762 |               
2763 |             case "memo":
2764 |               // Use openNodes to get the specific memo
2765 |               const memoResult = await knowledgeGraphManager.openNodes([params.memoName]);
2766 |               return {
2767 |                 content: [{
2768 |                   type: "text",
2769 |                   text: JSON.stringify({ success: true, memo: memoResult }, null, 2)
2770 |                 }]
2771 |               };
2772 |               
2773 |             case "analysis":
2774 |               // Use searchNodes to get analysis artifacts
2775 |               const analysisQuery = await knowledgeGraphManager.searchNodes(`entityType:analysis project:${params.projectName}`);
2776 |               return {
2777 |                 content: [{
2778 |                   type: "text",
2779 |                   text: JSON.stringify({ success: true, analysis: analysisQuery }, null, 2)
2780 |                 }]
2781 |               };
2782 |               
2783 |             case "codebook":
2784 |               // Use searchNodes to get codebook entries
2785 |               const codebookQuery = await knowledgeGraphManager.searchNodes(`entityType:code project:${params.projectName}`);
2786 |               return {
2787 |                 content: [{
2788 |                   type: "text",
2789 |                   text: JSON.stringify({ success: true, codebook: codebookQuery }, null, 2)
2790 |                 }]
2791 |               };
2792 |               
2793 |             case "related":
2794 |               // For the related case, we don't have a specialized method in the manager
2795 |               // So we'll use the generic KnowledgeGraph search capabilities
2796 |               const entityGraph = await knowledgeGraphManager.searchNodes(params.entityName);
2797 |               const entity = entityGraph.entities.find(e => e.name === params.entityName);
2798 |               
2799 |               if (!entity) {
2800 |                 throw new Error(`Entity "${params.entityName}" not found`);
2801 |               }
2802 |               
2803 |               // Find related entities
2804 |               const relations = entityGraph.relations.filter(r => 
2805 |                 r.from === params.entityName || r.to === params.entityName
2806 |               );
2807 |               
2808 |               const relatedNames = relations.map(r => 
2809 |                 r.from === params.entityName ? r.to : r.from
2810 |               );
2811 |               
2812 |               if (relatedNames.length === 0) {
2813 |                 return {
2814 |                   content: [{
2815 |                     type: "text",
2816 |                     text: JSON.stringify({ success: true, related: { entity, relatedEntities: [] } }, null, 2)
2817 |                   }]
2818 |                 };
2819 |               }
2820 |               
2821 |               const relatedEntitiesGraph = await knowledgeGraphManager.openNodes(relatedNames);
2822 |               
2823 |               return {
2824 |                 content: [{
2825 |                   type: "text",
2826 |                   text: JSON.stringify({
2827 |                     success: true,
2828 |                     related: {
2829 |                       entity,
2830 |                       relations,
2831 |                       relatedEntities: relatedEntitiesGraph.entities
2832 |                     }
2833 |                   }, null, 2)
2834 |                 }]
2835 |               };
2836 |               
2837 |             default:
2838 |               throw new Error(`Invalid type: ${type}. Must be one of the supported get operation types.`);
2839 |           }
2840 |         } catch (error) {
2841 |           return {
2842 |             content: [{
2843 |               type: "text",
2844 |               text: JSON.stringify({ 
2845 |                 success: false,
2846 |                 error: error instanceof Error ? error.message : String(error)
2847 |               }, null, 2)
2848 |             }]
2849 |           };
2850 |         }
2851 |       }
2852 |     );
2853 |   
2854 |     // Connect the server to the transport
2855 |     const transport = new StdioServerTransport();
2856 |     await server.connect(transport);
2857 |   } catch (error) {
2858 |     console.error("Error starting server:", error);
2859 |     process.exit(1);
2860 |   }
2861 | }
2862 | 
2863 | // Run the main function
2864 | main().catch(error => {
2865 |   console.error("Unhandled error:", error);
2866 |   process.exit(1);
2867 | });
2868 | 
2869 | // Export the KnowledgeGraphManager for testing
2870 | export { KnowledgeGraphManager }; 
```
Page 7/13FirstPrevNextLast