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.
- biostar_x_mcp_server/__init__.py +25 -0
- biostar_x_mcp_server/__main__.py +15 -0
- biostar_x_mcp_server/config.py +87 -0
- biostar_x_mcp_server/handlers/__init__.py +35 -0
- biostar_x_mcp_server/handlers/access_handler.py +2162 -0
- biostar_x_mcp_server/handlers/audit_handler.py +489 -0
- biostar_x_mcp_server/handlers/auth_handler.py +216 -0
- biostar_x_mcp_server/handlers/base_handler.py +228 -0
- biostar_x_mcp_server/handlers/card_handler.py +746 -0
- biostar_x_mcp_server/handlers/device_handler.py +4344 -0
- biostar_x_mcp_server/handlers/door_handler.py +3969 -0
- biostar_x_mcp_server/handlers/event_handler.py +1331 -0
- biostar_x_mcp_server/handlers/file_handler.py +212 -0
- biostar_x_mcp_server/handlers/help_web_handler.py +379 -0
- biostar_x_mcp_server/handlers/log_handler.py +1051 -0
- biostar_x_mcp_server/handlers/navigation_handler.py +109 -0
- biostar_x_mcp_server/handlers/occupancy_handler.py +541 -0
- biostar_x_mcp_server/handlers/user_handler.py +3568 -0
- biostar_x_mcp_server/schemas/__init__.py +21 -0
- biostar_x_mcp_server/schemas/access.py +158 -0
- biostar_x_mcp_server/schemas/audit.py +73 -0
- biostar_x_mcp_server/schemas/auth.py +24 -0
- biostar_x_mcp_server/schemas/cards.py +128 -0
- biostar_x_mcp_server/schemas/devices.py +496 -0
- biostar_x_mcp_server/schemas/doors.py +306 -0
- biostar_x_mcp_server/schemas/events.py +104 -0
- biostar_x_mcp_server/schemas/files.py +7 -0
- biostar_x_mcp_server/schemas/help.py +29 -0
- biostar_x_mcp_server/schemas/logs.py +33 -0
- biostar_x_mcp_server/schemas/occupancy.py +19 -0
- biostar_x_mcp_server/schemas/tool_response.py +29 -0
- biostar_x_mcp_server/schemas/users.py +166 -0
- biostar_x_mcp_server/server.py +335 -0
- biostar_x_mcp_server/session.py +221 -0
- biostar_x_mcp_server/tool_manager.py +172 -0
- biostar_x_mcp_server/tools/__init__.py +45 -0
- biostar_x_mcp_server/tools/access.py +510 -0
- biostar_x_mcp_server/tools/audit.py +227 -0
- biostar_x_mcp_server/tools/auth.py +59 -0
- biostar_x_mcp_server/tools/cards.py +269 -0
- biostar_x_mcp_server/tools/categories.py +197 -0
- biostar_x_mcp_server/tools/devices.py +1552 -0
- biostar_x_mcp_server/tools/doors.py +865 -0
- biostar_x_mcp_server/tools/events.py +305 -0
- biostar_x_mcp_server/tools/files.py +28 -0
- biostar_x_mcp_server/tools/help.py +80 -0
- biostar_x_mcp_server/tools/logs.py +123 -0
- biostar_x_mcp_server/tools/navigation.py +89 -0
- biostar_x_mcp_server/tools/occupancy.py +91 -0
- biostar_x_mcp_server/tools/users.py +1113 -0
- biostar_x_mcp_server/utils/__init__.py +31 -0
- biostar_x_mcp_server/utils/category_mapper.py +206 -0
- biostar_x_mcp_server/utils/decorators.py +101 -0
- biostar_x_mcp_server/utils/language_detector.py +51 -0
- biostar_x_mcp_server/utils/search.py +42 -0
- biostar_x_mcp_server/utils/timezone.py +122 -0
- suprema_biostar_mcp-1.0.1.dist-info/METADATA +163 -0
- suprema_biostar_mcp-1.0.1.dist-info/RECORD +61 -0
- suprema_biostar_mcp-1.0.1.dist-info/WHEEL +4 -0
- suprema_biostar_mcp-1.0.1.dist-info/entry_points.txt +2 -0
- suprema_biostar_mcp-1.0.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .tool_response import ToolResponse
|
|
2
|
+
from .users import (
|
|
3
|
+
CreateUserInput,
|
|
4
|
+
DeleteUserInput,
|
|
5
|
+
UpdateUserInput,
|
|
6
|
+
AdvancedSearchUserInput,
|
|
7
|
+
ExportCSVInput,
|
|
8
|
+
BulkAddUsersInput,
|
|
9
|
+
BulkEditUsersInput,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"ToolResponse",
|
|
14
|
+
"CreateUserInput",
|
|
15
|
+
"DeleteUserInput",
|
|
16
|
+
"UpdateUserInput",
|
|
17
|
+
"AdvancedSearchUserInput",
|
|
18
|
+
"ExportCSVInput",
|
|
19
|
+
"BulkAddUsersInput",
|
|
20
|
+
"BulkEditUsersInput",
|
|
21
|
+
]
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
from typing import Optional, Union, List, Dict, Any
|
|
2
|
+
from pydantic import BaseModel, Field, model_validator
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# ============================================================================
|
|
6
|
+
# Access Group Schemas
|
|
7
|
+
# ============================================================================
|
|
8
|
+
|
|
9
|
+
class GetAccessGroupsInput(BaseModel):
|
|
10
|
+
"""Input schema for get-access-groups tool"""
|
|
11
|
+
pass # No required fields
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GetAccessGroupInput(BaseModel):
|
|
15
|
+
"""Input schema for get-access-group tool"""
|
|
16
|
+
group_id: int = Field(..., description="ID of the access group to retrieve")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CreateAccessGroupInput(BaseModel):
|
|
20
|
+
"""Input schema for create-access-group tool"""
|
|
21
|
+
name: str = Field(..., description="Access group name")
|
|
22
|
+
description: Optional[str] = Field(default="", description="Description for the access group")
|
|
23
|
+
user_ids: Optional[List[Union[str, int]]] = Field(None, description="Explicit list of user ids to include. Do NOT auto-select.")
|
|
24
|
+
access_level_ids: Optional[List[Union[str, int]]] = Field(None, description="Existing access level ids to assign to this group.")
|
|
25
|
+
user_group_ids: Optional[List[Union[int, str]]] = Field(None, description="Explicit user group ids to include (validated against GET /api/user_groups)")
|
|
26
|
+
user_group_search_text: Optional[str] = Field(None, description="Substring to search user group name (3-case resolution)")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class UpdateAccessGroupInput(BaseModel):
|
|
30
|
+
"""Input schema for update-access-group tool"""
|
|
31
|
+
group_id: int = Field(..., description="ID of the access group to update")
|
|
32
|
+
name: Optional[str] = Field(None, description="New name")
|
|
33
|
+
description: Optional[str] = Field(None, description="New description")
|
|
34
|
+
|
|
35
|
+
# Users
|
|
36
|
+
user_ids: Optional[List[Union[str, int]]] = Field(None, description="Final set of user ids (replacement).")
|
|
37
|
+
new_users: Optional[List[Union[str, int]]] = Field(None, description="User ids to add (delta).")
|
|
38
|
+
delete_users: Optional[List[Union[str, int]]] = Field(None, description="User ids to remove (delta).")
|
|
39
|
+
|
|
40
|
+
# Access Levels (replacement)
|
|
41
|
+
access_level_ids: Optional[List[Union[str, int]]] = Field(None, description="Replace the group's access_levels with these ids.")
|
|
42
|
+
|
|
43
|
+
# User Groups - Replacement
|
|
44
|
+
user_group_ids: Optional[List[Union[int, str]]] = Field(None, description="Replace user_groups with these ids. If [], clears all.")
|
|
45
|
+
user_group_search_text: Optional[str] = Field(None, description="(Legacy) Single-match replacement by name.")
|
|
46
|
+
|
|
47
|
+
# User Groups - Delta
|
|
48
|
+
add_user_group_ids: Optional[List[Union[int, str]]] = Field(None, description="Add these user group ids (delta).")
|
|
49
|
+
remove_user_group_ids: Optional[List[Union[int, str]]] = Field(None, description="Remove these user group ids (delta).")
|
|
50
|
+
add_user_group_search_text: Optional[str] = Field(None, description="Add by name (3-case resolution).")
|
|
51
|
+
remove_user_group_search_text: Optional[str] = Field(None, description="Remove by name (3-case resolution).")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class DeleteAccessGroupInput(BaseModel):
|
|
55
|
+
"""Input schema for delete-access-group tool"""
|
|
56
|
+
group_id: int = Field(..., description="ID of the access group to delete")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class SearchAccessGroupsInput(BaseModel):
|
|
60
|
+
"""Input schema for search-access-groups tool"""
|
|
61
|
+
limit: int = Field(..., description="To limit result by n record(s), 0 to show all")
|
|
62
|
+
order_by: str = Field(..., description="Order by string like 'id:false' or 'name:true'")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# ============================================================================
|
|
66
|
+
# Access Level Schemas
|
|
67
|
+
# ============================================================================
|
|
68
|
+
|
|
69
|
+
class GetAccessLevelsInput(BaseModel):
|
|
70
|
+
"""Input schema for get-access-levels tool"""
|
|
71
|
+
limit: Optional[int] = Field(default=50, description="Limit number of records (0 to show all)")
|
|
72
|
+
offset: Optional[int] = Field(default=0, description="Offset/skip records")
|
|
73
|
+
order_by: Optional[str] = Field(default="id:false", description="Order by (e.g., 'id:false')")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class GetAccessLevelInput(BaseModel):
|
|
77
|
+
"""Input schema for get-access-level tool"""
|
|
78
|
+
level_id: int = Field(..., description="ID of the access level to retrieve")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class AccessLevelItemInput(BaseModel):
|
|
82
|
+
"""Input schema for access_level_items array item"""
|
|
83
|
+
doors: List[Union[int, str]] = Field(..., min_length=1, description="Door IDs or names; de-duplicated per item.")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class CreateAccessLevelInput(BaseModel):
|
|
87
|
+
"""Input schema for create-access-level tool"""
|
|
88
|
+
name: str = Field(..., description="Unique Access Level name.")
|
|
89
|
+
description: Optional[str] = Field(default="", description="Description for the Access Level.")
|
|
90
|
+
confirm: Optional[bool] = Field(default=True, description="If true, create the Access Level; otherwise preview only. Defaults to True for automatic creation.")
|
|
91
|
+
door_ids: Optional[List[int]] = Field(None, description="🆕 Simple mode: List of door IDs to include (e.g., [177, 178, 179]). Mutually exclusive with access_level_items.")
|
|
92
|
+
door_names: Optional[List[str]] = Field(None, 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.")
|
|
93
|
+
auto_update_on_exist: Optional[bool] = Field(default=True, description="🆕 If true (default), automatically update if Access Level with same name exists. If false, skip creation and return existing info.")
|
|
94
|
+
access_level_items: Optional[List[AccessLevelItemInput]] = Field(None, min_length=1, 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.")
|
|
95
|
+
|
|
96
|
+
@model_validator(mode="after")
|
|
97
|
+
def validate_door_specification(self):
|
|
98
|
+
"""Ensure at least one door specification method is provided"""
|
|
99
|
+
has_door_ids = self.door_ids is not None and len(self.door_ids) > 0
|
|
100
|
+
has_door_names = self.door_names is not None and len(self.door_names) > 0
|
|
101
|
+
has_access_level_items = self.access_level_items is not None and len(self.access_level_items) > 0
|
|
102
|
+
|
|
103
|
+
if not (has_door_ids or has_door_names or has_access_level_items):
|
|
104
|
+
raise ValueError("At least one of door_ids, door_names, or access_level_items must be provided")
|
|
105
|
+
|
|
106
|
+
# Check mutual exclusivity
|
|
107
|
+
provided = sum([has_door_ids, has_door_names, has_access_level_items])
|
|
108
|
+
if provided > 1:
|
|
109
|
+
raise ValueError("Only one of door_ids, door_names, or access_level_items can be provided")
|
|
110
|
+
|
|
111
|
+
return self
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class UpdateAccessLevelInput(BaseModel):
|
|
115
|
+
"""Input schema for update-access-level tool"""
|
|
116
|
+
level_id: Optional[int] = Field(None, description="ID of the access level to update")
|
|
117
|
+
level_name: Optional[str] = Field(None, description="Exact name of the access level to update")
|
|
118
|
+
search_text: Optional[str] = Field(None, description="Fuzzy search text (name/description contains)")
|
|
119
|
+
name: Optional[str] = Field(None, description="New name (optional)")
|
|
120
|
+
description: Optional[str] = Field(None, description="New description (optional; empty string clears)")
|
|
121
|
+
set_doors: Optional[List[Union[int, str]]] = Field(None, description="Full replacement of door ids (takes precedence over add/remove)")
|
|
122
|
+
add_doors: Optional[List[Union[int, str]]] = Field(None, description="Door ids to add")
|
|
123
|
+
remove_doors: Optional[List[Union[int, str]]] = Field(None, description="Door ids to remove (ignored if not currently assigned)")
|
|
124
|
+
dry_run: Optional[bool] = Field(default=False, description="Preview changes without calling the API")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class DeleteAccessLevelInput(BaseModel):
|
|
128
|
+
"""Input schema for delete-access-level tool"""
|
|
129
|
+
level_id: int = Field(..., description="ID of the access level to delete")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class AddAccessLevelToGroupInput(BaseModel):
|
|
133
|
+
"""Input schema for add-access-level-to-group tool"""
|
|
134
|
+
access_group_id: int = Field(..., description="Target access group ID")
|
|
135
|
+
access_level_id: Optional[int] = Field(None, description="An access level id to add (single)")
|
|
136
|
+
access_level_ids: Optional[List[Union[int, str]]] = Field(None, description="Access level ids to add (one or many)")
|
|
137
|
+
|
|
138
|
+
@model_validator(mode="after")
|
|
139
|
+
def validate_access_level_specification(self):
|
|
140
|
+
"""Ensure at least one access level is specified"""
|
|
141
|
+
if self.access_level_id is None and (self.access_level_ids is None or len(self.access_level_ids) == 0):
|
|
142
|
+
raise ValueError("Either access_level_id or access_level_ids must be provided")
|
|
143
|
+
return self
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class RemoveAccessLevelFromGroupInput(BaseModel):
|
|
147
|
+
"""Input schema for remove-access-level-from-group tool"""
|
|
148
|
+
access_group_id: int = Field(..., description="Target access group ID")
|
|
149
|
+
access_level_id: Optional[int] = Field(None, description="An access level id to remove (single)")
|
|
150
|
+
access_level_ids: Optional[List[Union[int, str]]] = Field(None, description="Access level ids to remove (one or many)")
|
|
151
|
+
|
|
152
|
+
@model_validator(mode="after")
|
|
153
|
+
def validate_access_level_specification(self):
|
|
154
|
+
"""Ensure at least one access level is specified"""
|
|
155
|
+
if self.access_level_id is None and (self.access_level_ids is None or len(self.access_level_ids) == 0):
|
|
156
|
+
raise ValueError("Either access_level_id or access_level_ids must be provided")
|
|
157
|
+
return self
|
|
158
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from typing import Optional, List
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# ============================================================================
|
|
6
|
+
# Audit Condition Schemas (nested)
|
|
7
|
+
# ============================================================================
|
|
8
|
+
|
|
9
|
+
class AuditConditionInput(BaseModel):
|
|
10
|
+
"""Input schema for audit condition item"""
|
|
11
|
+
column: str = Field(..., description="Column to search (e.g., 'datetime', 'user_id', 'target_type')")
|
|
12
|
+
operator: int = Field(..., description="Search operator (0=EQUAL, 1=NOT_EQUAL, 2=CONTAINS, 3=BETWEEN, 5=GREATER, 6=LESS)")
|
|
13
|
+
values: List[str] = Field(..., description="Values to search for")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# ============================================================================
|
|
17
|
+
# Audit Search Schemas
|
|
18
|
+
# ============================================================================
|
|
19
|
+
|
|
20
|
+
class AuditSearchInput(BaseModel):
|
|
21
|
+
"""Input schema for audit-search tool"""
|
|
22
|
+
conditions: Optional[List[AuditConditionInput]] = Field(None, description="Search conditions for audit trail")
|
|
23
|
+
limit: Optional[int] = Field(default=100, description="Maximum number of results")
|
|
24
|
+
offset: Optional[int] = Field(default=0, description="Offset for pagination")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AuditSearchUserInput(BaseModel):
|
|
28
|
+
"""Input schema for audit-search-user tool"""
|
|
29
|
+
search: Optional[str] = Field(None, description="Search string for user name")
|
|
30
|
+
limit: Optional[int] = Field(default=201, description="Maximum number of results")
|
|
31
|
+
offset: Optional[int] = Field(default=0, description="Offset for pagination")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AuditSearchOperatorLevelInput(BaseModel):
|
|
35
|
+
"""Input schema for audit-search-operator-level tool"""
|
|
36
|
+
search: Optional[str] = Field(None, description="Search string for permission level")
|
|
37
|
+
limit: Optional[int] = Field(default=201, description="Maximum number of results")
|
|
38
|
+
offset: Optional[int] = Field(default=0, description="Offset for pagination")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AuditSearchIPListInput(BaseModel):
|
|
42
|
+
"""Input schema for audit-search-ip-list tool"""
|
|
43
|
+
search: Optional[str] = Field(None, description="Search string for IP address")
|
|
44
|
+
limit: Optional[int] = Field(default=201, description="Maximum number of results")
|
|
45
|
+
offset: Optional[int] = Field(default=0, description="Offset for pagination")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class AuditSearchTargetListInput(BaseModel):
|
|
49
|
+
"""Input schema for audit-search-target-list tool"""
|
|
50
|
+
search: Optional[str] = Field(None, description="Search string for target")
|
|
51
|
+
limit: Optional[int] = Field(default=201, description="Maximum number of results")
|
|
52
|
+
offset: Optional[int] = Field(default=0, description="Offset for pagination")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class AuditCSVExportInput(BaseModel):
|
|
56
|
+
"""Input schema for audit-csv-export tool"""
|
|
57
|
+
conditions: Optional[List[AuditConditionInput]] = Field(None, description="Filter conditions (use 'DATE' for time).")
|
|
58
|
+
columns: Optional[List[str]] = Field(
|
|
59
|
+
default=["DATE","USRID","PERM","IP","MENU","TARGET","METHOD","CONTENT"],
|
|
60
|
+
description="CSV columns (UPPERCASE per API spec)."
|
|
61
|
+
)
|
|
62
|
+
headers: Optional[List[str]] = Field(
|
|
63
|
+
default=["Datetime","User","Operator Level","IP","Category","Target","Action","Modification"],
|
|
64
|
+
description="CSV header names (same length as columns)."
|
|
65
|
+
)
|
|
66
|
+
offset: Optional[int] = Field(default=0, description="Offset for export pagination.")
|
|
67
|
+
time_offset_minutes: Optional[int] = Field(default=0, description="Timezone offset in minutes (e.g., UTC+8 => 480).")
|
|
68
|
+
start_datetime: Optional[str] = Field(None, description="Convenience: start time (ISO). Builds a DATE condition if 'conditions' omitted.")
|
|
69
|
+
end_datetime: Optional[str] = Field(None, description="Convenience: end time (ISO). Builds a DATE condition if 'conditions' omitted.")
|
|
70
|
+
copy_to_downloads: Optional[bool] = Field(default=True, description="Copy exported CSV to Windows Downloads folder.")
|
|
71
|
+
dest_dir: Optional[str] = Field(None, description="Optional absolute destination directory (overrides Downloads).")
|
|
72
|
+
target_username: Optional[str] = Field(None, description="If provided, copies to C:\\Users\\<name>\\Downloads.")
|
|
73
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class LoginInput(BaseModel):
|
|
6
|
+
"""Input schema for login tool"""
|
|
7
|
+
username: Optional[str] = Field(None, description="Optional: BioStar 2 username (defaults to BIOSTAR_USERNAME from .env)")
|
|
8
|
+
password: Optional[str] = Field(None, description="Optional: BioStar 2 password (defaults to BIOSTAR_PASSWORD from .env)")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LogoutInput(BaseModel):
|
|
12
|
+
"""Input schema for logout tool"""
|
|
13
|
+
pass # No required fields
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GetSessionInfoInput(BaseModel):
|
|
17
|
+
"""Input schema for get-session-info tool"""
|
|
18
|
+
pass # No required fields
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class GetServerPreferencesInput(BaseModel):
|
|
22
|
+
"""Input schema for get-server-preferences tool"""
|
|
23
|
+
pass # No required fields
|
|
24
|
+
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from typing import Optional, Union
|
|
2
|
+
from pydantic import BaseModel, Field, model_validator
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# ============================================================================
|
|
6
|
+
# Card Creation Schemas
|
|
7
|
+
# ============================================================================
|
|
8
|
+
|
|
9
|
+
class CreateCardCSNInput(BaseModel):
|
|
10
|
+
"""Input schema for create-card-csn tool"""
|
|
11
|
+
card_id: Union[str, int] = Field(..., description="Value shown/read when scanning the card")
|
|
12
|
+
card_type_type: Optional[int] = Field(None, description="Type allocation of the card type; auto-resolved if omitted")
|
|
13
|
+
assign_to_user_id: Optional[Union[str, int]] = Field(None, description="If provided, assign the created card to this user immediately")
|
|
14
|
+
skip_availability_check: Optional[bool] = Field(default=False, description="Skip server-side availability check before create (default: false)")
|
|
15
|
+
dry_run: Optional[bool] = Field(default=False, description="Return the request body without calling the API (default: false)")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CreateCardWiegandInput(BaseModel):
|
|
19
|
+
"""Input schema for create-card-wiegand tool"""
|
|
20
|
+
card_id: Union[str, int] = Field(..., description="Numeric required")
|
|
21
|
+
facility_code: int = Field(..., description="Facility Code (required)")
|
|
22
|
+
card_number: int = Field(..., description="Card Number (required)")
|
|
23
|
+
wiegand_format_id: Optional[int] = Field(None, description="Format id (e.g., 0=26bit/H10301, 1=HID37/H10302)")
|
|
24
|
+
wiegand_format: Optional[str] = Field(None, description="Format alias (e.g., '26bit', 'H10301', 'HID37')")
|
|
25
|
+
display_card_id: Optional[str] = Field(None, description="Override display (must be 'FC-ID', e.g., '12-3456')")
|
|
26
|
+
card_type_type: Optional[int] = None
|
|
27
|
+
assign_to_user_id: Optional[Union[str, int]] = None
|
|
28
|
+
skip_availability_check: Optional[bool] = None
|
|
29
|
+
dry_run: Optional[bool] = None
|
|
30
|
+
|
|
31
|
+
@model_validator(mode="after")
|
|
32
|
+
def validate_wiegand_format(self):
|
|
33
|
+
"""Ensure either wiegand_format_id or wiegand_format is provided"""
|
|
34
|
+
if self.wiegand_format_id is None and self.wiegand_format is None:
|
|
35
|
+
raise ValueError("Either wiegand_format_id or wiegand_format must be provided")
|
|
36
|
+
return self
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class CreateCardSecureCredentialInput(BaseModel):
|
|
40
|
+
"""Input schema for create-card-secure-credential tool"""
|
|
41
|
+
card_id: Union[str, int] = Field(..., description="Card ID")
|
|
42
|
+
card_type_type: Optional[int] = None
|
|
43
|
+
assign_to_user_id: Optional[Union[str, int]] = None
|
|
44
|
+
skip_availability_check: Optional[bool] = None
|
|
45
|
+
dry_run: Optional[bool] = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class CreateCardAccessOnCardInput(BaseModel):
|
|
49
|
+
"""Input schema for create-card-access-on-card tool"""
|
|
50
|
+
card_id: Union[str, int] = Field(..., description="Card ID")
|
|
51
|
+
card_type_type: Optional[int] = None
|
|
52
|
+
assign_to_user_id: Optional[Union[str, int]] = None
|
|
53
|
+
skip_availability_check: Optional[bool] = None
|
|
54
|
+
dry_run: Optional[bool] = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class CreateCardMobileCSNInput(BaseModel):
|
|
58
|
+
"""Input schema for create-card-mobile-csn tool"""
|
|
59
|
+
card_id: Union[str, int] = Field(..., description="Card ID")
|
|
60
|
+
card_type_type: Optional[int] = None
|
|
61
|
+
assign_to_user_id: Optional[Union[str, int]] = None
|
|
62
|
+
isUserPhoto: Optional[bool] = None
|
|
63
|
+
isDepartment: Optional[bool] = None
|
|
64
|
+
isTitle: Optional[bool] = None
|
|
65
|
+
start_datetime: Optional[str] = None
|
|
66
|
+
expiry_datetime: Optional[str] = None
|
|
67
|
+
display_card_id: Optional[str] = None
|
|
68
|
+
skip_availability_check: Optional[bool] = None
|
|
69
|
+
dry_run: Optional[bool] = None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class CreateCardWiegandMobileInput(BaseModel):
|
|
73
|
+
"""Input schema for create-card-wiegand-mobile tool"""
|
|
74
|
+
card_id: Union[str, int] = Field(..., description="Card ID")
|
|
75
|
+
facility_code: int = Field(..., description="Facility Code (required)")
|
|
76
|
+
card_number: int = Field(..., description="Card Number (required)")
|
|
77
|
+
wiegand_format_id: Optional[int] = Field(None, description="Format id")
|
|
78
|
+
wiegand_format: Optional[str] = Field(None, description="Format alias")
|
|
79
|
+
display_card_id: Optional[str] = None
|
|
80
|
+
card_type_type: Optional[int] = None
|
|
81
|
+
assign_to_user_id: Optional[Union[str, int]] = None
|
|
82
|
+
skip_availability_check: Optional[bool] = None
|
|
83
|
+
dry_run: Optional[bool] = None
|
|
84
|
+
|
|
85
|
+
@model_validator(mode="after")
|
|
86
|
+
def validate_wiegand_format(self):
|
|
87
|
+
"""Ensure either wiegand_format_id or wiegand_format is provided"""
|
|
88
|
+
if self.wiegand_format_id is None and self.wiegand_format is None:
|
|
89
|
+
raise ValueError("Either wiegand_format_id or wiegand_format must be provided")
|
|
90
|
+
return self
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class CreateCardQRBarcodeInput(BaseModel):
|
|
94
|
+
"""Input schema for create-card-qr-barcode tool"""
|
|
95
|
+
card_id: Union[str, int] = Field(..., description="Card ID")
|
|
96
|
+
card_type_type: Optional[int] = None
|
|
97
|
+
assign_to_user_id: Optional[Union[str, int]] = None
|
|
98
|
+
skip_availability_check: Optional[bool] = None
|
|
99
|
+
dry_run: Optional[bool] = None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class CreateCardBioStar2QRInput(BaseModel):
|
|
103
|
+
"""Input schema for create-card-biostar2-qr tool"""
|
|
104
|
+
card_id: Union[str, int] = Field(..., description="Card ID")
|
|
105
|
+
card_type_type: Optional[int] = None
|
|
106
|
+
assign_to_user_id: Optional[Union[str, int]] = None
|
|
107
|
+
skip_availability_check: Optional[bool] = None
|
|
108
|
+
dry_run: Optional[bool] = None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# ============================================================================
|
|
112
|
+
# Supporting Tool Schemas
|
|
113
|
+
# ============================================================================
|
|
114
|
+
|
|
115
|
+
class GetCardTypesInput(BaseModel):
|
|
116
|
+
"""Input schema for get-card-types tool"""
|
|
117
|
+
pass # No required fields
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class CheckCardAvailabilityInput(BaseModel):
|
|
121
|
+
"""Input schema for check-card-availability tool"""
|
|
122
|
+
card_id: Union[str, int] = Field(..., description="Card ID to check")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class GetWiegandFormatPresetsInput(BaseModel):
|
|
126
|
+
"""Input schema for get-wiegand-format-presets tool"""
|
|
127
|
+
pass # No required fields
|
|
128
|
+
|