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