smart-image-scraper-mcp 2.12.2 → 2.12.4
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/package.json +1 -1
- package/src/config/index.js +1 -1
- package/src/index.js +5 -5
- package/src/infrastructure/logger.js +17 -4
- package/src/services/fileManager.js +4 -3
package/package.json
CHANGED
package/src/config/index.js
CHANGED
|
@@ -33,7 +33,7 @@ function ensureSaveRoot(savePath) {
|
|
|
33
33
|
fs.unlinkSync(testFile);
|
|
34
34
|
return savePath;
|
|
35
35
|
} catch (error) {
|
|
36
|
-
|
|
36
|
+
process.stderr.write(`[CONFIG] Cannot write to SAVE_ROOT: ${savePath}, using default\n`);
|
|
37
37
|
const defaultPath = path.join(__dirname, '../../images');
|
|
38
38
|
if (!fs.existsSync(defaultPath)) {
|
|
39
39
|
fs.mkdirSync(defaultPath, { recursive: true });
|
package/src/index.js
CHANGED
|
@@ -229,7 +229,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
229
229
|
} catch (error) {
|
|
230
230
|
clearTimeout(mcpTimeoutId);
|
|
231
231
|
orchestrator.cleanup();
|
|
232
|
-
|
|
232
|
+
process.stderr.write(`[MCP Error] ${error.message}\n`);
|
|
233
233
|
return {
|
|
234
234
|
content: [{
|
|
235
235
|
type: 'text',
|
|
@@ -242,16 +242,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
242
242
|
|
|
243
243
|
// 启动服务器(主流做法:最简启动,使用 stderr 输出日志避免干扰 stdio 通信)
|
|
244
244
|
async function main() {
|
|
245
|
-
|
|
246
|
-
|
|
245
|
+
process.stderr.write(`[MCP] Starting Smart Image Scraper v${packageJson.version}\n`);
|
|
246
|
+
process.stderr.write(`[MCP] Save root: ${config.SAVE_ROOT}\n`);
|
|
247
247
|
|
|
248
248
|
const transport = new StdioServerTransport();
|
|
249
249
|
await server.connect(transport);
|
|
250
250
|
|
|
251
|
-
|
|
251
|
+
process.stderr.write('[MCP] Server is running\n');
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
main().catch((error) => {
|
|
255
|
-
|
|
255
|
+
process.stderr.write(`[MCP] Startup error: ${error.message}\n`);
|
|
256
256
|
process.exit(1);
|
|
257
257
|
});
|
|
@@ -43,6 +43,10 @@ class Logger {
|
|
|
43
43
|
this.logFile = options.logFile || process.env.LOG_FILE;
|
|
44
44
|
this.maxLogSize = options.maxLogSize || 10 * 1024 * 1024; // 10MB
|
|
45
45
|
|
|
46
|
+
// stderr 限流计数器(每秒窗口)
|
|
47
|
+
this._stderrWindowStart = 0;
|
|
48
|
+
this._stderrWindowCount = 0;
|
|
49
|
+
|
|
46
50
|
// 初始化文件日志
|
|
47
51
|
if (this.logFile) {
|
|
48
52
|
this._initFileLogging();
|
|
@@ -127,17 +131,26 @@ class Logger {
|
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
/**
|
|
130
|
-
*
|
|
134
|
+
* 写入日志(带限流保护)
|
|
131
135
|
*/
|
|
132
136
|
_write(level, message, data) {
|
|
133
137
|
if (level < this.level) return;
|
|
134
138
|
|
|
135
139
|
const formatted = this._format(level, message, data);
|
|
136
140
|
|
|
137
|
-
// 输出到 stderr
|
|
138
|
-
// 仅输出 WARN 及以上级别到 stderr,减少 IO 压力
|
|
141
|
+
// 输出到 stderr(带限流:每秒最多 20 条,防止高频调用时 stderr 缓冲区堆积阻塞事件循环)
|
|
139
142
|
if (level >= LogLevel.WARN) {
|
|
140
|
-
|
|
143
|
+
const now = Date.now();
|
|
144
|
+
if (now - this._stderrWindowStart > 1000) {
|
|
145
|
+
// 新的1秒窗口
|
|
146
|
+
this._stderrWindowStart = now;
|
|
147
|
+
this._stderrWindowCount = 0;
|
|
148
|
+
}
|
|
149
|
+
if (this._stderrWindowCount < 20) {
|
|
150
|
+
this._stderrWindowCount++;
|
|
151
|
+
process.stderr.write(formatted + '\n');
|
|
152
|
+
}
|
|
153
|
+
// 超出限流的日志静默丢弃,不阻塞
|
|
141
154
|
}
|
|
142
155
|
|
|
143
156
|
// 输出到文件(异步写入,避免阻塞事件循环)
|
|
@@ -169,10 +169,11 @@ export class FileManager {
|
|
|
169
169
|
|
|
170
170
|
response = await httpClient.get(url, {
|
|
171
171
|
responseType: 'stream',
|
|
172
|
-
timeout:
|
|
172
|
+
timeout: 10000, // 连接超时10秒
|
|
173
173
|
maxContentLength: 50 * 1024 * 1024, // 最大50MB
|
|
174
174
|
maxBodyLength: 50 * 1024 * 1024,
|
|
175
175
|
headers: downloadHeaders,
|
|
176
|
+
signal: AbortSignal.timeout(12000), // 12秒硬超时,防止连接挂起
|
|
176
177
|
});
|
|
177
178
|
|
|
178
179
|
if (response.status !== 200) {
|
|
@@ -214,7 +215,7 @@ export class FileManager {
|
|
|
214
215
|
let downloadedBytes = 0;
|
|
215
216
|
const maxBytes = 50 * 1024 * 1024;
|
|
216
217
|
|
|
217
|
-
// 下载超时保护(
|
|
218
|
+
// 下载超时保护(15秒)
|
|
218
219
|
downloadTimeout = setTimeout(() => {
|
|
219
220
|
if (!resolved) {
|
|
220
221
|
resolved = true;
|
|
@@ -223,7 +224,7 @@ export class FileManager {
|
|
|
223
224
|
this._cleanupFile(filePath);
|
|
224
225
|
resolve({ success: false, url, error: 'Download timeout' });
|
|
225
226
|
}
|
|
226
|
-
},
|
|
227
|
+
}, 15000);
|
|
227
228
|
|
|
228
229
|
response.data.on('data', (chunk) => {
|
|
229
230
|
downloadedBytes += chunk.length;
|