Coverage for mcpgateway/schemas.py: 74%

476 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-09 11:03 +0100

1# -*- coding: utf-8 -*- 

2"""MCP Gateway Schema Definitions. 

3 

4Copyright 2025 

5SPDX-License-Identifier: Apache-2.0 

6Authors: Mihai Criveti 

7 

8This module provides Pydantic models for request/response validation in the MCP Gateway. 

9It implements schemas for: 

10- Tool registration and invocation 

11- Resource management and subscriptions 

12- Prompt templates and arguments 

13- Gateway federation 

14- RPC message formats 

15- Event messages 

16- Admin interface 

17 

18The schemas ensure proper validation according to the MCP specification while adding 

19gateway-specific extensions for federation support. 

20""" 

21 

22# Standard 

23import base64 

24from datetime import datetime, timezone 

25import json 

26import logging 

27from typing import Any, Dict, List, Literal, Optional, Union 

28 

29# Third-Party 

30from pydantic import AnyHttpUrl, BaseModel, ConfigDict, Field, field_serializer, field_validator, model_validator, ValidationInfo 

31 

32# First-Party 

33from mcpgateway.models import ImageContent 

34from mcpgateway.models import Prompt as MCPPrompt 

35from mcpgateway.models import Resource as MCPResource 

36from mcpgateway.models import ResourceContent, TextContent 

37from mcpgateway.models import Tool as MCPTool 

38from mcpgateway.utils.services_auth import decode_auth, encode_auth 

39 

40logger = logging.getLogger(__name__) 

41 

42 

43def to_camel_case(s: str) -> str: 

44 """ 

45 Convert a string from snake_case to camelCase. 

46 

47 Args: 

48 s (str): The string to be converted, which is assumed to be in snake_case. 

49 

50 Returns: 

51 str: The string converted to camelCase. 

52 

53 Example: 

54 >>> to_camel_case("hello_world_example") 

55 'helloWorldExample' 

56 """ 

57 return "".join(word.capitalize() if i else word for i, word in enumerate(s.split("_"))) 

58 

59 

60def encode_datetime(v: datetime) -> str: 

61 """ 

62 Convert a datetime object to an ISO 8601 formatted string. 

63 

64 Args: 

65 v (datetime): The datetime object to be encoded. 

66 

67 Returns: 

68 str: The ISO 8601 formatted string representation of the datetime object. 

69 

70 Example: 

71 >>> encode_datetime(datetime(2023, 5, 22, 14, 30, 0)) 

72 '2023-05-22T14:30:00' 

73 """ 

74 return v.isoformat() 

75 

76 

77# --- Base Model --- 

78class BaseModelWithConfigDict(BaseModel): 

79 """Base model with common configuration. 

80 

81 Provides: 

82 - ORM mode for SQLAlchemy integration 

83 - JSON encoders for datetime handling 

84 - Automatic conversion from snake_case to camelCase for output 

85 """ 

86 

87 model_config = ConfigDict( 

88 from_attributes=True, 

89 alias_generator=to_camel_case, 

90 populate_by_name=True, 

91 use_enum_values=True, 

92 extra="ignore", 

93 json_schema_extra={"nullable": True}, 

94 ) 

95 

96 def to_dict(self, use_alias: bool = False) -> Dict[str, Any]: 

97 """ 

98 Converts the model instance into a dictionary representation. 

99 

100 Args: 

101 use_alias (bool): Whether to use aliases for field names (default is False). If True, 

102 field names will be converted using the alias generator function. 

103 

104 Returns: 

105 Dict[str, Any]: A dictionary where keys are field names and values are corresponding field values, 

106 with any nested models recursively converted to dictionaries. 

107 """ 

108 output = {} 

109 for key, value in self.dict(by_alias=use_alias).items(): 

110 output[key] = value if not isinstance(value, BaseModel) else value.to_dict(use_alias) 

111 return output 

112 

113 

114# --- Metrics Schemas --- 

115 

116 

117class ToolMetrics(BaseModelWithConfigDict): 

118 """ 

119 Represents the performance and execution statistics for a tool. 

120 

121 Attributes: 

122 total_executions (int): Total number of tool invocations. 

123 successful_executions (int): Number of successful tool invocations. 

124 failed_executions (int): Number of failed tool invocations. 

125 failure_rate (float): Failure rate (failed invocations / total invocations). 

126 min_response_time (Optional[float]): Minimum response time in seconds. 

127 max_response_time (Optional[float]): Maximum response time in seconds. 

128 avg_response_time (Optional[float]): Average response time in seconds. 

129 last_execution_time (Optional[datetime]): Timestamp of the most recent invocation. 

130 """ 

131 

132 total_executions: int = Field(..., description="Total number of tool invocations") 

133 successful_executions: int = Field(..., description="Number of successful tool invocations") 

134 failed_executions: int = Field(..., description="Number of failed tool invocations") 

135 failure_rate: float = Field(..., description="Failure rate (failed invocations / total invocations)") 

136 min_response_time: Optional[float] = Field(None, description="Minimum response time in seconds") 

137 max_response_time: Optional[float] = Field(None, description="Maximum response time in seconds") 

138 avg_response_time: Optional[float] = Field(None, description="Average response time in seconds") 

139 last_execution_time: Optional[datetime] = Field(None, description="Timestamp of the most recent invocation") 

140 

141 

142class ResourceMetrics(BaseModelWithConfigDict): 

143 """ 

144 Represents the performance and execution statistics for a resource. 

145 

146 Attributes: 

147 total_executions (int): Total number of resource invocations. 

148 successful_executions (int): Number of successful resource invocations. 

149 failed_executions (int): Number of failed resource invocations. 

150 failure_rate (float): Failure rate (failed invocations / total invocations). 

151 min_response_time (Optional[float]): Minimum response time in seconds. 

152 max_response_time (Optional[float]): Maximum response time in seconds. 

153 avg_response_time (Optional[float]): Average response time in seconds. 

154 last_execution_time (Optional[datetime]): Timestamp of the most recent invocation. 

155 """ 

156 

157 total_executions: int = Field(..., description="Total number of resource invocations") 

158 successful_executions: int = Field(..., description="Number of successful resource invocations") 

159 failed_executions: int = Field(..., description="Number of failed resource invocations") 

160 failure_rate: float = Field(..., description="Failure rate (failed invocations / total invocations)") 

161 min_response_time: Optional[float] = Field(None, description="Minimum response time in seconds") 

162 max_response_time: Optional[float] = Field(None, description="Maximum response time in seconds") 

