Skip to main content
Glama
math_server.py7.51 kB
#!/usr/bin/env python3 # src/chuk_mcp_math_server/math_server.py """ Mathematical MCP Server - using chuk-mcp-server framework. """ import json import logging from typing import Dict, Any from chuk_mcp_server import ChukMCPServer from .config import ServerConfig from .function_filter import FunctionFilter logger = logging.getLogger(__name__) class ConfigurableMCPMathServer: """MCP Math Server with granular control over exposed mathematical functionality.""" def __init__(self, config: ServerConfig): self.config = config # Configure logging early log_level = getattr(logging, config.log_level.upper()) logging.getLogger().setLevel(log_level) # For stdio mode with WARNING or higher, silence noisy dependencies if config.transport == "stdio" and log_level >= logging.WARNING: logging.getLogger("chuk_mcp_math").setLevel(logging.WARNING) logging.getLogger("chuk_mcp_math_server.function_filter").setLevel( logging.WARNING ) logging.getLogger("chuk_mcp_math_server.math_server").setLevel( logging.WARNING ) logging.getLogger("chuk_mcp_math_server.cli").setLevel(logging.WARNING) self.function_filter = FunctionFilter(config) # Create the underlying chuk-mcp-server instance self.mcp_server = ChukMCPServer( name=config.server_name or "chuk-mcp-math-server" ) # Register all tools and resources self._register_math_tools() self._register_math_resources() # Log math-specific initialization stats = self.function_filter.get_function_stats() logger.info( f"Math server initialized with {stats['total_filtered']}/{stats['total_available']} functions" ) def _register_math_tools(self): """Register filtered mathematical functions as MCP tools.""" # Get filtered functions filtered_functions = self.function_filter.get_filtered_functions() registered_count = 0 for qualified_name, func_spec in filtered_functions.items(): try: # Create a wrapper function that will be registered as a tool # We need to dynamically create the function with proper signature self._register_dynamic_tool(func_spec) registered_count += 1 except Exception as e: logger.error(f"Failed to register tool {qualified_name}: {e}") logger.info(f"Registered {registered_count} mathematical tools") def _register_dynamic_tool(self, func_spec): """Dynamically register a mathematical function as a tool.""" # Get the original function original_func = func_spec.function_ref if not original_func: logger.warning(f"No function reference for {func_spec.function_name}") return # Create description with domain and category info description = f"{func_spec.description} (Domain: {func_spec.namespace}, Category: {func_spec.category})" # Register the tool using the server's tool decorator # The function already has proper type hints and async handling from chuk_mcp_math self.mcp_server.tool(name=func_spec.function_name, description=description)( original_func ) def _register_math_resources(self): """Register mathematical resources with configuration info.""" # Create a reference to self for use in closures server = self # Available functions list resource @self.mcp_server.resource( # type: ignore[untyped-decorator] "math://available-functions", name="Available Functions", description="List of currently available mathematical functions after filtering", mime_type="application/json", ) async def available_functions() -> str: filtered_functions = server.function_filter.get_filtered_functions() functions_by_domain: dict[str, list[dict[str, Any]]] = {} for func_spec in filtered_functions.values(): domain = func_spec.namespace if domain not in functions_by_domain: functions_by_domain[domain] = [] functions_by_domain[domain].append( { "name": func_spec.function_name, "description": func_spec.description, "category": func_spec.category, "async_native": func_spec.is_async_native, "cached": func_spec.cache_strategy != "none", } ) return json.dumps( { "total_functions": len(filtered_functions), "functions_by_domain": functions_by_domain, "filtering_applied": server.function_filter.get_function_stats()[ "filtering_active" ], }, indent=2, ) # Function statistics resource @self.mcp_server.resource( # type: ignore[untyped-decorator] "math://function-stats", name="Function Statistics", description="Statistics about function filtering and availability", mime_type="application/json", ) async def function_stats() -> str: stats = server.function_filter.get_function_stats() return json.dumps(stats, indent=2) # Server configuration resource @self.mcp_server.resource( # type: ignore[untyped-decorator] "math://server-config", name="Server Configuration", description="Current server configuration", mime_type="application/json", ) async def server_config() -> str: return json.dumps(server.config.model_dump(), indent=2) logger.info("Registered mathematical resources") def get_function_stats(self) -> Dict[str, Any]: """Get function filtering statistics.""" return self.function_filter.get_function_stats() def run(self): """Run the MCP server.""" # Determine transport mode if self.config.transport == "http": logger.info( f"Starting HTTP server on {self.config.host}:{self.config.port}" ) self.mcp_server.run( host=self.config.host, port=self.config.port, log_level=self.config.log_level.lower(), ) else: logger.info("Starting stdio server") self.mcp_server.run(stdio=True, log_level=self.config.log_level.lower()) # Factory function for easy server creation def create_math_server(config_file=None, **kwargs) -> ConfigurableMCPMathServer: """Create a configured math server instance. Args: config_file: Optional path to configuration file **kwargs: Additional configuration options Returns: ConfigurableMCPMathServer instance """ if config_file: config = ServerConfig.from_file(config_file) # Apply any overrides for key, value in kwargs.items(): if hasattr(config, key): setattr(config, key, value) else: config = ServerConfig(**kwargs) return ConfigurableMCPMathServer(config)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/chrishayuk/chuk-mcp-math-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server