Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/mcpm/commands/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
from rich.prompt import Confirm

from mcpm.clients.client_registry import ClientRegistry
from mcpm.core.utils.log_manager import get_log_directory
from mcpm.router.share import Tunnel
from mcpm.utils.config import MCPM_AUTH_HEADER, MCPM_PROFILE_HEADER, ConfigManager
from mcpm.utils.platform import get_log_directory, get_pid_directory
from mcpm.utils.platform import get_pid_directory

logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)
Expand Down
377 changes: 377 additions & 0 deletions src/mcpm/core/router/router.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,13 +1,45 @@
import os
import sys
from pathlib import Path
from typing import Optional, TextIO

from .platform import get_log_directory

def get_log_directory(app_name: str = "mcpm") -> Path:
"""
Return the appropriate log directory path based on the current operating system.

Args:
app_name: The name of the application, used in the path

Returns:
Path object representing the log directory
"""
# macOS
if sys.platform == "darwin":
return Path.home() / "Library" / "Logs" / app_name / "logs"

# Windows
elif sys.platform == "win32":
localappdata = os.environ.get("LOCALAPPDATA")
if localappdata:
return Path(localappdata) / app_name / "logs"
return Path.home() / "AppData" / "Local" / app_name / "logs"

# Linux and other Unix-like systems
else:
# Check if XDG_DATA_HOME is defined
xdg_data_home = os.environ.get("XDG_DATA_HOME")
if xdg_data_home:
return Path(xdg_data_home) / app_name / "logs"

# Default to ~/.local/share if XDG_DATA_HOME is not defined
return Path.home() / ".local" / "share" / app_name / "logs"


DEFAULT_ROOT_STDERR_LOG_DIR = get_log_directory("mcpm") / "errlogs"


class ServerErrorLogManager:
class ServerLogManager:
"""
A manager for server error logs.
"""
Expand Down
2 changes: 1 addition & 1 deletion src/mcpm/monitor/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
TextContent,
)

from mcpm.utils.config import PROMPT_SPLITOR, RESOURCE_SPLITOR, TOOL_SPLITOR
from mcpm.core.router.router import PROMPT_SPLITOR, RESOURCE_SPLITOR, TOOL_SPLITOR

from .base import AccessEventType
from .duckdb import DuckDBAccessMonitor
Expand Down
2 changes: 1 addition & 1 deletion src/mcpm/router/app.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import logging
import os

from mcpm.core.utils.log_manager import get_log_directory
from mcpm.monitor.event import monitor
from mcpm.router.router import MCPRouter
from mcpm.router.router_config import RouterConfig
from mcpm.utils.config import ConfigManager
from mcpm.utils.platform import get_log_directory

LOG_DIR = get_log_directory("mcpm")
LOG_DIR.mkdir(parents=True, exist_ok=True)
Expand Down
399 changes: 34 additions & 365 deletions src/mcpm/router/router.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/mcpm/router/sse_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
from starlette.routing import Mount, Route
from starlette.types import Receive, Scope, Send

from mcpm.core.utils.log_manager import get_log_directory
from mcpm.monitor.base import AccessEventType
from mcpm.monitor.event import monitor
from mcpm.router.router import MCPRouter
from mcpm.router.transport import RouterSseTransport
from mcpm.utils.config import ConfigManager
from mcpm.utils.platform import get_log_directory

LOG_DIR = get_log_directory("mcpm")
LOG_DIR.mkdir(parents=True, exist_ok=True)
Expand Down
4 changes: 0 additions & 4 deletions src/mcpm/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@
DEFAULT_HOST = "localhost"
DEFAULT_PORT = 6276 # 6276 represents MCPM on a T9 keypad (6=M, 2=C, 7=P, 6=M)
# default splitor pattern
TOOL_SPLITOR = "_t_"
RESOURCE_SPLITOR = ":"
RESOURCE_TEMPLATE_SPLITOR = ":"
PROMPT_SPLITOR = "_p_"
DEFAULT_SHARE_ADDRESS = f"share.mcpm.sh:{DEFAULT_PORT}"
MCPM_AUTH_HEADER = "X-MCPM-SECRET"
MCPM_PROFILE_HEADER = "X-MCPM-PROFILE"
Expand Down
32 changes: 0 additions & 32 deletions src/mcpm/utils/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,6 @@
from pathlib import Path


