@lmcc-dev/mult-fetch-mcp-server 1.1.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.
Files changed (216) hide show
  1. package/README.md +8 -8
  2. package/README.zh.md +16 -8
  3. package/dist/i18n-test-report.json +8 -0
  4. package/dist/i18n-unused-keys-report.json +8 -0
  5. package/dist/src/client.js +70 -37
  6. package/dist/src/index.js +0 -0
  7. package/dist/src/lib/BrowserFetcher.js +12 -12
  8. package/dist/src/lib/NodeFetcher.js +22 -22
  9. package/dist/src/lib/fetch.js +40 -0
  10. package/dist/src/lib/fetchers/FetcherFactory.js +43 -0
  11. package/dist/src/lib/fetchers/browser/BrowserFetcher.js +381 -0
  12. package/dist/src/lib/fetchers/browser/BrowserInstance.js +180 -0
  13. package/dist/src/lib/fetchers/browser/PageOperations.js +168 -0
  14. package/dist/src/lib/fetchers/common/types.js +41 -0
  15. package/dist/src/lib/fetchers/common/utils.js +141 -0
  16. package/dist/src/lib/fetchers/index.js +62 -0
  17. package/dist/src/lib/fetchers/node/HttpClient.js +239 -0
  18. package/dist/src/lib/fetchers/node/NodeFetcher.js +180 -0
  19. package/dist/src/lib/i18n/index.js +105 -17
  20. package/dist/src/lib/i18n/keys/base.js +41 -0
  21. package/dist/src/lib/i18n/keys/browser.js +91 -0
  22. package/dist/src/lib/i18n/keys/client.js +68 -0
  23. package/dist/src/lib/i18n/keys/errors.js +49 -0
  24. package/dist/src/lib/i18n/keys/factory.js +46 -0
  25. package/dist/src/lib/i18n/keys/fetcher.js +87 -0
  26. package/dist/src/lib/i18n/keys/index.js +16 -0
  27. package/dist/src/lib/i18n/keys/node.js +69 -0
  28. package/dist/src/lib/i18n/keys/prompts.js +79 -0
  29. package/dist/src/lib/i18n/keys/resources.js +53 -0
  30. package/dist/src/lib/i18n/keys/server.js +67 -0
  31. package/dist/src/lib/i18n/keys/tools.js +39 -0
  32. package/dist/src/lib/i18n/keys.js +488 -119
  33. package/dist/src/lib/i18n/locales/en/browser.js +67 -57
  34. package/dist/src/lib/i18n/locales/en/client.js +57 -33
  35. package/dist/src/lib/i18n/locales/en/errors.js +36 -12
  36. package/dist/src/lib/i18n/locales/en/factory.js +38 -0
  37. package/dist/src/lib/i18n/locales/en/fetcher.js +8 -2
  38. package/dist/src/lib/i18n/locales/en/index.js +10 -8
  39. package/dist/src/lib/i18n/locales/en/node.js +57 -35
  40. package/dist/src/lib/i18n/locales/en/prompts.js +42 -65
  41. package/dist/src/lib/i18n/locales/en/resources.js +52 -55
  42. package/dist/src/lib/i18n/locales/en/server.js +49 -24
  43. package/dist/src/lib/i18n/locales/en/tools.js +27 -19
  44. package/dist/src/lib/i18n/locales/zh/browser.js +71 -62
  45. package/dist/src/lib/i18n/locales/zh/client.js +57 -32
  46. package/dist/src/lib/i18n/locales/zh/errors.js +36 -12
  47. package/dist/src/lib/i18n/locales/zh/factory.js +38 -0
  48. package/dist/src/lib/i18n/locales/zh/fetcher.js +36 -30
  49. package/dist/src/lib/i18n/locales/zh/index.js +10 -8
  50. package/dist/src/lib/i18n/locales/zh/node.js +61 -36
  51. package/dist/src/lib/i18n/locales/zh/prompts.js +35 -57
  52. package/dist/src/lib/i18n/locales/zh/resources.js +52 -55
  53. package/dist/src/lib/i18n/locales/zh/server.js +51 -25
  54. package/dist/src/lib/i18n/locales/zh/tools.js +27 -19
  55. package/dist/src/lib/i18n/logger.js +90 -19
  56. package/dist/src/lib/logger.js +135 -14
  57. package/dist/src/lib/server/browser.js +42 -10
  58. package/dist/src/lib/server/fetcher.js +87 -37
  59. package/dist/src/lib/server/index.js +2 -2
  60. package/dist/src/lib/server/prompts.js +1 -1
  61. package/dist/src/lib/server/resources.js +1 -1
  62. package/dist/src/lib/server/tools.js +46 -10
  63. package/dist/src/lib/utils/ErrorHandler.js +125 -0
  64. package/dist/src/mcp-server.js +0 -0
  65. package/dist/src/test-i18n.js +139 -0
  66. package/dist/tests/BrowserFetcher.test.js +0 -0
  67. package/dist/tests/NodeFetcher.test.js +0 -0
  68. package/dist/tests/client.test.js +42 -81
  69. package/dist/tests/fetch.test.js +70 -0
  70. package/dist/tests/fetchers/Fetcher.test.js +108 -0
  71. package/dist/tests/fetchers/browser/BrowserFetcher.test.js +195 -0
  72. package/dist/tests/fetchers/browser/BrowserInstance.test.js +163 -0
  73. package/dist/tests/fetchers/node/NodeFetcher.test.js +211 -0
  74. package/dist/tests/i18n-missing-keys.js +364 -0
  75. package/dist/tests/i18n-remove-unused-keys.js +236 -0
  76. package/dist/tests/i18n-test-report.json +2004 -0
  77. package/dist/tests/i18n-unused-keys.js +425 -0
  78. package/dist/tests/i18n.test.js +6 -7
  79. package/dist/tests/logger.test.js +14 -13
  80. package/dist/tests/mcp-server.test.js +7 -7
  81. package/dist/tests/server/browser.test.js +25 -21
  82. package/dist/tests/server/fetcher.test.js +58 -101
  83. package/dist/tests/server/index.test.js +30 -30
  84. package/dist/tests/server/prompts.test.js +36 -33
  85. package/dist/tests/server/resources.test.js +120 -84
  86. package/dist/tests/server/tools.test.js +7 -6
  87. package/dist/tests/setup.js +36 -0
  88. package/dist/tests/src/lib/i18n/index.js +108 -0
  89. package/dist/tests/src/lib/i18n/keys/base.js +47 -0
  90. package/dist/tests/src/lib/i18n/keys/browser.js +93 -0
  91. package/dist/tests/src/lib/i18n/keys/client.js +70 -0
  92. package/dist/tests/src/lib/i18n/keys/errors.js +34 -0
  93. package/dist/tests/src/lib/i18n/keys/fetcher.js +84 -0
  94. package/dist/tests/src/lib/i18n/keys/index.js +31 -0
  95. package/dist/tests/src/lib/i18n/keys/node.js +56 -0
  96. package/dist/tests/src/lib/i18n/keys/prompts.js +82 -0
  97. package/dist/tests/src/lib/i18n/keys/resources.js +50 -0
  98. package/dist/tests/src/lib/i18n/keys/server.js +64 -0
  99. package/dist/tests/src/lib/i18n/keys/tools.js +34 -0
  100. package/dist/tests/src/lib/i18n/locales/en/browser.js +88 -0
  101. package/dist/tests/src/lib/i18n/locales/en/client.js +66 -0
  102. package/dist/tests/src/lib/i18n/locales/en/errors.js +28 -0
  103. package/dist/tests/src/lib/i18n/locales/en/fetcher.js +71 -0
  104. package/dist/tests/src/lib/i18n/locales/en/index.js +29 -0
  105. package/dist/tests/src/lib/i18n/locales/en/node.js +51 -0
  106. package/dist/tests/src/lib/i18n/locales/en/prompts.js +52 -0
  107. package/dist/tests/src/lib/i18n/locales/en/resources.js +50 -0
  108. package/dist/tests/src/lib/i18n/locales/en/server.js +56 -0
  109. package/dist/tests/src/lib/i18n/locales/en/tools.js +28 -0
  110. package/dist/tests/src/lib/i18n/locales/zh/browser.js +87 -0
  111. package/dist/tests/src/lib/i18n/locales/zh/client.js +66 -0
  112. package/dist/tests/src/lib/i18n/locales/zh/errors.js +28 -0
  113. package/dist/tests/src/lib/i18n/locales/zh/fetcher.js +71 -0
  114. package/dist/tests/src/lib/i18n/locales/zh/index.js +29 -0
  115. package/dist/tests/src/lib/i18n/locales/zh/node.js +51 -0
  116. package/dist/tests/src/lib/i18n/locales/zh/prompts.js +53 -0
  117. package/dist/tests/src/lib/i18n/locales/zh/resources.js +50 -0
  118. package/dist/tests/src/lib/i18n/locales/zh/server.js +57 -0
  119. package/dist/tests/src/lib/i18n/locales/zh/tools.js +28 -0
  120. package/dist/tests/src/lib/i18n/logger.js +114 -0
  121. package/dist/tests/src/lib/logger.js +181 -0
  122. package/dist/tests/test-direct-client.js +0 -0
  123. package/dist/tests/test-i18n.js +530 -0
  124. package/dist/tests/test-mcp-methods.js +1 -1
  125. package/dist/tests/test-mcp.js +0 -0
  126. package/dist/tests/test-mini4k.js +0 -0
  127. package/dist/tests/tests/test-i18n.js +588 -0
  128. package/dist/tests/types.test.js +1 -1
  129. package/dist/tests/utils/ErrorHandler.test.js +144 -0
  130. package/dist/vitest.config.js +29 -0
  131. package/package.json +31 -27
  132. package/dist/src/cli-language.js +0 -78
  133. package/dist/src/client.d.ts +0 -6
  134. package/dist/src/client.js.map +0 -1
  135. package/dist/src/index.d.ts +0 -15
  136. package/dist/src/index.js.map +0 -1
  137. package/dist/src/lib/BrowserFetcher.d.ts +0 -175
  138. package/dist/src/lib/BrowserFetcher.js.map +0 -1
  139. package/dist/src/lib/NodeFetcher.d.ts +0 -92
  140. package/dist/src/lib/NodeFetcher.js.map +0 -1
  141. package/dist/src/lib/example.js +0 -93
  142. package/dist/src/lib/i18n/index.js.map +0 -1
  143. package/dist/src/lib/i18n/locales/en/browser.d.ts +0 -61
  144. package/dist/src/lib/i18n/locales/en/browser.js.map +0 -1
  145. package/dist/src/lib/i18n/locales/en/client.d.ts +0 -39
  146. package/dist/src/lib/i18n/locales/en/client.js.map +0 -1
  147. package/dist/src/lib/i18n/locales/en/errors.d.ts +0 -17
  148. package/dist/src/lib/i18n/locales/en/errors.js.map +0 -1
  149. package/dist/src/lib/i18n/locales/en/fetcher.d.ts +0 -66
  150. package/dist/src/lib/i18n/locales/en/fetcher.js.map +0 -1
  151. package/dist/src/lib/i18n/locales/en/index.d.ts +0 -344
  152. package/dist/src/lib/i18n/locales/en/index.js.map +0 -1
  153. package/dist/src/lib/i18n/locales/en/node.d.ts +0 -41
  154. package/dist/src/lib/i18n/locales/en/node.js.map +0 -1
  155. package/dist/src/lib/i18n/locales/en/prompts.d.ts +0 -65
  156. package/dist/src/lib/i18n/locales/en/prompts.js.map +0 -1
  157. package/dist/src/lib/i18n/locales/en/resources.d.ts +0 -41
  158. package/dist/src/lib/i18n/locales/en/resources.js.map +0 -1
  159. package/dist/src/lib/i18n/locales/en/server.d.ts +0 -30
  160. package/dist/src/lib/i18n/locales/en/server.js.map +0 -1
  161. package/dist/src/lib/i18n/locales/en/tools.d.ts +0 -24
  162. package/dist/src/lib/i18n/locales/en/tools.js.map +0 -1
  163. package/dist/src/lib/i18n/locales/zh/browser.d.ts +0 -61
  164. package/dist/src/lib/i18n/locales/zh/browser.js.map +0 -1
  165. package/dist/src/lib/i18n/locales/zh/client.d.ts +0 -38
  166. package/dist/src/lib/i18n/locales/zh/client.js.map +0 -1
  167. package/dist/src/lib/i18n/locales/zh/errors.d.ts +0 -17
  168. package/dist/src/lib/i18n/locales/zh/errors.js.map +0 -1
  169. package/dist/src/lib/i18n/locales/zh/fetcher.d.ts +0 -66
  170. package/dist/src/lib/i18n/locales/zh/fetcher.js.map +0 -1
  171. package/dist/src/lib/i18n/locales/zh/index.d.ts +0 -343
  172. package/dist/src/lib/i18n/locales/zh/index.js.map +0 -1
  173. package/dist/src/lib/i18n/locales/zh/node.d.ts +0 -41
  174. package/dist/src/lib/i18n/locales/zh/node.js.map +0 -1
  175. package/dist/src/lib/i18n/locales/zh/prompts.d.ts +0 -65
  176. package/dist/src/lib/i18n/locales/zh/prompts.js.map +0 -1
  177. package/dist/src/lib/i18n/locales/zh/resources.d.ts +0 -41
  178. package/dist/src/lib/i18n/locales/zh/resources.js.map +0 -1
  179. package/dist/src/lib/i18n/locales/zh/server.d.ts +0 -30
  180. package/dist/src/lib/i18n/locales/zh/server.js.map +0 -1
  181. package/dist/src/lib/i18n/locales/zh/tools.d.ts +0 -24
  182. package/dist/src/lib/i18n/locales/zh/tools.js.map +0 -1
  183. package/dist/src/lib/i18n/logger.d.ts +0 -38
  184. package/dist/src/lib/i18n/logger.js.map +0 -1
  185. package/dist/src/lib/logger.d.ts +0 -32
  186. package/dist/src/lib/logger.js.map +0 -1
  187. package/dist/src/lib/server/browser.d.ts +0 -34
  188. package/dist/src/lib/server/browser.js.map +0 -1
  189. package/dist/src/lib/server/fetcher.d.ts +0 -20
  190. package/dist/src/lib/server/fetcher.js.map +0 -1
  191. package/dist/src/lib/server/index.d.ts +0 -10
  192. package/dist/src/lib/server/index.js.map +0 -1
  193. package/dist/src/lib/server/logger.js +0 -23
  194. package/dist/src/lib/server/prompts.d.ts +0 -11
  195. package/dist/src/lib/server/prompts.js.map +0 -1
  196. package/dist/src/lib/server/resources.d.ts +0 -11
  197. package/dist/src/lib/server/resources.js.map +0 -1
  198. package/dist/src/lib/server/tools.d.ts +0 -12
  199. package/dist/src/lib/server/tools.js.map +0 -1
  200. package/dist/src/lib/server/types.d.ts +0 -71
  201. package/dist/src/lib/server/types.js.map +0 -1
  202. package/dist/src/lib/types.d.ts +0 -119
  203. package/dist/src/lib/types.js.map +0 -1
  204. package/dist/src/mcp-server.d.ts +0 -6
  205. package/dist/src/mcp-server.js.map +0 -1
  206. package/dist/tests/jest.setup.d.ts +0 -6
  207. package/dist/tests/jest.setup.js +0 -30
  208. package/dist/tests/jest.setup.js.map +0 -1
  209. package/dist/tests/test-direct-client.d.ts +0 -6
  210. package/dist/tests/test-direct-client.js.map +0 -1
  211. package/dist/tests/test-mcp-methods.d.ts +0 -6
  212. package/dist/tests/test-mcp-methods.js.map +0 -1
  213. package/dist/tests/test-mcp.d.ts +0 -6
  214. package/dist/tests/test-mcp.js.map +0 -1
  215. package/dist/tests/test-mini4k.d.ts +0 -6
  216. package/dist/tests/test-mini4k.js.map +0 -1
