@contextstream/mcp-server 0.2.5 → 0.2.7

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.
package/src/tools.ts DELETED
@@ -1,1040 +0,0 @@
1
- import { z } from 'zod';
2
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- import type { ContextStreamClient } from './client.js';
4
- import { readFilesFromDirectory, readAllFilesInBatches } from './files.js';
5
-
6
- type StructuredContent = { [x: string]: unknown } | undefined;
7
-
8
- function formatContent(data: unknown) {
9
- return JSON.stringify(data, null, 2);
10
- }
11
-
12
- function toStructured(data: unknown): StructuredContent {
13
- if (data && typeof data === 'object' && !Array.isArray(data)) {
14
- return data as { [x: string]: unknown };
15
- }
16
- return undefined;
17
- }
18
-
19
- export function registerTools(server: McpServer, client: ContextStreamClient) {
20
- // Auth
21
- server.registerTool(
22
- 'auth_me',
23
- {
24
- title: 'Get current user',
25
- description: 'Fetch authenticated user profile',
26
- inputSchema: z.object({}),
27
- },
28
- async () => {
29
- const result = await client.me();
30
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
31
- }
32
- );
33
-
34
- // Workspaces
35
- server.registerTool(
36
- 'workspaces_list',
37
- {
38
- title: 'List workspaces',
39
- description: 'List accessible workspaces',
40
- inputSchema: z.object({ page: z.number().optional(), page_size: z.number().optional() }),
41
- },
42
- async (input) => {
43
- const result = await client.listWorkspaces(input);
44
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
45
- }
46
- );
47
-
48
- server.registerTool(
49
- 'workspaces_create',
50
- {
51
- title: 'Create workspace',
52
- description: 'Create a new workspace',
53
- inputSchema: z.object({
54
- name: z.string(),
55
- description: z.string().optional(),
56
- visibility: z.enum(['private', 'team', 'org']).optional(),
57
- }),
58
- },
59
- async (input) => {
60
- const result = await client.createWorkspace(input);
61
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
62
- }
63
- );
64
-
65
- // Projects
66
- server.registerTool(
67
- 'projects_list',
68
- {
69
- title: 'List projects',
70
- description: 'List projects (optionally by workspace)',
71
- inputSchema: z.object({ workspace_id: z.string().uuid().optional(), page: z.number().optional(), page_size: z.number().optional() }),
72
- },
73
- async (input) => {
74
- const result = await client.listProjects(input);
75
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
76
- }
77
- );
78
-
79
- server.registerTool(
80
- 'projects_create',
81
- {
82
- title: 'Create project',
83
- description: 'Create a project within a workspace',
84
- inputSchema: z.object({
85
- name: z.string(),
86
- description: z.string().optional(),
87
- workspace_id: z.string().uuid().optional(),
88
- }),
89
- },
90
- async (input) => {
91
- const result = await client.createProject(input);
92
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
93
- }
94
- );
95
-
96
- server.registerTool(
97
- 'projects_index',
98
- {
99
- title: 'Index project',
100
- description: 'Trigger indexing for a project',
101
- inputSchema: z.object({ project_id: z.string().uuid() }),
102
- },
103
- async (input) => {
104
- const result = await client.indexProject(input.project_id);
105
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
106
- }
107
- );
108
-
109
- // Search
110
- const searchSchema = z.object({
111
- query: z.string(),
112
- workspace_id: z.string().uuid().optional(),
113
- project_id: z.string().uuid().optional(),
114
- limit: z.number().optional(),
115
- });
116
-
117
- server.registerTool(
118
- 'search_semantic',
119
- { title: 'Semantic search', description: 'Semantic vector search', inputSchema: searchSchema },
120
- async (input) => {
121
- const result = await client.searchSemantic(input);
122
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
123
- }
124
- );
125
-
126
- server.registerTool(
127
- 'search_hybrid',
128
- { title: 'Hybrid search', description: 'Hybrid search (semantic + keyword)', inputSchema: searchSchema },
129
- async (input) => {
130
- const result = await client.searchHybrid(input);
131
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
132
- }
133
- );
134
-
135
- server.registerTool(
136
- 'search_keyword',
137
- { title: 'Keyword search', description: 'Keyword search', inputSchema: searchSchema },
138
- async (input) => {
139
- const result = await client.searchKeyword(input);
140
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
141
- }
142
- );
143
-
144
- server.registerTool(
145
- 'search_pattern',
146
- { title: 'Pattern search', description: 'Pattern/regex search', inputSchema: searchSchema },
147
- async (input) => {
148
- const result = await client.searchPattern(input);
149
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
150
- }
151
- );
152
-
153
- // Memory / Knowledge
154
- server.registerTool(
155
- 'memory_create_event',
156
- {
157
- title: 'Create memory event',
158
- description: 'Create a memory event for a workspace/project',
159
- inputSchema: z.object({
160
- workspace_id: z.string().uuid().optional(),
161
- project_id: z.string().uuid().optional(),
162
- event_type: z.string(),
163
- title: z.string(),
164
- content: z.string(),
165
- metadata: z.record(z.any()).optional(),
166
- }),
167
- },
168
- async (input) => {
169
- const result = await client.createMemoryEvent(input);
170
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
171
- }
172
- );
173
-
174
- server.registerTool(
175
- 'memory_bulk_ingest',
176
- {
177
- title: 'Bulk ingest events',
178
- description: 'Bulk ingest multiple memory events',
179
- inputSchema: z.object({
180
- workspace_id: z.string().uuid().optional(),
181
- project_id: z.string().uuid().optional(),
182
- events: z.array(z.record(z.any())),
183
- }),
184
- },
185
- async (input) => {
186
- const result = await client.bulkIngestEvents(input);
187
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
188
- }
189
- );
190
-
191
- server.registerTool(
192
- 'memory_list_events',
193
- {
194
- title: 'List memory events',
195
- description: 'List memory events (optionally scoped)',
196
- inputSchema: z.object({
197
- workspace_id: z.string().uuid().optional(),
198
- project_id: z.string().uuid().optional(),
199
- limit: z.number().optional(),
200
- }),
201
- },
202
- async (input) => {
203
- const result = await client.listMemoryEvents(input);
204
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
205
- }
206
- );
207
-
208
- server.registerTool(
209
- 'memory_create_node',
210
- {
211
- title: 'Create knowledge node',
212
- description: 'Create a knowledge node with optional relations',
213
- inputSchema: z.object({
214
- workspace_id: z.string().uuid().optional(),
215
- project_id: z.string().uuid().optional(),
216
- node_type: z.string(),
217
- title: z.string(),
218
- content: z.string(),
219
- relations: z
220
- .array(
221
- z.object({
222
- type: z.string(),
223
- target_id: z.string().uuid(),
224
- })
225
- )
226
- .optional(),
227
- }),
228
- },
229
- async (input) => {
230
- const result = await client.createKnowledgeNode(input);
231
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
232
- }
233
- );
234
-
235
- server.registerTool(
236
- 'memory_list_nodes',
237
- {
238
- title: 'List knowledge nodes',
239
- description: 'List knowledge graph nodes',
240
- inputSchema: z.object({
241
- workspace_id: z.string().uuid().optional(),
242
- project_id: z.string().uuid().optional(),
243
- limit: z.number().optional(),
244
- }),
245
- },
246
- async (input) => {
247
- const result = await client.listKnowledgeNodes(input);
248
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
249
- }
250
- );
251
-
252
- server.registerTool(
253
- 'memory_search',
254
- {
255
- title: 'Memory-aware search',
256
- description: 'Search memory events/notes',
257
- inputSchema: z.object({
258
- query: z.string(),
259
- workspace_id: z.string().uuid().optional(),
260
- project_id: z.string().uuid().optional(),
261
- limit: z.number().optional(),
262
- }),
263
- },
264
- async (input) => {
265
- const result = await client.memorySearch(input);
266
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
267
- }
268
- );
269
-
270
- server.registerTool(
271
- 'memory_decisions',
272
- {
273
- title: 'Decision summaries',
274
- description: 'List decision summaries from workspace memory',
275
- inputSchema: z.object({
276
- workspace_id: z.string().uuid().optional(),
277
- project_id: z.string().uuid().optional(),
278
- limit: z.number().optional(),
279
- }),
280
- },
281
- async (input) => {
282
- const result = await client.memoryDecisions(input);
283
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
284
- }
285
- );
286
-
287
- // Graph
288
- server.registerTool(
289
- 'graph_related',
290
- {
291
- title: 'Related knowledge nodes',
292
- description: 'Find related nodes in the knowledge graph',
293
- inputSchema: z.object({
294
- node_id: z.string(),
295
- workspace_id: z.string().uuid().optional(),
296
- project_id: z.string().uuid().optional(),
297
- limit: z.number().optional(),
298
- }),
299
- },
300
- async (input) => {
301
- const result = await client.graphRelated(input);
302
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
303
- }
304
- );
305
-
306
- server.registerTool(
307
- 'graph_path',
308
- {
309
- title: 'Knowledge path',
310
- description: 'Find path between two nodes',
311
- inputSchema: z.object({
312
- source_id: z.string(),
313
- target_id: z.string(),
314
- workspace_id: z.string().uuid().optional(),
315
- project_id: z.string().uuid().optional(),
316
- }),
317
- },
318
- async (input) => {
319
- const result = await client.graphPath(input);
320
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
321
- }
322
- );
323
-
324
- server.registerTool(
325
- 'graph_decisions',
326
- {
327
- title: 'Decision graph',
328
- description: 'Decision history in the knowledge graph',
329
- inputSchema: z.object({
330
- workspace_id: z.string().uuid().optional(),
331
- project_id: z.string().uuid().optional(),
332
- limit: z.number().optional(),
333
- }),
334
- },
335
- async (input) => {
336
- const result = await client.graphDecisions(input);
337
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
338
- }
339
- );
340
-
341
- server.registerTool(
342
- 'graph_dependencies',
343
- {
344
- title: 'Code dependencies',
345
- description: 'Dependency graph query',
346
- inputSchema: z.object({
347
- target: z.object({ type: z.string(), id: z.string() }),
348
- max_depth: z.number().optional(),
349
- include_transitive: z.boolean().optional(),
350
- }),
351
- },
352
- async (input) => {
353
- const result = await client.graphDependencies(input);
354
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
355
- }
356
- );
357
-
358
- server.registerTool(
359
- 'graph_call_path',
360
- {
361
- title: 'Call path',
362
- description: 'Find call path between two targets',
363
- inputSchema: z.object({
364
- source: z.object({ type: z.string(), id: z.string() }),
365
- target: z.object({ type: z.string(), id: z.string() }),
366
- max_depth: z.number().optional(),
367
- }),
368
- },
369
- async (input) => {
370
- const result = await client.graphCallPath(input);
371
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
372
- }
373
- );
374
-
375
- server.registerTool(
376
- 'graph_impact',
377
- {
378
- title: 'Impact analysis',
379
- description: 'Analyze impact of a target node',
380
- inputSchema: z.object({ target: z.object({ type: z.string(), id: z.string() }), max_depth: z.number().optional() }),
381
- },
382
- async (input) => {
383
- const result = await client.graphImpact(input);
384
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
385
- }
386
- );
387
-
388
- // AI
389
- server.registerTool(
390
- 'ai_context',
391
- {
392
- title: 'Build AI context',
393
- description: 'Build LLM context (docs/memory/code) for a query',
394
- inputSchema: z.object({
395
- query: z.string(),
396
- workspace_id: z.string().uuid().optional(),
397
- project_id: z.string().uuid().optional(),
398
- include_code: z.boolean().optional(),
399
- include_docs: z.boolean().optional(),
400
- include_memory: z.boolean().optional(),
401
- limit: z.number().optional(),
402
- }),
403
- },
404
- async (input) => {
405
- const result = await client.aiContext(input);
406
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
407
- }
408
- );
409
-
410
- server.registerTool(
411
- 'ai_embeddings',
412
- {
413
- title: 'Generate embeddings',
414
- description: 'Generate embeddings for a text',
415
- inputSchema: z.object({ text: z.string() }),
416
- },
417
- async (input) => {
418
- const result = await client.aiEmbeddings(input);
419
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
420
- }
421
- );
422
-
423
- server.registerTool(
424
- 'ai_plan',
425
- {
426
- title: 'Generate dev plan',
427
- description: 'Generate development plan from description',
428
- inputSchema: z.object({
429
- description: z.string(),
430
- project_id: z.string().uuid().optional(),
431
- complexity: z.string().optional(),
432
- }),
433
- },
434
- async (input) => {
435
- const result = await client.aiPlan(input);
436
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
437
- }
438
- );
439
-
440
- server.registerTool(
441
- 'ai_tasks',
442
- {
443
- title: 'Generate tasks',
444
- description: 'Generate tasks from plan or description',
445
- inputSchema: z.object({
446
- plan_id: z.string().optional(),
447
- description: z.string().optional(),
448
- project_id: z.string().uuid().optional(),
449
- granularity: z.string().optional(),
450
- }),
451
- },
452
- async (input) => {
453
- const result = await client.aiTasks(input);
454
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
455
- }
456
- );
457
-
458
- server.registerTool(
459
- 'ai_enhanced_context',
460
- {
461
- title: 'Enhanced AI context',
462
- description: 'Build enhanced LLM context with deeper analysis',
463
- inputSchema: z.object({
464
- query: z.string(),
465
- workspace_id: z.string().uuid().optional(),
466
- project_id: z.string().uuid().optional(),
467
- include_code: z.boolean().optional(),
468
- include_docs: z.boolean().optional(),
469
- include_memory: z.boolean().optional(),
470
- limit: z.number().optional(),
471
- }),
472
- },
473
- async (input) => {
474
- const result = await client.aiEnhancedContext(input);
475
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
476
- }
477
- );
478
-
479
- // Extended project operations
480
- server.registerTool(
481
- 'projects_get',
482
- {
483
- title: 'Get project',
484
- description: 'Get project details by ID',
485
- inputSchema: z.object({ project_id: z.string().uuid() }),
486
- },
487
- async (input) => {
488
- const result = await client.getProject(input.project_id);
489
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
490
- }
491
- );
492
-
493
- server.registerTool(
494
- 'projects_overview',
495
- {
496
- title: 'Project overview',
497
- description: 'Get project overview with summary information',
498
- inputSchema: z.object({ project_id: z.string().uuid() }),
499
- },
500
- async (input) => {
501
- const result = await client.projectOverview(input.project_id);
502
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
503
- }
504
- );
505
-
506
- server.registerTool(
507
- 'projects_statistics',
508
- {
509
- title: 'Project statistics',
510
- description: 'Get project statistics (files, lines, complexity)',
511
- inputSchema: z.object({ project_id: z.string().uuid() }),
512
- },
513
- async (input) => {
514
- const result = await client.projectStatistics(input.project_id);
515
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
516
- }
517
- );
518
-
519
- server.registerTool(
520
- 'projects_files',
521
- {
522
- title: 'List project files',
523
- description: 'List all indexed files in a project',
524
- inputSchema: z.object({ project_id: z.string().uuid() }),
525
- },
526
- async (input) => {
527
- const result = await client.projectFiles(input.project_id);
528
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
529
- }
530
- );
531
-
532
- server.registerTool(
533
- 'projects_index_status',
534
- {
535
- title: 'Index status',
536
- description: 'Get project indexing status',
537
- inputSchema: z.object({ project_id: z.string().uuid() }),
538
- },
539
- async (input) => {
540
- const result = await client.projectIndexStatus(input.project_id);
541
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
542
- }
543
- );
544
-
545
- server.registerTool(
546
- 'projects_ingest_local',
547
- {
548
- title: 'Ingest local files',
549
- description: `Read ALL files from a local directory and ingest them for indexing.
550
- This indexes your entire project by reading files in batches.
551
- Automatically detects code files and skips ignored directories like node_modules, target, dist, etc.`,
552
- inputSchema: z.object({
553
- project_id: z.string().uuid().describe('Project to ingest files into'),
554
- path: z.string().describe('Local directory path to read files from'),
555
- }),
556
- },
557
- async (input) => {
558
- // Start ingestion in background to avoid blocking the agent
559
- (async () => {
560
- try {
561
- let totalIndexed = 0;
562
- let batchCount = 0;
563
-
564
- console.error(`[ContextStream] Starting background ingestion for project ${input.project_id} from ${input.path}`);
565
-
566
- for await (const batch of readAllFilesInBatches(input.path, { batchSize: 50 })) {
567
- const result = await client.ingestFiles(input.project_id, batch) as { data?: { files_indexed: number } };
568
- totalIndexed += result.data?.files_indexed ?? batch.length;
569
- batchCount++;
570
- }
571
-
572
- console.error(`[ContextStream] Completed background ingestion: ${totalIndexed} files in ${batchCount} batches`);
573
- } catch (error) {
574
- console.error(`[ContextStream] Ingestion failed:`, error);
575
- }
576
- })();
577
-
578
- const summary = {
579
- status: 'started',
580
- message: 'Ingestion running in background',
581
- project_id: input.project_id,
582
- path: input.path,
583
- note: "Use 'projects_index_status' to monitor progress."
584
- };
585
-
586
- return {
587
- content: [{
588
- type: 'text' as const,
589
- text: `Ingestion started in background for directory: ${input.path}. Use 'projects_index_status' to monitor progress.`
590
- }],
591
- structuredContent: toStructured(summary)
592
- };
593
- }
594
- );
595
-
596
- // Extended workspace operations
597
- server.registerTool(
598
- 'workspaces_get',
599
- {
600
- title: 'Get workspace',
601
- description: 'Get workspace details by ID',
602
- inputSchema: z.object({ workspace_id: z.string().uuid() }),
603
- },
604
- async (input) => {
605
- const result = await client.getWorkspace(input.workspace_id);
606
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
607
- }
608
- );
609
-
610
- server.registerTool(
611
- 'workspaces_overview',
612
- {
613
- title: 'Workspace overview',
614
- description: 'Get workspace overview with summary information',
615
- inputSchema: z.object({ workspace_id: z.string().uuid() }),
616
- },
617
- async (input) => {
618
- const result = await client.workspaceOverview(input.workspace_id);
619
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
620
- }
621
- );
622
-
623
- server.registerTool(
624
- 'workspaces_analytics',
625
- {
626
- title: 'Workspace analytics',
627
- description: 'Get workspace usage analytics',
628
- inputSchema: z.object({ workspace_id: z.string().uuid() }),
629
- },
630
- async (input) => {
631
- const result = await client.workspaceAnalytics(input.workspace_id);
632
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
633
- }
634
- );
635
-
636
- server.registerTool(
637
- 'workspaces_content',
638
- {
639
- title: 'Workspace content',
640
- description: 'List content in a workspace',
641
- inputSchema: z.object({ workspace_id: z.string().uuid() }),
642
- },
643
- async (input) => {
644
- const result = await client.workspaceContent(input.workspace_id);
645
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
646
- }
647
- );
648
-
649
- // Extended memory operations
650
- server.registerTool(
651
- 'memory_get_event',
652
- {
653
- title: 'Get memory event',
654
- description: 'Get a specific memory event by ID',
655
- inputSchema: z.object({ event_id: z.string().uuid() }),
656
- },
657
- async (input) => {
658
- const result = await client.getMemoryEvent(input.event_id);
659
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
660
- }
661
- );
662
-
663
- server.registerTool(
664
- 'memory_update_event',
665
- {
666
- title: 'Update memory event',
667
- description: 'Update a memory event',
668
- inputSchema: z.object({
669
- event_id: z.string().uuid(),
670
- title: z.string().optional(),
671
- content: z.string().optional(),
672
- metadata: z.record(z.any()).optional(),
673
- }),
674
- },
675
- async (input) => {
676
- const { event_id, ...body } = input;
677
- const result = await client.updateMemoryEvent(event_id, body);
678
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
679
- }
680
- );
681
-
682
- server.registerTool(
683
- 'memory_delete_event',
684
- {
685
- title: 'Delete memory event',
686
- description: 'Delete a memory event',
687
- inputSchema: z.object({ event_id: z.string().uuid() }),
688
- },
689
- async (input) => {
690
- const result = await client.deleteMemoryEvent(input.event_id);
691
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
692
- }
693
- );
694
-
695
- server.registerTool(
696
- 'memory_distill_event',
697
- {
698
- title: 'Distill memory event',
699
- description: 'Extract and condense key insights from a memory event',
700
- inputSchema: z.object({ event_id: z.string().uuid() }),
701
- },
702
- async (input) => {
703
- const result = await client.distillMemoryEvent(input.event_id);
704
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
705
- }
706
- );
707
-
708
- server.registerTool(
709
- 'memory_get_node',
710
- {
711
- title: 'Get knowledge node',
712
- description: 'Get a specific knowledge node by ID',
713
- inputSchema: z.object({ node_id: z.string().uuid() }),
714
- },
715
- async (input) => {
716
- const result = await client.getKnowledgeNode(input.node_id);
717
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
718
- }
719
- );
720
-
721
- server.registerTool(
722
- 'memory_update_node',
723
- {
724
- title: 'Update knowledge node',
725
- description: 'Update a knowledge node',
726
- inputSchema: z.object({
727
- node_id: z.string().uuid(),
728
- title: z.string().optional(),
729
- content: z.string().optional(),
730
- relations: z.array(z.object({ type: z.string(), target_id: z.string().uuid() })).optional(),
731
- }),
732
- },
733
- async (input) => {
734
- const { node_id, ...body } = input;
735
- const result = await client.updateKnowledgeNode(node_id, body);
736
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
737
- }
738
- );
739
-
740
- server.registerTool(
741
- 'memory_delete_node',
742
- {
743
- title: 'Delete knowledge node',
744
- description: 'Delete a knowledge node',
745
- inputSchema: z.object({ node_id: z.string().uuid() }),
746
- },
747
- async (input) => {
748
- const result = await client.deleteKnowledgeNode(input.node_id);
749
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
750
- }
751
- );
752
-
753
- server.registerTool(
754
- 'memory_supersede_node',
755
- {
756
- title: 'Supersede knowledge node',
757
- description: 'Replace a knowledge node with updated information (maintains history)',
758
- inputSchema: z.object({
759
- node_id: z.string().uuid(),
760
- new_content: z.string(),
761
- reason: z.string().optional(),
762
- }),
763
- },
764
- async (input) => {
765
- const { node_id, ...body } = input;
766
- const result = await client.supersedeKnowledgeNode(node_id, body);
767
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
768
- }
769
- );
770
-
771
- server.registerTool(
772
- 'memory_timeline',
773
- {
774
- title: 'Memory timeline',
775
- description: 'Get chronological timeline of memory events for a workspace',
776
- inputSchema: z.object({ workspace_id: z.string().uuid() }),
777
- },
778
- async (input) => {
779
- const result = await client.memoryTimeline(input.workspace_id);
780
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
781
- }
782
- );
783
-
784
- server.registerTool(
785
- 'memory_summary',
786
- {
787
- title: 'Memory summary',
788
- description: 'Get condensed summary of workspace memory',
789
- inputSchema: z.object({ workspace_id: z.string().uuid() }),
790
- },
791
- async (input) => {
792
- const result = await client.memorySummary(input.workspace_id);
793
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
794
- }
795
- );
796
-
797
- // Extended graph operations
798
- server.registerTool(
799
- 'graph_circular_dependencies',
800
- {
801
- title: 'Find circular dependencies',
802
- description: 'Detect circular dependencies in project code',
803
- inputSchema: z.object({ project_id: z.string().uuid() }),
804
- },
805
- async (input) => {
806
- const result = await client.findCircularDependencies(input.project_id);
807
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
808
- }
809
- );
810
-
811
- server.registerTool(
812
- 'graph_unused_code',
813
- {
814
- title: 'Find unused code',
815
- description: 'Detect unused code in project',
816
- inputSchema: z.object({ project_id: z.string().uuid() }),
817
- },
818
- async (input) => {
819
- const result = await client.findUnusedCode(input.project_id);
820
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
821
- }
822
- );
823
-
824
- server.registerTool(
825
- 'graph_contradictions',
826
- {
827
- title: 'Find contradictions',
828
- description: 'Find contradicting information related to a knowledge node',
829
- inputSchema: z.object({ node_id: z.string().uuid() }),
830
- },
831
- async (input) => {
832
- const result = await client.findContradictions(input.node_id);
833
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
834
- }
835
- );
836
-
837
- // Search suggestions
838
- server.registerTool(
839
- 'search_suggestions',
840
- {
841
- title: 'Search suggestions',
842
- description: 'Get search suggestions based on partial query',
843
- inputSchema: z.object({
844
- query: z.string(),
845
- workspace_id: z.string().uuid().optional(),
846
- project_id: z.string().uuid().optional(),
847
- }),
848
- },
849
- async (input) => {
850
- const result = await client.searchSuggestions(input);
851
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
852
- }
853
- );
854
-
855
- // ============================================
856
- // Session & Auto-Context Tools (CORE-like)
857
- // ============================================
858
-
859
- server.registerTool(
860
- 'session_init',
861
- {
862
- title: 'Initialize conversation session',
863
- description: `Initialize a new conversation session and automatically retrieve relevant context.
864
- This is the FIRST tool AI assistants should call when starting a conversation.
865
- Returns: workspace info, project info, recent memory, recent decisions, and relevant context.
866
- Automatically detects the IDE workspace/project path and can auto-index code.
867
- IMPORTANT: If you know the current workspace folder path, pass it as folder_path for accurate context resolution.`,
868
- inputSchema: z.object({
869
- folder_path: z.string().optional().describe('Current workspace/project folder path (absolute). Use this when IDE roots are not available.'),
870
- workspace_id: z.string().uuid().optional().describe('Workspace to initialize context for'),
871
- project_id: z.string().uuid().optional().describe('Project to initialize context for'),
872
- session_id: z.string().optional().describe('Custom session ID (auto-generated if not provided)'),
873
- context_hint: z.string().optional().describe('Hint about what the user wants to work on (used for context search)'),
874
- include_recent_memory: z.boolean().optional().describe('Include recent memory events (default: true)'),
875
- include_decisions: z.boolean().optional().describe('Include recent decisions (default: true)'),
876
- auto_index: z.boolean().optional().describe('Automatically create and index project from IDE workspace (default: true)'),
877
- }),
878
- },
879
- async (input) => {
880
- // Get IDE workspace roots if available
881
- let ideRoots: string[] = [];
882
- try {
883
- const rootsResponse = await server.server.listRoots();
884
- if (rootsResponse?.roots) {
885
- ideRoots = rootsResponse.roots.map((r: { uri: string; name?: string }) => r.uri.replace('file://', ''));
886
- }
887
- } catch {
888
- // IDE may not support roots - that's okay
889
- }
890
-
891
- // Fallback to explicit folder_path if IDE roots not available
892
- if (ideRoots.length === 0 && input.folder_path) {
893
- ideRoots = [input.folder_path];
894
- }
895
-
896
- const result = await client.initSession(input, ideRoots);
897
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
898
- }
899
- );
900
-
901
- server.registerTool(
902
- 'session_get_user_context',
903
- {
904
- title: 'Get user context and preferences',
905
- description: `Retrieve user preferences, coding style, and persona from memory.
906
- Use this to understand how the user likes to work and adapt your responses accordingly.`,
907
- inputSchema: z.object({
908
- workspace_id: z.string().uuid().optional().describe('Workspace to get user context from'),
909
- }),
910
- },
911
- async (input) => {
912
- const result = await client.getUserContext(input);
913
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
914
- }
915
- );
916
-
917
- server.registerTool(
918
- 'workspace_associate',
919
- {
920
- title: 'Associate folder with workspace',
921
- description: `Associate a folder/repo with a workspace after user selection.
922
- Call this after session_init returns status='requires_workspace_selection' and the user has chosen a workspace.
923
- This persists the selection to .contextstream/config.json so future sessions auto-connect.
924
- Optionally creates a parent folder mapping (e.g., all repos under /dev/company/* map to the same workspace).`,
925
- inputSchema: z.object({
926
- folder_path: z.string().describe('Absolute path to the folder/repo to associate'),
927
- workspace_id: z.string().uuid().describe('Workspace ID to associate with'),
928
- workspace_name: z.string().optional().describe('Workspace name for reference'),
929
- create_parent_mapping: z.boolean().optional().describe('Also create a parent folder mapping (e.g., /dev/maker/* -> workspace)'),
930
- }),
931
- },
932
- async (input) => {
933
- const result = await client.associateWorkspace(input);
934
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
935
- }
936
- );
937
-
938
- server.registerTool(
939
- 'session_capture',
940
- {
941
- title: 'Capture context to memory',
942
- description: `Automatically capture and store important context from the conversation.
943
- Use this to persist decisions, insights, preferences, or important information.`,
944
- inputSchema: z.object({
945
- workspace_id: z.string().uuid().optional(),
946
- project_id: z.string().uuid().optional(),
947
- session_id: z.string().optional().describe('Session ID to associate with this capture'),
948
- event_type: z.enum(['conversation', 'decision', 'insight', 'preference', 'task', 'bug', 'feature']).describe('Type of context being captured'),
949
- title: z.string().describe('Brief title for the captured context'),
950
- content: z.string().describe('Full content/details to capture'),
951
- tags: z.array(z.string()).optional().describe('Tags for categorization'),
952
- importance: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Importance level'),
953
- }),
954
- },
955
- async (input) => {
956
- const result = await client.captureContext(input);
957
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
958
- }
959
- );
960
-
961
- server.registerTool(
962
- 'session_smart_search',
963
- {
964
- title: 'Smart context search',
965
- description: `Search memory with automatic context enrichment.
966
- Returns memory matches, relevant code, and related decisions in one call.`,
967
- inputSchema: z.object({
968
- query: z.string().describe('What to search for'),
969
- workspace_id: z.string().uuid().optional(),
970
- project_id: z.string().uuid().optional(),
971
- include_related: z.boolean().optional().describe('Include related context (default: true)'),
972
- include_decisions: z.boolean().optional().describe('Include related decisions (default: true)'),
973
- }),
974
- },
975
- async (input) => {
976
- const result = await client.smartSearch(input);
977
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
978
- }
979
- );
980
-
981
- server.registerTool(
982
- 'session_remember',
983
- {
984
- title: 'Remember this',
985
- description: `Quick way to store something in memory. Use natural language.
986
- Example: "Remember that I prefer TypeScript strict mode" or "Remember we decided to use PostgreSQL"`,
987
- inputSchema: z.object({
988
- content: z.string().describe('What to remember (natural language)'),
989
- workspace_id: z.string().uuid().optional(),
990
- project_id: z.string().uuid().optional(),
991
- importance: z.enum(['low', 'medium', 'high']).optional(),
992
- }),
993
- },
994
- async (input) => {
995
- // Auto-detect type from content
996
- const lowerContent = input.content.toLowerCase();
997
- let eventType: 'preference' | 'decision' | 'insight' | 'task' = 'insight';
998
- if (lowerContent.includes('prefer') || lowerContent.includes('like') || lowerContent.includes('always')) {
999
- eventType = 'preference';
1000
- } else if (lowerContent.includes('decided') || lowerContent.includes('decision') || lowerContent.includes('chose')) {
1001
- eventType = 'decision';
1002
- } else if (lowerContent.includes('todo') || lowerContent.includes('task') || lowerContent.includes('need to')) {
1003
- eventType = 'task';
1004
- }
1005
-
1006
- const result = await client.captureContext({
1007
- workspace_id: input.workspace_id,
1008
- project_id: input.project_id,
1009
- event_type: eventType,
1010
- title: input.content.slice(0, 100),
1011
- content: input.content,
1012
- importance: input.importance || 'medium',
1013
- });
1014
- return { content: [{ type: 'text' as const, text: `Remembered: ${input.content.slice(0, 100)}...` }], structuredContent: toStructured(result) };
1015
- }
1016
- );
1017
-
1018
- server.registerTool(
1019
- 'session_recall',
1020
- {
1021
- title: 'Recall from memory',
1022
- description: `Quick way to recall relevant context. Use natural language.
1023
- Example: "What were the auth decisions?" or "What are my TypeScript preferences?"`,
1024
- inputSchema: z.object({
1025
- query: z.string().describe('What to recall (natural language)'),
1026
- workspace_id: z.string().uuid().optional(),
1027
- project_id: z.string().uuid().optional(),
1028
- }),
1029
- },
1030
- async (input) => {
1031
- const result = await client.smartSearch({
1032
- query: input.query,
1033
- workspace_id: input.workspace_id,
1034
- project_id: input.project_id,
1035
- include_decisions: true,
1036
- });
1037
- return { content: [{ type: 'text' as const, text: formatContent(result) }], structuredContent: toStructured(result) };
1038
- }
1039
- );
1040
- }