|
11 | 11 |
|
12 | 12 | from pydantic import Field |
13 | 13 |
|
| 14 | +from fastmcp.exceptions import ToolError |
| 15 | + |
14 | 16 | from ..errors import ErrorCode, create_error_response |
15 | | -from .helpers import exception_to_structured_error, log_tool_usage |
| 17 | +from .helpers import exception_to_structured_error, log_tool_usage, raise_tool_error |
16 | 18 | from .tools_voice_assistant import KNOWN_ASSISTANTS |
17 | 19 | from .util_helpers import coerce_bool_param, parse_json_param, parse_string_list_param |
18 | 20 |
|
@@ -445,65 +447,65 @@ async def ha_set_entity( |
445 | 447 | try: |
446 | 448 | parsed_aliases = parse_string_list_param(aliases, "aliases") |
447 | 449 | except ValueError as e: |
448 | | - return create_error_response( |
| 450 | + raise_tool_error(create_error_response( |
449 | 451 | ErrorCode.VALIDATION_INVALID_PARAMETER, |
450 | 452 | f"Invalid aliases parameter: {e}", |
451 | | - ) |
| 453 | + )) |
452 | 454 |
|
453 | 455 | parsed_labels = None |
454 | 456 | if labels is not None: |
455 | 457 | try: |
456 | 458 | parsed_labels = parse_string_list_param(labels, "labels") |
457 | 459 | except ValueError as e: |
458 | | - return create_error_response( |
| 460 | + raise_tool_error(create_error_response( |
459 | 461 | ErrorCode.VALIDATION_INVALID_PARAMETER, |
460 | 462 | f"Invalid labels parameter: {e}", |
461 | | - ) |
| 463 | + )) |
462 | 464 |
|
463 | 465 | # Parse and validate expose_to parameter |
464 | 466 | parsed_expose_to: dict[str, bool] | None = None |
465 | 467 | if expose_to is not None: |
466 | 468 | try: |
467 | 469 | parsed = parse_json_param(expose_to, "expose_to") |
468 | 470 | except ValueError as e: |
469 | | - return create_error_response( |
| 471 | + raise_tool_error(create_error_response( |
470 | 472 | ErrorCode.VALIDATION_INVALID_PARAMETER, |
471 | 473 | str(e), |
472 | | - ) |
| 474 | + )) |
473 | 475 |
|
474 | 476 | if not isinstance(parsed, dict): |
475 | | - return create_error_response( |
| 477 | + raise_tool_error(create_error_response( |
476 | 478 | ErrorCode.VALIDATION_INVALID_PARAMETER, |
477 | 479 | "expose_to must be a dict mapping assistant IDs to booleans, " |
478 | 480 | 'e.g. {"conversation": true, "cloud.alexa": false}', |
479 | | - ) |
| 481 | + )) |
480 | 482 | parsed_expose_to = parsed |
481 | 483 |
|
482 | 484 | # Validate assistant names |
483 | 485 | invalid_assistants = [ |
484 | 486 | a for a in parsed_expose_to if a not in KNOWN_ASSISTANTS |
485 | 487 | ] |
486 | 488 | if invalid_assistants: |
487 | | - return create_error_response( |
| 489 | + raise_tool_error(create_error_response( |
488 | 490 | ErrorCode.VALIDATION_INVALID_PARAMETER, |
489 | 491 | f"Invalid assistant(s) in expose_to: {invalid_assistants}. " |
490 | 492 | f"Valid: {KNOWN_ASSISTANTS}", |
491 | | - ) |
| 493 | + )) |
492 | 494 |
|
493 | 495 | # Coerce values to bool |
494 | 496 | for asst, val in parsed_expose_to.items(): |
495 | 497 | try: |
496 | 498 | coerced = coerce_bool_param(val, f"expose_to[{asst}]") |
497 | 499 | except ValueError as e: |
498 | | - return create_error_response( |
| 500 | + raise_tool_error(create_error_response( |
499 | 501 | ErrorCode.VALIDATION_INVALID_PARAMETER, |
500 | 502 | str(e), |
501 | | - ) |
| 503 | + )) |
502 | 504 | if coerced is None: |
503 | | - return create_error_response( |
| 505 | + raise_tool_error(create_error_response( |
504 | 506 | ErrorCode.VALIDATION_INVALID_PARAMETER, |
505 | 507 | f"expose_to[{asst}] must be a boolean value", |
506 | | - ) |
| 508 | + )) |
507 | 509 | parsed_expose_to[asst] = coerced |
508 | 510 |
|
509 | 511 | # Single entity case - use existing logic |
@@ -579,10 +581,12 @@ async def ha_set_entity( |
579 | 581 |
|
580 | 582 | return response |
581 | 583 |
|
| 584 | + except ToolError: |
| 585 | + raise |
582 | 586 | except Exception as e: |
583 | 587 | logger.error(f"Error updating entity: {e}") |
584 | 588 | eid_context = entity_id if isinstance(entity_id, str) else entity_ids |
585 | | - return exception_to_structured_error(e, context={"entity_id": eid_context}) |
| 589 | + exception_to_structured_error(e, context={"entity_id": eid_context}) |
586 | 590 |
|
587 | 591 | @mcp.tool( |
588 | 592 | annotations={ |
@@ -768,6 +772,6 @@ async def _fetch_entity(eid: str) -> dict[str, Any]: |
768 | 772 |
|
769 | 773 | except Exception as e: |
770 | 774 | logger.error(f"Error getting entity: {e}") |
771 | | - return exception_to_structured_error( |
| 775 | + exception_to_structured_error( |
772 | 776 | e, context={"entity_id": entity_id if isinstance(entity_id, str) else entity_ids} |
773 | 777 | ) |
0 commit comments