def get_log_directory(app_name: str = "mcpm") -> Path:
"""
Return the appropriate log directory path based on the current operating system.

Args:
app_name: The name of the application, used in the path

Returns:
Path object representing the log directory
"""
# macOS
if sys.platform == "darwin":
return Path.home() / "Library" / "Logs" / app_name / "logs"

# Windows
elif sys.platform == "win32":
localappdata = os.environ.get("LOCALAPPDATA")
if localappdata:
return Path(localappdata) / app_name / "logs"
return Path.home() / "AppData" / "Local" / app_name / "logs"

# Linux and other Unix-like systems
else:
# Check if XDG_DATA_HOME is defined
xdg_data_home = os.environ.get("XDG_DATA_HOME")
if xdg_data_home:
return Path(xdg_data_home) / app_name / "logs"

# Default to ~/.local/share if XDG_DATA_HOME is not defined
return Path.home() / ".local" / "share" / app_name / "logs"


def get_pid_directory(app_name: str = "mcpm") -> Path:
"""
Return the appropriate PID directory path based on the current operating system.
Expand Down
12 changes: 6 additions & 6 deletions tests/test_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from mcp import InitializeResult
from mcp.types import ListToolsResult, ServerCapabilities, Tool, ToolsCapability

from mcpm.router.client_connection import ServerConnection
from mcpm.core.router.client_connection import ServerConnection
from mcpm.router.router import MCPRouter
from mcpm.router.router_config import RouterConfig
from mcpm.schemas.server_config import RemoteServerConfig
Expand Down Expand Up @@ -82,11 +82,11 @@ def mock_get_active_servers(_profile):

# Patch the _patch_handler_func method to use our mock
with patch.object(router, "_patch_handler_func", wraps=router._patch_handler_func) as mock_patch_handler:
mock_patch_handler.return_value.get_active_servers = mock_get_active_servers
mock_patch_handler.return_value.get_target_servers = mock_get_active_servers

server_config = RemoteServerConfig(name="test-server", url="http://localhost:8080/sse")

with patch("mcpm.router.router.ServerConnection", return_value=mock_server_connection):
with patch("mcpm.core.router.router.ServerConnection", return_value=mock_server_connection):
await router.add_server("test-server", server_config)

# Verify server was added
Expand Down Expand Up @@ -114,7 +114,7 @@ async def test_add_server_unhealthy():
mock_conn = MagicMock(spec=ServerConnection)
mock_conn.healthy.return_value = False

with patch("mcpm.router.router.ServerConnection", return_value=mock_conn):
with patch("mcpm.core.router.router.ServerConnection", return_value=mock_conn):
with pytest.raises(ValueError, match="Failed to connect to server unhealthy-server"):
await router.add_server("unhealthy-server", server_config)

Expand Down Expand Up @@ -166,7 +166,7 @@ def mock_get_active_servers(_profile):

# Patch the _patch_handler_func method to use our mock
with patch.object(router, "_patch_handler_func", wraps=router._patch_handler_func) as mock_patch_handler:
mock_patch_handler.return_value.get_active_servers = mock_get_active_servers
mock_patch_handler.return_value.get_target_servers = mock_get_active_servers

# Setup initial servers with awaitable request_for_shutdown
mock_old_server = MagicMock(spec=ServerConnection)
Expand All @@ -180,7 +180,7 @@ def mock_get_active_servers(_profile):
# Configure new servers
server_configs = [RemoteServerConfig(name="test-server", url="http://localhost:8080/sse")]

with patch("mcpm.router.router.ServerConnection", return_value=mock_server_connection):
with patch("mcpm.core.router.router.ServerConnection", return_value=mock_server_connection):
await router.update_servers(server_configs)

# Verify old server was removed
Expand Down
Loading
Loading