forked from Mvp2o-ai/mcp-ide-bridge
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtest_mcp_client.py
More file actions
230 lines (196 loc) Β· 9.92 KB
/
test_mcp_client.py
File metadata and controls
230 lines (196 loc) Β· 9.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#!/usr/bin/env python3
"""
MCP Test Harness - A client for testing MCP tools
Usage:
python test_mcp_client.py get_my_identity
python test_mcp_client.py checkin_client --client_id "test-client" --name "Test Client"
python test_mcp_client.py send_message_and_wait --sender_id "test" --recipient_id "target" --message "Hello"
python test_mcp_client.py get_messages --client_id "test-client"
python test_mcp_client.py get_active_sessions
"""
import json
import argparse
import sys
import requests
from typing import Dict, Any, Optional
class MCPTestClient:
def __init__(self, base_url: str = "http://localhost:8111"):
self.base_url = base_url
self.session = requests.Session()
# Set required headers for MCP streamable HTTP
self.session.headers.update({
"Content-Type": "application/json",
"Accept": "application/json, text/event-stream"
})
def call_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
"""Call an MCP tool and return the parsed response."""
payload = {
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": tool_name,
"arguments": arguments
}
}
print(f"π§ Calling tool: {tool_name}")
print(f"π¦ Arguments: {json.dumps(arguments, indent=2)}")
print(f"π URL: {self.base_url}/mcp/")
print("=" * 50)
try:
response = self.session.post(f"{self.base_url}/mcp/", json=payload)
print(f"π Status Code: {response.status_code}")
print(f"π Headers: {dict(response.headers)}")
print("=" * 50)
if response.status_code == 200:
# Handle streaming response
response_text = response.text
print(f"π Raw Response:\n{response_text}")
print("=" * 50)
# Parse the streaming response
if "event: message" in response_text and "data: " in response_text:
# Extract JSON from SSE format - handle both \n and \r\n
lines = response_text.replace('\r\n', '\n').split('\n')
for i, line in enumerate(lines):
if line.startswith("data: "):
json_data = line[6:].strip() # Remove "data: " prefix
try:
parsed = json.loads(json_data)
return parsed
except json.JSONDecodeError as e:
print(f"β JSON parsing error: {e}")
return {"error": f"JSON parsing failed: {e}", "raw": response_text}
else:
# Try parsing as direct JSON
try:
return response.json()
except json.JSONDecodeError:
return {"error": "Non-JSON response", "raw": response_text}
else:
try:
error_data = response.json()
return {"error": f"HTTP {response.status_code}", "details": error_data}
except json.JSONDecodeError:
return {"error": f"HTTP {response.status_code}", "raw": response.text}
except requests.exceptions.ConnectionError:
return {"error": "β Connection failed - is the MCP server running on port 8111?"}
except requests.exceptions.Timeout:
return {"error": "β Request timed out - server may be overloaded"}
except requests.exceptions.RequestException as e:
return {"error": f"β Request failed: {e}"}
def format_result(self, result: Dict[str, Any]) -> str:
"""Format the MCP tool result for display."""
if "error" in result:
return f"β Error: {result['error']}\nDetails: {result.get('details', result.get('raw', 'No details'))}"
if "result" in result:
tool_result = result["result"]
if isinstance(tool_result, dict) and "content" in tool_result:
# Extract text content from MCP response
content = tool_result["content"]
if isinstance(content, list) and len(content) > 0:
if isinstance(content[0], dict) and "text" in content[0]:
return content[0]["text"]
return str(content)
return str(tool_result)
return str(result)
def main():
parser = argparse.ArgumentParser(description="MCP Test Harness")
parser.add_argument("tool_name", help="Name of the MCP tool to call")
parser.add_argument("--url", default="http://localhost:8111", help="MCP server URL")
# Tool-specific arguments
parser.add_argument("--client_id", help="Client ID parameter")
parser.add_argument("--sender_id", help="Sender ID parameter")
parser.add_argument("--recipient_id", help="Recipient ID parameter")
parser.add_argument("--name", help="Name parameter")
parser.add_argument("--capabilities", help="Capabilities parameter")
parser.add_argument("--message", help="Message parameter")
parser.add_argument("--expectation", help="Expectation parameter")
parser.add_argument("--random_string", help="Random string parameter")
# Generic argument support
parser.add_argument("--args", help="JSON string of arguments")
args = parser.parse_args()
# Build arguments dict
arguments = {}
# Add non-None arguments
if args.client_id:
arguments["client_id"] = args.client_id
if args.sender_id:
arguments["sender_id"] = args.sender_id
if args.recipient_id:
arguments["recipient_id"] = args.recipient_id
if args.name:
arguments["name"] = args.name
if args.capabilities:
arguments["capabilities"] = args.capabilities
if args.message:
arguments["message"] = args.message
if args.expectation:
arguments["expectation"] = args.expectation
if args.random_string:
arguments["random_string"] = args.random_string
# Override with JSON args if provided
if args.args:
try:
json_args = json.loads(args.args)
arguments.update(json_args)
except json.JSONDecodeError as e:
print(f"β Invalid JSON in --args: {e}")
sys.exit(1)
# Some tools need default arguments
if args.tool_name in ["get_my_identity", "get_active_sessions"] and not arguments:
arguments["random_string"] = "test"
# Validate required arguments for specific tools
validation_errors = []
if args.tool_name == "checkin_client":
if not arguments.get("client_id"):
validation_errors.append("--client_id is required for checkin_client")
if not arguments.get("name"):
validation_errors.append("--name is required for checkin_client")
elif args.tool_name == "send_message_and_wait":
if not arguments.get("sender_id"):
validation_errors.append("--sender_id is required for send_message_and_wait")
if not arguments.get("recipient_id"):
validation_errors.append("--recipient_id is required for send_message_and_wait")
if not arguments.get("message"):
validation_errors.append("--message is required for send_message_and_wait")
elif args.tool_name == "send_message_without_waiting":
if not arguments.get("sender_id"):
validation_errors.append("--sender_id is required for send_message_without_waiting")
if not arguments.get("recipient_ids"):
validation_errors.append("--args with recipient_ids array is required for send_message_without_waiting")
if not arguments.get("messages"):
validation_errors.append("--args with messages array is required for send_message_without_waiting")
elif args.tool_name == "get_messages":
if not arguments.get("client_id"):
validation_errors.append("--client_id is required for get_messages")
if validation_errors:
print("β Validation Errors:")
for error in validation_errors:
print(f" β’ {error}")
print(f"\nπ‘ Example usage for {args.tool_name}:")
if args.tool_name == "checkin_client":
print(" python test_mcp_client.py checkin_client --client_id \"mcp-ide-bridge\" --name \"Test Client\"")
elif args.tool_name == "send_message_and_wait":
print(" python test_mcp_client.py send_message_and_wait --sender_id \"mcp-ide-bridge\" --recipient_id \"miles_mcp_server\" --message \"Hello\"")
elif args.tool_name == "send_message_without_waiting":
print(" # Broadcast same message:")
print(" python test_mcp_client.py send_message_without_waiting --sender_id \"mcp-ide-bridge\" --args '{\"recipient_ids\": [\"miles_mcp_server\", \"dyson_frontend\"], \"messages\": [\"Hello everyone!\"]}'")
print(" # Different messages:")
print(" python test_mcp_client.py send_message_without_waiting --sender_id \"mcp-ide-bridge\" --args '{\"recipient_ids\": [\"miles_mcp_server\", \"dyson_frontend\"], \"messages\": [\"Hi Miles\", \"Hi Dyson\"]}'")
elif args.tool_name == "get_messages":
print(" python test_mcp_client.py get_messages --client_id \"mcp-ide-bridge\"")
sys.exit(1)
# Create client and call tool
client = MCPTestClient(args.url)
result = client.call_tool(args.tool_name, arguments)
# Display formatted result
print("π― RESULT:")
print("=" * 50)
formatted = client.format_result(result)
print(formatted)
# Also show raw JSON for debugging
print("\nπ RAW JSON:")
print("=" * 50)
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()