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,378 @@
1
+ """
2
+ Orchestrator tools for Engineering integrations (Trello, ClickUp, GitHub).
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Dict, Any, Optional, Union
8
+
9
+ from ..integrations.eng.trello.trello_client import TrelloClient
10
+ from ..integrations.eng.clickup.clickup_client import ClickUpClient
11
+ from ..integrations.eng.github.github_client import GitHubClient
12
+
13
+
14
+ def create_fine_tuning_recommendation(
15
+ title: str,
16
+ description: str,
17
+ list_name: Optional[str] = None,
18
+ labels: Optional[list[str]] = None,
19
+ status: Optional[str] = None,
20
+ tags: Optional[list[str]] = None,
21
+ client: Optional[Union[TrelloClient, ClickUpClient, GitHubClient]] = None,
22
+ ) -> Dict[str, Any]:
23
+ """
24
+ Create a fine-tuning recommendation on the fine-tuning board.
25
+
26
+ Supports Trello (cards), ClickUp (tasks), and GitHub (project items).
27
+
28
+ Args:
29
+ title: Task/card title
30
+ description: Task/card description
31
+ list_name: Optional list name (Trello only, defaults to first list on board)
32
+ labels: Optional list of label names (Trello only)
33
+ status: Optional status name (ClickUp only, defaults to first status in list)
34
+ tags: Optional list of tag names (ClickUp only)
35
+ client: TrelloClient, ClickUpClient, or GitHubClient instance (required)
36
+
37
+ Returns:
38
+ Dictionary with task/card/project item information
39
+ """
40
+ if not client:
41
+ raise ValueError("Engineering client (TrelloClient, ClickUpClient, or GitHubClient) is required")
42
+
43
+ if isinstance(client, TrelloClient):
44
+ result = client.create_fine_tuning_recommendation(
45
+ title=title,
46
+ description=description,
47
+ list_name=list_name,
48
+ labels=labels,
49
+ )
50
+ return {
51
+ "success": True,
52
+ "provider": "trello",
53
+ "card": {
54
+ "id": result.get("id"),
55
+ "name": result.get("name"),
56
+ "url": result.get("url"),
57
+ "board_id": result.get("idBoard"),
58
+ },
59
+ }
60
+ elif isinstance(client, ClickUpClient):
61
+ result = client.create_fine_tuning_recommendation(
62
+ title=title,
63
+ description=description,
64
+ status=status,
65
+ tags=tags,
66
+ )
67
+ return {
68
+ "success": True,
69
+ "provider": "clickup",
70
+ "task": {
71
+ "id": result.get("id"),
72
+ "name": result.get("name"),
73
+ "url": result.get("url"),
74
+ "list_id": result.get("list", {}).get("id") if isinstance(result.get("list"), dict) else None,
75
+ },
76
+ }
77
+ elif isinstance(client, GitHubClient):
78
+ result = client.create_fine_tuning_recommendation(
79
+ title=title,
80
+ description=description,
81
+ )
82
+ return {
83
+ "success": True,
84
+ "provider": "github",
85
+ "project_item": {
86
+ "id": result.get("id"),
87
+ "note": result.get("note"),
88
+ "url": result.get("url"),
89
+ "project_id": result.get("project_id"),
90
+ },
91
+ }
92
+ else:
93
+ raise ValueError(f"Unsupported client type: {type(client)}")
94
+
95
+
96
+ def create_visibility_recommendation(
97
+ title: str,
98
+ description: str,
99
+ list_name: Optional[str] = None,
100
+ labels: Optional[list[str]] = None,
101
+ status: Optional[str] = None,
102
+ tags: Optional[list[str]] = None,
103
+ client: Optional[Union[TrelloClient, ClickUpClient, GitHubClient]] = None,
104
+ ) -> Dict[str, Any]:
105
+ """
106
+ Create a visibility/engineering recommendation on the engineering board.
107
+
108
+ Supports Trello (cards), ClickUp (tasks), and GitHub (project items).
109
+
110
+ Args:
111
+ title: Task/card title
112
+ description: Task/card description
113
+ list_name: Optional list name (Trello only, defaults to first list on board)
114
+ labels: Optional list of label names (Trello only)
115
+ status: Optional status name (ClickUp only, defaults to first status in list)
116
+ tags: Optional list of tag names (ClickUp only)
117
+ client: TrelloClient, ClickUpClient, or GitHubClient instance (required)
118
+
119
+ Returns:
120
+ Dictionary with task/card/project item information
121
+ """
122
+ if not client:
123
+ raise ValueError("Engineering client (TrelloClient, ClickUpClient, or GitHubClient) is required")
124
+
125
+ if isinstance(client, TrelloClient):
126
+ result = client.create_visibility_recommendation(
127
+ title=title,
128
+ description=description,
129
+ list_name=list_name,
130
+ labels=labels,
131
+ )
132
+ return {
133
+ "success": True,
134
+ "provider": "trello",
135
+ "card": {
136
+ "id": result.get("id"),
137
+ "name": result.get("name"),
138
+ "url": result.get("url"),
139
+ "board_id": result.get("idBoard"),
140
+ },
141
+ }
142
+ elif isinstance(client, ClickUpClient):
143
+ result = client.create_visibility_recommendation(
144
+ title=title,
145
+ description=description,
146
+ status=status,
147
+ tags=tags,
148
+ )
149
+ return {
150
+ "success": True,
151
+ "provider": "clickup",
152
+ "task": {
153
+ "id": result.get("id"),
154
+ "name": result.get("name"),
155
+ "url": result.get("url"),
156
+ "list_id": result.get("list", {}).get("id") if isinstance(result.get("list"), dict) else None,
157
+ },
158
+ }
159
+ elif isinstance(client, GitHubClient):
160
+ result = client.create_visibility_recommendation(
161
+ title=title,
162
+ description=description,
163
+ )
164
+ return {
165
+ "success": True,
166
+ "provider": "github",
167
+ "project_item": {
168
+ "id": result.get("id"),
169
+ "note": result.get("note"),
170
+ "url": result.get("url"),
171
+ "project_id": result.get("project_id"),
172
+ },
173
+ }
174
+ else:
175
+ raise ValueError(f"Unsupported client type: {type(client)}")
176
+
177
+
178
+
179
+ def list_fine_tuning_recommendations(
180
+ archived: bool = False,
181
+ include_closed: bool = True,
182
+ order_by: Optional[str] = None,
183
+ reverse: bool = False,
184
+ subtasks: bool = False,
185
+ statuses: Optional[list[str]] = None,
186
+ include_markdown_description: bool = False,
187
+ client: Optional[Union[TrelloClient, ClickUpClient, GitHubClient]] = None,
188
+ ) -> Dict[str, Any]:
189
+ """
190
+ List all fine-tuning recommendations.
191
+
192
+ Currently only supports ClickUp. Trello and GitHub support can be added later.
193
+
194
+ Args:
195
+ archived: Include archived tasks (ClickUp only, default: False)
196
+ include_closed: Include closed tasks (ClickUp only, default: True)
197
+ order_by: Order tasks by field (ClickUp only)
198
+ reverse: Reverse the order (ClickUp only, default: False)
199
+ subtasks: Include subtasks (ClickUp only, default: False)
200
+ statuses: Filter by status names (ClickUp only)
201
+ include_markdown_description: Include markdown in descriptions (ClickUp only, default: False)
202
+ client: TrelloClient, ClickUpClient, or GitHubClient instance (required)
203
+
204
+ Returns:
205
+ Dictionary with list of recommendations
206
+ """
207
+ if not client:
208
+ raise ValueError("Engineering client (TrelloClient, ClickUpClient, or GitHubClient) is required")
209
+
210
+ if isinstance(client, ClickUpClient):
211
+ tasks = client.list_fine_tuning_recommendations(
212
+ archived=archived,
213
+ include_closed=include_closed,
214
+ order_by=order_by,
215
+ reverse=reverse,
216
+ subtasks=subtasks,
217
+ statuses=statuses,
218
+ include_markdown_description=include_markdown_description,
219
+ )
220
+ return {
221
+ "success": True,
222
+ "provider": "clickup",
223
+ "count": len(tasks),
224
+ "tasks": [
225
+ {
226
+ "id": task.get("id"),
227
+ "name": task.get("name"),
228
+ "url": task.get("url"),
229
+ "status": task.get("status", {}).get("status") if isinstance(task.get("status"), dict) else None,
230
+ "description": task.get("description", ""),
231
+ }
232
+ for task in tasks
233
+ ],
234
+ }
235
+ else:
236
+ raise ValueError(f"list_fine_tuning_recommendations is not supported for client type: {type(client)}")
237
+
238
+
239
+ def list_visibility_recommendations(
240
+ archived: bool = False,
241
+ include_closed: bool = True,
242
+ order_by: Optional[str] = None,
243
+ reverse: bool = False,
244
+ subtasks: bool = False,
245
+ statuses: Optional[list[str]] = None,
246
+ include_markdown_description: bool = False,
247
+ client: Optional[Union[TrelloClient, ClickUpClient, GitHubClient]] = None,
248
+ ) -> Dict[str, Any]:
249
+ """
250
+ List all visibility/engineering recommendations.
251
+
252
+ Currently only supports ClickUp. Trello and GitHub support can be added later.
253
+
254
+ Args:
255
+ archived: Include archived tasks (ClickUp only, default: False)
256
+ include_closed: Include closed tasks (ClickUp only, default: True)
257
+ order_by: Order tasks by field (ClickUp only)
258
+ reverse: Reverse the order (ClickUp only, default: False)
259
+ subtasks: Include subtasks (ClickUp only, default: False)
260
+ statuses: Filter by status names (ClickUp only)
261
+ include_markdown_description: Include markdown in descriptions (ClickUp only, default: False)
262
+ client: TrelloClient, ClickUpClient, or GitHubClient instance (required)
263
+
264
+ Returns:
265
+ Dictionary with list of recommendations
266
+ """
267
+ if not client:
268
+ raise ValueError("Engineering client (TrelloClient, ClickUpClient, or GitHubClient) is required")
269
+
270
+ if isinstance(client, ClickUpClient):
271
+ tasks = client.list_visibility_recommendations(
272
+ archived=archived,
273
+ include_closed=include_closed,
274
+ order_by=order_by,
275
+ reverse=reverse,
276
+ subtasks=subtasks,
277
+ statuses=statuses,
278
+ include_markdown_description=include_markdown_description,
279
+ )
280
+ return {
281
+ "success": True,
282
+ "provider": "clickup",
283
+ "count": len(tasks),
284
+ "tasks": [
285
+ {
286
+ "id": task.get("id"),
287
+ "name": task.get("name"),
288
+ "url": task.get("url"),
289
+ "status": task.get("status", {}).get("status") if isinstance(task.get("status"), dict) else None,
290
+ "description": task.get("description", ""),
291
+ }
292
+ for task in tasks
293
+ ],
294
+ }
295
+ else:
296
+ raise ValueError(f"list_visibility_recommendations is not supported for client type: {type(client)}")
297
+
298
+
299
+ def add_comment_to_fine_tuning_recommendation(
300
+ task_id: str,
301
+ comment_text: str,
302
+ client: Optional[Union[TrelloClient, ClickUpClient, GitHubClient]] = None,
303
+ ) -> Dict[str, Any]:
304
+ """
305
+ Add a comment to a fine-tuning recommendation task.
306
+
307
+ Currently only supports ClickUp. Trello and GitHub support can be added later.
308
+
309
+ Args:
310
+ task_id: Task ID
311
+ comment_text: Comment text/content
312
+ client: TrelloClient, ClickUpClient, or GitHubClient instance (required)
313
+
314
+ Returns:
315
+ Dictionary with comment information
316
+ """
317
+ if not client:
318
+ raise ValueError("Engineering client (TrelloClient, ClickUpClient, or GitHubClient) is required")
319
+
320
+ if isinstance(client, ClickUpClient):
321
+ comment = client.add_comment_to_fine_tuning_recommendation(
322
+ task_id=task_id,
323
+ comment_text=comment_text,
324
+ )
325
+ return {
326
+ "success": True,
327
+ "provider": "clickup",
328
+ "comment": {
329
+ "id": comment.get("id"),
330
+ "comment_text": comment.get("comment", [{}])[0].get("text") if isinstance(comment.get("comment"), list) and comment.get("comment") else comment.get("comment_text", ""),
331
+ "user": comment.get("user", {}).get("username") if isinstance(comment.get("user"), dict) else None,
332
+ },
333
+ "task_id": task_id,
334
+ "message": f"Comment added to fine-tuning recommendation task {task_id}",
335
+ }
336
+ else:
337
+ raise ValueError(f"add_comment_to_fine_tuning_recommendation is not supported for client type: {type(client)}")
338
+
339
+
340
+ def add_comment_to_visibility_recommendation(
341
+ task_id: str,
342
+ comment_text: str,
343
+ client: Optional[Union[TrelloClient, ClickUpClient, GitHubClient]] = None,
344
+ ) -> Dict[str, Any]:
345
+ """
346
+ Add a comment to a visibility/engineering recommendation task.
347
+
348
+ Currently only supports ClickUp. Trello and GitHub support can be added later.
349
+
350
+ Args:
351
+ task_id: Task ID
352
+ comment_text: Comment text/content
353
+ client: TrelloClient, ClickUpClient, or GitHubClient instance (required)
354
+
355
+ Returns:
356
+ Dictionary with comment information
357
+ """
358
+ if not client:
359
+ raise ValueError("Engineering client (TrelloClient, ClickUpClient, or GitHubClient) is required")
360
+
361
+ if isinstance(client, ClickUpClient):
362
+ comment = client.add_comment_to_visibility_recommendation(
363
+ task_id=task_id,
364
+ comment_text=comment_text,
365
+ )
366
+ return {
367
+ "success": True,
368
+ "provider": "clickup",
369
+ "comment": {
370
+ "id": comment.get("id"),
371
+ "comment_text": comment.get("comment", [{}])[0].get("text") if isinstance(comment.get("comment"), list) and comment.get("comment") else comment.get("comment_text", ""),
372
+ "user": comment.get("user", {}).get("username") if isinstance(comment.get("user"), dict) else None,
373
+ },
374
+ "task_id": task_id,
375
+ "message": f"Comment added to visibility recommendation task {task_id}",
376
+ }
377
+ else:
378
+ raise ValueError(f"add_comment_to_visibility_recommendation is not supported for client type: {type(client)}")
@@ -0,0 +1,156 @@
1
+ """
2
+ LLM-callable tools for client infrastructure knowledge base (KB) operations.
3
+
4
+ These functions wrap the KBClient interface and provide LLM-friendly error
5
+ handling and return values, reading from ``client_env/*`` on the filesystem.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any, Dict, Optional
11
+
12
+ from ..api.kb import KBClient
13
+ from ..core.errors import IntegrationError
14
+
15
+
16
+ def list_kb_clients(
17
+ client: KBClient = None, # type: ignore
18
+ ) -> Dict[str, Any]:
19
+ """
20
+ List available client environments.
21
+
22
+ Tool schema:
23
+ - name: kb_list_clients
24
+ - description: List available client environments based on folders under client_env/*.
25
+ - parameters: none
26
+
27
+ Args:
28
+ client: The KB client.
29
+
30
+ Returns:
31
+ Dictionary containing list of clients.
32
+
33
+ Raises:
34
+ IntegrationError: If KB client is not provided.
35
+ """
36
+ if client is None:
37
+ raise IntegrationError("KB client not provided")
38
+
39
+ try:
40
+ clients = client.list_clients()
41
+ return {
42
+ "success": True,
43
+ "count": len(clients),
44
+ "clients": clients,
45
+ }
46
+ except Exception as e:
47
+ raise IntegrationError(f"Failed to list KB clients: {str(e)}") from e
48
+
49
+
50
+ def get_client_infra(
51
+ client_name: str,
52
+ client: KBClient = None, # type: ignore
53
+ ) -> Dict[str, Any]:
54
+ """
55
+ Get an aggregated view of a client's infrastructure.
56
+
57
+ Tool schema:
58
+ - name: kb_get_client_infra
59
+ - description: Load and summarize client infrastructure (subnets, servers, users, naming schemas, env rules) from client_env/*.
60
+ - parameters:
61
+ - client_name (str, required): Name of the client environment (e.g., "acme_corp_client" or "acme_corp").
62
+
63
+ Args:
64
+ client_name: Client identifier.
65
+ client: The KB client.
66
+
67
+ Returns:
68
+ Dictionary with normalized infrastructure data and a concise summary.
69
+
70
+ Raises:
71
+ IntegrationError: If KB client is not provided or loading fails.
72
+ """
73
+ if client is None:
74
+ raise IntegrationError("KB client not provided")
75
+
76
+ try:
77
+ infra = client.get_client_infra(client_name)
78
+
79
+ return {
80
+ "success": True,
81
+ "client_name": infra.client_name,
82
+ "summary": infra.summary,
83
+ "subnets": [
84
+ {
85
+ "name": s.name,
86
+ "cidr": s.cidr,
87
+ "network_type": s.network_type,
88
+ "access_method": s.access_method,
89
+ "description": s.description,
90
+ "tags": s.tags or [],
91
+ }
92
+ for s in infra.subnets
93
+ ],
94
+ "servers": [
95
+ {
96
+ "hostname": srv.hostname,
97
+ "ip_address": srv.ip_address,
98
+ "role": srv.role,
99
+ "environment": srv.environment,
100
+ "os": srv.os,
101
+ "description": srv.description,
102
+ "criticality": srv.criticality,
103
+ "tags": srv.tags or [],
104
+ }
105
+ for srv in infra.servers
106
+ ],
107
+ "users": [
108
+ {
109
+ "username": u.username,
110
+ "display_name": u.display_name,
111
+ "account_type": u.account_type,
112
+ "department": u.department,
113
+ "privilege_level": u.privilege_level,
114
+ "description": u.description,
115
+ "tags": u.tags or [],
116
+ }
117
+ for u in infra.users
118
+ ],
119
+ "device_schemas": [
120
+ {
121
+ "pattern": d.pattern,
122
+ "pattern_style": d.pattern_style,
123
+ "device_type": d.device_type,
124
+ "example": d.example,
125
+ "description": d.description,
126
+ "tags": d.tags or [],
127
+ }
128
+ for d in infra.device_schemas
129
+ ],
130
+ "user_schemas": [
131
+ {
132
+ "pattern": us.pattern,
133
+ "pattern_style": us.pattern_style,
134
+ "user_type": us.user_type,
135
+ "example": us.example,
136
+ "description": us.description,
137
+ "tags": us.tags or [],
138
+ }
139
+ for us in infra.user_schemas
140
+ ],
141
+ "env_rules": {
142
+ "version": infra.env_rules.version if infra.env_rules else None,
143
+ "environment_types": infra.env_rules.environment_types if infra.env_rules else None,
144
+ "network_classification": infra.env_rules.network_classification if infra.env_rules else None,
145
+ "user_categories": infra.env_rules.user_categories if infra.env_rules else None,
146
+ "general_rules": infra.env_rules.general_rules if infra.env_rules else None,
147
+ }
148
+ if infra.env_rules
149
+ else None,
150
+ }
151
+ except Exception as e:
152
+ raise IntegrationError(
153
+ f"Failed to load infrastructure for client '{client_name}': {str(e)}"
154
+ ) from e
155
+
156
+