iflow-mcp-m507_ai-soc-agent 1.0.0__py3-none-any.whl

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 (85) hide show
  1. iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/METADATA +410 -0
  2. iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/RECORD +85 -0
  3. iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/WHEEL +5 -0
  4. iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/entry_points.txt +2 -0
  5. iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/licenses/LICENSE +21 -0
  6. iflow_mcp_m507_ai_soc_agent-1.0.0.dist-info/top_level.txt +1 -0
  7. src/__init__.py +8 -0
  8. src/ai_controller/README.md +139 -0
  9. src/ai_controller/__init__.py +12 -0
  10. src/ai_controller/agent_executor.py +596 -0
  11. src/ai_controller/cli/__init__.py +2 -0
  12. src/ai_controller/cli/main.py +243 -0
  13. src/ai_controller/session_manager.py +409 -0
  14. src/ai_controller/web/__init__.py +2 -0
  15. src/ai_controller/web/server.py +1181 -0
  16. src/ai_controller/web/static/css/README.md +102 -0
  17. src/api/__init__.py +13 -0
  18. src/api/case_management.py +271 -0
  19. src/api/edr.py +187 -0
  20. src/api/kb.py +136 -0
  21. src/api/siem.py +308 -0
  22. src/core/__init__.py +10 -0
  23. src/core/config.py +242 -0
  24. src/core/config_storage.py +684 -0
  25. src/core/dto.py +50 -0
  26. src/core/errors.py +36 -0
  27. src/core/logging.py +128 -0
  28. src/integrations/__init__.py +8 -0
  29. src/integrations/case_management/__init__.py +5 -0
  30. src/integrations/case_management/iris/__init__.py +11 -0
  31. src/integrations/case_management/iris/iris_client.py +885 -0
  32. src/integrations/case_management/iris/iris_http.py +274 -0
  33. src/integrations/case_management/iris/iris_mapper.py +263 -0
  34. src/integrations/case_management/iris/iris_models.py +128 -0
  35. src/integrations/case_management/thehive/__init__.py +8 -0
  36. src/integrations/case_management/thehive/thehive_client.py +193 -0
  37. src/integrations/case_management/thehive/thehive_http.py +147 -0
  38. src/integrations/case_management/thehive/thehive_mapper.py +190 -0
  39. src/integrations/case_management/thehive/thehive_models.py +125 -0
  40. src/integrations/cti/__init__.py +6 -0
  41. src/integrations/cti/local_tip/__init__.py +10 -0
  42. src/integrations/cti/local_tip/local_tip_client.py +90 -0
  43. src/integrations/cti/local_tip/local_tip_http.py +110 -0
  44. src/integrations/cti/opencti/__init__.py +10 -0
  45. src/integrations/cti/opencti/opencti_client.py +101 -0
  46. src/integrations/cti/opencti/opencti_http.py +418 -0
  47. src/integrations/edr/__init__.py +6 -0
  48. src/integrations/edr/elastic_defend/__init__.py +6 -0
  49. src/integrations/edr/elastic_defend/elastic_defend_client.py +351 -0
  50. src/integrations/edr/elastic_defend/elastic_defend_http.py +162 -0
  51. src/integrations/eng/__init__.py +10 -0
  52. src/integrations/eng/clickup/__init__.py +8 -0
  53. src/integrations/eng/clickup/clickup_client.py +513 -0
  54. src/integrations/eng/clickup/clickup_http.py +156 -0
  55. src/integrations/eng/github/__init__.py +8 -0
  56. src/integrations/eng/github/github_client.py +169 -0
  57. src/integrations/eng/github/github_http.py +158 -0
  58. src/integrations/eng/trello/__init__.py +8 -0
  59. src/integrations/eng/trello/trello_client.py +207 -0
  60. src/integrations/eng/trello/trello_http.py +162 -0
  61. src/integrations/kb/__init__.py +12 -0
  62. src/integrations/kb/fs_kb_client.py +313 -0
  63. src/integrations/siem/__init__.py +6 -0
  64. src/integrations/siem/elastic/__init__.py +6 -0
  65. src/integrations/siem/elastic/elastic_client.py +3319 -0
  66. src/integrations/siem/elastic/elastic_http.py +165 -0
  67. src/mcp/README.md +183 -0
  68. src/mcp/TOOLS.md +2827 -0
  69. src/mcp/__init__.py +13 -0
  70. src/mcp/__main__.py +18 -0
  71. src/mcp/agent_profiles.py +408 -0
  72. src/mcp/flow_agent_profiles.py +424 -0
  73. src/mcp/mcp_server.py +4086 -0
  74. src/mcp/rules_engine.py +487 -0
  75. src/mcp/runbook_manager.py +264 -0
  76. src/orchestrator/__init__.py +11 -0
  77. src/orchestrator/incident_workflow.py +244 -0
  78. src/orchestrator/tools_case.py +1085 -0
  79. src/orchestrator/tools_cti.py +359 -0
  80. src/orchestrator/tools_edr.py +315 -0
  81. src/orchestrator/tools_eng.py +378 -0
  82. src/orchestrator/tools_kb.py +156 -0
  83. src/orchestrator/tools_siem.py +1709 -0
  84. src/web/__init__.py +8 -0
  85. src/web/config_server.py +511 -0