163 avg_response_time: Optional[float] = Field(None, description="Average response time in seconds") 

164 last_execution_time: Optional[datetime] = Field(None, description="Timestamp of the most recent invocation") 

165 

166 

167class ServerMetrics(BaseModelWithConfigDict): 

168 """ 

169 Represents the performance and execution statistics for a server. 

170 

171 Attributes: 

172 total_executions (int): Total number of server invocations. 

173 successful_executions (int): Number of successful server invocations. 

174 failed_executions (int): Number of failed server invocations. 

175 failure_rate (float): Failure rate (failed invocations / total invocations). 

176 min_response_time (Optional[float]): Minimum response time in seconds. 

177 max_response_time (Optional[float]): Maximum response time in seconds. 

178 avg_response_time (Optional[float]): Average response time in seconds. 

179 last_execution_time (Optional[datetime]): Timestamp of the most recent invocation. 

180 """ 

181 

182 total_executions: int = Field(..., description="Total number of server invocations") 

183 successful_executions: int = Field(..., description="Number of successful server invocations") 

184 failed_executions: int = Field(..., description="Number of failed server invocations") 

185 failure_rate: float = Field(..., description="Failure rate (failed invocations / total invocations)") 

186 min_response_time: Optional[float] = Field(None, description="Minimum response time in seconds") 

187 max_response_time: Optional[float] = Field(None, description="Maximum response time in seconds") 

188 avg_response_time: Optional[float] = Field(None, description="Average response time in seconds") 

189 last_execution_time: Optional[datetime] = Field(None, description="Timestamp of the most recent invocation") 

190 

191 

192class PromptMetrics(BaseModelWithConfigDict): 

193 """ 

194 Represents the performance and execution statistics for a prompt. 

195 

196 Attributes: 

197 total_executions (int): Total number of prompt invocations. 

198 successful_executions (int): Number of successful prompt invocations. 

199 failed_executions (int): Number of failed prompt invocations. 

200 failure_rate (float): Failure rate (failed invocations / total invocations). 

201 min_response_time (Optional[float]): Minimum response time in seconds. 

202 max_response_time (Optional[float]): Maximum response time in seconds. 

203 avg_response_time (Optional[float]): Average response time in seconds. 

204 last_execution_time (Optional[datetime]): Timestamp of the most recent invocation. 

205 """ 

206 

207 total_executions: int = Field(..., description="Total number of prompt invocations") 

208 successful_executions: int = Field(..., description="Number of successful prompt invocations") 

209 failed_executions: int = Field(..., description="Number of failed prompt invocations") 

210 failure_rate: float = Field(..., description="Failure rate (failed invocations / total invocations)") 

211 min_response_time: Optional[float] = Field(None, description="Minimum response time in seconds") 

212 max_response_time: Optional[float] = Field(None, description="Maximum response time in seconds") 

213 avg_response_time: Optional[float] = Field(None, description="Average response time in seconds") 

214 last_execution_time: Optional[datetime] = Field(None, description="Timestamp of the most recent invocation") 

215 

216 

217# --- JSON Path API modifier Schema 

218 

219 

220class JsonPathModifier(BaseModelWithConfigDict): 

221 """Schema for JSONPath queries. 

222 

223 Provides the structure for parsing JSONPath queries and optional mapping. 

224 """ 

225 

226 jsonpath: Optional[str] = Field(None, description="JSONPath expression for querying JSON data.") 

227 mapping: Optional[Dict[str, str]] = Field(None, description="Mapping of fields from original data to output.") 

228 

229 

230# --- Tool Schemas --- 

231# Authentication model 

232class AuthenticationValues(BaseModelWithConfigDict): 

233 """Schema for all Authentications. 

234 Provides the authentication values for different types of authentication. 

235 """ 

236 

237 auth_type: Optional[str] = Field(None, description="Type of authentication: basic, bearer, headers or None") 

238 auth_value: Optional[str] = Field(None, description="Encoded Authentication values") 

239 

240 # Only For tool read and view tool 

241 username: str = Field("", description="Username for basic authentication") 

242 password: str = Field("", description="Password for basic authentication") 

243 token: str = Field("", description="Bearer token for authentication") 

244 auth_header_key: str = Field("", description="Key for custom headers authentication") 

245 auth_header_value: str = Field("", description="Value for custom headers authentication") 

246 

247 

248class ToolCreate(BaseModelWithConfigDict): 

249 """Schema for creating a new tool. 

250 

251 Supports both MCP-compliant tools and REST integrations. Validates: 

252 - Unique tool name 

253 - Valid endpoint URL 

254 - Valid JSON Schema for input validation 

255 - Integration type: 'MCP' for MCP-compliant tools or 'REST' for REST integrations 

256 - Request type (For REST-GET,POST,PUT,DELETE and for MCP-SSE,STDIO,STREAMABLEHTTP) 

257 - Optional authentication credentials: BasicAuth or BearerTokenAuth or HeadersAuth. 

258 """ 

259 

260 name: str = Field(..., description="Unique name for the tool") 

261 url: Union[str, AnyHttpUrl] = Field(None, description="Tool endpoint URL") 

262 description: Optional[str] = Field(None, description="Tool description") 

263 request_type: Literal["GET", "POST", "PUT", "DELETE", "SSE", "STDIO", "STREAMABLEHTTP"] = Field("SSE", description="HTTP method to be used for invoking the tool") 

264 integration_type: Literal["MCP", "REST"] = Field("MCP", description="Tool integration type: 'MCP' for MCP-compliant tools, 'REST' for REST integrations") 

265 headers: Optional[Dict[str, str]] = Field(None, description="Additional headers to send when invoking the tool") 

266 input_schema: Optional[Dict[str, Any]] = Field( 

267 default_factory=lambda: {"type": "object", "properties": {}}, 

268 description="JSON Schema for validating tool parameters", 

269 ) 

270 annotations: Optional[Dict[str, Any]] = Field( 

271 default_factory=dict, 

272 description="Tool annotations for behavior hints (title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint)", 

273 ) 

274 jsonpath_filter: Optional[str] = Field(default="", description="JSON modification filter") 

275 auth: Optional[AuthenticationValues] = Field(None, description="Authentication credentials (Basic or Bearer Token or custom headers) if required") 

276 gateway_id: Optional[str] = Field(None, description="id of gateway for the tool") 

277 

278 @model_validator(mode="before") 

279 def assemble_auth(cls, values: Dict[str, Any]) -> Dict[str, Any]: 

