suprema-biostar-mcp 1.0.1__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 (61) hide show
  1. biostar_x_mcp_server/__init__.py +25 -0
  2. biostar_x_mcp_server/__main__.py +15 -0
  3. biostar_x_mcp_server/config.py +87 -0
  4. biostar_x_mcp_server/handlers/__init__.py +35 -0
  5. biostar_x_mcp_server/handlers/access_handler.py +2162 -0
  6. biostar_x_mcp_server/handlers/audit_handler.py +489 -0
  7. biostar_x_mcp_server/handlers/auth_handler.py +216 -0
  8. biostar_x_mcp_server/handlers/base_handler.py +228 -0
  9. biostar_x_mcp_server/handlers/card_handler.py +746 -0
  10. biostar_x_mcp_server/handlers/device_handler.py +4344 -0
  11. biostar_x_mcp_server/handlers/door_handler.py +3969 -0
  12. biostar_x_mcp_server/handlers/event_handler.py +1331 -0
  13. biostar_x_mcp_server/handlers/file_handler.py +212 -0
  14. biostar_x_mcp_server/handlers/help_web_handler.py +379 -0
  15. biostar_x_mcp_server/handlers/log_handler.py +1051 -0
  16. biostar_x_mcp_server/handlers/navigation_handler.py +109 -0
  17. biostar_x_mcp_server/handlers/occupancy_handler.py +541 -0
  18. biostar_x_mcp_server/handlers/user_handler.py +3568 -0
  19. biostar_x_mcp_server/schemas/__init__.py +21 -0
  20. biostar_x_mcp_server/schemas/access.py +158 -0
  21. biostar_x_mcp_server/schemas/audit.py +73 -0
  22. biostar_x_mcp_server/schemas/auth.py +24 -0
  23. biostar_x_mcp_server/schemas/cards.py +128 -0
  24. biostar_x_mcp_server/schemas/devices.py +496 -0
  25. biostar_x_mcp_server/schemas/doors.py +306 -0
  26. biostar_x_mcp_server/schemas/events.py +104 -0
  27. biostar_x_mcp_server/schemas/files.py +7 -0
  28. biostar_x_mcp_server/schemas/help.py +29 -0
  29. biostar_x_mcp_server/schemas/logs.py +33 -0
  30. biostar_x_mcp_server/schemas/occupancy.py +19 -0
  31. biostar_x_mcp_server/schemas/tool_response.py +29 -0
  32. biostar_x_mcp_server/schemas/users.py +166 -0
  33. biostar_x_mcp_server/server.py +335 -0
  34. biostar_x_mcp_server/session.py +221 -0
  35. biostar_x_mcp_server/tool_manager.py +172 -0
  36. biostar_x_mcp_server/tools/__init__.py +45 -0
  37. biostar_x_mcp_server/tools/access.py +510 -0
  38. biostar_x_mcp_server/tools/audit.py +227 -0
  39. biostar_x_mcp_server/tools/auth.py +59 -0
  40. biostar_x_mcp_server/tools/cards.py +269 -0
  41. biostar_x_mcp_server/tools/categories.py +197 -0
  42. biostar_x_mcp_server/tools/devices.py +1552 -0
  43. biostar_x_mcp_server/tools/doors.py +865 -0
  44. biostar_x_mcp_server/tools/events.py +305 -0
  45. biostar_x_mcp_server/tools/files.py +28 -0
  46. biostar_x_mcp_server/tools/help.py +80 -0
  47. biostar_x_mcp_server/tools/logs.py +123 -0
  48. biostar_x_mcp_server/tools/navigation.py +89 -0
  49. biostar_x_mcp_server/tools/occupancy.py +91 -0
  50. biostar_x_mcp_server/tools/users.py +1113 -0
  51. biostar_x_mcp_server/utils/__init__.py +31 -0
  52. biostar_x_mcp_server/utils/category_mapper.py +206 -0
  53. biostar_x_mcp_server/utils/decorators.py +101 -0
  54. biostar_x_mcp_server/utils/language_detector.py +51 -0
  55. biostar_x_mcp_server/utils/search.py +42 -0
  56. biostar_x_mcp_server/utils/timezone.py +122 -0
  57. suprema_biostar_mcp-1.0.1.dist-info/METADATA +163 -0
  58. suprema_biostar_mcp-1.0.1.dist-info/RECORD +61 -0
  59. suprema_biostar_mcp-1.0.1.dist-info/WHEEL +4 -0
  60. suprema_biostar_mcp-1.0.1.dist-info/entry_points.txt +2 -0
  61. suprema_biostar_mcp-1.0.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,510 @@
