Skip to main content
Glama

MCP Server for Odoo

model_discovery.py•6.02 kB
"""Test helper for discovering available MCP models. This module provides utilities for making tests model-agnostic by discovering and using whatever models are available. """ import logging from dataclasses import dataclass from typing import Dict, List, Optional from mcp_server_odoo.access_control import AccessController from mcp_server_odoo.config import OdooConfig logger = logging.getLogger(__name__) @dataclass class TestModelInfo: """Information about a model available for testing.""" model: str can_read: bool can_write: bool can_create: bool can_unlink: bool @property def readable(self) -> bool: """Check if model is readable.""" return self.can_read @property def writable(self) -> bool: """Check if model is writable.""" return self.can_write @property def fully_accessible(self) -> bool: """Check if model has all permissions.""" return all([self.can_read, self.can_write, self.can_create, self.can_unlink]) class ModelDiscovery: """Discovers available models for testing. This class helps tests adapt to whatever models are currently enabled in the MCP configuration. """ def __init__(self, config: OdooConfig): """Initialize model discovery. Args: config: Odoo configuration """ self.config = config self.controller = AccessController(config) self._cache: Optional[Dict[str, TestModelInfo]] = None def discover_models(self) -> Dict[str, TestModelInfo]: """Discover all available models and their permissions. Returns: Dictionary mapping model names to their test info """ if self._cache is not None: return self._cache models = {} try: # Get all enabled models enabled = self.controller.get_enabled_models() for model_info in enabled: model_name = model_info.get("model") if not model_name: continue # Get permissions for this model perms = self.controller.get_model_permissions(model_name) models[model_name] = TestModelInfo( model=model_name, can_read=perms.can_read, can_write=perms.can_write, can_create=perms.can_create, can_unlink=perms.can_unlink, ) except Exception as e: logger.warning(f"Failed to discover models: {e}") self._cache = models return models def get_readable_model(self) -> Optional[TestModelInfo]: """Get any model with read permission. Returns: TestModelInfo or None if no readable models """ models = self.discover_models() for model in models.values(): if model.readable: return model return None def get_writable_model(self) -> Optional[TestModelInfo]: """Get any model with write permission. Returns: TestModelInfo or None if no writable models """ models = self.discover_models() for model in models.values(): if model.writable: return model return None def get_fully_accessible_model(self) -> Optional[TestModelInfo]: """Get any model with all permissions. Returns: TestModelInfo or None if no fully accessible models """ models = self.discover_models() for model in models.values(): if model.fully_accessible: return model return None def get_common_models(self) -> List[TestModelInfo]: """Get commonly available models for testing. Prioritizes models like res.partner, res.users, etc. Returns: List of available common models """ models = self.discover_models() common_model_names = [ "res.partner", "res.users", "res.company", "product.product", "sale.order", "purchase.order", ] available = [] for name in common_model_names: if name in models: available.append(models[name]) return available def get_disabled_model(self) -> str: """Get a model name that is NOT enabled. Returns: Name of a model that should not be accessible """ models = self.discover_models() # These system models are rarely enabled in MCP system_models = [ "ir.model", "ir.model.fields", "ir.ui.view", "ir.rule", "base.automation", "ir.config_parameter", "ir.cron", "ir.module.module", ] for model in system_models: if model not in models: return model # If all system models are somehow enabled, # return a non-existent model return "non.existent.model" def require_readable_model(self) -> TestModelInfo: """Get a readable model or skip test. Returns: TestModelInfo for a readable model Raises: pytest.skip if no readable models available """ import pytest model = self.get_readable_model() if not model: pytest.skip("No readable models available for testing") return model def require_writable_model(self) -> TestModelInfo: """Get a writable model or skip test. Returns: TestModelInfo for a writable model Raises: pytest.skip if no writable models available """ import pytest model = self.get_writable_model() if not model: pytest.skip("No writable models available for testing") return model

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/AlejandroLaraPolanco/mcp-odoo'

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