Skip to content

Commit 2b7e74b

Browse files
committed
fix: sniffer
1 parent 5c1db44 commit 2b7e74b

17 files changed

+1294
-232
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Traffic Generation Examples
2+
3+
This directory contains example servers and clients for generating MCP traffic to test MCPHawk.
4+
5+
## TCP-based MCP
6+
7+
### Server
8+
```bash
9+
python3 tcp_server.py
10+
```
11+
Starts a TCP MCP server on port 12345.
12+
13+
### Client
14+
```bash
15+
python3 tcp_client.py
16+
```
17+
Sends various MCP messages to the TCP server.
18+
19+
## WebSocket-based MCP
20+
21+
### Server
22+
```bash
23+
python3 ws_server.py
24+
```
25+
Starts a WebSocket MCP server on port 8765.
26+
27+
### Client
28+
```bash
29+
python3 ws_client.py
30+
```
31+
Sends various MCP messages to the WebSocket server.
32+
33+
## Generate All Traffic
34+
35+
To generate both TCP and WebSocket traffic for testing:
36+
37+
```bash
38+
python3 generate_all.py
39+
```
40+
41+
This will:
42+
1. Start both TCP and WebSocket servers
43+
2. Send a variety of MCP messages to each
44+
3. Display the traffic being generated
45+
4. Clean up when done
46+
47+
## Testing with MCPHawk
48+
49+
In another terminal, run MCPHawk to capture the traffic:
50+
51+
```bash
52+
# Capture TCP traffic
53+
sudo mcphawk sniff --port 12345
54+
55+
# Capture WebSocket traffic
56+
sudo mcphawk sniff --port 8765
57+
58+
# Capture both
59+
sudo mcphawk sniff --filter "tcp port 12345 or tcp port 8765"
60+
61+
# Or use auto-detect
62+
sudo mcphawk sniff --auto-detect
63+
```
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#!/usr/bin/env python3
2+
"""Generate both TCP and WebSocket MCP traffic for testing MCPHawk."""
3+
4+
import asyncio
5+
import contextlib
6+
import json
7+
import logging
8+
import socket
9+
import subprocess
10+
import sys
11+
import time
12+
from pathlib import Path
13+
14+
import websockets
15+
16+
logging.basicConfig(
17+
level=logging.INFO,
18+
format='%(asctime)s [%(levelname)s] %(message)s'
19+
)
20+
logger = logging.getLogger(__name__)
21+
22+
23+
def run_tcp_server():
24+
"""Run the TCP server in a subprocess."""
25+
script_path = Path(__file__).parent / "tcp_server.py"
26+
return subprocess.Popen([sys.executable, str(script_path)])
27+
28+
29+
def run_ws_server():
30+
"""Run the WebSocket server in a subprocess."""
31+
script_path = Path(__file__).parent / "ws_server.py"
32+
return subprocess.Popen([sys.executable, str(script_path)])
33+
34+
35+
def send_tcp_traffic():
36+
"""Send TCP MCP traffic."""
37+
logger.info("Sending TCP traffic...")
38+
39+
try:
40+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
41+
sock.connect(('localhost', 12345))
42+
43+
messages = [
44+
{"jsonrpc": "2.0", "method": "initialize", "params": {"protocolVersion": "2024-11-05"}, "id": 1},
45+
{"jsonrpc": "2.0", "method": "tools/list", "id": 2},
46+
{"jsonrpc": "2.0", "method": "notifications/tcp_test", "params": {"source": "tcp"}},
47+
{"jsonrpc": "2.0", "method": "tools/call", "params": {"name": "test_tool", "arguments": {}}, "id": 3},
48+
]
49+
50+
for msg in messages:
51+
data = json.dumps(msg).encode('utf-8')
52+
sock.sendall(data)
53+
logger.info(f" TCP sent: {msg.get('method')}")
54+
55+
# Read response if it has an ID
56+
if "id" in msg:
57+
try:
58+
response = sock.recv(1024)
59+
if response:
60+
logger.info(" TCP received response")
61+
except Exception:
62+
pass
63+
64+
time.sleep(0.2)
65+
66+
# Keep connection open a bit longer
67+
time.sleep(1)
68+
sock.close()
69+
70+
except Exception as e:
71+
logger.error(f"TCP client error: {e}")
72+
73+
74+
async def send_ws_traffic():
75+
"""Send WebSocket MCP traffic."""
76+
logger.info("Sending WebSocket traffic...")
77+
78+
try:
79+
async with websockets.connect("ws://localhost:8765", compression=None) as ws:
80+
messages = [
81+
{"jsonrpc": "2.0", "method": "initialize", "params": {"protocolVersion": "2024-11-05"}, "id": 1},
82+
{"jsonrpc": "2.0", "method": "tools/list", "id": 2},
83+
{"jsonrpc": "2.0", "method": "notifications/ws_test", "params": {"source": "websocket"}},
84+
{"jsonrpc": "2.0", "method": "tools/call", "params": {"name": "calculator", "arguments": {"a": 10, "b": 20}}, "id": 3},
85+
]
86+
87+
for msg in messages:
88+
await ws.send(json.dumps(msg))
89+
logger.info(f" WS sent: {msg.get('method')}")
90+
91+
# Wait for response if it has an ID
92+
if "id" in msg:
93+
await ws.recv()
94+
logger.info(" WS received response")
95+
else:
96+
await asyncio.sleep(0.2)
97+
98+
except Exception as e:
99+
logger.error(f"WebSocket client error: {e}")
100+
101+
102+
def check_port(port):
103+
"""Check if a port is available."""
104+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
105+
result = sock.connect_ex(('localhost', port))
106+
sock.close()
107+
return result != 0
108+
109+
110+
async def main():
111+
"""Generate all traffic types."""
112+
logger.info("MCPHawk Traffic Generator")
113+
logger.info("========================\n")
114+
115+
# Check if ports are available
116+
if not check_port(12345):
117+
logger.error("Port 12345 is already in use. Please stop the existing TCP server.")
118+
return
119+
120+
if not check_port(8765):
121+
logger.error("Port 8765 is already in use. Please stop the existing WebSocket server.")
122+
return
123+
124+
logger.info("Starting servers...")
125+
126+
# Start servers
127+
tcp_server = run_tcp_server()
128+
ws_server = run_ws_server()
129+
130+
# Give servers time to start
131+
logger.info("Waiting for servers to start...")
132+
await asyncio.sleep(2)
133+
134+
try:
135+
# Send TCP traffic
136+
send_tcp_traffic()
137+
138+
# Send WebSocket traffic
139+
await send_ws_traffic()
140+
141+
logger.info("\n✅ All traffic sent successfully!")
142+
logger.info("\nServers will continue running. Press Ctrl+C to stop.")
143+
144+
# Keep running
145+
await asyncio.Future()
146+
147+
except KeyboardInterrupt:
148+
logger.info("\nStopping servers...")
149+
finally:
150+
# Clean up
151+
tcp_server.terminate()
152+
ws_server.terminate()
153+
tcp_server.wait()
154+
ws_server.wait()
155+
logger.info("Servers stopped.")
156+
157+
158+
if __name__ == "__main__":
159+
with contextlib.suppress(KeyboardInterrupt):
160+
asyncio.run(main())
161+
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#!/usr/bin/env python3
2+
"""TCP MCP client for testing."""
3+
4+
import json
5+
import socket
6+
import time
7+
8+
9+
def send_mcp_message(sock, message):
10+
"""Send a JSON-RPC message."""
11+
data = json.dumps(message).encode('utf-8')
12+
sock.sendall(data)
13+
print(f"Sent: {message.get('method', message.get('result', 'response'))}")
14+
15+
# Read response if message has an ID
16+
if "id" in message:
17+
try:
18+
response = sock.recv(1024)
19+
if response:
20+
print(" Received response")
21+
except Exception:
22+
pass
23+
24+
25+
def main():
26+
"""Connect to TCP MCP server and send test messages."""
27+
print("Connecting to TCP MCP server on localhost:12345...")
28+
29+
try:
30+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
31+
sock.connect(('localhost', 12345))
32+
print("Connected!")
33+
34+
# Send initialize
35+
send_mcp_message(sock, {
36+
"jsonrpc": "2.0",
37+
"method": "initialize",
38+
"params": {
39+
"protocolVersion": "2024-11-05",
40+
"capabilities": {}
41+
},
42+
"id": 1
43+
})
44+
time.sleep(0.5)
45+
46+
# Send tools/list
47+
send_mcp_message(sock, {
48+
"jsonrpc": "2.0",
49+
"method": "tools/list",
50+
"id": 2
51+
})
52+
time.sleep(0.5)
53+
54+
# Send a notification (no id)
55+
send_mcp_message(sock, {
56+
"jsonrpc": "2.0",
57+
"method": "notifications/progress",
58+
"params": {
59+
"progress": 50,
60+
"operation": "processing"
61+
}
62+
})
63+
time.sleep(0.5)
64+
65+
# Send tools/call
66+
send_mcp_message(sock, {
67+
"jsonrpc": "2.0",
68+
"method": "tools/call",
69+
"params": {
70+
"name": "calculator",
71+
"arguments": {"a": 5, "b": 3}
72+
},
73+
"id": 3
74+
})
75+
time.sleep(0.5)
76+
77+
# Send a batch of messages quickly
78+
print("\nSending batch of messages...")
79+
for i in range(4, 8):
80+
send_mcp_message(sock, {
81+
"jsonrpc": "2.0",
82+
"method": f"test/message_{i}",
83+
"params": {"value": i * 10},
84+
"id": i
85+
})
86+
time.sleep(0.1)
87+
88+
print("\nAll messages sent!")
89+
# Keep connection open briefly to avoid reset
90+
time.sleep(2)
91+
92+
except ConnectionRefusedError:
93+
print("Error: Could not connect to server. Make sure tcp_server.py is running.")
94+
except Exception as e:
95+
print(f"Error: {e}")
96+
97+
98+
if __name__ == "__main__":
99+
main()
100+

dummy_mcp_server.py renamed to examples/generate_traffic/tcp_server.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,4 @@ def start_server():
5757

5858
if __name__ == "__main__":
5959
start_server()
60+

0 commit comments

Comments
 (0)