280 """ 

281 Assemble authentication information from separate keys if provided. 

282 

283 Looks for keys "auth_type", "auth_username", "auth_password", "auth_token", "auth_header_key" and "auth_header_value". 

284 Constructs the "auth" field as a dictionary suitable for BasicAuth or BearerTokenAuth or HeadersAuth. 

285 

286 Args: 

287 values: Dict with authentication information 

288 

289 Returns: 

290 Dict: Reformatedd values dict 

291 """ 

292 logger.debug( 

293 "Assembling auth in ToolCreate with raw values", 

294 extra={ 

295 "auth_type": values.get("auth_type"), 

296 "auth_username": values.get("auth_username"), 

297 "auth_password": values.get("auth_password"), 

298 "auth_token": values.get("auth_token"), 

299 "auth_header_key": values.get("auth_header_key"), 

300 "auth_header_value": values.get("auth_header_value"), 

301 }, 

302 ) 

303 

304 auth_type = values.get("auth_type") 

305 if auth_type: 305 ↛ 306line 305 didn't jump to line 306 because the condition on line 305 was never true

306 if auth_type.lower() == "basic": 

307 creds = base64.b64encode(f"{values.get('auth_username', '')}:{values.get('auth_password', '')}".encode("utf-8")).decode() 

308 encoded_auth = encode_auth({"Authorization": f"Basic {creds}"}) 

309 values["auth"] = {"auth_type": "basic", "auth_value": encoded_auth} 

310 elif auth_type.lower() == "bearer": 

311 encoded_auth = encode_auth({"Authorization": f"Bearer {values.get('auth_token', '')}"}) 

312 values["auth"] = {"auth_type": "bearer", "auth_value": encoded_auth} 

313 elif auth_type.lower() == "authheaders": 

314 encoded_auth = encode_auth({values.get("auth_header_key", ""): values.get("auth_header_value", "")}) 

315 values["auth"] = {"auth_type": "authheaders", "auth_value": encoded_auth} 

316 return values 

317 

318 

319class ToolUpdate(BaseModelWithConfigDict): 

320 """Schema for updating an existing tool. 

321 

322 Similar to ToolCreate but all fields are optional to allow partial updates. 

323 """ 

324 

325 name: Optional[str] = Field(None, description="Unique name for the tool") 

326 url: Optional[Union[str, AnyHttpUrl]] = Field(None, description="Tool endpoint URL") 

327 description: Optional[str] = Field(None, description="Tool description") 

328 request_type: Optional[Literal["GET", "POST", "PUT", "DELETE", "SSE", "STDIO", "STREAMABLEHTTP"]] = Field(None, description="HTTP method to be used for invoking the tool") 

329 integration_type: Optional[Literal["MCP", "REST"]] = Field(None, description="Tool integration type") 

330 headers: Optional[Dict[str, str]] = Field(None, description="Additional headers to send when invoking the tool") 

331 input_schema: Optional[Dict[str, Any]] = Field(None, description="JSON Schema for validating tool parameters") 

332 annotations: Optional[Dict[str, Any]] = Field(None, description="Tool annotations for behavior hints") 

333 jsonpath_filter: Optional[str] = Field(None, description="JSON path filter for rpc tool calls") 

334 auth: Optional[AuthenticationValues] = Field(None, description="Authentication credentials (Basic or Bearer Token or custom headers) if required") 

335 gateway_id: Optional[str] = Field(None, description="id of gateway for the tool") 

336 

337 @model_validator(mode="before") 

338 def assemble_auth(cls, values: Dict[str, Any]) -> Dict[str, Any]: 

339 """ 

340 Assemble authentication information from separate keys if provided. 

341 

342 Looks for keys "auth_type", "auth_username", "auth_password", "auth_token", "auth_header_key" and "auth_header_value". 

343 Constructs the "auth" field as a dictionary suitable for BasicAuth or BearerTokenAuth or HeadersAuth. 

344 

345 Args: 

346 values: Dict with authentication information 

347 

348 Returns: 

349 Dict: Reformatedd values dict 

350 """ 

351 

352 logger.debug( 

353 "Assembling auth in ToolCreate with raw values", 

354 extra={ 

355 "auth_type": values.get("auth_type"), 

356 "auth_username": values.get("auth_username"), 

357 "auth_password": values.get("auth_password"), 

358 "auth_token": values.get("auth_token"), 

359 "auth_header_key": values.get("auth_header_key"), 

360 "auth_header_value": values.get("auth_header_value"), 

361 }, 

362 ) 

363 

364 auth_type = values.get("auth_type") 

365 if auth_type: 365 ↛ 366line 365 didn't jump to line 366 because the condition on line 365 was never true

366 if auth_type.lower() == "basic": 

367 creds = base64.b64encode(f"{values.get('auth_username', '')}:{values.get('auth_password', '')}".encode("utf-8")).decode() 

368 encoded_auth = encode_auth({"Authorization": f"Basic {creds}"}) 

369 values["auth"] = {"auth_type": "basic", "auth_value": encoded_auth} 

370 elif auth_type.lower() == "bearer": 

371 encoded_auth = encode_auth({"Authorization": f"Bearer {values.get('auth_token', '')}"}) 

372 values["auth"] = {"auth_type": "bearer", "auth_value": encoded_auth} 

373 elif auth_type.lower() == "authheaders": 

374 encoded_auth = encode_auth({values.get("auth_header_key", ""): values.get("auth_header_value", "")}) 

375 values["auth"] = {"auth_type": "authheaders", "auth_value": encoded_auth} 

376 return values 

377 

378 

379class ToolRead(BaseModelWithConfigDict): 

380 """Schema for reading tool information. 

381 

382 Includes all tool fields plus: 

383 - Database ID 

384 - Creation/update timestamps 

385 - enabled: If Tool is enabled or disabled. 

386 - reachable: If Tool is reachable or not. 

387 - Gateway ID for federation 

388 - Execution count indicating the number of times the tool has been executed. 

389 - Metrics: Aggregated metrics for the tool invocations. 

390 - Request type and authentication settings. 

391 """ 

392 

393 id: str 

394 original_name: str 

395 url: Optional[str] 

396 description: Optional[str] 

397 request_type: str 

398 integration_type: str 

399 headers: Optional[Dict[str, str]] 

400 input_schema: Dict[str, Any] 

401 annotations: Optional[Dict[str, Any]] 

402 jsonpath_filter: Optional[str] 

403 auth: Optional[AuthenticationValues] 

404 created_at: datetime 

