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,169 @@
1
+ """
2
+ GitHub client for creating project items and recommendations.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Dict, Any, Optional
8
+
9
+ from ....core.config import SamiConfig
10
+ from ....core.errors import IntegrationError
11
+ from ....core.logging import get_logger
12
+ from .github_http import GitHubHttpClient
13
+
14
+
15
+ logger = get_logger("sami.integrations.github.client")
16
+
17
+
18
+ class GitHubClient:
19
+ """
20
+ Client for interacting with GitHub Projects API to create project items and recommendations.
21
+ """
22
+
23
+ def __init__(
24
+ self,
25
+ http_client: GitHubHttpClient,
26
+ fine_tuning_project_id: str,
27
+ engineering_project_id: str,
28
+ ) -> None:
29
+ """
30
+ Initialize GitHub client.
31
+
32
+ Args:
33
+ http_client: HTTP client for GitHub API
34
+ fine_tuning_project_id: GitHub project ID for fine-tuning recommendations
35
+ engineering_project_id: GitHub project ID for engineering/visibility recommendations
36
+ """
37
+ self._http = http_client
38
+ self.fine_tuning_project_id = fine_tuning_project_id
39
+ self.engineering_project_id = engineering_project_id
40
+
41
+ @classmethod
42
+ def from_config(cls, config: SamiConfig) -> "GitHubClient":
43
+ """
44
+ Factory to construct a client from ``SamiConfig``.
45
+
46
+ Args:
47
+ config: SamiConfig instance with GitHub configuration
48
+
49
+ Returns:
50
+ GitHubClient instance
51
+
52
+ Raises:
53
+ IntegrationError: If GitHub configuration is not set
54
+ """
55
+ if not config.eng or not config.eng.github:
56
+ raise IntegrationError("GitHub configuration is not set in SamiConfig")
57
+
58
+ github_config = config.eng.github
59
+ http_client = GitHubHttpClient(
60
+ api_token=github_config.api_token,
61
+ timeout_seconds=github_config.timeout_seconds,
62
+ verify_ssl=github_config.verify_ssl,
63
+ )
64
+
65
+ return cls(
66
+ http_client=http_client,
67
+ fine_tuning_project_id=github_config.fine_tuning_project_id,
68
+ engineering_project_id=github_config.engineering_project_id,
69
+ )
70
+
71
+ def create_fine_tuning_recommendation(
72
+ self,
73
+ title: str,
74
+ description: str,
75
+ content_id: Optional[str] = None,
76
+ content_type: str = "DraftIssue",
77
+ ) -> Dict[str, Any]:
78
+ """
79
+ Create a fine-tuning recommendation project item in GitHub.
80
+
81
+ Args:
82
+ title: Item title
83
+ description: Item description/body
84
+ content_id: Optional content ID (for linking to issues/PRs)
85
+ content_type: Content type (DraftIssue, Issue, PullRequest). Default: DraftIssue
86
+
87
+ Returns:
88
+ Dictionary with project item information
89
+ """
90
+ logger.info(f"Creating fine-tuning recommendation: {title}")
91
+
92
+ # GitHub Projects API v1 (REST) - Note: v1 is deprecated but still functional
93
+ # We use REST API v1 for simplicity: POST /projects/columns/{column_id}/cards
94
+
95
+ # Get the project columns
96
+ columns = self._http.get(f"/projects/{self.fine_tuning_project_id}/columns")
97
+
98
+ if not columns:
99
+ raise IntegrationError("No columns found in fine-tuning project")
100
+
101
+ # Use the first column
102
+ column_id = columns[0].get("id")
103
+
104
+ # Create a card/item in the column
105
+ # For draft issues, we use note parameter
106
+ card_data = {
107
+ "note": f"## {title}\n\n{description}",
108
+ }
109
+
110
+ card = self._http.post(f"/projects/columns/{column_id}/cards", json_data=card_data)
111
+
112
+ logger.info(f"Created fine-tuning recommendation project item: {card.get('id')}")
113
+ return card
114
+
115
+ def create_visibility_recommendation(
116
+ self,
117
+ title: str,
118
+ description: str,
119
+ content_id: Optional[str] = None,
120
+ content_type: str = "DraftIssue",
121
+ ) -> Dict[str, Any]:
122
+ """
123
+ Create a visibility/engineering recommendation project item in GitHub.
124
+
125
+ Args:
126
+ title: Item title
127
+ description: Item description/body
128
+ content_id: Optional content ID (for linking to issues/PRs)
129
+ content_type: Content type (DraftIssue, Issue, PullRequest). Default: DraftIssue
130
+
131
+ Returns:
132
+ Dictionary with project item information
133
+ """
134
+ logger.info(f"Creating visibility recommendation: {title}")
135
+
136
+ # Get the project columns
137
+ columns = self._http.get(f"/projects/{self.engineering_project_id}/columns")
138
+
139
+ if not columns:
140
+ raise IntegrationError("No columns found in engineering project")
141
+
142
+ # Use the first column
143
+ column_id = columns[0].get("id")
144
+
145
+ # Create a card/item in the column
146
+ card_data = {
147
+ "note": f"## {title}\n\n{description}",
148
+ }
149
+
150
+ card = self._http.post(f"/projects/columns/{column_id}/cards", json_data=card_data)
151
+
152
+ logger.info(f"Created visibility recommendation project item: {card.get('id')}")
153
+ return card
154
+
155
+ def ping(self) -> bool:
156
+ """
157
+ Check if GitHub API is reachable.
158
+
159
+ Returns:
160
+ True if API is reachable, False otherwise
161
+ """
162
+ try:
163
+ # Try to get authenticated user
164
+ self._http.get("/user")
165
+ return True
166
+ except IntegrationError:
167
+ logger.exception("GitHub ping failed")
168
+ return False
169
+
@@ -0,0 +1,158 @@
1
+ """
2
+ Low-level HTTP client for GitHub API.
3
+
4
+ This module is responsible for:
5
+ - authentication (Personal Access Token)
6
+ - building URLs
7
+ - making HTTP requests
8
+ - basic error handling
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ from dataclasses import dataclass
15
+ from typing import Any, Dict, Optional
16
+
17
+ import requests
18
+
19
+ from ....core.errors import IntegrationError
20
+ from ....core.logging import get_logger
21
+
22
+
23
+ logger = get_logger("sami.integrations.github.http")
24
+
25
+
26
+ @dataclass
27
+ class GitHubHttpClient:
28
+ """
29
+ Simple HTTP client for GitHub's REST API.
30
+
31
+ GitHub API documentation: https://docs.github.com/en/rest
32
+ """
33
+
34
+ api_token: str
35
+ timeout_seconds: int = 30
36
+ verify_ssl: bool = True
37
+
38
+ def _headers(self) -> Dict[str, str]:
39
+ """Get authentication headers."""
40
+ return {
41
+ "Authorization": f"Bearer {self.api_token}",
42
+ "Accept": "application/vnd.github+json",
43
+ "X-GitHub-Api-Version": "2022-11-28",
44
+ }
45
+
46
+ def _build_url(self, endpoint: str) -> str:
47
+ """
48
+ Build a full URL from an endpoint.
49
+
50
+ Args:
51
+ endpoint: API endpoint path (e.g., "/repos/{owner}/{repo}/projects")
52
+
53
+ Returns:
54
+ Full URL string
55
+ """
56
+ base_url = "https://api.github.com"
57
+ endpoint = endpoint.lstrip("/")
58
+ return f"{base_url}/{endpoint}"
59
+
60
+ def _handle_github_error(self, response: requests.Response) -> None:
61
+ """
62
+ Raise IntegrationError if the response indicates an error.
63
+
64
+ Args:
65
+ response: HTTP response object
66
+
67
+ Raises:
68
+ IntegrationError: If the response indicates an error
69
+ """
70
+ if response.status_code < 400:
71
+ return
72
+
73
+ try:
74
+ error_data = response.json()
75
+ message = error_data.get("message", f"HTTP {response.status_code}")
76
+ errors = error_data.get("errors", [])
77
+ if errors:
78
+ error_details = "; ".join([str(e) for e in errors])
79
+ message = f"{message}: {error_details}"
80
+ except Exception:
81
+ message = f"HTTP {response.status_code}: {response.text[:200]}"
82
+
83
+ raise IntegrationError(f"GitHub API error: {message}")
84
+
85
+ def request(
86
+ self,
87
+ method: str,
88
+ endpoint: str,
89
+ json_data: Optional[Dict[str, Any]] = None,
90
+ params: Optional[Dict[str, Any]] = None,
91
+ ) -> Dict[str, Any]:
92
+ """
93
+ Make an HTTP request to GitHub API.
94
+
95
+ Args:
96
+ method: HTTP method (GET, POST, PUT, PATCH, DELETE)
97
+ endpoint: API endpoint path
98
+ json_data: JSON payload (for POST, PUT, PATCH)
99
+ params: Query parameters
100
+
101
+ Returns:
102
+ Response JSON as dictionary
103
+
104
+ Raises:
105
+ IntegrationError: If the request fails
106
+ """
107
+ url = self._build_url(endpoint)
108
+ headers = self._headers()
109
+
110
+ try:
111
+ logger.debug(f"GitHub {method} {url}")
112
+ if params:
113
+ logger.debug(f" Query params: {params}")
114
+ if json_data:
115
+ logger.debug(f" JSON payload: {json.dumps(json_data)[:200]}...")
116
+
117
+ response = requests.request(
118
+ method=method,
119
+ url=url,
120
+ headers=headers,
121
+ json=json_data,
122
+ params=params,
123
+ timeout=self.timeout_seconds,
124
+ verify=self.verify_ssl,
125
+ )
126
+
127
+ logger.debug(f"GitHub response status: {response.status_code}")
128
+ if response.status_code >= 400:
129
+ logger.error(f"GitHub API error - Status: {response.status_code}, URL: {url}, Response: {response.text[:500]}")
130
+
131
+ self._handle_github_error(response)
132
+
133
+ if response.status_code == 204: # No Content
134
+ return {}
135
+
136
+ return response.json()
137
+
138
+ except requests.exceptions.Timeout as e:
139
+ raise IntegrationError(f"GitHub API request timeout: {e}") from e
140
+ except requests.exceptions.RequestException as e:
141
+ raise IntegrationError(f"GitHub API request failed: {e}") from e
142
+
143
+ def get(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
144
+ """GET request."""
145
+ return self.request("GET", endpoint, params=params)
146
+
147
+ def post(self, endpoint: str, json_data: Optional[Dict[str, Any]] = None, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
148
+ """POST request."""
149
+ return self.request("POST", endpoint, json_data=json_data, params=params)
150
+
151
+ def put(self, endpoint: str, json_data: Optional[Dict[str, Any]] = None, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
152
+ """PUT request."""
153
+ return self.request("PUT", endpoint, json_data=json_data, params=params)
154
+
155
+ def delete(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
156
+ """DELETE request."""
157
+ return self.request("DELETE", endpoint, params=params)
158
+
@@ -0,0 +1,8 @@
1
+ """
2
+ Trello integration for engineering task management.
3
+ """
4
+
5
+ from .trello_client import TrelloClient
6
+
7
+ __all__ = ["TrelloClient"]
8
+
@@ -0,0 +1,207 @@
1
+ """
2
+ Trello client for creating tasks and recommendations.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Dict, Any, Optional
8
+
9
+ from ....core.config import SamiConfig
10
+ from ....core.errors import IntegrationError
11
+ from ....core.logging import get_logger
12
+ from .trello_http import TrelloHttpClient
13
+
14
+
15
+ logger = get_logger("sami.integrations.trello.client")
16
+
17
+
18
+ class TrelloClient:
19
+ """
20
+ Client for interacting with Trello API to create tasks and recommendations.
21
+ """
22
+
23
+ def __init__(self, http_client: TrelloHttpClient, fine_tuning_board_id: str, engineering_board_id: str) -> None:
24
+ """
25
+ Initialize Trello client.
26
+
27
+ Args:
28
+ http_client: HTTP client for Trello API
29
+ fine_tuning_board_id: Trello board ID for fine-tuning recommendations
30
+ engineering_board_id: Trello board ID for engineering/visibility recommendations
31
+ """
32
+ self._http = http_client
33
+ self.fine_tuning_board_id = fine_tuning_board_id
34
+ self.engineering_board_id = engineering_board_id
35
+
36
+ @classmethod
37
+ def from_config(cls, config: SamiConfig) -> "TrelloClient":
38
+ """
39
+ Factory to construct a client from ``SamiConfig``.
40
+
41
+ Args:
42
+ config: SamiConfig instance with Trello configuration
43
+
44
+ Returns:
45
+ TrelloClient instance
46
+
47
+ Raises:
48
+ IntegrationError: If Trello configuration is not set
49
+ """
50
+ if not config.eng or not config.eng.trello:
51
+ raise IntegrationError("Trello configuration is not set in SamiConfig")
52
+
53
+ trello_config = config.eng.trello
54
+ http_client = TrelloHttpClient(
55
+ api_key=trello_config.api_key,
56
+ api_token=trello_config.api_token,
57
+ timeout_seconds=trello_config.timeout_seconds,
58
+ verify_ssl=trello_config.verify_ssl,
59
+ )
60
+
61
+ return cls(
62
+ http_client=http_client,
63
+ fine_tuning_board_id=trello_config.fine_tuning_board_id,
64
+ engineering_board_id=trello_config.engineering_board_id,
65
+ )
66
+
67
+ def create_fine_tuning_recommendation(
68
+ self,
69
+ title: str,
70
+ description: str,
71
+ list_name: Optional[str] = None,
72
+ labels: Optional[list[str]] = None,
73
+ ) -> Dict[str, Any]:
74
+ """
75
+ Create a fine-tuning recommendation card on the fine-tuning board.
76
+
77
+ Args:
78
+ title: Card title
79
+ description: Card description
80
+ list_name: Optional list name (defaults to first list on board)
81
+ labels: Optional list of label names to add to the card
82
+
83
+ Returns:
84
+ Dictionary with card information
85
+ """
86
+ logger.info(f"Creating fine-tuning recommendation: {title}")
87
+
88
+ # Get lists on the board
89
+ lists = self._http.get(f"/1/boards/{self.fine_tuning_board_id}/lists")
90
+
91
+ # Find the target list
92
+ target_list_id = None
93
+ if list_name:
94
+ for list_item in lists:
95
+ if list_item.get("name") == list_name:
96
+ target_list_id = list_item.get("id")
97
+ break
98
+ if not target_list_id:
99
+ raise IntegrationError(f"List '{list_name}' not found on fine-tuning board")
100
+ else:
101
+ # Use the first list if no list_name specified
102
+ if not lists:
103
+ raise IntegrationError("No lists found on fine-tuning board")
104
+ target_list_id = lists[0].get("id")
105
+
106
+ # Create the card
107
+ card_data = {
108
+ "name": title,
109
+ "desc": description,
110
+ "idList": target_list_id,
111
+ }
112
+
113
+ card = self._http.post("/1/cards", json_data=card_data)
114
+
115
+ # Add labels if provided
116
+ if labels:
117
+ board_labels = self._http.get(f"/1/boards/{self.fine_tuning_board_id}/labels")
118
+ label_map = {label.get("name"): label.get("id") for label in board_labels if label.get("name")}
119
+
120
+ for label_name in labels:
121
+ if label_name in label_map:
122
+ # Trello API uses POST /1/cards/{id}/idLabels with value parameter
123
+ self._http.post(f"/1/cards/{card['id']}/idLabels", params={"value": label_map[label_name]})
124
+ else:
125
+ logger.warning(f"Label '{label_name}' not found on board, skipping")
126
+
127
+ logger.info(f"Created fine-tuning recommendation card: {card.get('id')}")
128
+ return card
129
+
130
+ def create_visibility_recommendation(
131
+ self,
132
+ title: str,
133
+ description: str,
134
+ list_name: Optional[str] = None,
135
+ labels: Optional[list[str]] = None,
136
+ ) -> Dict[str, Any]:
137
+ """
138
+ Create a visibility/engineering recommendation card on the engineering board.
139
+
140
+ Args:
141
+ title: Card title
142
+ description: Card description
143
+ list_name: Optional list name (defaults to first list on board)
144
+ labels: Optional list of label names to add to the card
145
+
146
+ Returns:
147
+ Dictionary with card information
148
+ """
149
+ logger.info(f"Creating visibility recommendation: {title}")
150
+
151
+ # Get lists on the board
152
+ lists = self._http.get(f"/1/boards/{self.engineering_board_id}/lists")
153
+
154
+ # Find the target list
155
+ target_list_id = None
156
+ if list_name:
157
+ for list_item in lists:
158
+ if list_item.get("name") == list_name:
159
+ target_list_id = list_item.get("id")
160
+ break
161
+ if not target_list_id:
162
+ raise IntegrationError(f"List '{list_name}' not found on engineering board")
163
+ else:
164
+ # Use the first list if no list_name specified
165
+ if not lists:
166
+ raise IntegrationError("No lists found on engineering board")
167
+ target_list_id = lists[0].get("id")
168
+
169
+ # Create the card
170
+ card_data = {
171
+ "name": title,
172
+ "desc": description,
173
+ "idList": target_list_id,
174
+ }
175
+
176
+ card = self._http.post("/1/cards", json_data=card_data)
177
+
178
+ # Add labels if provided
179
+ if labels:
180
+ board_labels = self._http.get(f"/1/boards/{self.engineering_board_id}/labels")
181
+ label_map = {label.get("name"): label.get("id") for label in board_labels if label.get("name")}
182
+
183
+ for label_name in labels:
184
+ if label_name in label_map:
185
+ # Trello API uses POST /1/cards/{id}/idLabels with value parameter
186
+ self._http.post(f"/1/cards/{card['id']}/idLabels", params={"value": label_map[label_name]})
187
+ else:
188
+ logger.warning(f"Label '{label_name}' not found on board, skipping")
189
+
190
+ logger.info(f"Created visibility recommendation card: {card.get('id')}")
191
+ return card
192
+
193
+ def ping(self) -> bool:
194
+ """
195
+ Check if Trello API is reachable.
196
+
197
+ Returns:
198
+ True if API is reachable, False otherwise
199
+ """
200
+ try:
201
+ # Try to get boards for the authenticated user
202
+ self._http.get("/1/members/me/boards", params={"limit": 1})
203
+ return True
204
+ except IntegrationError:
205
+ logger.exception("Trello ping failed")
206
+ return False
207
+