@mrxkun/mcfast-mcp 4.0.5 → 4.0.11

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.
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Streaming utilities for MCP
3
+ * Supports real-time streaming of tool results
4
+ */
5
+
6
+ import { colors } from './colors.js';
7
+
8
+ /**
9
+ * Create a streaming response handler
10
+ * Allows tools to stream results progressively
11
+ */
12
+ export class StreamingHandler {
13
+ constructor(options = {}) {
14
+ this.onProgress = options.onProgress || (() => {});
15
+ this.onComplete = options.onComplete || (() => {});
16
+ this.onError = options.onError || (() => {});
17
+ this.chunks = [];
18
+ this.isComplete = false;
19
+ }
20
+
21
+ /**
22
+ * Stream a chunk of data
23
+ */
24
+ stream(chunk, metadata = {}) {
25
+ if (this.isComplete) {
26
+ console.error(`${colors.yellow}[Stream]${colors.reset} Warning: Streaming after completion`);
27
+ return;
28
+ }
29
+
30
+ this.chunks.push({ content: chunk, metadata, timestamp: Date.now() });
31
+ this.onProgress(chunk, metadata);
32
+
33
+ // Log progress for debugging
34
+ if (process.env.MCFAST_VERBOSE === 'true') {
35
+ console.error(`${colors.dim}[Stream]${colors.reset} Chunk #${this.chunks.length}: ${chunk.length} chars`);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Complete the stream
41
+ */
42
+ complete(finalData = null) {
43
+ this.isComplete = true;
44
+
45
+ // Combine all chunks
46
+ const fullContent = this.chunks.map(c => c.content).join('');
47
+
48
+ this.onComplete({
49
+ content: fullContent,
50
+ chunks: this.chunks,
51
+ finalData
52
+ });
53
+
54
+ return fullContent;
55
+ }
56
+
57
+ /**
58
+ * Error occurred during streaming
59
+ */
60
+ error(error) {
61
+ this.isComplete = true;
62
+ this.onError(error);
63
+ }
64
+
65
+ /**
66
+ * Get current progress
67
+ */
68
+ getProgress() {
69
+ return {
70
+ chunks: this.chunks.length,
71
+ totalChars: this.chunks.reduce((sum, c) => sum + c.content.length, 0),
72
+ isComplete: this.isComplete
73
+ };
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Stream file reading for large files
79
+ * Reads file in chunks and streams progressively
80
+ */
81
+ export async function streamFileRead(filePath, options = {}) {
82
+ const { chunkSize = 1000, onChunk } = options;
83
+ const handler = new StreamingHandler({ onProgress: onChunk });
84
+
85
+ try {
86
+ const fs = await import('fs/promises');
87
+ const handle = await fs.open(filePath, 'r');
88
+
89
+ let position = 0;
90
+ const buffer = Buffer.alloc(chunkSize);
91
+
92
+ while (true) {
93
+ const { bytesRead } = await handle.read(buffer, 0, chunkSize, position);
94
+ if (bytesRead === 0) break;
95
+
96
+ const chunk = buffer.toString('utf8', 0, bytesRead);
97
+ handler.stream(chunk, { position, bytesRead });
98
+ position += bytesRead;
99
+ }
100
+
101
+ await handle.close();
102
+ return handler.complete({ filePath, totalBytes: position });
103
+
104
+ } catch (error) {
105
+ handler.error(error);
106
+ throw error;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Stream search results as they're found
112
+ */
113
+ export async function streamSearchResults(searchFn, query, options = {}) {
114
+ const { onResult, onComplete } = options;
115
+ const results = [];
116
+
117
+ try {
118
+ // For streaming, we simulate progressive results
119
+ // In real implementation, search would yield results as found
120
+ const allResults = await searchFn(query);
121
+
122
+ for (let i = 0; i < allResults.length; i++) {
123
+ const result = allResults[i];
124
+ results.push(result);
125
+
126
+ if (onResult) {
127
+ onResult(result, {
128
+ index: i,
129
+ total: allResults.length,
130
+ progress: ((i + 1) / allResults.length * 100).toFixed(1)
131
+ });
132
+ }
133
+
134
+ // Small delay to simulate streaming
135
+ if (i < allResults.length - 1) {
136
+ await new Promise(resolve => setTimeout(resolve, 10));
137
+ }
138
+ }
139
+
140
+ if (onComplete) {
141
+ onComplete(results);
142
+ }
143
+
144
+ return results;
145
+
146
+ } catch (error) {
147
+ console.error(`${colors.red}[Stream]${colors.reset} Search error:`, error.message);
148
+ throw error;
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Stream edit progress
154
+ */
155
+ export function createEditStream(options = {}) {
156
+ const handler = new StreamingHandler(options);
157
+
158
+ return {
159
+ // Report parsing phase
160
+ parsing: () => handler.stream('Parsing code...\n', { phase: 'parsing' }),
161
+
162
+ // Report analysis phase
163
+ analyzing: () => handler.stream('Analyzing impact...\n', { phase: 'analysis' }),
164
+
165
+ // Report applying phase
166
+ applying: (file) => handler.stream(`Applying changes to ${file}...\n`, { phase: 'applying', file }),
167
+
168
+ // Report validation phase
169
+ validating: () => handler.stream('Validating changes...\n', { phase: 'validation' }),
170
+
171
+ // Report completion
172
+ complete: (result) => handler.complete(result),
173
+
174
+ // Report error
175
+ error: (err) => handler.error(err),
176
+
177
+ // Get current state
178
+ getProgress: () => handler.getProgress()
179
+ };
180
+ }