405 updated_at: datetime 

406 enabled: bool 

407 reachable: bool 

408 gateway_id: Optional[str] 

409 execution_count: int 

410 metrics: ToolMetrics 

411 name: str 

412 gateway_slug: str 

413 original_name_slug: str 

414 

415 

416class ToolInvocation(BaseModelWithConfigDict): 

417 """Schema for tool invocation requests. 

418 

419 Captures: 

420 - Tool name to invoke 

421 - Arguments matching tool's input schema 

422 """ 

423 

424 name: str = Field(..., description="Name of tool to invoke") 

425 arguments: Dict[str, Any] = Field(default_factory=dict, description="Arguments matching tool's input schema") 

426 

427 

428class ToolResult(BaseModelWithConfigDict): 

429 """Schema for tool invocation results. 

430 

431 Supports: 

432 - Multiple content types (text/image) 

433 - Error reporting 

434 - Optional error messages 

435 """ 

436 

437 content: List[Union[TextContent, ImageContent]] 

438 is_error: bool = False 

439 error_message: Optional[str] = None 

440 

441 

442class ResourceCreate(BaseModelWithConfigDict): 

443 """Schema for creating a new resource. 

444 

445 Supports: 

446 - Static resources with URI 

447 - Template resources with parameters 

448 - Both text and binary content 

449 """ 

450 

451 uri: str = Field(..., description="Unique URI for the resource") 

452 name: str = Field(..., description="Human-readable resource name") 

453 description: Optional[str] = Field(None, description="Resource description") 

454 mime_type: Optional[str] = Field(None, description="Resource MIME type") 

455 template: Optional[str] = Field(None, description="URI template for parameterized resources") 

456 content: Union[str, bytes] = Field(..., description="Resource content (text or binary)") 

457 

458 

459class ResourceUpdate(BaseModelWithConfigDict): 

460 """Schema for updating an existing resource. 

461 

462 Similar to ResourceCreate but URI is not required and all fields are optional. 

463 """ 

464 

465 name: Optional[str] = Field(None, description="Human-readable resource name") 

466 description: Optional[str] = Field(None, description="Resource description") 

467 mime_type: Optional[str] = Field(None, description="Resource MIME type") 

468 template: Optional[str] = Field(None, description="URI template for parameterized resources") 

469 content: Optional[Union[str, bytes]] = Field(None, description="Resource content (text or binary)") 

470 

471 

472class ResourceRead(BaseModelWithConfigDict): 

473 """Schema for reading resource information. 

474 

475 Includes all resource fields plus: 

476 - Database ID 

477 - Content size 

478 - Creation/update timestamps 

479 - Active status 

480 - Metrics: Aggregated metrics for the resource invocations. 

481 """ 

482 

483 id: int 

484 uri: str 

485 name: str 

486 description: Optional[str] 

487 mime_type: Optional[str] 

488 size: Optional[int] 

489 created_at: datetime 

490 updated_at: datetime 

491 is_active: bool 

492 metrics: ResourceMetrics 

493 

494 

495class ResourceSubscription(BaseModelWithConfigDict): 

496 """Schema for resource subscriptions. 

497 

498 Tracks: 

499 - Resource URI being subscribed to 

500 - Unique subscriber identifier 

501 """ 

502 

503 uri: str = Field(..., description="URI of resource to subscribe to") 

504 subscriber_id: str = Field(..., description="Unique subscriber identifier") 

505 

506 

507class ResourceNotification(BaseModelWithConfigDict): 

508 """Schema for resource update notifications. 

509 

510 Contains: 

511 - Resource URI 

512 - Updated content 

513 - Update timestamp 

514 """ 

515 

516 uri: str 

517 content: ResourceContent 

518 timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) 

519 

520 @field_serializer("timestamp") 

521 def serialize_timestamp(self, dt: datetime) -> str: 

522 """Serialize the `timestamp` field as an ISO 8601 string with UTC timezone. 

523 

524 Converts the given datetime to UTC and returns it in ISO 8601 format, 

525 replacing the "+00:00" suffix with "Z" to indicate UTC explicitly. 

526 

527 Args: 

528 dt (datetime): The datetime object to serialize. 

529 

530 Returns: 

531 str: ISO 8601 formatted string in UTC, ending with 'Z'. 

532 """ 

533 return dt.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") 

534 

535 

536# --- Prompt Schemas --- 

537 

538 

539class PromptArgument(BaseModelWithConfigDict): 

540 """Schema for prompt template arguments. 

541 

542 Defines: 

543 - Argument name 

544 - Optional description 

545 - Required flag 

546 """ 

547 

548 name: str = Field(..., description="Argument name") 

549 description: Optional[str] = Field(None, description="Argument description") 

550 required: bool = Field(default=False, description="Whether argument is required") 

551 

552 model_config: ConfigDict = ConfigDict( 

553 **{ 

554 # start with every key from the base 

555 **BaseModelWithConfigDict.model_config, 

556 # override only json_schema_extra by merging the two dicts: 

557 "json_schema_extra": { 

558 **BaseModelWithConfigDict.model_config.get("json_schema_extra", {}), 

559 "example": { 

560 "name": "language", 

561 "description": "Programming language", 

562 "required": True, 

563 }, 

564 }, 

565 } 

566 ) 

567 

568 

569class PromptCreate(BaseModelWithConfigDict): 

570 """Schema for creating a new prompt template. 

571 

572 Includes: 

573 - Template name and description 

574 - Template text 

575 - Expected arguments 

576 """ 

577 

578 name: str = Field(..., description="Unique name for the prompt") 

579 description: Optional[str] = Field(None, description="Prompt description") 

580 template: str = Field(..., description="Prompt template text") 

581 arguments: List[PromptArgument] = Field(default_factory=list, description="List of arguments for the template") 

582 

583 

584class PromptUpdate(BaseModelWithConfigDict): 

585 """Schema for updating an existing prompt. 

586 

587 Similar to PromptCreate but all fields are optional to allow partial updates. 

588 """ 

589 

590 name: Optional[str] = Field(None, description="Unique name for the prompt") 

591 description: Optional[str] = Field(None, description="Prompt description") 

592 template: Optional[str] = Field(None, description="Prompt template text") 

593 arguments: Optional[List[PromptArgument]] = Field(None, description="List of arguments for the template") 

594 

595 

596class PromptRead(BaseModelWithConfigDict): 

