From 38df5217cac32c8191d008602f5e433ba1e05040 Mon Sep 17 00:00:00 2001 From: Jonathan Wang Date: Tue, 29 Apr 2025 14:51:44 +0800 Subject: [PATCH] ref: move schema to core --- docs/router_tech_design.md | 2 +- src/mcpm/clients/base.py | 2 +- src/mcpm/clients/managers/claude_desktop.py | 2 +- .../clients/managers/continue_extension.py | 2 +- src/mcpm/clients/managers/fiveire.py | 2 +- src/mcpm/clients/managers/goose.py | 2 +- src/mcpm/clients/managers/trae.py | 2 +- src/mcpm/clients/managers/windsurf.py | 2 +- src/mcpm/commands/list.py | 2 +- src/mcpm/commands/profile.py | 2 +- src/mcpm/commands/server_operations/common.py | 2 +- src/mcpm/commands/server_operations/custom.py | 2 +- src/mcpm/commands/server_operations/pop.py | 2 +- src/mcpm/core/schema.py | 84 +++++++++++++++++++ src/mcpm/profile/profile_config.py | 2 +- src/mcpm/router/client_connection.py | 2 +- src/mcpm/router/router.py | 2 +- src/mcpm/schemas/full_server_config.py | 2 +- src/mcpm/schemas/server_config.py | 79 +---------------- src/mcpm/utils/display.py | 2 +- src/mcpm/utils/router_server.py | 2 +- tests/test_add.py | 2 +- tests/test_windsurf.py | 2 +- 23 files changed, 109 insertions(+), 96 deletions(-) create mode 100644 src/mcpm/core/schema.py diff --git a/docs/router_tech_design.md b/docs/router_tech_design.md index cf84fd8b..d8de4aab 100644 --- a/docs/router_tech_design.md +++ b/docs/router_tech_design.md @@ -49,7 +49,7 @@ flowchart TB ```python import asyncio from mcpm.router import MCPRouter -from mcpm.schema.server_config import STDIOServerConfig, SSEServerConfig +from mcpm.core.schema import STDIOServerConfig, SSEServerConfig async def main(): # Create a router diff --git a/src/mcpm/clients/base.py b/src/mcpm/clients/base.py index a9f2134d..dba5c7a6 100644 --- a/src/mcpm/clients/base.py +++ b/src/mcpm/clients/base.py @@ -13,7 +13,7 @@ from pydantic import TypeAdapter from ruamel.yaml import YAML -from mcpm.schemas.server_config import ServerConfig, STDIOServerConfig +from mcpm.core.schema import ServerConfig, STDIOServerConfig from mcpm.utils.config import ROUTER_SERVER_NAME from mcpm.utils.router_server import format_server_url diff --git a/src/mcpm/clients/managers/claude_desktop.py b/src/mcpm/clients/managers/claude_desktop.py index 3cf22859..b23a8498 100644 --- a/src/mcpm/clients/managers/claude_desktop.py +++ b/src/mcpm/clients/managers/claude_desktop.py @@ -7,7 +7,7 @@ from typing import Any, Dict from mcpm.clients.base import JSONClientManager -from mcpm.schemas.server_config import ServerConfig, SSEServerConfig +from mcpm.core.schema import ServerConfig, SSEServerConfig from mcpm.utils.router_server import format_server_url_with_proxy_headers logger = logging.getLogger(__name__) diff --git a/src/mcpm/clients/managers/continue_extension.py b/src/mcpm/clients/managers/continue_extension.py index cf30b800..be811979 100644 --- a/src/mcpm/clients/managers/continue_extension.py +++ b/src/mcpm/clients/managers/continue_extension.py @@ -9,7 +9,7 @@ from pydantic import TypeAdapter from mcpm.clients.base import YAMLClientManager -from mcpm.schemas.server_config import ServerConfig, STDIOServerConfig +from mcpm.core.schema import ServerConfig, STDIOServerConfig from mcpm.utils.router_server import format_server_url_with_proxy_headers logger = logging.getLogger(__name__) diff --git a/src/mcpm/clients/managers/fiveire.py b/src/mcpm/clients/managers/fiveire.py index 3a9510d5..c45b0e5a 100644 --- a/src/mcpm/clients/managers/fiveire.py +++ b/src/mcpm/clients/managers/fiveire.py @@ -7,7 +7,7 @@ from pydantic import TypeAdapter from mcpm.clients.base import JSONClientManager -from mcpm.schemas.server_config import ServerConfig, STDIOServerConfig +from mcpm.core.schema import ServerConfig, STDIOServerConfig logger = logging.getLogger(__name__) diff --git a/src/mcpm/clients/managers/goose.py b/src/mcpm/clients/managers/goose.py index d2e45ee6..65492c2b 100644 --- a/src/mcpm/clients/managers/goose.py +++ b/src/mcpm/clients/managers/goose.py @@ -9,7 +9,7 @@ from pydantic import TypeAdapter from mcpm.clients.base import YAMLClientManager -from mcpm.schemas.server_config import ServerConfig, STDIOServerConfig +from mcpm.core.schema import ServerConfig, STDIOServerConfig logger = logging.getLogger(__name__) diff --git a/src/mcpm/clients/managers/trae.py b/src/mcpm/clients/managers/trae.py index f1a57e9d..2fa60679 100644 --- a/src/mcpm/clients/managers/trae.py +++ b/src/mcpm/clients/managers/trae.py @@ -5,7 +5,7 @@ from typing import Any, Dict from mcpm.clients.base import JSONClientManager -from mcpm.schemas.server_config import ServerConfig +from mcpm.core.schema import ServerConfig logger = logging.getLogger(__name__) diff --git a/src/mcpm/clients/managers/windsurf.py b/src/mcpm/clients/managers/windsurf.py index a33514cf..9ccb5442 100644 --- a/src/mcpm/clients/managers/windsurf.py +++ b/src/mcpm/clients/managers/windsurf.py @@ -7,7 +7,7 @@ from typing import Any, Dict from mcpm.clients.base import JSONClientManager -from mcpm.schemas.server_config import ServerConfig, STDIOServerConfig +from mcpm.core.schema import ServerConfig, STDIOServerConfig logger = logging.getLogger(__name__) diff --git a/src/mcpm/commands/list.py b/src/mcpm/commands/list.py index 80f9400d..21ed6f8d 100644 --- a/src/mcpm/commands/list.py +++ b/src/mcpm/commands/list.py @@ -9,8 +9,8 @@ from mcpm.clients.client_config import ClientConfigManager from mcpm.clients.client_registry import ClientRegistry from mcpm.commands.server_operations.common import determine_scope +from mcpm.core.schema import ServerConfig from mcpm.profile.profile_config import ProfileConfigManager -from mcpm.schemas.server_config import ServerConfig from mcpm.utils.display import print_client_error, print_server_config from mcpm.utils.scope import ScopeType, format_scope diff --git a/src/mcpm/commands/profile.py b/src/mcpm/commands/profile.py index 77152884..555d3743 100644 --- a/src/mcpm/commands/profile.py +++ b/src/mcpm/commands/profile.py @@ -3,8 +3,8 @@ from rich.table import Table from mcpm.clients.client_registry import ClientRegistry +from mcpm.core.schema import STDIOServerConfig from mcpm.profile.profile_config import ProfileConfigManager -from mcpm.schemas.server_config import STDIOServerConfig from mcpm.utils.config import ConfigManager profile_config_manager = ProfileConfigManager() diff --git a/src/mcpm/commands/server_operations/common.py b/src/mcpm/commands/server_operations/common.py index 71798e22..03f86d83 100644 --- a/src/mcpm/commands/server_operations/common.py +++ b/src/mcpm/commands/server_operations/common.py @@ -1,8 +1,8 @@ from rich.console import Console from mcpm.clients.client_registry import ClientRegistry +from mcpm.core.schema import ServerConfig from mcpm.profile.profile_config import ProfileConfigManager -from mcpm.schemas.server_config import ServerConfig from mcpm.utils.display import print_active_scope, print_no_active_scope from mcpm.utils.scope import ScopeType, extract_from_scope, parse_server diff --git a/src/mcpm/commands/server_operations/custom.py b/src/mcpm/commands/server_operations/custom.py index 925de4b1..e3797167 100644 --- a/src/mcpm/commands/server_operations/custom.py +++ b/src/mcpm/commands/server_operations/custom.py @@ -5,7 +5,7 @@ from rich.prompt import Confirm, Prompt from mcpm.commands.server_operations.common import client_add_server, determine_scope, profile_add_server -from mcpm.schemas.server_config import SSEServerConfig, STDIOServerConfig +from mcpm.core.schema import SSEServerConfig, STDIOServerConfig from mcpm.utils.display import print_server_config from mcpm.utils.scope import ScopeType diff --git a/src/mcpm/commands/server_operations/pop.py b/src/mcpm/commands/server_operations/pop.py index 07c904a8..35feed80 100644 --- a/src/mcpm/commands/server_operations/pop.py +++ b/src/mcpm/commands/server_operations/pop.py @@ -9,8 +9,8 @@ from mcpm.clients.client_config import ClientConfigManager from mcpm.clients.client_registry import ClientRegistry from mcpm.commands.server_operations.common import determine_target +from mcpm.core.schema import ServerConfig from mcpm.profile.profile_config import ProfileConfigManager -from mcpm.schemas.server_config import ServerConfig from mcpm.utils.scope import ScopeType, format_scope console = Console() diff --git a/src/mcpm/core/schema.py b/src/mcpm/core/schema.py new file mode 100644 index 00000000..50b346fa --- /dev/null +++ b/src/mcpm/core/schema.py @@ -0,0 +1,84 @@ +from typing import Any, Dict, List, Optional, Union + +from pydantic import BaseModel + + +class BaseServerConfig(BaseModel): + name: str + + def to_dict(self) -> Dict[str, Any]: + return self.model_dump() + + +class STDIOServerConfig(BaseServerConfig): + command: str + args: List[str] = [] + env: Dict[str, str] = {} + + def get_filtered_env_vars(self, env: Dict[str, str]) -> Dict[str, str]: + """Get filtered environment variables with empty values removed + + This is a utility for clients to filter out empty environment + variables, regardless of client-specific formatting. + + Args: + env: Dictionary of environment variables to use for resolving + ${VAR_NAME} references. + + Returns: + Dictionary of non-empty environment variables + """ + if not self.env: + return {} + + # Use provided environment without falling back to os.environ + environment = env + + # Keep all environment variables, including empty strings + filtered_env = {} + for key, value in self.env.items(): + # For environment variable references like ${VAR_NAME}, check if the variable exists + # and has a non-empty value. If it doesn't exist or is empty, exclude it. + if value is not None and isinstance(value, str): + if value.startswith("${") and value.endswith("}"): + # Extract the variable name from ${VAR_NAME} + env_var_name = value[2:-1] + env_value = environment.get(env_var_name, "") + # Include all values, even empty ones + filtered_env[key] = env_value + else: + # Include all values, even empty ones + filtered_env[key] = value + + return filtered_env + + +class SSEServerConfig(BaseServerConfig): + url: str + headers: Dict[str, Any] = {} + + def to_mcp_proxy_stdio(self) -> STDIOServerConfig: + proxy_args = [ + "mcp-proxy", + self.url, + ] + if self.headers: + proxy_args.append("--headers") + for key, value in self.headers.items(): + proxy_args.append(f"{key}") + proxy_args.append(f"{value}") + + return STDIOServerConfig( + name=self.name, + command="uvx", + args=proxy_args, + ) + + +ServerConfig = Union[STDIOServerConfig, SSEServerConfig] + + +class Profile(BaseModel): + name: str + api_key: Optional[str] + servers: list[ServerConfig] diff --git a/src/mcpm/profile/profile_config.py b/src/mcpm/profile/profile_config.py index 6fb62877..0293e32c 100644 --- a/src/mcpm/profile/profile_config.py +++ b/src/mcpm/profile/profile_config.py @@ -4,7 +4,7 @@ from pydantic import TypeAdapter -from mcpm.schemas.server_config import ServerConfig +from mcpm.core.schema import ServerConfig DEFAULT_PROFILE_PATH = os.path.expanduser("~/.config/mcpm/profiles.json") diff --git a/src/mcpm/router/client_connection.py b/src/mcpm/router/client_connection.py index 1a4f4ae1..bd7df88b 100644 --- a/src/mcpm/router/client_connection.py +++ b/src/mcpm/router/client_connection.py @@ -5,7 +5,7 @@ from mcp import ClientSession, InitializeResult, StdioServerParameters, stdio_client from mcp.client.sse import sse_client -from mcpm.schemas.server_config import ServerConfig, SSEServerConfig, STDIOServerConfig +from mcpm.core.schema import ServerConfig, SSEServerConfig, STDIOServerConfig logger = logging.getLogger(__name__) diff --git a/src/mcpm/router/router.py b/src/mcpm/router/router.py index 64df52d2..2d1085d1 100644 --- a/src/mcpm/router/router.py +++ b/src/mcpm/router/router.py @@ -19,10 +19,10 @@ from starlette.routing import Mount, Route from starlette.types import AppType, Lifespan +from mcpm.core.schema import ServerConfig from mcpm.monitor.base import AccessEventType from mcpm.monitor.event import trace_event from mcpm.profile.profile_config import ProfileConfigManager -from mcpm.schemas.server_config import ServerConfig from mcpm.utils.config import PROMPT_SPLITOR, RESOURCE_SPLITOR, RESOURCE_TEMPLATE_SPLITOR, TOOL_SPLITOR from mcpm.utils.errlog_manager import ServerErrorLogManager diff --git a/src/mcpm/schemas/full_server_config.py b/src/mcpm/schemas/full_server_config.py index dd73e82b..bada6d10 100644 --- a/src/mcpm/schemas/full_server_config.py +++ b/src/mcpm/schemas/full_server_config.py @@ -4,7 +4,7 @@ from pydantic import BaseModel, Field, model_validator -from mcpm.schemas.server_config import ServerConfig, STDIOServerConfig +from mcpm.core.schema import ServerConfig, STDIOServerConfig class FullServerConfig(BaseModel): diff --git a/src/mcpm/schemas/server_config.py b/src/mcpm/schemas/server_config.py index e96764a3..8ab22973 100644 --- a/src/mcpm/schemas/server_config.py +++ b/src/mcpm/schemas/server_config.py @@ -1,78 +1,7 @@ -from typing import Any, Dict, List, Union +import warnings -from pydantic import BaseModel +from mcpm.core.schema import ServerConfig, SSEServerConfig, STDIOServerConfig +__all__ = ["ServerConfig", "SSEServerConfig", "STDIOServerConfig"] -class BaseServerConfig(BaseModel): - name: str - - def to_dict(self) -> Dict[str, Any]: - return self.model_dump() - - -class STDIOServerConfig(BaseServerConfig): - command: str - args: List[str] = [] - env: Dict[str, str] = {} - - def get_filtered_env_vars(self, env: Dict[str, str]) -> Dict[str, str]: - """Get filtered environment variables with empty values removed - - This is a utility for clients to filter out empty environment - variables, regardless of client-specific formatting. - - Args: - env: Dictionary of environment variables to use for resolving - ${VAR_NAME} references. - - Returns: - Dictionary of non-empty environment variables - """ - if not self.env: - return {} - - # Use provided environment without falling back to os.environ - environment = env - - # Keep all environment variables, including empty strings - filtered_env = {} - for key, value in self.env.items(): - # For environment variable references like ${VAR_NAME}, check if the variable exists - # and has a non-empty value. If it doesn't exist or is empty, exclude it. - if value is not None and isinstance(value, str): - if value.startswith("${") and value.endswith("}"): - # Extract the variable name from ${VAR_NAME} - env_var_name = value[2:-1] - env_value = environment.get(env_var_name, "") - # Include all values, even empty ones - filtered_env[key] = env_value - else: - # Include all values, even empty ones - filtered_env[key] = value - - return filtered_env - - -class SSEServerConfig(BaseServerConfig): - url: str - headers: Dict[str, Any] = {} - - def to_mcp_proxy_stdio(self) -> STDIOServerConfig: - proxy_args = [ - "mcp-proxy", - self.url, - ] - if self.headers: - proxy_args.append("--headers") - for key, value in self.headers.items(): - proxy_args.append(f"{key}") - proxy_args.append(f"{value}") - - return STDIOServerConfig( - name=self.name, - command="uvx", - args=proxy_args, - ) - - -ServerConfig = Union[STDIOServerConfig, SSEServerConfig] +warnings.warn("mcpm.schemas.server_config is deprecated, use mcpm.core.schema instead", DeprecationWarning) diff --git a/src/mcpm/utils/display.py b/src/mcpm/utils/display.py index 342a29ca..af3bede2 100644 --- a/src/mcpm/utils/display.py +++ b/src/mcpm/utils/display.py @@ -6,7 +6,7 @@ from rich.markup import escape from rich.table import Table -from mcpm.schemas.server_config import ServerConfig, SSEServerConfig +from mcpm.core.schema import ServerConfig, SSEServerConfig from mcpm.utils.scope import CLIENT_PREFIX, PROFILE_PREFIX console = Console() diff --git a/src/mcpm/utils/router_server.py b/src/mcpm/utils/router_server.py index 4a2247ab..3a1cf93a 100644 --- a/src/mcpm/utils/router_server.py +++ b/src/mcpm/utils/router_server.py @@ -1,5 +1,5 @@ from mcpm.clients.base import ROUTER_SERVER_NAME -from mcpm.schemas.server_config import ServerConfig, SSEServerConfig, STDIOServerConfig +from mcpm.core.schema import ServerConfig, SSEServerConfig, STDIOServerConfig def format_server_url(client: str, profile: str, router_url: str) -> ServerConfig: diff --git a/tests/test_add.py b/tests/test_add.py index 2c6531e3..77c4b94a 100644 --- a/tests/test_add.py +++ b/tests/test_add.py @@ -4,7 +4,7 @@ from mcpm.clients.client_registry import ClientRegistry from mcpm.commands.server_operations.add import add -from mcpm.schemas.server_config import SSEServerConfig +from mcpm.core.schema import SSEServerConfig from mcpm.utils.repository import RepositoryManager diff --git a/tests/test_windsurf.py b/tests/test_windsurf.py index 20081b0d..0ce3c103 100644 --- a/tests/test_windsurf.py +++ b/tests/test_windsurf.py @@ -13,7 +13,7 @@ import pytest from mcpm.clients.managers.windsurf import WindsurfManager -from mcpm.schemas.server_config import ServerConfig, STDIOServerConfig +from mcpm.core.schema import ServerConfig, STDIOServerConfig from mcpm.utils.config import ConfigManager