cucumberstudio-mcp 1.1.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.
Files changed (130) hide show
  1. package/.env.example +36 -0
  2. package/.github/workflows/pr-checks.yml +41 -0
  3. package/.github/workflows/release.yml +194 -0
  4. package/.prettierignore +26 -0
  5. package/.prettierrc +14 -0
  6. package/CLAUDE.md +140 -0
  7. package/Dockerfile +50 -0
  8. package/Dockerfile.dev +31 -0
  9. package/LICENSE +21 -0
  10. package/README.md +395 -0
  11. package/build/api/client.d.ts +49 -0
  12. package/build/api/client.d.ts.map +1 -0
  13. package/build/api/client.js +204 -0
  14. package/build/api/client.js.map +1 -0
  15. package/build/api/types.d.ts +113 -0
  16. package/build/api/types.d.ts.map +1 -0
  17. package/build/api/types.js +2 -0
  18. package/build/api/types.js.map +1 -0
  19. package/build/config/settings.d.ts +123 -0
  20. package/build/config/settings.d.ts.map +1 -0
  21. package/build/config/settings.js +97 -0
  22. package/build/config/settings.js.map +1 -0
  23. package/build/constants.d.ts +16 -0
  24. package/build/constants.d.ts.map +1 -0
  25. package/build/constants.js +24 -0
  26. package/build/constants.js.map +1 -0
  27. package/build/generated/version.d.ts +3 -0
  28. package/build/generated/version.d.ts.map +1 -0
  29. package/build/generated/version.js +5 -0
  30. package/build/generated/version.js.map +1 -0
  31. package/build/index.d.ts +3 -0
  32. package/build/index.d.ts.map +1 -0
  33. package/build/index.js +81 -0
  34. package/build/index.js.map +1 -0
  35. package/build/mcp-server.d.ts +6 -0
  36. package/build/mcp-server.d.ts.map +1 -0
  37. package/build/mcp-server.js +263 -0
  38. package/build/mcp-server.js.map +1 -0
  39. package/build/tools/action-words.d.ts +18 -0
  40. package/build/tools/action-words.d.ts.map +1 -0
  41. package/build/tools/action-words.js +191 -0
  42. package/build/tools/action-words.js.map +1 -0
  43. package/build/tools/projects.d.ts +19 -0
  44. package/build/tools/projects.d.ts.map +1 -0
  45. package/build/tools/projects.js +123 -0
  46. package/build/tools/projects.js.map +1 -0
  47. package/build/tools/scenarios.d.ts +18 -0
  48. package/build/tools/scenarios.d.ts.map +1 -0
  49. package/build/tools/scenarios.js +194 -0
  50. package/build/tools/scenarios.js.map +1 -0
  51. package/build/tools/test-runs.d.ts +21 -0
  52. package/build/tools/test-runs.d.ts.map +1 -0
  53. package/build/tools/test-runs.js +324 -0
  54. package/build/tools/test-runs.js.map +1 -0
  55. package/build/transports/http.d.ts +38 -0
  56. package/build/transports/http.d.ts.map +1 -0
  57. package/build/transports/http.js +381 -0
  58. package/build/transports/http.js.map +1 -0
  59. package/build/transports/index.d.ts +22 -0
  60. package/build/transports/index.d.ts.map +1 -0
  61. package/build/transports/index.js +10 -0
  62. package/build/transports/index.js.map +1 -0
  63. package/build/transports/stdio.d.ts +13 -0
  64. package/build/transports/stdio.d.ts.map +1 -0
  65. package/build/transports/stdio.js +24 -0
  66. package/build/transports/stdio.js.map +1 -0
  67. package/build/utils/errors.d.ts +10 -0
  68. package/build/utils/errors.d.ts.map +1 -0
  69. package/build/utils/errors.js +35 -0
  70. package/build/utils/errors.js.map +1 -0
  71. package/build/utils/logger-constants.d.ts +15 -0
  72. package/build/utils/logger-constants.d.ts.map +1 -0
  73. package/build/utils/logger-constants.js +16 -0
  74. package/build/utils/logger-constants.js.map +1 -0
  75. package/build/utils/logger.d.ts +55 -0
  76. package/build/utils/logger.d.ts.map +1 -0
  77. package/build/utils/logger.js +113 -0
  78. package/build/utils/logger.js.map +1 -0
  79. package/build/utils/validation.d.ts +89 -0
  80. package/build/utils/validation.d.ts.map +1 -0
  81. package/build/utils/validation.js +78 -0
  82. package/build/utils/validation.js.map +1 -0
  83. package/docker-compose.yml +20 -0
  84. package/eslint.config.js +97 -0
  85. package/package.json +92 -0
  86. package/scripts/generate-version.js +31 -0
  87. package/src/api/client.ts +286 -0
  88. package/src/api/types.ts +137 -0
  89. package/src/config/settings.ts +113 -0
  90. package/src/constants.ts +29 -0
  91. package/src/index.ts +99 -0
  92. package/src/mcp-server.ts +342 -0
  93. package/src/tools/action-words.ts +240 -0
  94. package/src/tools/projects.ts +144 -0
  95. package/src/tools/scenarios.ts +231 -0
  96. package/src/tools/test-runs.ts +400 -0
  97. package/src/transports/http.ts +467 -0
  98. package/src/transports/index.ts +26 -0
  99. package/src/transports/stdio.ts +28 -0
  100. package/src/utils/errors.ts +45 -0
  101. package/src/utils/logger-constants.ts +18 -0
  102. package/src/utils/logger.ts +150 -0
  103. package/src/utils/validation.ts +94 -0
  104. package/test/api/client-with-msw.test.ts +122 -0
  105. package/test/api/client.test.ts +326 -0
  106. package/test/api/types.test.ts +88 -0
  107. package/test/config/settings.test.ts +204 -0
  108. package/test/mocks/data/action-words.ts +40 -0
  109. package/test/mocks/data/index.ts +13 -0
  110. package/test/mocks/data/projects.ts +38 -0
  111. package/test/mocks/data/scenarios.ts +53 -0
  112. package/test/mocks/data/test-runs.ts +101 -0
  113. package/test/mocks/handlers/action-words.ts +52 -0
  114. package/test/mocks/handlers/index.ts +10 -0
  115. package/test/mocks/handlers/projects.ts +45 -0
  116. package/test/mocks/handlers/scenarios.ts +72 -0
  117. package/test/mocks/handlers/test-runs.ts +106 -0
  118. package/test/mocks/server.ts +26 -0
  119. package/test/setup/vitest.setup.ts +18 -0
  120. package/test/tools/coverage-boost.test.ts +252 -0
  121. package/test/tools/projects.test.ts +290 -0
  122. package/test/tools/tools-basic.test.ts +146 -0
  123. package/test/transports/http-basic.test.ts +87 -0
  124. package/test/transports/http-simple.test.ts +33 -0
  125. package/test/transports/stdio.test.ts +73 -0
  126. package/test/utils/errors.test.ts +117 -0
  127. package/test/utils/validation.test.ts +261 -0
  128. package/tsconfig.build.json +8 -0
  129. package/tsconfig.json +27 -0
  130. package/vitest.config.ts +43 -0
