1+ #!/usr/bin/env python3
2+ """
3+ MCP Protocol Integration Tests
4+
5+ Validates JSON-RPC 2.0 protocol compliance and MCP server behavior.
6+ """
7+
8+ import sys
9+ import json
10+ import subprocess
11+ import time
12+ from pathlib import Path
13+
14+ def test_mcp_server_startup ():
15+ """Test MCP server starts and responds to initialization"""
16+ print ("🧪 Testing MCP server startup..." )
17+
18+ try :
19+ # Start MCP server
20+ mcp_cmd = ["/workspaces/openfoam-mcp-server/build/openfoam-mcp-server" ]
21+ process = subprocess .Popen (
22+ mcp_cmd ,
23+ stdin = subprocess .PIPE ,
24+ stdout = subprocess .PIPE ,
25+ stderr = subprocess .PIPE ,
26+ text = True
27+ )
28+
29+ # Send initialize request
30+ init_request = {
31+ "jsonrpc" : "2.0" ,
32+ "id" : 1 ,
33+ "method" : "initialize" ,
34+ "params" : {
35+ "protocolVersion" : "2024-11-05" ,
36+ "capabilities" : {
37+ "tools" : {}
38+ },
39+ "clientInfo" : {
40+ "name" : "test-client" ,
41+ "version" : "1.0.0"
42+ }
43+ }
44+ }
45+
46+ stdout , stderr = process .communicate (json .dumps (init_request ), timeout = 10 )
47+
48+ if process .returncode != 0 :
49+ print (f" ❌ Server failed to start: { stderr } " )
50+ return False
51+
52+ # Parse response
53+ response = json .loads (stdout )
54+
55+ # Validate response structure
56+ assert "jsonrpc" in response , "Missing jsonrpc field"
57+ assert response ["jsonrpc" ] == "2.0" , "Invalid jsonrpc version"
58+ assert "id" in response , "Missing id field"
59+ assert response ["id" ] == 1 , "Invalid response id"
60+ assert "result" in response , "Missing result field"
61+
62+ # Validate server capabilities
63+ result = response ["result" ]
64+ assert "capabilities" in result , "Missing capabilities"
65+ assert "tools" in result ["capabilities" ], "Missing tools capability"
66+
67+ print (" ✅ MCP server startup test passed" )
68+ return True
69+
70+ except subprocess .TimeoutExpired :
71+ print (" ❌ Server startup timeout" )
72+ return False
73+ except Exception as e :
74+ print (f" ❌ Server startup failed: { e } " )
75+ return False
76+
77+ def test_tools_list ():
78+ """Test tools/list endpoint returns available tools"""
79+ print ("🧪 Testing tools/list endpoint..." )
80+
81+ try :
82+ # Build the MCP server if not already built
83+ build_cmd = ["cmake" , "--build" , "/workspaces/openfoam-mcp-server/build" ]
84+ subprocess .run (build_cmd , check = True , capture_output = True )
85+
86+ # Create JSON-RPC request
87+ request = {
88+ "jsonrpc" : "2.0" ,
89+ "id" : 1 ,
90+ "method" : "tools/list" ,
91+ "params" : {}
92+ }
93+
94+ # Run MCP server
95+ mcp_cmd = ["/workspaces/openfoam-mcp-server/build/openfoam-mcp-server" ]
96+ process = subprocess .Popen (
97+ mcp_cmd ,
98+ stdin = subprocess .PIPE ,
99+ stdout = subprocess .PIPE ,
100+ stderr = subprocess .PIPE ,
101+ text = True
102+ )
103+
104+ # Send request and get response
105+ stdout , stderr = process .communicate (json .dumps (request ), timeout = 10 )
106+
107+ if process .returncode != 0 :
108+ print (f" ❌ Server failed: { stderr } " )
109+ return False
110+
111+ # Parse response
112+ response = json .loads (stdout )
113+
114+ # Validate response structure
115+ assert "jsonrpc" in response , "Missing jsonrpc field"
116+ assert response ["jsonrpc" ] == "2.0" , "Invalid jsonrpc version"
117+ assert "id" in response , "Missing id field"
118+ assert "result" in response , "Missing result field"
119+
120+ # Validate tools list
121+ result = response ["result" ]
122+ assert "tools" in result , "Missing tools array"
123+ tools = result ["tools" ]
124+
125+ # Expected tools
126+ expected_tools = [
127+ "analyze_pipe_flow" ,
128+ "analyze_heat_transfer" ,
129+ "analyze_external_flow"
130+ ]
131+
132+ tool_names = [tool ["name" ] for tool in tools ]
133+
134+ for expected_tool in expected_tools :
135+ assert expected_tool in tool_names , f"Missing tool: { expected_tool } "
136+
137+ print (f" ✅ Found { len (tools )} tools: { tool_names } " )
138+ return True
139+
140+ except subprocess .TimeoutExpired :
141+ print (" ❌ Tools list timeout" )
142+ return False
143+ except Exception as e :
144+ print (f" ❌ Tools list failed: { e } " )
145+ return False
146+
147+ def test_pipe_flow_tool ():
148+ """Test pipe flow tool execution"""
149+ print ("🧪 Testing pipe flow tool..." )
150+
151+ try :
152+ # Build the MCP server if not already built
153+ build_cmd = ["cmake" , "--build" , "/workspaces/openfoam-mcp-server/build" ]
154+ subprocess .run (build_cmd , check = True , capture_output = True )
155+
156+ # Create JSON-RPC request
157+ request = {
158+ "jsonrpc" : "2.0" ,
159+ "id" : 1 ,
160+ "method" : "tools/call" ,
161+ "params" : {
162+ "name" : "analyze_pipe_flow" ,
163+ "arguments" : {
164+ "diameter" : 0.1 ,
165+ "length" : 1.0 ,
166+ "velocity" : 1.0 ,
167+ "fluid" : "water" ,
168+ "temperature" : 298.15 ,
169+ "roughness" : 0.000045
170+ }
171+ }
172+ }
173+
174+ # Run MCP server
175+ mcp_cmd = ["/workspaces/openfoam-mcp-server/build/openfoam-mcp-server" ]
176+ process = subprocess .Popen (
177+ mcp_cmd ,
178+ stdin = subprocess .PIPE ,
179+ stdout = subprocess .PIPE ,
180+ stderr = subprocess .PIPE ,
181+ text = True
182+ )
183+
184+ # Send request and get response
185+ stdout , stderr = process .communicate (json .dumps (request ), timeout = 30 )
186+
187+ if process .returncode != 0 :
188+ print (f" ❌ Tool execution failed: { stderr } " )
189+ return False
190+
191+ # Parse response
192+ response = json .loads (stdout )
193+
194+ # Validate response structure
195+ assert "jsonrpc" in response , "Missing jsonrpc field"
196+ assert response ["jsonrpc" ] == "2.0" , "Invalid jsonrpc version"
197+ assert "id" in response , "Missing id field"
198+ assert "result" in response , "Missing result field"
199+
200+ # Validate tool result
201+ result = response ["result" ]
202+ assert "content" in result , "Missing content array"
203+
204+ content = result ["content" ]
205+ assert len (content ) > 0 , "Empty content array"
206+
207+ # Check for expected result structure
208+ found_resource = False
209+ for item in content :
210+ if item .get ("type" ) == "resource" :
211+ found_resource = True
212+ break
213+
214+ assert found_resource , "No resource content found"
215+
216+ print (" ✅ Pipe flow tool test passed" )
217+ return True
218+
219+ except subprocess .TimeoutExpired :
220+ print (" ❌ Pipe flow tool timeout" )
221+ return False
222+ except Exception as e :
223+ print (f" ❌ Pipe flow tool failed: { e } " )
224+ return False
225+
226+ def test_invalid_request ():
227+ """Test server handles invalid JSON-RPC requests properly"""
228+ print ("🧪 Testing invalid request handling..." )
229+
230+ try :
231+ # Build the MCP server if not already built
232+ build_cmd = ["cmake" , "--build" , "/workspaces/openfoam-mcp-server/build" ]
233+ subprocess .run (build_cmd , check = True , capture_output = True )
234+
235+ # Create invalid JSON-RPC request (missing jsonrpc field)
236+ request = {
237+ "id" : 1 ,
238+ "method" : "tools/call" ,
239+ "params" : {
240+ "name" : "nonexistent_tool" ,
241+ "arguments" : {}
242+ }
243+ }
244+
245+ # Run MCP server
246+ mcp_cmd = ["/workspaces/openfoam-mcp-server/build/openfoam-mcp-server" ]
247+ process = subprocess .Popen (
248+ mcp_cmd ,
249+ stdin = subprocess .PIPE ,
250+ stdout = subprocess .PIPE ,
251+ stderr = subprocess .PIPE ,
252+ text = True
253+ )
254+
255+ # Send request and get response
256+ stdout , stderr = process .communicate (json .dumps (request ), timeout = 10 )
257+
258+ # Parse response
259+ response = json .loads (stdout )
260+
261+ # Should return error response
262+ assert "jsonrpc" in response , "Missing jsonrpc field"
263+ assert response ["jsonrpc" ] == "2.0" , "Invalid jsonrpc version"
264+ assert "id" in response , "Missing id field"
265+ assert "error" in response , "Missing error field for invalid request"
266+
267+ error = response ["error" ]
268+ assert "code" in error , "Missing error code"
269+ assert "message" in error , "Missing error message"
270+
271+ print (" ✅ Invalid request handling test passed" )
272+ return True
273+
274+ except subprocess .TimeoutExpired :
275+ print (" ❌ Invalid request test timeout" )
276+ return False
277+ except Exception as e :
278+ print (f" ❌ Invalid request test failed: { e } " )
279+ return False
280+
281+ def main ():
282+ """Run all MCP protocol integration tests"""
283+ print ("🔗 OpenFOAM MCP Server - Protocol Integration Tests" )
284+ print ("=" * 60 )
285+
286+ # Test suite
287+ tests = [
288+ ("MCP Server Startup" , test_mcp_server_startup ),
289+ ("Tools List" , test_tools_list ),
290+ ("Pipe Flow Tool" , test_pipe_flow_tool ),
291+ ("Invalid Request" , test_invalid_request )
292+ ]
293+
294+ # Run tests
295+ passed = 0
296+ total = len (tests )
297+
298+ for test_name , test_func in tests :
299+ try :
300+ if test_func ():
301+ passed += 1
302+ except Exception as e :
303+ print (f" ❌ { test_name } failed with exception: { e } " )
304+
305+ # Results
306+ print (f"\n 📊 Integration Test Results: { passed } /{ total } passed" )
307+
308+ if passed == total :
309+ print ("✅ All MCP protocol integration tests passed!" )
310+ return 0
311+ else :
312+ print (f"❌ { total - passed } integration tests failed!" )
313+ return 1
314+
315+ if __name__ == "__main__" :
316+ sys .exit (main ())
0 commit comments