@mxf-dev/core 1.0.1 → 2.0.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 (303) hide show
  1. package/README.md +18 -0
  2. package/dist/adapters/FrameworkAdapter.js +1 -1
  3. package/dist/adapters/FrameworkAdapter.js.map +1 -1
  4. package/dist/config/ConfigManager.d.ts +9 -8
  5. package/dist/config/ConfigManager.d.ts.map +1 -1
  6. package/dist/config/ConfigManager.js +47 -17
  7. package/dist/config/ConfigManager.js.map +1 -1
  8. package/dist/config/memory-strata.config.d.ts +25 -0
  9. package/dist/config/memory-strata.config.d.ts.map +1 -0
  10. package/dist/config/memory-strata.config.js +107 -0
  11. package/dist/config/memory-strata.config.js.map +1 -0
  12. package/dist/constants/ToolNames.d.ts +1 -29
  13. package/dist/constants/ToolNames.d.ts.map +1 -1
  14. package/dist/constants/ToolNames.js +4 -17
  15. package/dist/constants/ToolNames.js.map +1 -1
  16. package/dist/database/DatabaseAdapterFactory.d.ts +14 -2
  17. package/dist/database/DatabaseAdapterFactory.d.ts.map +1 -1
  18. package/dist/database/DatabaseAdapterFactory.js +21 -6
  19. package/dist/database/DatabaseAdapterFactory.js.map +1 -1
  20. package/dist/database/adapters/mongodb/MongoBaseRepository.d.ts +7 -1
  21. package/dist/database/adapters/mongodb/MongoBaseRepository.d.ts.map +1 -1
  22. package/dist/database/adapters/mongodb/MongoBaseRepository.js +12 -2
  23. package/dist/database/adapters/mongodb/MongoBaseRepository.js.map +1 -1
  24. package/dist/events/ClientEventBus.d.ts +16 -77
  25. package/dist/events/ClientEventBus.d.ts.map +1 -1
  26. package/dist/events/ClientEventBus.js +38 -311
  27. package/dist/events/ClientEventBus.js.map +1 -1
  28. package/dist/events/EventBus.d.ts +1 -1
  29. package/dist/events/EventBus.d.ts.map +1 -1
  30. package/dist/events/EventBus.js +5 -4
  31. package/dist/events/EventBus.js.map +1 -1
  32. package/dist/events/EventBusBase.d.ts +139 -34
  33. package/dist/events/EventBusBase.d.ts.map +1 -1
  34. package/dist/events/EventBusBase.js +366 -88
  35. package/dist/events/EventBusBase.js.map +1 -1
  36. package/dist/events/EventNames.d.ts +10 -1
  37. package/dist/events/EventNames.d.ts.map +1 -1
  38. package/dist/events/EventNames.js +3 -0
  39. package/dist/events/EventNames.js.map +1 -1
  40. package/dist/events/PublicEvents.d.ts +6 -2
  41. package/dist/events/PublicEvents.d.ts.map +1 -1
  42. package/dist/events/PublicEvents.js +7 -4
  43. package/dist/events/PublicEvents.js.map +1 -1
  44. package/dist/events/ServerEventBus.d.ts +23 -112
  45. package/dist/events/ServerEventBus.d.ts.map +1 -1
  46. package/dist/events/ServerEventBus.js +58 -323
  47. package/dist/events/ServerEventBus.js.map +1 -1
  48. package/dist/events/event-definitions/LlmBudgetEvents.d.ts +68 -0
  49. package/dist/events/event-definitions/LlmBudgetEvents.d.ts.map +1 -0
  50. package/dist/events/event-definitions/LlmBudgetEvents.js +35 -0
  51. package/dist/events/event-definitions/LlmBudgetEvents.js.map +1 -0
  52. package/dist/interfaces/IMemoryPersistence.d.ts +41 -0
  53. package/dist/interfaces/IMemoryPersistence.d.ts.map +1 -0
  54. package/dist/interfaces/IMemoryPersistence.js +15 -0
  55. package/dist/interfaces/IMemoryPersistence.js.map +1 -0
  56. package/dist/middleware/MxpMiddleware.d.ts +8 -1
  57. package/dist/middleware/MxpMiddleware.d.ts.map +1 -1
  58. package/dist/middleware/MxpMiddleware.js +42 -20
  59. package/dist/middleware/MxpMiddleware.js.map +1 -1
  60. package/dist/models/channelKey.d.ts +1 -0
  61. package/dist/models/channelKey.d.ts.map +1 -1
  62. package/dist/models/channelKey.js +5 -0
  63. package/dist/models/channelKey.js.map +1 -1
  64. package/dist/models/memoryUtility.d.ts +39 -0
  65. package/dist/models/memoryUtility.d.ts.map +1 -0
  66. package/dist/models/memoryUtility.js +39 -0
  67. package/dist/models/memoryUtility.js.map +1 -0
  68. package/dist/models/user.d.ts.map +1 -1
  69. package/dist/models/user.js +23 -1
  70. package/dist/models/user.js.map +1 -1
  71. package/dist/mxp/BinaryProtocolLayer.d.ts +24 -8
  72. package/dist/mxp/BinaryProtocolLayer.d.ts.map +1 -1
  73. package/dist/mxp/BinaryProtocolLayer.js +90 -103
  74. package/dist/mxp/BinaryProtocolLayer.js.map +1 -1
  75. package/dist/mxp/ContextCompressionEngine.d.ts +57 -10
  76. package/dist/mxp/ContextCompressionEngine.d.ts.map +1 -1
  77. package/dist/mxp/ContextCompressionEngine.js +170 -128
  78. package/dist/mxp/ContextCompressionEngine.js.map +1 -1
  79. package/dist/mxp/MxpTokenOptimizer.d.ts +7 -3
  80. package/dist/mxp/MxpTokenOptimizer.d.ts.map +1 -1
  81. package/dist/mxp/MxpTokenOptimizer.js +8 -5
  82. package/dist/mxp/MxpTokenOptimizer.js.map +1 -1
  83. package/dist/prompts/MxfAgentSystemPrompt.d.ts +1 -1
  84. package/dist/prompts/MxfAgentSystemPrompt.d.ts.map +1 -1
  85. package/dist/prompts/MxfAgentSystemPrompt.js +47 -57
  86. package/dist/prompts/MxfAgentSystemPrompt.js.map +1 -1
  87. package/dist/protocols/mcp/ToolError.d.ts +123 -0
  88. package/dist/protocols/mcp/ToolError.d.ts.map +1 -0
  89. package/dist/protocols/mcp/ToolError.js +108 -0
  90. package/dist/protocols/mcp/ToolError.js.map +1 -0
  91. package/dist/protocols/mcp/defineTool.d.ts +133 -0
  92. package/dist/protocols/mcp/defineTool.d.ts.map +1 -0
  93. package/dist/protocols/mcp/defineTool.js +236 -0
  94. package/dist/protocols/mcp/defineTool.js.map +1 -0
  95. package/dist/protocols/mcp/security/HttpTargetGuard.d.ts +38 -0
  96. package/dist/protocols/mcp/security/HttpTargetGuard.d.ts.map +1 -0
  97. package/dist/protocols/mcp/security/HttpTargetGuard.js +227 -0
  98. package/dist/protocols/mcp/security/HttpTargetGuard.js.map +1 -0
  99. package/dist/protocols/mcp/security/McpSecurityGuard.d.ts +9 -1
  100. package/dist/protocols/mcp/security/McpSecurityGuard.d.ts.map +1 -1
  101. package/dist/protocols/mcp/security/McpSecurityGuard.js +10 -2
  102. package/dist/protocols/mcp/security/McpSecurityGuard.js.map +1 -1
  103. package/dist/protocols/mcp/security/McpToolPolicy.d.ts +81 -0
  104. package/dist/protocols/mcp/security/McpToolPolicy.d.ts.map +1 -0
  105. package/dist/protocols/mcp/security/McpToolPolicy.js +202 -0
  106. package/dist/protocols/mcp/security/McpToolPolicy.js.map +1 -0
  107. package/dist/protocols/mcp/services/ExternalMcpServerManager.d.ts +51 -18
  108. package/dist/protocols/mcp/services/ExternalMcpServerManager.d.ts.map +1 -1
  109. package/dist/protocols/mcp/services/ExternalMcpServerManager.js +256 -283
  110. package/dist/protocols/mcp/services/ExternalMcpServerManager.js.map +1 -1
  111. package/dist/protocols/mcp/services/ExternalServerConfigs.d.ts +39 -6
  112. package/dist/protocols/mcp/services/ExternalServerConfigs.d.ts.map +1 -1
  113. package/dist/protocols/mcp/services/ExternalServerConfigs.js +76 -18
  114. package/dist/protocols/mcp/services/ExternalServerConfigs.js.map +1 -1
  115. package/dist/protocols/mcp/tools/AnalyticsTools.js +1 -1
  116. package/dist/protocols/mcp/tools/AnalyticsTools.js.map +1 -1
  117. package/dist/protocols/mcp/tools/CodeAnalysisTools.d.ts.map +1 -1
  118. package/dist/protocols/mcp/tools/CodeAnalysisTools.js +15 -0
  119. package/dist/protocols/mcp/tools/CodeAnalysisTools.js.map +1 -1
  120. package/dist/protocols/mcp/tools/EffectivenessTools.js +3 -3
  121. package/dist/protocols/mcp/tools/EffectivenessTools.js.map +1 -1
  122. package/dist/protocols/mcp/tools/GitTools.d.ts +9 -369
  123. package/dist/protocols/mcp/tools/GitTools.d.ts.map +1 -1
  124. package/dist/protocols/mcp/tools/GitTools.js +222 -517
  125. package/dist/protocols/mcp/tools/GitTools.js.map +1 -1
  126. package/dist/protocols/mcp/tools/InfrastructureTools.d.ts +56 -24
  127. package/dist/protocols/mcp/tools/InfrastructureTools.d.ts.map +1 -1
  128. package/dist/protocols/mcp/tools/InfrastructureTools.js +117 -26
  129. package/dist/protocols/mcp/tools/InfrastructureTools.js.map +1 -1
  130. package/dist/protocols/mcp/tools/MemorySearchTools.d.ts +0 -3
  131. package/dist/protocols/mcp/tools/MemorySearchTools.d.ts.map +1 -1
  132. package/dist/protocols/mcp/tools/MemorySearchTools.js +58 -1
  133. package/dist/protocols/mcp/tools/MemorySearchTools.js.map +1 -1
  134. package/dist/protocols/mcp/tools/OrparTools.d.ts +8 -0
  135. package/dist/protocols/mcp/tools/OrparTools.d.ts.map +1 -1
  136. package/dist/protocols/mcp/tools/OrparTools.js +66 -0
  137. package/dist/protocols/mcp/tools/OrparTools.js.map +1 -1
  138. package/dist/protocols/mcp/tools/SafetyTools.d.ts +7 -525
  139. package/dist/protocols/mcp/tools/SafetyTools.d.ts.map +1 -1
  140. package/dist/protocols/mcp/tools/SafetyTools.js +349 -514
  141. package/dist/protocols/mcp/tools/SafetyTools.js.map +1 -1
  142. package/dist/protocols/mcp/tools/TaskPlanningTools.d.ts.map +1 -1
  143. package/dist/protocols/mcp/tools/TaskPlanningTools.js +56 -26
  144. package/dist/protocols/mcp/tools/TaskPlanningTools.js.map +1 -1
  145. package/dist/protocols/mcp/tools/TestTools.d.ts +15 -376
  146. package/dist/protocols/mcp/tools/TestTools.d.ts.map +1 -1
  147. package/dist/protocols/mcp/tools/TestTools.js +241 -363
  148. package/dist/protocols/mcp/tools/TestTools.js.map +1 -1
  149. package/dist/protocols/mcp/tools/TypeScriptTools.d.ts +6 -276
  150. package/dist/protocols/mcp/tools/TypeScriptTools.d.ts.map +1 -1
  151. package/dist/protocols/mcp/tools/TypeScriptTools.js +223 -337
  152. package/dist/protocols/mcp/tools/TypeScriptTools.js.map +1 -1
  153. package/dist/protocols/mcp/tools/WebTools.d.ts +15 -728
  154. package/dist/protocols/mcp/tools/WebTools.d.ts.map +1 -1
  155. package/dist/protocols/mcp/tools/WebTools.js +151 -257
  156. package/dist/protocols/mcp/tools/WebTools.js.map +1 -1
  157. package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.d.ts +5 -3
  158. package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.d.ts.map +1 -1
  159. package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.js +31 -6
  160. package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.js.map +1 -1
  161. package/dist/schemas/EventPayloadSchema.d.ts +21 -0
  162. package/dist/schemas/EventPayloadSchema.d.ts.map +1 -1
  163. package/dist/schemas/EventPayloadSchema.js +14 -0
  164. package/dist/schemas/EventPayloadSchema.js.map +1 -1
  165. package/dist/services/BackgroundTaskManager.d.ts.map +1 -1
  166. package/dist/services/BackgroundTaskManager.js +12 -4
  167. package/dist/services/BackgroundTaskManager.js.map +1 -1
  168. package/dist/services/BrowserManager.d.ts +1 -1
  169. package/dist/services/BrowserManager.d.ts.map +1 -1
  170. package/dist/services/BrowserManager.js +26 -3
  171. package/dist/services/BrowserManager.js.map +1 -1
  172. package/dist/services/MemoryCompressor.d.ts +11 -2
  173. package/dist/services/MemoryCompressor.d.ts.map +1 -1
  174. package/dist/services/MemoryCompressor.js +8 -26
  175. package/dist/services/MemoryCompressor.js.map +1 -1
  176. package/dist/services/MemoryService.d.ts +16 -3
  177. package/dist/services/MemoryService.d.ts.map +1 -1
  178. package/dist/services/MemoryService.js +79 -55
  179. package/dist/services/MemoryService.js.map +1 -1
  180. package/dist/services/MxfMLService.d.ts +5 -0
  181. package/dist/services/MxfMLService.d.ts.map +1 -1
  182. package/dist/services/MxfMLService.js +32 -6
  183. package/dist/services/MxfMLService.js.map +1 -1
  184. package/dist/services/PredictiveAnalyticsService.d.ts +45 -9
  185. package/dist/services/PredictiveAnalyticsService.d.ts.map +1 -1
  186. package/dist/services/PredictiveAnalyticsService.js +82 -44
  187. package/dist/services/PredictiveAnalyticsService.js.map +1 -1
  188. package/dist/services/QValueManager.d.ts +7 -0
  189. package/dist/services/QValueManager.d.ts.map +1 -1
  190. package/dist/services/QValueManager.js +9 -0
  191. package/dist/services/QValueManager.js.map +1 -1
  192. package/dist/services/RewardSignalProcessor.d.ts +38 -1
  193. package/dist/services/RewardSignalProcessor.d.ts.map +1 -1
  194. package/dist/services/RewardSignalProcessor.js +109 -7
  195. package/dist/services/RewardSignalProcessor.js.map +1 -1
  196. package/dist/services/SharedStateService.d.ts +16 -2
  197. package/dist/services/SharedStateService.d.ts.map +1 -1
  198. package/dist/services/SharedStateService.js +44 -3
  199. package/dist/services/SharedStateService.js.map +1 -1
  200. package/dist/services/TaskEffectivenessService.d.ts +34 -18
  201. package/dist/services/TaskEffectivenessService.d.ts.map +1 -1
  202. package/dist/services/TaskEffectivenessService.js +91 -155
  203. package/dist/services/TaskEffectivenessService.js.map +1 -1
  204. package/dist/services/WebSearchService.d.ts.map +1 -1
  205. package/dist/services/WebSearchService.js.map +1 -1
  206. package/dist/services/dag/TaskDagService.d.ts.map +1 -1
  207. package/dist/services/dag/TaskDagService.js +2 -0
  208. package/dist/services/dag/TaskDagService.js.map +1 -1
  209. package/dist/services/orpar-memory/OrparMemoryCoordinator.d.ts +7 -0
  210. package/dist/services/orpar-memory/OrparMemoryCoordinator.d.ts.map +1 -1
  211. package/dist/services/orpar-memory/OrparMemoryCoordinator.js +71 -10
  212. package/dist/services/orpar-memory/OrparMemoryCoordinator.js.map +1 -1
  213. package/dist/types/MemoryUtilityTypes.d.ts +2 -0
  214. package/dist/types/MemoryUtilityTypes.d.ts.map +1 -1
  215. package/dist/types/MemoryUtilityTypes.js.map +1 -1
  216. package/dist/utils/Logger.d.ts +43 -3
  217. package/dist/utils/Logger.d.ts.map +1 -1
  218. package/dist/utils/Logger.js +78 -36
  219. package/dist/utils/Logger.js.map +1 -1
  220. package/dist/utils/MxpEncryption.d.ts +19 -6
  221. package/dist/utils/MxpEncryption.d.ts.map +1 -1
  222. package/dist/utils/MxpEncryption.js +48 -16
  223. package/dist/utils/MxpEncryption.js.map +1 -1
  224. package/package.json +19 -4
  225. package/src/adapters/FrameworkAdapter.ts +1 -1
  226. package/src/config/ConfigManager.ts +52 -19
  227. package/src/config/memory-strata.config.ts +136 -0
  228. package/src/constants/ToolNames.ts +4 -18
  229. package/src/database/DatabaseAdapterFactory.ts +23 -6
  230. package/src/database/adapters/mongodb/MongoBaseRepository.ts +16 -2
  231. package/src/events/ClientEventBus.ts +68 -403
  232. package/src/events/EventBus.ts +7 -8
  233. package/src/events/EventBusBase.ts +479 -116
  234. package/src/events/EventNames.ts +6 -0
  235. package/src/events/PublicEvents.ts +7 -4
  236. package/src/events/ServerEventBus.ts +78 -473
  237. package/src/events/event-definitions/LlmBudgetEvents.ts +74 -0
  238. package/src/interfaces/IMemoryPersistence.ts +51 -0
  239. package/src/middleware/MxpMiddleware.ts +51 -24
  240. package/src/models/channelKey.ts +18 -4
  241. package/src/models/memoryUtility.ts +64 -0
  242. package/src/models/user.ts +36 -4
  243. package/src/mxp/BinaryProtocolLayer.ts +108 -124
  244. package/src/mxp/ContextCompressionEngine.ts +219 -161
  245. package/src/mxp/MxpTokenOptimizer.ts +10 -10
  246. package/src/prompts/MxfAgentSystemPrompt.ts +45 -55
  247. package/src/protocols/mcp/ToolError.ts +201 -0
  248. package/src/protocols/mcp/defineTool.ts +379 -0
  249. package/src/protocols/mcp/security/HttpTargetGuard.ts +267 -0
  250. package/src/protocols/mcp/security/McpSecurityGuard.ts +10 -3
  251. package/src/protocols/mcp/security/McpToolPolicy.ts +221 -0
  252. package/src/protocols/mcp/services/ExternalMcpServerManager.ts +340 -322
  253. package/src/protocols/mcp/services/ExternalServerConfigs.ts +82 -19
  254. package/src/protocols/mcp/tools/AnalyticsTools.ts +1 -1
  255. package/src/protocols/mcp/tools/CodeAnalysisTools.ts +15 -0
  256. package/src/protocols/mcp/tools/EffectivenessTools.ts +3 -3
  257. package/src/protocols/mcp/tools/GitTools.ts +290 -569
  258. package/src/protocols/mcp/tools/InfrastructureTools.ts +189 -43
  259. package/src/protocols/mcp/tools/MemorySearchTools.ts +77 -1
  260. package/src/protocols/mcp/tools/OrparTools.ts +87 -0
  261. package/src/protocols/mcp/tools/SafetyTools.ts +501 -579
  262. package/src/protocols/mcp/tools/TaskPlanningTools.ts +81 -39
  263. package/src/protocols/mcp/tools/TestTools.ts +339 -419
  264. package/src/protocols/mcp/tools/TypeScriptTools.ts +316 -376
  265. package/src/protocols/mcp/tools/WebTools.ts +209 -307
  266. package/src/protocols/mcp/tools/shell/ShellExecuteHandler.ts +53 -14
  267. package/src/schemas/EventPayloadSchema.ts +37 -0
  268. package/src/services/BackgroundTaskManager.ts +12 -4
  269. package/src/services/BrowserManager.ts +41 -1
  270. package/src/services/MemoryCompressor.ts +20 -32
  271. package/src/services/MemoryService.ts +96 -58
  272. package/src/services/MxfMLService.ts +41 -7
  273. package/src/services/PredictiveAnalyticsService.ts +101 -51
  274. package/src/services/QValueManager.ts +10 -0
  275. package/src/services/RewardSignalProcessor.ts +138 -9
  276. package/src/services/SharedStateService.ts +56 -8
  277. package/src/services/TaskEffectivenessService.ts +114 -187
  278. package/src/services/WebSearchService.ts +2 -1
  279. package/src/services/dag/TaskDagService.ts +2 -0
  280. package/src/services/orpar-memory/OrparMemoryCoordinator.ts +86 -10
  281. package/src/types/MemoryUtilityTypes.ts +2 -0
  282. package/src/utils/Logger.ts +116 -40
  283. package/src/utils/MxpEncryption.ts +69 -34
  284. package/dist/mxp/MxpEventForwardingEnhancer.d.ts +0 -134
  285. package/dist/mxp/MxpEventForwardingEnhancer.d.ts.map +0 -1
  286. package/dist/mxp/MxpEventForwardingEnhancer.js +0 -361
  287. package/dist/mxp/MxpEventForwardingEnhancer.js.map +0 -1
  288. package/dist/protocols/mcp/services/ToolSchemaEnhancements.d.ts +0 -56
  289. package/dist/protocols/mcp/services/ToolSchemaEnhancements.d.ts.map +0 -1
  290. package/dist/protocols/mcp/services/ToolSchemaEnhancements.js +0 -57
  291. package/dist/protocols/mcp/services/ToolSchemaEnhancements.js.map +0 -1
  292. package/dist/protocols/mcp/tools/ControlLoopLifecycle.d.ts +0 -270
  293. package/dist/protocols/mcp/tools/ControlLoopLifecycle.d.ts.map +0 -1
  294. package/dist/protocols/mcp/tools/ControlLoopLifecycle.js +0 -256
  295. package/dist/protocols/mcp/tools/ControlLoopLifecycle.js.map +0 -1
  296. package/dist/protocols/mcp/tools/LspTools.d.ts +0 -495
  297. package/dist/protocols/mcp/tools/LspTools.d.ts.map +0 -1
  298. package/dist/protocols/mcp/tools/LspTools.js +0 -469
  299. package/dist/protocols/mcp/tools/LspTools.js.map +0 -1
  300. package/src/mxp/MxpEventForwardingEnhancer.ts +0 -471
  301. package/src/protocols/mcp/services/ToolSchemaEnhancements.ts +0 -92
  302. package/src/protocols/mcp/tools/ControlLoopLifecycle.ts +0 -302
  303. package/src/protocols/mcp/tools/LspTools.ts +0 -476
