#
tokens: 35690/50000 1/128 files (page 12/13)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 12 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

--------------------------------------------------------------------------------
/quantitativeresearch/index.js:
--------------------------------------------------------------------------------

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