597 """Schema for reading prompt information. 

598 

599 Includes all prompt fields plus: 

600 - Database ID 

601 - Creation/update timestamps 

602 - Active status 

603 - Metrics: Aggregated metrics for the prompt invocations. 

604 """ 

605 

606 id: int 

607 name: str 

608 description: Optional[str] 

609 template: str 

610 arguments: List[PromptArgument] 

611 created_at: datetime 

612 updated_at: datetime 

613 is_active: bool 

614 metrics: PromptMetrics 

615 

616 

617class PromptInvocation(BaseModelWithConfigDict): 

618 """Schema for prompt invocation requests. 

619 

620 Contains: 

621 - Prompt name to use 

622 - Arguments for template rendering 

623 """ 

624 

625 name: str = Field(..., description="Name of prompt to use") 

626 arguments: Dict[str, str] = Field(default_factory=dict, description="Arguments for template rendering") 

627 

628 

629# --- Gateway Schemas --- 

630 

631 

632class GatewayCreate(BaseModelWithConfigDict): 

633 """Schema for registering a new federation gateway. 

634 

635 Captures: 

636 - Gateway name 

637 - Endpoint URL 

638 - Optional description 

639 - Authentication type: basic, bearer, headers 

640 - Authentication credentials: username/password or token or custom headers 

641 """ 

642 

643 name: str = Field(..., description="Unique name for the gateway") 

644 url: Union[str, AnyHttpUrl] = Field(..., description="Gateway endpoint URL") 

645 description: Optional[str] = Field(None, description="Gateway description") 

646 transport: str = Field(default="SSE", description="Transport used by MCP server: SSE or STREAMABLEHTTP") 

647 

648 # Authorizations 

649 auth_type: Optional[str] = Field(None, description="Type of authentication: basic, bearer, headers, or none") 

650 # Fields for various types of authentication 

651 auth_username: Optional[str] = Field(None, description="Username for basic authentication") 

652 auth_password: Optional[str] = Field(None, description="Password for basic authentication") 

653 auth_token: Optional[str] = Field(None, description="Token for bearer authentication") 

654 auth_header_key: Optional[str] = Field(None, description="Key for custom headers authentication") 

655 auth_header_value: Optional[str] = Field(None, description="Value for custom headers authentication") 

656 

657 # Adding `auth_value` as an alias for better access post-validation 

658 auth_value: Optional[str] = Field(None, validate_default=True) 

659 

660 @field_validator("url", mode="before") 

661 def ensure_url_scheme(cls, v: str) -> str: 

662 """ 

663 Ensure URL has an http/https scheme. 

664 

665 Args: 

666 v: Input url 

667 

668 Returns: 

669 str: URL with correct schema 

670 

671 """ 

672 if isinstance(v, str) and not (v.startswith("http://") or v.startswith("https://")): 672 ↛ 673line 672 didn't jump to line 673 because the condition on line 672 was never true

673 return f"http://{v}" 

674 return v 

675 

676 @field_validator("auth_value", mode="before") 

677 def create_auth_value(cls, v, info): 

678 """ 

679 This validator will run before the model is fully instantiated (mode="before") 

680 It will process the auth fields based on auth_type and generate auth_value. 

681 

682 Args: 

683 v: Input url 

684 info: ValidationInfo containing auth_type 

685 

686 Returns: 

687 str: Auth value 

688 """ 

689 data = info.data 

690 auth_type = data.get("auth_type") 

691 

692 if (auth_type is None) or (auth_type == ""): 692 ↛ 696line 692 didn't jump to line 696 because the condition on line 692 was always true

693 return v # If no auth_type is provided, no need to create auth_value 

694 

695 # Process the auth fields and generate auth_value based on auth_type 

696 auth_value = cls._process_auth_fields(info) 

697 

698 return auth_value 

699 

700 @staticmethod 

701 def _process_auth_fields(info: ValidationInfo) -> Optional[Dict[str, Any]]: 

702 """ 

703 Processes the input authentication fields and returns the correct auth_value. 

704 This method is called based on the selected auth_type. 

705 

706 Args: 

707 info: ValidationInfo containing auth fields 

708 

709 Returns: 

710 Dict with encoded auth 

711 

712 Raises: 

713 ValueError: If auth_type is invalid 

714 """ 

715 data = info.data 

716 auth_type = data.get("auth_type") 

717 

718 if auth_type == "basic": 

719 # For basic authentication, both username and password must be present 

720 username = data.get("auth_username") 

721 password = data.get("auth_password") 

722 

723 if not username or not password: 

724 raise ValueError("For 'basic' auth, both 'auth_username' and 'auth_password' must be provided.") 

725 

726 creds = base64.b64encode(f"{username}:{password}".encode("utf-8")).decode() 

727 return encode_auth({"Authorization": f"Basic {creds}"}) 

728 

729 if auth_type == "bearer": 

730 # For bearer authentication, only token is required 

731 token = data.get("auth_token") 

732 

733 if not token: 

734 raise ValueError("For 'bearer' auth, 'auth_token' must be provided.") 

735 

736 return encode_auth({"Authorization": f"Bearer {token}"}) 

737 

738 if auth_type == "authheaders": 

739 # For headers authentication, both key and value must be present 

740 header_key = data.get("auth_header_key") 

741 header_value = data.get("auth_header_value") 

742 

743 if not header_key or not header_value: 

744 raise ValueError("For 'headers' auth, both 'auth_header_key' and 'auth_header_value' must be provided.") 

745 

746 return encode_auth({header_key: header_value}) 

747 

748 raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, or headers.") 

749 

750 

751class GatewayUpdate(BaseModelWithConfigDict): 

752 """Schema for updating an existing federation gateway. 

753 

754 Similar to GatewayCreate but all fields are optional to allow partial updates. 

755 """ 

756 

757 name: Optional[str] = Field(None, description="Unique name for the gateway") 

758 url: Optional[Union[str, AnyHttpUrl]] = Field(None, description="Gateway endpoint URL") 

759 description: Optional[str] = Field(None, description="Gateway description") 

760 transport: str = Field(default="SSE", description="Transport used by MCP server: SSE or STREAMABLEHTTP") 

761 

762 name: Optional[str] = Field(None, description="Unique name for the prompt") 

763 # Authorizations 

764 auth_type: Optional[str] = Field(None, description="auth_type: basic, bearer, headers or None") 

765 auth_username: Optional[str] = Field(None, description="username for basic authentication") 

766 auth_password: Optional[str] = Field(None, description="password for basic authentication") 

767 auth_token: Optional[str] = Field(None, description="token for bearer authentication") 

