@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
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Author: Martin <lmccc.dev@gmail.com>
|
|
3
|
+
* Co-Author: AI Assistant (Claude)
|
|
4
|
+
* Description: This code was collaboratively developed by Martin and AI Assistant.
|
|
5
|
+
*/
|
|
6
|
+
import { isAccessDeniedError, isNetworkError } from './errorDetection.js';
|
|
7
|
+
import { log, COMPONENTS } from '../logger.js';
|
|
8
|
+
/**
|
|
9
|
+
* 错误类型枚举
|
|
10
|
+
* (Error type enumeration)
|
|
11
|
+
*/
|
|
12
|
+
export var ErrorType;
|
|
13
|
+
(function (ErrorType) {
|
|
14
|
+
ErrorType["NETWORK"] = "network";
|
|
15
|
+
ErrorType["ACCESS_DENIED"] = "access_denied";
|
|
16
|
+
ErrorType["TIMEOUT"] = "timeout";
|
|
17
|
+
ErrorType["PARSE"] = "parse";
|
|
18
|
+
ErrorType["VALIDATION"] = "validation";
|
|
19
|
+
ErrorType["BROWSER"] = "browser";
|
|
20
|
+
ErrorType["UNKNOWN"] = "unknown";
|
|
21
|
+
})(ErrorType || (ErrorType = {}));
|
|
22
|
+
/**
|
|
23
|
+
* 错误处理器类,用于统一处理和分类错误
|
|
24
|
+
* (Error handler class for unified error handling and classification)
|
|
25
|
+
*/
|
|
26
|
+
export class ErrorHandler {
|
|
27
|
+
/**
|
|
28
|
+
* 处理错误并返回格式化的错误消息
|
|
29
|
+
* (Handle error and return formatted error message)
|
|
30
|
+
*
|
|
31
|
+
* @param error 捕获的错误对象 (Caught error object)
|
|
32
|
+
* @param component 组件名称,用于日志 (Component name for logging)
|
|
33
|
+
* @param debug 是否启用调试模式 (Whether debug mode is enabled)
|
|
34
|
+
* @param context 错误上下文信息 (Error context information)
|
|
35
|
+
* @returns 格式化的错误消息 (Formatted error message)
|
|
36
|
+
*/
|
|
37
|
+
static handleError(error, component = COMPONENTS.SERVER, debug = false, context = {}) {
|
|
38
|
+
const errorType = this.classifyError(error);
|
|
39
|
+
const errorMessage = this.getErrorMessage(error);
|
|
40
|
+
// 记录错误日志
|
|
41
|
+
// (Log error)
|
|
42
|
+
log(`errors.${errorType}_error`, debug, {
|
|
43
|
+
error: errorMessage,
|
|
44
|
+
...context
|
|
45
|
+
}, component);
|
|
46
|
+
return errorMessage;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 分类错误类型
|
|
50
|
+
* (Classify error type)
|
|
51
|
+
*
|
|
52
|
+
* @param error 捕获的错误对象 (Caught error object)
|
|
53
|
+
* @returns 错误类型 (Error type)
|
|
54
|
+
*/
|
|
55
|
+
static classifyError(error) {
|
|
56
|
+
const errorMessage = this.getErrorMessage(error);
|
|
57
|
+
// 先检查超时错误 (Check timeout errors first)
|
|
58
|
+
if (errorMessage.toLowerCase().includes('timeout')) {
|
|
59
|
+
return ErrorType.TIMEOUT;
|
|
60
|
+
}
|
|
61
|
+
if (isNetworkError(errorMessage)) {
|
|
62
|
+
return ErrorType.NETWORK;
|
|
63
|
+
}
|
|
64
|
+
if (isAccessDeniedError(errorMessage)) {
|
|
65
|
+
return ErrorType.ACCESS_DENIED;
|
|
66
|
+
}
|
|
67
|
+
if (errorMessage.toLowerCase().includes('parse') ||
|
|
68
|
+
errorMessage.toLowerCase().includes('json') ||
|
|
69
|
+
errorMessage.toLowerCase().includes('syntax')) {
|
|
70
|
+
return ErrorType.PARSE;
|
|
71
|
+
}
|
|
72
|
+
if (errorMessage.toLowerCase().includes('browser') ||
|
|
73
|
+
errorMessage.toLowerCase().includes('puppeteer') ||
|
|
74
|
+
errorMessage.toLowerCase().includes('page') ||
|
|
75
|
+
errorMessage.toLowerCase().includes('chrome')) {
|
|
76
|
+
return ErrorType.BROWSER;
|
|
77
|
+
}
|
|
78
|
+
if (errorMessage.toLowerCase().includes('valid') ||
|
|
79
|
+
errorMessage.toLowerCase().includes('schema') ||
|
|
80
|
+
errorMessage.toLowerCase().includes('type')) {
|
|
81
|
+
return ErrorType.VALIDATION;
|
|
82
|
+
}
|
|
83
|
+
return ErrorType.UNKNOWN;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 获取错误消息
|
|
87
|
+
* (Get error message)
|
|
88
|
+
*
|
|
89
|
+
* @param error 捕获的错误对象 (Caught error object)
|
|
90
|
+
* @returns 错误消息字符串 (Error message string)
|
|
91
|
+
*/
|
|
92
|
+
static getErrorMessage(error) {
|
|
93
|
+
if (error instanceof Error) {
|
|
94
|
+
return error.message;
|
|
95
|
+
}
|
|
96
|
+
if (typeof error === 'string') {
|
|
97
|
+
return error;
|
|
98
|
+
}
|
|
99
|
+
if (error && typeof error === 'object') {
|
|
100
|
+
if ('message' in error && typeof error.message === 'string') {
|
|
101
|
+
return error.message;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
return JSON.stringify(error);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return String(error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return String(error);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 创建带有错误类型的错误对象
|
|
114
|
+
* (Create error object with error type)
|
|
115
|
+
*
|
|
116
|
+
* @param message 错误消息 (Error message)
|
|
117
|
+
* @param type 错误类型 (Error type)
|
|
118
|
+
* @returns 带有类型的错误对象 (Error object with type)
|
|
119
|
+
*/
|
|
120
|
+
static createError(message, type = ErrorType.UNKNOWN) {
|
|
121
|
+
const error = new Error(message);
|
|
122
|
+
error.type = type;
|
|
123
|
+
return error;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Author: Martin <lmccc.dev@gmail.com>
|
|
3
|
+
* Co-Author: AI Assistant (Claude)
|
|
4
|
+
* Description: This code was collaboratively developed by Martin and AI Assistant.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* 检查错误文本是否表示访问被拒绝或需要人机验证
|
|
8
|
+
* (Check if error text indicates access denied or requires human verification)
|
|
9
|
+
*
|
|
10
|
+
* @param errorText 错误文本 (Error text to check)
|
|
11
|
+
* @returns 是否为访问被拒绝错误 (Whether it's an access denied error)
|
|
12
|
+
*/
|
|
13
|
+
export function isAccessDeniedError(errorText) {
|
|
14
|
+
if (!errorText)
|
|
15
|
+
return false;
|
|
16
|
+
const lowerCaseError = errorText.toLowerCase();
|
|
17
|
+
return lowerCaseError.includes('403') ||
|
|
18
|
+
lowerCaseError.includes('forbidden') ||
|
|
19
|
+
lowerCaseError.includes('access denied') ||
|
|
20
|
+
lowerCaseError.includes('cloudflare') ||
|
|
21
|
+
lowerCaseError.includes('captcha') ||
|
|
22
|
+
lowerCaseError.includes('blocked') ||
|
|
23
|
+
lowerCaseError.includes('security check');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 检查错误文本是否表示网络错误
|
|
27
|
+
* (Check if error text indicates a network error)
|
|
28
|
+
*
|
|
29
|
+
* @param errorText 错误文本 (Error text to check)
|
|
30
|
+
* @returns 是否为网络错误 (Whether it's a network error)
|
|
31
|
+
*/
|
|
32
|
+
export function isNetworkError(errorText) {
|
|
33
|
+
if (!errorText)
|
|
34
|
+
return false;
|
|
35
|
+
const lowerCaseError = errorText.toLowerCase();
|
|
36
|
+
return lowerCaseError.includes('network') ||
|
|
37
|
+
lowerCaseError.includes('connection') ||
|
|
38
|
+
lowerCaseError.includes('timeout') ||
|
|
39
|
+
lowerCaseError.includes('unreachable') ||
|
|
40
|
+
lowerCaseError.includes('econnrefused');
|
|
41
|
+
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Author: Martin <lmccc.dev@gmail.com>
|
|
3
|
+
* Co-Author: AI Assistant (Claude)
|
|
4
|
+
* Description: This code was collaboratively developed by Martin and AI Assistant.
|
|
5
|
+
*/
|
|
6
|
+
import { log, COMPONENTS } from '../logger.js';
|
|
7
|
+
import { isAccessDeniedError, isNetworkError } from './errorDetection.js';
|
|
8
|
+
// 扩展组件常量 (Extend component constants)
|
|
9
|
+
const EXTENDED_COMPONENTS = {
|
|
10
|
+
...COMPONENTS,
|
|
11
|
+
UNKNOWN: 'UNKNOWN',
|
|
12
|
+
BROWSER: 'BROWSER'
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* 错误类型枚举 (Error type enumeration)
|
|
16
|
+
*/
|
|
17
|
+
export var ErrorType;
|
|
18
|
+
(function (ErrorType) {
|
|
19
|
+
// 网络错误 (Network errors)
|
|
20
|
+
ErrorType["NETWORK"] = "NETWORK";
|
|
21
|
+
ErrorType["TIMEOUT"] = "TIMEOUT";
|
|
22
|
+
ErrorType["CONNECTION_REFUSED"] = "CONNECTION_REFUSED";
|
|
23
|
+
// HTTP 错误 (HTTP errors)
|
|
24
|
+
ErrorType["HTTP_CLIENT_ERROR"] = "HTTP_CLIENT_ERROR";
|
|
25
|
+
ErrorType["HTTP_SERVER_ERROR"] = "HTTP_SERVER_ERROR";
|
|
26
|
+
// 访问控制错误 (Access control errors)
|
|
27
|
+
ErrorType["ACCESS_DENIED"] = "ACCESS_DENIED";
|
|
28
|
+
ErrorType["CLOUDFLARE_PROTECTION"] = "CLOUDFLARE_PROTECTION";
|
|
29
|
+
ErrorType["CAPTCHA_REQUIRED"] = "CAPTCHA_REQUIRED";
|
|
30
|
+
// 解析错误 (Parsing errors)
|
|
31
|
+
ErrorType["JSON_PARSE_ERROR"] = "JSON_PARSE_ERROR";
|
|
32
|
+
ErrorType["HTML_PARSE_ERROR"] = "HTML_PARSE_ERROR";
|
|
33
|
+
// 浏览器错误 (Browser errors)
|
|
34
|
+
ErrorType["BROWSER_LAUNCH_ERROR"] = "BROWSER_LAUNCH_ERROR";
|
|
35
|
+
ErrorType["BROWSER_NAVIGATION_ERROR"] = "BROWSER_NAVIGATION_ERROR";
|
|
36
|
+
ErrorType["BROWSER_CONTENT_ERROR"] = "BROWSER_CONTENT_ERROR";
|
|
37
|
+
// 资源错误 (Resource errors)
|
|
38
|
+
ErrorType["RESOURCE_NOT_FOUND"] = "RESOURCE_NOT_FOUND";
|
|
39
|
+
ErrorType["RESOURCE_ACCESS_ERROR"] = "RESOURCE_ACCESS_ERROR";
|
|
40
|
+
// 工具错误 (Tool errors)
|
|
41
|
+
ErrorType["TOOL_NOT_FOUND"] = "TOOL_NOT_FOUND";
|
|
42
|
+
ErrorType["TOOL_EXECUTION_ERROR"] = "TOOL_EXECUTION_ERROR";
|
|
43
|
+
// 参数错误 (Parameter errors)
|
|
44
|
+
ErrorType["INVALID_PARAMETER"] = "INVALID_PARAMETER";
|
|
45
|
+
ErrorType["MISSING_PARAMETER"] = "MISSING_PARAMETER";
|
|
46
|
+
// 其他错误 (Other errors)
|
|
47
|
+
ErrorType["UNKNOWN"] = "UNKNOWN";
|
|
48
|
+
})(ErrorType || (ErrorType = {}));
|
|
49
|
+
/**
|
|
50
|
+
* 基础错误类 (Base error class)
|
|
51
|
+
*/
|
|
52
|
+
export class FetchError extends Error {
|
|
53
|
+
type;
|
|
54
|
+
statusCode;
|
|
55
|
+
originalError;
|
|
56
|
+
details;
|
|
57
|
+
component;
|
|
58
|
+
/**
|
|
59
|
+
* 构造函数 (Constructor)
|
|
60
|
+
* @param message 错误消息 (Error message)
|
|
61
|
+
* @param type 错误类型 (Error type)
|
|
62
|
+
* @param component 组件名称 (Component name)
|
|
63
|
+
* @param details 错误详情 (Error details)
|
|
64
|
+
*/
|
|
65
|
+
constructor(message, type = ErrorType.UNKNOWN, component = EXTENDED_COMPONENTS.UNKNOWN, details) {
|
|
66
|
+
super(message);
|
|
67
|
+
this.name = 'FetchError';
|
|
68
|
+
this.type = type;
|
|
69
|
+
this.component = component;
|
|
70
|
+
this.details = details;
|
|
71
|
+
// 确保 instanceof 正常工作 (Ensure instanceof works correctly)
|
|
72
|
+
Object.setPrototypeOf(this, FetchError.prototype);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 记录错误日志 (Log error)
|
|
76
|
+
* @param debug 是否启用调试模式 (Whether debug mode is enabled)
|
|
77
|
+
* @param forceLog 是否强制记录日志 (Whether to force logging)
|
|
78
|
+
*/
|
|
79
|
+
log(debug = false, forceLog = false) {
|
|
80
|
+
const logDetails = {
|
|
81
|
+
type: this.type,
|
|
82
|
+
message: this.message,
|
|
83
|
+
...(this.statusCode ? { statusCode: this.statusCode } : {}),
|
|
84
|
+
...(this.details || {})
|
|
85
|
+
};
|
|
86
|
+
log('error.occurred', debug || forceLog, logDetails, this.component);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 转换为标准响应格式 (Convert to standard response format)
|
|
90
|
+
* @returns 标准响应对象 (Standard response object)
|
|
91
|
+
*/
|
|
92
|
+
toResponse() {
|
|
93
|
+
return {
|
|
94
|
+
isError: true,
|
|
95
|
+
content: [
|
|
96
|
+
{
|
|
97
|
+
text: this.message
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* 网络错误类 (Network error class)
|
|
105
|
+
*/
|
|
106
|
+
export class NetworkError extends FetchError {
|
|
107
|
+
constructor(message, component = EXTENDED_COMPONENTS.UNKNOWN, details, originalError) {
|
|
108
|
+
super(message, ErrorType.NETWORK, component, details);
|
|
109
|
+
this.name = 'NetworkError';
|
|
110
|
+
this.originalError = originalError;
|
|
111
|
+
// 确保 instanceof 正常工作 (Ensure instanceof works correctly)
|
|
112
|
+
Object.setPrototypeOf(this, NetworkError.prototype);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 超时错误类 (Timeout error class)
|
|
117
|
+
*/
|
|
118
|
+
export class TimeoutError extends NetworkError {
|
|
119
|
+
constructor(message, component = EXTENDED_COMPONENTS.UNKNOWN, details, originalError) {
|
|
120
|
+
super(message, component, details, originalError);
|
|
121
|
+
this.name = 'TimeoutError';
|
|
122
|
+
this.type = ErrorType.TIMEOUT;
|
|
123
|
+
// 确保 instanceof 正常工作 (Ensure instanceof works correctly)
|
|
124
|
+
Object.setPrototypeOf(this, TimeoutError.prototype);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* HTTP 错误类 (HTTP error class)
|
|
129
|
+
*/
|
|
130
|
+
export class HttpError extends FetchError {
|
|
131
|
+
constructor(message, statusCode, component = EXTENDED_COMPONENTS.UNKNOWN, details, originalError) {
|
|
132
|
+
const type = statusCode >= 400 && statusCode < 500
|
|
133
|
+
? ErrorType.HTTP_CLIENT_ERROR
|
|
134
|
+
: ErrorType.HTTP_SERVER_ERROR;
|
|
135
|
+
super(message, type, component, details);
|
|
136
|
+
this.name = 'HttpError';
|
|
137
|
+
this.statusCode = statusCode;
|
|
138
|
+
this.originalError = originalError;
|
|
139
|
+
// 确保 instanceof 正常工作 (Ensure instanceof works correctly)
|
|
140
|
+
Object.setPrototypeOf(this, HttpError.prototype);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 访问被拒绝错误类 (Access denied error class)
|
|
145
|
+
*/
|
|
146
|
+
export class AccessDeniedError extends HttpError {
|
|
147
|
+
constructor(message, statusCode = 403, component = EXTENDED_COMPONENTS.UNKNOWN, details, originalError) {
|
|
148
|
+
super(message, statusCode, component, details, originalError);
|
|
149
|
+
this.name = 'AccessDeniedError';
|
|
150
|
+
this.type = ErrorType.ACCESS_DENIED;
|
|
151
|
+
// 确保 instanceof 正常工作 (Ensure instanceof works correctly)
|
|
152
|
+
Object.setPrototypeOf(this, AccessDeniedError.prototype);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 解析错误类 (Parse error class)
|
|
157
|
+
*/
|
|
158
|
+
export class ParseError extends FetchError {
|
|
159
|
+
constructor(message, type = ErrorType.JSON_PARSE_ERROR, component = EXTENDED_COMPONENTS.UNKNOWN, details, originalError) {
|
|
160
|
+
super(message, type, component, details);
|
|
161
|
+
this.name = 'ParseError';
|
|
162
|
+
this.originalError = originalError;
|
|
163
|
+
// 确保 instanceof 正常工作 (Ensure instanceof works correctly)
|
|
164
|
+
Object.setPrototypeOf(this, ParseError.prototype);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* 浏览器错误类 (Browser error class)
|
|
169
|
+
*/
|
|
170
|
+
export class BrowserError extends FetchError {
|
|
171
|
+
constructor(message, type = ErrorType.BROWSER_LAUNCH_ERROR, component = EXTENDED_COMPONENTS.BROWSER, details, originalError) {
|
|
172
|
+
super(message, type, component, details);
|
|
173
|
+
this.name = 'BrowserError';
|
|
174
|
+
this.originalError = originalError;
|
|
175
|
+
// 确保 instanceof 正常工作 (Ensure instanceof works correctly)
|
|
176
|
+
Object.setPrototypeOf(this, BrowserError.prototype);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* 工具错误类 (Tool error class)
|
|
181
|
+
*/
|
|
182
|
+
export class ToolError extends FetchError {
|
|
183
|
+
constructor(message, type = ErrorType.TOOL_EXECUTION_ERROR, component = COMPONENTS.TOOLS, details, originalError) {
|
|
184
|
+
super(message, type, component, details);
|
|
185
|
+
this.name = 'ToolError';
|
|
186
|
+
this.originalError = originalError;
|
|
187
|
+
// 确保 instanceof 正常工作 (Ensure instanceof works correctly)
|
|
188
|
+
Object.setPrototypeOf(this, ToolError.prototype);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* 参数错误类 (Parameter error class)
|
|
193
|
+
*/
|
|
194
|
+
export class ParameterError extends FetchError {
|
|
195
|
+
constructor(message, type = ErrorType.INVALID_PARAMETER, component = EXTENDED_COMPONENTS.UNKNOWN, details) {
|
|
196
|
+
super(message, type, component, details);
|
|
197
|
+
this.name = 'ParameterError';
|
|
198
|
+
// 确保 instanceof 正常工作 (Ensure instanceof works correctly)
|
|
199
|
+
Object.setPrototypeOf(this, ParameterError.prototype);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* 创建错误对象 (Create error object)
|
|
204
|
+
* @param error 原始错误 (Original error)
|
|
205
|
+
* @param component 组件名称 (Component name)
|
|
206
|
+
* @param details 错误详情 (Error details)
|
|
207
|
+
* @returns 标准化的错误对象 (Standardized error object)
|
|
208
|
+
*/
|
|
209
|
+
export function createError(error, component = EXTENDED_COMPONENTS.UNKNOWN, details) {
|
|
210
|
+
// 如果已经是 FetchError 类型,直接返回
|
|
211
|
+
if (error instanceof FetchError) {
|
|
212
|
+
return error;
|
|
213
|
+
}
|
|
214
|
+
// 获取错误消息
|
|
215
|
+
const errorMessage = error instanceof Error
|
|
216
|
+
? error.message
|
|
217
|
+
: typeof error === 'string'
|
|
218
|
+
? error
|
|
219
|
+
: 'Unknown error';
|
|
220
|
+
// 检查错误类型并创建相应的错误对象
|
|
221
|
+
const errorText = errorMessage.toLowerCase();
|
|
222
|
+
// 检查是否为访问被拒绝错误
|
|
223
|
+
if (isAccessDeniedError(errorText)) {
|
|
224
|
+
return new AccessDeniedError(errorMessage, 403, component, details, error);
|
|
225
|
+
}
|
|
226
|
+
// 检查是否为网络错误
|
|
227
|
+
if (isNetworkError(errorText)) {
|
|
228
|
+
// 检查是否为超时错误
|
|
229
|
+
if (errorText.includes('timeout')) {
|
|
230
|
+
return new TimeoutError(errorMessage, component, details, error);
|
|
231
|
+
}
|
|
232
|
+
return new NetworkError(errorMessage, component, details, error);
|
|
233
|
+
}
|
|
234
|
+
// 检查是否为 HTTP 错误
|
|
235
|
+
const statusCodeMatch = errorText.match(/(\d{3})/);
|
|
236
|
+
if (statusCodeMatch) {
|
|
237
|
+
const statusCode = parseInt(statusCodeMatch[1], 10);
|
|
238
|
+
if (statusCode >= 400 && statusCode < 600) {
|
|
239
|
+
return new HttpError(errorMessage, statusCode, component, details, error);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// 检查是否为解析错误
|
|
243
|
+
if (errorText.includes('json') && (errorText.includes('parse') || errorText.includes('invalid'))) {
|
|
244
|
+
return new ParseError(errorMessage, ErrorType.JSON_PARSE_ERROR, component, details, error);
|
|
245
|
+
}
|
|
246
|
+
if (errorText.includes('html') && (errorText.includes('parse') || errorText.includes('invalid'))) {
|
|
247
|
+
return new ParseError(errorMessage, ErrorType.HTML_PARSE_ERROR, component, details, error);
|
|
248
|
+
}
|
|
249
|
+
// 检查是否为浏览器错误
|
|
250
|
+
if (errorText.includes('browser') || errorText.includes('puppeteer') || errorText.includes('chrome')) {
|
|
251
|
+
if (errorText.includes('launch') || errorText.includes('start')) {
|
|
252
|
+
return new BrowserError(errorMessage, ErrorType.BROWSER_LAUNCH_ERROR, component, details, error);
|
|
253
|
+
}
|
|
254
|
+
if (errorText.includes('navigate') || errorText.includes('goto')) {
|
|
255
|
+
return new BrowserError(errorMessage, ErrorType.BROWSER_NAVIGATION_ERROR, component, details, error);
|
|
256
|
+
}
|
|
257
|
+
return new BrowserError(errorMessage, ErrorType.BROWSER_CONTENT_ERROR, component, details, error);
|
|
258
|
+
}
|
|
259
|
+
// 默认返回通用错误
|
|
260
|
+
return new FetchError(errorMessage, ErrorType.UNKNOWN, component, details);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* 处理错误 (Handle error)
|
|
264
|
+
* @param error 原始错误 (Original error)
|
|
265
|
+
* @param component 组件名称 (Component name)
|
|
266
|
+
* @param debug 是否启用调试模式 (Whether debug mode is enabled)
|
|
267
|
+
* @param details 错误详情 (Error details)
|
|
268
|
+
* @returns 标准化的错误响应 (Standardized error response)
|
|
269
|
+
*/
|
|
270
|
+
export function handleError(error, component = EXTENDED_COMPONENTS.UNKNOWN, debug = false, details) {
|
|
271
|
+
// 创建标准化的错误对象
|
|
272
|
+
const standardError = createError(error, component, details);
|
|
273
|
+
// 记录错误日志
|
|
274
|
+
standardError.log(debug, true);
|
|
275
|
+
// 返回标准化的错误响应
|
|
276
|
+
return standardError.toResponse();
|
|
277
|
+
}
|
|
278
|
+
// 导出错误检测函数
|
|
279
|
+
export { isAccessDeniedError, isNetworkError };
|
package/dist/src/mcp-server.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Author: Martin <lmccc.dev@gmail.com>
|
|
3
|
+
* Co-Author: AI Assistant (Claude)
|
|
4
|
+
* Description: This code was collaboratively developed by Martin and AI Assistant.
|
|
5
|
+
*/
|
|
6
|
+
// 测试国际化功能 (Test internationalization functionality)
|
|
7
|
+
import { log, COMPONENTS, clearTranslationCache } from './lib/logger.js';
|
|
8
|
+
import { t, changeLanguage, getCurrentLanguage } from './lib/i18n/index.js';
|
|
9
|
+
/**
|
|
10
|
+
* 测试翻译键 (Test translation keys)
|
|
11
|
+
* @param keys 要测试的键数组 (Array of keys to test)
|
|
12
|
+
* @param language 语言 (Language)
|
|
13
|
+
*/
|
|
14
|
+
function testKeys(keys, language) {
|
|
15
|
+
console.log(`\n=== 测试 ${language} 翻译 (Testing ${language} translation) ===`);
|
|
16
|
+
for (const key of keys) {
|
|
17
|
+
try {
|
|
18
|
+
const translated = t(key);
|
|
19
|
+
console.log(`Key: ${key}`);
|
|
20
|
+
console.log(`Translation: ${translated}`);
|
|
21
|
+
console.log('---');
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
console.error(`Error translating key: ${key}`);
|
|
25
|
+
console.error(error);
|
|
26
|
+
console.log('---');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 测试日志函数 (Test log function)
|
|
32
|
+
* @param keys 要测试的键数组 (Array of keys to test)
|
|
33
|
+
* @param component 组件名称 (Component name)
|
|
34
|
+
*/
|
|
35
|
+
function testLogFunction(keys, component) {
|
|
36
|
+
console.log(`\n=== 测试日志函数 (Testing log function) ===`);
|
|
37
|
+
for (const key of keys) {
|
|
38
|
+
log(key, true, {}, component);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 测试带参数的翻译 (Test translation with parameters)
|
|
43
|
+
* @param keys 要测试的键数组 (Array of keys to test)
|
|
44
|
+
* @param params 参数对象 (Parameters object)
|
|
45
|
+
*/
|
|
46
|
+
function testWithParams(keys, params) {
|
|
47
|
+
console.log(`\n=== 测试带参数的翻译 (Testing translation with parameters) ===`);
|
|
48
|
+
for (const key of keys) {
|
|
49
|
+
try {
|
|
50
|
+
const translated = t(key, params);
|
|
51
|
+
console.log(`Key: ${key}`);
|
|
52
|
+
console.log(`Params: ${JSON.stringify(params)}`);
|
|
53
|
+
console.log(`Translation: ${translated}`);
|
|
54
|
+
console.log('---');
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
console.error(`Error translating key with params: ${key}`);
|
|
58
|
+
console.error(error);
|
|
59
|
+
console.log('---');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 测试翻译缓存 (Test translation cache)
|
|
65
|
+
* @param key 要测试的键 (Key to test)
|
|
66
|
+
* @param iterations 迭代次数 (Number of iterations)
|
|
67
|
+
*/
|
|
68
|
+
function testTranslationCache(key, iterations) {
|
|
69
|
+
console.log(`\n=== 测试翻译缓存 (Testing translation cache) ===`);
|
|
70
|
+
console.log(`Key: ${key}`);
|
|
71
|
+
console.log(`Iterations: ${iterations}`);
|
|
72
|
+
// 清除翻译缓存 (Clear translation cache)
|
|
73
|
+
clearTranslationCache();
|
|
74
|
+
// 测试不使用缓存的性能 (Test performance without cache)
|
|
75
|
+
console.log('Without cache:');
|
|
76
|
+
const startWithoutCache = Date.now();
|
|
77
|
+
for (let i = 0; i < iterations; i++) {
|
|
78
|
+
t(key);
|
|
79
|
+
}
|
|
80
|
+
const endWithoutCache = Date.now();
|
|
81
|
+
console.log(`Time: ${endWithoutCache - startWithoutCache}ms`);
|
|
82
|
+
// 测试使用缓存的性能 (Test performance with cache)
|
|
83
|
+
console.log('With cache:');
|
|
84
|
+
const startWithCache = Date.now();
|
|
85
|
+
for (let i = 0; i < iterations; i++) {
|
|
86
|
+
log(key, true, {}, COMPONENTS.SERVER);
|
|
87
|
+
}
|
|
88
|
+
const endWithCache = Date.now();
|
|
89
|
+
console.log(`Time: ${endWithCache - startWithCache}ms`);
|
|
90
|
+
console.log('---');
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* 设置环境变量 (Set environment variable)
|
|
94
|
+
* @param name 环境变量名称 (Environment variable name)
|
|
95
|
+
* @param value 环境变量值 (Environment variable value)
|
|
96
|
+
*/
|
|
97
|
+
function setEnv(name, value) {
|
|
98
|
+
process.env[name] = value;
|
|
99
|
+
console.log(`Set environment variable ${name}=${value}`);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 主测试函数 (Main test function)
|
|
103
|
+
*/
|
|
104
|
+
async function testI18n() {
|
|
105
|
+
console.log('=== 测试国际化功能 (Testing internationalization functionality) ===');
|
|
106
|
+
// 测试简单的键 (Test simple keys)
|
|
107
|
+
const simpleKeys = [
|
|
108
|
+
'server.starting',
|
|
109
|
+
'server.started',
|
|
110
|
+
'server.stopping',
|
|
111
|
+
'server.stopped'
|
|
112
|
+
];
|
|
113
|
+
// 确保 MCP_LANG 环境变量设置为英文 (Ensure MCP_LANG environment variable is set to English)
|
|
114
|
+
setEnv('MCP_LANG', 'en');
|
|
115
|
+
// 测试英文翻译 (Test English translation)
|
|
116
|
+
changeLanguage('en');
|
|
117
|
+
console.log(`Current language: ${getCurrentLanguage()}`);
|
|
118
|
+
testKeys(simpleKeys, 'English');
|
|
119
|
+
// 测试日志函数 (Test log function)
|
|
120
|
+
testLogFunction(simpleKeys, COMPONENTS.SERVER);
|
|
121
|
+
// 测试带参数的翻译 (Test translation with parameters)
|
|
122
|
+
testWithParams(['server.listeningOn'], { port: 8080 });
|
|
123
|
+
// 测试翻译缓存 (Test translation cache)
|
|
124
|
+
testTranslationCache('server.starting', 10);
|
|
125
|
+
// 确保 MCP_LANG 环境变量设置为中文 (Ensure MCP_LANG environment variable is set to Chinese)
|
|
126
|
+
setEnv('MCP_LANG', 'zh');
|
|
127
|
+
// 测试中文翻译 (Test Chinese translation)
|
|
128
|
+
changeLanguage('zh');
|
|
129
|
+
console.log(`Current language: ${getCurrentLanguage()}`);
|
|
130
|
+
testKeys(simpleKeys, 'Chinese');
|
|
131
|
+
// 测试日志函数 (Test log function)
|
|
132
|
+
testLogFunction(simpleKeys, COMPONENTS.SERVER);
|
|
133
|
+
// 测试带参数的翻译 (Test translation with parameters)
|
|
134
|
+
testWithParams(['server.listeningOn'], { port: 8080 });
|
|
135
|
+
// 测试翻译缓存 (Test translation cache)
|
|
136
|
+
testTranslationCache('server.starting', 10);
|
|
137
|
+
}
|
|
138
|
+
// 运行测试 (Run test)
|
|
139
|
+
testI18n().catch(console.error);
|