1+ #!/usr/bin/env python3
2+ """
3+ Test script for tool versioning functionality.
4+
5+ This script demonstrates the new tool versioning features implemented according to SEP-1575.
6+ """
7+
8+ import asyncio
9+ from mcp .server .fastmcp import FastMCP
10+ from mcp .server .fastmcp .utilities .versioning import (
11+ parse_version ,
12+ compare_versions ,
13+ satisfies_constraint ,
14+ find_best_version ,
15+ validate_tool_requirements ,
16+ VersionConstraintError ,
17+ )
18+
19+
20+ # Test version parsing and comparison
21+ def test_version_parsing ():
22+ """Test version parsing functionality."""
23+ print ("Testing version parsing..." )
24+
25+ # Test valid versions
26+ assert parse_version ("1.2.3" ) == (1 , 2 , 3 , None )
27+ assert parse_version ("2.0.0-alpha.1" ) == (2 , 0 , 0 , "alpha.1" )
28+ assert parse_version ("0.1.0-beta" ) == (0 , 1 , 0 , "beta" )
29+
30+ # Test version comparison
31+ assert compare_versions ("1.2.3" , "1.2.4" ) == - 1
32+ assert compare_versions ("2.0.0" , "1.9.9" ) == 1
33+ assert compare_versions ("1.2.3" , "1.2.3" ) == 0
34+ assert compare_versions ("1.2.3" , "1.2.3-alpha" ) == 1 # Stable > prerelease
35+
36+ print ("✓ Version parsing tests passed" )
37+
38+
39+ def test_constraint_satisfaction ():
40+ """Test constraint satisfaction functionality."""
41+ print ("Testing constraint satisfaction..." )
42+
43+ # Test exact version
44+ assert satisfies_constraint ("1.2.3" , "1.2.3" ) == True
45+ assert satisfies_constraint ("1.2.4" , "1.2.3" ) == False
46+
47+ # Test caret (^) - allows non-breaking updates
48+ assert satisfies_constraint ("1.2.3" , "^1.2.3" ) == True
49+ assert satisfies_constraint ("1.3.0" , "^1.2.3" ) == True
50+ assert satisfies_constraint ("2.0.0" , "^1.2.3" ) == False
51+
52+ # Test tilde (~) - allows patch-level updates
53+ assert satisfies_constraint ("1.2.3" , "~1.2.3" ) == True
54+ assert satisfies_constraint ("1.2.4" , "~1.2.3" ) == True
55+ assert satisfies_constraint ("1.3.0" , "~1.2.3" ) == False
56+
57+ # Test comparison operators
58+ assert satisfies_constraint ("1.2.3" , ">=1.2.0" ) == True
59+ assert satisfies_constraint ("1.1.9" , ">=1.2.0" ) == False
60+ assert satisfies_constraint ("1.2.3" , "<1.3.0" ) == True
61+ assert satisfies_constraint ("1.3.0" , "<1.3.0" ) == False
62+
63+ print ("✓ Constraint satisfaction tests passed" )
64+
65+
66+ def test_version_selection ():
67+ """Test best version selection."""
68+ print ("Testing version selection..." )
69+
70+ available_versions = ["1.0.0" , "1.1.0" , "1.2.0" , "2.0.0-alpha.1" , "2.0.0" ]
71+
72+ # Test caret constraint
73+ best = find_best_version (available_versions , "^1.0.0" )
74+ assert best == "1.2.0" # Latest in 1.x range
75+
76+ # Test tilde constraint
77+ best = find_best_version (available_versions , "~1.1.0" )
78+ assert best == "1.1.0" # Exact match for patch level
79+
80+ # Test exact version
81+ best = find_best_version (available_versions , "2.0.0" )
82+ assert best == "2.0.0"
83+
84+ # Test no match
85+ best = find_best_version (available_versions , "^3.0.0" )
86+ assert best is None
87+
88+ print ("✓ Version selection tests passed" )
89+
90+
91+ def test_tool_requirements_validation ():
92+ """Test tool requirements validation."""
93+ print ("Testing tool requirements validation..." )
94+
95+ available_tools = {
96+ "weather" : ["1.0.0" , "1.1.0" , "2.0.0" ],
97+ "calculator" : ["1.0.0" , "1.0.1" , "1.1.0" ],
98+ }
99+
100+ # Test valid requirements
101+ requirements = {
102+ "weather" : "^1.0.0" ,
103+ "calculator" : "~1.0.0"
104+ }
105+
106+ selected = validate_tool_requirements (requirements , available_tools )
107+ assert selected ["weather" ] == "1.1.0" # Latest in 1.x range
108+ assert selected ["calculator" ] == "1.0.1" # Latest patch in 1.0.x range
109+
110+ # Test unsatisfied requirement
111+ requirements = {
112+ "weather" : "^3.0.0"
113+ }
114+
115+ try :
116+ validate_tool_requirements (requirements , available_tools )
117+ assert False , "Should have raised VersionConstraintError"
118+ except VersionConstraintError :
119+ pass # Expected
120+
121+ print ("✓ Tool requirements validation tests passed" )
122+
123+
124+ # Create a simple FastMCP server with versioned tools
125+ def create_test_server ():
126+ """Create a test server with versioned tools."""
127+ server = FastMCP ("test-server" )
128+
129+ def get_weather_v1 (location : str ) -> str :
130+ """Get weather for a location (v1)."""
131+ return f"Weather in { location } : Sunny, 72°F (v1.0.0)"
132+
133+ def get_weather_v1_1 (location : str ) -> str :
134+ """Get weather for a location (v1.1)."""
135+ return f"Weather in { location } : Partly cloudy, 75°F (v1.1.0)"
136+
137+ def get_weather_v2 (location : str ) -> str :
138+ """Get weather for a location (v2)."""
139+ return f"Weather in { location } : Clear skies, 78°F (v2.0.0)"
140+
141+ def calculate_v1 (expression : str ) -> float :
142+ """Calculate a simple expression (v1)."""
143+ return eval (expression ) # Simple implementation for demo
144+
145+ server .add_tool (get_weather_v1 , version = "1.0.0" )
146+ server .add_tool (get_weather_v1_1 , version = "1.1.0" )
147+ server .add_tool (get_weather_v2 , version = "2.0.0" )
148+ server .add_tool (calculate_v1 , version = "1.0.0" )
149+
150+ return server
151+
152+
153+ async def test_server_versioning ():
154+ """Test server versioning functionality."""
155+ print ("Testing server versioning..." )
156+
157+ server = create_test_server ()
158+
159+ # Test listing tools (should show latest versions)
160+ tools = server ._tool_manager .list_tools ()
161+ tool_names = [t .name for t in tools ]
162+ print (f"Available tools: { tool_names } " )
163+ assert "get_weather_v1" in tool_names
164+ assert "calculate_v1" in tool_names
165+
166+ # Test getting specific version
167+ weather_v1 = server ._tool_manager .get_tool ("get_weather_v1" , "1.0.0" )
168+ assert weather_v1 is not None
169+ assert weather_v1 .version == "1.0.0"
170+
171+ # Test getting latest version
172+ weather_latest = server ._tool_manager .get_tool ("get_weather_v1" )
173+ assert weather_latest is not None
174+ assert weather_latest .version == "1.0.0" # Only one version for this tool
175+
176+ # Test available versions
177+ versions = server ._tool_manager .get_available_versions ("get_weather_v1" )
178+ assert "1.0.0" in versions
179+
180+ print ("✓ Server versioning tests passed" )
181+
182+
183+ async def main ():
184+ """Run all tests."""
185+ print ("Running tool versioning tests...\n " )
186+
187+ test_version_parsing ()
188+ print ()
189+
190+ test_constraint_satisfaction ()
191+ print ()
192+
193+ test_version_selection ()
194+ print ()
195+
196+ test_tool_requirements_validation ()
197+ print ()
198+
199+ await test_server_versioning ()
200+ print ()
201+
202+ print ("🎉 All tests passed! Tool versioning implementation is working correctly." )
203+
204+
205+ if __name__ == "__main__" :
206+ asyncio .run (main ())
0 commit comments