768 auth_header_key: Optional[str] = Field(None, description="key for custom headers authentication") 

769 auth_header_value: Optional[str] = Field(None, description="vallue for custom headers authentication") 

770 

771 # Adding `auth_value` as an alias for better access post-validation 

772 auth_value: Optional[str] = Field(None, validate_default=True) 

773 

774 @field_validator("url", mode="before") 

775 def ensure_url_scheme(cls, v: Optional[str]) -> Optional[str]: 

776 """ 

777 Ensure URL has an http/https scheme. 

778 

779 Args: 

780 v: Input URL 

781 

782 Returns: 

783 str: Validated URL 

784 """ 

785 if isinstance(v, str) and not (v.startswith("http://") or v.startswith("https://")): 785 ↛ 786line 785 didn't jump to line 786 because the condition on line 785 was never true

786 return f"http://{v}" 

787 return v 

788 

789 @field_validator("auth_value", mode="before") 

790 def create_auth_value(cls, v, info): 

791 """ 

792 This validator will run before the model is fully instantiated (mode="before") 

793 It will process the auth fields based on auth_type and generate auth_value. 

794 

795 Args: 

796 v: Input URL 

797 info: ValidationInfo containing auth_type 

798 

799 Returns: 

800 str: Auth value or URL 

801 """ 

802 data = info.data 

803 auth_type = data.get("auth_type") 

804 

805 if (auth_type is None) or (auth_type == ""): 805 ↛ 809line 805 didn't jump to line 809 because the condition on line 805 was always true

806 return v # If no auth_type is provided, no need to create auth_value 

807 

808 # Process the auth fields and generate auth_value based on auth_type 

809 auth_value = cls._process_auth_fields(info) 

810 

811 return auth_value 

812 

813 @staticmethod 

814 def _process_auth_fields(values: Dict[str, Any]) -> Optional[Dict[str, Any]]: 

815 """ 

816 Processes the input authentication fields and returns the correct auth_value. 

817 This method is called based on the selected auth_type. 

818 

819 Args: 

820 values: Dict container auth information auth_type, auth_username, auth_password, auth_token, auth_header_key and auth_header_value 

821 

822 Returns: 

823 dict: Encoded auth information 

824 

825 Raises: 

826 ValueError: If auth type is invalid 

827 """ 

828 auth_type = values.get("auth_type") 

829 

830 if auth_type == "basic": 

831 # For basic authentication, both username and password must be present 

832 username = values.get("auth_username") 

833 password = values.get("auth_password") 

834 

835 if not username or not password: 

836 raise ValueError("For 'basic' auth, both 'auth_username' and 'auth_password' must be provided.") 

837 

838 creds = base64.b64encode(f"{username}:{password}".encode("utf-8")).decode() 

839 return encode_auth({"Authorization": f"Basic {creds}"}) 

840 

841 if auth_type == "bearer": 

842 # For bearer authentication, only token is required 

843 token = values.get("auth_token") 

844 

845 if not token: 

846 raise ValueError("For 'bearer' auth, 'auth_token' must be provided.") 

847 

848 return encode_auth({"Authorization": f"Bearer {token}"}) 

849 

850 if auth_type == "authheaders": 

851 # For headers authentication, both key and value must be present 

852 header_key = values.get("auth_header_key") 

853 header_value = values.get("auth_header_value") 

854 

855 if not header_key or not header_value: 

856 raise ValueError("For 'headers' auth, both 'auth_header_key' and 'auth_header_value' must be provided.") 

857 

858 return encode_auth({header_key: header_value}) 

859 

860 raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, or headers.") 

861 

862 

863class GatewayRead(BaseModelWithConfigDict): 

864 """Schema for reading gateway information. 

865 

866 Includes all gateway fields plus: 

867 - Database ID 

868 - Capabilities dictionary 

869 - Creation/update timestamps 

870 - enabled status 

871 - reachable status 

872 - Last seen timestamp 

873 - Authentication type: basic, bearer, headers 

874 - Authentication value: username/password or token or custom headers 

875 

876 Auto Populated fields: 

877 - Authentication username: for basic auth 

878 - Authentication password: for basic auth 

879 - Authentication token: for bearer auth 

880 - Authentication header key: for headers auth 

881 - Authentication header value: for headers auth 

882 """ 

883 

884 id: str = Field(None, description="Unique ID of the gateway") 

885 name: str = Field(..., description="Unique name for the gateway") 

886 url: str = Field(..., description="Gateway endpoint URL") 

887 description: Optional[str] = Field(None, description="Gateway description") 

888 transport: str = Field(default="SSE", description="Transport used by MCP server: SSE or STREAMABLEHTTP") 

889 capabilities: Dict[str, Any] = Field(default_factory=dict, description="Gateway capabilities") 

890 created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc), description="Creation timestamp") 

891 updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc), description="Last update timestamp") 

892 enabled: bool = Field(default=True, description="Is the gateway enabled?") 

893 reachable: bool = Field(default=True, description="Is the gateway reachable/online?") 

894 

895 last_seen: Optional[datetime] = Field(default_factory=lambda: datetime.now(timezone.utc), description="Last seen timestamp") 

896 

897 # Authorizations 

898 auth_type: Optional[str] = Field(None, description="auth_type: basic, bearer, headers or None") 

899 auth_value: Optional[str] = Field(None, description="auth value: username/password or token or custom headers") 

900 

901 # auth_value will populate the following fields 

902 auth_username: Optional[str] = Field(None, description="username for basic authentication") 

903 auth_password: Optional[str] = Field(None, description="password for basic authentication") 

904 auth_token: Optional[str] = Field(None, description="token for bearer authentication") 

905 auth_header_key: Optional[str] = Field(None, description="key for custom headers authentication") 

906 auth_header_value: Optional[str] = Field(None, description="vallue for custom headers authentication") 

907 

908 slug: str = Field(None, description="Slug for gateway endpoint URL") 

909 

910 # This will be the main method to automatically populate fields 

911 @model_validator(mode="after") 

912 def _populate_auth(cls, values: Dict[str, Any]) -> Dict[str, Any]: 

913 auth_type = values.auth_type 

914 auth_value_encoded = values.auth_value 

915 auth_value = decode_auth(auth_value_encoded) 

916 if auth_type == "basic": 916 ↛ 917line 916 didn't jump to line 917 because the condition on line 916 was never true

917 u = auth_value.get("username") 

918 p = auth_value.get("password") 

919 if not u or not p: 

920 raise ValueError("basic auth requires both username and password") 

