"""Tests for MCP server upgrade to protocol version 2025-11-25.""" from __future__ import annotations import pytest from openjarvis.mcp.protocol import MCPRequest from openjarvis.mcp.server import MCPServer from openjarvis.tools.calculator import CalculatorTool from openjarvis.tools.think import ThinkTool @pytest.fixture def server(): return MCPServer([CalculatorTool(), ThinkTool()]) class TestProtocolVersion: def test_protocol_version_2025(self, server): assert server.PROTOCOL_VERSION == "2025-11-25" def test_initialize_returns_2025_version(self, server): req = MCPRequest(method="initialize", id=1) resp = server.handle(req) assert resp.result["protocolVersion"] == "2025-11-25" class TestServerTitle: def test_initialize_includes_title(self, server): req = MCPRequest(method="initialize", id=1) resp = server.handle(req) server_info = resp.result["serverInfo"] assert "title" in server_info assert server_info["title"] == "OpenJarvis Tool Server" class TestListChanged: def test_tools_capability_list_changed_true(self, server): req = MCPRequest(method="initialize", id=1) resp = server.handle(req) caps = resp.result["capabilities"] assert caps["tools"]["listChanged"] is True class TestAnnotations: def test_calculator_has_read_only_annotation(self, server): req = MCPRequest(method="tools/list", id=1) resp = server.handle(req) tools = resp.result["tools"] calc = next(t for t in tools if t["name"] == "calculator") assert "annotations" in calc assert calc["annotations"]["readOnlyHint"] is True assert calc["annotations"]["destructiveHint"] is False def test_think_has_read_only_annotation(self, server): req = MCPRequest(method="tools/list", id=1) resp = server.handle(req) tools = resp.result["tools"] think = next(t for t in tools if t["name"] == "think") assert "annotations" in think assert think["annotations"]["readOnlyHint"] is True class TestAutoDiscovery: def test_auto_discover_creates_server(self): """MCPServer() with no tools arg should auto-discover.""" server = MCPServer() req = MCPRequest(method="tools/list", id=1) resp = server.handle(req) # Should have at least calculator and think (via direct import) names = {t["name"] for t in resp.result["tools"]} assert "calculator" in names assert "think" in names def test_auto_discover_includes_storage_tools(self): """Auto-discovered server should include storage tools.""" server = MCPServer() req = MCPRequest(method="tools/list", id=1) resp = server.handle(req) names = {t["name"] for t in resp.result["tools"]} assert "memory_store" in names assert "memory_retrieve" in names assert "memory_search" in names assert "memory_index" in names def test_auto_discover_tool_count(self): """Auto-discovered server should have all built-in tools.""" server = MCPServer() req = MCPRequest(method="tools/list", id=1) resp = server.handle(req) # At least: calculator, think, file_read, web_search, # code_interpreter, memory_store/retrieve/search/index, # llm, retrieval assert len(resp.result["tools"]) >= 8 def test_explicit_tools_override_auto_discover(self): """Passing explicit tools should NOT auto-discover.""" server = MCPServer([CalculatorTool()]) req = MCPRequest(method="tools/list", id=1) resp = server.handle(req) tools = resp.result["tools"] assert len(tools) == 1 assert tools[0]["name"] == "calculator" def test_empty_list_means_no_tools(self): """Passing empty list should give empty tools (not auto-discover).""" server = MCPServer([]) req = MCPRequest(method="tools/list", id=1) resp = server.handle(req) assert resp.result["tools"] == [] class TestStorageToolAnnotations: def test_memory_store_destructive(self): from openjarvis.tools.storage_tools import MemoryStoreTool server = MCPServer([MemoryStoreTool()]) req = MCPRequest(method="tools/list", id=1) resp = server.handle(req) tool = resp.result["tools"][0] assert tool["annotations"]["destructiveHint"] is True assert tool["annotations"]["readOnlyHint"] is False def test_memory_retrieve_read_only(self): from openjarvis.tools.storage_tools import MemoryRetrieveTool server = MCPServer([MemoryRetrieveTool()]) req = MCPRequest(method="tools/list", id=1) resp = server.handle(req) tool = resp.result["tools"][0] assert tool["annotations"]["readOnlyHint"] is True assert tool["annotations"]["destructiveHint"] is False