Spaces:
Running
Running
File size: 17,119 Bytes
a66d4bd |
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 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
"""
MCP Debug / Diagnostic Script
==============================
Tests every layer of the request flow with detailed logging:
Layer 1: Config validation (paths, keys, env vars)
Layer 2: MCP subprocess spawn (can it start?)
Layer 3: MCP tool listing (does list_tools() work?)
Layer 4: Direct MCP tool call (does call_tool() work?)
Layer 5: Inner Agent + Runner (does the model call MCP tools?)
Layer 6: Orchestrator function_tool (does the outer model call gmail_task?)
Usage:
cd D:\\deploy\\chatbot\\src\\Agentic_System\\test
python _debug_mcp.py # Run all layers
python _debug_mcp.py --layer 3 # Run only layer 3
"""
import asyncio
import json
import logging
import os
import sys
import time
import argparse
from pathlib import Path
from dotenv import load_dotenv
# Setup paths
SCRIPT_DIR = Path(__file__).resolve().parent # .../test/
AGENTIC_DIR = SCRIPT_DIR.parent # .../Agentic_System/
PROJECT_ROOT = AGENTIC_DIR.parent.parent # .../chatbot/
load_dotenv(PROJECT_ROOT / ".env")
load_dotenv(AGENTIC_DIR / ".env")
# Add Agentic_System to sys.path so imports work from the test/ subfolder
if str(AGENTIC_DIR) not in sys.path:
sys.path.insert(0, str(AGENTIC_DIR))
# Configure logging to show EVERYTHING
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s [%(levelname)-7s] %(name)s: %(message)s",
datefmt="%H:%M:%S",
)
# Quiet down noisy libraries but keep our stuff at DEBUG
for noisy in ["httpcore", "httpx", "urllib3", "asyncio", "hpack", "openai._base_client"]:
logging.getLogger(noisy).setLevel(logging.WARNING)
logger = logging.getLogger("DEBUG-MCP")
def banner(text: str):
print("\n" + "=" * 70)
print(f" {text}")
print("=" * 70)
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# LAYER 1: Config Validation
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def layer1_config():
banner("LAYER 1: Config Validation")
from google_mcp_config import (
MCP_SERVER_DIR, MCP_PYTHON, OPENROUTER_API_KEY, OPENROUTER_BASE_URL,
MODEL_NAME, GMAIL_TOOLS, SERVICE_MAP,
)
checks = {
"MCP_SERVER_DIR exists": os.path.isdir(MCP_SERVER_DIR),
"MCP_PYTHON exists": os.path.isfile(MCP_PYTHON),
"main.py exists": os.path.isfile(os.path.join(MCP_SERVER_DIR, "main.py")),
"OPENROUTER_API_KEY set": bool(OPENROUTER_API_KEY),
"OPENROUTER_BASE_URL set": bool(OPENROUTER_BASE_URL),
"MODEL_NAME set": bool(MODEL_NAME),
"GMAIL_TOOLS non-empty": len(GMAIL_TOOLS) > 0,
"SERVICE_MAP has gmail": "gmail" in SERVICE_MAP,
}
print(f"\n MCP_SERVER_DIR : {MCP_SERVER_DIR}")
print(f" MCP_PYTHON : {MCP_PYTHON}")
print(f" OPENROUTER_BASE_URL: {OPENROUTER_BASE_URL}")
print(f" MODEL_NAME : {MODEL_NAME}")
print(f" OPENROUTER_API_KEY: {(OPENROUTER_API_KEY or '')[:15]}...")
print(f" USER_GOOGLE_EMAIL : {os.getenv('USER_GOOGLE_EMAIL', 'NOT SET')}")
print(f" MCP_ENABLED : {os.getenv('MCP_ENABLED', 'true')}")
print(f" GMAIL_TOOLS count : {len(GMAIL_TOOLS)}")
print()
all_pass = True
for check, passed in checks.items():
status = "PASS" if passed else "FAIL"
symbol = "[+]" if passed else "[X]"
print(f" {symbol} {check}: {status}")
if not passed:
all_pass = False
# Check MCP server .env
mcp_env = os.path.join(MCP_SERVER_DIR, ".env")
if os.path.isfile(mcp_env):
print(f"\n MCP server .env ({mcp_env}):")
with open(mcp_env) as f:
for line in f:
line = line.strip()
if line and not line.startswith("#"):
key = line.split("=")[0]
if "SECRET" in key.upper():
print(f" {key}=****")
else:
print(f" {line}")
else:
print(f"\n [X] MCP server .env NOT FOUND at {mcp_env}")
# Check credentials directory
cred_dir = os.path.expanduser("~/.google_workspace_mcp/credentials/")
print(f"\n Credentials dir: {cred_dir}")
if os.path.isdir(cred_dir):
files = os.listdir(cred_dir)
if files:
for f in files:
fpath = os.path.join(cred_dir, f)
size = os.path.getsize(fpath)
print(f" {f} ({size} bytes)")
else:
print(" (empty - NO credentials stored)")
else:
print(" (directory does not exist)")
return all_pass
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# LAYER 2: MCP Subprocess Spawn
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
async def layer2_subprocess():
banner("LAYER 2: MCP Subprocess Spawn")
from google_mcp_config import create_google_mcp_server, GMAIL_TOOLS
print("\n Creating MCPServerStdio for gmail...")
mcp_server = create_google_mcp_server(service="gmail", tool_names=GMAIL_TOOLS)
print(f" Server object: {mcp_server}")
print(f" Server name : {mcp_server.name}")
print("\n Entering 'async with mcp_server:' (this spawns the subprocess)...")
t0 = time.time()
try:
async with mcp_server:
elapsed = time.time() - t0
print(f" [+] Subprocess started successfully in {elapsed:.2f}s")
print(f" [+] MCP connection is ALIVE")
# Quick test: list tools
tools = await mcp_server.list_tools()
print(f" [+] list_tools() returned {len(tools)} tools")
return True
except Exception as e:
elapsed = time.time() - t0
print(f" [X] FAILED after {elapsed:.2f}s: {e}")
logger.error("Layer 2 error", exc_info=True)
return False
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# LAYER 3: MCP Tool Listing
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
async def layer3_tool_listing():
banner("LAYER 3: MCP Tool Listing (detailed)")
from google_mcp_config import create_google_mcp_server, GMAIL_TOOLS
mcp_server = create_google_mcp_server(service="gmail", tool_names=GMAIL_TOOLS)
async with mcp_server:
print("\n Calling list_tools()...")
t0 = time.time()
tools = await mcp_server.list_tools()
elapsed = time.time() - t0
print(f" Got {len(tools)} tools in {elapsed:.2f}s\n")
for i, tool in enumerate(tools):
name = getattr(tool, 'name', str(tool))
desc = getattr(tool, 'description', '')[:80]
schema = getattr(tool, 'inputSchema', getattr(tool, 'parameters', {}))
print(f" [{i:2d}] {name}")
print(f" desc: {desc}")
if schema:
params = schema.get('properties', {}) if isinstance(schema, dict) else {}
print(f" params: {list(params.keys())}")
# Check if our expected tools are present
tool_names_found = {getattr(t, 'name', str(t)) for t in tools}
expected = set(GMAIL_TOOLS)
missing = expected - tool_names_found
extra = tool_names_found - expected
print(f"\n Expected tools : {len(expected)}")
print(f" Tools found : {len(tool_names_found)}")
if missing:
print(f" [X] MISSING tools : {missing}")
if extra:
print(f" [!] Extra tools : {extra}")
if not missing:
print(f" [+] All expected tools present!")
return len(tools) > 0
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# LAYER 4: Direct MCP Tool Call
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
async def layer4_direct_tool_call():
banner("LAYER 4: Direct MCP Tool Call")
from google_mcp_config import create_google_mcp_server, GMAIL_TOOLS
mcp_server = create_google_mcp_server(service="gmail", tool_names=GMAIL_TOOLS)
async with mcp_server:
print("\n Directly calling 'search_gmail_messages' via MCP...")
print(" (This bypasses the LLM entirely - tests raw MCP communication)")
t0 = time.time()
try:
result = await mcp_server.call_tool(
"search_gmail_messages",
{"query": "is:unread", "user_google_email": "aiwithjawadsaghir@gmail.com", "page_size": 2},
)
elapsed = time.time() - t0
print(f" [+] call_tool() returned in {elapsed:.2f}s")
print(f" Result type: {type(result)}")
result_str = str(result)
print(f" Result preview ({len(result_str)} chars):")
print(f" {result_str[:500]}")
# Check if it's an auth error
if "auth" in result_str.lower() or "oauth" in result_str.lower() or "credential" in result_str.lower():
print(f"\n [!] Looks like an AUTH/CREDENTIAL issue in the response!")
return True
except Exception as e:
elapsed = time.time() - t0
print(f" [X] call_tool() FAILED after {elapsed:.2f}s: {e}")
logger.error("Layer 4 error", exc_info=True)
return False
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# LAYER 5: Inner Agent + Runner (LLM + MCP tools)
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
async def layer5_inner_agent():
banner("LAYER 5: Inner Agent + Runner (model calls MCP tools)")
from google_mcp_config import (
create_google_mcp_server, GMAIL_TOOLS,
OPENROUTER_API_KEY, OPENROUTER_BASE_URL, MODEL_NAME,
)
from agents import Agent, Runner, OpenAIChatCompletionsModel
from agents.model_settings import ModelSettings
from agents import set_tracing_disabled
from openai import AsyncOpenAI
set_tracing_disabled(True)
client = AsyncOpenAI(
api_key=OPENROUTER_API_KEY,
base_url=OPENROUTER_BASE_URL,
timeout=30.0,
)
mcp_server = create_google_mcp_server(service="gmail", tool_names=GMAIL_TOOLS)
async with mcp_server:
tools = await mcp_server.list_tools()
print(f"\n MCP tools loaded: {len(tools)}")
agent = Agent(
name="Gmail Test Agent",
instructions="You are a Gmail assistant. Use the available tools to answer the user's query. The user's email is aiwithjawadsaghir@gmail.com.",
mcp_servers=[mcp_server],
model=OpenAIChatCompletionsModel(
model=MODEL_NAME,
openai_client=client,
),
model_settings=ModelSettings(tool_choice="auto"),
)
query = "Search for unread emails. My email is aiwithjawadsaghir@gmail.com"
print(f" Query: {query}")
print(f" Model: {MODEL_NAME}")
print(f" tool_choice: auto")
print(f"\n Calling Runner.run()...")
print(f" (The model should see the MCP tools and call one of them)")
t0 = time.time()
try:
result = await asyncio.wait_for(
Runner.run(agent, input=query),
timeout=60.0,
)
elapsed = time.time() - t0
print(f" [+] Runner.run() completed in {elapsed:.2f}s")
print(f" final_output length: {len(result.final_output or '')}")
print(f" final_output preview: {(result.final_output or '')[:500]}")
# Show what the model actually did
if hasattr(result, 'new_items'):
print(f"\n --- Model Actions ({len(result.new_items)} items) ---")
for idx, item in enumerate(result.new_items):
item_type = type(item).__name__
print(f" [{idx}] {item_type}: {str(item)[:200]}")
return True
except Exception as e:
elapsed = time.time() - t0
print(f" [X] Runner.run() FAILED after {elapsed:.2f}s: {e}")
logger.error("Layer 5 error", exc_info=True)
return False
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# LAYER 6: Orchestrator function_tool delegation
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
async def layer6_orchestrator():
banner("LAYER 6: Orchestrator (full flow)")
from Orchestrator_Agent import service
query = "Search for my unread emails. My email is aiwithjawadsaghir@gmail.com"
print(f"\n Query: {query}")
print(f" Calling Orchestrator_Agent.service()...")
print(f" (The outer model should recognize this as a Gmail task")
print(f" and call gmail_task() function_tool)")
t0 = time.time()
try:
result = await asyncio.wait_for(service(query), timeout=120.0)
elapsed = time.time() - t0
print(f"\n [+] service() completed in {elapsed:.2f}s")
print(f" Result length: {len(result or '')}")
print(f" Result preview: {(result or '')[:500]}")
return True
except Exception as e:
elapsed = time.time() - t0
print(f"\n [X] service() FAILED after {elapsed:.2f}s: {e}")
logger.error("Layer 6 error", exc_info=True)
return False
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# MAIN
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
async def run_all(specific_layer: int = None):
results = {}
if specific_layer is None or specific_layer == 1:
results[1] = layer1_config()
if specific_layer is None or specific_layer == 2:
results[2] = await layer2_subprocess()
if specific_layer is None or specific_layer == 3:
results[3] = await layer3_tool_listing()
if specific_layer is None or specific_layer == 4:
results[4] = await layer4_direct_tool_call()
if specific_layer is None or specific_layer == 5:
results[5] = await layer5_inner_agent()
if specific_layer is None or specific_layer == 6:
results[6] = await layer6_orchestrator()
banner("SUMMARY")
names = {
1: "Config Validation",
2: "MCP Subprocess Spawn",
3: "MCP Tool Listing",
4: "Direct MCP Tool Call",
5: "Inner Agent + Runner",
6: "Orchestrator Full Flow",
}
for layer, passed in results.items():
status = "PASS" if passed else "FAIL"
symbol = "[+]" if passed else "[X]"
print(f" {symbol} Layer {layer}: {names[layer]} - {status}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="MCP Debug Tool")
parser.add_argument("--layer", type=int, choices=[1, 2, 3, 4, 5, 6],
help="Run only a specific layer (1-6)")
args = parser.parse_args()
asyncio.run(run_all(args.layer))
|