921 values.auth_username, values.auth_password = u, p 

922 

923 elif auth_type == "bearer": 

924 auth = auth_value.get("Authorization") 

925 if not (isinstance(auth, str) and auth.startswith("Bearer ")): 925 ↛ 926line 925 didn't jump to line 926 because the condition on line 925 was never true

926 raise ValueError("bearer auth requires an Authorization header of the form 'Bearer <token>'") 

927 values.auth_token = auth.removeprefix("Bearer ") 

928 

929 elif auth_type == "authheaders": 929 ↛ 931line 929 didn't jump to line 931 because the condition on line 929 was never true

930 # must be exactly one header 

931 if len(auth_value) != 1: 

932 raise ValueError("authheaders requires exactly one key/value pair") 

933 k, v = next(iter(auth_value.items())) 

934 values.auth_header_key, values.auth_header_value = k, v 

935 

936 return values 

937 

938 

939class FederatedTool(BaseModelWithConfigDict): 

940 """Schema for tools provided by federated gateways. 

941 

942 Contains: 

943 - Tool definition 

944 - Source gateway information 

945 """ 

946 

947 tool: MCPTool 

948 gateway_id: str 

949 gateway_name: str 

950 gateway_url: str 

951 

952 

953class FederatedResource(BaseModelWithConfigDict): 

954 """Schema for resources from federated gateways. 

955 

956 Contains: 

957 - Resource definition 

958 - Source gateway information 

959 """ 

960 

961 resource: MCPResource 

962 gateway_id: str 

963 gateway_name: str 

964 gateway_url: str 

965 

966 

967class FederatedPrompt(BaseModelWithConfigDict): 

968 """Schema for prompts from federated gateways. 

969 

970 Contains: 

971 - Prompt definition 

972 - Source gateway information 

973 """ 

974 

975 prompt: MCPPrompt 

976 gateway_id: str 

977 gateway_name: str 

978 gateway_url: str 

979 

980 

981# --- RPC Schemas --- 

982 

983 

984class RPCRequest(BaseModelWithConfigDict): 

985 """Schema for JSON-RPC 2.0 requests. 

986 

987 Validates: 

988 - Protocol version 

989 - Method name 

990 - Optional parameters 

991 - Optional request ID 

992 """ 

993 

994 jsonrpc: Literal["2.0"] 

995 method: str 

996 params: Optional[Dict[str, Any]] = None 

997 id: Optional[Union[int, str]] = None 

998 

999 

1000class RPCResponse(BaseModelWithConfigDict): 

1001 """Schema for JSON-RPC 2.0 responses. 

1002 

1003 Contains: 

1004 - Protocol version 

1005 - Result or error 

1006 - Request ID 

1007 """ 

1008 

1009 jsonrpc: Literal["2.0"] 

1010 result: Optional[Any] = None 

1011 error: Optional[Dict[str, Any]] = None 

1012 id: Optional[Union[int, str]] = None 

1013 

1014 

1015# --- Event and Admin Schemas --- 

1016 

1017 

1018class EventMessage(BaseModelWithConfigDict): 

1019 """Schema for SSE event messages. 

1020 

1021 Includes: 

1022 - Event type 

1023 - Event data payload 

1024 - Event timestamp 

1025 """ 

1026 

1027 type: str = Field(..., description="Event type (tool_added, resource_updated, etc)") 

1028 data: Dict[str, Any] = Field(..., description="Event payload") 

1029 timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) 

1030 

1031 @field_serializer("timestamp") 

1032 def serialize_timestamp(self, dt: datetime) -> str: 

1033 """ 

1034 Serialize the `timestamp` field as an ISO 8601 string with UTC timezone. 

1035 

1036 Converts the given datetime to UTC and returns it in ISO 8601 format, 

1037 replacing the "+00:00" suffix with "Z" to indicate UTC explicitly. 

1038 

1039 Args: 

1040 dt (datetime): The datetime object to serialize. 

1041 

1042 Returns: 

1043 str: ISO 8601 formatted string in UTC, ending with 'Z'. 

1044 """ 

1045 return dt.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") 

1046 

1047 

1048class AdminToolCreate(BaseModelWithConfigDict): 

1049 """Schema for creating tools via admin UI. 

1050 

1051 Handles: 

1052 - Basic tool information 

1053 - JSON string inputs for headers/schema 

1054 """ 

1055 

1056 name: str 

1057 url: str 

1058 description: Optional[str] = None 

1059 integration_type: str = "MCP" 

1060 headers: Optional[str] = None # JSON string 

1061 input_schema: Optional[str] = None # JSON string 

1062 

1063 @field_validator("headers", "input_schema") 

1064 def validate_json(cls, v: Optional[str]) -> Optional[Dict[str, Any]]: 

1065 """ 

1066 Validate and parse JSON string inputs. 

1067 

1068 Args: 

1069 v: Input string 

1070 

1071 Returns: 

1072 dict: Output JSON version of v 

1073 

1074 Raises: 

1075 ValueError: When unable to convert to JSON 

1076 """ 

1077 if not v: 1077 ↛ 1078line 1077 didn't jump to line 1078 because the condition on line 1077 was never true

1078 return None 

1079 try: 

1080 return json.loads(v) 

1081 except json.JSONDecodeError: 

1082 raise ValueError("Invalid JSON") 

1083 

1084 

1085class AdminGatewayCreate(BaseModelWithConfigDict): 

1086 """Schema for creating gateways via admin UI. 

1087 

1088 Captures: 

1089 - Gateway name 

1090 - Endpoint URL 

1091 - Optional description 

1092 """ 

1093 

1094 name: str 

1095 url: str 

1096 description: Optional[str] = None 

1097 

1098 

1099# --- New Schemas for Status Toggle Operations --- 

1100 

1101 

1102class StatusToggleRequest(BaseModelWithConfigDict): 

1103 """Request schema for toggling active status.""" 

1104 

1105 activate: bool = Field(..., description="Whether to activate (true) or deactivate (false) the item") 

1106 

1107 

1108class StatusToggleResponse(BaseModelWithConfigDict): 

1109 """Response schema for status toggle operations.""" 

1110 

1111 id: int 

1112 name: str 

1113 is_active: bool 

1114 message: str = Field(..., description="Success message") 

1115 

1116 

1117# --- Optional Filter Parameters for Listing Operations --- 

1118 

1119 

1120class ListFilters(BaseModelWithConfigDict): 

1121 """Filtering options for list operations.""" 

1122 

