quash-mcp 0.2.13__py3-none-any.whl → 0.2.15__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of quash-mcp might be problematic. Click here for more details.

@@ -20,7 +20,7 @@ class BackendClient:
20
20
 
21
21
  def __init__(self):
22
22
  # Get backend URL from environment variable, default to production backend
23
- self.base_url = os.getenv("MAHORAGA_BACKEND_URL", "https://mcpbe.quashbugs.com")
23
+ self.base_url = os.getenv("MAHORAGA_BACKEND_URL", "http://localhost:8000")
24
24
  self.timeout = 300.0 # 5 minutes for long-running LLM calls
25
25
  logger.info(f"🔧 Backend client initialized: URL={self.base_url}")
26
26
 
@@ -35,7 +35,8 @@ class BackendClient:
35
35
  Dict with validation result:
36
36
  {
37
37
  "valid": bool,
38
- "user": {"email": str, "name": str, "credits": float},
38
+ "user": {"email": str, "name": str},
39
+ "organization_credits": float,
39
40
  "openrouter_api_key": str,
40
41
  "error": str (if invalid)
41
42
  }
@@ -17,6 +17,7 @@ from ..state import get_state
17
17
  from ..backend_client import get_backend_client
18
18
  from ..device.state_capture import get_device_state
19
19
  from ..device.adb_tools import AdbTools
20
+ import logging
20
21
 
21
22
  # Import mahoraga components for tool functions
22
23
  try:
@@ -236,12 +237,12 @@ async def execute_v3(
236
237
 
237
238
  # Check credits
238
239
  user_info = validation_result.get("user", {})
239
- credits = user_info.get("credits", 0)
240
+ organization_credits = validation_result.get("organization_credits", 0)
240
241
 
241
- if credits <= 0:
242
+ if organization_credits <= 0:
242
243
  return {
243
244
  "status": "error",
244
- "message": f"❌ Insufficient credits. Current balance: ${credits:.2f}",
245
+ "message": f"❌ Insufficient credits. Current balance: ${organization_credits:.2f}",
245
246
  "user": user_info
246
247
  }
247
248
 
@@ -250,7 +251,7 @@ async def execute_v3(
250
251
  if progress_callback:
251
252
  progress_callback(message)
252
253
 
253
- log_progress(f"✅ API Key validated - Credits: ${credits:.2f}")
254
+ log_progress(f"✅ API Key validated - Credits: ${organization_credits:.2f}")
254
255
  log_progress(f"👤 User: {user_info.get('name', 'Unknown')}")
255
256
  log_progress(f"🚀 Starting task: {task}")
256
257
  log_progress(f"📱 Device: {state.device_serial}")
@@ -315,6 +316,15 @@ async def execute_v3(
315
316
  # Add wrapped function to globals so code can call it directly
316
317
  executor_globals[tool_name] = make_printing_wrapper(tool_function)
317
318
 
319
+ # Override the 'complete' function to be a no-op
320
+ # The backend already handles completion via the 'completed' flag
321
+ def complete_no_op(success=True, reason=""):
322
+ """No-op wrapper for complete() - completion is handled by backend."""
323
+ print(f"complete() called: success={success}, reason='{reason}'")
324
+ return None
325
+
326
+ executor_globals['complete'] = complete_no_op
327
+
318
328
  log_progress(f"🔧 Loaded {len(filtered_tools)} tool functions: {list(filtered_tools.keys())}")
319
329
  except Exception as e:
320
330
  log_progress(f"⚠️ Warning: Could not load tool functions: {e}")
@@ -402,50 +412,8 @@ async def execute_v3(
402
412
  log_progress(f"🤔 Reasoning: {reasoning}")
403
413
 
404
414
 
405
- # 3. Check if task is complete BEFORE executing action
406
- if step_result.get("completed", False):
407
- success = step_result.get("success", False)
408
- final_message = step_result.get("final_message", "Task completed")
409
-
410
- duration = time.time() - start_time
411
-
412
- if success:
413
- log_progress(f"✅ Task completed successfully!")
414
- else:
415
- log_progress(f"❌ Task marked as failed")
416
-
417
- # Finalize session on backend
418
- finalize_result = await backend.finalize_session(session=session)
419
-
420
- if success:
421
- log_progress(f"✅ Task completed successfully in {len(session.steps)} steps")
422
- log_progress(f"💰 Usage: {finalize_result.get('total_tokens', {}).get('total')} tokens, ${finalize_result.get('total_cost', 0):.4f}")
423
-
424
- return {
425
- "status": "success",
426
- "steps_taken": len(session.steps),
427
- "final_message": final_message,
428
- "message": f"✅ Success: {final_message}",
429
- "tokens": finalize_result.get("total_tokens"),
430
- "cost": finalize_result.get("total_cost"),
431
- "duration_seconds": duration
432
- }
433
- else:
434
- log_progress(f"❌ Task failed: {final_message}")
435
- log_progress(f"💰 Usage: {finalize_result.get('total_tokens', {}).get('total')} tokens, ${finalize_result.get('total_cost', 0):.4f}")
436
-
437
- return {
438
- "status": "failed",
439
- "steps_taken": len(session.steps),
440
- "final_message": final_message,
441
- "message": f"❌ Failed: {final_message}",
442
- "tokens": finalize_result.get("total_tokens"),
443
- "cost": finalize_result.get("total_cost"),
444
- "duration_seconds": duration
445
- }
446
-
447
-
448
- # 4. Execute action locally (only if task is not complete)
415
+ # 3. Execute action locally FIRST (if provided)
416
+ # NOTE: Backend should have already removed complete() from the code
449
417
  if code and action_type == "execute_code":
450
418
  log_progress(f"⚡ Executing action...")
451
419
 
@@ -514,6 +482,8 @@ async def execute_v3(
514
482
 
515
483
  session.chat_history.append(ChatHistoryMessage(role="user", content=f"Execution Result:\n```\n{feedback}\n```"))
516
484
 
485
+ # Introduce a small delay to allow UI effects to settle before checking completion
486
+ time.sleep(1.0) # Added delay
517
487
 
518
488
  except Exception as e:
519
489
  error_msg = f"Error during execution: {str(e)}"
@@ -526,6 +496,48 @@ async def execute_v3(
526
496
  session.chat_history.append(ChatHistoryMessage(role="user", content="No code was provided. Please provide code to execute."))
527
497
 
528
498
 
499
+ # 4. Check if task is complete AFTER executing action
500
+ if step_result.get("completed", False):
501
+ success = step_result.get("success", False)
502
+ final_message = step_result.get("final_message", "Task completed")
503
+
504
+ duration = time.time() - start_time
505
+
506
+ if success:
507
+ log_progress(f"✅ Task completed successfully!")
508
+ else:
509
+ log_progress(f"❌ Task marked as failed")
510
+
511
+ # Finalize session on backend
512
+ finalize_result = await backend.finalize_session(session=session)
513
+
514
+ if success:
515
+ log_progress(f"✅ Task completed successfully in {len(session.steps)} steps")
516
+ log_progress(f"💰 Usage: {finalize_result.get('total_tokens', {}).get('total')} tokens, ${finalize_result.get('total_cost', 0):.4f}")
517
+
518
+ return {
519
+ "status": "success",
520
+ "steps_taken": len(session.steps),
521
+ "final_message": final_message,
522
+ "message": f"✅ Success: {final_message}",
523
+ "tokens": finalize_result.get("total_tokens"),
524
+ "cost": finalize_result.get("total_cost"),
525
+ "duration_seconds": duration
526
+ }
527
+ else:
528
+ log_progress(f"❌ Task failed: {final_message}")
529
+ log_progress(f"💰 Usage: {finalize_result.get('total_tokens', {}).get('total')} tokens, ${finalize_result.get('total_cost', 0):.4f}")
530
+
531
+ return {
532
+ "status": "failed",
533
+ "steps_taken": len(session.steps),
534
+ "final_message": final_message,
535
+ "message": f"❌ Failed: {final_message}",
536
+ "tokens": finalize_result.get("total_tokens"),
537
+ "cost": finalize_result.get("total_cost"),
538
+ "duration_seconds": duration
539
+ }
540
+
529
541
 
530
542
  # Max steps reached
531
543
  log_progress(f"⚠️ Reached maximum steps ({max_steps})")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quash-mcp
3
- Version: 0.2.13
3
+ Version: 0.2.15
4
4
  Summary: Model Context Protocol server for Quash - AI-powered mobile automation agent
5
5
  Project-URL: Homepage, https://quashbugs.com
6
6
  Project-URL: Repository, https://github.com/quash/quash-mcp
@@ -1,6 +1,6 @@
1
1
  quash_mcp/__init__.py,sha256=LImiWCRgjAbb5DZXBq2DktUEAbftvnO61Vil4Ayun9A,39
2
2
  quash_mcp/__main__.py,sha256=WCg5OlnXhr6i0XJHAUGpbhliMy3qE2SJkFzVD4wO-lw,239
3
- quash_mcp/backend_client.py,sha256=S7IF95GEUtPZJPK9Dy0Gbbv7EV6SexOuAX0sw-u5i6I,9926
3
+ quash_mcp/backend_client.py,sha256=jQ_OFOhdbGlTr42VCZMu5XP3_TJPsWCpTX_2iBppafo,9949
4
4
  quash_mcp/models.py,sha256=0S7uCZZRRtQuSwIwKTDKZnX2HRMYOBDoDcl7-b8Tzpk,1001
5
5
  quash_mcp/server.py,sha256=scUGnplxjsvyYLK2q6hrjl-5Chkdnat9pODDtLzsQFY,15519
6
6
  quash_mcp/state.py,sha256=Tnt795GnZcas-h62Y6KYyIZVopeoWPM0TbRwOeVFYj4,4394
@@ -15,10 +15,10 @@ quash_mcp/tools/configure.py,sha256=cv4RTolu6qae-XzyACSJUDrALfd0gYC-XE5s66_zfNk,
15
15
  quash_mcp/tools/connect.py,sha256=Kc7RGRUgtd2sR_bv6U4CB4kWSaLfsDc5kBo9u4FEjzs,4799
16
16
  quash_mcp/tools/execute.py,sha256=kR3VzIl31Lek-js4Hgxs-S_ls4YwKnbqkt79KFbvFuM,909
17
17
  quash_mcp/tools/execute_v2_backup.py,sha256=waWnaD0dEVcOJgRBbqZo3HnxME1s6YUOn8aRbm4R3X4,6081
18
- quash_mcp/tools/execute_v3.py,sha256=_tm0pcgSeGchooUFpiv1sTHY7w0psrCy2lQ3IDGhwKo,21899
18
+ quash_mcp/tools/execute_v3.py,sha256=zlkQqjdohE6bFQKMwWf4MeL59iCAbcfOVW6dCNl_veQ,22661
19
19
  quash_mcp/tools/runsuite.py,sha256=gohLk9FpN8v7F0a69fspqOqUexTcslpYf3qU-iIZZ3s,7220
20
20
  quash_mcp/tools/usage.py,sha256=g76A6FO36fThoyRFG7q92QmS3Kh1pIKOrhYOzUdIubA,1155
21
- quash_mcp-0.2.13.dist-info/METADATA,sha256=EdlucJ9MZlAjuTOgQzA-GVGgTcR8Eqo0NfhG4Tfv6EQ,8424
22
- quash_mcp-0.2.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
23
- quash_mcp-0.2.13.dist-info/entry_points.txt,sha256=9sbDxrx0ApGDVRS-IE3mQgSao3DwKnnV_k-_ipFn9QI,52
24
- quash_mcp-0.2.13.dist-info/RECORD,,
21
+ quash_mcp-0.2.15.dist-info/METADATA,sha256=G_9JMWS5-GVwCIOodJsQHqJ9n1YzteL_RofiPW3o5YY,8424
22
+ quash_mcp-0.2.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
23
+ quash_mcp-0.2.15.dist-info/entry_points.txt,sha256=9sbDxrx0ApGDVRS-IE3mQgSao3DwKnnV_k-_ipFn9QI,52
24
+ quash_mcp-0.2.15.dist-info/RECORD,,