@dahawa/hawa-cli-analysis 1.0.7 → 1.0.8
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/.tools.json +6 -6
- package/.vscode/launch.json +26 -26
- package/_uclaude.js +0 -0
- package/anthropic-transformer.js +986 -986
- package/api-anthropic.js +279 -279
- package/api-openai.js +538 -538
- package/clogger-openai.js +190 -190
- package/clogger.js +318 -318
- package/codex/mcp-client.js +556 -556
- package/codex/mcpclient.js +117 -117
- package/codex/mcpserver.js +374 -374
- package/codex/mcpserverproxy.js +143 -143
- package/codex/test.js +30 -30
- package/mcp/claude-mcpproxy-launcher.js +4 -4
- package/package.json +2 -1
- package/simple-transform-example.js +212 -212
- package/tests/test-lazy-load.js +35 -35
- package/tests/test_mcp_proxy.js +50 -50
- package/tests/test_supabase_mcp.js +41 -41
- package/uclaude.js +3 -3
- package/ucodex-proxy.js +172 -172
- package/ucodex.js +127 -127
|
@@ -1,213 +1,213 @@
|
|
|
1
|
-
// Simple example of using transformResponseIn with streaming data
|
|
2
|
-
import AnthropicTransformer from './anthropic-transformer.js';
|
|
3
|
-
|
|
4
|
-
// Example streaming response data (like the one you provided)
|
|
5
|
-
const exampleStreamData = [
|
|
6
|
-
'event: response.created\ndata: {"id":"resp_abc123","type":"response.created","created":1739558401,"response":{"id":"resp_abc123","model":"gpt-4o-mini","status":"in_progress"}}\n\n',
|
|
7
|
-
'event: response.in_progress\ndata: {"id":"resp_abc123","type":"response.in_progress","response":{"id":"resp_abc123","status":"in_progress"}}\n\n',
|
|
8
|
-
'event: response.output_text.delta\ndata: {"id":"resp_abc123","type":"response.output_text.delta","response_id":"resp_abc123","output_index":0,"delta":"早"}\n\n',
|
|
9
|
-
'event: response.output_text.delta\ndata: {"id":"resp_abc123","type":"response.output_text.delta","response_id":"resp_abc123","output_index":0,"delta":"上"}\n\n',
|
|
10
|
-
'event: response.output_text.delta\ndata: {"id":"resp_abc123","type":"response.output_text.delta","response_id":"resp_abc123","output_index":0,"delta":"好,"}\n\n',
|
|
11
|
-
'event: response.output_text.delta\ndata: {"id":"resp_abc123","type":"response.output_text.delta","response_id":"resp_abc123","output_index":0,"delta":"给你一段流式返回示例。"}\n\n',
|
|
12
|
-
'event: response.output_text.done\ndata: {"id":"resp_abc123","type":"response.output_text.done","response_id":"resp_abc123","output_index":0}\n\n'
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
// Simple method to transform streaming response data
|
|
16
|
-
async function transformResponseIn(streamData) {
|
|
17
|
-
const transformer = new AnthropicTransformer();
|
|
18
|
-
|
|
19
|
-
// Create a mock Response object with a readable stream
|
|
20
|
-
const stream = new ReadableStream({
|
|
21
|
-
start(controller) {
|
|
22
|
-
// Simulate streaming by sending data chunks with delays
|
|
23
|
-
let index = 0;
|
|
24
|
-
const encoder = new TextEncoder();
|
|
25
|
-
|
|
26
|
-
const sendNextChunk = () => {
|
|
27
|
-
if (index < streamData.length) {
|
|
28
|
-
controller.enqueue(encoder.encode(streamData[index]));
|
|
29
|
-
index++;
|
|
30
|
-
setTimeout(sendNextChunk, 100); // Simulate network delay
|
|
31
|
-
} else {
|
|
32
|
-
controller.close();
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
sendNextChunk();
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// Create a Response object that mimics the actual streaming response
|
|
41
|
-
const mockResponse = new Response(stream, {
|
|
42
|
-
headers: {
|
|
43
|
-
'Content-Type': 'text/event-stream',
|
|
44
|
-
'Cache-Control': 'no-cache',
|
|
45
|
-
'Connection': 'keep-alive'
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
// Use the existing transformResponseIn method
|
|
51
|
-
const transformedResponse = await transformer.transformResponseIn(mockResponse);
|
|
52
|
-
|
|
53
|
-
// Process the transformed stream
|
|
54
|
-
const reader = transformedResponse.body.getReader();
|
|
55
|
-
const decoder = new TextDecoder();
|
|
56
|
-
|
|
57
|
-
console.log('Transformed Anthropic-style streaming response:');
|
|
58
|
-
console.log('==============================================');
|
|
59
|
-
|
|
60
|
-
while (true) {
|
|
61
|
-
const { done, value } = await reader.read();
|
|
62
|
-
if (done) break;
|
|
63
|
-
|
|
64
|
-
const chunk = decoder.decode(value);
|
|
65
|
-
const lines = chunk.split('\n');
|
|
66
|
-
|
|
67
|
-
for (const line of lines) {
|
|
68
|
-
if (line.startsWith('event:')) {
|
|
69
|
-
const eventType = line.replace('event:', '').trim();
|
|
70
|
-
console.log(`Event: ${eventType}`);
|
|
71
|
-
} else if (line.startsWith('data:')) {
|
|
72
|
-
const data = line.replace('data:', '').trim();
|
|
73
|
-
try {
|
|
74
|
-
const parsedData = JSON.parse(data);
|
|
75
|
-
console.log('Data:', JSON.stringify(parsedData, null, 2));
|
|
76
|
-
} catch (e) {
|
|
77
|
-
console.log('Data:', data);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return transformedResponse;
|
|
84
|
-
} catch (error) {
|
|
85
|
-
console.error('Error transforming response:', error);
|
|
86
|
-
throw error;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Even simpler method for direct data transformation
|
|
91
|
-
function simpleTransformResponse(data) {
|
|
92
|
-
// Convert the streaming data to Anthropic format
|
|
93
|
-
const anthropicEvents = [];
|
|
94
|
-
let messageId = `msg_${Date.now()}`;
|
|
95
|
-
let contentIndex = 0;
|
|
96
|
-
let accumulatedText = '';
|
|
97
|
-
|
|
98
|
-
// Simulate message start
|
|
99
|
-
anthropicEvents.push({
|
|
100
|
-
event: 'message_start',
|
|
101
|
-
data: {
|
|
102
|
-
type: 'message_start',
|
|
103
|
-
message: {
|
|
104
|
-
id: messageId,
|
|
105
|
-
type: 'message',
|
|
106
|
-
role: 'assistant',
|
|
107
|
-
content: [],
|
|
108
|
-
model: 'gpt-4o-mini',
|
|
109
|
-
stop_reason: null,
|
|
110
|
-
stop_sequence: null,
|
|
111
|
-
usage: {
|
|
112
|
-
input_tokens: 0,
|
|
113
|
-
output_tokens: 0
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
// Process text content
|
|
120
|
-
for (const item of data) {
|
|
121
|
-
if (item.includes('response.output_text.delta')) {
|
|
122
|
-
const match = item.match(/"delta":"([^"]*)"/);
|
|
123
|
-
if (match) {
|
|
124
|
-
accumulatedText += match[1];
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Add content block
|
|
130
|
-
anthropicEvents.push({
|
|
131
|
-
event: 'content_block_start',
|
|
132
|
-
data: {
|
|
133
|
-
type: 'content_block_start',
|
|
134
|
-
index: contentIndex,
|
|
135
|
-
content_block: {
|
|
136
|
-
type: 'text',
|
|
137
|
-
text: ''
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// Add the accumulated text
|
|
143
|
-
anthropicEvents.push({
|
|
144
|
-
event: 'content_block_delta',
|
|
145
|
-
data: {
|
|
146
|
-
type: 'content_block_delta',
|
|
147
|
-
index: contentIndex,
|
|
148
|
-
delta: {
|
|
149
|
-
type: 'text_delta',
|
|
150
|
-
text: accumulatedText
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
// Close content block
|
|
156
|
-
anthropicEvents.push({
|
|
157
|
-
event: 'content_block_stop',
|
|
158
|
-
data: {
|
|
159
|
-
type: 'content_block_stop',
|
|
160
|
-
index: contentIndex
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
// Message delta and stop
|
|
165
|
-
anthropicEvents.push({
|
|
166
|
-
event: 'message_delta',
|
|
167
|
-
data: {
|
|
168
|
-
type: 'message_delta',
|
|
169
|
-
delta: {
|
|
170
|
-
stop_reason: 'end_turn',
|
|
171
|
-
stop_sequence: null
|
|
172
|
-
},
|
|
173
|
-
usage: {
|
|
174
|
-
input_tokens: 10,
|
|
175
|
-
output_tokens: accumulatedText.length,
|
|
176
|
-
cache_read_input_tokens: 0
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
anthropicEvents.push({
|
|
182
|
-
event: 'message_stop',
|
|
183
|
-
data: {
|
|
184
|
-
type: 'message_stop'
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
return anthropicEvents;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Usage examples
|
|
192
|
-
async function runExamples() {
|
|
193
|
-
console.log('=== Simple Transform Example ===');
|
|
194
|
-
const simpleResult = simpleTransformResponse(exampleStreamData);
|
|
195
|
-
simpleResult.forEach(event => {
|
|
196
|
-
console.log(`${event.event}:`, JSON.stringify(event.data, null, 2));
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
console.log('\n=== Full Transform Example ===');
|
|
200
|
-
try {
|
|
201
|
-
await transformResponseIn(exampleStreamData);
|
|
202
|
-
} catch (error) {
|
|
203
|
-
console.error('Error:', error.message);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Export the functions
|
|
208
|
-
export { transformResponseIn, simpleTransformResponse, runExamples };
|
|
209
|
-
|
|
210
|
-
// Run examples if this file is executed directly
|
|
211
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
212
|
-
runExamples();
|
|
1
|
+
// Simple example of using transformResponseIn with streaming data
|
|
2
|
+
import AnthropicTransformer from './anthropic-transformer.js';
|
|
3
|
+
|
|
4
|
+
// Example streaming response data (like the one you provided)
|
|
5
|
+
const exampleStreamData = [
|
|
6
|
+
'event: response.created\ndata: {"id":"resp_abc123","type":"response.created","created":1739558401,"response":{"id":"resp_abc123","model":"gpt-4o-mini","status":"in_progress"}}\n\n',
|
|
7
|
+
'event: response.in_progress\ndata: {"id":"resp_abc123","type":"response.in_progress","response":{"id":"resp_abc123","status":"in_progress"}}\n\n',
|
|
8
|
+
'event: response.output_text.delta\ndata: {"id":"resp_abc123","type":"response.output_text.delta","response_id":"resp_abc123","output_index":0,"delta":"早"}\n\n',
|
|
9
|
+
'event: response.output_text.delta\ndata: {"id":"resp_abc123","type":"response.output_text.delta","response_id":"resp_abc123","output_index":0,"delta":"上"}\n\n',
|
|
10
|
+
'event: response.output_text.delta\ndata: {"id":"resp_abc123","type":"response.output_text.delta","response_id":"resp_abc123","output_index":0,"delta":"好,"}\n\n',
|
|
11
|
+
'event: response.output_text.delta\ndata: {"id":"resp_abc123","type":"response.output_text.delta","response_id":"resp_abc123","output_index":0,"delta":"给你一段流式返回示例。"}\n\n',
|
|
12
|
+
'event: response.output_text.done\ndata: {"id":"resp_abc123","type":"response.output_text.done","response_id":"resp_abc123","output_index":0}\n\n'
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
// Simple method to transform streaming response data
|
|
16
|
+
async function transformResponseIn(streamData) {
|
|
17
|
+
const transformer = new AnthropicTransformer();
|
|
18
|
+
|
|
19
|
+
// Create a mock Response object with a readable stream
|
|
20
|
+
const stream = new ReadableStream({
|
|
21
|
+
start(controller) {
|
|
22
|
+
// Simulate streaming by sending data chunks with delays
|
|
23
|
+
let index = 0;
|
|
24
|
+
const encoder = new TextEncoder();
|
|
25
|
+
|
|
26
|
+
const sendNextChunk = () => {
|
|
27
|
+
if (index < streamData.length) {
|
|
28
|
+
controller.enqueue(encoder.encode(streamData[index]));
|
|
29
|
+
index++;
|
|
30
|
+
setTimeout(sendNextChunk, 100); // Simulate network delay
|
|
31
|
+
} else {
|
|
32
|
+
controller.close();
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
sendNextChunk();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Create a Response object that mimics the actual streaming response
|
|
41
|
+
const mockResponse = new Response(stream, {
|
|
42
|
+
headers: {
|
|
43
|
+
'Content-Type': 'text/event-stream',
|
|
44
|
+
'Cache-Control': 'no-cache',
|
|
45
|
+
'Connection': 'keep-alive'
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
// Use the existing transformResponseIn method
|
|
51
|
+
const transformedResponse = await transformer.transformResponseIn(mockResponse);
|
|
52
|
+
|
|
53
|
+
// Process the transformed stream
|
|
54
|
+
const reader = transformedResponse.body.getReader();
|
|
55
|
+
const decoder = new TextDecoder();
|
|
56
|
+
|
|
57
|
+
console.log('Transformed Anthropic-style streaming response:');
|
|
58
|
+
console.log('==============================================');
|
|
59
|
+
|
|
60
|
+
while (true) {
|
|
61
|
+
const { done, value } = await reader.read();
|
|
62
|
+
if (done) break;
|
|
63
|
+
|
|
64
|
+
const chunk = decoder.decode(value);
|
|
65
|
+
const lines = chunk.split('\n');
|
|
66
|
+
|
|
67
|
+
for (const line of lines) {
|
|
68
|
+
if (line.startsWith('event:')) {
|
|
69
|
+
const eventType = line.replace('event:', '').trim();
|
|
70
|
+
console.log(`Event: ${eventType}`);
|
|
71
|
+
} else if (line.startsWith('data:')) {
|
|
72
|
+
const data = line.replace('data:', '').trim();
|
|
73
|
+
try {
|
|
74
|
+
const parsedData = JSON.parse(data);
|
|
75
|
+
console.log('Data:', JSON.stringify(parsedData, null, 2));
|
|
76
|
+
} catch (e) {
|
|
77
|
+
console.log('Data:', data);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return transformedResponse;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error('Error transforming response:', error);
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Even simpler method for direct data transformation
|
|
91
|
+
function simpleTransformResponse(data) {
|
|
92
|
+
// Convert the streaming data to Anthropic format
|
|
93
|
+
const anthropicEvents = [];
|
|
94
|
+
let messageId = `msg_${Date.now()}`;
|
|
95
|
+
let contentIndex = 0;
|
|
96
|
+
let accumulatedText = '';
|
|
97
|
+
|
|
98
|
+
// Simulate message start
|
|
99
|
+
anthropicEvents.push({
|
|
100
|
+
event: 'message_start',
|
|
101
|
+
data: {
|
|
102
|
+
type: 'message_start',
|
|
103
|
+
message: {
|
|
104
|
+
id: messageId,
|
|
105
|
+
type: 'message',
|
|
106
|
+
role: 'assistant',
|
|
107
|
+
content: [],
|
|
108
|
+
model: 'gpt-4o-mini',
|
|
109
|
+
stop_reason: null,
|
|
110
|
+
stop_sequence: null,
|
|
111
|
+
usage: {
|
|
112
|
+
input_tokens: 0,
|
|
113
|
+
output_tokens: 0
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Process text content
|
|
120
|
+
for (const item of data) {
|
|
121
|
+
if (item.includes('response.output_text.delta')) {
|
|
122
|
+
const match = item.match(/"delta":"([^"]*)"/);
|
|
123
|
+
if (match) {
|
|
124
|
+
accumulatedText += match[1];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Add content block
|
|
130
|
+
anthropicEvents.push({
|
|
131
|
+
event: 'content_block_start',
|
|
132
|
+
data: {
|
|
133
|
+
type: 'content_block_start',
|
|
134
|
+
index: contentIndex,
|
|
135
|
+
content_block: {
|
|
136
|
+
type: 'text',
|
|
137
|
+
text: ''
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Add the accumulated text
|
|
143
|
+
anthropicEvents.push({
|
|
144
|
+
event: 'content_block_delta',
|
|
145
|
+
data: {
|
|
146
|
+
type: 'content_block_delta',
|
|
147
|
+
index: contentIndex,
|
|
148
|
+
delta: {
|
|
149
|
+
type: 'text_delta',
|
|
150
|
+
text: accumulatedText
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Close content block
|
|
156
|
+
anthropicEvents.push({
|
|
157
|
+
event: 'content_block_stop',
|
|
158
|
+
data: {
|
|
159
|
+
type: 'content_block_stop',
|
|
160
|
+
index: contentIndex
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Message delta and stop
|
|
165
|
+
anthropicEvents.push({
|
|
166
|
+
event: 'message_delta',
|
|
167
|
+
data: {
|
|
168
|
+
type: 'message_delta',
|
|
169
|
+
delta: {
|
|
170
|
+
stop_reason: 'end_turn',
|
|
171
|
+
stop_sequence: null
|
|
172
|
+
},
|
|
173
|
+
usage: {
|
|
174
|
+
input_tokens: 10,
|
|
175
|
+
output_tokens: accumulatedText.length,
|
|
176
|
+
cache_read_input_tokens: 0
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
anthropicEvents.push({
|
|
182
|
+
event: 'message_stop',
|
|
183
|
+
data: {
|
|
184
|
+
type: 'message_stop'
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
return anthropicEvents;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Usage examples
|
|
192
|
+
async function runExamples() {
|
|
193
|
+
console.log('=== Simple Transform Example ===');
|
|
194
|
+
const simpleResult = simpleTransformResponse(exampleStreamData);
|
|
195
|
+
simpleResult.forEach(event => {
|
|
196
|
+
console.log(`${event.event}:`, JSON.stringify(event.data, null, 2));
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
console.log('\n=== Full Transform Example ===');
|
|
200
|
+
try {
|
|
201
|
+
await transformResponseIn(exampleStreamData);
|
|
202
|
+
} catch (error) {
|
|
203
|
+
console.error('Error:', error.message);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Export the functions
|
|
208
|
+
export { transformResponseIn, simpleTransformResponse, runExamples };
|
|
209
|
+
|
|
210
|
+
// Run examples if this file is executed directly
|
|
211
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
212
|
+
runExamples();
|
|
213
213
|
}
|
package/tests/test-lazy-load.js
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 测试懒加载MCP Server的启动性能
|
|
3
|
-
*/
|
|
4
|
-
import { startMCPServerProxy } from '../codex/mcpserver.js';
|
|
5
|
-
import LogManager from '../logger-manager.js';
|
|
6
|
-
|
|
7
|
-
const logger = LogManager.getSystemLogger();
|
|
8
|
-
|
|
9
|
-
console.log('=== 测试MCP Server懒加载性能 ===');
|
|
10
|
-
|
|
11
|
-
// 记录启动开始时间
|
|
12
|
-
const startTime = Date.now();
|
|
13
|
-
|
|
14
|
-
logger.info('开始启动MCP Server代理...');
|
|
15
|
-
|
|
16
|
-
// 启动MCP Server代理(现在不会立即连接所有MCP服务)
|
|
17
|
-
startMCPServerProxy();
|
|
18
|
-
|
|
19
|
-
// 记录启动完成时间
|
|
20
|
-
const endTime = Date.now();
|
|
21
|
-
const startupTime = endTime - startTime;
|
|
22
|
-
|
|
23
|
-
console.log(`\n=== 测试结果 ===`);
|
|
24
|
-
console.log(`MCP Server代理启动时间: ${startupTime}ms`);
|
|
25
|
-
console.log(`启动方式: 懒加载(不会立即连接所有MCP服务)`);
|
|
26
|
-
console.log(`状态: 代理服务已启动,等待客户端请求时才会连接具体的MCP服务`);
|
|
27
|
-
|
|
28
|
-
logger.info(`MCP Server代理启动完成,耗时: ${startupTime}ms`);
|
|
29
|
-
|
|
30
|
-
// 保持进程运行一段时间以便观察
|
|
31
|
-
setTimeout(() => {
|
|
32
|
-
console.log('\n=== 测试完成 ===');
|
|
33
|
-
console.log('MCP Server代理采用懒加载机制,只有在收到客户端请求时才会连接对应的MCP服务');
|
|
34
|
-
console.log('这样可以显著减少启动时间,特别是当配置了多个MCP服务时');
|
|
35
|
-
process.exit(0);
|
|
1
|
+
/**
|
|
2
|
+
* 测试懒加载MCP Server的启动性能
|
|
3
|
+
*/
|
|
4
|
+
import { startMCPServerProxy } from '../codex/mcpserver.js';
|
|
5
|
+
import LogManager from '../logger-manager.js';
|
|
6
|
+
|
|
7
|
+
const logger = LogManager.getSystemLogger();
|
|
8
|
+
|
|
9
|
+
console.log('=== 测试MCP Server懒加载性能 ===');
|
|
10
|
+
|
|
11
|
+
// 记录启动开始时间
|
|
12
|
+
const startTime = Date.now();
|
|
13
|
+
|
|
14
|
+
logger.info('开始启动MCP Server代理...');
|
|
15
|
+
|
|
16
|
+
// 启动MCP Server代理(现在不会立即连接所有MCP服务)
|
|
17
|
+
startMCPServerProxy();
|
|
18
|
+
|
|
19
|
+
// 记录启动完成时间
|
|
20
|
+
const endTime = Date.now();
|
|
21
|
+
const startupTime = endTime - startTime;
|
|
22
|
+
|
|
23
|
+
console.log(`\n=== 测试结果 ===`);
|
|
24
|
+
console.log(`MCP Server代理启动时间: ${startupTime}ms`);
|
|
25
|
+
console.log(`启动方式: 懒加载(不会立即连接所有MCP服务)`);
|
|
26
|
+
console.log(`状态: 代理服务已启动,等待客户端请求时才会连接具体的MCP服务`);
|
|
27
|
+
|
|
28
|
+
logger.info(`MCP Server代理启动完成,耗时: ${startupTime}ms`);
|
|
29
|
+
|
|
30
|
+
// 保持进程运行一段时间以便观察
|
|
31
|
+
setTimeout(() => {
|
|
32
|
+
console.log('\n=== 测试完成 ===');
|
|
33
|
+
console.log('MCP Server代理采用懒加载机制,只有在收到客户端请求时才会连接对应的MCP服务');
|
|
34
|
+
console.log('这样可以显著减少启动时间,特别是当配置了多个MCP服务时');
|
|
35
|
+
process.exit(0);
|
|
36
36
|
}, 3000);
|
package/tests/test_mcp_proxy.js
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
import { spawn } from 'child_process';
|
|
2
|
-
import JsonRpcClient from '../codex/mcpclient.js';
|
|
3
|
-
import LogManager from '../logger-manager.js';
|
|
4
|
-
|
|
5
|
-
const logger = LogManager.getSystemLogger();
|
|
6
|
-
|
|
7
|
-
async function testMCPProxy() {
|
|
8
|
-
logger.debug('Starting MCP proxy server test...');
|
|
9
|
-
|
|
10
|
-
// Start the MCP proxy server
|
|
11
|
-
const proxyProcess = spawn('node', ['../codex/mcpserverproxy.js', '--mcpServerName=supabase'], {
|
|
12
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
proxyProcess.stderr.on('data', (data) => {
|
|
16
|
-
logger.error('Proxy stderr:', data.toString());
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
proxyProcess.stdout.on('data', (data) => {
|
|
20
|
-
logger.debug('Proxy stdout:', data.toString());
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
// Wait a bit for the server to start
|
|
24
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
logger.debug('Creating JSON-RPC client...');
|
|
28
|
-
const client = new JsonRpcClient();
|
|
29
|
-
|
|
30
|
-
logger.debug('Testing initialize...');
|
|
31
|
-
const initResult = await client.call('supabase_initialize');
|
|
32
|
-
logger.debug('Initialize result:', JSON.stringify(initResult, null, 2));
|
|
33
|
-
|
|
34
|
-
logger.debug('Testing list tools...');
|
|
35
|
-
const toolsResult = await client.call('supabase_list');
|
|
36
|
-
logger.debug('Tools result:', JSON.stringify(toolsResult, null, 2));
|
|
37
|
-
|
|
38
|
-
if (toolsResult && toolsResult.tools) {
|
|
39
|
-
logger.debug(`Found ${toolsResult.tools.length} tools via proxy`);
|
|
40
|
-
} else {
|
|
41
|
-
logger.debug('No tools found via proxy or unexpected format');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
} catch (error) {
|
|
45
|
-
console.error('Error testing MCP proxy:', error);
|
|
46
|
-
} finally {
|
|
47
|
-
proxyProcess.kill();
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import JsonRpcClient from '../codex/mcpclient.js';
|
|
3
|
+
import LogManager from '../logger-manager.js';
|
|
4
|
+
|
|
5
|
+
const logger = LogManager.getSystemLogger();
|
|
6
|
+
|
|
7
|
+
async function testMCPProxy() {
|
|
8
|
+
logger.debug('Starting MCP proxy server test...');
|
|
9
|
+
|
|
10
|
+
// Start the MCP proxy server
|
|
11
|
+
const proxyProcess = spawn('node', ['../codex/mcpserverproxy.js', '--mcpServerName=supabase'], {
|
|
12
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
proxyProcess.stderr.on('data', (data) => {
|
|
16
|
+
logger.error('Proxy stderr:', data.toString());
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
proxyProcess.stdout.on('data', (data) => {
|
|
20
|
+
logger.debug('Proxy stdout:', data.toString());
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Wait a bit for the server to start
|
|
24
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
logger.debug('Creating JSON-RPC client...');
|
|
28
|
+
const client = new JsonRpcClient();
|
|
29
|
+
|
|
30
|
+
logger.debug('Testing initialize...');
|
|
31
|
+
const initResult = await client.call('supabase_initialize');
|
|
32
|
+
logger.debug('Initialize result:', JSON.stringify(initResult, null, 2));
|
|
33
|
+
|
|
34
|
+
logger.debug('Testing list tools...');
|
|
35
|
+
const toolsResult = await client.call('supabase_list');
|
|
36
|
+
logger.debug('Tools result:', JSON.stringify(toolsResult, null, 2));
|
|
37
|
+
|
|
38
|
+
if (toolsResult && toolsResult.tools) {
|
|
39
|
+
logger.debug(`Found ${toolsResult.tools.length} tools via proxy`);
|
|
40
|
+
} else {
|
|
41
|
+
logger.debug('No tools found via proxy or unexpected format');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('Error testing MCP proxy:', error);
|
|
46
|
+
} finally {
|
|
47
|
+
proxyProcess.kill();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
51
|
testMCPProxy();
|