1123 include_inactive: bool = Field(False, description="Whether to include inactive items in the results") 

1124 

1125 

1126# --- Server Schemas --- 

1127 

1128 

1129class ServerCreate(BaseModelWithConfigDict): 

1130 """Schema for creating a new server. 

1131 

1132 Attributes: 

1133 name: The server's name (required). 

1134 description: Optional text description. 

1135 icon: Optional URL for the server's icon. 

1136 associated_tools: Optional list of tool IDs (as strings). 

1137 associated_resources: Optional list of resource IDs (as strings). 

1138 associated_prompts: Optional list of prompt IDs (as strings). 

1139 """ 

1140 

1141 name: str = Field(..., description="The server's name") 

1142 description: Optional[str] = Field(None, description="Server description") 

1143 icon: Optional[str] = Field(None, description="URL for the server's icon") 

1144 associated_tools: Optional[List[str]] = Field(None, description="Comma-separated tool IDs") 

1145 associated_resources: Optional[List[str]] = Field(None, description="Comma-separated resource IDs") 

1146 associated_prompts: Optional[List[str]] = Field(None, description="Comma-separated prompt IDs") 

1147 

1148 @field_validator("associated_tools", "associated_resources", "associated_prompts", mode="before") 

1149 def split_comma_separated(cls, v): 

1150 """ 

1151 Splits a comma-separated string into a list of strings if needed. 

1152 

1153 Args: 

1154 v: Input string 

1155 

1156 Returns: 

1157 list: Comma separated array of input string 

1158 """ 

1159 if isinstance(v, str): 

1160 return [item.strip() for item in v.split(",") if item.strip()] 

1161 return v 

1162 

1163 

1164class ServerUpdate(BaseModelWithConfigDict): 

1165 """Schema for updating an existing server. 

1166 

1167 All fields are optional to allow partial updates. 

1168 """ 

1169 

1170 name: Optional[str] = Field(None, description="The server's name") 

1171 description: Optional[str] = Field(None, description="Server description") 

1172 icon: Optional[str] = Field(None, description="URL for the server's icon") 

1173 associated_tools: Optional[List[str]] = Field(None, description="Comma-separated tool IDs") 

1174 associated_resources: Optional[List[str]] = Field(None, description="Comma-separated resource IDs") 

1175 associated_prompts: Optional[List[str]] = Field(None, description="Comma-separated prompt IDs") 

1176 

1177 @field_validator("associated_tools", "associated_resources", "associated_prompts", mode="before") 

1178 def split_comma_separated(cls, v): 

1179 """ 

1180 Splits a comma-separated string into a list of strings if needed. 

1181 

1182 Args: 

1183 v: Input string 

1184 

1185 Returns: 

1186 list: Comma separated array of input string 

1187 """ 

1188 if isinstance(v, str): 

1189 return [item.strip() for item in v.split(",") if item.strip()] 

1190 return v 

1191 

1192 

1193class ServerRead(BaseModelWithConfigDict): 

1194 """Schema for reading server information. 

1195 

1196 Includes all server fields plus: 

1197 - Database ID 

1198 - Associated tool, resource, and prompt IDs 

1199 - Creation/update timestamps 

1200 - Active status 

1201 - Metrics: Aggregated metrics for the server invocations. 

1202 """ 

1203 

1204 id: str 

1205 name: str 

1206 description: Optional[str] 

1207 icon: Optional[str] 

1208 created_at: datetime 

1209 updated_at: datetime 

1210 is_active: bool 

1211 associated_tools: List[str] = [] 

1212 associated_resources: List[int] = [] 

1213 associated_prompts: List[int] = [] 

1214 metrics: ServerMetrics 

1215 

1216 @model_validator(mode="before") 

1217 def populate_associated_ids(cls, values): 

1218 """ 

1219 Pre-validation method that converts associated objects to their 'id'. 

1220 

1221 This method checks 'associated_tools', 'associated_resources', and 

1222 'associated_prompts' in the input and replaces each object with its `id` 

1223 if present. 

1224 

1225 Args: 

1226 values (dict): The input values. 

1227 

1228 Returns: 

1229 dict: Updated values with object ids, or the original values if no 

1230 changes are made. 

1231 """ 

1232 # If values is not a dict (e.g. it's a Server instance), convert it 

1233 if not isinstance(values, dict): 1233 ↛ 1234line 1233 didn't jump to line 1234 because the condition on line 1233 was never true

1234 try: 

1235 values = vars(values) 

1236 except Exception: 

1237 return values 

1238 if "associated_tools" in values and values["associated_tools"]: 1238 ↛ 1240line 1238 didn't jump to line 1240 because the condition on line 1238 was always true

1239 values["associated_tools"] = [tool.id if hasattr(tool, "id") else tool for tool in values["associated_tools"]] 

1240 if "associated_resources" in values and values["associated_resources"]: 

1241 values["associated_resources"] = [res.id if hasattr(res, "id") else res for res in values["associated_resources"]] 

1242 if "associated_prompts" in values and values["associated_prompts"]: 

1243 values["associated_prompts"] = [prompt.id if hasattr(prompt, "id") else prompt for prompt in values["associated_prompts"]] 

1244 return values 

1245 

1246 

1247class GatewayTestRequest(BaseModelWithConfigDict): 

1248 """Schema for testing gateway connectivity. 

1249 

1250 Includes the HTTP method, base URL, path, optional headers, and body. 

1251 """ 

1252 

1253 method: str = Field(..., description="HTTP method to test (GET, POST, etc.)") 

1254 base_url: AnyHttpUrl = Field(..., description="Base URL of the gateway to test") 

1255 path: str = Field(..., description="Path to append to the base URL") 

1256 headers: Optional[Dict[str, str]] = Field(None, description="Optional headers for the request") 

1257 body: Optional[Union[str, Dict[str, Any]]] = Field(None, description="Optional body for the request, can be a string or JSON object") 

1258 

1259 

1260class GatewayTestResponse(BaseModelWithConfigDict): 

1261 """Schema for the response from a gateway test request. 

1262 

1263 Contains: 

1264 - HTTP status code 

1265 - Latency in milliseconds 

1266 - Optional response body, which can be a string or JSON object 

1267 """ 

1268 

1269 status_code: int = Field(..., description="HTTP status code returned by the gateway") 

1270 latency_ms: int = Field(..., description="Latency of the request in milliseconds") 

1271 body: Optional[Union[str, Dict[str, Any]]] = Field(None, description="Response body, can be a string or JSON object")