@@ -18,469 +18,409 @@
18
18
  * @documentation https://mxf-dev.github.io/mxf/
19
19
  */
20
20
 
21
- import { Logger } from '../../../utils/Logger.js';
22
- import { executeShellCommand } from './InfrastructureTools.js';
23
- import { McpToolHandlerContext, McpToolHandlerResult, McpToolResultContent } from '../McpServerTypes.js';
21
+ /**
22
+ * TypeScriptTools.ts
23
+ *
24
+ * TypeScript checking, building, formatting, linting and test running. Each tool
25
+ * shells out to the real binary (tsc, prettier, eslint, jest) through
26
+ * executeShellCommand, which validates the command and strips the child's
27
+ * environment.
28
+ *
29
+ * On exit codes: tsc, eslint and jest all exit non-zero to mean "I found
30
+ * problems". That is a successful tool call with a negative finding, not a tool
31
+ * failure — so those tools return `passed: false` with the diagnostics, and
32
+ * `isError` stays false. `isError` is reserved for the tool genuinely not
33
+ * working: the binary is missing, the working directory does not exist, output
34
+ * cannot be parsed.
35
+ */
36
+
37
+ import { TOOL_CATEGORIES } from '../../../constants/ToolNames.js';
38
+ import { defineTool, ToolRunContext } from '../defineTool.js';
39
+ import { ToolError } from '../ToolError.js';
40
+ import { executeShellCommand, ShellCommandResult } from './InfrastructureTools.js';
24
41
 
