kratos-mcp 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +351 -0
  3. package/dist/host-middleware-v2.d.ts +3 -0
  4. package/dist/host-middleware-v2.d.ts.map +1 -0
  5. package/dist/host-middleware-v2.js +471 -0
  6. package/dist/host-middleware-v2.js.map +1 -0
  7. package/dist/index.d.ts +21 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +939 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/memory-server/concept-store-enhanced.d.ts +88 -0
  12. package/dist/memory-server/concept-store-enhanced.d.ts.map +1 -0
  13. package/dist/memory-server/concept-store-enhanced.js +392 -0
  14. package/dist/memory-server/concept-store-enhanced.js.map +1 -0
  15. package/dist/memory-server/concept-store.d.ts +58 -0
  16. package/dist/memory-server/concept-store.d.ts.map +1 -0
  17. package/dist/memory-server/concept-store.js +329 -0
  18. package/dist/memory-server/concept-store.js.map +1 -0
  19. package/dist/memory-server/context-broker.d.ts +63 -0
  20. package/dist/memory-server/context-broker.d.ts.map +1 -0
  21. package/dist/memory-server/context-broker.js +340 -0
  22. package/dist/memory-server/context-broker.js.map +1 -0
  23. package/dist/memory-server/database.d.ts +61 -0
  24. package/dist/memory-server/database.d.ts.map +1 -0
  25. package/dist/memory-server/database.js +309 -0
  26. package/dist/memory-server/database.js.map +1 -0
  27. package/dist/modules/prd/index.d.ts +47 -0
  28. package/dist/modules/prd/index.d.ts.map +1 -0
  29. package/dist/modules/prd/index.js +220 -0
  30. package/dist/modules/prd/index.js.map +1 -0
  31. package/dist/modules/prompt/index.d.ts +47 -0
  32. package/dist/modules/prompt/index.d.ts.map +1 -0
  33. package/dist/modules/prompt/index.js +313 -0
  34. package/dist/modules/prompt/index.js.map +1 -0
  35. package/dist/project-manager.d.ts +69 -0
  36. package/dist/project-manager.d.ts.map +1 -0
  37. package/dist/project-manager.js +207 -0
  38. package/dist/project-manager.js.map +1 -0
  39. package/dist/security/data-retention.d.ts +104 -0
  40. package/dist/security/data-retention.d.ts.map +1 -0
  41. package/dist/security/data-retention.js +444 -0
  42. package/dist/security/data-retention.js.map +1 -0
  43. package/dist/security/encryption.d.ts +48 -0
  44. package/dist/security/encryption.d.ts.map +1 -0
  45. package/dist/security/encryption.js +131 -0
  46. package/dist/security/encryption.js.map +1 -0
  47. package/dist/security/pii-detector.d.ts +61 -0
  48. package/dist/security/pii-detector.d.ts.map +1 -0
  49. package/dist/security/pii-detector.js +220 -0
  50. package/dist/security/pii-detector.js.map +1 -0
  51. package/dist/tools/ci-hooks.d.ts +48 -0
  52. package/dist/tools/ci-hooks.d.ts.map +1 -0
  53. package/dist/tools/ci-hooks.js +452 -0
  54. package/dist/tools/ci-hooks.js.map +1 -0
  55. package/dist/tools/migrate-to-sqlite.d.ts +32 -0
  56. package/dist/tools/migrate-to-sqlite.d.ts.map +1 -0
  57. package/dist/tools/migrate-to-sqlite.js +341 -0
  58. package/dist/tools/migrate-to-sqlite.js.map +1 -0
  59. package/dist/types/index.d.ts +151 -0
  60. package/dist/types/index.d.ts.map +1 -0
  61. package/dist/types/index.js +2 -0
  62. package/dist/types/index.js.map +1 -0
  63. package/dist/utils/logger.d.ts +9 -0
  64. package/dist/utils/logger.d.ts.map +1 -0
  65. package/dist/utils/logger.js +33 -0
  66. package/dist/utils/logger.js.map +1 -0
  67. package/dist/utils/mcp-logger.d.ts +14 -0
  68. package/dist/utils/mcp-logger.d.ts.map +1 -0
  69. package/dist/utils/mcp-logger.js +40 -0
  70. package/dist/utils/mcp-logger.js.map +1 -0
  71. package/package.json +88 -0
