@lmcc-dev/mult-fetch-mcp-server 1.2.0 → 1.2.1

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.
@@ -55,6 +55,11 @@ function responseRequiresBrowser(response, debug = false) {
55
55
  }
56
56
  }
57
57
  }
58
+ // 检查是否包含 HTTP 403 Forbidden 错误,这是最常见的需要切换到浏览器模式的情况
59
+ if (errorText.includes('403') ||
60
+ errorText.toLowerCase().includes('forbidden')) {
61
+ return true;
62
+ }
58
63
  return isAccessDeniedError(errorText) ||
59
64
  isNetworkError(errorText) ||
60
65
  errorText.toLowerCase().includes('javascript required') ||
@@ -97,9 +102,14 @@ async function smartFetch(params) {
97
102
  if (params.autoDetectMode !== false) {
98
103
  // 首先尝试使用标准模式 (First try using standard mode)
99
104
  log('client.usingMode', debug, { mode: params.useBrowser ? 'browser' : 'standard', url }, COMPONENTS.CLIENT);
105
+ // 确保服务端也启用了自动检测模式
106
+ const paramsWithAutoDetect = {
107
+ ...params,
108
+ autoDetectMode: true
109
+ };
100
110
  const result = await client.callTool({
101
111
  name: method,
102
- arguments: params
112
+ arguments: paramsWithAutoDetect
103
113
  });
104
114
  // 在调试模式下打印响应结果的摘要 (Print response result summary in debug mode)
105
115
  if (debug) {
@@ -115,12 +125,15 @@ async function smartFetch(params) {
115
125
  log('client.browserModeNeeded', debug, { url }, COMPONENTS.CLIENT);
116
126
  log('client.retryingWithBrowser', debug, { url }, COMPONENTS.CLIENT);
117
127
  // 切换到浏览器模式重试 (Switch to browser mode and retry)
128
+ const browserParams = {
129
+ ...params,
130
+ useBrowser: true,
131
+ debug: true, // 确保在浏览器模式下启用调试
132
+ autoDetectMode: true // 确保自动检测模式也被传递
133
+ };
118
134
  const browserResult = await client.callTool({
119
135
  name: method,
120
- arguments: {
121
- ...params,
122
- useBrowser: true
123
- }
136
+ arguments: browserParams
124
137
  });
125
138
  if (browserResult.isError) {
126
139
  log('client.browserModeFetchFailed', debug, { url, error: browserResult.content[0].text }, COMPONENTS.CLIENT);
@@ -141,9 +154,14 @@ async function smartFetch(params) {
141
154
  else {
142
155
  // 直接使用指定的模式 (Directly use the specified mode)
143
156
  log('client.usingMode', debug, { mode: params.useBrowser ? 'browser' : 'standard', url }, COMPONENTS.CLIENT);
157
+ // 确保明确设置 autoDetectMode 为 false
158
+ const paramsWithoutAutoDetect = {
159
+ ...params,
160
+ autoDetectMode: false
161
+ };
144
162
  const result = await client.callTool({
145
163
  name: method,
146
- arguments: params
164
+ arguments: paramsWithoutAutoDetect
147
165
  });
148
166
  if (result.isError) {
149
167
  log('client.fetchFailed', debug, { url, error: result.content[0].text }, COMPONENTS.CLIENT);
@@ -46,15 +46,47 @@ export async function closeBrowserInstance(debug = false) {
46
46
  */
47
47
  export function shouldSwitchToBrowser(error) {
48
48
  // 检查错误是否表明需要浏览器模式 (Check if error indicates browser mode is needed)
49
- if (error && error.message) {
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');
49
+ if (!error)
50
+ return false;
51
+ // 提取错误消息,无论错误对象的形式如何
52
+ let errorMessage;
53
+ if (typeof error === 'string') {
54
+ errorMessage = error;
56
55
  }
57
- return false;
56
+ else if (error instanceof Error) {
57
+ errorMessage = error.message;
58
+ }
59
+ else if (error && typeof error === 'object') {
60
+ if ('message' in error && typeof error.message === 'string') {
61
+ errorMessage = error.message;
62
+ }
63
+ else if ('content' in error && Array.isArray(error.content) && error.content.length > 0) {
64
+ // 处理 MCP 响应格式
65
+ const content = error.content[0];
66
+ errorMessage = content && typeof content === 'object' && 'text' in content ? String(content.text) : String(error);
67
+ }
68
+ else {
69
+ try {
70
+ errorMessage = JSON.stringify(error);
71
+ }
72
+ catch {
73
+ errorMessage = String(error);
74
+ }
75
+ }
76
+ }
77
+ else {
78
+ errorMessage = String(error);
79
+ }
80
+ // 检查是否包含 HTTP 403 Forbidden 错误,这是最常见的需要切换到浏览器模式的情况
81
+ if (errorMessage.includes('403') ||
82
+ errorMessage.toLowerCase().includes('forbidden')) {
83
+ return true;
84
+ }
85
+ return isAccessDeniedError(errorMessage) ||
86
+ isNetworkError(errorMessage) ||
87
+ errorMessage.toLowerCase().includes('javascript required') ||
88
+ errorMessage.toLowerCase().includes('und_err_connect_timeout') ||
89
+ errorMessage.toLowerCase().includes('fetch failed');
58
90
  }
59
91
  /**
60
92
  * 检查是否应该使用浏览器模式 (Check if browser mode should be used)
@@ -38,6 +38,8 @@ export async function fetchWithAutoDetect(params, type) {
38
38
  return await Fetcher.txt(params);
39
39
  case 'markdown':
40
40
  return await Fetcher.markdown(params);
41
+ default:
42
+ throw new Error(`Unsupported content type: ${type}`);
41
43
  }
42
44
  }
43
45
  else {
@@ -47,43 +49,112 @@ export async function fetchWithAutoDetect(params, type) {
47
49
  }
48
50
  try {
49
51
  // 根据类型选择合适的标准获取方法 (Choose appropriate standard fetching method based on type)
52
+ let result;
50
53
  switch (type) {
51
54
  case 'html': {
52
- return await Fetcher.html(params);
55
+ result = await Fetcher.html(params);
56
+ break;
53
57
  }
54
58
  case 'json': {
55
- return await Fetcher.json(params);
59
+ result = await Fetcher.json(params);
60
+ break;
56
61
  }
57
62
  case 'txt': {
58
- return await Fetcher.txt(params);
63
+ result = await Fetcher.txt(params);
64
+ break;
59
65
  }
60
66
  case 'markdown': {
61
- return await Fetcher.markdown(params);
67
+ result = await Fetcher.markdown(params);
68
+ break;
62
69
  }
70
+ default:
71
+ throw new Error(`Unsupported content type: ${type}`);
63
72
  }
73
+ // 检查结果是否为错误,并且是否包含403或forbidden关键词
74
+ if (result.isError && autoDetectMode) {
75
+ const errorText = result.content && result.content[0] && result.content[0].text
76
+ ? String(result.content[0].text)
77
+ : '';
78
+ const is403Error = errorText.includes('403') ||
79
+ errorText.toLowerCase().includes('forbidden');
80
+ if (is403Error || shouldSwitchToBrowser(errorText)) {
81
+ if (debug) {
82
+ log('server.switchingToBrowserMode', debug, {
83
+ url: params.url,
84
+ error: errorText,
85
+ reason: is403Error ? '403 Forbidden' : 'Other error requiring browser'
86
+ }, COMPONENTS.SERVER);
87
+ }
88
+ // 确保浏览器已初始化
89
+ await initializeBrowser(debug);
90
+ // 设置浏览器模式参数
91
+ const browserParams = {
92
+ ...params,
93
+ useBrowser: true,
94
+ debug: debug
95
+ };
96
+ // 根据类型选择合适的浏览器获取方法
97
+ let browserResult;
98
+ switch (type) {
99
+ case 'html':
100
+ browserResult = await Fetcher.html(browserParams);
101
+ break;
102
+ case 'json':
103
+ browserResult = await Fetcher.json(browserParams);
104
+ break;
105
+ case 'txt':
106
+ browserResult = await Fetcher.txt(browserParams);
107
+ break;
108
+ case 'markdown':
109
+ browserResult = await Fetcher.markdown(browserParams);
110
+ break;
111
+ default:
112
+ throw new Error(`Unsupported content type: ${type}`);
113
+ }
114
+ return browserResult;
115
+ }
116
+ }
117
+ return result;
64
118
  }
65
119
  catch (error) {
66
120
  // 如果标准模式失败且启用了自动检测
67
- if (autoDetectMode && shouldSwitchToBrowser(error)) {
121
+ const errorMessage = error instanceof Error ? error.message : String(error);
122
+ // 检查是否应该切换到浏览器模式
123
+ // 特别处理 HTTP 403 Forbidden 错误,这是最常见的需要切换到浏览器模式的情况
124
+ const shouldSwitch = autoDetectMode && (shouldSwitchToBrowser(error) ||
125
+ errorMessage.includes('403') ||
126
+ errorMessage.toLowerCase().includes('forbidden'));
127
+ if (shouldSwitch) {
68
128
  if (debug) {
69
129
  log('server.switchingToBrowserMode', debug, { url: params.url }, COMPONENTS.SERVER);
70
130
  }
71
131
  // 确保浏览器已初始化
72
132
  await initializeBrowser(debug);
73
133
  // 设置浏览器模式参数
74
- params.useBrowser = true;
75
- params.debug = debug;
134
+ const browserParams = {
135
+ ...params,
136
+ useBrowser: true,
137
+ debug: debug
138
+ };
76
139
  // 根据类型选择合适的浏览器获取方法
140
+ let browserResult;
77
141
  switch (type) {
78
142
  case 'html':
79
- return await Fetcher.html(params);
143
+ browserResult = await Fetcher.html(browserParams);
144
+ break;
80
145
  case 'json':
81
- return await Fetcher.json(params);
146
+ browserResult = await Fetcher.json(browserParams);
147
+ break;
82
148
  case 'txt':
83
- return await Fetcher.txt(params);
149
+ browserResult = await Fetcher.txt(browserParams);
150
+ break;
84
151
  case 'markdown':
85
- return await Fetcher.markdown(params);
152
+ browserResult = await Fetcher.markdown(browserParams);
153
+ break;
154
+ default:
155
+ throw new Error(`Unsupported content type: ${type}`);
86
156
  }
157
+ return browserResult;
87
158
  }
88
159
  // 如果不需要切换到浏览器模式,则抛出原始错误
89
160
  throw error;
@@ -106,4 +177,4 @@ export async function fetchWithAutoDetect(params, type) {
106
177
  content: [{ type: 'text', text: `Unsupported content type: ${type}` }],
107
178
  isError: true
108
179
  };
109
- }
180
+ } // 修复注释
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lmcc-dev/mult-fetch-mcp-server",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "An MCP protocol-based web content fetching tool that supports multiple modes and formats, can be integrated with AI assistants like Claude",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",