717 lines
25 KiB
Python
717 lines
25 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
MCP Browser command-line interface.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import asyncio
|
|
import argparse
|
|
import json
|
|
import signal
|
|
from pathlib import Path
|
|
from typing import Optional, Dict, Any
|
|
import yaml
|
|
|
|
from .proxy import MCPBrowser
|
|
from .config import ConfigLoader
|
|
from .default_configs import ConfigManager
|
|
from .daemon import MCPBrowserDaemon, MCPBrowserClient, get_socket_path, is_daemon_running
|
|
from .utils import debug_print, debug_json
|
|
|
|
|
|
def build_mcp_request(args) -> Dict[str, Any]:
|
|
"""Build JSON-RPC request from command line arguments."""
|
|
if args.command == "tools-list":
|
|
return {
|
|
"jsonrpc": "2.0",
|
|
"id": 1,
|
|
"method": "tools/list",
|
|
"params": {}
|
|
}
|
|
|
|
elif args.command == "tools-call":
|
|
return {
|
|
"jsonrpc": "2.0",
|
|
"id": 1,
|
|
"method": "tools/call",
|
|
"params": {
|
|
"name": args.name,
|
|
"arguments": json.loads(args.arguments)
|
|
}
|
|
}
|
|
|
|
elif args.command == "resources-list":
|
|
return {
|
|
"jsonrpc": "2.0",
|
|
"id": 1,
|
|
"method": "resources/list",
|
|
"params": {}
|
|
}
|
|
|
|
elif args.command == "resources-read":
|
|
return {
|
|
"jsonrpc": "2.0",
|
|
"id": 1,
|
|
"method": "resources/read",
|
|
"params": {
|
|
"uri": args.uri
|
|
}
|
|
}
|
|
|
|
elif args.command == "prompts-list":
|
|
return {
|
|
"jsonrpc": "2.0",
|
|
"id": 1,
|
|
"method": "prompts/list",
|
|
"params": {}
|
|
}
|
|
|
|
elif args.command == "prompts-get":
|
|
return {
|
|
"jsonrpc": "2.0",
|
|
"id": 1,
|
|
"method": "prompts/get",
|
|
"params": {
|
|
"name": args.name,
|
|
"arguments": json.loads(args.arguments)
|
|
}
|
|
}
|
|
|
|
elif args.command == "completion":
|
|
params = {}
|
|
if args.ref:
|
|
params["ref"] = {"type": "ref/resource", "uri": args.ref}
|
|
if args.argument:
|
|
params["argument"] = args.argument
|
|
|
|
return {
|
|
"jsonrpc": "2.0",
|
|
"id": 1,
|
|
"method": "completion/complete",
|
|
"params": params
|
|
}
|
|
|
|
elif args.command == "jsonrpc":
|
|
request = json.loads(args.request)
|
|
if "jsonrpc" not in request:
|
|
request["jsonrpc"] = "2.0"
|
|
if "id" not in request:
|
|
request["id"] = 1
|
|
return request
|
|
|
|
else:
|
|
raise ValueError(f"Unknown command: {args.command}")
|
|
|
|
|
|
def format_mcp_response(args, request: Dict[str, Any], response: Dict[str, Any]):
|
|
"""Format and print MCP response based on command."""
|
|
if args.debug:
|
|
debug_json("Request", request)
|
|
debug_json("Response", response)
|
|
|
|
# Format output based on command
|
|
if args.command == "tools-list" and "result" in response:
|
|
tools = response["result"].get("tools", [])
|
|
print(f"Found {len(tools)} tools:")
|
|
for tool in tools:
|
|
print(f" - {tool['name']}: {tool.get('description', 'No description')}")
|
|
|
|
elif args.command == "tools-call" and "result" in response:
|
|
# Format tool response
|
|
result = response["result"]
|
|
if "content" in result:
|
|
for content in result["content"]:
|
|
if content.get("type") == "text":
|
|
print(content.get("text", ""))
|
|
else:
|
|
print(json.dumps(content, indent=2))
|
|
else:
|
|
print(json.dumps(result, indent=2))
|
|
|
|
elif args.command == "resources-list" and "result" in response:
|
|
resources = response["result"].get("resources", [])
|
|
print(f"Found {len(resources)} resources:")
|
|
for res in resources:
|
|
print(f" - {res['uri']}: {res.get('name', 'Unnamed')}")
|
|
|
|
elif args.command == "prompts-list" and "result" in response:
|
|
prompts = response["result"].get("prompts", [])
|
|
print(f"Found {len(prompts)} prompts:")
|
|
for prompt in prompts:
|
|
print(f" - {prompt['name']}: {prompt.get('description', 'No description')}")
|
|
|
|
elif args.command == "jsonrpc":
|
|
# For raw JSON-RPC, output the full response as JSON
|
|
print(json.dumps(response))
|
|
|
|
else:
|
|
# Default: pretty print result
|
|
if "result" in response:
|
|
print(json.dumps(response["result"], indent=2))
|
|
elif "error" in response:
|
|
print(f"Error: {response['error'].get('message', 'Unknown error')}")
|
|
if args.debug:
|
|
debug_json("Error details", response["error"])
|
|
else:
|
|
print(json.dumps(response, indent=2))
|
|
|
|
|
|
async def interactive_mode(browser: MCPBrowser):
|
|
"""Run MCP Browser in interactive mode."""
|
|
print("MCP Browser Interactive Mode")
|
|
print("=" * 50)
|
|
print(f"Server: {browser._server_name or browser.config.default_server}")
|
|
print(f"Sparse mode: {'enabled' if browser.config.sparse_mode else 'disabled'}")
|
|
print("Type 'help' for commands, 'exit' to quit\n")
|
|
|
|
while True:
|
|
try:
|
|
command = input("> ").strip()
|
|
|
|
if command == "exit":
|
|
break
|
|
elif command == "help":
|
|
print("\nCommands:")
|
|
print(" list - List available tools (sparse mode)")
|
|
print(" discover <path> - Discover tools using JSONPath")
|
|
print(" call <json> - Execute JSON-RPC call")
|
|
print(" onboard [<id>] - Show/set onboarding for identity")
|
|
print(" reload - Reload configuration")
|
|
print(" status - Show connection status")
|
|
print(" exit - Exit interactive mode")
|
|
print("\nExamples:")
|
|
print(' discover $.tools[*].name # Get all tool names')
|
|
print(' discover $.tools[0].inputSchema # Get first tool schema')
|
|
print(' call {"method": "tools/list"} # Raw JSON-RPC call')
|
|
print(' onboard MyProject # Get project onboarding')
|
|
print('\nJSONPath syntax:')
|
|
print(' $ - Root object')
|
|
print(' .tools[*] - All tools')
|
|
print(' .tools[0] - First tool')
|
|
print(' .tools[*].name - All tool names')
|
|
continue
|
|
|
|
elif command.startswith("discover "):
|
|
jsonpath = command[9:]
|
|
result = browser.discover(jsonpath)
|
|
print(json.dumps(result, indent=2))
|
|
|
|
elif command.startswith("call "):
|
|
json_str = command[5:]
|
|
request = json.loads(json_str)
|
|
if "jsonrpc" not in request:
|
|
request["jsonrpc"] = "2.0"
|
|
response = await browser.call(request)
|
|
print(json.dumps(response, indent=2))
|
|
|
|
elif command.startswith("onboard "):
|
|
identity = command[8:]
|
|
response = await browser.call({
|
|
"jsonrpc": "2.0",
|
|
"method": "tools/call",
|
|
"params": {
|
|
"name": "onboarding",
|
|
"arguments": {"identity": identity}
|
|
}
|
|
})
|
|
if "result" in response:
|
|
print(response["result"]["content"][0]["text"])
|
|
|
|
elif command == "list":
|
|
response = await browser.call({
|
|
"jsonrpc": "2.0",
|
|
"method": "tools/list"
|
|
})
|
|
if "result" in response:
|
|
tools = response["result"]["tools"]
|
|
for tool in tools:
|
|
print(f"- {tool['name']}: {tool['description']}")
|
|
|
|
else:
|
|
print("Unknown command. Type 'help' for available commands.")
|
|
|
|
except KeyboardInterrupt:
|
|
print("\nUse 'exit' to quit")
|
|
except Exception as e:
|
|
print(f"Error: {e}")
|
|
|
|
|
|
def show_available_servers(config_path: Optional[str] = None):
|
|
"""Show list of available MCP servers from configuration."""
|
|
loader = ConfigLoader(Path(config_path) if config_path else None)
|
|
config = loader.load()
|
|
|
|
print("Available MCP Servers:")
|
|
print("=" * 50)
|
|
|
|
for name, server in config.servers.items():
|
|
print(f"\n{name}:")
|
|
print(f" Description: {server.description or 'No description'}")
|
|
print(f" Command: {' '.join(server.command) if server.command else 'Built-in only'}")
|
|
if server.env:
|
|
print(f" Environment: {', '.join(server.env.keys())}")
|
|
|
|
print(f"\nDefault server: {config.default_server}")
|
|
print(f"Config location: {loader.config_path}")
|
|
|
|
|
|
def show_configuration(config_path: Optional[str] = None):
|
|
"""Show current configuration file path and content."""
|
|
loader = ConfigLoader(Path(config_path) if config_path else None)
|
|
|
|
print(f"Configuration file: {loader.config_path}")
|
|
print("=" * 50)
|
|
|
|
if loader.config_path.exists():
|
|
with open(loader.config_path) as f:
|
|
print(f.read())
|
|
else:
|
|
print("Configuration file not found. Will be created on first run.")
|
|
|
|
|
|
async def test_server_connection(browser: MCPBrowser, server_name: Optional[str] = None):
|
|
"""Test connection to specified MCP server."""
|
|
print(f"Testing connection to server: {server_name or 'default'}")
|
|
print("=" * 50)
|
|
|
|
try:
|
|
await browser.initialize()
|
|
print("✓ Successfully connected to server")
|
|
|
|
# Try to list tools
|
|
response = await browser.call({
|
|
"jsonrpc": "2.0",
|
|
"method": "tools/list"
|
|
})
|
|
|
|
if "result" in response:
|
|
tools = response["result"]["tools"]
|
|
print(f"✓ Server provides {len(tools)} tools")
|
|
if browser.config.sparse_mode:
|
|
print(" (Showing sparse tools only)")
|
|
else:
|
|
print("✗ Failed to list tools")
|
|
|
|
except Exception as e:
|
|
print(f"✗ Connection failed: {e}")
|
|
return
|
|
finally:
|
|
await browser.close()
|
|
|
|
print("\nConnection test completed successfully!")
|
|
|
|
|
|
async def handle_mcp_command(args):
|
|
"""Handle MCP method commands by simulating JSON-RPC calls."""
|
|
# Check if we should use daemon mode
|
|
socket_path = get_socket_path(args.server)
|
|
|
|
if hasattr(args, 'use_daemon') and args.use_daemon and is_daemon_running(socket_path):
|
|
# Use daemon client
|
|
async with MCPBrowserClient(socket_path) as client:
|
|
request = build_mcp_request(args)
|
|
response = await client.call(request)
|
|
format_mcp_response(args, request, response)
|
|
return
|
|
|
|
# Create browser directly
|
|
config_path = Path(args.config) if args.config else None
|
|
|
|
# Set debug in config if requested
|
|
if args.debug and config_path is None:
|
|
from .config import ConfigLoader
|
|
loader = ConfigLoader()
|
|
config = loader.load()
|
|
config.debug = True
|
|
|
|
browser = MCPBrowser(
|
|
server_name=args.server,
|
|
config_path=config_path,
|
|
enable_builtin_servers=not args.no_builtin
|
|
)
|
|
|
|
try:
|
|
await browser.initialize()
|
|
|
|
# Build and send request
|
|
request = build_mcp_request(args)
|
|
response = await browser.call(request)
|
|
|
|
# Format response
|
|
format_mcp_response(args, request, response)
|
|
|
|
except Exception as e:
|
|
print(f"Error: {e}")
|
|
if args.debug:
|
|
import traceback
|
|
traceback.print_exc(file=sys.stderr)
|
|
finally:
|
|
await browser.close()
|
|
|
|
|
|
async def run_daemon_mode(browser: MCPBrowser, socket_path: Path):
|
|
"""Run MCP Browser in daemon mode."""
|
|
daemon = MCPBrowserDaemon(browser, socket_path)
|
|
await daemon.start()
|
|
|
|
|
|
async def start_daemon_background(args):
|
|
"""Start daemon in background."""
|
|
socket_path = get_socket_path(args.server)
|
|
|
|
if is_daemon_running(socket_path):
|
|
print(f"Daemon already running for server: {args.server or 'default'}")
|
|
return
|
|
|
|
# Fork to background
|
|
pid = os.fork()
|
|
if pid > 0:
|
|
# Parent process
|
|
print(f"Starting daemon in background (PID: {pid})")
|
|
return
|
|
|
|
# Child process
|
|
# Detach from terminal
|
|
os.setsid()
|
|
|
|
# Redirect stdout/stderr to log file
|
|
log_dir = socket_path.parent / "logs"
|
|
log_dir.mkdir(exist_ok=True)
|
|
log_file = log_dir / f"mcp-browser-{args.server or 'default'}.log"
|
|
|
|
with open(log_file, 'a') as log:
|
|
sys.stdout = log
|
|
sys.stderr = log
|
|
|
|
# Create browser
|
|
config_path = Path(args.config) if args.config else None
|
|
browser = MCPBrowser(
|
|
server_name=args.server,
|
|
config_path=config_path,
|
|
enable_builtin_servers=not args.no_builtin
|
|
)
|
|
|
|
# Run daemon
|
|
asyncio.run(run_daemon_mode(browser, socket_path))
|
|
|
|
|
|
def stop_daemon(args):
|
|
"""Stop running daemon."""
|
|
socket_path = get_socket_path(args.server)
|
|
|
|
if not is_daemon_running(socket_path):
|
|
print(f"No daemon running for server: {args.server or 'default'}")
|
|
return
|
|
|
|
pid_file = socket_path.with_suffix('.pid')
|
|
try:
|
|
pid = int(pid_file.read_text().strip())
|
|
os.kill(pid, signal.SIGTERM)
|
|
print(f"Sent SIGTERM to daemon (PID: {pid})")
|
|
except Exception as e:
|
|
print(f"Error stopping daemon: {e}")
|
|
|
|
|
|
def show_daemon_status(args):
|
|
"""Show daemon status."""
|
|
socket_path = get_socket_path(args.server)
|
|
|
|
if is_daemon_running(socket_path):
|
|
pid_file = socket_path.with_suffix('.pid')
|
|
pid = pid_file.read_text().strip()
|
|
print(f"Daemon running (PID: {pid})")
|
|
print(f"Socket: {socket_path}")
|
|
else:
|
|
print(f"No daemon running for server: {args.server or 'default'}")
|
|
|
|
|
|
async def run_server_mode(browser: MCPBrowser):
|
|
"""Run MCP Browser as an MCP server (stdin/stdout)."""
|
|
import sys
|
|
|
|
await browser.initialize()
|
|
|
|
# Read JSON-RPC from stdin, write to stdout
|
|
buffer = ""
|
|
while True:
|
|
try:
|
|
chunk = sys.stdin.read(4096)
|
|
if not chunk:
|
|
break
|
|
|
|
buffer += chunk
|
|
while '\n' in buffer:
|
|
line, buffer = buffer.split('\n', 1)
|
|
if line.strip():
|
|
try:
|
|
request = json.loads(line)
|
|
response = await browser.call(request)
|
|
print(json.dumps(response))
|
|
sys.stdout.flush()
|
|
except json.JSONDecodeError:
|
|
pass
|
|
|
|
except KeyboardInterrupt:
|
|
break
|
|
except EOFError:
|
|
break
|
|
|
|
|
|
async def run_server_mode_with_daemon(socket_path: Path):
|
|
"""Run as MCP server but forward to daemon."""
|
|
import sys
|
|
|
|
async with MCPBrowserClient(socket_path) as client:
|
|
# Read JSON-RPC from stdin, forward to daemon, write to stdout
|
|
buffer = ""
|
|
while True:
|
|
try:
|
|
chunk = sys.stdin.read(4096)
|
|
if not chunk:
|
|
break
|
|
|
|
buffer += chunk
|
|
while '\n' in buffer:
|
|
line, buffer = buffer.split('\n', 1)
|
|
if line.strip():
|
|
try:
|
|
request = json.loads(line)
|
|
response = await client.call(request)
|
|
print(json.dumps(response))
|
|
sys.stdout.flush()
|
|
except json.JSONDecodeError:
|
|
pass
|
|
|
|
except KeyboardInterrupt:
|
|
break
|
|
except EOFError:
|
|
break
|
|
|
|
|
|
async def interactive_mode_with_daemon(socket_path: Path):
|
|
"""Run interactive mode connected to daemon."""
|
|
async with MCPBrowserClient(socket_path) as client:
|
|
print("MCP Browser Interactive Mode (Daemon)")
|
|
print("=" * 50)
|
|
print(f"Connected to daemon at: {socket_path}")
|
|
print("Type 'help' for commands, 'exit' to quit\n")
|
|
|
|
while True:
|
|
try:
|
|
command = input("> ").strip()
|
|
|
|
if command == "exit":
|
|
break
|
|
elif command == "help":
|
|
print("\nCommands:")
|
|
print(" list - List available tools (sparse mode)")
|
|
print(" discover <path> - Discover tools using JSONPath")
|
|
print(" call <json> - Execute JSON-RPC call")
|
|
print(" exit - Exit interactive mode")
|
|
continue
|
|
|
|
elif command.startswith("call "):
|
|
json_str = command[5:]
|
|
request = json.loads(json_str)
|
|
if "jsonrpc" not in request:
|
|
request["jsonrpc"] = "2.0"
|
|
if "id" not in request:
|
|
request["id"] = 1
|
|
response = await client.call(request)
|
|
print(json.dumps(response, indent=2))
|
|
|
|
elif command == "list":
|
|
response = await client.call({
|
|
"jsonrpc": "2.0",
|
|
"id": 1,
|
|
"method": "tools/list"
|
|
})
|
|
if "result" in response:
|
|
tools = response["result"]["tools"]
|
|
for tool in tools:
|
|
print(f"- {tool['name']}: {tool['description']}")
|
|
|
|
else:
|
|
print("Unknown command. Type 'help' for available commands.")
|
|
|
|
except KeyboardInterrupt:
|
|
print("\nUse 'exit' to quit")
|
|
except Exception as e:
|
|
print(f"Error: {e}")
|
|
|
|
|
|
def main():
|
|
"""Main entry point."""
|
|
parser = argparse.ArgumentParser(
|
|
description="MCP Browser - Universal Model Context Protocol Interface",
|
|
epilog="""
|
|
Examples:
|
|
# Interactive mode
|
|
mcp-browser # Start interactive mode with default server
|
|
mcp-browser --server brave-search # Use Brave Search server
|
|
|
|
# Configuration
|
|
mcp-browser --list-servers # List configured servers
|
|
mcp-browser --show-config # Show current configuration
|
|
mcp-browser --test # Test server connection
|
|
|
|
# MCP method commands
|
|
mcp-browser tools-list # List available tools
|
|
mcp-browser tools-call brave_web_search '{"query": "MCP protocol"}'
|
|
mcp-browser resources-list # List available resources
|
|
mcp-browser resources-read "file:///path/to/file"
|
|
mcp-browser prompts-list # List available prompts
|
|
mcp-browser prompts-get "greeting" --arguments '{"name": "Alice"}'
|
|
|
|
# Raw JSON-RPC
|
|
mcp-browser jsonrpc '{"method": "tools/list", "params": {}}'
|
|
|
|
# Server mode
|
|
mcp-browser --mode server # Run as MCP server (stdin/stdout)
|
|
|
|
Configuration:
|
|
Default config: ~/.claude/mcp-browser/config.yaml
|
|
First run creates default configuration with examples
|
|
|
|
Environment:
|
|
Set API keys as needed: BRAVE_API_KEY, GITHUB_TOKEN, etc.
|
|
Or source from: source ~/.secrets/api-keys.sh
|
|
""",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter
|
|
)
|
|
parser.add_argument("--server", "-s", help="Target MCP server name (see --list-servers)")
|
|
parser.add_argument("--config", "-c", help="Custom configuration file path")
|
|
parser.add_argument("--mode", choices=["interactive", "server", "daemon"],
|
|
default="interactive", help="Operating mode (default: interactive)")
|
|
parser.add_argument("--no-sparse", action="store_true",
|
|
help="Disable sparse mode (show all tools)")
|
|
parser.add_argument("--no-builtin", action="store_true",
|
|
help="Disable built-in servers (screen, memory, patterns)")
|
|
parser.add_argument("--list-servers", action="store_true",
|
|
help="List available MCP servers from config")
|
|
parser.add_argument("--show-config", action="store_true",
|
|
help="Show current configuration path and content")
|
|
parser.add_argument("--test", action="store_true",
|
|
help="Test connection to specified server")
|
|
parser.add_argument("--debug", action="store_true",
|
|
help="Enable debug output")
|
|
parser.add_argument("--use-daemon", action="store_true",
|
|
help="Automatically use daemon if available")
|
|
parser.add_argument("--daemon-start", action="store_true",
|
|
help="Start daemon in background")
|
|
parser.add_argument("--daemon-stop", action="store_true",
|
|
help="Stop running daemon")
|
|
parser.add_argument("--daemon-status", action="store_true",
|
|
help="Check daemon status")
|
|
|
|
# MCP method commands
|
|
subparsers = parser.add_subparsers(dest="command", help="MCP methods")
|
|
|
|
# tools/list command
|
|
list_tools = subparsers.add_parser("tools-list", help="List available tools")
|
|
|
|
# tools/call command
|
|
call_tool = subparsers.add_parser("tools-call", help="Call a tool")
|
|
call_tool.add_argument("name", help="Tool name")
|
|
call_tool.add_argument("arguments", help="Tool arguments as JSON")
|
|
|
|
# resources/list command
|
|
list_resources = subparsers.add_parser("resources-list", help="List available resources")
|
|
|
|
# resources/read command
|
|
read_resource = subparsers.add_parser("resources-read", help="Read a resource")
|
|
read_resource.add_argument("uri", help="Resource URI")
|
|
|
|
# prompts/list command
|
|
list_prompts = subparsers.add_parser("prompts-list", help="List available prompts")
|
|
|
|
# prompts/get command
|
|
get_prompt = subparsers.add_parser("prompts-get", help="Get a prompt")
|
|
get_prompt.add_argument("name", help="Prompt name")
|
|
get_prompt.add_argument("--arguments", "-a", help="Prompt arguments as JSON", default="{}")
|
|
|
|
# completion command
|
|
completion = subparsers.add_parser("completion", help="Get completion")
|
|
completion.add_argument("--ref", help="Reference for completion")
|
|
completion.add_argument("--argument", help="Argument name")
|
|
|
|
# Generic JSON-RPC command
|
|
jsonrpc = subparsers.add_parser("jsonrpc", help="Send raw JSON-RPC request")
|
|
jsonrpc.add_argument("request", help="JSON-RPC request")
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Handle special commands first
|
|
if args.list_servers:
|
|
show_available_servers(args.config)
|
|
return
|
|
|
|
if args.show_config:
|
|
show_configuration(args.config)
|
|
return
|
|
|
|
# Handle daemon management commands
|
|
if args.daemon_start:
|
|
asyncio.run(start_daemon_background(args))
|
|
return
|
|
|
|
if args.daemon_stop:
|
|
stop_daemon(args)
|
|
return
|
|
|
|
if args.daemon_status:
|
|
show_daemon_status(args)
|
|
return
|
|
|
|
# Handle MCP method commands
|
|
if args.command:
|
|
asyncio.run(handle_mcp_command(args))
|
|
return
|
|
|
|
# Create browser
|
|
config_path = Path(args.config) if args.config else None
|
|
browser = MCPBrowser(
|
|
server_name=args.server,
|
|
config_path=config_path,
|
|
enable_builtin_servers=not args.no_builtin
|
|
)
|
|
|
|
# Handle test mode
|
|
if args.test:
|
|
asyncio.run(test_server_connection(browser, args.server))
|
|
return
|
|
|
|
# Run in appropriate mode
|
|
if args.mode == "server":
|
|
# Run as MCP server (stdin/stdout) - can connect to daemon
|
|
socket_path = get_socket_path(args.server)
|
|
if args.use_daemon and is_daemon_running(socket_path):
|
|
# Use daemon as backend
|
|
asyncio.run(run_server_mode_with_daemon(socket_path))
|
|
else:
|
|
asyncio.run(run_server_mode(browser))
|
|
elif args.mode == "daemon":
|
|
# Run as daemon
|
|
socket_path = get_socket_path(args.server)
|
|
asyncio.run(run_daemon_mode(browser, socket_path))
|
|
else:
|
|
# Interactive mode - can use daemon if available
|
|
socket_path = get_socket_path(args.server)
|
|
if args.use_daemon and is_daemon_running(socket_path):
|
|
asyncio.run(interactive_mode_with_daemon(socket_path))
|
|
else:
|
|
asyncio.run(async_main(browser))
|
|
|
|
|
|
async def async_main(browser: MCPBrowser):
|
|
"""Async main for interactive mode."""
|
|
try:
|
|
await browser.initialize()
|
|
await interactive_mode(browser)
|
|
finally:
|
|
await browser.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |