"""Tests for send_message tool-description resolution. ``server._start_agent`` passes the agents.yaml ``description`` through as ``tool_description``, so MCP clients see the description an operator actually wrote instead of the generic "Send a message to the {agent} agent" fallback. These pin the precedence implemented in ``MultimodalAgentMCPServer.register_agent_tools``: tool_description (agents.yaml) > @fast.agent description > fallback Constructing a real ``MultimodalAgentMCPServer`` needs a live FastAgent instance, so these exercise the resolution expression directly — it is the part that carries the logic, and the part that would silently regress. """ from __future__ import annotations import pytest # ── Helpers ────────────────────────────────────────────────────────────────── def _resolve( tool_description: str | None, agent_description: str | None = None, agent_name: str = "scotty", ) -> str: """Mirror register_agent_tools' description resolution.""" resolved = ( tool_description.format(agent=agent_name) if tool_description and "{agent}" in tool_description else tool_description ) return ( resolved or agent_description or f"Send a message to the {agent_name} agent" ) # ── Precedence ─────────────────────────────────────────────────────────────── def test_agents_yaml_description_is_used() -> None: """The agents.yaml description reaches the tool, not the fallback.""" assert _resolve("Systems administration expert", None) == ( "Systems administration expert" ) def test_agents_yaml_wins_over_decorator() -> None: """agents.yaml is the deployment's source of truth for agent metadata.""" assert _resolve("From agents.yaml", "From the decorator") == "From agents.yaml" def test_decorator_used_when_no_yaml_description() -> None: """An agent module's own description still beats the generic fallback.""" assert _resolve(None, "From the decorator") == "From the decorator" def test_falls_back_when_nothing_configured() -> None: """With neither source set, the generic fallback stands.""" assert _resolve(None, None) == "Send a message to the scotty agent" @pytest.mark.parametrize("empty", ["", None]) def test_empty_description_falls_through(empty: str | None) -> None: """An empty agents.yaml description must not shadow the other sources.""" assert _resolve(empty, "From the decorator") == "From the decorator" # ── Templating ─────────────────────────────────────────────────────────────── def test_agent_placeholder_is_interpolated() -> None: """``{agent}`` in a description is substituted with the agent name.""" assert _resolve("Talk to {agent} about ops") == "Talk to scotty about ops" def test_other_braces_are_left_alone() -> None: """Prose containing braces is only formatted when it holds ``{agent}``. Guards the ``"{agent}" in ...`` check — an unconditional ``.format()`` would raise KeyError on a description mentioning JSON. """ description = 'Handles JSON like {"a": 1} safely' assert _resolve(description, None) == description