xray-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,851 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ import { XrayClient } from './xray-client.js';
6
+ // Validate required environment variables
7
+ const XRAY_CLIENT_ID = process.env.XRAY_CLIENT_ID;
8
+ const XRAY_CLIENT_SECRET = process.env.XRAY_CLIENT_SECRET;
9
+ if (!XRAY_CLIENT_ID || !XRAY_CLIENT_SECRET) {
10
+ console.error('Error: XRAY_CLIENT_ID and XRAY_CLIENT_SECRET must be set in environment variables');
11
+ process.exit(1);
12
+ }
13
+ // Initialize Xray client
14
+ const xrayClient = new XrayClient({
15
+ clientId: XRAY_CLIENT_ID,
16
+ clientSecret: XRAY_CLIENT_SECRET,
17
+ });
18
+ // Define available tools
19
+ const tools = [
20
+ {
21
+ name: 'create_test_case',
22
+ description: 'Create a new test case in Xray Cloud',
23
+ inputSchema: {
24
+ type: 'object',
25
+ properties: {
26
+ projectKey: {
27
+ type: 'string',
28
+ description: 'The Jira project key (e.g., "PROJ")',
29
+ },
30
+ summary: {
31
+ type: 'string',
32
+ description: 'The test case summary/title',
33
+ },
34
+ description: {
35
+ type: 'string',
36
+ description: 'The test case description',
37
+ },
38
+ testType: {
39
+ type: 'string',
40
+ enum: ['Manual', 'Cucumber', 'Generic'],
41
+ description: 'The type of test case',
42
+ default: 'Manual',
43
+ },
44
+ labels: {
45
+ type: 'array',
46
+ items: { type: 'string' },
47
+ description: 'Labels to attach to the test case',
48
+ },
49
+ priority: {
50
+ type: 'string',
51
+ description: 'Priority of the test case (e.g., "High", "Medium", "Low")',
52
+ },
53
+ },
54
+ required: ['projectKey', 'summary'],
55
+ },
56
+ },
57
+ {
58
+ name: 'get_test_case',
59
+ description: 'Get details of a specific test case by key',
60
+ inputSchema: {
61
+ type: 'object',
62
+ properties: {
63
+ testKey: {
64
+ type: 'string',
65
+ description: 'The test case key (e.g., "PROJ-123")',
66
+ },
67
+ },
68
+ required: ['testKey'],
69
+ },
70
+ },
71
+ {
72
+ name: 'update_test_case',
73
+ description: 'Update an existing test case',
74
+ inputSchema: {
75
+ type: 'object',
76
+ properties: {
77
+ testKey: {
78
+ type: 'string',
79
+ description: 'The test case key (e.g., "PROJ-123")',
80
+ },
81
+ summary: {
82
+ type: 'string',
83
+ description: 'New summary/title for the test case',
84
+ },
85
+ description: {
86
+ type: 'string',
87
+ description: 'New description for the test case',
88
+ },
89
+ labels: {
90
+ type: 'array',
91
+ items: { type: 'string' },
92
+ description: 'New labels for the test case',
93
+ },
94
+ priority: {
95
+ type: 'string',
96
+ description: 'New priority for the test case',
97
+ },
98
+ },
99
+ required: ['testKey'],
100
+ },
101
+ },
102
+ {
103
+ name: 'delete_test_case',
104
+ description: 'Delete a test case',
105
+ inputSchema: {
106
+ type: 'object',
107
+ properties: {
108
+ testKey: {
109
+ type: 'string',
110
+ description: 'The test case key to delete (e.g., "PROJ-123")',
111
+ },
112
+ },
113
+ required: ['testKey'],
114
+ },
115
+ },
116
+ {
117
+ name: 'search_test_cases',
118
+ description: 'Search for test cases using JQL (Jira Query Language)',
119
+ inputSchema: {
120
+ type: 'object',
121
+ properties: {
122
+ jql: {
123
+ type: 'string',
124
+ description: 'JQL query to search test cases (e.g., "project = PROJ AND labels = automation")',
125
+ },
126
+ maxResults: {
127
+ type: 'number',
128
+ description: 'Maximum number of results to return',
129
+ default: 50,
130
+ },
131
+ },
132
+ required: ['jql'],
133
+ },
134
+ },
135
+ {
136
+ name: 'get_project_test_cases',
137
+ description: 'Get all test cases for a specific project',
138
+ inputSchema: {
139
+ type: 'object',
140
+ properties: {
141
+ projectKey: {
142
+ type: 'string',
143
+ description: 'The Jira project key (e.g., "PROJ")',
144
+ },
145
+ maxResults: {
146
+ type: 'number',
147
+ description: 'Maximum number of results to return',
148
+ default: 50,
149
+ },
150
+ },
151
+ required: ['projectKey'],
152
+ },
153
+ },
154
+ // Test Execution tools
155
+ {
156
+ name: 'create_test_execution',
157
+ description: 'Create a new test execution in Xray Cloud to run tests',
158
+ inputSchema: {
159
+ type: 'object',
160
+ properties: {
161
+ projectKey: {
162
+ type: 'string',
163
+ description: 'The Jira project key (e.g., "PROJ")',
164
+ },
165
+ summary: {
166
+ type: 'string',
167
+ description: 'The test execution summary/title',
168
+ },
169
+ description: {
170
+ type: 'string',
171
+ description: 'The test execution description',
172
+ },
173
+ testIssueIds: {
174
+ type: 'array',
175
+ items: { type: 'string' },
176
+ description: 'Array of test issue IDs to include in this execution (e.g., ["10001", "10002"])',
177
+ },
178
+ testEnvironments: {
179
+ type: 'array',
180
+ items: { type: 'string' },
181
+ description: 'Array of test environments (e.g., ["Chrome", "iOS"])',
182
+ },
183
+ },
184
+ required: ['projectKey', 'summary'],
185
+ },
186
+ },
187
+ {
188
+ name: 'get_test_execution',
189
+ description: 'Get details of a specific test execution by key, including all test runs',
190
+ inputSchema: {
191
+ type: 'object',
192
+ properties: {
193
+ testExecutionKey: {
194
+ type: 'string',
195
+ description: 'The test execution key (e.g., "PROJ-456")',
196
+ },
197
+ },
198
+ required: ['testExecutionKey'],
199
+ },
200
+ },
201
+ {
202
+ name: 'search_test_executions',
203
+ description: 'Search for test executions using JQL (Jira Query Language)',
204
+ inputSchema: {
205
+ type: 'object',
206
+ properties: {
207
+ jql: {
208
+ type: 'string',
209
+ description: 'JQL query to search test executions (e.g., "project = PROJ AND created >= -7d")',
210
+ },
211
+ maxResults: {
212
+ type: 'number',
213
+ description: 'Maximum number of results to return',
214
+ default: 50,
215
+ },
216
+ },
217
+ required: ['jql'],
218
+ },
219
+ },
220
+ {
221
+ name: 'get_project_test_executions',
222
+ description: 'Get all test executions for a specific project',
223
+ inputSchema: {
224
+ type: 'object',
225
+ properties: {
226
+ projectKey: {
227
+ type: 'string',
228
+ description: 'The Jira project key (e.g., "PROJ")',
229
+ },
230
+ maxResults: {
231
+ type: 'number',
232
+ description: 'Maximum number of results to return',
233
+ default: 50,
234
+ },
235
+ },
236
+ required: ['projectKey'],
237
+ },
238
+ },
239
+ {
240
+ name: 'update_test_run_status',
241
+ description: 'Update the status of a specific test run (e.g., mark as PASS or FAIL)',
242
+ inputSchema: {
243
+ type: 'object',
244
+ properties: {
245
+ testRunId: {
246
+ type: 'string',
247
+ description: 'The test run ID (obtained from test execution details)',
248
+ },
249
+ status: {
250
+ type: 'string',
251
+ enum: ['TODO', 'EXECUTING', 'PASS', 'FAIL', 'ABORTED', 'PASSED', 'FAILED'],
252
+ description: 'The new status for the test run',
253
+ },
254
+ },
255
+ required: ['testRunId', 'status'],
256
+ },
257
+ },
258
+ // Test Plan tools
259
+ {
260
+ name: 'create_test_plan',
261
+ description: 'Create a new test plan in Xray Cloud to organize tests',
262
+ inputSchema: {
263
+ type: 'object',
264
+ properties: {
265
+ projectKey: {
266
+ type: 'string',
267
+ description: 'The Jira project key (e.g., "PROJ")',
268
+ },
269
+ summary: {
270
+ type: 'string',
271
+ description: 'The test plan summary/title',
272
+ },
273
+ description: {
274
+ type: 'string',
275
+ description: 'The test plan description',
276
+ },
277
+ testIssueIds: {
278
+ type: 'array',
279
+ items: { type: 'string' },
280
+ description: 'Array of test issue IDs to include in this plan',
281
+ },
282
+ },
283
+ required: ['projectKey', 'summary'],
284
+ },
285
+ },
286
+ {
287
+ name: 'get_test_plan',
288
+ description: 'Get details of a specific test plan by key, including all tests',
289
+ inputSchema: {
290
+ type: 'object',
291
+ properties: {
292
+ testPlanKey: {
293
+ type: 'string',
294
+ description: 'The test plan key (e.g., "PROJ-789")',
295
+ },
296
+ },
297
+ required: ['testPlanKey'],
298
+ },
299
+ },
300
+ {
301
+ name: 'search_test_plans',
302
+ description: 'Search for test plans using JQL (Jira Query Language)',
303
+ inputSchema: {
304
+ type: 'object',
305
+ properties: {
306
+ jql: {
307
+ type: 'string',
308
+ description: 'JQL query to search test plans',
309
+ },
310
+ maxResults: {
311
+ type: 'number',
312
+ description: 'Maximum number of results to return',
313
+ default: 50,
314
+ },
315
+ },
316
+ required: ['jql'],
317
+ },
318
+ },
319
+ {
320
+ name: 'get_project_test_plans',
321
+ description: 'Get all test plans for a specific project',
322
+ inputSchema: {
323
+ type: 'object',
324
+ properties: {
325
+ projectKey: {
326
+ type: 'string',
327
+ description: 'The Jira project key (e.g., "PROJ")',
328
+ },
329
+ maxResults: {
330
+ type: 'number',
331
+ description: 'Maximum number of results to return',
332
+ default: 50,
333
+ },
334
+ },
335
+ required: ['projectKey'],
336
+ },
337
+ },
338
+ {
339
+ name: 'add_tests_to_test_plan',
340
+ description: 'Add tests to an existing test plan',
341
+ inputSchema: {
342
+ type: 'object',
343
+ properties: {
344
+ testPlanIssueId: {
345
+ type: 'string',
346
+ description: 'The test plan issue ID (not key)',
347
+ },
348
+ testIssueIds: {
349
+ type: 'array',
350
+ items: { type: 'string' },
351
+ description: 'Array of test issue IDs to add',
352
+ },
353
+ },
354
+ required: ['testPlanIssueId', 'testIssueIds'],
355
+ },
356
+ },
357
+ {
358
+ name: 'remove_tests_from_test_plan',
359
+ description: 'Remove tests from an existing test plan',
360
+ inputSchema: {
361
+ type: 'object',
362
+ properties: {
363
+ testPlanIssueId: {
364
+ type: 'string',
365
+ description: 'The test plan issue ID (not key)',
366
+ },
367
+ testIssueIds: {
368
+ type: 'array',
369
+ items: { type: 'string' },
370
+ description: 'Array of test issue IDs to remove',
371
+ },
372
+ },
373
+ required: ['testPlanIssueId', 'testIssueIds'],
374
+ },
375
+ },
376
+ // Test Set tools
377
+ {
378
+ name: 'create_test_set',
379
+ description: 'Create a new test set in Xray Cloud to group related tests',
380
+ inputSchema: {
381
+ type: 'object',
382
+ properties: {
383
+ projectKey: {
384
+ type: 'string',
385
+ description: 'The Jira project key (e.g., "PROJ")',
386
+ },
387
+ summary: {
388
+ type: 'string',
389
+ description: 'The test set summary/title',
390
+ },
391
+ description: {
392
+ type: 'string',
393
+ description: 'The test set description',
394
+ },
395
+ testIssueIds: {
396
+ type: 'array',
397
+ items: { type: 'string' },
398
+ description: 'Array of test issue IDs to include in this set',
399
+ },
400
+ },
401
+ required: ['projectKey', 'summary'],
402
+ },
403
+ },
404
+ {
405
+ name: 'get_test_set',
406
+ description: 'Get details of a specific test set by key, including all tests',
407
+ inputSchema: {
408
+ type: 'object',
409
+ properties: {
410
+ testSetKey: {
411
+ type: 'string',
412
+ description: 'The test set key (e.g., "PROJ-890")',
413
+ },
414
+ },
415
+ required: ['testSetKey'],
416
+ },
417
+ },
418
+ {
419
+ name: 'search_test_sets',
420
+ description: 'Search for test sets using JQL (Jira Query Language)',
421
+ inputSchema: {
422
+ type: 'object',
423
+ properties: {
424
+ jql: {
425
+ type: 'string',
426
+ description: 'JQL query to search test sets',
427
+ },
428
+ maxResults: {
429
+ type: 'number',
430
+ description: 'Maximum number of results to return',
431
+ default: 50,
432
+ },
433
+ },
434
+ required: ['jql'],
435
+ },
436
+ },
437
+ {
438
+ name: 'get_project_test_sets',
439
+ description: 'Get all test sets for a specific project',
440
+ inputSchema: {
441
+ type: 'object',
442
+ properties: {
443
+ projectKey: {
444
+ type: 'string',
445
+ description: 'The Jira project key (e.g., "PROJ")',
446
+ },
447
+ maxResults: {
448
+ type: 'number',
449
+ description: 'Maximum number of results to return',
450
+ default: 50,
451
+ },
452
+ },
453
+ required: ['projectKey'],
454
+ },
455
+ },
456
+ {
457
+ name: 'add_tests_to_test_set',
458
+ description: 'Add tests to an existing test set',
459
+ inputSchema: {
460
+ type: 'object',
461
+ properties: {
462
+ testSetIssueId: {
463
+ type: 'string',
464
+ description: 'The test set issue ID (not key)',
465
+ },
466
+ testIssueIds: {
467
+ type: 'array',
468
+ items: { type: 'string' },
469
+ description: 'Array of test issue IDs to add',
470
+ },
471
+ },
472
+ required: ['testSetIssueId', 'testIssueIds'],
473
+ },
474
+ },
475
+ {
476
+ name: 'remove_tests_from_test_set',
477
+ description: 'Remove tests from an existing test set',
478
+ inputSchema: {
479
+ type: 'object',
480
+ properties: {
481
+ testSetIssueId: {
482
+ type: 'string',
483
+ description: 'The test set issue ID (not key)',
484
+ },
485
+ testIssueIds: {
486
+ type: 'array',
487
+ items: { type: 'string' },
488
+ description: 'Array of test issue IDs to remove',
489
+ },
490
+ },
491
+ required: ['testSetIssueId', 'testIssueIds'],
492
+ },
493
+ },
494
+ ];
495
+ // Create server instance
496
+ const server = new Server({
497
+ name: 'xray-mcp-server',
498
+ version: '1.0.0',
499
+ }, {
500
+ capabilities: {
501
+ tools: {},
502
+ },
503
+ });
504
+ // Handle tool listing
505
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
506
+ return { tools };
507
+ });
508
+ // Handle tool execution
509
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
510
+ const { name, arguments: args } = request.params;
511
+ if (!args) {
512
+ throw new Error('Missing arguments');
513
+ }
514
+ try {
515
+ switch (name) {
516
+ case 'create_test_case': {
517
+ const testCase = {
518
+ projectKey: args.projectKey,
519
+ summary: args.summary,
520
+ description: args.description,
521
+ testType: args.testType,
522
+ labels: args.labels,
523
+ priority: args.priority,
524
+ };
525
+ const result = await xrayClient.createTestCase(testCase);
526
+ return {
527
+ content: [
528
+ {
529
+ type: 'text',
530
+ text: JSON.stringify(result, null, 2),
531
+ },
532
+ ],
533
+ };
534
+ }
535
+ case 'get_test_case': {
536
+ const result = await xrayClient.getTestCase(args.testKey);
537
+ return {
538
+ content: [
539
+ {
540
+ type: 'text',
541
+ text: JSON.stringify(result, null, 2),
542
+ },
543
+ ],
544
+ };
545
+ }
546
+ case 'update_test_case': {
547
+ const updates = {};
548
+ if (args.summary)
549
+ updates.summary = args.summary;
550
+ if (args.description)
551
+ updates.description = args.description;
552
+ if (args.labels)
553
+ updates.labels = args.labels;
554
+ if (args.priority)
555
+ updates.priority = args.priority;
556
+ await xrayClient.updateTestCase(args.testKey, updates);
557
+ return {
558
+ content: [
559
+ {
560
+ type: 'text',
561
+ text: `Test case ${args.testKey} updated successfully`,
562
+ },
563
+ ],
564
+ };
565
+ }
566
+ case 'delete_test_case': {
567
+ await xrayClient.deleteTestCase(args.testKey);
568
+ return {
569
+ content: [
570
+ {
571
+ type: 'text',
572
+ text: `Test case ${args.testKey} deleted successfully`,
573
+ },
574
+ ],
575
+ };
576
+ }
577
+ case 'search_test_cases': {
578
+ const result = await xrayClient.searchTestCases(args.jql, args.maxResults);
579
+ return {
580
+ content: [
581
+ {
582
+ type: 'text',
583
+ text: JSON.stringify(result, null, 2),
584
+ },
585
+ ],
586
+ };
587
+ }
588
+ case 'get_project_test_cases': {
589
+ const result = await xrayClient.getTestCasesByProject(args.projectKey, args.maxResults);
590
+ return {
591
+ content: [
592
+ {
593
+ type: 'text',
594
+ text: JSON.stringify(result, null, 2),
595
+ },
596
+ ],
597
+ };
598
+ }
599
+ // Test Execution handlers
600
+ case 'create_test_execution': {
601
+ const testExecution = {
602
+ projectKey: args.projectKey,
603
+ summary: args.summary,
604
+ description: args.description,
605
+ testIssueIds: args.testIssueIds,
606
+ testEnvironments: args.testEnvironments,
607
+ };
608
+ const result = await xrayClient.createTestExecution(testExecution);
609
+ return {
610
+ content: [
611
+ {
612
+ type: 'text',
613
+ text: JSON.stringify(result, null, 2),
614
+ },
615
+ ],
616
+ };
617
+ }
618
+ case 'get_test_execution': {
619
+ const result = await xrayClient.getTestExecution(args.testExecutionKey);
620
+ return {
621
+ content: [
622
+ {
623
+ type: 'text',
624
+ text: JSON.stringify(result, null, 2),
625
+ },
626
+ ],
627
+ };
628
+ }
629
+ case 'search_test_executions': {
630
+ const result = await xrayClient.searchTestExecutions(args.jql, args.maxResults);
631
+ return {
632
+ content: [
633
+ {
634
+ type: 'text',
635
+ text: JSON.stringify(result, null, 2),
636
+ },
637
+ ],
638
+ };
639
+ }
640
+ case 'get_project_test_executions': {
641
+ const result = await xrayClient.getTestExecutionsByProject(args.projectKey, args.maxResults);
642
+ return {
643
+ content: [
644
+ {
645
+ type: 'text',
646
+ text: JSON.stringify(result, null, 2),
647
+ },
648
+ ],
649
+ };
650
+ }
651
+ case 'update_test_run_status': {
652
+ const result = await xrayClient.updateTestRunStatus(args.testRunId, args.status);
653
+ return {
654
+ content: [
655
+ {
656
+ type: 'text',
657
+ text: `Test run ${args.testRunId} status updated to ${args.status}: ${result}`,
658
+ },
659
+ ],
660
+ };
661
+ }
662
+ case 'create_test_plan': {
663
+ const testPlan = {
664
+ projectKey: args.projectKey,
665
+ summary: args.summary,
666
+ description: args.description,
667
+ testIssueIds: args.testIssueIds,
668
+ };
669
+ const result = await xrayClient.createTestPlan(testPlan);
670
+ return {
671
+ content: [
672
+ {
673
+ type: 'text',
674
+ text: JSON.stringify(result, null, 2),
675
+ },
676
+ ],
677
+ };
678
+ }
679
+ case 'get_test_plan': {
680
+ const result = await xrayClient.getTestPlan(args.testPlanKey);
681
+ return {
682
+ content: [
683
+ {
684
+ type: 'text',
685
+ text: JSON.stringify(result, null, 2),
686
+ },
687
+ ],
688
+ };
689
+ }
690
+ case 'search_test_plans': {
691
+ const result = await xrayClient.searchTestPlans(args.jql, args.maxResults);
692
+ return {
693
+ content: [
694
+ {
695
+ type: 'text',
696
+ text: JSON.stringify(result, null, 2),
697
+ },
698
+ ],
699
+ };
700
+ }
701
+ case 'get_project_test_plans': {
702
+ const result = await xrayClient.getTestPlansByProject(args.projectKey, args.maxResults);
703
+ return {
704
+ content: [
705
+ {
706
+ type: 'text',
707
+ text: JSON.stringify(result, null, 2),
708
+ },
709
+ ],
710
+ };
711
+ }
712
+ case 'add_tests_to_test_plan': {
713
+ const result = await xrayClient.addTestsToTestPlan(args.testPlanIssueId, args.testIssueIds);
714
+ return {
715
+ content: [
716
+ {
717
+ type: 'text',
718
+ text: JSON.stringify(result, null, 2),
719
+ },
720
+ ],
721
+ };
722
+ }
723
+ case 'remove_tests_from_test_plan': {
724
+ const result = await xrayClient.removeTestsFromTestPlan(args.testPlanIssueId, args.testIssueIds);
725
+ return {
726
+ content: [
727
+ {
728
+ type: 'text',
729
+ text: JSON.stringify(result, null, 2),
730
+ },
731
+ ],
732
+ };
733
+ }
734
+ case 'create_test_set': {
735
+ const testSet = {
736
+ projectKey: args.projectKey,
737
+ summary: args.summary,
738
+ description: args.description,
739
+ testIssueIds: args.testIssueIds,
740
+ };
741
+ const result = await xrayClient.createTestSet(testSet);
742
+ return {
743
+ content: [
744
+ {
745
+ type: 'text',
746
+ text: JSON.stringify(result, null, 2),
747
+ },
748
+ ],
749
+ };
750
+ }
751
+ case 'get_test_set': {
752
+ const result = await xrayClient.getTestSet(args.testSetKey);
753
+ return {
754
+ content: [
755
+ {
756
+ type: 'text',
757
+ text: JSON.stringify(result, null, 2),
758
+ },
759
+ ],
760
+ };
761
+ }
762
+ case 'search_test_sets': {
763
+ const result = await xrayClient.searchTestSets(args.jql, args.maxResults);
764
+ return {
765
+ content: [
766
+ {
767
+ type: 'text',
768
+ text: JSON.stringify(result, null, 2),
769
+ },
770
+ ],
771
+ };
772
+ }
773
+ case 'get_project_test_sets': {
774
+ const result = await xrayClient.getTestSetsByProject(args.projectKey, args.maxResults);
775
+ return {
776
+ content: [
777
+ {
778
+ type: 'text',
779
+ text: JSON.stringify(result, null, 2),
780
+ },
781
+ ],
782
+ };
783
+ }
784
+ case 'add_tests_to_test_set': {
785
+ const result = await xrayClient.addTestsToTestSet(args.testSetIssueId, args.testIssueIds);
786
+ return {
787
+ content: [
788
+ {
789
+ type: 'text',
790
+ text: JSON.stringify(result, null, 2),
791
+ },
792
+ ],
793
+ };
794
+ }
795
+ case 'remove_tests_from_test_set': {
796
+ const result = await xrayClient.removeTestsFromTestSet(args.testSetIssueId, args.testIssueIds);
797
+ return {
798
+ content: [
799
+ {
800
+ type: 'text',
801
+ text: JSON.stringify(result, null, 2),
802
+ },
803
+ ],
804
+ };
805
+ }
806
+ default:
807
+ throw new Error(`Unknown tool: ${name}`);
808
+ }
809
+ }
810
+ catch (error) {
811
+ const errorMessage = error instanceof Error ? error.message : String(error);
812
+ return {
813
+ content: [
814
+ {
815
+ type: 'text',
816
+ text: `Error: ${errorMessage}`,
817
+ },
818
+ ],
819
+ isError: true,
820
+ };
821
+ }
822
+ });
823
+ // Start the server
824
+ async function main() {
825
+ const transport = new StdioServerTransport();
826
+ await server.connect(transport);
827
+ // Handle EPIPE errors (broken pipe when client disconnects)
828
+ process.stdout.on('error', (err) => {
829
+ if (err.code === 'EPIPE') {
830
+ process.exit(0);
831
+ }
832
+ });
833
+ process.stderr.on('error', (err) => {
834
+ if (err.code === 'EPIPE') {
835
+ process.exit(0);
836
+ }
837
+ });
838
+ // Handle graceful shutdown
839
+ process.on('SIGINT', async () => {
840
+ await server.close();
841
+ process.exit(0);
842
+ });
843
+ process.on('SIGTERM', async () => {
844
+ await server.close();
845
+ process.exit(0);
846
+ });
847
+ }
848
+ main().catch((error) => {
849
+ console.error('Fatal error in MCP server:', error);
850
+ process.exit(1);
851
+ });