@@ -3,33 +3,59 @@
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 "@modelcontextprotocol/sdk/types.js";
6
+ import { ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
7
7
  import fs from 'fs';
8
8
  import path from 'path';
9
- // 模拟logger模块
10
- jest.mock('../../src/lib/logger.js', () => ({
11
- log: jest.fn(),
12
- getLogFilePath: jest.fn().mockReturnValue('/mock/path/to/debug.log'),
13
- clearLogFile: jest.fn(),
9
+ import { vi, describe, it, expect, beforeEach } from 'vitest';
10
+ // 创建模拟logger模块
11
+ const mockLogger = {
12
+ log: vi.fn(),
13
+ getLogFilePath: vi.fn().mockReturnValue('/mock/path/to/debug.log'),
14
+ clearLogFile: vi.fn(),
14
15
  COMPONENTS: {
15
16
  RESOURCES: 'resources'
16
17
  }
17
- }));
18
+ };
19
+ // 模拟logger模块
20
+ vi.mock('../../src/lib/logger.js', () => {
21
+ return {
22
+ default: mockLogger,
23
+ ...mockLogger
24
+ };
25
+ });
18
26
  // 模拟fs模块
19
- jest.mock('fs', () => ({
20
- readFileSync: jest.fn()
21
- }));
27
+ vi.mock('fs', () => {
28
+ return {
29
+ default: {
30
+ readFileSync: vi.fn()
31
+ },
32
+ readFileSync: vi.fn()
33
+ };
34
+ });
22
35
  // 模拟path模块
23
- jest.mock('path', () => ({
24
- dirname: jest.fn(),
25
- resolve: jest.fn(),
26
- join: jest.fn(),
27
- extname: jest.fn()
28
- }));
36
+ vi.mock('path', () => {
37
+ return {
38
+ default: {
39
+ dirname: vi.fn(),
40
+ resolve: vi.fn(),
41
+ join: vi.fn(),
42
+ extname: vi.fn()
43
+ },
44
+ dirname: vi.fn(),
45
+ resolve: vi.fn(),
46
+ join: vi.fn(),
47
+ extname: vi.fn()
48
+ };
49
+ });
29
50
  // 模拟url模块
30
- jest.mock('url', () => ({
31
- fileURLToPath: jest.fn()
32
- }));
51
+ vi.mock('url', () => {
52
+ return {
53
+ default: {
54
+ fileURLToPath: vi.fn()
55
+ },
56
+ fileURLToPath: vi.fn()
57
+ };
58
+ });
33
59
  // 获取MIME类型函数
34
60
  function getMimeType(filePath) {
35
61
  const ext = path.extname(filePath).toLowerCase();
@@ -46,6 +72,15 @@ function getMimeType(filePath) {
46
72
  return 'text/html';
47
73
  case '.css':
48
74
  return 'text/css';
75
+ case '.png':
76
+ return 'image/png';
77
+ case '.jpg':
78
+ case '.jpeg':
79
+ return 'image/jpeg';
80
+ case '.gif':
81
+ return 'image/gif';
82
+ case '.svg':
83
+ return 'image/svg+xml';
49
84
  case '.txt':
50
85
  case '.log':
51
86
  return 'text/plain';
@@ -58,7 +93,7 @@ const registerResources = (server) => {
58
93
  // 注册资源列表处理程序
59
94
  server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
60
95
  const debug = request.params?.debug === true;
61
- require('../../src/lib/logger.js').log('resources.list.request', debug, { params: request.params }, 'resources');
96
+ mockLogger.log('resources.list.request', debug, { params: request.params }, 'resources');
62
97
  // 返回实际可用的资源列表
63
98
  return {
64
99
  resources: [
@@ -91,60 +126,60 @@ const registerResources = (server) => {
91
126
  server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
92
127
  const uri = request.params.uri;
93
128
  const debug = request.params?.debug === true;
94
- require('../../src/lib/logger.js').log('resources.read.request', debug, { uri }, 'resources');
129
+ mockLogger.log('resources.read.request', debug, { uri }, 'resources');
95
130
  try {
96
131
  // 解析URI并读取相应文件
97
132
  let filePath = null;
98
133
  let content = '';
99
134
  // 处理日志文件URI
100
135
  if (uri === 'file:///logs/debug') {
101
- filePath = require('../../src/lib/logger.js').getLogFilePath();
136
+ filePath = mockLogger.getLogFilePath();
102
137
  content = fs.readFileSync(filePath, 'utf-8');
103
138
  }
104
- // 处理清理日志文件URI
105
139
  else if (uri === 'file:///logs/clear') {
106
- require('../../src/lib/logger.js').clearLogFile();
107
- content = '日志文件已清理 (Log file has been cleared)';
140
+ mockLogger.clearLogFile();
141
+ content = 'Log file cleared';
142
+ filePath = '/mock/path/to/clear.log';
108
143
  }
109
- // 处理文档资源
110
144
  else if (uri.startsWith('file:///docs/')) {
111
- const resourceName = uri.substring('file:///docs/'.length);
112
- filePath = path.join('/mock/path/to', resourceName + '.md');
145
+ // 处理文档文件URI
146
+ const docPath = uri.replace('file:///docs/', '');
147
+ filePath = path.join('/mock/path/to', `${docPath}.md`);
113
148
  content = fs.readFileSync(filePath, 'utf-8');
114
149
  }
115
- // 处理源代码资源
116
150
  else if (uri.startsWith('file:///src/')) {
117
- const resourceName = uri.substring('file:///src/'.length);
118
- filePath = path.join('/mock/path/to', resourceName + '.ts');
151
+ // 处理源代码文件URI
152
+ const srcPath = uri.replace('file:///src/', '');
153
+ filePath = path.join('/mock/path/to', `${srcPath}.ts`);
119
154
  content = fs.readFileSync(filePath, 'utf-8');
120
155
  }
121
- // 处理无效URI
122
156
  else {
123
- throw new Error('Invalid URI');
157
+ throw new Error(`resources.notFound: ${uri}`);
124
158
  }
125
159
  // 获取MIME类型
126
- const mimeType = getMimeType(filePath || '');
160
+ const mimeType = getMimeType(filePath);
161
+ // 返回资源内容
127
162
  return {
128
163
  contents: [
129
164
  {
130
165
  uri,
131
- mimeType,
166
+ type: mimeType,
132
167
  text: content
133
168
  }
134
169
  ]
135
170
  };
136
171
  }
137
172
  catch (error) {
138
- require('../../src/lib/logger.js').log('resources.fileReadError', true, {
173
+ mockLogger.log('resources.fileReadError', true, {
139
174
  filePath: 'unknown',
140
175
  error: error instanceof Error ? error.message : String(error)
141
176
  }, 'resources');
142
- throw new Error(`resources.notFound: ${uri}`);
177
+ throw error;
143
178
  }
144
179
  });
145
180
  };
146
181
  // 模拟resources模块
147
- jest.mock('../../src/lib/server/resources.js', () => ({
182
+ vi.mock('../../src/lib/server/resources.js', () => ({
148
183
  registerResources
149
184
  }));
150
185
  describe('resources模块测试', () => {
@@ -153,10 +188,10 @@ describe('resources模块测试', () => {
153
188
  let readResourceHandler;
154
189
  beforeEach(() => {
155
190
  // 重置所有模拟
156
- jest.clearAllMocks();
191
+ vi.clearAllMocks();
157
192
  // 创建模拟Server实例
158
193
  server = {
159
- setRequestHandler: jest.fn((schema, handler) => {
194
+ setRequestHandler: vi.fn((schema, handler) => {
160
195
  if (schema === ListResourcesRequestSchema) {
161
196
  listResourcesHandler = handler;
162
197
  }
@@ -182,23 +217,27 @@ describe('resources模块测试', () => {
182
217
  expect(response).toHaveProperty('resources');
183
218
  expect(Array.isArray(response.resources)).toBe(true);
184
219
  expect(response.resources.length).toBe(2);
185
- // 验证返回的资源
186
- const resourceNames = response.resources.map((r) => r.name);
187
- expect(resourceNames).toContain('mult-fetch-mcp:log:debug-log');
188
- expect(resourceNames).toContain('mult-fetch-mcp:log:clear-debug-log');
220
+ // 验证资源列表内容
221
+ expect(response.resources[0]).toHaveProperty('name', 'mult-fetch-mcp:log:debug-log');
222
+ expect(response.resources[0]).toHaveProperty('uri', 'file:///logs/debug');
223
+ expect(response.resources[1]).toHaveProperty('name', 'mult-fetch-mcp:log:clear-debug-log');
224
+ expect(response.resources[1]).toHaveProperty('uri', 'file:///logs/clear');
189
225
  // 验证资源模板
190
226
  expect(response).toHaveProperty('resourceTemplates');
191
227
  expect(Array.isArray(response.resourceTemplates)).toBe(true);
192
228
  expect(response.resourceTemplates.length).toBe(2);
193
- const templateNames = response.resourceTemplates.map((t) => t.name);
194
- expect(templateNames).toContain('mult-fetch-mcp:src:file');
195
- expect(templateNames).toContain('mult-fetch-mcp:docs:file');
229
+ expect(response.resourceTemplates[0]).toHaveProperty('uriTemplate', 'file:///src/{path}');
230
+ expect(response.resourceTemplates[1]).toHaveProperty('uriTemplate', 'file:///docs/{path}');
196
231
  });
197
232
  it('应该处理debug参数', async () => {
198
- const request = { params: { debug: true } };
233
+ const request = {
234
+ params: {
235
+ debug: true
236
+ }
237
+ };
199
238
  await listResourcesHandler(request);
200
239
  // 验证日志调用
201
- expect(require('../../src/lib/logger.js').log).toHaveBeenCalledWith('resources.list.request', true, { params: { debug: true } }, 'resources');
240
+ expect(mockLogger.log).toHaveBeenCalledWith('resources.list.request', true, { params: { debug: true } }, 'resources');
202
241
  });
203
242
  });
204
243
  describe('ReadResourceRequestSchema处理程序', () => {
@@ -217,11 +256,11 @@ describe('resources模块测试', () => {
217
256
  expect(response.contents.length).toBe(1);
218
257
  // 验证内容
219
258
  const content = response.contents[0];
220
- expect(content.uri).toBe('file:///logs/debug');
221
- expect(content.mimeType).toBe('text/plain');
222
- expect(content.text).toBe('Debug log content');
259
+ expect(content).toHaveProperty('uri', 'file:///logs/debug');
260
+ expect(content).toHaveProperty('type', 'text/plain');
261
+ expect(content).toHaveProperty('text', 'Debug log content');
223
262
  // 验证getLogFilePath被调用
224
- expect(require('../../src/lib/logger.js').getLogFilePath).toHaveBeenCalled();
263
+ expect(mockLogger.getLogFilePath).toHaveBeenCalled();
225
264
  expect(fs.readFileSync).toHaveBeenCalledWith('/mock/path/to/debug.log', 'utf-8');
226
265
  });
227
266
  it('应该处理清理日志资源', async () => {
@@ -236,10 +275,10 @@ describe('resources模块测试', () => {
236
275
  expect(response.contents.length).toBe(1);
237
276
  // 验证内容
238
277
  const content = response.contents[0];
239
- expect(content.uri).toBe('file:///logs/clear');
240
- expect(content.text).toContain('日志文件已清理');
278
+ expect(content).toHaveProperty('uri', 'file:///logs/clear');
279
+ expect(content).toHaveProperty('text', 'Log file cleared');
241
280
  // 验证clearLogFile被调用
242
- expect(require('../../src/lib/logger.js').clearLogFile).toHaveBeenCalled();
281
+ expect(mockLogger.clearLogFile).toHaveBeenCalled();
243
282
  });
244
283
  it('应该处理文档资源', async () => {
245
284
  // 模拟路径和文件内容
@@ -248,7 +287,7 @@ describe('resources模块测试', () => {
248
287
  path.extname.mockReturnValue('.md');
249
288
  const request = {
250
289
  params: {
251
- uri: 'file:///docs/readme'
290
+ uri: 'file:///docs/README'
252
291
  }
253
292
  };
254
293
  const response = await readResourceHandler(request);
@@ -257,9 +296,9 @@ describe('resources模块测试', () => {
257
296
  expect(response.contents.length).toBe(1);
258
297
  // 验证内容
259
298
  const content = response.contents[0];
260
- expect(content.uri).toBe('file:///docs/readme');
261
- expect(content.mimeType).toBe('text/markdown');
262
- expect(content.text).toBe('# README content');
299
+ expect(content).toHaveProperty('uri', 'file:///docs/README');
300
+ expect(content).toHaveProperty('type', 'text/markdown');
301
+ expect(content).toHaveProperty('text', '# README content');
263
302
  // 验证path.join被调用
264
303
  expect(path.join).toHaveBeenCalled();
265
304
  expect(fs.readFileSync).toHaveBeenCalledWith('/mock/path/to/README.md', 'utf-8');
@@ -280,9 +319,9 @@ describe('resources模块测试', () => {
280
319
  expect(response.contents.length).toBe(1);
281
320
  // 验证内容
282
321
  const content = response.contents[0];
283
- expect(content.uri).toBe('file:///src/index');
284
- expect(content.mimeType).toBe('text/typescript');
285
- expect(content.text).toBe('// Source code');
322
+ expect(content).toHaveProperty('uri', 'file:///src/index');
323
+ expect(content).toHaveProperty('type', 'text/typescript');
324
+ expect(content).toHaveProperty('text', '// Source code');
286
325
  // 验证path.join被调用
287
326
  expect(path.join).toHaveBeenCalled();
288
327
  expect(fs.readFileSync).toHaveBeenCalledWith('/mock/path/to/index.ts', 'utf-8');
@@ -290,7 +329,7 @@ describe('resources模块测试', () => {
290
329
  it('应该处理无效的URI', async () => {
291
330
  const request = {
292
331
  params: {
293
- uri: 'invalid-uri'
332
+ uri: 'invalid://uri'
294
333
  }
295
334
  };
296
335
  await expect(readResourceHandler(request)).rejects.toThrow('resources.notFound');
@@ -300,17 +339,17 @@ describe('resources模块测试', () => {
300
339
  path.join.mockReturnValue('/mock/path/to/file.txt');
301
340
  // 模拟文件读取错误
302
341
  fs.readFileSync.mockImplementation(() => {
303
- throw new Error('File not found');
342
+ throw new Error('File read error');
304
343
  });
305
344
  const request = {
306
345
  params: {
307
- uri: 'file:///docs/file'
346
+ uri: 'file:///src/error/file'
308
347
  }
309
348
  };
310
- await expect(readResourceHandler(request)).rejects.toThrow('resources.notFound');
349
+ await expect(readResourceHandler(request)).rejects.toThrow('File read error');
311
350
  // 验证错误日志
312
- expect(require('../../src/lib/logger.js').log).toHaveBeenCalledWith('resources.fileReadError', expect.any(Boolean), expect.objectContaining({
313
- error: expect.any(String)
351
+ expect(mockLogger.log).toHaveBeenCalledWith('resources.fileReadError', true, expect.objectContaining({
352
+ error: 'File read error'
314
353
  }), 'resources');
315
354
  });
316
355
  it('应该处理debug参数', async () => {
@@ -325,34 +364,31 @@ describe('resources模块测试', () => {
325
364
  };
326
365
  await readResourceHandler(request);
327
366
  // 验证日志调用
328
- expect(require('../../src/lib/logger.js').log).toHaveBeenCalledWith('resources.read.request', true, { uri: 'file:///logs/debug' }, 'resources');
367
+ expect(mockLogger.log).toHaveBeenCalledWith('resources.read.request', true, { uri: 'file:///logs/debug' }, 'resources');
329
368
  });
330
369
  });
331
370
  describe('getMimeType函数', () => {
332
- it('应该返回正确的MIME类型', async () => {
333
- // 测试不同的文件扩展名
371
+ it('应该返回正确的MIME类型', () => {
334
372
  const testExtensions = [
335
- { ext: '.md', mime: 'text/markdown' },
336
- { ext: '.json', mime: 'application/json' },
337
- { ext: '.ts', mime: 'text/typescript' },
338
- { ext: '.js', mime: 'text/javascript' },
339
373
  { ext: '.html', mime: 'text/html' },
374
+ { ext: '.js', mime: 'text/javascript' },
340
375
  { ext: '.css', mime: 'text/css' },
376
+ { ext: '.json', mime: 'application/json' },
377
+ { ext: '.png', mime: 'image/png' },
378
+ { ext: '.jpg', mime: 'image/jpeg' },
379
+ { ext: '.gif', mime: 'image/gif' },
380
+ { ext: '.svg', mime: 'image/svg+xml' },
381
+ { ext: '.md', mime: 'text/markdown' },
341
382
  { ext: '.txt', mime: 'text/plain' },
342
383
  { ext: '.log', mime: 'text/plain' },
384
+ { ext: '.ts', mime: 'text/typescript' },
343
385
  { ext: '.unknown', mime: 'application/octet-stream' }
344
386
  ];
345
387
  for (const test of testExtensions) {
346
388
  // 模拟文件路径和内容
347
389
  path.extname.mockReturnValue(test.ext);
348
- fs.readFileSync.mockReturnValue('File content');
349
- const request = {
350
- params: {
351
- uri: `file:///docs/file${test.ext}`
352
- }
353
- };
354
- const response = await readResourceHandler(request);
355
- expect(response.contents[0].mimeType).toBe(test.mime);
390
+ const mime = getMimeType(`/path/to/file${test.ext}`);
391
+ expect(mime).toBe(test.mime);
356
392
  }
357
393
  });
358
394
  });
@@ -6,13 +6,14 @@
6
6
  import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
7
7
  import { registerTools } from '../../src/lib/server/tools.js';
8
8
  import { fetchWithAutoDetect } from '../../src/lib/server/fetcher.js';
9
+ import { vi, describe, test, expect, beforeEach } from 'vitest';
9
10
  // 模拟 fetchWithAutoDetect 函数
10
- jest.mock('../../src/lib/server/fetcher.js', () => ({
11
- fetchWithAutoDetect: jest.fn()
11
+ vi.mock('../../src/lib/server/fetcher.js', () => ({
12
+ fetchWithAutoDetect: vi.fn()
12
13
  }));
13
14
  // 模拟 logger
14
- jest.mock('../../src/lib/logger.js', () => ({
15
- log: jest.fn(),
15
+ vi.mock('../../src/lib/logger.js', () => ({
16
+ log: vi.fn(),
16
17
  COMPONENTS: {
17
18
  TOOLS: 'tools'
18
19
  }
@@ -22,9 +23,9 @@ describe('工具注册和调用测试 (Tools Registration and Calling Tests)', (
22
23
  let mockSetRequestHandler;
23
24
  beforeEach(() => {
24
25
  // 重置所有模拟
25
- jest.clearAllMocks();
26
+ vi.clearAllMocks();
26
27
  // 创建模拟的 Server 实例
27
- mockSetRequestHandler = jest.fn();
28
+ mockSetRequestHandler = vi.fn();
28
29
  mockServer = {
29
30
  setRequestHandler: mockSetRequestHandler
30
31
  };
@@ -0,0 +1,36 @@
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
+ // 导入Vitest的全局函数 (Import Vitest global functions)
7
+ import { beforeAll, afterAll, vi } from 'vitest';
8
+ // 全局模拟设置
9
+ // 将 vi 添加到全局,以便在测试中使用
10
+ global.vi = vi;
11
+ // 为了兼容 Jest 的 jest.fn(), jest.mock() 等
12
+ global.jest = {
13
+ fn: vi.fn,
14
+ mock: vi.mock,
15
+ spyOn: vi.spyOn,
16
+ clearAllMocks: vi.clearAllMocks,
17
+ resetAllMocks: vi.resetAllMocks,
18
+ restoreAllMocks: vi.restoreAllMocks
19
+ };
20
+ // 设置测试环境 (Setup test environment)
21
+ process.env.NODE_ENV = 'test';
22
+ // 模拟控制台输出,避免测试时输出过多日志 (Mock console output to avoid too many logs during testing)
23
+ console.log = vi.fn();
24
+ console.info = vi.fn();
25
+ console.warn = vi.fn();
26
+ console.error = vi.fn();
27
+ // 在所有测试开始前的操作 (Operations before all tests)
28
+ beforeAll(() => {
29
+ // 全局设置,在所有测试开始前运行
30
+ // 可以在这里添加全局设置 (Add global setup here)
31
+ });
32
+ // 在所有测试结束后的操作 (Operations after all tests)
33
+ afterAll(() => {
34
+ // 全局清理,在所有测试结束后运行
35
+ // 可以在这里添加全局清理 (Add global cleanup here)
36
+ });
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ /**
3
+ * Author: Martin <lmccc.dev@gmail.com>
4
+ * Co-Author: AI Assistant (Claude)
5
+ * Description: This code was collaboratively developed by Martin and AI Assistant.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.getSupportedLanguages = exports.hasTranslation = exports.getCurrentLanguage = exports.changeLanguage = exports.t = void 0;
9
+ var i18next_1 = require("i18next");
10
+ var index_js_1 = require("./locales/en/index.js");
11
+ var index_js_2 = require("./locales/zh/index.js");
12
+ // 定义语言资源 (Define language resources)
13
+ var resources = {
14
+ en: {
15
+ translation: index_js_1.enTranslation
16
+ },
17
+ zh: {
18
+ translation: index_js_2.zhTranslation
19
+ }
20
+ };
21
+ /**
22
+ * 初始化 i18next 配置 (Initialize i18next configuration)
23
+ */
24
+ var initializeI18n = function () {
25
+ // 获取当前语言环境 (Get current language environment)
26
+ var currentLanguage = process.env.MCP_LANG || 'en';
27
+ // 初始化 i18next (Initialize i18next)
28
+ i18next_1.default.init({
29
+ resources: resources,
30
+ // 优先使用专门的环境变量,其次默认使用英语
31
+ // (Priority: dedicated environment variable, then default to English)
32
+ lng: currentLanguage,
33
+ fallbackLng: 'en',
34
+ interpolation: {
35
+ escapeValue: false // 不转义插值 (Don't escape interpolation)
36
+ },
37
+ debug: process.env.DEBUG === 'true', // 只在调试模式下启用 i18n 调试 (Enable i18n debug only in debug mode)
38
+ returnEmptyString: false, // 不返回空字符串 (Don't return empty string)
39
+ returnNull: false, // 不返回 null (Don't return null)
40
+ returnObjects: true, // 返回对象 (Return objects)
41
+ saveMissing: true, // 保存缺失的翻译键 (Save missing translation keys)
42
+ missingKeyHandler: function (lng, ns, key, fallbackValue) {
43
+ console.error("[i18n] Missing translation key: ".concat(key, " for language: ").concat(lng));
44
+ }
45
+ });
46
+ console.log("[i18n] Initialized with language: ".concat(currentLanguage));
47
+ return i18next_1.default;
48
+ };
49
+ // 初始化 i18next 实例 (Initialize i18next instance)
50
+ var i18n = initializeI18n();
51
+ // 导出 i18next 实例 (Export i18next instance)
52
+ exports.default = i18n;
53
+ /**
54
+ * 翻译函数 - 提供类型安全和错误处理 (Translation function - provides type safety and error handling)
55
+ * @param key 翻译键 (Translation key)
56
+ * @param options 翻译选项 (Translation options)
57
+ * @returns 翻译结果 (Translation result)
58
+ */
59
+ var t = function (key, options) {
60
+ try {
61
+ var result = i18n.t(key, options);
62
+ // 如果结果与键相同,说明没有找到翻译 (If the result is the same as the key, the translation was not found)
63
+ if (result === key) {
64
+ console.warn("[i18n] Translation key not found: ".concat(key));
65
+ }
66
+ return result;
67
+ }
68
+ catch (error) {
69
+ console.error("[i18n] Error translating key: ".concat(key), error);
70
+ return key; // 出错时返回原始键 (Return the original key when an error occurs)
71
+ }
72
+ };
73
+ exports.t = t;
74
+ /**
75
+ * 切换语言函数 (Language switching function)
76
+ * @param lng 目标语言 (Target language)
77
+ * @returns Promise,解析为切换后的语言 (Promise that resolves to the switched language)
78
+ */
79
+ var changeLanguage = function (lng) {
80
+ console.log("[i18n] Changing language to: ".concat(lng));
81
+ return i18n.changeLanguage(lng).then(function () { return lng; });
82
+ };
83
+ exports.changeLanguage = changeLanguage;
84
+ /**
85
+ * 获取当前语言函数 (Get current language function)
86
+ * @returns 当前语言 (Current language)
87
+ */
88
+ var getCurrentLanguage = function () {
89
+ return (i18n.language || 'en');
90
+ };
91
+ exports.getCurrentLanguage = getCurrentLanguage;
92
+ /**
93
+ * 检查键是否存在 (Check if a key exists)
94
+ * @param key 要检查的键 (Key to check)
95
+ * @returns 键是否存在 (Whether the key exists)
96
+ */
97
+ var hasTranslation = function (key) {
98
+ return i18n.exists(key);
99
+ };
100
+ exports.hasTranslation = hasTranslation;
101
+ /**
102
+ * 获取所有支持的语言 (Get all supported languages)
103
+ * @returns 支持的语言数组 (Array of supported languages)
104
+ */
105
+ var getSupportedLanguages = function () {
106
+ return Object.keys(resources);
107
+ };
108
+ exports.getSupportedLanguages = getSupportedLanguages;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ /**
3
+ * Author: Martin <lmccc.dev@gmail.com>
4
+ * Co-Author: AI Assistant (Claude)
5
+ * Description: This code was collaboratively developed by Martin and AI Assistant.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.createResourceKeys = exports.createResourceOperationKeys = exports.createKeyGenerator = void 0;
9
+ /**
10
+ * 创建键名生成器 - 用于生成特定模块的键名 (Create key name generator - for generating key names for specific modules)
11
+ * @param module 模块名称 (Module name)
12
+ * @returns 键名生成函数 (Key name generator function)
13
+ */
14
+ var createKeyGenerator = function (module) { return function (key) { return "".concat(module, ".").concat(key); }; };
15
+ exports.createKeyGenerator = createKeyGenerator;
16
+ /**
17
+ * 创建资源操作键 - 用于生成资源操作相关的键 (Create resource operation keys - for generating resource operation related keys)
18
+ * @param module 模块名称 (Module name)
19
+ * @param operation 操作名称 (Operation name)
20
+ * @returns 资源操作键对象 (Resource operation keys object)
21
+ */
22
+ var createResourceOperationKeys = function (module, operation) {
23
+ var keyGen = (0, exports.createKeyGenerator)("".concat(module, ".").concat(operation));
24
+ return {
25
+ request: keyGen('request'),
26
+ success: keyGen('success'),
27
+ error: keyGen('error'),
28
+ notFound: keyGen('notFound'),
29
+ duplicate: keyGen('duplicate')
30
+ };
31
+ };
32
+ exports.createResourceOperationKeys = createResourceOperationKeys;
33
+ /**
34
+ * 创建资源键 - 用于生成资源相关的键 (Create resource keys - for generating resource related keys)
35
+ * @param module 模块名称 (Module name)
36
+ * @returns 资源键对象 (Resource keys object)
37
+ */
38
+ var createResourceKeys = function (module) {
39
+ return {
40
+ list: (0, exports.createResourceOperationKeys)(module, 'list'),
41
+ get: (0, exports.createResourceOperationKeys)(module, 'get'),
42
+ create: (0, exports.createResourceOperationKeys)(module, 'create'),
43
+ update: (0, exports.createResourceOperationKeys)(module, 'update'),
44
+ delete: (0, exports.createResourceOperationKeys)(module, 'delete')
45
+ };
46
+ };
47
+ exports.createResourceKeys = createResourceKeys;