@@ -0,0 +1,1085 @@
1
+ """
2
+ LLM-callable tools for case management operations.
3
+
4
+ These functions wrap the generic CaseManagementClient interface and provide
5
+ LLM-friendly error handling and return values.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any, Dict, List, Optional
11
+
12
+ from ..api.case_management import (
13
+ Case,
14
+ CaseAssignment,
15
+ CaseComment,
16
+ CaseManagementClient,
17
+ CaseObservable,
18
+ CasePriority,
19
+ CaseSearchQuery,
20
+ CaseStatus,
21
+ CaseSummary,
22
+ )
23
+ from ..core.errors import IntegrationError
24
+
25
+
26
+ def create_case(
27
+ title: str,
28
+ description: str,
29
+ priority: str = "medium",
30
+ status: str = "open",
31
+ tags: Optional[List[str]] = None,
32
+ alert_id: Optional[str] = None,
33
+ client: CaseManagementClient = None, # type: ignore
34
+ ) -> Dict[str, Any]:
35
+ """
36
+ Create a new case.
37
+
38
+ Tool schema:
39
+ - name: create_case
40
+ - description: Create a new case for investigation. Follows the case standard
41
+ format defined in standards/case_standard.md.
42
+ - parameters:
43
+ - title (str, required): Case title following format: [Alert Type] - [Primary Entity] - [Date/Time]
44
+ - description (str, required): Comprehensive case description
45
+ - priority (str, optional): Case priority (low, medium, high, critical). Default: medium
46
+ - status (str, optional): Case status (open, in_progress, closed). Default: open
47
+ - tags (list[str], optional): Tags for categorization
48
+ - alert_id (str, optional): Associated alert ID if case is created from an alert
49
+
50
+ Args:
51
+ title: Case title.
52
+ description: Case description.
53
+ priority: Case priority.
54
+ status: Case status.
55
+ tags: Optional tags.
56
+ alert_id: Optional associated alert ID.
57
+ client: The case management client.
58
+
59
+ Returns:
60
+ Dictionary containing created case details.
61
+
62
+ Raises:
63
+ IntegrationError: If case creation fails.
64
+ """
65
+ if client is None:
66
+ raise IntegrationError("Case management client not provided")
67
+
68
+ try:
69
+ priority_enum = CasePriority(priority.lower())
70
+ status_enum = CaseStatus(status.lower())
71
+
72
+ new_case = Case(
73
+ id=None,
74
+ title=title,
75
+ description=description,
76
+ status=status_enum,
77
+ priority=priority_enum,
78
+ tags=tags,
79
+ )
80
+
81
+ created = client.create_case(new_case)
82
+
83
+ return {
84
+ "success": True,
85
+ "case_id": created.id,
86
+ "case": {
87
+ "id": created.id,
88
+ "title": created.title,
89
+ "description": created.description,
90
+ "status": created.status.value,
91
+ "priority": created.priority.value,
92
+ "tags": created.tags or [],
93
+ "created_at": created.created_at.isoformat() if created.created_at else None,
94
+ "updated_at": created.updated_at.isoformat() if created.updated_at else None,
95
+ },
96
+ }
97
+ except ValueError as e:
98
+ raise IntegrationError(f"Invalid priority or status: {str(e)}")
99
+ except Exception as e:
100
+ raise IntegrationError(f"Failed to create case: {str(e)}") from e
101
+
102
+
103
+ def review_case(
104
+ case_id: str,
105
+ client: CaseManagementClient = None, # type: ignore
106
+ ) -> Dict[str, Any]:
107
+ """
108
+ Review a case by retrieving its full details.
109
+
110
+ Tool schema:
111
+ - name: review_case
112
+ - description: Retrieve and review the full details of a case including
113
+ title, description, status, priority, observables, and comments.
114
+ - parameters:
115
+ - case_id (str, required): The ID of the case to review.
116
+
117
+ Args:
118
+ case_id: The ID of the case to review.
119
+ client: The case management client.
120
+
121
+ Returns:
122
+ Dictionary containing case details in a format suitable for LLM consumption.
123
+
124
+ Raises:
125
+ IntegrationError: If the case cannot be retrieved.
126
+ """
127
+ if client is None:
128
+ raise IntegrationError("Case management client not provided")
129
+
130
+ try:
131
+ case = client.get_case(case_id)
132
+ timeline = client.get_case_timeline(case_id)
133
+
134
+ return {
135
+ "success": True,
136
+ "case": {
137
+ "id": case.id,
138
+ "title": case.title,
139
+ "description": case.description,
140
+ "status": case.status.value,
141
+ "priority": case.priority.value,
142
+ "assignee": case.assignee,
143
+ "tags": case.tags or [],
144
+ "observables": [
145
+ {
146
+ "type": obs.type,
147
+ "value": obs.value,
148
+ "description": obs.description,
149
+ "tags": obs.tags or [],
150
+ }
151
+ for obs in (case.observables or [])
152
+ ],
153
+ "created_at": case.created_at.isoformat() if case.created_at else None,
154
+ "updated_at": case.updated_at.isoformat() if case.updated_at else None,
155
+ },
156
+ "timeline": [
157
+ {
158
+ "author": comment.author,
159
+ "content": comment.content,
160
+ "created_at": comment.created_at.isoformat()
161
+ if comment.created_at
162
+ else None,
163
+ }
164
+ for comment in timeline
165
+ ],
166
+ }
167
+ except Exception as e:
168
+ error_msg = f"Failed to review case {case_id}: {str(e)}"
169
+ if isinstance(e, IntegrationError):
170
+ raise
171
+ raise IntegrationError(error_msg) from e
172
+
173
+
174
+ def list_cases(
175
+ status: Optional[str] = None,
176
+ limit: int = 50,
177
+ client: CaseManagementClient = None, # type: ignore
178
+ ) -> Dict[str, Any]:
179
+ """
180
+ List cases, optionally filtered by status.
181
+
182
+ Tool schema:
183
+ - name: list_cases
184
+ - description: List cases from the case management system, optionally
185
+ filtered by status (open, in_progress, closed).
186
+ - parameters:
187
+ - status (str, optional): Filter by status (open, in_progress, closed).
188
+ - limit (int, optional): Maximum number of cases to return (default: 50).
189
+
190
+ Args:
191
+ status: Optional status filter.
192
+ limit: Maximum number of cases to return.
193
+ client: The case management client.
194
+
195
+ Returns:
196
+ Dictionary containing list of case summaries.
197
+
198
+ Raises:
199
+ IntegrationError: If listing cases fails.
200
+ """
201
+ if client is None:
202
+ raise IntegrationError("Case management client not provided")
203
+
204
+ try:
205
+ status_enum = None
206
+ if status:
207
+ try:
208
+ status_enum = CaseStatus(status.lower())
209
+ except ValueError:
210
+ raise IntegrationError(f"Invalid status: {status}")
211
+
212
+ cases = client.list_cases(status=status_enum, limit=limit)
213
+
214
+ # Filter out case ID 1 (default demo case) - always ignore it
215
+ filtered_cases = [case for case in cases if str(case.id) != "1"]
216
+
217
+ return {
218
+ "success": True,
219
+ "count": len(filtered_cases),
220
+ "cases": [
221
+ {
222
+ "id": case.id,
223
+ "title": case.title,
224
+ "status": case.status.value,
225
+ "priority": case.priority.value,
226
+ "assignee": case.assignee,
227
+ "created_at": case.created_at.isoformat()
228
+ if case.created_at
229
+ else None,
230
+ }
231
+ for case in filtered_cases
232
+ ],
233
+ }
234
+ except IntegrationError:
235
+ raise
236
+ except Exception as e:
237
+ raise IntegrationError(f"Failed to list cases: {str(e)}") from e
238
+
239
+
240
+ def search_cases(
241
+ text: Optional[str] = None,
242
+ status: Optional[str] = None,
243
+ priority: Optional[str] = None,
244
+ tags: Optional[List[str]] = None,
245
+ assignee: Optional[str] = None,
246
+ limit: int = 50,
247
+ client: CaseManagementClient = None, # type: ignore
248
+ ) -> Dict[str, Any]:
249
+ """
250
+ Search for cases using various filters.
251
+
252
+ Tool schema:
253
+ - name: search_cases
254
+ - description: Search for cases using text search, status, priority, tags,
255
+ or assignee filters.
256
+ - parameters:
257
+ - text (str, optional): Text to search for in case title/description.
258
+ - status (str, optional): Filter by status.
259
+ - priority (str, optional): Filter by priority (low, medium, high, critical).
260
+ - tags (list[str], optional): Filter by tags.
261
+ - assignee (str, optional): Filter by assignee.
262
+ - limit (int, optional): Maximum results (default: 50).
263
+
264
+ Args:
265
+ text: Text search query.
266
+ status: Status filter.
267
+ priority: Priority filter.
268
+ tags: Tags to filter by.
269
+ assignee: Assignee to filter by.
270
+ limit: Maximum results.
271
+ client: The case management client.
272
+
273
+ Returns:
274
+ Dictionary containing search results.
275
+
276
+ Raises:
277
+ IntegrationError: If search fails.
278
+ """
279
+ if client is None:
280
+ raise IntegrationError("Case management client not provided")
281
+
282
+ try:
283
+ status_enum = None
284
+ if status:
285
+ try:
286
+ status_enum = CaseStatus(status.lower())
287
+ except ValueError:
288
+ raise IntegrationError(f"Invalid status: {status}")
289
+
290
+ priority_enum = None
291
+ if priority:
292
+ try:
293
+ priority_enum = CasePriority(priority.lower())
294
+ except ValueError:
295
+ raise IntegrationError(f"Invalid priority: {priority}")
296
+
297
+ query = CaseSearchQuery(
298
+ text=text,
299
+ status=status_enum,
300
+ priority=priority_enum,
301
+ tags=tags,
302
+ assignee=assignee,
303
+ limit=limit,
304
+ )
305
+
306
+ results = client.search_cases(query)
307
+
308
+ return {
309
+ "success": True,
310
+ "count": len(results),
311
+ "cases": [
312
+ {
313
+ "id": case.id,
314
+ "title": case.title,
315
+ "status": case.status.value,
316
+ "priority": case.priority.value,
317
+ "assignee": case.assignee,
318
+ "created_at": case.created_at.isoformat()
319
+ if case.created_at
320
+ else None,
321
+ }
322
+ for case in results
323
+ ],
324
+ }
325
+ except IntegrationError:
326
+ raise
327
+ except Exception as e:
328
+ raise IntegrationError(f"Failed to search cases: {str(e)}") from e
329
+
330
+
331
+ def add_case_comment(
332
+ case_id: str,
333
+ content: str,
334
+ author: Optional[str] = None,
335
+ client: CaseManagementClient = None, # type: ignore
336
+ ) -> Dict[str, Any]:
337
+ """
338
+ Add a comment to a case.
339
+
340
+ Tool schema:
341
+ - name: add_case_comment
342
+ - description: Add a comment or note to a case.
343
+ - parameters:
344
+ - case_id (str, required): The ID of the case.
345
+ - content (str, required): The comment content.
346
+ - author (str, optional): The author of the comment.
347
+
348
+ Args:
349
+ case_id: The case ID.
350
+ content: The comment content.
351
+ author: Optional author name.
352
+ client: The case management client.
353
+
354
+ Returns:
355
+ Dictionary with comment details.
356
+
357
+ Raises:
358
+ IntegrationError: If adding comment fails.
359
+ """
360
+ if client is None:
361
+ raise IntegrationError("Case management client not provided")
362
+
363
+ try:
364
+ comment = client.add_case_comment(case_id, content, author)
365
+
366
+ return {
367
+ "success": True,
368
+ "comment": {
369
+ "id": comment.id,
370
+ "case_id": comment.case_id,
371
+ "author": comment.author,
372
+ "content": comment.content,
373
+ "created_at": comment.created_at.isoformat()
374
+ if comment.created_at
375
+ else None,
376
+ },
377
+ }
378
+ except Exception as e:
379
+ raise IntegrationError(f"Failed to add comment to case {case_id}: {str(e)}") from e
380
+
381
+
382
+ def attach_observable_to_case(
383
+ case_id: str,
384
+ observable_type: str,
385
+ observable_value: str,
386
+ description: Optional[str] = None,
387
+ tags: Optional[List[str]] = None,
388
+ client: CaseManagementClient = None, # type: ignore
389
+ ) -> Dict[str, Any]:
390
+ """
391
+ Attach an observable (IP, hash, domain, etc.) to a case.
392
+
393
+ Tool schema:
394
+ - name: attach_observable_to_case
395
+ - description: Attach an observable such as an IP address, file hash,
396
+ domain, or URL to a case for tracking and analysis.
397
+ - parameters:
398
+ - case_id (str, required): The ID of the case.
399
+ - observable_type (str, required): Type of observable (ip, hash, domain, url, etc.).
400
+ - observable_value (str, required): The value of the observable.
401
+ - description (str, optional): Description of the observable.
402
+ - tags (list[str], optional): Tags for the observable.
403
+
404
+ Args:
405
+ case_id: The case ID.
406
+ observable_type: Type of observable (ip, hash, domain, url, etc.).
407
+ observable_value: The observable value.
408
+ description: Optional description.
409
+ tags: Optional tags.
410
+ client: The case management client.
411
+
412
+ Returns:
413
+ Dictionary with observable details.
414
+
415
+ Raises:
416
+ IntegrationError: If adding observable fails.
417
+ """
418
+ if client is None:
419
+ raise IntegrationError("Case management client not provided")
420
+
421
+ try:
422
+ observable = CaseObservable(
423
+ type=observable_type,
424
+ value=observable_value,
425
+ description=description,
426
+ tags=tags,
427
+ )
428
+
429
+ added = client.add_case_observable(case_id, observable)
430
+
431
+ return {
432
+ "success": True,
433
+ "observable": {
434
+ "type": added.type,
435
+ "value": added.value,
436
+ "description": added.description,
437
+ "tags": added.tags or [],
438
+ },
439
+ }
440
+ except Exception as e:
441
+ raise IntegrationError(
442
+ f"Failed to attach observable to case {case_id}: {str(e)}"
443
+ ) from e
444
+
445
+
446
+ def update_case_status(
447
+ case_id: str,
448
+ status: str,
449
+ client: CaseManagementClient = None, # type: ignore
450
+ ) -> Dict[str, Any]:
451
+ """
452
+ Update the status of a case.
453
+
454
+ Tool schema:
455
+ - name: update_case_status
456
+ - description: Update the status of a case (open, in_progress, closed).
457
+ - parameters:
458
+ - case_id (str, required): The ID of the case.
459
+ - status (str, required): New status (open, in_progress, closed).
460
+
461
+ Args:
462
+ case_id: The case ID.
463
+ status: New status value.
464
+ client: The case management client.
465
+
466
+ Returns:
467
+ Dictionary with updated case details.
468
+
469
+ Raises:
470
+ IntegrationError: If status update fails.
471
+ """
472
+ if client is None:
473
+ raise IntegrationError("Case management client not provided")
474
+
475
+ try:
476
+ status_enum = CaseStatus(status.lower())
477
+ updated = client.update_case_status(case_id, status_enum)
478
+
479
+ return {
480
+ "success": True,
481
+ "case": {
482
+ "id": updated.id,
483
+ "title": updated.title,
484
+ "status": updated.status.value,
485
+ },
486
+ }
487
+ except ValueError:
488
+ raise IntegrationError(f"Invalid status: {status}")
489
+ except Exception as e:
490
+ raise IntegrationError(f"Failed to update case status: {str(e)}") from e
491
+
492
+
493
+ def assign_case(
494
+ case_id: str,
495
+ assignee: str,
496
+ client: CaseManagementClient = None, # type: ignore
497
+ ) -> Dict[str, Any]:
498
+ """
499
+ Assign a case to a user.
500
+
501
+ Tool schema:
502
+ - name: assign_case
503
+ - description: Assign a case to a specific user or analyst.
504
+ - parameters:
505
+ - case_id (str, required): The ID of the case.
506
+ - assignee (str, required): The username or ID of the assignee.
507
+
508
+ Args:
509
+ case_id: The case ID.
510
+ assignee: The assignee username/ID.
511
+ client: The case management client.
512
+
513
+ Returns:
514
+ Dictionary with assignment details.
515
+
516
+ Raises:
517
+ IntegrationError: If assignment fails.
518
+ """
519
+ if client is None:
520
+ raise IntegrationError("Case management client not provided")
521
+
522
+ try:
523
+ assignment = client.assign_case(case_id, assignee)
524
+
525
+ return {
526
+ "success": True,
527
+ "assignment": {
528
+ "case_id": assignment.case_id,
529
+ "assignee": assignment.assignee,
530
+ "assigned_at": assignment.assigned_at.isoformat()
531
+ if assignment.assigned_at
532
+ else None,
533
+ },
534
+ }
535
+ except Exception as e:
536
+ raise IntegrationError(f"Failed to assign case {case_id}: {str(e)}") from e
537
+
538
+
539
+ def get_case_timeline(
540
+ case_id: str,
541
+ client: CaseManagementClient = None, # type: ignore
542
+ ) -> Dict[str, Any]:
543
+ """
544
+ Get the timeline of events/comments for a case.
545
+
546
+ Tool schema:
547
+ - name: get_case_timeline
548
+ - description: Retrieve the timeline of comments and events for a case,
549
+ ordered chronologically.
550
+ - parameters:
551
+ - case_id (str, required): The ID of the case.
552
+
553
+ Args:
554
+ case_id: The case ID.
555
+ client: The case management client.
556
+
557
+ Returns:
558
+ Dictionary containing timeline events.
559
+
560
+ Raises:
561
+ IntegrationError: If retrieving timeline fails.
562
+ """
563
+ if client is None:
564
+ raise IntegrationError("Case management client not provided")
565
+
566
+ try:
567
+ timeline = client.get_case_timeline(case_id)
568
+
569
+ return {
570
+ "success": True,
571
+ "case_id": case_id,
572
+ "count": len(timeline),
573
+ "timeline": [
574
+ {
575
+ "author": comment.author,
576
+ "content": comment.content,
577
+ "created_at": comment.created_at.isoformat()
578
+ if comment.created_at
579
+ else None,
580
+ }
581
+ for comment in timeline
582
+ ],
583
+ }
584
+ except Exception as e:
585
+ raise IntegrationError(f"Failed to get timeline for case {case_id}: {str(e)}") from e
586
+
587
+
588
+ def add_case_task(
589
+ case_id: str,
590
+ title: str,
591
+ description: str,
592
+ assignee: Optional[str] = None,
593
+ priority: str = "medium",
594
+ status: str = "pending",
595
+ client: CaseManagementClient = None, # type: ignore
596
+ ) -> Dict[str, Any]:
597
+ """
598
+ Add a task to a case.
599
+
600
+ Tool schema:
601
+ - name: add_case_task
602
+ - description: Add a task to a case. Tasks represent actionable items for investigation and response, typically assigned to SOC2 or SOC3 tiers.
603
+ - parameters:
604
+ - case_id (str, required): The ID of the case
605
+ - title (str, required): Task title
606
+ - description (str, required): Task description
607
+ - assignee (str, optional): Assignee ID or SOC tier (e.g., "SOC2", "SOC3")
608
+ - priority (str, optional): Task priority (low, medium, high, critical). Default: medium
609
+ - status (str, optional): Task status (pending, in_progress, completed, blocked). Default: pending
610
+ """
611
+ if client is None:
612
+ raise IntegrationError("Case management client not provided")
613
+
614
+ if not hasattr(client, "add_case_task"):
615
+ raise IntegrationError("Case management client does not support tasks")
616
+
617
+ try:
618
+ result = client.add_case_task(
619
+ case_id=case_id,
620
+ title=title,
621
+ description=description,
622
+ assignee=assignee,
623
+ priority=priority,
624
+ status=status,
625
+ )
626
+
627
+ return {
628
+ "success": True,
629
+ "case_id": case_id,
630
+ "task": result,
631
+ "message": f"Task '{title}' added to case {case_id}"
632
+ }
633
+ except Exception as e:
634
+ raise IntegrationError(f"Failed to add task to case {case_id}: {str(e)}") from e
635
+
636
+
637
+ def list_case_tasks(
638
+ case_id: str,
639
+ client: CaseManagementClient = None, # type: ignore
640
+ ) -> Dict[str, Any]:
641
+ """
642
+ List tasks for a case.
643
+
644
+ Tool schema:
645
+ - name: list_case_tasks
646
+ - description: List all tasks associated with a case
647
+ - parameters:
648
+ - case_id (str, required): The ID of the case
649
+ """
650
+ if client is None:
651
+ raise IntegrationError("Case management client not provided")
652
+
653
+ if not hasattr(client, "list_case_tasks"):
654
+ raise IntegrationError("Case management client does not support tasks")
655
+
656
+ try:
657
+ tasks = client.list_case_tasks(case_id)
658
+
659
+ return {
660
+ "success": True,
661
+ "case_id": case_id,
662
+ "count": len(tasks),
663
+ "tasks": tasks,
664
+ }
665
+ except Exception as e:
666
+ raise IntegrationError(f"Failed to list tasks for case {case_id}: {str(e)}") from e
667
+
668
+
669
+ def update_case_task_status(
670
+ case_id: str,
671
+ task_id: str,
672
+ status: str,
673
+ client: CaseManagementClient = None, # type: ignore
674
+ ) -> Dict[str, Any]:
675
+ """
676
+ Update the status of a task.
677
+
678
+ Tool schema:
679
+ - name: update_case_task_status
680
+ - description: Update the status of a task (pending, in_progress, completed, blocked)
681
+ - parameters:
682
+ - case_id (str, required): The ID of the case
683
+ - task_id (str, required): The ID of the task to update
684
+ - status (str, required): New task status (pending, in_progress, completed, blocked)
685
+ """
686
+ if client is None:
687
+ raise IntegrationError("Case management client not provided")
688
+
689
+ if not hasattr(client, "update_case_task_status"):
690
+ raise IntegrationError("Case management client does not support task status updates")
691
+
692
+ try:
693
+ valid_statuses = ["pending", "in_progress", "completed", "blocked"]
694
+ if status.lower() not in valid_statuses:
695
+ raise IntegrationError(f"Invalid task status: {status}. Valid statuses: {', '.join(valid_statuses)}")
696
+
697
+ result = client.update_case_task_status(
698
+ case_id=case_id,
699
+ task_id=task_id,
700
+ status=status,
701
+ )
702
+
703
+ return {
704
+ "success": True,
705
+ "case_id": case_id,
706
+ "task_id": task_id,
707
+ "task": result,
708
+ "message": f"Task {task_id} status updated to '{status}'"
709
+ }
710
+ except Exception as e:
711
+ raise IntegrationError(f"Failed to update task status: {str(e)}") from e
712
+
713
+
714
+ def add_case_asset(
715
+ case_id: str,
716
+ asset_name: str,
717
+ asset_type: str,
718
+ description: Optional[str] = None,
719
+ ip_address: Optional[str] = None,
720
+ hostname: Optional[str] = None,
721
+ tags: Optional[List[str]] = None,
722
+ client: CaseManagementClient = None, # type: ignore
723
+ ) -> Dict[str, Any]:
724
+ """
725
+ Add an asset to a case.
726
+
727
+ Tool schema:
728
+ - name: add_case_asset
729
+ - description: Add an asset (endpoint, server, network, user account, application) to a case
730
+ - parameters:
731
+ - case_id (str, required): The ID of the case
732
+ - asset_name (str, required): Asset name/identifier
733
+ - asset_type (str, required): Asset type (endpoint, server, network, user_account, application)
734
+ - description (str, optional): Asset description
735
+ - ip_address (str, optional): IP address if applicable
736
+ - hostname (str, optional): Hostname if applicable
737
+ - tags (list[str], optional): Tags for the asset
738
+ """
739
+ if client is None:
740
+ raise IntegrationError("Case management client not provided")
741
+
742
+ if not hasattr(client, "add_case_asset"):
743
+ raise IntegrationError("Case management client does not support assets")
744
+
745
+ try:
746
+ result = client.add_case_asset(
747
+ case_id=case_id,
748
+ asset_name=asset_name,
749
+ asset_type=asset_type,
750
+ description=description,
751
+ ip_address=ip_address,
752
+ hostname=hostname,
753
+ tags=tags,
754
+ )
755
+
756
+ return {
757
+ "success": True,
758
+ "case_id": case_id,
759
+ "asset": result,
760
+ "message": f"Asset '{asset_name}' added to case {case_id}"
761
+ }
762
+ except Exception as e:
763
+ raise IntegrationError(f"Failed to add asset to case {case_id}: {str(e)}") from e
764
+
765
+
766
+ def list_case_assets(
767
+ case_id: str,
768
+ client: CaseManagementClient = None, # type: ignore
769
+ ) -> Dict[str, Any]:
770
+ """
771
+ List assets for a case.
772
+
773
+ Tool schema:
774
+ - name: list_case_assets
775
+ - description: List all assets associated with a case
776
+ - parameters:
777
+ - case_id (str, required): The ID of the case
778
+ """
779
+ if client is None:
780
+ raise IntegrationError("Case management client not provided")
781
+
782
+ if not hasattr(client, "list_case_assets"):
783
+ raise IntegrationError("Case management client does not support assets")
784
+
785
+ try:
786
+ assets = client.list_case_assets(case_id)
787
+
788
+ return {
789
+ "success": True,
790
+ "case_id": case_id,
791
+ "count": len(assets),
792
+ "assets": assets,
793
+ }
794
+ except Exception as e:
795
+ raise IntegrationError(f"Failed to list assets for case {case_id}: {str(e)}") from e
796
+
797
+
798
+ def add_case_evidence(
799
+ case_id: str,
800
+ file_path: str,
801
+ description: Optional[str] = None,
802
+ evidence_type: Optional[str] = None,
803
+ client: CaseManagementClient = None, # type: ignore
804
+ ) -> Dict[str, Any]:
805
+ """
806
+ Add evidence (file) to a case.
807
+
808
+ Tool schema:
809
+ - name: add_case_evidence
810
+ - description: Upload and attach evidence (file, log, screenshot, network capture, etc.) to a case
811
+ - parameters:
812
+ - case_id (str, required): The ID of the case
813
+ - file_path (str, required): Path to the evidence file
814
+ - description (str, optional): Description of the evidence
815
+ - evidence_type (str, optional): Type of evidence (file, screenshot, log, network_capture, memory_dump, registry, other)
816
+ """
817
+ if client is None:
818
+ raise IntegrationError("Case management client not provided")
819
+
820
+ if not hasattr(client, "add_case_evidence"):
821
+ raise IntegrationError("Case management client does not support evidence")
822
+
823
+ try:
824
+ result = client.add_case_evidence(
825
+ case_id=case_id,
826
+ file_path=file_path,
827
+ description=description,
828
+ evidence_type=evidence_type,
829
+ )
830
+
831
+ return {
832
+ "success": True,
833
+ "case_id": case_id,
834
+ "evidence": result,
835
+ "message": f"Evidence file '{file_path}' added to case {case_id}"
836
+ }
837
+ except Exception as e:
838
+ raise IntegrationError(f"Failed to add evidence to case {case_id}: {str(e)}") from e
839
+
840
+
841
+ def list_case_evidence(
842
+ case_id: str,
843
+ client: CaseManagementClient = None, # type: ignore
844
+ ) -> Dict[str, Any]:
845
+ """
846
+ List evidence for a case.
847
+
848
+ Tool schema:
849
+ - name: list_case_evidence
850
+ - description: List all evidence files associated with a case
851
+ - parameters:
852
+ - case_id (str, required): The ID of the case
853
+ """
854
+ if client is None:
855
+ raise IntegrationError("Case management client not provided")
856
+
857
+ if not hasattr(client, "list_case_evidence"):
858
+ raise IntegrationError("Case management client does not support evidence")
859
+
860
+ try:
861
+ evidence = client.list_case_evidence(case_id)
862
+
863
+ return {
864
+ "success": True,
865
+ "case_id": case_id,
866
+ "count": len(evidence),
867
+ "evidence": evidence,
868
+ }
869
+ except Exception as e:
870
+ raise IntegrationError(f"Failed to list evidence for case {case_id}: {str(e)}") from e
871
+
872
+
873
+ def update_case(
874
+ case_id: str,
875
+ title: Optional[str] = None,
876
+ description: Optional[str] = None,
877
+ priority: Optional[str] = None,
878
+ status: Optional[str] = None,
879
+ tags: Optional[List[str]] = None,
880
+ assignee: Optional[str] = None,
881
+ client: CaseManagementClient = None, # type: ignore
882
+ ) -> Dict[str, Any]:
883
+ """
884
+ Update a case with new information.
885
+
886
+ Tool schema:
887
+ - name: update_case
888
+ - description: Update a case with new information (title, description, priority, status, tags, assignee)
889
+ - parameters:
890
+ - case_id (str, required): The ID of the case to update
891
+ - title (str, optional): New case title
892
+ - description (str, optional): New case description
893
+ - priority (str, optional): New priority (low, medium, high, critical)
894
+ - status (str, optional): New status (open, in_progress, closed)
895
+ - tags (list[str], optional): New tags list
896
+ - assignee (str, optional): New assignee
897
+ """
898
+ if client is None:
899
+ raise IntegrationError("Case management client not provided")
900
+
901
+ try:
902
+ updates = {}
903
+ if title is not None:
904
+ updates["title"] = title
905
+ if description is not None:
906
+ updates["description"] = description
907
+ if priority is not None:
908
+ updates["priority"] = CasePriority(priority.lower())
909
+ if status is not None:
910
+ updates["status"] = CaseStatus(status.lower())
911
+ if tags is not None:
912
+ updates["tags"] = tags
913
+ if assignee is not None:
914
+ updates["assignee"] = assignee
915
+
916
+ if not updates:
917
+ raise IntegrationError("No updates provided")
918
+
919
+ updated = client.update_case(case_id, updates)
920
+
921
+ return {
922
+ "success": True,
923
+ "case": {
924
+ "id": updated.id,
925
+ "title": updated.title,
926
+ "description": updated.description,
927
+ "status": updated.status.value,
928
+ "priority": updated.priority.value,
929
+ "tags": updated.tags or [],
930
+ "assignee": updated.assignee,
931
+ "updated_at": updated.updated_at.isoformat() if updated.updated_at else None,
932
+ },
933
+ }
934
+ except ValueError as e:
935
+ raise IntegrationError(f"Invalid priority or status: {str(e)}")
936
+ except Exception as e:
937
+ raise IntegrationError(f"Failed to update case {case_id}: {str(e)}") from e
938
+
939
+
940
+ def link_cases(
941
+ source_case_id: str,
942
+ target_case_id: str,
943
+ link_type: str = "related_to",
944
+ client: CaseManagementClient = None, # type: ignore
945
+ ) -> Dict[str, Any]:
946
+ """
947
+ Link two cases together.
948
+
949
+ Tool schema:
950
+ - name: link_cases
951
+ - description: Link two cases together to indicate a relationship (e.g., duplicate, related, escalated from)
952
+ - parameters:
953
+ - source_case_id (str, required): The ID of the source case
954
+ - target_case_id (str, required): The ID of the target case to link to
955
+ - link_type (str, optional): Type of link (related_to, duplicate_of, escalated_from, child_of, blocked_by). Default: related_to
956
+ """
957
+ if client is None:
958
+ raise IntegrationError("Case management client not provided")
959
+
960
+ try:
961
+ client.link_cases(source_case_id, target_case_id, link_type)
962
+
963
+ return {
964
+ "success": True,
965
+ "source_case_id": source_case_id,
966
+ "target_case_id": target_case_id,
967
+ "link_type": link_type,
968
+ "message": f"Case {source_case_id} linked to {target_case_id} with type '{link_type}'",
969
+ }
970
+ except Exception as e:
971
+ raise IntegrationError(
972
+ f"Failed to link cases {source_case_id} and {target_case_id}: {str(e)}"
973
+ ) from e
974
+
975
+
976
+ def add_case_timeline_event(
977
+ case_id: str,
978
+ title: str,
979
+ content: str,
980
+ source: Optional[str] = None,
981
+ category_id: Optional[int] = None,
982
+ tags: Optional[List[str]] = None,
983
+ color: Optional[str] = None,
984
+ event_date: Optional[str] = None,
985
+ include_in_summary: bool = True,
986
+ include_in_graph: bool = True,
987
+ sync_iocs_assets: bool = True,
988
+ asset_ids: Optional[List[int]] = None,
989
+ ioc_ids: Optional[List[int]] = None,
990
+ custom_attributes: Optional[Dict[str, Any]] = None,
991
+ raw: Optional[str] = None,
992
+ tz: Optional[str] = None,
993
+ client: CaseManagementClient = None, # type: ignore
994
+ ) -> Dict[str, Any]:
995
+ """
996
+ Add an event to a case timeline.
997
+
998
+ Tool schema:
999
+ - name: add_case_timeline_event
1000
+ - description: Add an event to a case timeline for tracking investigation activities and milestones
1001
+ - parameters:
1002
+ - case_id (str, required): The ID of the case
1003
+ - title (str, required): Event title
1004
+ - content (str, required): Event content/description
1005
+ - source (str, optional): Event source (e.g., "SamiGPT", "SIEM", "EDR")
1006
+ - category_id (int, optional): Event category ID
1007
+ - tags (list[str], optional): Event tags
1008
+ - color (str, optional): Event color (hex format, e.g., "#1572E899")
1009
+ - event_date (str, optional): Event date in ISO format (defaults to current time)
1010
+ - include_in_summary (bool, optional): Include event in case summary (default: true)
1011
+ - include_in_graph (bool, optional): Include event in case graph (default: true)
1012
+ - sync_iocs_assets (bool, optional): Sync with IOCs and assets (default: true)
1013
+ - asset_ids (list[int], optional): Related asset IDs
1014
+ - ioc_ids (list[int], optional): Related IOC IDs
1015
+ - custom_attributes (dict, optional): Custom attributes
1016
+ - raw (str, optional): Raw event data
1017
+ - tz (str, optional): Timezone (default: "+00:00")
1018
+ """
1019
+ if client is None:
1020
+ raise IntegrationError("Case management client not provided")
1021
+
1022
+ if not hasattr(client, "add_case_timeline_event"):
1023
+ raise IntegrationError("Case management client does not support timeline events")
1024
+
1025
+ try:
1026
+ result = client.add_case_timeline_event(
1027
+ case_id=case_id,
1028
+ title=title,
1029
+ content=content,
1030
+ source=source,
1031
+ category_id=category_id,
1032
+ tags=tags,
1033
+ color=color,
1034
+ event_date=event_date,
1035
+ include_in_summary=include_in_summary,
1036
+ include_in_graph=include_in_graph,
1037
+ sync_iocs_assets=sync_iocs_assets,
1038
+ asset_ids=asset_ids,
1039
+ ioc_ids=ioc_ids,
1040
+ custom_attributes=custom_attributes,
1041
+ raw=raw,
1042
+ tz=tz,
1043
+ )
1044
+
1045
+ return {
1046
+ "success": True,
1047
+ "case_id": case_id,
1048
+ "event": result,
1049
+ "message": f"Timeline event '{title}' added to case {case_id}",
1050
+ }
1051
+ except Exception as e:
1052
+ raise IntegrationError(f"Failed to add timeline event to case {case_id}: {str(e)}") from e
1053
+
1054
+
1055
+ def list_case_timeline_events(
1056
+ case_id: str,
1057
+ client: CaseManagementClient = None, # type: ignore
1058
+ ) -> Dict[str, Any]:
1059
+ """
1060
+ List timeline events for a case.
1061
+
1062
+ Tool schema:
1063
+ - name: list_case_timeline_events
1064
+ - description: List all timeline events associated with a case
1065
+ - parameters:
1066
+ - case_id (str, required): The ID of the case
1067
+ """
1068
+ if client is None:
1069
+ raise IntegrationError("Case management client not provided")
1070
+
1071
+ if not hasattr(client, "list_case_timeline_events"):
1072
+ raise IntegrationError("Case management client does not support timeline events")
1073
+
1074
+ try:
1075
+ events = client.list_case_timeline_events(case_id)
1076
+
1077
+ return {
1078
+ "success": True,
1079
+ "case_id": case_id,
1080
+ "count": len(events),
1081
+ "events": events,
1082
+ }
1083
+ except Exception as e:
1084
+ raise IntegrationError(f"Failed to list timeline events for case {case_id}: {str(e)}") from e
1085
+