1
+ from mcp.types import Tool
2
+
3
+ # -----------------------------
4
+ # Access Group management tools
5
+ # -----------------------------
6
+ GET_ACCESS_GROUPS_TOOL = Tool(
7
+ name="get-access-groups",
8
+ description="Retrieve all access groups from the BioStar 2 system",
9
+ inputSchema={
10
+ "type": "object",
11
+ "properties": {},
12
+ "required": []
13
+ }
14
+ )
15
+
16
+ GET_ACCESS_GROUP_TOOL = Tool(
17
+ name="get-access-group",
18
+ description="Get detailed information about a specific access group",
19
+ inputSchema={
20
+ "type": "object",
21
+ "properties": {
22
+ "group_id": {
23
+ "type": "integer",
24
+ "description": "ID of the access group to retrieve"
25
+ }
26
+ },
27
+ "required": ["group_id"]
28
+ }
29
+ )
30
+
31
+ CREATE_ACCESS_GROUP_TOOL = Tool(
32
+ name="create-access-group",
33
+ description="""
34
+ Create a new access group (POST /api/access_groups).
35
+
36
+ USER GROUP BEHAVIOR (NEW):
37
+ - Optional user group inclusion via either:
38
+ * user_group_ids: explicit list of ids (validated), or
39
+ * user_group_search_text: substring search (3-case):
40
+ 0 match -> return all user groups + needs_selection=true,
41
+ 1 match -> proceed with that group,
42
+ 2+ matches -> return candidates + needs_selection=true.
43
+ - If neither is provided, user_groups are not included.
44
+
45
+ STRICT CALLER RULES:
46
+ - Do NOT attempt to set floor levels here (ignored).
47
+ - Do NOT guess users or access levels; list and pick explicitly before calling.
48
+
49
+ PAYLOAD:
50
+ {
51
+ "AccessGroup": {
52
+ "name": "...",
53
+ "description": "...",
54
+ "users": [ {"user_id": "1"}, ... ],
55
+ "user_groups": [ {"id": 1106, "name": "ACE Group"}, ... ],
56
+ "access_levels": [ {"id": "2"}, ... ]
57
+ }
58
+ }
59
+ """,
60
+ inputSchema={
61
+ "type": "object",
62
+ "properties": {
63
+ "name": {
64
+ "type": "string",
65
+ "description": "Access group name"
66
+ },
67
+ "description": {
68
+ "type": "string",
69
+ "description": "Description for the access group",
70
+ "default": ""
71
+ },
72
+ "user_ids": {
73
+ "type": "array",
74
+ "items": {"oneOf": [{"type": "string"}, {"type": "integer"}]},
75
+ "description": "Explicit list of user ids to include. Do NOT auto-select."
76
+ },
77
+ "access_level_ids": {
78
+ "type": "array",
79
+ "items": {"oneOf": [{"type": "string"}, {"type": "integer"}]},
80
+ "description": "Existing access level ids to assign to this group."
81
+ },
82
+ "user_group_ids": {
83
+ "type": "array",
84
+ "items": {"oneOf": [{"type": "integer"}, {"type": "string"}]},
85
+ "description": "Explicit user group ids to include (validated against GET /api/user_groups)"
86
+ },
87
+ "user_group_search_text": {
88
+ "type": "string",
89
+ "description": "Substring to search user group name (3-case resolution)"
90
+ }
91
+ },
92
+ "required": ["name"]
93
+ }
94
+ )
95
+
96
+ UPDATE_ACCESS_GROUP_TOOL = Tool(
97
+ name="update-access-group",
98
+ description="""
99
+ Update an existing access group (PUT /api/access_groups/{group_id}).
100
+
101
+ User groups:
102
+ - Replacement:
103
+ * If "user_group_ids" KEY IS PRESENT (even []), replace with that exact list.
104
+ * "user_group_search_text" (legacy) -> single-match replacement; 0/Many -> needs_selection=true.
105
+ - Delta:
106
+ * add_user_group_ids / add_user_group_search_text
107
+ * remove_user_group_ids / remove_user_group_search_text
108
+ Each search_text uses 3-case logic (0/1/Many) identical to create().
109
+ Users:
110
+ - user_ids -> full replacement of 'users'
111
+ - Or apply delta with new_users / delete_users (and include those arrays in payload)
112
+ Access levels:
113
+ - access_level_ids -> full replacement of 'access_levels'
114
+ Floor levels:
115
+ - Ignored with a warning
116
+ Only provided fields are included in the PUT body; others are preserved server-side.
117
+ """,
118
+ inputSchema={
119
+ "type": "object",
120
+ "properties": {
121
+ "group_id": {"type": "integer", "description": "ID of the access group to update"},
122
+ "name": {"type": "string", "description": "New name"},
123
+ "description": {"type": "string", "description": "New description"},
124
+
125
+ # Users
126
+ "user_ids": {
127
+ "type": "array",
128
+ "items": {"oneOf": [{"type": "string"}, {"type": "integer"}]},
129
+ "description": "Final set of user ids (replacement)."
130
+ },
131
+ "new_users": {
132
+ "type": "array",
133
+ "items": {"oneOf": [{"type": "string"}, {"type": "integer"}]},
134
+ "description": "User ids to add (delta)."
135
+ },
136
+ "delete_users": {
137
+ "type": "array",
138
+ "items": {"oneOf": [{"type": "string"}, {"type": "integer"}]},
139
+ "description": "User ids to remove (delta)."
140
+ },
141
+
142
+ # Access Levels (replacement)
143
+ "access_level_ids": {
144
+ "type": "array",
145
+ "items": {"oneOf": [{"type": "string"}, {"type": "integer"}]},
146
+ "description": "Replace the group's access_levels with these ids."
147
+ },
148
+
149
+ # User Groups - Replacement
150
+ "user_group_ids": {
151
+ "type": "array",
152
+ "items": {"oneOf": [{"type": "integer"}, {"type": "string"}]},
153
+ "description": "Replace user_groups with these ids. If [], clears all."
154
+ },
155
+ "user_group_search_text": {
156
+ "type": "string",
157
+ "description": "(Legacy) Single-match replacement by name."
158
+ },
159
+
160
+ # User Groups - Delta
161
+ "add_user_group_ids": {
162
+ "type": "array",
163
+ "items": {"oneOf": [{"type": "integer"}, {"type": "string"}]},
164
+ "description": "Add these user group ids (delta)."
165
+ },
166
+ "remove_user_group_ids": {
167
+ "type": "array",
168
+ "items": {"oneOf": [{"type": "integer"}, {"type": "string"}]},
169
+ "description": "Remove these user group ids (delta)."
170
+ },
171
+ "add_user_group_search_text": {
172
+ "type": "string",
173
+ "description": "Add by name (3-case resolution)."
174
+ },
175
+ "remove_user_group_search_text": {
176
+ "type": "string",
177
+ "description": "Remove by name (3-case resolution)."
178
+ }
179
+ },
180
+ "required": ["group_id"]
181
+ }
182
+ )
183
+
184
+ DELETE_ACCESS_GROUP_TOOL = Tool(
185
+ name="delete-access-group",
186
+ description=" CRITICAL: Delete an access group. THIS IS PERMANENT and CANNOT BE UNDONE! ALWAYS confirm with user before executing. For demo data, REQUIRE explicit 'delete confirm' or '삭제 확인' from user.",
187
+ inputSchema={
188
+ "type": "object",
189
+ "properties": {
190
+ "group_id": {
191
+ "type": "integer",
192
+ "description": "ID of the access group to delete"
193
+ }
194
+ },
195
+ "required": ["group_id"]
196
+ }
197
+ )
198
+
199
+ # -----------------------------
200
+ # Access Level management tools
201
+ # -----------------------------
202
+ GET_ACCESS_LEVELS_TOOL = Tool(
203
+ name="get-access-levels",
204
+ description="""
205
+ Retrieve access levels from the BioStar 2 system.
206
+
207
+ Supports the official "View All AL" query parameters (GET /api/access_levels):
208
+ - limit: integer (0 shows all; default 50)
209
+ - offset: integer (default 0)
210
+ - order_by: string (e.g., "id:false" for ascending by id)
211
+
212
+ Example:
213
+ curl --location 'https://127.0.0.1/api/access_levels?limit=50&offset=0&order_by=id:false'
214
+ """,
215
+ inputSchema={
216
+ "type": "object",
217
+ "properties": {
218
+ "limit": {"type": "integer", "description": "Limit number of records (0 to show all)", "default": 50},
219
+ "offset": {"type": "integer", "description": "Offset/skip records", "default": 0},
220
+ "order_by": {"type": "string", "description": "Order by (e.g., 'id:false')", "default": "id:false"}
221
+ },
222
+ "required": []
223
+ }
224
+ )
225
+
226
+ GET_ACCESS_LEVEL_TOOL = Tool(
227
+ name="get-access-level",
228
+ description="Get detailed information about a specific access level",
229
+ inputSchema={
230
+ "type": "object",
231
+ "properties": {
232
+ "level_id": {
233
+ "type": "integer",
234
+ "description": "ID of the access level to retrieve"
235
+ }
236
+ },
237
+ "required": ["level_id"]
238
+ }
239
+ )
240
+
241
+ CREATE_ACCESS_LEVEL_TOOL = Tool(
242
+ name="create-access-level",
243
+ description="""
244
+ Create a new Access Level (AL) with strict door validation and an enforced 'Always' schedule.
245
+
246
+ SMART AUTO-SELECT MODE:
247
+ - If `access_level_items` is NOT provided or empty, the tool will automatically fetch all available doors.
248
+ - You can then specify door IDs/names in the `door_ids` or `door_names` parameter for simple cases.
249
+ - For complex multi-item scenarios, provide full `access_level_items` structure.
250
+
251
+ CALLER RULES:
252
+ - Recommended: Use `door_ids=[177, 178, 179]` or `door_names=["Main Entrance", "CEO Office"]` for simple access levels.
253
+ - Advanced: Use `access_level_items=[{"doors": [...]}]` for multiple schedule items (though all use 'Always' internally).
254
+ - Do NOT use this tool as a fallback for `update-access-level` when the target does not exist. Creation must be explicitly requested by the user.
255
+
256
+ WHAT THIS TOOL DOES:
257
+ - Accepts `door_ids`, `door_names`, OR `access_level_items` (at least one required).
258
+ - Any `schedule_id` provided in the input is ignored; 'Always' will be applied internally.
259
+ - Nonexistent door ids -> error with `available_doors` list for retry.
260
+
261
+ BEHAVIOR:
262
+ - Duplicate-name check (case-insensitive). If an AL with the same name exists, returns that info and does not create.
263
+ - Normalizes per-item doors (int-cast & de-dup) to avoid API errors.
264
+ - Preview (confirm=false) is allowed ONLY when all provided door ids are valid.
265
+ - On proceed (confirm=true), calls POST /api/access_levels (no trailing slash).
266
+ - If 'Always' schedule is missing, it is created and used automatically.
267
+ """,
268
+ inputSchema={
269
+ "type": "object",
270
+ "properties": {
271
+ "confirm": {
272
+ "type": "boolean",
273
+ "default": True,
274
+ "description": "If true, create the Access Level; otherwise preview only. Defaults to True for automatic creation."
275
+ },
276
+ "name": {
277
+ "type": "string",
278
+ "description": "Unique Access Level name."
279
+ },
280
+ "description": {
281
+ "type": "string",
282
+ "default": "",
283
+ "description": "Description for the Access Level."
284
+ },
285
+ "door_ids": {
286
+ "type": "array",
287
+ "items": {"type": "integer"},
288
+ "description": "Simple mode: List of door IDs to include (e.g., [177, 178, 179]). Mutually exclusive with access_level_items."
289
+ },
290
+ "door_names": {
291
+ "type": "array",
292
+ "items": {"type": "string"},
293
+ "description": "Simple mode: List of door names to include (e.g., ['Main Entrance', 'CEO Office']). Will be resolved to IDs. Mutually exclusive with access_level_items."
294
+ },
295
+ "auto_update_on_exist": {
296
+ "type": "boolean",
297
+ "default": True,
298
+ "description": "If true (default), automatically update if Access Level with same name exists. If false, skip creation and return existing info."
299
+ },
300
+ "access_level_items": {
301
+ "type": "array",
302
+ "minItems": 1,
303
+ "description": "Advanced mode: Array tying doors (1..n) to the enforced 'Always' schedule (applied internally). Each item must include `doors`. Mutually exclusive with door_ids/door_names.",
304
+ "items": {
305
+ "type": "object",
306
+ "properties": {
307
+ "doors": {
308
+ "type": "array",
309
+ "minItems": 1,
310
+ "items": {"anyOf": [{"type": "integer"}, {"type": "string"}]},
311
+ "description": "Door IDs or names; de-duplicated per item."
312
+ }
313
+ },
314
+ "required": ["doors"]
315
+ }
316
+ }
317
+ },
318
+ "required": ["name"],
319
+ "oneOf": [
320
+ {"required": ["door_ids"]},
321
+ {"required": ["door_names"]},
322
+ {"required": ["access_level_items"]}
323
+ ]
324
+ }
325
+ )
326
+
327
+ from mcp.types import Tool
328
+
329
+ UPDATE_ACCESS_LEVEL_TOOL = Tool(
330
+ name="update-access-level",
331
+ description="""
332
+ Update an Access Level (PUT /api/access_levels/{level_id}) with a SINGLE enforced schedule 'Always'.
333
+
334
+ HARD GUARD (DO NOT BYPASS):
335
+ - This tool MUST NEVER create a new access level when the target does not exist.
336
+ - If the target cannot be resolved, this tool returns an error with status="target_not_found",
337
+ includes a list of existing access levels (`available_levels`), and sets `do_not_autocreate=true`.
338
+ - Callers MUST NOT chain to `create-access-level` based solely on this result. They MUST ask the user first.
339
+
340
+ Target specification:
341
+ - Prefer `level_id`. Or provide `level_name` (exact) and/or `search_text` for fuzzy match.
342
+
343
+ Editable fields:
344
+ - name (optional)
345
+ - description (optional; empty string clears)
346
+ - doors (optional): choose exactly one of set_doors / add_doors / remove_doors
347
+
348
+ STRICT RULES:
349
+ - Schedule is not user-editable; it is always set to 'Always' (created if missing).
350
+ - If no door operation is provided, existing doors are preserved.
351
+ - All door ids are validated against GET /api/doors; invalid ids -> error with `available_doors`.
352
+ - Duplicate door ids are removed automatically.
353
+ - If multiple access_level_items exist on server, they are collapsed into one (first item's id).
354
+
355
+ Optional:
356
+ - dry_run: true -> return the planned PUT body & door diff without calling the API.
357
+ """,
358
+ inputSchema={
359
+ "type": "object",
360
+ "properties": {
361
+ "level_id": {"type": "integer", "description": "ID of the access level to update"},
362
+ "level_name": {"type": "string", "description": "Exact name of the access level to update"},
363
+ "search_text": {"type": "string", "description": "Fuzzy search text (name/description contains)"},
364
+ "name": {"type": "string", "description": "New name (optional)"},
365
+ "description": {"type": "string", "description": "New description (optional; empty string clears)"},
366
+ "set_doors": {
367
+ "type": "array",
368
+ "items": {"oneOf": [{"type": "integer"}, {"type": "string"}]},
369
+ "description": "Full replacement of door ids (takes precedence over add/remove)"
370
+ },
371
+ "add_doors": {
372
+ "type": "array",
373
+ "items": {"oneOf": [{"type": "integer"}, {"type": "string"}]},
374
+ "description": "Door ids to add"
375
+ },
376
+ "remove_doors": {
377
+ "type": "array",
378
+ "items": {"oneOf": [{"type": "integer"}, {"type": "string"}]},
379
+ "description": "Door ids to remove (ignored if not currently assigned)"
380
+ },
381
+ "dry_run": {"type": "boolean", "default": False, "description": "Preview changes without calling the API"}
382
+ },
383
+ "required": []
384
+ }
385
+ )
386
+
387
+
388
+ DELETE_ACCESS_LEVEL_TOOL = Tool(
389
+ name="delete-access-level",
390
+ description=" CRITICAL: Delete an access level. THIS IS PERMANENT and CANNOT BE UNDONE! ALWAYS confirm with user before executing. For demo data, REQUIRE explicit 'delete confirm' or '삭제 확인' from user.",
391
+ inputSchema={
392
+ "type": "object",
393
+ "properties": {
394
+ "level_id": {
395
+ "type": "integer",
396
+ "description": "ID of the access level to delete"
397
+ }
398
+ },
399
+ "required": ["level_id"]
400
+ }
401
+ )
402
+
403
+ ADD_ACCESS_LEVEL_TO_GROUP_TOOL = Tool(
404
+ name="add-access-level-to-group",
405
+ description="""
406
+ Add one or more access levels to an access group by updating the group's access_levels array via PUT /api/access_groups/{id}.
407
+ - Preserves other fields (name, description, users, user_groups, floor_levels, new_users, delete_users) as-is.
408
+ - Validates that requested access level ids exist; otherwise returns error and a full list of existing access levels.
409
+ - Idempotent: already-assigned ids are ignored.
410
+ """.strip(),
411
+ inputSchema={
412
+ "type": "object",
413
+ "properties": {
414
+ "access_group_id": {
415
+ "type": "integer",
416
+ "description": "Target access group ID"
417
+ },
418
+ "access_level_id": {
419
+ "type": "integer",
420
+ "description": "An access level id to add (single)"
421
+ },
422
+ "access_level_ids": {
423
+ "type": "array",
424
+ "items": {"oneOf": [{"type": "integer"}, {"type": "string"}]},
425
+ "description": "Access level ids to add (one or many)"
426
+ }
427
+ },
428
+ "required": ["access_group_id"]
429
+ }
430
+ )
431
+
432
+ REMOVE_ACCESS_LEVEL_FROM_GROUP_TOOL = Tool(
433
+ name="remove-access-level-from-group",
434
+ description="""
435
+ Remove one or more access levels from an access group by updating the group's access_levels array via PUT /api/access_groups/{id}.
436
+ - Preserves other fields (name, description, users, user_groups, floor_levels, new_users, delete_users) as-is.
437
+ - Validates that requested access level ids exist; otherwise returns error and a full list of existing access levels.
438
+ - Idempotent: ids not currently assigned are ignored.
439
+ """.strip(),
440
+ inputSchema={
441
+ "type": "object",
442
+ "properties": {
443
+ "access_group_id": {
444
+ "type": "integer",
445
+ "description": "Target access group ID"
446
+ },
447
+ "access_level_id": {
448
+ "type": "integer",
449
+ "description": "An access level id to remove (single)"
450
+ },
451
+ "access_level_ids": {
452
+ "type": "array",
453
+ "items": {"oneOf": [{"type": "integer"}, {"type": "string"}]},
454
+ "description": "Access level ids to remove (one or many)"
455
+ }
456
+ },
457
+ "required": ["access_group_id"]
458
+ }
459
+ )
460
+
461
+ # -----------------------------------------
462
+ # Access Group v2 search (optional helper)
463
+ # -----------------------------------------
464
+ SEARCH_ACCESS_GROUPS_V2_TOOL = Tool(
465
+ name="search-access-groups",
466
+ description="""
467
+ Search (list) access groups using v2 endpoint (POST /api/v2/access_groups/search/).
468
+ Required body fields:
469
+ - limit: number (0 shows all)
470
+ - order_by: string "name:false" or "id:false" (false=ascending, true=descending)
471
+ """,
472
+ inputSchema={
473
+ "type": "object",
474
+ "properties": {
475
+ "limit": {
476
+ "type": "integer",
477
+ "description": "To limit result by n record(s), 0 to show all",
478
+ "default": 0
479
+ },
480
+ "order_by": {
481
+ "type": "string",
482
+ "description": "Order by string like 'id:false' or 'name:true'",
483
+ "default": "id:false"
484
+ }
485
+ },
486
+ "required": ["limit", "order_by"]
487
+ }
488
+ )
489
+
490
+ # -----------------------------
491
+ # Access Log tools
492
+ # -----------------------------
493
+ # Export all access tools
494
+ # -----------------------------
495
+ # Note: get-access-logs is in events.py (EventHandler has the implementation)
496
+ ACCESS_TOOLS = [
497
+ GET_ACCESS_GROUPS_TOOL,
498
+ GET_ACCESS_GROUP_TOOL,
499
+ CREATE_ACCESS_GROUP_TOOL,
500
+ UPDATE_ACCESS_GROUP_TOOL,
501
+ DELETE_ACCESS_GROUP_TOOL,
502
+ SEARCH_ACCESS_GROUPS_V2_TOOL,
503
+ GET_ACCESS_LEVELS_TOOL,
504
+ GET_ACCESS_LEVEL_TOOL,
505
+ CREATE_ACCESS_LEVEL_TOOL,
506
+ UPDATE_ACCESS_LEVEL_TOOL,
507
+ DELETE_ACCESS_LEVEL_TOOL,
508
+ ADD_ACCESS_LEVEL_TO_GROUP_TOOL,
509
+ REMOVE_ACCESS_LEVEL_FROM_GROUP_TOOL
510
+ ]