debug_prompt_serialization.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. #!/usr/bin/env python3
  2. """
  3. Debug script to test prompt serialization and identify the exact issue
  4. """
  5. import subprocess
  6. import time
  7. import json
  8. import sys
  9. import os
  10. import threading
  11. import queue
  12. class MCPServerTester:
  13. def __init__(self, server_path):
  14. self.server_path = server_path
  15. self.server_process = None
  16. self.message_queue = queue.Queue()
  17. def start_server(self):
  18. """Start MCP server process"""
  19. print(f"Starting MCP server: {self.server_path}")
  20. self.server_process = subprocess.Popen(
  21. [self.server_path],
  22. stdin=subprocess.PIPE,
  23. stdout=subprocess.PIPE,
  24. stderr=subprocess.PIPE,
  25. text=False,
  26. bufsize=0
  27. )
  28. # Start thread to read stderr
  29. stderr_thread = threading.Thread(target=self._read_stderr)
  30. stderr_thread.daemon = True
  31. stderr_thread.start()
  32. # Start thread to read stdout
  33. stdout_thread = threading.Thread(target=self._read_stdout)
  34. stdout_thread.daemon = True
  35. stdout_thread.start()
  36. time.sleep(1)
  37. def _read_stderr(self):
  38. """Read stderr output from server"""
  39. while self.server_process and self.server_process.poll() is None:
  40. try:
  41. line = self.server_process.stderr.readline()
  42. if line:
  43. print(f"SERVER STDERR: {line.decode().strip()}")
  44. except:
  45. break
  46. def _read_stdout(self):
  47. """Read stdout output from server with Content-Length parsing"""
  48. while self.server_process and self.server_process.poll() is None:
  49. try:
  50. # Read Content-Length header
  51. line = self.server_process.stdout.readline()
  52. if not line:
  53. break
  54. line_str = line.decode().strip()
  55. if line_str.startswith("Content-Length:"):
  56. content_length = int(line_str.split(":")[1].strip())
  57. # Read empty line
  58. self.server_process.stdout.readline()
  59. # Read JSON content
  60. json_data = self.server_process.stdout.read(content_length)
  61. if json_data:
  62. try:
  63. parsed = json.loads(json_data.decode())
  64. self.message_queue.put(parsed)
  65. print(f"SERVER MESSAGE: {json.dumps(parsed, indent=2)}")
  66. except json.JSONDecodeError as e:
  67. print(f"JSON DECODE ERROR: {e}")
  68. print(f"RAW DATA: {json_data}")
  69. except:
  70. break
  71. def send_jsonrpc_request(self, method, params=None, id=1):
  72. """Send a JSON-RPC request with Content-Length header"""
  73. request = {
  74. "jsonrpc": "2.0",
  75. "method": method,
  76. "id": id,
  77. "params": params or {}
  78. }
  79. request_str = json.dumps(request)
  80. message = f"Content-Length: {len(request_str)}\r\n\r\n{request_str}"
  81. print(f"SENDING: {repr(message)}")
  82. self.server_process.stdin.write(message.encode())
  83. self.server_process.stdin.flush()
  84. try:
  85. response = self.message_queue.get(timeout=10)
  86. return response
  87. except queue.Empty:
  88. print("TIMEOUT: No response received")
  89. return None
  90. def test_prompt_get(self):
  91. """Test prompts/get to trigger the serialization issue"""
  92. print("\n=== Testing Prompt Get ===")
  93. # Initialize first
  94. init_params = {
  95. "protocolVersion": "2025-11-25",
  96. "capabilities": {},
  97. "clientInfo": {
  98. "name": "test-client",
  99. "version": "1.0.0"
  100. }
  101. }
  102. init_response = self.send_jsonrpc_request("initialize", init_params, id=0)
  103. if init_response and "result" in init_response:
  104. print("✓ Server initialized successfully")
  105. else:
  106. print("✗ Failed to initialize server")
  107. return False
  108. # List prompts first
  109. print("\nListing prompts...")
  110. list_response = self.send_jsonrpc_request("prompts/list", id=1)
  111. if list_response and "result" in list_response:
  112. prompts = list_response["result"].get("prompts", [])
  113. print(f"✓ Found {len(prompts)} prompts")
  114. if prompts:
  115. # Try to get the first prompt
  116. prompt_name = prompts[0]["name"]
  117. print(f"\nGetting prompt: {prompt_name}")
  118. get_response = self.send_jsonrpc_request("prompts/get", {
  119. "name": prompt_name
  120. }, id=2)
  121. if get_response:
  122. if "result" in get_response:
  123. print("✓ Prompt retrieved successfully")
  124. result = get_response["result"]
  125. # Check the structure of the response
  126. if "messages" in result:
  127. messages = result["messages"]
  128. print(f"✓ Found {len(messages)} messages")
  129. for i, message in enumerate(messages):
  130. print(f"\nMessage {i}:")
  131. print(f" Role: {message.get('role')}")
  132. if "content" in message:
  133. content = message["content"]
  134. print(f" Content type: {content.get('type')}")
  135. print(f" Content keys: {list(content.keys())}")
  136. # Check for specific issues
  137. if content.get("type") in ["image", "audio"]:
  138. if "mimeType" in content:
  139. print(f" ✓ Has mimeType: {content['mimeType']}")
  140. else:
  141. print(" ✗ Missing mimeType field")
  142. if "mime_type" in content:
  143. print(f" ✗ Has mime_type instead of mimeType: {content['mime_type']}")
  144. if "data" in content:
  145. print(f" ✓ Has data field")
  146. else:
  147. print(" ✗ Missing data field")
  148. elif content.get("type") == "resource_link":
  149. if "name" in content:
  150. print(f" ✓ Has name: {content['name']}")
  151. else:
  152. print(" ✗ Missing name field")
  153. if "uri" in content:
  154. print(f" ✓ Has uri: {content['uri']}")
  155. else:
  156. print(" ✗ Missing uri field")
  157. elif content.get("type") == "resource":
  158. if "resource" in content:
  159. print(f" ✓ Has resource field")
  160. resource = content["resource"]
  161. print(f" Resource keys: {list(resource.keys())}")
  162. else:
  163. print(" ✗ Missing resource field")
  164. else:
  165. print("✗ No messages field in response")
  166. else:
  167. print("✗ No result field in response")
  168. if "error" in get_response:
  169. error = get_response["error"]
  170. print(f"ERROR: {json.dumps(error, indent=2)}")
  171. else:
  172. print("✗ Failed to get prompt")
  173. else:
  174. print("✗ No prompts found")
  175. else:
  176. print("✗ Failed to list prompts")
  177. return False
  178. def stop_server(self):
  179. """Stop MCP server process"""
  180. if self.server_process:
  181. print("\nStopping server...")
  182. self.server_process.terminate()
  183. try:
  184. self.server_process.wait(timeout=5)
  185. except subprocess.TimeoutExpired:
  186. self.server_process.kill()
  187. self.server_process = None
  188. def main():
  189. if len(sys.argv) != 2:
  190. print("Usage: python3 debug_prompt_serialization.py <server-executable>")
  191. sys.exit(1)
  192. server_path = sys.argv[1]
  193. if not os.path.exists(server_path):
  194. print(f"Error: Server executable '{server_path}' not found")
  195. sys.exit(1)
  196. tester = MCPServerTester(server_path)
  197. try:
  198. tester.start_server()
  199. time.sleep(2)
  200. # Test prompt get
  201. tester.test_prompt_get()
  202. except KeyboardInterrupt:
  203. print("\nTest interrupted by user")
  204. except Exception as e:
  205. print(f"\nError during test: {e}")
  206. import traceback
  207. traceback.print_exc()
  208. finally:
  209. tester.stop_server()
  210. if __name__ == "__main__":
  211. main()