@@ -0,0 +1,72 @@
1
+ import { http, HttpResponse } from 'msw'
2
+
3
+ import { mockScenarios, mockScenario, mockFolders } from '../data/scenarios.js'
4
+
5
+ const BASE_URL = 'https://api.example.com'
6
+
7
+ export const scenarioHandlers = [
8
+ // List scenarios in a project
9
+ http.get(`${BASE_URL}/projects/:projectId/scenarios`, ({ params, request }) => {
10
+ const accessToken = request.headers.get('access-token')
11
+
12
+ if (!accessToken || accessToken !== 'token') {
13
+ return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 })
14
+ }
15
+
16
+ const projectId = Number(params.projectId)
17
+ const projectScenarios = mockScenarios.filter((s) => s.project_id === projectId)
18
+
19
+ // Handle query parameters for filtering
20
+ const url = new URL(request.url)
21
+ const tags = url.searchParams.get('tags')
22
+ const folderId = url.searchParams.get('folder_id')
23
+
24
+ let filteredScenarios = projectScenarios
25
+
26
+ if (tags) {
27
+ const tagList = tags.split(',')
28
+ filteredScenarios = filteredScenarios.filter((scenario) =>
29
+ tagList.some((tag) => scenario.tags.includes(tag.trim())),
30
+ )
31
+ }
32
+
33
+ if (folderId) {
34
+ const folderIdNum = Number(folderId)
35
+ filteredScenarios = filteredScenarios.filter((scenario) => scenario.folder_id === folderIdNum)
36
+ }
37
+
38
+ return HttpResponse.json(filteredScenarios)
39
+ }),
40
+
41
+ // Get specific scenario
42
+ http.get(`${BASE_URL}/projects/:projectId/scenarios/:id`, ({ params, request }) => {
43
+ const accessToken = request.headers.get('access-token')
44
+
45
+ if (!accessToken || accessToken !== 'token') {
46
+ return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 })
47
+ }
48
+
49
+ const scenarioId = Number(params.id)
50
+ const scenario = mockScenarios.find((s) => s.id === scenarioId)
51
+
52
+ if (!scenario) {
53
+ return HttpResponse.json({ error: 'Scenario not found' }, { status: 404 })
54
+ }
55
+
56
+ return HttpResponse.json(scenario)
57
+ }),
58
+
59
+ // List folders in a project
60
+ http.get(`${BASE_URL}/projects/:projectId/folders`, ({ params, request }) => {
61
+ const accessToken = request.headers.get('access-token')
62
+
63
+ if (!accessToken || accessToken !== 'token') {
64
+ return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 })
65
+ }
66
+
67
+ const projectId = Number(params.projectId)
68
+ const projectFolders = mockFolders.filter((f) => f.project_id === projectId)
69
+
70
+ return HttpResponse.json(projectFolders)
71
+ }),
72
+ ]
@@ -0,0 +1,106 @@
1
+ import { http, HttpResponse } from 'msw'
2
+
3
+ import {
4
+ mockTestRuns,
5
+ mockTestRun,
6
+ mockTestExecutions,
7
+ mockBuilds,
8
+ mockBuild,
9
+ mockExecutionEnvironments,
10
+ } from '../data/test-runs.js'
11
+
12
+ const BASE_URL = 'https://api.example.com'
13
+
14
+ export const testRunHandlers = [
15
+ // List test runs in a project
16
+ http.get(`${BASE_URL}/projects/:projectId/test_runs`, ({ params, request }) => {
17
+ const accessToken = request.headers.get('access-token')
18
+
19
+ if (!accessToken || accessToken !== 'token') {
20
+ return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 })
21
+ }
22
+
23
+ const projectId = Number(params.projectId)
24
+ const projectTestRuns = mockTestRuns.filter((t) => t.project_id === projectId)
25
+
26
+ return HttpResponse.json(projectTestRuns)
27
+ }),
28
+
29
+ // Get specific test run
30
+ http.get(`${BASE_URL}/projects/:projectId/test_runs/:id`, ({ params, request }) => {
31
+ const accessToken = request.headers.get('access-token')
32
+
33
+ if (!accessToken || accessToken !== 'token') {
34
+ return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 })
35
+ }
36
+
37
+ const testRunId = Number(params.id)
38
+ const testRun = mockTestRuns.find((t) => t.id === testRunId)
39
+
40
+ if (!testRun) {
41
+ return HttpResponse.json({ error: 'Test run not found' }, { status: 404 })
42
+ }
43
+
44
+ return HttpResponse.json(testRun)
45
+ }),
46
+
47
+ // Get test executions for a test run
48
+ http.get(`${BASE_URL}/projects/:projectId/test_runs/:testRunId/test_snapshots`, ({ params, request }) => {
49
+ const accessToken = request.headers.get('access-token')
50
+
51
+ if (!accessToken || accessToken !== 'token') {
52
+ return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 })
53
+ }
54
+
55
+ const testRunId = Number(params.testRunId)
56
+ const executions = mockTestExecutions.filter((e) => e.test_run_id === testRunId)
57
+
58
+ return HttpResponse.json(executions)
59
+ }),
60
+
61
+ // List builds in a project
62
+ http.get(`${BASE_URL}/projects/:projectId/builds`, ({ params, request }) => {
63
+ const accessToken = request.headers.get('access-token')
64
+
65
+ if (!accessToken || accessToken !== 'token') {
66
+ return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 })
67
+ }
68
+
69
+ const projectId = Number(params.projectId)
70
+ const projectBuilds = mockBuilds.filter((b) => b.project_id === projectId)
71
+
72
+ return HttpResponse.json(projectBuilds)
73
+ }),
74
+
75
+ // Get specific build
76
+ http.get(`${BASE_URL}/projects/:projectId/builds/:id`, ({ params, request }) => {
77
+ const accessToken = request.headers.get('access-token')
78
+
79
+ if (!accessToken || accessToken !== 'token') {
80
+ return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 })
81
+ }
82
+
83
+ const buildId = Number(params.id)
84
+ const build = mockBuilds.find((b) => b.id === buildId)
85
+
86
+ if (!build) {
87
+ return HttpResponse.json({ error: 'Build not found' }, { status: 404 })
88
+ }
89
+
90
+ return HttpResponse.json(build)
91
+ }),
92
+
93
+ // List execution environments
94
+ http.get(`${BASE_URL}/projects/:projectId/execution_environments`, ({ params, request }) => {
95
+ const accessToken = request.headers.get('access-token')
96
+
97
+ if (!accessToken || accessToken !== 'token') {
98
+ return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 })
99
+ }
100
+
101
+ const projectId = Number(params.projectId)
102
+ const environments = mockExecutionEnvironments.filter((e) => e.project_id === projectId)
103
+
104
+ return HttpResponse.json(environments)
105
+ }),
106
+ ]
@@ -0,0 +1,26 @@
1
+ import { setupServer } from 'msw/node'
2
+
3
+ import { handlers } from './handlers/index.js'
4
+
5
+ // Create and export the mock server
6
+ export const mockServer = setupServer(...handlers)
7
+
8
+ // Helper functions for test setup
9
+ export const startMockServer = () => {
10
+ mockServer.listen({
11
+ onUnhandledRequest: 'warn', // Warn about unhandled requests
12
+ })
13
+ }
14
+
15
+ export const stopMockServer = () => {
16
+ mockServer.close()
17
+ }
18
+
19
+ export const resetMockServer = () => {
20
+ mockServer.resetHandlers()
21
+ }
22
+
23
+ // Export handlers for selective testing
24
+ export { handlers } from './handlers/index.js'
25
+ export * from './handlers/index.js'
26
+ export * from './data/index.js'
@@ -0,0 +1,18 @@
1
+ import { beforeAll, afterEach, afterAll } from 'vitest'
2
+
3
+ import { startMockServer, stopMockServer, resetMockServer } from '../mocks/server.js'
4
+
5
+ // Start the mock server before all tests
6
+ beforeAll(() => {
7
+ startMockServer()
8
+ })
9
+
10
+ // Reset handlers after each test to ensure test isolation
11
+ afterEach(() => {
12
+ resetMockServer()
13
+ })
14
+
15
+ // Stop the mock server after all tests
16
+ afterAll(() => {
17
+ stopMockServer()
18
+ })
@@ -0,0 +1,252 @@
1
+ import { CallToolRequest } from '@modelcontextprotocol/sdk/types.js'
2
+ import { describe, it, expect, beforeEach, vi } from 'vitest'
3
+
4
+ import { CucumberStudioApiClient } from '@/api/client.js'
5
+ import { ActionWordTools } from '@/tools/action-words.js'
6
+ import { ScenarioTools } from '@/tools/scenarios.js'
7
+ import { TestRunTools } from '@/tools/test-runs.js'
8
+
9
+ // Mock the API client
10
+ vi.mock('@/api/client.js')
11
+
12
+ describe('Tools Coverage Boost', () => {
13
+ let mockApiClient: any
14
+
15
+ beforeEach(() => {
16
+ mockApiClient = {
17
+ getScenarios: vi.fn().mockResolvedValue({ data: [] }),
18
+ getScenario: vi.fn().mockResolvedValue({ data: { id: 1, attributes: { name: 'Test' } } }),
19
+ findScenariosByTag: vi.fn().mockResolvedValue({ data: [] }),
20
+ getActionWords: vi.fn().mockResolvedValue({ data: [] }),
21
+ getActionWord: vi.fn().mockResolvedValue({ data: { id: 1, attributes: { name: 'Test' } } }),
22
+ findActionWordsByTag: vi.fn().mockResolvedValue({ data: [] }),
23
+ getTestRuns: vi.fn().mockResolvedValue({ data: [] }),
24
+ getTestRun: vi.fn().mockResolvedValue({ data: { id: 1, attributes: { name: 'Test' } } }),
25
+ getTestExecutions: vi.fn().mockResolvedValue({ data: [] }),
26
+ getBuilds: vi.fn().mockResolvedValue({ data: [] }),
27
+ getBuild: vi.fn().mockResolvedValue({ data: { id: 1, attributes: { name: 'Test' } } }),
28
+ getExecutionEnvironments: vi.fn().mockResolvedValue({ data: [] }),
29
+ }
30
+ })
31
+
32
+ describe('ScenarioTools handleToolCall', () => {
33
+ let scenarioTools: ScenarioTools
34
+
35
+ beforeEach(() => {
36
+ scenarioTools = new ScenarioTools(mockApiClient)
37
+ })
38
+
39
+ it('should handle cucumberstudio_list_scenarios', async () => {
40
+ const request: CallToolRequest = {
41
+ method: 'tools/call',
42
+ params: {
43
+ name: 'cucumberstudio_list_scenarios',
44
+ arguments: { projectId: '1' },
45
+ },
46
+ }
47
+
48
+ const result = await scenarioTools.handleToolCall(request)
49
+ expect(result.content).toBeDefined()
50
+ expect(result.content[0].type).toBe('text')
51
+ })
52
+
53
+ it('should handle cucumberstudio_get_scenario', async () => {
54
+ const request: CallToolRequest = {
55
+ method: 'tools/call',
56
+ params: {
57
+ name: 'cucumberstudio_get_scenario',
58
+ arguments: { projectId: '1', scenarioId: '1' },
59
+ },
60
+ }
61
+
62
+ const result = await scenarioTools.handleToolCall(request)
63
+ expect(result.content).toBeDefined()
64
+ })
65
+
66
+ it('should handle cucumberstudio_find_scenarios_by_tags', async () => {
67
+ const request: CallToolRequest = {
68
+ method: 'tools/call',
69
+ params: {
70
+ name: 'cucumberstudio_find_scenarios_by_tags',
71
+ arguments: { projectId: '1', tags: 'test' },
72
+ },
73
+ }
74
+
75
+ const result = await scenarioTools.handleToolCall(request)
76
+ expect(result.content).toBeDefined()
77
+ })
78
+
79
+ // Note: cucumberstudio_list_folders is not implemented in ScenarioTools
80
+
81
+ it('should throw for unknown scenario tool', async () => {
82
+ const request: CallToolRequest = {
83
+ method: 'tools/call',
84
+ params: {
85
+ name: 'unknown_scenario_tool',
86
+ arguments: {},
87
+ },
88
+ }
89
+
90
+ await expect(scenarioTools.handleToolCall(request)).rejects.toThrow()
91
+ })
92
+ })
93
+
94
+ describe('ActionWordTools handleToolCall', () => {
95
+ let actionWordTools: ActionWordTools
96
+
97
+ beforeEach(() => {
98
+ actionWordTools = new ActionWordTools(mockApiClient)
99
+ })
100
+
101
+ it('should handle cucumberstudio_list_action_words', async () => {
102
+ const request: CallToolRequest = {
103
+ method: 'tools/call',
104
+ params: {
105
+ name: 'cucumberstudio_list_action_words',
106
+ arguments: { projectId: '1' },
107
+ },
108
+ }
109
+
110
+ const result = await actionWordTools.handleToolCall(request)
111
+ expect(result.content).toBeDefined()
112
+ expect(result.content[0].type).toBe('text')
113
+ })
114
+
115
+ it('should handle cucumberstudio_get_action_word', async () => {
116
+ const request: CallToolRequest = {
117
+ method: 'tools/call',
118
+ params: {
119
+ name: 'cucumberstudio_get_action_word',
120
+ arguments: { projectId: '1', actionWordId: '1' },
121
+ },
122
+ }
123
+
124
+ const result = await actionWordTools.handleToolCall(request)
125
+ expect(result.content).toBeDefined()
126
+ })
127
+
128
+ it('should handle cucumberstudio_find_action_words_by_tags', async () => {
129
+ const request: CallToolRequest = {
130
+ method: 'tools/call',
131
+ params: {
132
+ name: 'cucumberstudio_find_action_words_by_tags',
133
+ arguments: { projectId: '1', tags: 'test' },
134
+ },
135
+ }
136
+
137
+ const result = await actionWordTools.handleToolCall(request)
138
+ expect(result.content).toBeDefined()
139
+ })
140
+
141
+ it('should throw for unknown action word tool', async () => {
142
+ const request: CallToolRequest = {
143
+ method: 'tools/call',
144
+ params: {
145
+ name: 'unknown_action_word_tool',
146
+ arguments: {},
147
+ },
148
+ }
149
+
150
+ await expect(actionWordTools.handleToolCall(request)).rejects.toThrow()
151
+ })
152
+ })
153
+
154
+ describe('TestRunTools handleToolCall', () => {
155
+ let testRunTools: TestRunTools
156
+
157
+ beforeEach(() => {
158
+ testRunTools = new TestRunTools(mockApiClient)
159
+ })
160
+
161
+ it('should handle cucumberstudio_list_test_runs', async () => {
162
+ const request: CallToolRequest = {
163
+ method: 'tools/call',
164
+ params: {
165
+ name: 'cucumberstudio_list_test_runs',
166
+ arguments: { projectId: '1' },
167
+ },
168
+ }
169
+
170
+ const result = await testRunTools.handleToolCall(request)
171
+ expect(result.content).toBeDefined()
172
+ expect(result.content[0].type).toBe('text')
173
+ })
174
+
175
+ it('should handle cucumberstudio_get_test_run', async () => {
176
+ const request: CallToolRequest = {
177
+ method: 'tools/call',
178
+ params: {
179
+ name: 'cucumberstudio_get_test_run',
180
+ arguments: { projectId: '1', testRunId: '1' },
181
+ },
182
+ }
183
+
184
+ const result = await testRunTools.handleToolCall(request)
185
+ expect(result.content).toBeDefined()
186
+ })
187
+
188
+ it('should handle cucumberstudio_get_test_executions', async () => {
189
+ const request: CallToolRequest = {
190
+ method: 'tools/call',
191
+ params: {
192
+ name: 'cucumberstudio_get_test_executions',
193
+ arguments: { projectId: '1', testRunId: '1' },
194
+ },
195
+ }
196
+
197
+ const result = await testRunTools.handleToolCall(request)
198
+ expect(result.content).toBeDefined()
199
+ })
200
+
201
+ it('should handle cucumberstudio_list_builds', async () => {
202
+ const request: CallToolRequest = {
203
+ method: 'tools/call',
204
+ params: {
205
+ name: 'cucumberstudio_list_builds',
206
+ arguments: { projectId: '1' },
207
+ },
208
+ }
209
+
210
+ const result = await testRunTools.handleToolCall(request)
211
+ expect(result.content).toBeDefined()
212
+ })
213
+
214
+ it('should handle cucumberstudio_get_build', async () => {
215
+ const request: CallToolRequest = {
216
+ method: 'tools/call',
217
+ params: {
218
+ name: 'cucumberstudio_get_build',
219
+ arguments: { projectId: '1', buildId: '1' },
220
+ },
221
+ }
222
+
223
+ const result = await testRunTools.handleToolCall(request)
224
+ expect(result.content).toBeDefined()
225
+ })
226
+
227
+ it('should handle cucumberstudio_list_execution_environments', async () => {
228
+ const request: CallToolRequest = {
229
+ method: 'tools/call',
230
+ params: {
231
+ name: 'cucumberstudio_list_execution_environments',
232
+ arguments: { projectId: '1' },
233
+ },
234
+ }
235
+
236
+ const result = await testRunTools.handleToolCall(request)
237
+ expect(result.content).toBeDefined()
238
+ })
239
+
240
+ it('should throw for unknown test run tool', async () => {
241
+ const request: CallToolRequest = {
242
+ method: 'tools/call',
243
+ params: {
244
+ name: 'unknown_test_run_tool',
245
+ arguments: {},
246
+ },
247
+ }
248
+
249
+ await expect(testRunTools.handleToolCall(request)).rejects.toThrow()
250
+ })
251
+ })
252
+ })