25
- const logger = new Logger('info', 'TypeScriptTools', 'server');
42
+ /** Exit code the shell reports when a command could not be found or spawned. */
43
+ const COMMAND_NOT_FOUND = 127;
44
+
45
+ const workingDirectoryProperty = {
46
+ type: 'string',
47
+ description: 'Working directory path (defaults to the server working directory)'
48
+ };
26
49
 
27
50
  /**
28
- * TypeScript Language Server Tools for MXF
29
- *
30
- * Provides TypeScript analysis, diagnostics, and code intelligence
31
- * These tools are placeholders that demonstrate the interface
32
- * Full implementation would integrate with shell_execute tool
51
+ * Run one of the node toolchain binaries via npx.
52
+ *
53
+ * Throws only when the binary could not be run at all. A non-zero exit that the
54
+ * binary uses to report findings is handed back to the caller to interpret.
33
55
  */
56
+ async function runNpx(
57
+ args: string[],
58
+ workingDirectory: string | undefined,
59
+ context: ToolRunContext
60
+ ): Promise<ShellCommandResult> {
61
+ const result = await executeShellCommand('npx', args, {
62
+ context: {
63
+ agentId: context.agentId,
64
+ channelId: context.channelId,
65
+ requestId: context.requestId
66
+ },
67
+ workingDirectory: workingDirectory || process.cwd(),
68
+ captureOutput: true
69
+ });
70
+
71
+ if (result.exitCode === COMMAND_NOT_FOUND) {
72
+ throw ToolError.preconditionFailed(
73
+ `Could not run "npx ${args[0]}" — the command was not found. ` +
74
+ `${result.stderr?.trim() ?? ''}`.trim(),
75
+ { command: args[0] }
76
+ );
77
+ }
34
78
 
35
- export const typescriptCheckTool = {
79
+ return result;
80
+ }
81
+
82
+ /** Pull `file(line,col): error TSxxxx: message` diagnostics out of tsc output. */
83
+ function parseTscDiagnostics(output: string): Array<{
84
+ type: 'error' | 'warning';
85
+ file: string;
86
+ line: number;
87
+ column: number;
88
+ code: string;
89
+ message: string;
90
+ }> {
91
+ const diagnostics: Array<{
92
+ type: 'error' | 'warning';
93
+ file: string;
94
+ line: number;
95
+ column: number;
96
+ code: string;
97
+ message: string;
98
+ }> = [];
99
+
100
+ // tsc --pretty false emits: path/file.ts(12,5): error TS2304: Cannot find name 'x'.
101
+ const pattern = /^(.+?)\((\d+),(\d+)\):\s+(error|warning)\s+(TS\d+):\s+(.*)$/;
102
+
103
+ for (const line of output.split('\n')) {
104
+ const match = line.match(pattern);
105
+ if (!match) {
106
+ continue;
107
+ }
108
+ diagnostics.push({
109
+ type: match[4] as 'error' | 'warning',
110
+ file: match[1],
111
+ line: parseInt(match[2], 10),
112
+ column: parseInt(match[3], 10),
113
+ code: match[5],
114
+ message: match[6].trim()
115
+ });
116
+ }
117
+
118
+ return diagnostics;
119
+ }
120
+
121
+ export const typescriptCheckTool = defineTool<
122
+ { workingDirectory?: string; files?: string[] },
123
+ {
124
+ passed: boolean;
125
+ totalErrors: number;
126
+ totalWarnings: number;
127
+ diagnostics: ReturnType<typeof parseTscDiagnostics>;
128
+ }
129
+ >({
36
130
  name: 'typescript_check',
37
- description: 'Type-check TypeScript files and report diagnostics',
131
+ category: TOOL_CATEGORIES.INFRASTRUCTURE,
132
+ description:
133
+ 'Type-check TypeScript with `tsc --noEmit` and return the diagnostics. ' +
134
+ 'passed is false when there are type errors.',
38
135
  inputSchema: {
39
136
  type: 'object',
40
137
  properties: {
41
- workingDirectory: {
42
- type: 'string',
43
- description: 'Working directory path (defaults to current directory)'
44
- },
138
+ workingDirectory: workingDirectoryProperty,
45
139
  files: {
46
140
  type: 'array',
47
141
  items: { type: 'string' },
48
- description: 'Specific files to check (optional, checks all if not provided)'
142
+ description: 'Specific files to check. Checks the whole project when omitted.'
49
143
  }
50
144
  }
51
145
  },
52
- handler: async (args: {
53
- workingDirectory?: string;
54
- files?: string[];
55
- }, context: McpToolHandlerContext): Promise<McpToolHandlerResult> => {
56
-
57
- try {
58
- const tscArgs = ['--noEmit', '--pretty', 'false'];
59
- if (args.files && args.files.length > 0) {
60
- tscArgs.push(...args.files);
61
- }
62
-
63
- const result = await executeShellCommand('npx', ['tsc', ...tscArgs], {
64
- workingDirectory: args.workingDirectory || process.cwd(),
65
- captureOutput: true
66
- });
67
-
68
- // TypeScript compiler exits with code 1 if there are errors, but this is expected
69
- const diagnostics = [];
70
- let totalErrors = 0;
71
- let totalWarnings = 0;
72
- let filesChecked = 0;
73
-
74
- if (result.stdout) {
75
- // Parse TypeScript compiler output
76
- const lines = result.stdout.split('\n');
77
- for (const line of lines) {
78
- if (line.includes('error TS')) {
79
- totalErrors++;
80
- diagnostics.push({
81
- type: 'error',
82
- message: line.trim(),
83
- file: line.split('(')[0] || '',
84
- line: 0,
85
- column: 0
86
- });
87
- } else if (line.includes('warning TS')) {
88
- totalWarnings++;
89
- diagnostics.push({
90
- type: 'warning',
91
- message: line.trim(),
92
- file: line.split('(')[0] || '',
93
- line: 0,
94
- column: 0
95
- });
96
- }
97
- }
98
- }
146
+ run: async (input, context) => {
147
+ const tscArgs = ['tsc', '--noEmit', '--pretty', 'false'];
148
+ if (input.files && input.files.length > 0) {
149
+ tscArgs.push(...input.files);
150
+ }
151
+
152
+ const result = await runNpx(tscArgs, input.workingDirectory, context);
99
153
 
100
- const content: McpToolResultContent = {
101
- type: 'application/json',
102
- data: {
103
- success: result.exitCode === 0,
104
- diagnostics,
105
- totalErrors,
106
- totalWarnings,
107
- filesChecked: args.files ? args.files.length : 0,
108
- error: result.exitCode !== 0 ? result.stderr : null
109
- }
110
- };
111
- return { content };
112
- } catch (error) {
113
- logger.error('TypeScript check failed', { error });
114
- const content: McpToolResultContent = {
115
- type: 'application/json',
116
- data: {
117
- success: false,
118
- diagnostics: [],
119
- totalErrors: 0,
120
- totalWarnings: 0,
121
- filesChecked: 0,
122
- error: error instanceof Error ? error.message : String(error)
123
- }
124
- };
125
- return { content };
154
+ // tsc writes diagnostics to stdout and exits 1 when it finds any. That is
155
+ // the tool working, not the tool failing.
156
+ const diagnostics = parseTscDiagnostics(result.stdout ?? '');
157
+ const totalErrors = diagnostics.filter(d => d.type === 'error').length;
158
+ const totalWarnings = diagnostics.filter(d => d.type === 'warning').length;
159
+
160
+ // A non-zero exit with nothing parseable means tsc fell over for some other
161
+ // reason (bad tsconfig, unreadable directory). That is a real failure.
162
+ if (result.exitCode !== 0 && diagnostics.length === 0) {
163
+ throw ToolError.executionFailed(
164
+ `tsc exited ${result.exitCode} without producing diagnostics: ` +
165
+ `${(result.stderr || result.stdout)?.trim() || 'no output'}`
166
+ );
126
167
  }
168
+
169
+ return {
170
+ passed: totalErrors === 0,
171
+ totalErrors,
172
+ totalWarnings,
173
+ diagnostics
174
+ };
127
175
  }
128
- };
176
+ });
129
177
 
130
- export const typescriptBuildTool = {
178
+ export const typescriptBuildTool = defineTool<
179
+ { workingDirectory?: string; clean?: boolean },
180
+ { passed: boolean; buildTimeMs: number; errors: string[]; output: string }
181
+ >({
131
182
  name: 'typescript_build',
132
- description: 'Build TypeScript project and emit JavaScript files',
183
+ category: TOOL_CATEGORIES.INFRASTRUCTURE,
184
+ description: 'Build the TypeScript project with `tsc --build` and emit JavaScript.',
133
185
  inputSchema: {
134
186
  type: 'object',
135
187
  properties: {
136
- workingDirectory: {
137
- type: 'string',
138
- description: 'Working directory path (defaults to current directory)'
139
- },
188
+ workingDirectory: workingDirectoryProperty,
140
189
  clean: {
141
190
  type: 'boolean',
142
191
  default: false,
143
- description: 'Clean output directory before build'
192
+ description: 'Run `tsc --build --clean` first'
144
193
  }
145
194
  }
146
195
  },
147
- handler: async (args: {
148
- workingDirectory?: string;
149
- clean?: boolean;
150
- }, context: McpToolHandlerContext): Promise<McpToolHandlerResult> => {
151
-
152
- try {
153
- const startTime = Date.now();
154
-
155
- // Clean output directory if requested
156
- if (args.clean) {
157
- const cleanResult = await executeShellCommand('npx', ['tsc', '--build', '--clean'], {
158
- workingDirectory: args.workingDirectory || process.cwd(),
159
- captureOutput: true
160
- });
161
- if (cleanResult.exitCode !== 0) {
162
- logger.warn('Clean command failed', { error: cleanResult.stderr });
163
- }
164
- }
165
-
166
- const result = await executeShellCommand('npx', ['tsc', '--build'], {
167
- workingDirectory: args.workingDirectory || process.cwd(),
168
- captureOutput: true
169
- });
170
-
171
- const buildTime = Date.now() - startTime;
172
-
173
- const errors = [];
174
- const warnings = [];
175
-
176
- if (result.stdout) {
177
- const lines = result.stdout.split('\n');
178
- for (const line of lines) {
179
- if (line.includes('error TS')) {
180
- errors.push(line.trim());
181
- } else if (line.includes('warning TS')) {
182
- warnings.push(line.trim());
183
- }
184
- }
185
- }
196
+ run: async (input, context) => {
197
+ const startedAt = Date.now();
186
198
 
187
- const content: McpToolResultContent = {
188
- type: 'application/json',
189
- data: {
190
- success: result.exitCode === 0,
191
- output: result.stdout || '',
192
- errors,
193
- warnings,
194
- buildTime,
195
- error: result.exitCode !== 0 ? result.stderr : null
196
- }
197
- };
198
- return { content };
199
- } catch (error) {
200
- logger.error('TypeScript build failed', { error });
201
- const content: McpToolResultContent = {
202
- type: 'application/json',
203
- data: {
204
- success: false,
205
- output: '',
206
- errors: [],
207
- warnings: [],
208
- buildTime: 0,
209
- error: error instanceof Error ? error.message : String(error)
210
- }
211
- };
212
- return { content };
199
+ if (input.clean) {
200
+ const cleanResult = await runNpx(
201
+ ['tsc', '--build', '--clean'],
202
+ input.workingDirectory,
203
+ context
204
+ );
205
+ // A failed clean leaves stale output behind, which makes the build's
206
+ // result meaningless. Say so rather than building on top of it.
207
+ if (cleanResult.exitCode !== 0) {
208
+ throw ToolError.executionFailed(
209
+ `tsc --build --clean failed (exit ${cleanResult.exitCode}): ` +
210
+ `${(cleanResult.stderr || cleanResult.stdout)?.trim() || 'no output'}`
211
+ );
212
+ }
213
213
  }
214
+
215
+ const result = await runNpx(['tsc', '--build'], input.workingDirectory, context);
216
+
217
+ const errors = (result.stdout ?? '')
218
+ .split('\n')
219
+ .filter(line => line.includes('error TS'))
220
+ .map(line => line.trim());
221
+
222
+ return {
223
+ passed: result.exitCode === 0,
224
+ buildTimeMs: Date.now() - startedAt,
225
+ errors,
226
+ output: result.stdout ?? ''
227
+ };
214
228
  }
215
- };
229
+ });
216
230
 
217
- export const typescriptFormatTool = {
231
+ export const typescriptFormatTool = defineTool<
232
+ { files?: string[]; check?: boolean; workingDirectory?: string },
233
+ { mode: 'check' | 'write'; formatted: boolean; files: string[]; output: string }
234
+ >({
218
235
  name: 'typescript_format',
219
- description: 'Format TypeScript files using prettier or similar formatter',
236
+ category: TOOL_CATEGORIES.INFRASTRUCTURE,
237
+ description:
238
+ 'Format TypeScript/JavaScript files with prettier. ' +
239
+ 'With check: true it reports whether files are formatted without changing them.',
220
240
  inputSchema: {
221
241
  type: 'object',
222
242
  properties: {
223
243
  files: {
224
244
  type: 'array',
225
245
  items: { type: 'string' },
226
- description: 'Files to format (supports glob patterns)'
246
+ description: 'Files to format (glob patterns allowed). Defaults to all TS/JS sources.'
227
247
  },
228
248
  check: {
229
249
  type: 'boolean',
230
250
  default: false,
231
- description: 'Check if files are formatted without modifying them'
251
+ description: 'Report formatting problems instead of rewriting files'
232
252
  },
233
- workingDirectory: {
234
- type: 'string',
235
- description: 'Working directory path (defaults to current directory)'
236
- }
253
+ workingDirectory: workingDirectoryProperty
237
254
  }
238
255
  },
239
- handler: async (args: {
240
- files?: string[];
241
- check?: boolean;
242
- workingDirectory?: string;
243
- }, context: McpToolHandlerContext): Promise<McpToolHandlerResult> => {
244
-
245
- try {
246
- const prettierArgs = args.check ? ['--check'] : ['--write'];
247
- if (args.files && args.files.length > 0) {
248
- prettierArgs.push(...args.files);
249
- } else {
250
- prettierArgs.push('**/*.{ts,tsx,js,jsx}');
251
- }
252
-
253
- const result = await executeShellCommand('npx', ['prettier', ...prettierArgs], {
254
- workingDirectory: args.workingDirectory || process.cwd(),
255
- captureOutput: true
256
- });
257
-
258
- const formattedFiles = [];
259
- const unchanged = [];
260
-
261
- if (result.stdout) {
262
- const lines = result.stdout.split('\n');
263
- for (const line of lines) {
264
- if (line.includes('unchanged')) {
265
- unchanged.push(line.trim());
266
- } else if (line.trim() && !line.startsWith('Checking')) {
267
- formattedFiles.push(line.trim());
268
- }
269
- }
270
- }
256
+ run: async (input, context) => {
257
+ const mode: 'check' | 'write' = input.check ? 'check' : 'write';
258
+ const prettierArgs = ['prettier', input.check ? '--check' : '--write'];
271
259
 
272
- const content: McpToolResultContent = {
273
- type: 'application/json',
274
- data: {
275
- success: result.exitCode === 0,
276
- formattedFiles,
277
- unchanged,
278
- error: result.exitCode !== 0 ? result.stderr : null
279
- }
280
- };
281
- return { content };
282
- } catch (error) {
283
- logger.error('TypeScript format failed', { error });
284
- const content: McpToolResultContent = {
285
- type: 'application/json',
286
- data: {
287
- success: false,
288
- formattedFiles: [],
289
- unchanged: [],
290
- error: error instanceof Error ? error.message : String(error)
291
- }
292
- };
293
- return { content };
260
+ if (input.files && input.files.length > 0) {
261
+ prettierArgs.push(...input.files);
262
+ } else {
263
+ prettierArgs.push('**/*.{ts,tsx,js,jsx}');
294
264
  }
265
+
266
+ const result = await runNpx(prettierArgs, input.workingDirectory, context);
267
+
268
+ // In check mode prettier exits 1 when files need formatting — a finding,
269
+ // not a failure. In write mode a non-zero exit means it could not write.
270
+ if (mode === 'write' && result.exitCode !== 0) {
271
+ throw ToolError.executionFailed(
272
+ `prettier --write failed (exit ${result.exitCode}): ` +
273
+ `${(result.stderr || result.stdout)?.trim() || 'no output'}`
274
+ );
275
+ }
276
+
277
+ const files = (result.stdout ?? '')
278
+ .split('\n')
279
+ .map(line => line.trim())
280
+ .filter(line => line.length > 0 && !line.startsWith('Checking'));
281
+
282
+ return {
283
+ mode,
284
+ formatted: result.exitCode === 0,
285
+ files,
286
+ output: result.stdout ?? ''
287
+ };
295
288
  }
296
- };
289
+ });
297
290
 
298
- export const typescriptLintTool = {
291
+ export const typescriptLintTool = defineTool<
292
+ { files?: string[]; fix?: boolean; workingDirectory?: string },
293
+ { passed: boolean; errorCount: number; warningCount: number; results: unknown[] }
294
+ >({
299
295
  name: 'typescript_lint',
300
- description: 'Lint TypeScript files using ESLint',
296
+ category: TOOL_CATEGORIES.INFRASTRUCTURE,
297
+ description:
298
+ 'Lint TypeScript/JavaScript with ESLint and return the findings. ' +
299
+ 'passed is false when there are lint errors.',
301
300
  inputSchema: {
302
301
  type: 'object',
303
302
  properties: {
304
303
  files: {
305
304
  type: 'array',
306
305
  items: { type: 'string' },
307
- description: 'Files to lint (supports glob patterns)'
306
+ description: 'Files to lint (glob patterns allowed). Defaults to all TS/JS sources.'
308
307
  },
309
308
  fix: {
310
309
  type: 'boolean',
311
310
  default: false,
312
- description: 'Automatically fix problems'
311
+ description: 'Automatically fix the problems ESLint can fix'
313
312
  },
314
- workingDirectory: {
315
- type: 'string',
316
- description: 'Working directory path (defaults to current directory)'
317
- }
313
+ workingDirectory: workingDirectoryProperty
318
314
  }
319
315
  },
320
- handler: async (args: {
321
- files?: string[];
322
- fix?: boolean;
323
- workingDirectory?: string;
324
- }, context: McpToolHandlerContext): Promise<McpToolHandlerResult> => {
325
-
326
- try {
327
- const eslintArgs = ['--format', 'json'];
328
- if (args.fix) {
329
- eslintArgs.push('--fix');
330
- }
331
-
332
- if (args.files && args.files.length > 0) {
333
- eslintArgs.push(...args.files);
334
- } else {
335
- eslintArgs.push('**/*.{ts,tsx,js,jsx}');
336
- }
337
-
338
- const result = await executeShellCommand('npx', ['eslint', ...eslintArgs], {
339
- workingDirectory: args.workingDirectory || process.cwd(),
340
- captureOutput: true
341
- });
342
-
343
- let results = [];
344
- try {
345
- if (result.stdout) {
346
- results = JSON.parse(result.stdout);
347
- }
348
- } catch (parseError) {
349
- logger.warn('Could not parse ESLint output as JSON', { output: result.stdout });
350
- results = [{
351
- filePath: 'unknown',
352
- messages: [{
353
- message: result.stdout,
354
- severity: 1,
355
- line: 0,
356
- column: 0
357
- }]
358
- }];
359
- }
316
+ run: async (input, context) => {
317
+ const eslintArgs = ['eslint', '--format', 'json'];
318
+ if (input.fix) {
319
+ eslintArgs.push('--fix');
320
+ }
321
+
322
+ if (input.files && input.files.length > 0) {
323
+ eslintArgs.push(...input.files);
324
+ } else {
325
+ eslintArgs.push('**/*.{ts,tsx,js,jsx}');
326
+ }
327
+
328
+ const result = await runNpx(eslintArgs, input.workingDirectory, context);
360
329
 
361
- const content: McpToolResultContent = {
362
- type: 'application/json',
363
- data: {
364
- success: result.exitCode === 0,
365
- results,
366
- error: result.exitCode !== 0 ? result.stderr : null
367
- }
368
- };
369
- return { content };
370
- } catch (error) {
371
- logger.error('TypeScript lint failed', { error });
372
- const content: McpToolResultContent = {
373
- type: 'application/json',
374
- data: {
375
- success: false,
376
- results: [],
377
- error: error instanceof Error ? error.message : String(error)
378
- }
379
- };
380
- return { content };
330
+ // ESLint exits 1 when it finds errors, but always prints its JSON report.
331
+ // No parseable report means ESLint itself fell over — that is a failure,
332
+ // and it must not be reported as "no lint problems".
333
+ const raw = (result.stdout ?? '').trim();
334
+ if (raw.length === 0) {
335
+ throw ToolError.executionFailed(
336
+ `eslint produced no report (exit ${result.exitCode}): ` +
337
+ `${result.stderr?.trim() || 'no error output'}`
338
+ );
381
339
  }
340
+
341
+ let results: Array<{ errorCount?: number; warningCount?: number }>;
342
+ try {
343
+ results = JSON.parse(raw);
344
+ } catch (parseError) {
345
+ throw ToolError.executionFailed(
346
+ `Could not parse the ESLint JSON report: ` +
347
+ `${parseError instanceof Error ? parseError.message : String(parseError)}`,
348
+ { details: { outputPreview: raw.slice(0, 200) } }
349
+ );
350
+ }
351
+
352
+ const errorCount = results.reduce((sum, file) => sum + (file.errorCount ?? 0), 0);
353
+ const warningCount = results.reduce((sum, file) => sum + (file.warningCount ?? 0), 0);
354
+
355
+ return {
356
+ passed: errorCount === 0,
357
+ errorCount,
358
+ warningCount,
359
+ results
360
+ };
382
361
  }
383
- };
362
+ });
384
363
 
385
- export const typescriptTestTool = {
364
+ export const typescriptTestTool = defineTool<
365
+ { testFiles?: string[]; coverage?: boolean; workingDirectory?: string },
366
+ { passed: boolean; testsRun: number; testsPassed: number; testsFailed: number; output: string }
367
+ >({
386
368
  name: 'typescript_test',
387
- description: 'Run TypeScript tests using Jest or other test runners',
369
+ category: TOOL_CATEGORIES.INFRASTRUCTURE,
370
+ description:
371
+ 'Run Jest tests and return the counts. passed is false when any test fails.',
388
372
  inputSchema: {
389
373
  type: 'object',
390
374
  properties: {
391
375
  testFiles: {
392
376
  type: 'array',
393
377
  items: { type: 'string' },
394
- description: 'Specific test files to run'
378
+ description: 'Specific test files to run. Runs the whole suite when omitted.'
395
379
  },
396
380
  coverage: {
397
381
  type: 'boolean',
398
382
  default: false,
399
- description: 'Generate test coverage report'
383
+ description: 'Generate a coverage report'
400
384
  },
401
- workingDirectory: {
402
- type: 'string',
403
- description: 'Working directory path (defaults to current directory)'
404
- }
385
+ workingDirectory: workingDirectoryProperty
405
386
  }
406
387
  },
407
- handler: async (args: {
408
- testFiles?: string[];
409
- coverage?: boolean;
410
- workingDirectory?: string;
411
- }, context: McpToolHandlerContext): Promise<McpToolHandlerResult> => {
412
-
413
- try {
414
- const jestArgs = ['--no-watchman', '--passWithNoTests'];
415
-
416
- if (args.coverage) {
417
- jestArgs.push('--coverage');
418
- }
419
-
420
- if (args.testFiles && args.testFiles.length > 0) {
421
- jestArgs.push(...args.testFiles);
422
- }
423
-
424
- const result = await executeShellCommand('npx', ['jest', ...jestArgs], {
425
- workingDirectory: args.workingDirectory || process.cwd(),
426
- captureOutput: true
427
- });
428
-
429
- // Parse Jest output for test results
430
- let testsRun = 0;
431
- let testsPassed = 0;
432
- let testsFailed = 0;
433
-
434
- if (result.stdout) {
435
- const runMatch = result.stdout.match(/Tests:\s+(\d+)\s+total/);
436
- const passMatch = result.stdout.match(/Tests:\s+\d+\s+failed,\s+(\d+)\s+passed/);
437
- const failMatch = result.stdout.match(/Tests:\s+(\d+)\s+failed/);
438
-
439
- if (runMatch) testsRun = parseInt(runMatch[1]);
440
- if (passMatch) testsPassed = parseInt(passMatch[1]);
441
- if (failMatch) testsFailed = parseInt(failMatch[1]);
442
-
443
- // Alternative parsing for different Jest output formats
444
- if (testsRun === 0) {
445
- const totalMatch = result.stdout.match(/(\d+)\s+tests?\s+passed/);
446
- if (totalMatch) {
447
- testsRun = parseInt(totalMatch[1]);
448
- testsPassed = testsRun;
449
- }
450
- }
451
- }
388
+ run: async (input, context) => {
389
+ const jestArgs = ['jest', '--no-watchman', '--passWithNoTests'];
452
390
 
453
- const content: McpToolResultContent = {
454
- type: 'application/json',
455
- data: {
456
- success: result.exitCode === 0,
457
- output: result.stdout || '',
458
- testsRun,
459
- testsPassed,
460
- testsFailed,
461
- error: result.exitCode !== 0 ? result.stderr : null
462
- }
463
- };
464
- return { content };
465
- } catch (error) {
466
- logger.error('TypeScript test failed', { error });
467
- const content: McpToolResultContent = {
468
- type: 'application/json',
469
- data: {
470
- success: false,
471
- output: '',
472
- testsRun: 0,
473
- testsPassed: 0,
474
- testsFailed: 0,
475
- error: error instanceof Error ? error.message : String(error)
476
- }
477
- };
478
- return { content };
391
+ if (input.coverage) {
392
+ jestArgs.push('--coverage');
393
+ }
394
+ if (input.testFiles && input.testFiles.length > 0) {
395
+ jestArgs.push(...input.testFiles);
479
396
  }
397
+
398
+ const result = await runNpx(jestArgs, input.workingDirectory, context);
399
+
400
+ // Jest writes its summary to stderr, not stdout.
401
+ const output = [result.stdout, result.stderr].filter(Boolean).join('\n');
402
+
403
+ // Summary line: "Tests: 1 failed, 2 skipped, 17 passed, 20 total"
404
+ const summary = output.match(/^Tests:\s+(.+)$/m)?.[1] ?? '';
405
+ const readCount = (label: string): number => {
406
+ const match = summary.match(new RegExp(`(\\d+)\\s+${label}`));
407
+ return match ? parseInt(match[1], 10) : 0;
408
+ };
409
+
410
+ const testsRun = readCount('total');
411
+ const testsPassed = readCount('passed');
412
+ const testsFailed = readCount('failed');
413
+
414
+ return {
415
+ passed: result.exitCode === 0,
416
+ testsRun,
417
+ testsPassed,
418
+ testsFailed,
419
+ output
420
+ };
480
421
  }
481
- };
422
+ });
482
423
 
483
- // Export all TypeScript tools
484
424
  export const typescriptTools = [
485
425
  typescriptCheckTool,
486
426
  typescriptBuildTool,