content: import base64
  from pathlib import Path
  from typing import TYPE_CHECKING, Any
  from unittest.mock import patch
  
  import pytest
  from pydantic import AnyUrl, BaseModel
  from starlette.routing import Mount, Route
  
  from mcp.server.fastmcp import Context, FastMCP
  from mcp.server.fastmcp.prompts.base import Message, UserMessage
  from mcp.server.fastmcp.resources import FileResource, FunctionResource
  from mcp.server.fastmcp.utilities.types import Audio, Image
  from mcp.server.session import ServerSession
  from mcp.server.transport_security import TransportSecuritySettings
  from mcp.shared.exceptions import McpError
  from mcp.shared.memory import (
      create_connected_server_and_client_session as client_session,
  )
  from mcp.types import (
      AudioContent,
      BlobResourceContents,
      ContentBlock,
      EmbeddedResource,
      ImageContent,
      TextContent,
      TextResourceContents,
  )
  
  if TYPE_CHECKING:
      from mcp.server.fastmcp import Context
  
  
  class TestServer:
      @pytest.mark.anyio
      async def test_create_server(self):
          mcp = FastMCP(instructions="Server instructions")
          assert mcp.name == "FastMCP"
          assert mcp.instructions == "Server instructions"
  
      @pytest.mark.anyio
      async def test_normalize_path(self):
          """Test path normalization for mount paths."""
          mcp = FastMCP()
  
          # Test root path
          assert mcp._normalize_path("/", "/messages/") == "/messages/"
  
          # Test path with trailing slash
          assert mcp._normalize_path("/github/", "/messages/") == "/github/messages/"
  
          # Test path without trailing slash
          assert mcp._normalize_path("/github", "/messages/") == "/github/messages/"
  
          # Test endpoint without leading slash
          assert mcp._normalize_path("/github", "messages/") == "/github/messages/"
  
          # Test both with trailing/leading slashes
          assert mcp._normalize_path("/api/", "/v1/") == "/api/v1/"
  
      @pytest.mark.anyio
      async def test_sse_app_with_mount_path(self):
          """Test SSE app creation with different mount paths."""
          # Test with default mount path
          mcp = FastMCP()
          with patch.object(mcp, "_normalize_path", return_value="/messages/") as mock_normalize:
              mcp.sse_app()
              # Verify _normalize_path was called with correct args
              mock_normalize.assert_called_once_with("/", "/messages/")
  
          # Test with custom mount path in settings
          mcp = FastMCP()
          mcp.settings.mount_path = "/custom"
          with patch.object(mcp, "_normalize_path", return_value="/custom/messages/") as mock_normalize:
              mcp.sse_app()
              # Verify _normalize_path was called with correct args
              mock_normalize.assert_called_once_with("/custom", "/messages/")
  
          # Test with mount_path parameter
          mcp = FastMCP()
          with patch.object(mcp, "_normalize_path", return_value="/param/messages/") as mock_normalize:
              mcp.sse_app(mount_path="/param")
              # Verify _normalize_path was called with correct args
              mock_normalize.assert_called_once_with("/param", "/messages/")
  
      @pytest.mark.anyio
      async def test_starlette_routes_with_mount_path(self):
          """Test that Starlette routes are correctly configured with mount path."""
          # Test with mount path in settings
          mcp = FastMCP()
          mcp.settings.mount_path = "/api"
          app = mcp.sse_app()
  
          # Find routes by type
          sse_routes = [r for r in app.routes if isinstance(r, Route)]
          mount_routes = [r for r in app.routes if isinstance(r, Mount)]
  
          # Verify routes exist
          assert len(sse_routes) == 1, "Should have one SSE route"
          assert len(mount_routes) == 1, "Should have one mount route"
  
          # Verify path values
          assert sse_routes[0].path == "/sse", "SSE route path should be /sse"
          assert mount_routes[0].path == "/messages", "Mount route path should be /messages"
  
          # Test with mount path as parameter
          mcp = FastMCP()
          app = mcp.sse_app(mount_path="/param")
  
          # Find routes by type
          sse_routes = [r for r in app.routes if isinstance(r, Route)]
          mount_routes = [r for r in app.routes if isinstance(r, Mount)]
  
          # Verify routes exist
          assert len(sse_routes) == 1, "Should have one SSE route"
          assert len(mount_routes) == 1, "Should have one mount route"
  
          # Verify path values
          assert sse_routes[0].path == "/sse", "SSE route path should be /sse"
          assert mount_routes[0].path == "/messages", "Mount route path should be /messages"
  
      @pytest.mark.anyio
      async def test_non_ascii_description(self):
          """Test that FastMCP handles non-ASCII characters in descriptions correctly"""
          mcp = FastMCP()
  
          @mcp.tool(description=("🌟 This tool uses emojis and UTF-8 characters: á é í ó ú ñ 漢字 🎉"))
          def hello_world(name: str = "世界") -> str:
              return f"¡Hola, {name}! 👋"
  
          async with client_session(mcp._mcp_server) as client:
              tools = await client.list_tools()
              assert len(tools.tools) == 1
              tool = tools.tools[0]
              assert tool.description is not None
              assert "🌟" in tool.description
              assert "漢字" in tool.description
              assert "🎉" in tool.description
  
              result = await client.call_tool("hello_world", {})
              assert len(result.content) == 1
              content = result.content[0]
              assert isinstance(content, TextContent)
              assert "¡Hola, 世界! 👋" == content.text
  
      @pytest.mark.anyio
      async def test_add_tool_decorator(self):
          mcp = FastMCP()
  
          @mcp.tool()
          def sum(x: int, y: int) -> int:  # pragma: no cover
              return x + y
  
          assert len(mcp._tool_manager.list_tools()) == 1
  
      @pytest.mark.anyio
      async def test_add_tool_decorator_incorrect_usage(self):
          mcp = FastMCP()
  
          with pytest.raises(TypeError, match="The @tool decorator was used incorrectly"):
  
              @mcp.tool  # Missing parentheses #type: ignore
              def sum(x: int, y: int) -> int:  # pragma: no cover
                  return x + y
  
      @pytest.mark.anyio
      async def test_add_resource_decorator(self):
          mcp = FastMCP()
  
          @mcp.resource("r://{x}")
          def get_data(x: str) -> str:  # pragma: no cover
              return f"Data: {x}"
  
          assert len(mcp._resource_manager._templates) == 1
  
      @pytest.mark.anyio
      async def test_add_resource_decorator_incorrect_usage(self):
          mcp = FastMCP()
  
          with pytest.raises(TypeError, match="The @resource decorator was used incorrectly"):
  
              @mcp.resource  # Missing parentheses #type: ignore
              def get_data(x: str) -> str:  # pragma: no cover
                  return f"Data: {x}"
  
  
  class TestDnsRebindingProtection:
      """Tests for automatic DNS rebinding protection on localhost."""
  
      def test_auto_enabled_for_127_0_0_1(self):
          """DNS rebinding protection should auto-enable for host=127.0.0.1."""
          mcp = FastMCP(host="127.0.0.1")
          assert mcp.settings.transport_security is not None
          assert mcp.settings.transport_security.enable_dns_rebinding_protection is True
          assert "127.0.0.1:*" in mcp.settings.transport_security.allowed_hosts
          assert "localhost:*" in mcp.settings.transport_security.allowed_hosts
          assert "http://127.0.0.1:*" in mcp.settings.transport_security.allowed_origins
          assert "http://localhost:*" in mcp.settings.transport_security.allowed_origins
  
      def test_auto_enabled_for_localhost(self):
          """DNS rebinding protection should auto-enable for host=localhost."""
          mcp = FastMCP(host="localhost")
          assert mcp.settings.transport_security is not None
          assert mcp.settings.transport_security.enable_dns_rebinding_protection is True
          assert "127.0.0.1:*" in mcp.settings.transport_security.allowed_hosts
          assert "localhost:*" in mcp.settings.transport_security.allowed_hosts
  
      def test_auto_enabled_for_ipv6_localhost(self):
          """DNS rebinding protection should auto-enable for host=::1 (IPv6 localhost)."""
          mcp = FastMCP(host="::1")
          assert mcp.settings.transport_security is not None
          assert mcp.settings.transport_security.enable_dns_rebinding_protection is True
          assert "[::1]:*" in mcp.settings.transport_security.allowed_hosts
          assert "http://[::1]:*" in mcp.settings.transport_security.allowed_origins
  
      def test_not_auto_enabled_for_other_hosts(self):
          """DNS rebinding protection should NOT auto-enable for other hosts."""
          mcp = FastMCP(host="0.0.0.0")
          assert mcp.settings.transport_security is None
  
      def test_explicit_settings_not_overridden(self):
          """Explicit transport_security settings should not be overridden."""
          custom_settings = TransportSecuritySettings(
              enable_dns_rebinding_protection=False,
          )
          mcp = FastMCP(host="127.0.0.1", transport_security=custom_settings)
          # Settings are copied by pydantic, so check values not identity
          assert mcp.settings.transport_security is not None
          assert mcp.settings.transport_security.enable_dns_rebinding_protection is False
          assert mcp.settings.transport_security.allowed_hosts == []
  
  
  def tool_fn(x: int, y: int) -> int:
      return x + y
  
  
  def error_tool_fn() -> None:
      raise ValueError("Test error")
  
  
  def image_tool_fn(path: str) -> Image:
      return Image(path)
  
  
  def audio_tool_fn(path: str) -> Audio:
      return Audio(path)
  
  
  def mixed_content_tool_fn() -> list[ContentBlock]:
      return [
          TextContent(type="text", text="Hello"),
          ImageContent(type="image", data="abc", mimeType="image/png"),
          AudioContent(type="audio", data="def", mimeType="audio/wav"),
      ]
  
  
  class TestServerTools:
      @pytest.mark.anyio
      async def test_add_tool(self):
          mcp = FastMCP()
          mcp.add_tool(tool_fn)
          mcp.add_tool(tool_fn)
          assert len(mcp._tool_manager.list_tools()) == 1
  
      @pytest.mark.anyio
      async def test_list_tools(self):
          mcp = FastMCP()
          mcp.add_tool(tool_fn)
          async with client_session(mcp._mcp_server) as client:
              tools = await client.list_tools()
              assert len(tools.tools) == 1
  
      @pytest.mark.anyio
      async def test_call_tool(self):
          mcp = FastMCP()
          mcp.add_tool(tool_fn)
          async with client_session(mcp._mcp_server) as client:
              result = await client.call_tool("my_tool", {"arg1": "value"})
              assert not hasattr(result, "error")
              assert len(result.content) > 0
  
      @pytest.mark.anyio
      async def test_tool_exception_handling(self):
          mcp = FastMCP()
          mcp.add_tool(error_tool_fn)
          async with client_session(mcp._mcp_server) as client:
              result = await client.call_tool("error_tool_fn", {})
              assert len(result.content) == 1
              content = result.content[0]
              assert isinstance(content, TextContent)
              assert "Test error" in content.text
              assert result.isError is True
  
      @pytest.mark.anyio
      async def test_tool_error_handling(self):
          mcp = FastMCP()
          mcp.add_tool(error_tool_fn)
          async with client_session(mcp._mcp_server) as client:
              result = await client.call_tool("error_tool_fn", {})
              assert len(result.content) == 1
              content = result.content[0]
              assert isinstance(content, TextContent)
              assert "Test error" in content.text
              assert result.isError is True
  
      @pytest.mark.anyio
      async def test_tool_error_details(self):
          """Test that exception details are properly formatted in the response"""
          mcp = FastMCP()
          mcp.add_tool(error_tool_fn)
          async with client_session(mcp._mcp_server) as client:
              result = await client.call_tool("error_tool_fn", {})
              content = result.content[0]
              assert isinstance(content, TextContent)
              assert isinstance(content.text, str)
              assert "Test error" in content.text
              assert result.isError is True
  
      @pytest.mark.anyio
      async def test_tool_return_value_conversion(self):
          mcp = FastMCP()
          mcp.add_tool(tool_fn)
          async with client_session(mcp._mcp_server) as client:
              result = await client.call_tool("tool_fn", {"x": 1, "y": 2})
              assert len(result.content) == 1
              content = result.content[0]
              assert isinstance(content, TextContent)
              assert content.text == "3"
              # Check structured content - int return type should have structured output
              assert result.structuredContent is not None
              assert result.structuredContent == {"result": 3}
  
      @pytest.mark.anyio
      async def test_tool_image_helper(self, tmp_path: Path):
          # Create a test image
          image_path = tmp_path / "test.png"
          image_path.write_bytes(b"fake png data")
  
          mcp = FastMCP()
          mcp.add_tool(image_tool_fn)
          async with client_session(mcp._mcp_server) as client:
              result = await client.call_tool("image_tool_fn", {"path": str(image_path)})
              assert len(result.content) == 1
              content = result.content[0]
              assert isinstance(content, ImageContent)
              assert content.type == "image"
              assert content.mimeType == "image/png"
              # Verify base64 encoding
              decoded = base64.b64decode(content.data)
              assert decoded == b"fake png data"
              # Check structured content - Image return type should NOT have structured output
              assert result.structuredContent is None
  
      @pytest.mark.anyio
      async def test_tool_audio_helper(self, tmp_path: Path):
          # Create a test audio
          audio_path = tmp_path / "test.wav"
          audio_path.write_bytes(b"fake wav data")
  
          mcp = FastMCP()
          mcp.add_tool(audio_tool_fn)
          async with client_session(mcp._mcp_server) as client:
              result = await client.call_tool("audio_tool_fn", {"path": str(audio_path)})
              assert len(result.content) == 1
              content = result.content[0]
              assert isinstance(content, AudioContent)
              assert content.type == "audio"
              assert content.mimeType == "audio/wav"
              # Verify base64 encoding
              decoded = base64.b64decode(content.data)
              assert decoded == b"fake wav data"
              # Check structured content - Image return type should NOT have structured output
              assert result.structuredContent is None
  
      @pytest.mark.parametrize(
          "filename,expected_mime_type",
          [
              ("test.wav", "audio/wav"),
              ("test.mp3", "audio/mpeg"),
              ("test.ogg", "audio/ogg"),
              ("test.flac", "audio/flac"),
              ("test.aac", "audio/aac"),
              ("test.m4a", "audio/mp4"),
              ("test.unknown", "application/octet-stream"),  # Unknown extension fallback
          ],
      )
      @pytest.mark.anyio
      async def test_tool_audio_suffix_detection(self, tmp_path: Path, filename: str, expected_mime_type: str):
          """Test that Audio helper correctly detects MIME types from file suffixes"""
          mcp = FastMCP()
          mcp.add_tool(audio_tool_fn)
  
          # Create a test audio file with the specific extension
          audio_path = tmp_path / filename
          audio_path.write_bytes(b"fake audio data")
  
          async with client_session(mcp._mcp_server) as client:
              result = await client.call_tool("audio_tool_fn", {"path": str(audio_path)})
              assert len(result.content) == 1
              content = result.content[0]
              assert isinstance(content, AudioContent)
              assert content.type == "audio"
              assert content.mimeType == expected_mime_type
              # Verify base64 encoding
              decoded = base64.b64decode(content.data)
              assert decoded == b"fake audio data"
  
      @pytest.mark.anyio
      async def test_tool_mixed_content(self):
          mcp = FastMCP()
          mcp.add_tool(mixed_content_tool_fn)
          async with client_session(mcp._mcp_server) as client:
              result = await client.call_tool("mixed_content_tool_fn", {})
              assert len(result.content) == 3
              content1, content2, content3 = result.content
              assert isinstance(content1, TextContent)
              assert content1.text == "Hello"
              assert isinstance(content2, ImageContent)
              assert content2.mimeType == "image/png"
              assert content2.data == "abc"
              assert isinstance(content3, AudioContent)
              assert content3.mimeType == "audio/wav"
              assert content3.data == "def"
              assert result.structuredContent is not None
              assert "result" in result.structuredContent
              structured_result = result.structuredContent["result"]
              assert len(structured_result) == 3
  
              expected_content = [
                  {"type": "text", "text": "Hello"},
                  {"type": "image", "data": "abc", "mimeType": "image/png"},
                  {"type": "audio", "data": "def", "mimeType": "audio/wav"},
              ]
  
              for i, expected in enumerate(expected_content):
                  for key, value in expected.items():
                      assert structured_result[i][key] == value
  
      @pytest.mark.anyio
      async def test_tool_mixed_list_with_audio_and_image(self, tmp_path: Path):
          """Test that lists containing Image objects and other types are handled
          correctly"""
          # Create a test image
          image_path = tmp_path / "test.png"
          image_path.write_bytes(b"test image data")
  
          # Create a test audio
          audio_path = tmp_path / "test.wav"
          audio_path.write_bytes(b"test audio data")
  
          # TODO(Marcelo): It seems if we add the proper type hint, it generates an invalid JSON schema.
          # We need to fix this.
          def mixed_list_fn() -> list:  # type: ignore
              return [  # type: ignore
                  "text message",
                  Image(image_path),
                  Audio(audio_path),
                  {"key": "value"},
                  TextContent(type="text", text="direct content"),
              ]
  
          mcp = FastMCP()
          mcp.add_tool(mixed_list_fn)  # type: ignore
          async with client_session(mcp._mcp_server) as client:
              result = await client.call_tool("mixed_list_fn", {})
              assert len(result.content) == 5
              # Check text conversion
              content1 = result.content[0]
              assert isinstance(content1, TextContent)
              assert content1.text == "text message"
              # Check image conversion
              content2 = result.content[1]
              assert isinstance(content2, ImageContent)
              assert content2.mimeType == "image/png"
              assert base64.b64decode(content2.data) == b"test image data"
              # Check audio conversion
              content3 = result.content[2]
              assert isinstance(content3, AudioContent)
              assert content3.mimeType == "audio/wav"
              assert base64.b64decode(content3.data) == b"test audio data"
              # Check dict conversion
              content4 = result.content[3]
              assert isinstance(content4, TextContent)
              assert '"key": "value"' in content4.text
              # Check direct TextContent
              content5 = result.content[4]
              assert isinstance(content5, TextContent)
              assert content5.text == "direct content"
              # Check structured content - untyped list with Image objects should NOT have structured output
              assert result.structuredContent is None
  
      @pytest.mark.anyio
      async def test_tool_structured_output_basemodel(self):
          """Test tool with structured output returning BaseModel"""
  
          class UserOutput(BaseModel):
              name: str
              age: int
              active: bool = True
  
          def get_user(user_id: int) -> UserOutput:
              """Get user by ID"""
              return UserOutput(name="John Doe", age=30)
  
          mcp = FastMCP()
          mcp.add_tool(get_user)
  
          async with client_session(mcp._mcp_server) as client:
              # Check that the tool has outputSchema
              tools = await client.list_tools()
              tool = next(t for t in tools.tools if t.name == "get_user")
              assert tool.outputSchema is not None
              assert tool.outputSchema["type"] == "object"
              assert "name" in tool.outputSchema["properties"]
              assert "age" in tool.outputSchema["properties"]
  
              # Call the tool and check structured output
              result = await client.call_tool("get_user", {"user_id": 123})
              assert result.isError is False
              assert result.structuredContent is not None
              assert result.structuredContent == {"name": "John Doe", "age": 30, "active": True}
              # Content should be JSON serialized version
              assert len(result.content) == 1
              assert isinstance(result.content[0], TextContent)
              assert '"name": "John Doe"' in result.content[0].text
  
      @pytest.mark.anyio
      async def test_tool_structured_output_primitive(self):
          """Test tool with structured output returning primitive type"""
  
          def calculate_sum(a: int, b: int) -> int:
              """Add two numbers"""
              return a + b
  
          mcp = FastMCP()
          mcp.add_tool(calculate_sum)
  
          async with client_session(mcp._mcp_server) as client:
              # Check that the tool has outputSchema
              tools = await client.list_tools()
              tool = next(t for t in tools.tools if t.name == "calculate_sum")
              assert tool.outputSchema is not None
              # Primitive types are wrapped
              assert tool.outputSchema["type"] == "object"
              assert "result" in tool.outputSchema["properties"]
              assert tool.outputSchema["properties"]["result"]["type"] == "integer"
  
              # Call the tool
              result = await client.call_tool("calculate_sum", {"a": 5, "b": 7})
              assert result.isError is False
              assert result.structuredContent is not None
              assert result.structuredContent == {"result": 12}
  
      @pytest.mark.anyio
      async def test_tool_structured_output_list(self):
          """Test tool with structured output returning list"""
  
          def get_numbers() -> list[int]:
              """Get a list of numbers"""
              return [1, 2, 3, 4, 5]
  
          mcp = FastMCP()
          mcp.add_tool(get_numbers)
  
          async with client_session(mcp._mcp_server) as client:
              result = await client.call_tool("get_numbers", {})
              assert result.isError is False
              assert result.structuredContent is not None
              assert result.structuredContent == {"result": [1, 2, 3, 4, 5]}
  
      @pytest.mark.anyio
      async def test_tool_structured_output_server_side_validation_error(self):
          """Test that server-side validation errors are handled properly"""
  
          def get_numbers() -> list[int]:
              return [1, 2, 3, 4, [5]]  # type: ignore
  
          mcp = FastMCP()
          mcp.add_tool(get_numbers)
  
          async with client_session(mcp._mcp_server) as client:
              result = await client.call_tool("get_numbers", {})
              assert result.isError is True
              assert result.structuredContent is None
              assert len(result.content) == 1
              assert isinstance(result.content[0], TextContent)
  
      @pytest.mark.anyio
      async def test_tool_structured_output_dict_str_any(self):
          """Test tool with dict[str, Any] structured output"""
  
          def get_metadata() -> dict[str, Any]:
              """Get metadata dictionary"""
              return {
                  "version": "1.0.0",
                  "enabled": True,
                  "count": 42,
                  "tags": ["production", "stable"],
                  "config": {"nested": {"value": 123}},
              }
  
          mcp = FastMCP()
          mcp.add_tool(get_metadata)
  
          async with client_session(mcp._mcp_server) as client:
              # Check schema
              tools = await client.list_tools()
              tool = next(t for t in tools.tools if t.name == "get_metadata")
              assert tool.outputSchema is not None
              assert tool.outputSchema["type"] == "object"
              # dict[str, Any] should have minimal schema
              assert (
                  "additionalProperties" not in tool.outputSchema or tool.outputSchema.get("additionalProperties") is True
              )
  
              # Call tool
              result = await client.call_tool("get_metadata", {})
              assert result.isError is False
              assert result.structuredContent is not None
              expected = {
                  "version": "1.0.0",
                  "enabled": True,
                  "count": 42,
                  "tags": ["production", "stable"],
                  "config": {"nested": {"value": 123}},
              }
              assert result.structuredContent == expected
  
      @pytest.mark.anyio
      async def test_tool_structured_output_dict_str_typed(self):
          """Test tool with dict[str, T] structured output for specific T"""
  
          def get_settings() -> dict[str, str]:
              """Get settings as string dictionary"""
              return {"theme": "dark", "language": "en", "timezone": "UTC"}
  
          mcp = FastMCP()
          mcp.add_tool(get_settings)
  
          async with client_session(mcp._mcp_server) as client:
              # Check schema
              tools = await client.list_tools()
              tool = next(t for t in tools.tools if t.name == "get_settings")
              assert tool.outputSchema is not None
              assert tool.outputSchema["type"] == "object"
              assert tool.outputSchema["additionalProperties"]["type"] == "string"
  
              # Call tool
              result = await client.call_tool("get_settings", {})
              assert result.isError is False
              assert result.structuredContent == {"theme": "dark", "language": "en", "timezone": "UTC"}
  
      @pytest.mark.anyio
      async def test_remove_tool(self):
          """Test removing a tool from the server."""
          mcp = FastMCP()
          mcp.add_tool(tool_fn)
  
          # Verify tool exists
          assert len(mcp._tool_manager.list_tools()) == 1
  
          # Remove the tool
          mcp.remove_tool("tool_fn")
  
          # Verify tool is removed
          assert len(mcp._tool_manager.list_tools()) == 0
  
      @pytest.mark.anyio
      async def test_remove_nonexistent_tool(self):
          """Test that removing a non-existent tool raises ToolError."""
          from mcp.server.fastmcp.exceptions import ToolError
  
          mcp = FastMCP()
  
          with pytest.raises(ToolError, match="Unknown tool: nonexistent"):
              mcp.remove_tool("nonexistent")
  
      @pytest.mark.anyio
      async def test_remove_tool_and_list(self):
          """Test that a removed tool doesn't appear in list_tools."""
          mcp = FastMCP()
          mcp.add_tool(tool_fn)
          mcp.add_tool(error_tool_fn)
  
          # Verify both tools exist
          async with client_session(mcp._mcp_server) as client:
              tools = await client.list_tools()
              assert len(tools.tools) == 2
              tool_names = [t.name for t in tools.tools]
              assert "tool_fn" in tool_names
              assert "error_tool_fn" in tool_names
  
          # Remove one tool
          mcp.remove_tool("tool_fn")
  
          # Verify only one tool remains
          async with client_session(mcp._mcp_server) as client:
              tools = await client.list_tools()
              assert len(tools.tools) == 1
              assert tools.tools[0].name == "error_tool_fn"
  
      @pytest.mark.anyio
      async def test_remove_tool_and_call(self):
          """Test that calling a removed tool fails appropriately."""
          mcp = FastMCP()
          mcp.add_tool(tool_fn)
  
          # Verify tool works before removal
          async with client_session(mcp._mcp_server) as client:
              result = await client.call_tool("tool_fn", {"x": 1, "y": 2})
              assert not result.isError
              content = result.content[0]
              assert isinstance(content, TextContent)
              assert content.text == "3"
  
          # Remove the tool
          mcp.remove_tool("tool_fn")
  
          # Verify calling removed tool returns an error
          async with client_session(mcp._mcp_server) as client:
              result = await client.call_tool("tool_fn", {"x": 1, "y": 2})
              assert result.isError
              content = result.content[0]
              assert isinstance(content, TextContent)
              assert "Unknown tool" in content.text
  
  
  class TestServerResources:
      @pytest.mark.anyio
      async def test_text_resource(self):
          mcp = FastMCP()
  
          def get_text():
              return "Hello, world!"
  
          resource = FunctionResource(uri=AnyUrl("resource://test"), name="test", fn=get_text)
          mcp.add_resource(resource)
  
          async with client_session(mcp._mcp_server) as client:
              result = await client.read_resource(AnyUrl("resource://test"))
              assert isinstance(result.contents[0], TextResourceContents)
              assert result.contents[0].text == "Hello, world!"
  
      @pytest.mark.anyio
      async def test_binary_resource(self):
          mcp = FastMCP()
  
          def get_binary():
              return b"Binary data"
  
          resource = FunctionResource(
              uri=AnyUrl("resource://binary"),
              name="binary",
              fn=get_binary,
              mime_type="application/octet-stream",
          )
          mcp.add_resource(resource)
  
          async with client_session(mcp._mcp_server) as client:
              result = await client.read_resource(AnyUrl("resource://binary"))
              assert isinstance(result.contents[0], BlobResourceContents)
              assert result.contents[0].blob == base64.b64encode(b"Binary data").decode()
  
      @pytest.mark.anyio
      async def test_file_resource_text(self, tmp_path: Path):
          mcp = FastMCP()
  
          # Create a text file
          text_file = tmp_path / "test.txt"
          text_file.write_text("Hello from file!")
  
          resource = FileResource(uri=AnyUrl("file://test.txt"), name="test.txt", path=text_file)
          mcp.add_resource(resource)
  
          async with client_session(mcp._mcp_server) as client:
              result = await client.read_resource(AnyUrl("file://test.txt"))
              assert isinstance(result.contents[0], TextResourceContents)
              assert result.contents[0].text == "Hello from file!"
  
      @pytest.mark.anyio
      async def test_file_resource_binary(self, tmp_path: Path):
          mcp = FastMCP()
  
          # Create a binary file
          binary_file = tmp_path / "test.bin"
          binary_file.write_bytes(b"Binary file data")
  
          resource = FileResource(
              uri=AnyUrl("file://test.bin"),
              name="test.bin",
              path=binary_file,
              mime_type="application/octet-stream",
          )
          mcp.add_resource(resource)
  
          async with client_session(mcp._mcp_server) as client:
              result = await client.read_resource(AnyUrl("file://test.bin"))
              assert isinstance(result.contents[0], BlobResourceContents)
              assert result.contents[0].blob == base64.b64encode(b"Binary file data").decode()
  
      @pytest.mark.anyio
      async def test_function_resource(self):
          mcp = FastMCP()
  
          @mcp.resource("function://test", name="test_get_data")
          def get_data() -> str:  # pragma: no cover
              """get_data returns a string"""
              return "Hello, world!"
  
          async with client_session(mcp._mcp_server) as client:
              resources = await client.list_resources()
              assert len(resources.resources) == 1
              resource = resources.resources[0]
              assert resource.description == "get_data returns a string"
              assert resource.uri == AnyUrl("function://test")
              assert resource.name == "test_get_data"
              assert resource.mimeType == "text/plain"
  
  
  class TestServerResourceTemplates:
      @pytest.mark.anyio
      async def test_resource_with_params(self):
          """Test that a resource with function parameters raises an error if the URI
          parameters don't match"""
          mcp = FastMCP()
  
          with pytest.raises(ValueError, match="Mismatch between URI parameters"):
  
  