Spaces:
Running
on
Zero
Running
on
Zero
Commit
·
6c1b819
1
Parent(s):
3d6d107
Simplify MCP arch #2
Browse files
agent.py
CHANGED
|
@@ -18,7 +18,6 @@ from pathlib import Path
|
|
| 18 |
try:
|
| 19 |
from mcp.server import Server
|
| 20 |
from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource
|
| 21 |
-
from mcp.server.models import InitializationOptions
|
| 22 |
except ImportError:
|
| 23 |
print("Error: MCP SDK not installed. Install with: pip install mcp", file=sys.stderr)
|
| 24 |
sys.exit(1)
|
|
@@ -282,47 +281,15 @@ async def main():
|
|
| 282 |
|
| 283 |
try:
|
| 284 |
async with stdio_server() as streams:
|
| 285 |
-
#
|
| 286 |
-
#
|
| 287 |
-
#
|
| 288 |
-
try:
|
| 289 |
-
# Try to get capabilities from the server if the method exists
|
| 290 |
-
if hasattr(app, 'get_capabilities'):
|
| 291 |
-
try:
|
| 292 |
-
# Try with NotificationOptions if available
|
| 293 |
-
from mcp.server.lowlevel.server import NotificationOptions
|
| 294 |
-
server_capabilities = app.get_capabilities(
|
| 295 |
-
notification_options=NotificationOptions(),
|
| 296 |
-
experimental_capabilities={}
|
| 297 |
-
)
|
| 298 |
-
except (ImportError, AttributeError, TypeError):
|
| 299 |
-
# Fallback: try without NotificationOptions
|
| 300 |
-
try:
|
| 301 |
-
server_capabilities = app.get_capabilities()
|
| 302 |
-
except Exception:
|
| 303 |
-
# If get_capabilities doesn't work, create minimal capabilities
|
| 304 |
-
server_capabilities = {}
|
| 305 |
-
else:
|
| 306 |
-
# Server will provide capabilities automatically, use empty dict
|
| 307 |
-
server_capabilities = {}
|
| 308 |
-
except Exception:
|
| 309 |
-
# Server will handle capabilities automatically
|
| 310 |
-
server_capabilities = {}
|
| 311 |
-
|
| 312 |
-
# Create initialization options
|
| 313 |
-
init_options = InitializationOptions(
|
| 314 |
-
server_name="gemini-mcp-server",
|
| 315 |
-
server_version="1.0.0",
|
| 316 |
-
capabilities=server_capabilities
|
| 317 |
-
)
|
| 318 |
-
|
| 319 |
-
# Run the server with initialization options
|
| 320 |
logger.info("MCP server ready")
|
| 321 |
try:
|
|
|
|
| 322 |
await app.run(
|
| 323 |
read_stream=streams[0],
|
| 324 |
-
write_stream=streams[1]
|
| 325 |
-
initialization_options=init_options
|
| 326 |
)
|
| 327 |
except Exception as run_error:
|
| 328 |
logger.error(f"Error in app.run(): {run_error}")
|
|
|
|
| 18 |
try:
|
| 19 |
from mcp.server import Server
|
| 20 |
from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource
|
|
|
|
| 21 |
except ImportError:
|
| 22 |
print("Error: MCP SDK not installed. Install with: pip install mcp", file=sys.stderr)
|
| 23 |
sys.exit(1)
|
|
|
|
| 281 |
|
| 282 |
try:
|
| 283 |
async with stdio_server() as streams:
|
| 284 |
+
# The Server class automatically handles initialization and provides capabilities
|
| 285 |
+
# based on the registered @app.list_tools() and @app.call_tool() handlers
|
| 286 |
+
# No need to manually create InitializationOptions - the server handles this
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 287 |
logger.info("MCP server ready")
|
| 288 |
try:
|
| 289 |
+
# Run the server - it will automatically handle the initialization handshake
|
| 290 |
await app.run(
|
| 291 |
read_stream=streams[0],
|
| 292 |
+
write_stream=streams[1]
|
|
|
|
| 293 |
)
|
| 294 |
except Exception as run_error:
|
| 295 |
logger.error(f"Error in app.run(): {run_error}")
|
app.py
CHANGED
|
@@ -264,9 +264,6 @@ async def get_mcp_session():
|
|
| 264 |
stdio_ctx = stdio_client(server_params)
|
| 265 |
read, write = await stdio_ctx.__aenter__()
|
| 266 |
|
| 267 |
-
# Wait for the server process to start and be ready
|
| 268 |
-
await asyncio.sleep(1.5) # Wait for server startup
|
| 269 |
-
|
| 270 |
# Create ClientSession from the streams
|
| 271 |
session = ClientSession(read, write)
|
| 272 |
|
|
@@ -278,12 +275,11 @@ async def get_mcp_session():
|
|
| 278 |
# According to MCP protocol spec, the client MUST wait for the initialized notification
|
| 279 |
# before sending any other requests (like list_tools)
|
| 280 |
try:
|
|
|
|
|
|
|
|
|
|
| 281 |
await session.__aenter__()
|
| 282 |
logger.info("✅ MCP session initialized")
|
| 283 |
-
|
| 284 |
-
# After __aenter__() completes, wait a bit longer to ensure server's internal state is ready
|
| 285 |
-
# The server may have sent the initialized notification but needs time to set up handlers
|
| 286 |
-
await asyncio.sleep(1.0) # Give server time to finalize internal state
|
| 287 |
except Exception as e:
|
| 288 |
error_msg = str(e)
|
| 289 |
error_type = type(e).__name__
|
|
@@ -322,11 +318,24 @@ async def call_agent(user_prompt: str, system_prompt: str = None, files: list =
|
|
| 322 |
return ""
|
| 323 |
|
| 324 |
# List tools - session should be ready after proper initialization
|
|
|
|
|
|
|
| 325 |
try:
|
| 326 |
tools = await session.list_tools()
|
| 327 |
except Exception as e:
|
| 328 |
-
|
| 329 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 330 |
|
| 331 |
if not tools or not hasattr(tools, 'tools'):
|
| 332 |
logger.error("Invalid tools response from MCP server")
|
|
@@ -694,11 +703,24 @@ async def search_web_mcp_tool(query: str, max_results: int = 5) -> list:
|
|
| 694 |
return []
|
| 695 |
|
| 696 |
# List tools - session should be ready after proper initialization
|
|
|
|
|
|
|
| 697 |
try:
|
| 698 |
tools = await session.list_tools()
|
| 699 |
except Exception as e:
|
| 700 |
-
|
| 701 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 702 |
|
| 703 |
if not tools or not hasattr(tools, 'tools'):
|
| 704 |
return []
|
|
|
|
| 264 |
stdio_ctx = stdio_client(server_params)
|
| 265 |
read, write = await stdio_ctx.__aenter__()
|
| 266 |
|
|
|
|
|
|
|
|
|
|
| 267 |
# Create ClientSession from the streams
|
| 268 |
session = ClientSession(read, write)
|
| 269 |
|
|
|
|
| 275 |
# According to MCP protocol spec, the client MUST wait for the initialized notification
|
| 276 |
# before sending any other requests (like list_tools)
|
| 277 |
try:
|
| 278 |
+
# The __aenter__() method properly handles the full initialization sequence
|
| 279 |
+
# including waiting for the server's initialized notification
|
| 280 |
+
# This is a blocking call that completes only after the server sends initialized
|
| 281 |
await session.__aenter__()
|
| 282 |
logger.info("✅ MCP session initialized")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 283 |
except Exception as e:
|
| 284 |
error_msg = str(e)
|
| 285 |
error_type = type(e).__name__
|
|
|
|
| 318 |
return ""
|
| 319 |
|
| 320 |
# List tools - session should be ready after proper initialization
|
| 321 |
+
# Add a small delay to ensure server has fully processed initialization
|
| 322 |
+
await asyncio.sleep(0.1)
|
| 323 |
try:
|
| 324 |
tools = await session.list_tools()
|
| 325 |
except Exception as e:
|
| 326 |
+
error_msg = str(e)
|
| 327 |
+
# Check if it's an initialization error
|
| 328 |
+
if "initialization" in error_msg.lower() or "before initialization" in error_msg.lower():
|
| 329 |
+
logger.warning(f"⚠️ Server not ready yet, waiting a bit more...: {error_msg}")
|
| 330 |
+
await asyncio.sleep(0.5)
|
| 331 |
+
try:
|
| 332 |
+
tools = await session.list_tools()
|
| 333 |
+
except Exception as retry_error:
|
| 334 |
+
logger.error(f"❌ Failed to list MCP tools after retry: {retry_error}")
|
| 335 |
+
return ""
|
| 336 |
+
else:
|
| 337 |
+
logger.error(f"❌ Failed to list MCP tools: {error_msg}")
|
| 338 |
+
return ""
|
| 339 |
|
| 340 |
if not tools or not hasattr(tools, 'tools'):
|
| 341 |
logger.error("Invalid tools response from MCP server")
|
|
|
|
| 703 |
return []
|
| 704 |
|
| 705 |
# List tools - session should be ready after proper initialization
|
| 706 |
+
# Add a small delay to ensure server has fully processed initialization
|
| 707 |
+
await asyncio.sleep(0.1)
|
| 708 |
try:
|
| 709 |
tools = await session.list_tools()
|
| 710 |
except Exception as e:
|
| 711 |
+
error_msg = str(e)
|
| 712 |
+
# Check if it's an initialization error
|
| 713 |
+
if "initialization" in error_msg.lower() or "before initialization" in error_msg.lower():
|
| 714 |
+
logger.warning(f"⚠️ Server not ready yet, waiting a bit more...: {error_msg}")
|
| 715 |
+
await asyncio.sleep(0.5)
|
| 716 |
+
try:
|
| 717 |
+
tools = await session.list_tools()
|
| 718 |
+
except Exception as retry_error:
|
| 719 |
+
logger.error(f"Failed to list MCP tools after retry: {retry_error}")
|
| 720 |
+
return []
|
| 721 |
+
else:
|
| 722 |
+
logger.error(f"Failed to list MCP tools: {error_msg}")
|
| 723 |
+
return []
|
| 724 |
|
| 725 |
if not tools or not hasattr(tools, 'tools'):
|
| 726 |
return []
|