package/dist/index.js ADDED
@@ -0,0 +1,939 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
5
+ import chalk from 'chalk';
6
+ import { MemoryDatabase } from './memory-server/database.js';
7
+ import { ConceptStore } from './memory-server/concept-store.js';
8
+ import { ContextBroker } from './memory-server/context-broker.js';
9
+ // Evidence Guard removed - was flaky and not useful
10
+ import { PRDManager } from './modules/prd/index.js';
11
+ import { PromptManager } from './modules/prompt/index.js';
12
+ import { ProjectManager } from './project-manager.js';
13
+ import { MCPLogger as Logger } from './utils/mcp-logger.js';
14
+ import { EncryptionManager } from './security/encryption.js';
15
+ import { PIIDetector } from './security/pii-detector.js';
16
+ import { DataRetentionManager } from './security/data-retention.js';
17
+ const logger = new Logger('Kratos');
18
+ class KratosProtocolServer {
19
+ server;
20
+ projectManager;
21
+ memoryDb = null;
22
+ conceptStore = null;
23
+ contextBroker = null;
24
+ // Evidence Guard removed - was flaky and not delivering value
25
+ prdManager;
26
+ promptManager;
27
+ // Security components
28
+ encryption = null;
29
+ piiDetector;
30
+ dataRetention = null;
31
+ constructor() {
32
+ this.server = new Server({
33
+ name: 'kratos-protocol',
34
+ version: '4.0.0',
35
+ }, {
36
+ capabilities: {
37
+ tools: {},
38
+ resources: {},
39
+ },
40
+ });
41
+ this.projectManager = new ProjectManager();
42
+ this.prdManager = new PRDManager();
43
+ this.promptManager = new PromptManager();
44
+ this.piiDetector = new PIIDetector();
45
+ this.setupHandlers();
46
+ }
47
+ ensureInitialized() {
48
+ if (!this.memoryDb || !this.conceptStore || !this.contextBroker) {
49
+ throw new Error('Project not initialized. Please wait for initialization to complete.');
50
+ }
51
+ }
52
+ async initializeProject() {
53
+ try {
54
+ // AUTO-DETECT project from current directory
55
+ // No more manual project ID needed!
56
+ const workingDir = process.env.KRATOS_PROJECT_ROOT || process.cwd();
57
+ const project = await this.projectManager.detectProject(workingDir);
58
+ logger.info(`Auto-detected project: ${project.name} at ${project.root}`);
59
+ // Initialize components with ISOLATED project
60
+ this.memoryDb = new MemoryDatabase(project.root, project.id);
61
+ this.conceptStore = ConceptStore.getInstance(project.id);
62
+ this.contextBroker = new ContextBroker(project.root, project.id);
63
+ // Initialize security components
64
+ this.encryption = new EncryptionManager(project.root, project.id);
65
+ this.dataRetention = new DataRetentionManager(project.root, project.id);
66
+ logger.info(chalk.green(`āœ… Project initialized: ${project.name}`));
67
+ logger.info(chalk.blue(`šŸ“ Project root: ${project.root}`));
68
+ logger.info(chalk.yellow(`šŸ” Isolated data: ~/.kratos/projects/${project.id}/`));
69
+ }
70
+ catch (error) {
71
+ logger.error('Failed to initialize project:', error);
72
+ }
73
+ }
74
+ setupHandlers() {
75
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
76
+ tools: [
77
+ // Memory Management (per-project)
78
+ {
79
+ name: 'memory_save',
80
+ description: 'Save a memory document to the active project',
81
+ inputSchema: {
82
+ type: 'object',
83
+ properties: {
84
+ summary: { type: 'string', description: 'Short, 1-2 line summary' },
85
+ text: { type: 'string', description: 'Full memory content' },
86
+ tags: { type: 'array', items: { type: 'string' }, description: 'Tags for categorization' },
87
+ paths: { type: 'array', items: { type: 'string' }, description: 'File/directory paths (globs)' },
88
+ importance: { type: 'integer', minimum: 1, maximum: 5, description: 'Importance level' },
89
+ ttl: { type: 'integer', description: 'Time to live in seconds' },
90
+ },
91
+ required: ['summary', 'text'],
92
+ },
93
+ },
94
+ {
95
+ name: 'memory_search',
96
+ description: 'Search memory documents in the active project',
97
+ inputSchema: {
98
+ type: 'object',
99
+ properties: {
100
+ q: { type: 'string', description: 'Search query' },
101
+ k: { type: 'integer', description: 'Max results to return' },
102
+ require_path_match: { type: 'boolean', description: 'Require path matching' },
103
+ tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags' },
104
+ include_expired: { type: 'boolean', description: 'Include expired memories' },
105
+ },
106
+ required: ['q'],
107
+ },
108
+ },
109
+ {
110
+ name: 'memory_get_recent',
111
+ description: 'Get recent memories from active project',
112
+ inputSchema: {
113
+ type: 'object',
114
+ properties: {
115
+ k: { type: 'integer', description: 'Max results' },
116
+ path_prefix: { type: 'string', description: 'Filter by path prefix' },
117
+ include_expired: { type: 'boolean', description: 'Include expired memories' },
118
+ },
119
+ },
120
+ },
121
+ {
122
+ name: 'memory_forget',
123
+ description: 'Delete a memory by ID',
124
+ inputSchema: {
125
+ type: 'object',
126
+ properties: {
127
+ id: { type: 'string', description: 'Memory ID to delete' },
128
+ },
129
+ required: ['id'],
130
+ },
131
+ },
132
+ {
133
+ name: 'memory_link',
134
+ description: 'Link a global concept to the current project',
135
+ inputSchema: {
136
+ type: 'object',
137
+ properties: {
138
+ concept_id: { type: 'string', description: 'Global concept ID to link' },
139
+ local_tags: { type: 'array', items: { type: 'string' }, description: 'Additional local tags' },
140
+ },
141
+ required: ['concept_id'],
142
+ },
143
+ },
144
+ // Global Concept Store
145
+ {
146
+ name: 'concept_search',
147
+ description: 'Search global concept store',
148
+ inputSchema: {
149
+ type: 'object',
150
+ properties: {
151
+ q: { type: 'string', description: 'Search query' },
152
+ k: { type: 'integer', description: 'Max results' },
153
+ allowlist: { type: 'array', items: { type: 'string' }, description: 'Concept ID allowlist' },
154
+ },
155
+ required: ['q'],
156
+ },
157
+ },
158
+ {
159
+ name: 'concept_get',
160
+ description: 'Get a specific concept by ID',
161
+ inputSchema: {
162
+ type: 'object',
163
+ properties: {
164
+ id: { type: 'string', description: 'Concept ID' },
165
+ },
166
+ required: ['id'],
167
+ },
168
+ },
169
+ {
170
+ name: 'concept_save',
171
+ description: 'Save a concept to global store',
172
+ inputSchema: {
173
+ type: 'object',
174
+ properties: {
175
+ id: { type: 'string', description: 'Optional concept ID' },
176
+ title: { type: 'string', description: 'Concept title' },
177
+ body: { type: 'string', description: 'Concept body (600-900 chars recommended)' },
178
+ tags: { type: 'array', items: { type: 'string' }, description: 'Concept tags' },
179
+ importance: { type: 'integer', minimum: 1, maximum: 5, description: 'Importance level' },
180
+ },
181
+ required: ['title', 'body'],
182
+ },
183
+ },
184
+ {
185
+ name: 'concept_allowlist',
186
+ description: 'Manage project concept allowlist',
187
+ inputSchema: {
188
+ type: 'object',
189
+ properties: {
190
+ add: { type: 'array', items: { type: 'string' }, description: 'Concept IDs to add' },
191
+ remove: { type: 'array', items: { type: 'string' }, description: 'Concept IDs to remove' },
192
+ list: { type: 'boolean', description: 'List current allowlist' },
193
+ },
194
+ },
195
+ },
196
+ // Context Management
197
+ {
198
+ name: 'context_preview',
199
+ description: 'Preview context injection for current task',
200
+ inputSchema: {
201
+ type: 'object',
202
+ properties: {
203
+ open_files: { type: 'array', items: { type: 'string' }, description: 'Currently open files' },
204
+ task: { type: 'string', description: 'Current task description' },
205
+ budget_bytes: { type: 'integer', description: 'Maximum context size in bytes' },
206
+ top_k: { type: 'integer', description: 'Maximum number of injections' },
207
+ mode: { type: 'string', enum: ['hard', 'soft', 'smart'], description: 'Injection mode' },
208
+ },
209
+ required: ['task'],
210
+ },
211
+ },
212
+ {
213
+ name: 'context_rules_get',
214
+ description: 'Get current context injection rules',
215
+ inputSchema: {
216
+ type: 'object',
217
+ properties: {},
218
+ },
219
+ },
220
+ {
221
+ name: 'context_rules_set',
222
+ description: 'Update context injection rules',
223
+ inputSchema: {
224
+ type: 'object',
225
+ properties: {
226
+ maxMemoryAge: { type: 'integer', description: 'Max memory age in milliseconds' },
227
+ minImportance: { type: 'integer', description: 'Minimum importance threshold' },
228
+ pathBoostMultiplier: { type: 'number', description: 'Path matching boost multiplier' },
229
+ conceptImportanceThreshold: { type: 'integer', description: 'Concept importance threshold' },
230
+ dedupeThreshold: { type: 'number', description: 'Deduplication threshold' },
231
+ },
232
+ },
233
+ },
234
+ // PRD Management (Pillar 1)
235
+ {
236
+ name: 'prd_fetch',
237
+ description: 'Fetch PRD content for feature or path',
238
+ inputSchema: {
239
+ type: 'object',
240
+ properties: {
241
+ feature: { type: 'string', description: 'Feature name' },
242
+ path: { type: 'string', description: 'File path' },
243
+ },
244
+ },
245
+ },
246
+ {
247
+ name: 'prd_update',
248
+ description: 'Update PRD content',
249
+ inputSchema: {
250
+ type: 'object',
251
+ properties: {
252
+ feature: { type: 'string', description: 'Feature name' },
253
+ content: { type: 'string', description: 'Updated content' },
254
+ section: { type: 'string', description: 'PRD section to update' },
255
+ },
256
+ required: ['feature', 'content'],
257
+ },
258
+ },
259
+ // Prompt Engineering (Pillar 2)
260
+ {
261
+ name: 'prompt_build',
262
+ description: 'Build a structured prompt using best practices',
263
+ inputSchema: {
264
+ type: 'object',
265
+ properties: {
266
+ role: { type: 'string', description: 'AI role/persona' },
267
+ goal: { type: 'string', description: 'Task goal' },
268
+ scope: { type: 'string', description: 'Task scope' },
269
+ files: { type: 'array', items: { type: 'string' }, description: 'Relevant files' },
270
+ plan: { type: 'string', description: 'Execution plan' },
271
+ verify: { type: 'string', description: 'Verification criteria' },
272
+ memory_refs: { type: 'array', items: { type: 'string' }, description: 'Memory references to include' },
273
+ },
274
+ required: ['goal'],
275
+ },
276
+ },
277
+ // Security Tools
278
+ {
279
+ name: 'security_scan',
280
+ description: 'Scan text for PII and secrets',
281
+ inputSchema: {
282
+ type: 'object',
283
+ properties: {
284
+ text: { type: 'string', description: 'Text to scan' },
285
+ redact: { type: 'boolean', description: 'Return redacted version' },
286
+ },
287
+ required: ['text'],
288
+ },
289
+ },
290
+ {
291
+ name: 'security_encrypt',
292
+ description: 'Encrypt sensitive data for storage',
293
+ inputSchema: {
294
+ type: 'object',
295
+ properties: {
296
+ data: { type: 'object', description: 'Data to encrypt' },
297
+ },
298
+ required: ['data'],
299
+ },
300
+ },
301
+ {
302
+ name: 'security_rbac_check',
303
+ description: 'Check user access permission',
304
+ inputSchema: {
305
+ type: 'object',
306
+ properties: {
307
+ user_id: { type: 'string', description: 'User ID' },
308
+ action: { type: 'string', enum: ['read', 'write', 'search', 'delete', 'admin'], description: 'Action to check' },
309
+ resource: { type: 'string', description: 'Optional resource identifier' },
310
+ },
311
+ required: ['user_id', 'action'],
312
+ },
313
+ },
314
+ {
315
+ name: 'security_rbac_grant',
316
+ description: 'Grant user permission to project',
317
+ inputSchema: {
318
+ type: 'object',
319
+ properties: {
320
+ user_id: { type: 'string', description: 'User ID' },
321
+ permissions: { type: 'array', items: { type: 'string' }, description: 'Permissions to grant' },
322
+ },
323
+ required: ['user_id', 'permissions'],
324
+ },
325
+ },
326
+ {
327
+ name: 'security_retention_apply',
328
+ description: 'Apply retention policy to memory',
329
+ inputSchema: {
330
+ type: 'object',
331
+ properties: {
332
+ memory_id: { type: 'string', description: 'Memory ID' },
333
+ policy: { type: 'string', enum: ['default', 'temporary', 'important', 'permanent'], description: 'Retention policy' },
334
+ },
335
+ required: ['memory_id', 'policy'],
336
+ },
337
+ },
338
+ {
339
+ name: 'security_gdpr_delete',
340
+ description: 'Delete data (GDPR right to erasure)',
341
+ inputSchema: {
342
+ type: 'object',
343
+ properties: {
344
+ target_type: { type: 'string', enum: ['memory', 'project', 'user'], description: 'Type of data to delete' },
345
+ target_id: { type: 'string', description: 'ID of target to delete' },
346
+ user_id: { type: 'string', description: 'User requesting deletion' },
347
+ reason: { type: 'string', description: 'Reason for deletion' },
348
+ },
349
+ required: ['target_type', 'target_id', 'user_id', 'reason'],
350
+ },
351
+ },
352
+ {
353
+ name: 'security_gdpr_export',
354
+ description: 'Export user data (GDPR compliance)',
355
+ inputSchema: {
356
+ type: 'object',
357
+ properties: {
358
+ user_id: { type: 'string', description: 'User ID to export data for' },
359
+ },
360
+ required: ['user_id'],
361
+ },
362
+ },
363
+ // Project Management
364
+ {
365
+ name: 'project_switch',
366
+ description: 'Switch to a different project',
367
+ inputSchema: {
368
+ type: 'object',
369
+ properties: {
370
+ project_path: { type: 'string', description: 'Path to project directory' },
371
+ },
372
+ required: ['project_path'],
373
+ },
374
+ },
375
+ {
376
+ name: 'project_list',
377
+ description: 'List all known projects',
378
+ inputSchema: {
379
+ type: 'object',
380
+ properties: {},
381
+ },
382
+ },
383
+ {
384
+ name: 'project_current',
385
+ description: 'Get current active project',
386
+ inputSchema: {
387
+ type: 'object',
388
+ properties: {},
389
+ },
390
+ },
391
+ // System Management
392
+ {
393
+ name: 'system_status',
394
+ description: 'Get system status and statistics',
395
+ inputSchema: {
396
+ type: 'object',
397
+ properties: {},
398
+ },
399
+ },
400
+ {
401
+ name: 'system_migrate',
402
+ description: 'Migrate from legacy JSON format to SQLite',
403
+ inputSchema: {
404
+ type: 'object',
405
+ properties: {
406
+ dry_run: { type: 'boolean', description: 'Perform dry run without changes' },
407
+ create_backup: { type: 'boolean', description: 'Create backup before migration' },
408
+ extract_concepts: { type: 'boolean', description: 'Extract concepts during migration' },
409
+ },
410
+ },
411
+ },
412
+ ],
413
+ }));
414
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
415
+ const { name, arguments: args } = request.params;
416
+ // Ensure project is initialized for project-specific operations
417
+ if (name.startsWith('memory_') || name.startsWith('context_') || name.startsWith('prd_')) {
418
+ const project = this.projectManager.getCurrentProject();
419
+ if (!project) {
420
+ await this.initializeProject();
421
+ if (!this.projectManager.getCurrentProject()) {
422
+ return {
423
+ content: [{
424
+ type: 'text',
425
+ text: 'Error: No active project. Set KRATOS_ACTIVE_PROJECT environment variable.'
426
+ }]
427
+ };
428
+ }
429
+ }
430
+ }
431
+ try {
432
+ switch (name) {
433
+ // Memory operations
434
+ case 'memory_save':
435
+ const saveResult = this.memoryDb.save(args);
436
+ return {
437
+ content: [{
438
+ type: 'text',
439
+ text: JSON.stringify(saveResult, null, 2)
440
+ }]
441
+ };
442
+ case 'memory_search':
443
+ const searchResults = this.memoryDb.search(args);
444
+ return {
445
+ content: [{
446
+ type: 'text',
447
+ text: JSON.stringify({
448
+ count: searchResults.length,
449
+ results: searchResults.map(r => ({
450
+ id: r.memory.id,
451
+ summary: r.memory.summary,
452
+ text: r.memory.text.substring(0, 200) + '...',
453
+ score: r.score,
454
+ tags: r.memory.tags,
455
+ paths: r.memory.paths,
456
+ importance: r.memory.importance,
457
+ created_at: r.memory.created_at,
458
+ snippet: r.snippet
459
+ }))
460
+ }, null, 2)
461
+ }]
462
+ };
463
+ case 'memory_get_recent':
464
+ const recentResults = this.memoryDb.getRecent(args);
465
+ return {
466
+ content: [{
467
+ type: 'text',
468
+ text: JSON.stringify({
469
+ count: recentResults.length,
470
+ memories: recentResults.map(m => ({
471
+ id: m.id,
472
+ summary: m.summary,
473
+ tags: m.tags,
474
+ paths: m.paths,
475
+ importance: m.importance,
476
+ created_at: m.created_at
477
+ }))
478
+ }, null, 2)
479
+ }]
480
+ };
481
+ case 'memory_forget':
482
+ const forgetResult = this.memoryDb.forget(args?.id);
483
+ return {
484
+ content: [{
485
+ type: 'text',
486
+ text: JSON.stringify(forgetResult, null, 2)
487
+ }]
488
+ };
489
+ case 'memory_link':
490
+ this.ensureInitialized();
491
+ const concept = this.conceptStore.get(args?.concept_id);
492
+ if (!concept) {
493
+ throw new Error(`Concept not found: ${args?.concept_id}`);
494
+ }
495
+ const linkedMemory = this.memoryDb.save({
496
+ summary: `Linked: ${concept.title}`,
497
+ text: concept.body,
498
+ tags: [...concept.tags, ...(args?.local_tags || []), 'linked-concept'],
499
+ importance: concept.importance
500
+ });
501
+ return {
502
+ content: [{
503
+ type: 'text',
504
+ text: JSON.stringify({ ...linkedMemory, concept_id: concept.id }, null, 2)
505
+ }]
506
+ };
507
+ // Concept operations
508
+ case 'concept_search':
509
+ if (!this.conceptStore) {
510
+ throw new McpError(ErrorCode.InternalError, 'Concept store not initialized');
511
+ }
512
+ // Handle empty query or missing query by returning all concepts
513
+ const searchQuery = args?.q || '*';
514
+ const conceptSearchResults = this.conceptStore.search({
515
+ q: searchQuery.trim() || '*', // Default to wildcard if empty
516
+ k: args?.k || 10,
517
+ allowlist: args?.allowlist,
518
+ projectId: undefined // Don't filter by project to show all global concepts
519
+ });
520
+ // If no results and query wasn't wildcard, try wildcard as fallback
521
+ let finalConceptResults = conceptSearchResults;
522
+ if (conceptSearchResults.length === 0 && searchQuery !== '*') {
523
+ finalConceptResults = this.conceptStore.search({
524
+ q: '*',
525
+ k: args?.k || 10,
526
+ allowlist: args?.allowlist
527
+ });
528
+ }
529
+ return {
530
+ content: [{
531
+ type: 'text',
532
+ text: JSON.stringify({
533
+ query: searchQuery,
534
+ count: finalConceptResults.length,
535
+ concepts: finalConceptResults.map(c => ({
536
+ id: c.concept.id,
537
+ title: c.concept.title,
538
+ body: c.concept.body.substring(0, 200) + '...',
539
+ tags: c.concept.tags,
540
+ importance: c.concept.importance,
541
+ score: c.score
542
+ })),
543
+ note: finalConceptResults.length === 0 ?
544
+ 'No concepts found. Concepts need to be created first with concept_save.' :
545
+ searchQuery === '*' ?
546
+ 'Showing all available global concepts' :
547
+ `Found ${finalConceptResults.length} concepts matching "${searchQuery}"`
548
+ }, null, 2)
549
+ }]
550
+ };
551
+ case 'concept_get':
552
+ if (!this.conceptStore) {
553
+ throw new McpError(ErrorCode.InternalError, 'Concept store not initialized');
554
+ }
555
+ const conceptResult = this.conceptStore.get(args?.id);
556
+ return {
557
+ content: [{
558
+ type: 'text',
559
+ text: JSON.stringify(conceptResult, null, 2)
560
+ }]
561
+ };
562
+ case 'concept_save':
563
+ if (!this.conceptStore) {
564
+ throw new McpError(ErrorCode.InternalError, 'Concept store not initialized');
565
+ }
566
+ const conceptSaveResult = this.conceptStore.save(args);
567
+ return {
568
+ content: [{
569
+ type: 'text',
570
+ text: JSON.stringify(conceptSaveResult, null, 2)
571
+ }]
572
+ };
573
+ case 'concept_allowlist':
574
+ if (!this.conceptStore) {
575
+ throw new Error('Concept store not initialized');
576
+ }
577
+ const currentProj = this.projectManager.getCurrentProject();
578
+ const allowlistResult = this.conceptStore.updateAllowlist({
579
+ projectId: currentProj?.id || 'global',
580
+ ...args
581
+ });
582
+ return {
583
+ content: [{
584
+ type: 'text',
585
+ text: JSON.stringify(allowlistResult, null, 2)
586
+ }]
587
+ };
588
+ // Evidence Guard operations
589
+ // Context operations
590
+ case 'context_preview':
591
+ const preview = await this.contextBroker.preview(args);
592
+ return {
593
+ content: [{
594
+ type: 'text',
595
+ text: JSON.stringify(preview, null, 2)
596
+ }]
597
+ };
598
+ case 'context_rules_get':
599
+ const rules = this.contextBroker.getRules();
600
+ return {
601
+ content: [{
602
+ type: 'text',
603
+ text: JSON.stringify(rules, null, 2)
604
+ }]
605
+ };
606
+ case 'context_rules_set':
607
+ this.contextBroker.setRules(args);
608
+ return {
609
+ content: [{
610
+ type: 'text',
611
+ text: 'Context rules updated successfully'
612
+ }]
613
+ };
614
+ // PRD operations
615
+ case 'prd_fetch':
616
+ const prdResult = await this.prdManager.getPRD(args?.feature || args?.path);
617
+ return {
618
+ content: [{
619
+ type: 'text',
620
+ text: JSON.stringify(prdResult, null, 2)
621
+ }]
622
+ };
623
+ case 'prd_update':
624
+ const updateResult = await this.prdManager.updatePRD(args?.section, args?.content);
625
+ return {
626
+ content: [{
627
+ type: 'text',
628
+ text: JSON.stringify(updateResult, null, 2)
629
+ }]
630
+ };
631
+ // Prompt operations
632
+ case 'prompt_build':
633
+ const promptArgs = {
634
+ task: args?.goal,
635
+ role: args?.role,
636
+ stack: args?.files,
637
+ fileContext: args?.files,
638
+ templateId: args?.templateId
639
+ };
640
+ const promptResult = await this.promptManager.generatePrompt(promptArgs);
641
+ return {
642
+ content: [{
643
+ type: 'text',
644
+ text: JSON.stringify(promptResult, null, 2)
645
+ }]
646
+ };
647
+ // Security operations
648
+ case 'security_scan':
649
+ const scanResult = this.piiDetector.detect(args?.text);
650
+ if (args?.redact) {
651
+ return {
652
+ content: [{
653
+ type: 'text',
654
+ text: JSON.stringify({
655
+ hasPII: scanResult.hasPII,
656
+ hasSecrets: scanResult.hasSecrets,
657
+ redactedText: scanResult.redactedText,
658
+ findings: scanResult.findings
659
+ }, null, 2)
660
+ }]
661
+ };
662
+ }
663
+ const scanReport = this.piiDetector.scan(args?.text);
664
+ return {
665
+ content: [{
666
+ type: 'text',
667
+ text: JSON.stringify(scanReport, null, 2)
668
+ }]
669
+ };
670
+ case 'security_encrypt':
671
+ if (!this.encryption) {
672
+ throw new Error('Encryption not initialized. Initialize project first.');
673
+ }
674
+ const encrypted = this.encryption.encryptJSON(args?.data);
675
+ return {
676
+ content: [{
677
+ type: 'text',
678
+ text: JSON.stringify({ encrypted, projectId: this.projectManager.getCurrentProject()?.id }, null, 2)
679
+ }]
680
+ };
681
+ case 'security_retention_apply':
682
+ if (!this.dataRetention) {
683
+ throw new Error('Data retention not initialized. Initialize project first.');
684
+ }
685
+ this.dataRetention.applyPolicy(args?.memory_id, args?.policy);
686
+ return {
687
+ content: [{
688
+ type: 'text',
689
+ text: `Applied ${args?.policy} retention policy to memory ${args?.memory_id}`
690
+ }]
691
+ };
692
+ case 'security_gdpr_delete':
693
+ if (!this.dataRetention) {
694
+ throw new Error('Data retention not initialized. Initialize project first.');
695
+ }
696
+ await this.dataRetention.deleteData({
697
+ targetType: args?.target_type,
698
+ targetId: args?.target_id,
699
+ userId: args?.user_id,
700
+ reason: args?.reason
701
+ });
702
+ return {
703
+ content: [{
704
+ type: 'text',
705
+ text: `Deleted ${args?.target_type} ${args?.target_id} per GDPR request`
706
+ }]
707
+ };
708
+ case 'security_gdpr_export':
709
+ if (!this.dataRetention) {
710
+ throw new Error('Data retention not initialized. Initialize project first.');
711
+ }
712
+ const exportData = await this.dataRetention.exportUserData(args?.user_id);
713
+ return {
714
+ content: [{
715
+ type: 'text',
716
+ text: exportData
717
+ }]
718
+ };
719
+ // Project Management
720
+ case 'project_switch':
721
+ const newProject = await this.projectManager.switchProject(args?.project_path);
722
+ // Re-initialize components for new project
723
+ this.memoryDb = new MemoryDatabase(newProject.root, newProject.id);
724
+ this.conceptStore = ConceptStore.getInstance(newProject.id);
725
+ this.contextBroker = new ContextBroker(newProject.root, newProject.id);
726
+ return {
727
+ content: [{
728
+ type: 'text',
729
+ text: `āœ… Switched to project: ${newProject.name}\nRoot: ${newProject.root}\nIsolated data: ~/.kratos/projects/${newProject.id}/`
730
+ }]
731
+ };
732
+ case 'project_list':
733
+ const projects = this.projectManager.listProjects();
734
+ return {
735
+ content: [{
736
+ type: 'text',
737
+ text: JSON.stringify(projects.map(p => ({
738
+ name: p.name,
739
+ root: p.root,
740
+ lastAccessed: p.lastAccessed,
741
+ id: p.id
742
+ })), null, 2)
743
+ }]
744
+ };
745
+ case 'project_current':
746
+ const current = this.projectManager.getCurrentProject();
747
+ return {
748
+ content: [{
749
+ type: 'text',
750
+ text: current ?
751
+ `Current project: ${current.name}\nRoot: ${current.root}\nID: ${current.id}` :
752
+ 'No active project'
753
+ }]
754
+ };
755
+ // System operations
756
+ case 'system_status':
757
+ const status = await this.getSystemStatus();
758
+ return {
759
+ content: [{
760
+ type: 'text',
761
+ text: JSON.stringify(status, null, 2)
762
+ }]
763
+ };
764
+ case 'system_migrate':
765
+ // Import migration tool
766
+ const { KratosMigrationTool } = await import('./tools/migrate-to-sqlite.js');
767
+ const migrationTool = new KratosMigrationTool();
768
+ const migrationResult = await migrationTool.migrateProject(this.projectManager.getCurrentProject()?.root || process.cwd(), args);
769
+ return {
770
+ content: [{
771
+ type: 'text',
772
+ text: JSON.stringify(migrationResult, null, 2)
773
+ }]
774
+ };
775
+ default:
776
+ throw new Error(`Unknown tool: ${name}`);
777
+ }
778
+ }
779
+ catch (error) {
780
+ logger.error(`Tool error (${name}):`, error);
781
+ return {
782
+ content: [{
783
+ type: 'text',
784
+ text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`
785
+ }]
786
+ };
787
+ }
788
+ });
789
+ // Resource handlers
790
+ this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
791
+ const resources = [
792
+ {
793
+ uri: 'kratos://system/status',
794
+ name: 'System Status',
795
+ description: 'Current system status and statistics',
796
+ mimeType: 'application/json',
797
+ },
798
+ {
799
+ uri: 'kratos://concepts/all',
800
+ name: 'All Concepts',
801
+ description: 'Global concept store contents',
802
+ mimeType: 'application/json',
803
+ }
804
+ ];
805
+ const currentProject = this.projectManager.getCurrentProject();
806
+ if (currentProject) {
807
+ resources.push({
808
+ uri: `kratos://project/${currentProject.id}/memories`,
809
+ name: 'Project Memories',
810
+ description: `All memories for project ${currentProject.name}`,
811
+ mimeType: 'application/json',
812
+ }, {
813
+ uri: `kratos://project/${currentProject.id}/prd`,
814
+ name: 'Project PRD',
815
+ description: `PRD for project ${currentProject.name}`,
816
+ mimeType: 'application/json',
817
+ });
818
+ }
819
+ return { resources };
820
+ });
821
+ this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
822
+ const { uri } = request.params;
823
+ try {
824
+ if (uri === 'kratos://system/status') {
825
+ const status = await this.getSystemStatus();
826
+ return {
827
+ contents: [{
828
+ uri,
829
+ mimeType: 'application/json',
830
+ text: JSON.stringify(status, null, 2)
831
+ }]
832
+ };
833
+ }
834
+ if (uri === 'kratos://concepts/all') {
835
+ if (!this.conceptStore) {
836
+ throw new McpError(ErrorCode.InternalError, 'Concept store not initialized');
837
+ }
838
+ const concepts = this.conceptStore.search({ q: '*', k: 100 });
839
+ return {
840
+ contents: [{
841
+ uri,
842
+ mimeType: 'application/json',
843
+ text: JSON.stringify(concepts, null, 2)
844
+ }]
845
+ };
846
+ }
847
+ if (uri.includes('/memories')) {
848
+ const memories = this.memoryDb.search({ q: '*', k: 100 });
849
+ return {
850
+ contents: [{
851
+ uri,
852
+ mimeType: 'application/json',
853
+ text: JSON.stringify(memories, null, 2)
854
+ }]
855
+ };
856
+ }
857
+ throw new Error(`Unknown resource: ${uri}`);
858
+ }
859
+ catch (error) {
860
+ logger.error(`Resource error: ${error}`);
861
+ throw error;
862
+ }
863
+ });
864
+ }
865
+ async getSystemStatus() {
866
+ const status = {
867
+ version: '4.0.0',
868
+ timestamp: new Date().toISOString(),
869
+ activeProject: this.projectManager.getCurrentProject(),
870
+ components: {
871
+ memoryDb: !!this.memoryDb,
872
+ conceptStore: !!this.conceptStore,
873
+ contextBroker: !!this.contextBroker,
874
+ prdManager: !!this.prdManager,
875
+ promptManager: !!this.promptManager,
876
+ encryption: !!this.encryption,
877
+ dataRetention: !!this.dataRetention,
878
+ },
879
+ security: {
880
+ encryption: this.encryption ? 'AES-256-GCM' : 'disabled',
881
+ piiDetection: 'enabled',
882
+ dataRetention: this.dataRetention ? 'enabled' : 'disabled',
883
+ },
884
+ environment: {
885
+ nodeVersion: process.version,
886
+ platform: process.platform,
887
+ cwd: process.cwd(),
888
+ kratosProjectRoot: process.env.KRATOS_PROJECT_ROOT,
889
+ kratosActiveProject: process.env.KRATOS_ACTIVE_PROJECT,
890
+ }
891
+ };
892
+ if (this.memoryDb) {
893
+ const recentMemories = this.memoryDb.getRecent({ k: 5 });
894
+ status.stats = {
895
+ recentMemoryCount: recentMemories.length,
896
+ lastMemoryCreated: recentMemories[0]?.created_at || null,
897
+ };
898
+ }
899
+ if (this.dataRetention) {
900
+ status.retentionStats = this.dataRetention.getStats();
901
+ }
902
+ return status;
903
+ }
904
+ async run() {
905
+ const transport = new StdioServerTransport();
906
+ await this.server.connect(transport);
907
+ logger.info(chalk.green('šŸ”± Kratos Protocol - Production Edition'));
908
+ logger.info(chalk.blue('✨ SQLite + FTS5 | Concepts | Budget Control | Leak Protection'));
909
+ // Initialize project if available
910
+ await this.initializeProject();
911
+ const activeProject = this.projectManager.getCurrentProject();
912
+ if (activeProject) {
913
+ logger.info(chalk.yellow(`šŸ“ Active: ${activeProject.name} (${activeProject.id})`));
914
+ }
915
+ else {
916
+ logger.info(chalk.gray('šŸ“ No active project - set KRATOS_ACTIVE_PROJECT to enable all features'));
917
+ }
918
+ // Graceful shutdown
919
+ process.on('SIGINT', () => {
920
+ logger.info(chalk.yellow('\nšŸ”„ Shutting down...'));
921
+ this.memoryDb?.close();
922
+ this.contextBroker?.close();
923
+ this.conceptStore?.close();
924
+ this.dataRetention?.close();
925
+ process.exit(0);
926
+ });
927
+ }
928
+ }
929
+ // Export for testing
930
+ export { KratosProtocolServer };
931
+ // Run server if this is the main module
932
+ if (import.meta.url === `file://${process.argv[1]}`) {
933
+ const server = new KratosProtocolServer();
934
+ server.run().catch((error) => {
935
+ logger.error('Failed to start server:', error);
936
+ process.exit(1);
937
+ });
938
+ }
939
+ //# sourceMappingURL=index.js.map