Skip to main content
Glama
tuanle96

Odoo MCP Server

inspect_model_relationships

Read-onlyIdempotent

Examine relationships and required field metadata of any Odoo model to understand data structure and dependencies.

Instructions

Inspect model relationships and required field metadata

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
modelYes
fields_metadataNo
include_readonlyNo
include_computedNo
use_live_metadataNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • MCP tool handler for inspect_model_relationships. Accepts model, optional fields_metadata, include_readonly, include_computed, and use_live_metadata. When use_live_metadata=True (default), fetches fields_get from the Odoo server via the lifespan context's OdooClient. Delegates to inspect_model_relationships_report() for the actual report building.
    @mcp.tool(
        description="Inspect model relationships and required field metadata",
        annotations=READ_ONLY_TOOL,
        structured_output=True,
    )
    def inspect_model_relationships(
        ctx: Context,
        model: str,
        fields_metadata: Optional[Dict[str, Any]] = None,
        include_readonly: bool = True,
        include_computed: bool = True,
        use_live_metadata: bool = True,
    ) -> Dict[str, Any]:
        """
        Summarize relationship fields using provided metadata or bounded fields_get.
        """
        try:
            validate_model_name(model)
            metadata_source = "input" if fields_metadata is not None else "none"
            metadata_error = None
            if fields_metadata is None and use_live_metadata:
                metadata_source = "server"
                try:
                    odoo = ctx.request_context.lifespan_context.odoo
                    fields_metadata = odoo.get_model_fields(model)
                    if "error" in fields_metadata:
                        metadata_error = str(fields_metadata["error"])
                        fields_metadata = None
                except Exception as exc:
                    metadata_error = str(exc)
                    fields_metadata = None
            return inspect_model_relationships_report(
                model=model,
                fields_metadata=fields_metadata,
                metadata_source=metadata_source,
                metadata_error=metadata_error,
                include_readonly=include_readonly,
                include_computed=include_computed,
            )
        except Exception as e:
            return {
                "success": False,
                "tool": "inspect_model_relationships",
                "error": str(e),
            }
  • Pure function that builds the inspect_model_relationships report from field metadata. Iterates over fields_metadata, categorizing relational fields (many2one, one2many, many2many), collecting required fields, and generating create/write hints. Respects include_readonly and include_computed filters. Called by the MCP tool handler in server.py.
    def inspect_model_relationships_report(
        *,
        model: str,
        fields_metadata: dict[str, Any] | None,
        metadata_source: str,
        metadata_error: str | None = None,
        include_readonly: bool = True,
        include_computed: bool = True,
    ) -> dict[str, Any]:
        """Summarize relationship and write/create hints from fields_get metadata."""
        if not fields_metadata:
            return {
                "success": False,
                "tool": "inspect_model_relationships",
                "model": model,
                "error": metadata_error or "No field metadata available.",
                "summary": {"field_count": 0, "relationship_count": 0, "required_count": 0},
                "relationships": {"many2one": [], "one2many": [], "many2many": []},
                "required_fields": [],
                "create_hints": [],
                "write_hints": [],
                "metadata_used": {
                    "fields_get": False,
                    "source": metadata_source,
                    "error": metadata_error,
                },
            }
    
        relationships: dict[str, list[dict[str, Any]]] = {
            "many2one": [],
            "one2many": [],
            "many2many": [],
        }
        required_fields: list[dict[str, Any]] = []
        create_hints: list[dict[str, str]] = []
        write_hints: list[dict[str, str]] = []
    
        for field_name, raw_meta in sorted(fields_metadata.items()):
            if not isinstance(raw_meta, dict):
                continue
            meta = raw_meta
            field_type = str(meta.get("type", ""))
            readonly = bool(meta.get("readonly", False))
            required = bool(meta.get("required", False))
            computed = bool(meta.get("compute") or meta.get("computed"))
            relation = meta.get("relation")
            if readonly and not include_readonly:
                continue
            if computed and not include_computed:
                continue
    
            if field_type in relationships:
                relationships[field_type].append(
                    {
                        "name": field_name,
                        "relation": relation,
                        "required": required,
                        "readonly": readonly,
                        "string": meta.get("string"),
                    }
                )
            if required:
                required_fields.append(
                    {"name": field_name, "type": field_type, "relation": relation}
                )
                if not readonly and not computed:
                    create_hints.append(
                        {
                            "field": field_name,
                            "hint": "Required on create unless Odoo provides a default.",
                        }
                    )
            if readonly:
                write_hints.append(
                    {"field": field_name, "hint": "Readonly in fields_get; do not write."}
                )
            elif field_type == "many2one":
                write_hints.append(
                    {"field": field_name, "hint": "Write the related record ID."}
                )
            elif field_type in {"one2many", "many2many"}:
                write_hints.append(
                    {
                        "field": field_name,
                        "hint": "Use Odoo relational command lists for create/write.",
                    }
                )
    
        relationship_count = sum(len(items) for items in relationships.values())
        return {
            "success": True,
            "tool": "inspect_model_relationships",
            "model": model,
            "summary": {
                "field_count": len(fields_metadata),
                "relationship_count": relationship_count,
                "required_count": len(required_fields),
            },
            "relationships": relationships,
            "required_fields": required_fields,
            "create_hints": create_hints,
            "write_hints": write_hints,
            "metadata_used": {
                "fields_get": True,
                "source": metadata_source,
                "error": metadata_error,
            },
        }
  • MCP tool registration decorator. Registers 'inspect_model_relationships' as a read-only MCP tool with description 'Inspect model relationships and required field metadata'.
    @mcp.tool(
        description="Inspect model relationships and required field metadata",
        annotations=READ_ONLY_TOOL,
        structured_output=True,
    )
  • The _diagnostic_next_actions helper appends a suggestion to call inspect_model_relationships when metadata is not available.
    actions: list[str] = []
  • OdooClient method that calls fields_get on the Odoo model to retrieve field definitions. Used by the inspect_model_relationships MCP tool to fetch live metadata from the server.
    def get_model_fields(self, model_name: str) -> dict[str, Any]:
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, destructiveHint, idempotentHint, and openWorldHint. The description adds minimal behavioral context beyond 'inspect', which is already implied by the annotations. It does not clarify behavior regarding live vs cached data (though the parameter 'use_live_metadata' exists) or any side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very short (one sentence), which is concise but at the expense of necessary detail. It earns its place by stating the core purpose, but lacks structure and fails to include critical information about parameters and usage. It is appropriately front-loaded but undersized for the tool's complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has five parameters with zero schema coverage and no parameter descriptions, the one-sentence description is insufficient. The tool inspects relationships and metadata, but the description does not explain what the output contains (though an output schema exists) or how parameters affect behavior. The description is incomplete for effective tool selection and invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, meaning no parameter descriptions exist in the schema. The description fails to explain any of the five parameters (model, fields_metadata, include_readonly, include_computed, use_live_metadata). It loosely relates to 'model' and possibly 'fields_metadata', but provides no concrete meaning or usage hints, leaving the agent to guess.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'Inspect' and the resource 'model relationships and required field metadata'. This distinguishes it from sibling tools like get_model_fields (which retrieves fields) and list_models (which lists models). The tool's specific focus on relationships and metadata is well-communicated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It does not mention prerequisites, when not to use, or reference any sibling tools. For a tool with many siblings (e.g., get_model_fields, schema_catalog), this is a significant omission.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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

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