@lmcc-dev/mult-fetch-mcp-server 1.0.3 → 1.2.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.
- package/README.md +11 -9
- package/README.zh.md +19 -10
- package/dist/i18n-test-report.json +8 -0
- package/dist/i18n-unused-keys-report.json +8 -0
- package/dist/src/client.js +75 -62
- package/dist/src/index.js +0 -0
- package/dist/src/lib/BrowserFetcher.js +12 -12
- package/dist/src/lib/NodeFetcher.js +165 -106
- package/dist/src/lib/fetch.js +40 -0
- package/dist/src/lib/fetchers/FetcherFactory.js +43 -0
- package/dist/src/lib/fetchers/browser/BrowserFetcher.js +381 -0
- package/dist/src/lib/fetchers/browser/BrowserInstance.js +180 -0
- package/dist/src/lib/fetchers/browser/PageOperations.js +168 -0
- package/dist/src/lib/fetchers/common/types.js +41 -0
- package/dist/src/lib/fetchers/common/utils.js +141 -0
- package/dist/src/lib/fetchers/index.js +62 -0
- package/dist/src/lib/fetchers/node/HttpClient.js +239 -0
- package/dist/src/lib/fetchers/node/NodeFetcher.js +180 -0
- package/dist/src/lib/i18n/index.js +105 -17
- package/dist/src/lib/i18n/keys/base.js +41 -0
- package/dist/src/lib/i18n/keys/browser.js +91 -0
- package/dist/src/lib/i18n/keys/client.js +68 -0
- package/dist/src/lib/i18n/keys/errors.js +49 -0
- package/dist/src/lib/i18n/keys/factory.js +46 -0
- package/dist/src/lib/i18n/keys/fetcher.js +87 -0
- package/dist/src/lib/i18n/keys/index.js +16 -0
- package/dist/src/lib/i18n/keys/node.js +69 -0
- package/dist/src/lib/i18n/keys/prompts.js +79 -0
- package/dist/src/lib/i18n/keys/resources.js +53 -0
- package/dist/src/lib/i18n/keys/server.js +67 -0
- package/dist/src/lib/i18n/keys/tools.js +39 -0
- package/dist/src/lib/i18n/keys.js +504 -0
- package/dist/src/lib/i18n/locales/en/browser.js +79 -55
- package/dist/src/lib/i18n/locales/en/client.js +57 -33
- package/dist/src/lib/i18n/locales/en/errors.js +36 -12
- package/dist/src/lib/i18n/locales/en/factory.js +38 -0
- package/dist/src/lib/i18n/locales/en/fetcher.js +67 -60
- package/dist/src/lib/i18n/locales/en/index.js +9 -7
- package/dist/src/lib/i18n/locales/en/node.js +57 -35
- package/dist/src/lib/i18n/locales/en/prompts.js +42 -65
- package/dist/src/lib/i18n/locales/en/resources.js +52 -40
- package/dist/src/lib/i18n/locales/en/server.js +49 -24
- package/dist/src/lib/i18n/locales/en/tools.js +27 -19
- package/dist/src/lib/i18n/locales/zh/browser.js +79 -56
- package/dist/src/lib/i18n/locales/zh/client.js +57 -32
- package/dist/src/lib/i18n/locales/zh/errors.js +36 -12
- package/dist/src/lib/i18n/locales/zh/factory.js +38 -0
- package/dist/src/lib/i18n/locales/zh/fetcher.js +67 -60
- package/dist/src/lib/i18n/locales/zh/index.js +9 -7
- package/dist/src/lib/i18n/locales/zh/node.js +61 -36
- package/dist/src/lib/i18n/locales/zh/prompts.js +35 -57
- package/dist/src/lib/i18n/locales/zh/resources.js +51 -39
- package/dist/src/lib/i18n/locales/zh/server.js +51 -25
- package/dist/src/lib/i18n/locales/zh/tools.js +27 -19
- package/dist/src/lib/i18n/logger.js +90 -19
- package/dist/src/lib/logger.js +135 -14
- package/dist/src/lib/server/browser.js +12 -18
- package/dist/src/lib/server/fetcher.js +13 -34
- package/dist/src/lib/server/index.js +2 -2
- package/dist/src/lib/server/prompts.js +1 -1
- package/dist/src/lib/server/resources.js +1 -1
- package/dist/src/lib/server/tools.js +404 -308
- package/dist/src/lib/utils/ErrorHandler.js +125 -0
- package/dist/src/lib/utils/errorDetection.js +41 -0
- package/dist/src/lib/utils/errors.js +279 -0
- package/dist/src/mcp-server.js +0 -0
- package/dist/src/test-i18n.js +139 -0
- package/dist/tests/BrowserFetcher.test.js +951 -0
- package/dist/tests/NodeFetcher.test.js +263 -0
- package/dist/tests/client.test.js +211 -0
- package/dist/tests/fetch.test.js +70 -0
- package/dist/tests/fetchers/Fetcher.test.js +108 -0
- package/dist/tests/fetchers/browser/BrowserFetcher.test.js +195 -0
- package/dist/tests/fetchers/browser/BrowserInstance.test.js +163 -0
- package/dist/tests/fetchers/node/NodeFetcher.test.js +211 -0
- package/dist/tests/i18n-missing-keys.js +364 -0
- package/dist/tests/i18n-remove-unused-keys.js +236 -0
- package/dist/tests/i18n-test-report.json +2004 -0
- package/dist/tests/i18n-unused-keys.js +425 -0
- package/dist/tests/i18n.test.js +178 -0
- package/dist/tests/logger.test.js +100 -0
- package/dist/tests/mcp-server.test.js +113 -0
- package/dist/tests/server/browser.test.js +201 -0
- package/dist/tests/server/fetcher.test.js +173 -0
- package/dist/tests/server/index.test.js +122 -0
- package/dist/tests/server/prompts.test.js +246 -0
- package/dist/tests/server/resources.test.js +395 -0
- package/dist/tests/server/tools.test.js +223 -0
- package/dist/tests/setup.js +36 -0
- package/dist/tests/src/lib/i18n/index.js +108 -0
- package/dist/tests/src/lib/i18n/keys/base.js +47 -0
- package/dist/tests/src/lib/i18n/keys/browser.js +93 -0
- package/dist/tests/src/lib/i18n/keys/client.js +70 -0
- package/dist/tests/src/lib/i18n/keys/errors.js +34 -0
- package/dist/tests/src/lib/i18n/keys/fetcher.js +84 -0
- package/dist/tests/src/lib/i18n/keys/index.js +31 -0
- package/dist/tests/src/lib/i18n/keys/node.js +56 -0
- package/dist/tests/src/lib/i18n/keys/prompts.js +82 -0
- package/dist/tests/src/lib/i18n/keys/resources.js +50 -0
- package/dist/tests/src/lib/i18n/keys/server.js +64 -0
- package/dist/tests/src/lib/i18n/keys/tools.js +34 -0
- package/dist/tests/src/lib/i18n/locales/en/browser.js +88 -0
- package/dist/tests/src/lib/i18n/locales/en/client.js +66 -0
- package/dist/tests/src/lib/i18n/locales/en/errors.js +28 -0
- package/dist/tests/src/lib/i18n/locales/en/fetcher.js +71 -0
- package/dist/tests/src/lib/i18n/locales/en/index.js +29 -0
- package/dist/tests/src/lib/i18n/locales/en/node.js +51 -0
- package/dist/tests/src/lib/i18n/locales/en/prompts.js +52 -0
- package/dist/tests/src/lib/i18n/locales/en/resources.js +50 -0
- package/dist/tests/src/lib/i18n/locales/en/server.js +56 -0
- package/dist/tests/src/lib/i18n/locales/en/tools.js +28 -0
- package/dist/tests/src/lib/i18n/locales/zh/browser.js +87 -0
- package/dist/tests/src/lib/i18n/locales/zh/client.js +66 -0
- package/dist/tests/src/lib/i18n/locales/zh/errors.js +28 -0
- package/dist/tests/src/lib/i18n/locales/zh/fetcher.js +71 -0
- package/dist/tests/src/lib/i18n/locales/zh/index.js +29 -0
- package/dist/tests/src/lib/i18n/locales/zh/node.js +51 -0
- package/dist/tests/src/lib/i18n/locales/zh/prompts.js +53 -0
- package/dist/tests/src/lib/i18n/locales/zh/resources.js +50 -0
- package/dist/tests/src/lib/i18n/locales/zh/server.js +57 -0
- package/dist/tests/src/lib/i18n/locales/zh/tools.js +28 -0
- package/dist/tests/src/lib/i18n/logger.js +114 -0
- package/dist/tests/src/lib/logger.js +181 -0
- package/dist/tests/test-direct-client.js +0 -0
- package/dist/tests/test-i18n.js +530 -0
- package/dist/tests/test-mcp-methods.js +1 -1
- package/dist/tests/test-mcp.js +0 -0
- package/dist/tests/test-mini4k.js +0 -0
- package/dist/tests/tests/test-i18n.js +588 -0
- package/dist/tests/types.test.js +57 -0
- package/dist/tests/utils/ErrorHandler.test.js +144 -0
- package/dist/vitest.config.js +29 -0
- package/package.json +32 -19
- package/dist/src/cli-language.js +0 -78
- package/dist/src/lib/example.js +0 -93
- package/dist/src/lib/server/logger.js +0 -23
|
@@ -4,6 +4,65 @@
|
|
|
4
4
|
* Description: This code was collaboratively developed by Martin and AI Assistant.
|
|
5
5
|
*/
|
|
6
6
|
import { t } from './index.js';
|
|
7
|
+
/**
|
|
8
|
+
* 日志级别枚举 (Log level enum)
|
|
9
|
+
*/
|
|
10
|
+
export var LogLevel;
|
|
11
|
+
(function (LogLevel) {
|
|
12
|
+
LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
|
|
13
|
+
LogLevel[LogLevel["INFO"] = 1] = "INFO";
|
|
14
|
+
LogLevel[LogLevel["WARN"] = 2] = "WARN";
|
|
15
|
+
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
|
|
16
|
+
LogLevel[LogLevel["NONE"] = 4] = "NONE";
|
|
17
|
+
})(LogLevel || (LogLevel = {}));
|
|
18
|
+
/**
|
|
19
|
+
* 获取当前日志级别 (Get current log level)
|
|
20
|
+
* @returns 日志级别 (Log level)
|
|
21
|
+
*/
|
|
22
|
+
function getCurrentLogLevel() {
|
|
23
|
+
const debugEnv = process.env.DEBUG;
|
|
24
|
+
if (debugEnv === 'none')
|
|
25
|
+
return LogLevel.NONE;
|
|
26
|
+
if (debugEnv === 'error')
|
|
27
|
+
return LogLevel.ERROR;
|
|
28
|
+
if (debugEnv === 'warn')
|
|
29
|
+
return LogLevel.WARN;
|
|
30
|
+
if (debugEnv === 'info')
|
|
31
|
+
return LogLevel.INFO;
|
|
32
|
+
if (debugEnv === 'debug' || debugEnv === 'true')
|
|
33
|
+
return LogLevel.DEBUG;
|
|
34
|
+
// 默认为 INFO 级别 (Default to INFO level)
|
|
35
|
+
return LogLevel.INFO;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 尝试翻译键名 (Try to translate a key)
|
|
39
|
+
* @param key 翻译键 (Translation key)
|
|
40
|
+
* @param params 翻译参数 (Translation parameters)
|
|
41
|
+
* @returns 翻译后的文本 (Translated text)
|
|
42
|
+
*/
|
|
43
|
+
function translateKey(key, params) {
|
|
44
|
+
try {
|
|
45
|
+
// 尝试翻译键名 (Try to translate the key)
|
|
46
|
+
const translated = t(key, params);
|
|
47
|
+
// 确保结果是字符串 (Ensure the result is a string)
|
|
48
|
+
let translatedStr = typeof translated === 'string' ? translated : JSON.stringify(translated);
|
|
49
|
+
// 检查翻译后的字符串是否仍然包含占位符,如果包含,手动进行插值替换
|
|
50
|
+
// (Check if the translated string still contains placeholders, if so, manually perform interpolation)
|
|
51
|
+
if (params && typeof translatedStr === 'string') {
|
|
52
|
+
for (const key in params) {
|
|
53
|
+
const placeholder = `{{${key}}}`;
|
|
54
|
+
if (translatedStr.includes(placeholder)) {
|
|
55
|
+
translatedStr = translatedStr.replace(new RegExp(placeholder, 'g'), String(params[key]));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return translatedStr;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
// 如果翻译失败,返回原始键名 (If translation fails, return the original key)
|
|
63
|
+
return key;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
7
66
|
/**
|
|
8
67
|
* 创建一个带前缀的日志记录器 (Create a logger with prefix)
|
|
9
68
|
* @param prefix 日志前缀 (Log prefix)
|
|
@@ -12,25 +71,34 @@ import { t } from './index.js';
|
|
|
12
71
|
export function createLogger(prefix) {
|
|
13
72
|
return {
|
|
14
73
|
/**
|
|
15
|
-
*
|
|
74
|
+
* 输出调试日志 (Output debug log)
|
|
16
75
|
* @param key 翻译键 (Translation key)
|
|
17
76
|
* @param params 翻译参数 (Translation parameters)
|
|
18
|
-
* @param
|
|
77
|
+
* @param forceLog 是否强制输出日志 (Whether to force output logs)
|
|
19
78
|
*/
|
|
20
|
-
|
|
21
|
-
//
|
|
22
|
-
if (
|
|
23
|
-
|
|
79
|
+
debug: (key, params, forceLog) => {
|
|
80
|
+
// 只有在明确设置 forceLog 为 true 时才输出日志 (Only output logs when forceLog is explicitly set to true)
|
|
81
|
+
if (forceLog) {
|
|
82
|
+
const translatedMessage = translateKey(key, params);
|
|
83
|
+
// 在 MCP 环境中,所有日志都应该使用 error 级别输出
|
|
84
|
+
// (In MCP environment, all logs should be output at error level)
|
|
85
|
+
console.error(`[${prefix}] ${translatedMessage}`);
|
|
24
86
|
}
|
|
25
87
|
},
|
|
26
88
|
/**
|
|
27
|
-
*
|
|
89
|
+
* 输出信息日志 (Output info log)
|
|
28
90
|
* @param key 翻译键 (Translation key)
|
|
29
91
|
* @param params 翻译参数 (Translation parameters)
|
|
92
|
+
* @param forceLog 是否强制输出日志 (Whether to force output logs)
|
|
30
93
|
*/
|
|
31
|
-
|
|
32
|
-
//
|
|
33
|
-
|
|
94
|
+
info: (key, params, forceLog) => {
|
|
95
|
+
// 只有在明确设置 forceLog 为 true 时才输出日志 (Only output logs when forceLog is explicitly set to true)
|
|
96
|
+
if (forceLog) {
|
|
97
|
+
const translatedMessage = translateKey(key, params);
|
|
98
|
+
// 在 MCP 环境中,所有日志都应该使用 error 级别输出
|
|
99
|
+
// (In MCP environment, all logs should be output at error level)
|
|
100
|
+
console.error(`[${prefix}] ${translatedMessage}`);
|
|
101
|
+
}
|
|
34
102
|
},
|
|
35
103
|
/**
|
|
36
104
|
* 输出警告日志 (Output warning log)
|
|
@@ -38,20 +106,23 @@ export function createLogger(prefix) {
|
|
|
38
106
|
* @param params 翻译参数 (Translation parameters)
|
|
39
107
|
*/
|
|
40
108
|
warn: (key, params) => {
|
|
41
|
-
//
|
|
42
|
-
|
|
109
|
+
// 警告日志总是输出 (Warning logs are always output)
|
|
110
|
+
const translatedMessage = translateKey(key, params);
|
|
111
|
+
// 在 MCP 环境中,所有日志都应该使用 error 级别输出
|
|
112
|
+
// (In MCP environment, all logs should be output at error level)
|
|
113
|
+
console.error(`[${prefix}] ${translatedMessage}`);
|
|
43
114
|
},
|
|
44
115
|
/**
|
|
45
|
-
*
|
|
116
|
+
* 输出错误日志 (Output error log)
|
|
46
117
|
* @param key 翻译键 (Translation key)
|
|
47
118
|
* @param params 翻译参数 (Translation parameters)
|
|
48
|
-
* @param forceDebug 是否强制输出调试日志 (Whether to force output debug logs)
|
|
49
119
|
*/
|
|
50
|
-
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
120
|
+
error: (key, params) => {
|
|
121
|
+
// 错误日志总是输出 (Error logs are always output)
|
|
122
|
+
const translatedMessage = translateKey(key, params);
|
|
123
|
+
// 在 MCP 环境中,所有日志都应该使用 error 级别输出
|
|
124
|
+
// (In MCP environment, all logs should be output at error level)
|
|
125
|
+
console.error(`[${prefix}] ${translatedMessage}`);
|
|
55
126
|
}
|
|
56
127
|
};
|
|
57
128
|
}
|
package/dist/src/lib/logger.js
CHANGED
|
@@ -8,6 +8,30 @@ import { t } from './i18n/index.js';
|
|
|
8
8
|
import fs from 'fs';
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import os from 'os';
|
|
11
|
+
// 导入所有翻译文件 (Import all translation files)
|
|
12
|
+
import serverZh from './i18n/locales/zh/server.js';
|
|
13
|
+
import clientZh from './i18n/locales/zh/client.js';
|
|
14
|
+
import toolsZh from './i18n/locales/zh/tools.js';
|
|
15
|
+
import browserZh from './i18n/locales/zh/browser.js';
|
|
16
|
+
import nodeZh from './i18n/locales/zh/node.js';
|
|
17
|
+
import fetcherZh from './i18n/locales/zh/fetcher.js';
|
|
18
|
+
import factoryZh from './i18n/locales/zh/factory.js';
|
|
19
|
+
import errorsZh from './i18n/locales/zh/errors.js';
|
|
20
|
+
import resourcesZh from './i18n/locales/zh/resources.js';
|
|
21
|
+
import promptsZh from './i18n/locales/zh/prompts.js';
|
|
22
|
+
// 创建翻译映射 (Create translation mapping)
|
|
23
|
+
const translationMap = {
|
|
24
|
+
server: serverZh,
|
|
25
|
+
client: clientZh,
|
|
26
|
+
tools: toolsZh,
|
|
27
|
+
browser: browserZh,
|
|
28
|
+
node: nodeZh,
|
|
29
|
+
fetcher: fetcherZh,
|
|
30
|
+
factory: factoryZh,
|
|
31
|
+
errors: errorsZh,
|
|
32
|
+
resources: resourcesZh,
|
|
33
|
+
prompts: promptsZh
|
|
34
|
+
};
|
|
11
35
|
// 组件名称常量 (Component name constants)
|
|
12
36
|
export const COMPONENTS = {
|
|
13
37
|
SERVER: 'MCP-SERVER',
|
|
@@ -16,7 +40,8 @@ export const COMPONENTS = {
|
|
|
16
40
|
CLIENT: 'CLIENT',
|
|
17
41
|
RESOURCES: 'MCP-RESOURCES',
|
|
18
42
|
PROMPTS: 'MCP-PROMPTS',
|
|
19
|
-
TOOLS: 'MCP-TOOLS'
|
|
43
|
+
TOOLS: 'MCP-TOOLS',
|
|
44
|
+
FETCHER_FACTORY: 'FETCHER-FACTORY'
|
|
20
45
|
};
|
|
21
46
|
// 日志文件路径 (Log file path)
|
|
22
47
|
const LOG_DIR = path.join(os.homedir(), '.mult-fetch-mcp-server');
|
|
@@ -32,6 +57,8 @@ catch (error) {
|
|
|
32
57
|
}
|
|
33
58
|
// 组件日志记录器缓存 (Component logger cache)
|
|
34
59
|
const loggerCache = {};
|
|
60
|
+
// 翻译缓存,避免重复翻译相同的键名 (Translation cache to avoid repeated translation of the same key)
|
|
61
|
+
const translationCache = {};
|
|
35
62
|
/**
|
|
36
63
|
* 获取指定组件的日志记录器 (Get logger for specified component)
|
|
37
64
|
* @param component 组件名称 (Component name)
|
|
@@ -93,6 +120,101 @@ export function clearLogFile() {
|
|
|
93
120
|
export function getLogFilePath() {
|
|
94
121
|
return LOG_FILE;
|
|
95
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* 尝试翻译键名 (Try to translate a key)
|
|
125
|
+
* @param key 翻译键 (Translation key)
|
|
126
|
+
* @param options 翻译选项 (Translation options)
|
|
127
|
+
* @param component 组件名称 (Component name)
|
|
128
|
+
* @returns 翻译后的文本 (Translated text)
|
|
129
|
+
*/
|
|
130
|
+
function translateKey(key, options, component = COMPONENTS.SERVER) {
|
|
131
|
+
// 如果没有选项,尝试使用缓存 (If no options, try to use cache)
|
|
132
|
+
if (!options || Object.keys(options).length === 0) {
|
|
133
|
+
// 生成缓存键 (Generate cache key)
|
|
134
|
+
const cacheKey = `${key}`;
|
|
135
|
+
// 如果缓存中存在该键,直接返回缓存的翻译 (If the key exists in cache, return the cached translation)
|
|
136
|
+
if (translationCache[cacheKey]) {
|
|
137
|
+
return translationCache[cacheKey];
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
// 尝试翻译键名 (Try to translate the key)
|
|
141
|
+
const translated = t(key, options);
|
|
142
|
+
// 确保结果是字符串 (Ensure the result is a string)
|
|
143
|
+
const translatedStr = typeof translated === 'string' ? translated : JSON.stringify(translated);
|
|
144
|
+
// 如果翻译结果与键名相同,可能是嵌套键未被正确处理 (If translation result is the same as key, nested key may not be handled correctly)
|
|
145
|
+
if (translatedStr === key) {
|
|
146
|
+
// 尝试手动处理嵌套键 (Try to manually handle nested keys)
|
|
147
|
+
const parts = key.split('.');
|
|
148
|
+
if (parts.length > 1) {
|
|
149
|
+
const namespace = parts[0];
|
|
150
|
+
const subKey = parts.slice(1).join('.');
|
|
151
|
+
// 根据命名空间获取对应的翻译对象 (Get translation object based on namespace)
|
|
152
|
+
const translation = translationMap[namespace];
|
|
153
|
+
// 如果找到了翻译对象,尝试获取子键的翻译 (If translation object is found, try to get translation of subkey)
|
|
154
|
+
if (translation && translation[subKey]) {
|
|
155
|
+
// 将翻译结果存入缓存 (Store the translation result in cache)
|
|
156
|
+
translationCache[cacheKey] = translation[subKey];
|
|
157
|
+
return translation[subKey];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// 将翻译结果存入缓存 (Store the translation result in cache)
|
|
162
|
+
translationCache[cacheKey] = translatedStr;
|
|
163
|
+
return translatedStr;
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
// 如果翻译失败,返回原始键名 (If translation fails, return the original key)
|
|
167
|
+
return key;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// 如果有选项,不使用缓存 (If there are options, don't use cache)
|
|
172
|
+
try {
|
|
173
|
+
// 尝试翻译键名 (Try to translate the key)
|
|
174
|
+
const translated = t(key, options);
|
|
175
|
+
// 确保结果是字符串 (Ensure the result is a string)
|
|
176
|
+
let translatedStr = typeof translated === 'string' ? translated : JSON.stringify(translated);
|
|
177
|
+
// 检查翻译后的字符串是否仍然包含占位符,如果包含,手动进行插值替换
|
|
178
|
+
// (Check if the translated string still contains placeholders, if so, manually perform interpolation)
|
|
179
|
+
if (options && typeof translatedStr === 'string') {
|
|
180
|
+
for (const key in options) {
|
|
181
|
+
const placeholder = `{{${key}}}`;
|
|
182
|
+
if (translatedStr.includes(placeholder)) {
|
|
183
|
+
translatedStr = translatedStr.replace(new RegExp(placeholder, 'g'), String(options[key]));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// 如果翻译结果与键名相同,可能是嵌套键未被正确处理 (If translation result is the same as key, nested key may not be handled correctly)
|
|
188
|
+
if (translatedStr === key) {
|
|
189
|
+
// 尝试手动处理嵌套键 (Try to manually handle nested keys)
|
|
190
|
+
const parts = key.split('.');
|
|
191
|
+
if (parts.length > 1) {
|
|
192
|
+
const namespace = parts[0];
|
|
193
|
+
const subKey = parts.slice(1).join('.');
|
|
194
|
+
// 根据命名空间获取对应的翻译对象 (Get translation object based on namespace)
|
|
195
|
+
const translation = translationMap[namespace];
|
|
196
|
+
// 如果找到了翻译对象,尝试获取子键的翻译 (If translation object is found, try to get translation of subkey)
|
|
197
|
+
if (translation && translation[subKey]) {
|
|
198
|
+
// 如果有选项,进行插值 (If there are options, do interpolation)
|
|
199
|
+
let result = translation[subKey];
|
|
200
|
+
for (const key in options) {
|
|
201
|
+
const placeholder = `{{${key}}}`;
|
|
202
|
+
if (result.includes(placeholder)) {
|
|
203
|
+
result = result.replace(new RegExp(placeholder, 'g'), String(options[key]));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return translatedStr;
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
// 如果翻译失败,返回原始键名 (If translation fails, return the original key)
|
|
214
|
+
return key;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
96
218
|
/**
|
|
97
219
|
* 统一日志函数 (Unified log function)
|
|
98
220
|
* 输出调试信息到标准错误流和日志文件 (Output debug information to standard error stream and log file)
|
|
@@ -108,19 +230,18 @@ export function log(key, debug = false, options, component = COMPONENTS.SERVER)
|
|
|
108
230
|
}
|
|
109
231
|
// 获取组件的日志记录器 (Get logger for component)
|
|
110
232
|
const logger = getLogger(component);
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
try {
|
|
116
|
-
const translated = t(key, options);
|
|
117
|
-
// 确保 translatedMessage 是字符串 (Ensure translatedMessage is a string)
|
|
118
|
-
translatedMessage = typeof translated === 'string' ? translated : JSON.stringify(translated);
|
|
119
|
-
}
|
|
120
|
-
catch (error) {
|
|
121
|
-
// 如果翻译失败,使用原始消息 (If translation fails, use original message)
|
|
122
|
-
translatedMessage = typeof key === 'string' ? key : JSON.stringify(key);
|
|
123
|
-
}
|
|
233
|
+
// 翻译键名 (Translate the key)
|
|
234
|
+
const translatedMessage = translateKey(key, options, component);
|
|
235
|
+
// 在 MCP 环境中,所有日志都应该使用 error 级别输出 (In MCP environment, all logs should be output at error level)
|
|
236
|
+
console.error(`[${component}] ${translatedMessage}`);
|
|
124
237
|
// 写入日志文件 (Write to log file)
|
|
125
238
|
writeToLogFile(component, translatedMessage);
|
|
126
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* 清除翻译缓存 (Clear translation cache)
|
|
242
|
+
*/
|
|
243
|
+
export function clearTranslationCache() {
|
|
244
|
+
Object.keys(translationCache).forEach(key => {
|
|
245
|
+
delete translationCache[key];
|
|
246
|
+
});
|
|
247
|
+
}
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* Co-Author: AI Assistant (Claude)
|
|
4
4
|
* Description: This code was collaboratively developed by Martin and AI Assistant.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { BrowserInstance } from '../fetchers/browser/BrowserInstance.js';
|
|
7
7
|
import { log, COMPONENTS } from '../logger.js';
|
|
8
|
+
import { isAccessDeniedError, isNetworkError } from '../utils/errorDetection.js';
|
|
8
9
|
// 全局浏览器实例 (Global browser instance)
|
|
9
10
|
let browserInitialized = false;
|
|
10
11
|
/**
|
|
@@ -33,32 +34,25 @@ export async function closeBrowserInstance(debug = false) {
|
|
|
33
34
|
if (debug) {
|
|
34
35
|
log('server.closingBrowser', debug, {}, COMPONENTS.SERVER);
|
|
35
36
|
}
|
|
36
|
-
await
|
|
37
|
+
await BrowserInstance.closeBrowser(debug);
|
|
37
38
|
browserInitialized = false;
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
/**
|
|
41
|
-
*
|
|
42
|
-
* 根据错误信息判断是否需要浏览器模式 (
|
|
42
|
+
* 判断是否应该切换到浏览器模式 (Determine if should switch to browser mode)
|
|
43
|
+
* 根据错误信息判断是否需要浏览器模式 (Based on error message to determine if browser mode is needed)
|
|
43
44
|
* @param error 错误对象 (Error object)
|
|
44
|
-
* @returns 是否应该切换到浏览器模式 (Whether
|
|
45
|
+
* @returns 是否应该切换到浏览器模式 (Whether should switch to browser mode)
|
|
45
46
|
*/
|
|
46
47
|
export function shouldSwitchToBrowser(error) {
|
|
47
48
|
// 检查错误是否表明需要浏览器模式 (Check if error indicates browser mode is needed)
|
|
48
49
|
if (error && error.message) {
|
|
49
|
-
const errorMessage = error.message
|
|
50
|
-
return errorMessage
|
|
51
|
-
errorMessage
|
|
52
|
-
errorMessage.includes('
|
|
53
|
-
errorMessage.includes('
|
|
54
|
-
errorMessage.includes('
|
|
55
|
-
errorMessage.includes('javascript required') ||
|
|
56
|
-
errorMessage.includes('timeout') ||
|
|
57
|
-
errorMessage.includes('connect timeout') ||
|
|
58
|
-
errorMessage.includes('socket') ||
|
|
59
|
-
errorMessage.includes('econnrefused') ||
|
|
60
|
-
errorMessage.includes('und_err_connect_timeout') ||
|
|
61
|
-
errorMessage.includes('fetch failed');
|
|
50
|
+
const errorMessage = error.message;
|
|
51
|
+
return isAccessDeniedError(errorMessage) ||
|
|
52
|
+
isNetworkError(errorMessage) ||
|
|
53
|
+
errorMessage.toLowerCase().includes('javascript required') ||
|
|
54
|
+
errorMessage.toLowerCase().includes('und_err_connect_timeout') ||
|
|
55
|
+
errorMessage.toLowerCase().includes('fetch failed');
|
|
62
56
|
}
|
|
63
57
|
return false;
|
|
64
58
|
}
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* Co-Author: AI Assistant (Claude)
|
|
4
4
|
* Description: This code was collaboratively developed by Martin and AI Assistant.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import { NodeFetcher } from '../NodeFetcher.js';
|
|
6
|
+
import { Fetcher } from '../fetch.js';
|
|
8
7
|
import { log, COMPONENTS } from '../logger.js';
|
|
9
8
|
import { initializeBrowser, closeBrowserInstance, shouldSwitchToBrowser } from './browser.js';
|
|
10
9
|
/**
|
|
@@ -32,13 +31,13 @@ export async function fetchWithAutoDetect(params, type) {
|
|
|
32
31
|
// 根据类型选择合适的浏览器获取方法 (Choose appropriate browser fetching method based on type)
|
|
33
32
|
switch (type) {
|
|
34
33
|
case 'html':
|
|
35
|
-
return await
|
|
34
|
+
return await Fetcher.html(params);
|
|
36
35
|
case 'json':
|
|
37
|
-
return await
|
|
36
|
+
return await Fetcher.json(params);
|
|
38
37
|
case 'txt':
|
|
39
|
-
return await
|
|
38
|
+
return await Fetcher.txt(params);
|
|
40
39
|
case 'markdown':
|
|
41
|
-
return await
|
|
40
|
+
return await Fetcher.markdown(params);
|
|
42
41
|
}
|
|
43
42
|
}
|
|
44
43
|
else {
|
|
@@ -50,36 +49,16 @@ export async function fetchWithAutoDetect(params, type) {
|
|
|
50
49
|
// 根据类型选择合适的标准获取方法 (Choose appropriate standard fetching method based on type)
|
|
51
50
|
switch (type) {
|
|
52
51
|
case 'html': {
|
|
53
|
-
|
|
54
|
-
// 转换为统一格式 (Convert to unified format)
|
|
55
|
-
return {
|
|
56
|
-
content: [{ type: 'text', text: result.html }],
|
|
57
|
-
isError: false
|
|
58
|
-
};
|
|
52
|
+
return await Fetcher.html(params);
|
|
59
53
|
}
|
|
60
54
|
case 'json': {
|
|
61
|
-
|
|
62
|
-
// 转换为统一格式 (Convert to unified format)
|
|
63
|
-
return {
|
|
64
|
-
content: [{ type: 'text', text: typeof result.json === 'string' ? result.json : JSON.stringify(result.json, null, 2) }],
|
|
65
|
-
isError: false
|
|
66
|
-
};
|
|
55
|
+
return await Fetcher.json(params);
|
|
67
56
|
}
|
|
68
57
|
case 'txt': {
|
|
69
|
-
|
|
70
|
-
// 转换为统一格式 (Convert to unified format)
|
|
71
|
-
return {
|
|
72
|
-
content: [{ type: 'text', text: result.text }],
|
|
73
|
-
isError: false
|
|
74
|
-
};
|
|
58
|
+
return await Fetcher.txt(params);
|
|
75
59
|
}
|
|
76
60
|
case 'markdown': {
|
|
77
|
-
|
|
78
|
-
// 转换为统一格式 (Convert to unified format)
|
|
79
|
-
return {
|
|
80
|
-
content: [{ type: 'text', text: result.markdown }],
|
|
81
|
-
isError: false
|
|
82
|
-
};
|
|
61
|
+
return await Fetcher.markdown(params);
|
|
83
62
|
}
|
|
84
63
|
}
|
|
85
64
|
}
|
|
@@ -97,13 +76,13 @@ export async function fetchWithAutoDetect(params, type) {
|
|
|
97
76
|
// 根据类型选择合适的浏览器获取方法
|
|
98
77
|
switch (type) {
|
|
99
78
|
case 'html':
|
|
100
|
-
return await
|
|
79
|
+
return await Fetcher.html(params);
|
|
101
80
|
case 'json':
|
|
102
|
-
return await
|
|
81
|
+
return await Fetcher.json(params);
|
|
103
82
|
case 'txt':
|
|
104
|
-
return await
|
|
83
|
+
return await Fetcher.txt(params);
|
|
105
84
|
case 'markdown':
|
|
106
|
-
return await
|
|
85
|
+
return await Fetcher.markdown(params);
|
|
107
86
|
}
|
|
108
87
|
}
|
|
109
88
|
// 如果不需要切换到浏览器模式,则抛出原始错误
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Co-Author: AI Assistant (Claude)
|
|
4
4
|
* Description: This code was collaboratively developed by Martin and AI Assistant.
|
|
5
5
|
*/
|
|
6
|
-
import { Server } from
|
|
7
|
-
import { StdioServerTransport } from
|
|
6
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
7
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
8
8
|
import { log, COMPONENTS } from '../logger.js';
|
|
9
9
|
import { closeBrowserInstance } from './browser.js';
|
|
10
10
|
import { registerTools } from './tools.js';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Co-Author: AI Assistant (Claude)
|
|
4
4
|
* Description: This code was collaboratively developed by Martin and AI Assistant.
|
|
5
5
|
*/
|
|
6
|
-
import { ListPromptsRequestSchema, GetPromptRequestSchema } from
|
|
6
|
+
import { ListPromptsRequestSchema, GetPromptRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
7
7
|
import { log, COMPONENTS } from '../logger.js';
|
|
8
8
|
import { t } from '../i18n/index.js';
|
|
9
9
|
// 定义提示模板 (Define prompt templates)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Co-Author: AI Assistant (Claude)
|
|
4
4
|
* Description: This code was collaboratively developed by Martin and AI Assistant.
|
|
5
5
|
*/
|
|
6
|
-
import { ListResourcesRequestSchema, ReadResourceRequestSchema } from
|
|
6
|
+
import { ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
7
7
|
import { log, getLogFilePath, clearLogFile, COMPONENTS } from '../logger.js';
|
|
8
8
|
import fs from 'fs';
|
|
9
9
|
import path from 'path';
|