Skip to main content
Glama
voducdan

metabase-mcp

by voducdan

update_model

Update an existing Metabase model by modifying its name, description, SQL query, database, collection, column metadata, visualization settings, or archive status.

Instructions

Update an existing model in Metabase.

Args: card_id: The ID of the model to update. name: New name for the model. description: New description for the model. query: New SQL query for the model. database_id: New database ID (required if changing query). collection_id: Move the model to a different collection. result_metadata: Updated column metadata list. Each dict can include keys like "name", "display_name", "base_type", "semantic_type", "description", and "field_ref". visualization_settings: Visualization settings to apply. archived: Set to true to archive the model, false to unarchive.

Returns: The updated model object.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
card_idYes
nameNo
descriptionNo
queryNo
database_idNo
collection_idNo
result_metadataNo
visualization_settingsNo
archivedNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The update_model tool handler function. It is an MCP tool decorated with @mcp.tool that updates an existing model (card with type 'model') in Metabase. It accepts card_id, name, description, query, database_id, collection_id, result_metadata, visualization_settings, and archived parameters. It builds a payload with only the provided fields and sends a PUT request to /card/{card_id}. When query is updated, it either uses the provided database_id or fetches the current card to get the existing database_id.
    @mcp.tool
    async def update_model(
        card_id: int,
        ctx: Context,
        name: str | None = None,
        description: str | None = None,
        query: str | None = None,
        database_id: int | None = None,
        collection_id: int | None = None,
        result_metadata: list[dict[str, Any]] | None = None,
        visualization_settings: dict[str, Any] | None = None,
        archived: bool | None = None,
    ) -> dict[str, Any]:
        """
        Update an existing model in Metabase.
    
        Args:
            card_id: The ID of the model to update.
            name: New name for the model.
            description: New description for the model.
            query: New SQL query for the model.
            database_id: New database ID (required if changing query).
            collection_id: Move the model to a different collection.
            result_metadata: Updated column metadata list. Each dict can include
                keys like "name", "display_name", "base_type", "semantic_type",
                "description", and "field_ref".
            visualization_settings: Visualization settings to apply.
            archived: Set to true to archive the model, false to unarchive.
    
        Returns:
            The updated model object.
        """
        try:
            await ctx.info(f"Updating model {card_id}")
    
            payload: dict[str, Any] = {}
    
            if name is not None:
                payload["name"] = name
            if description is not None:
                payload["description"] = description
            if collection_id is not None:
                payload["collection_id"] = collection_id
            if result_metadata is not None:
                payload["result_metadata"] = result_metadata
                await ctx.debug(f"Updating {len(result_metadata)} column metadata entries")
            if visualization_settings is not None:
                payload["visualization_settings"] = visualization_settings
            if archived is not None:
                payload["archived"] = archived
            if query is not None:
                db = database_id
                if db is None:
                    current_card = await metabase_client.request("GET", f"/card/{card_id}")
                    db = current_card.get("database_id")
                    await ctx.debug(f"Using existing database_id {db} for query update")
                payload["dataset_query"] = {
                    "database": db,
                    "type": "native",
                    "native": {"query": query},
                }
    
            if not payload:
                raise ToolError("No update fields provided. Specify at least one field to update.")
    
            result = await metabase_client.request("PUT", f"/card/{card_id}", json=payload)
            await ctx.info(f"Successfully updated model {card_id}")
    
            return result
        except ToolError:
            raise
        except Exception as e:
            error_msg = f"Error updating model {card_id}: {e}"
            await ctx.error(error_msg)
            raise ToolError(error_msg) from e
  • server.py:766-767 (registration)
    The tool is registered via the @mcp.tool decorator on line 766, which registers 'update_model' with the FastMCP server instance named 'mcp'.
    @mcp.tool
    async def update_model(
  • The function signature and docstring define the input schema for the update_model tool: card_id (int, required), ctx (Context), and optional parameters name, description, query, database_id, collection_id, result_metadata, visualization_settings, archived. The docstring describes each parameter's purpose.
    async def update_model(
        card_id: int,
        ctx: Context,
        name: str | None = None,
        description: str | None = None,
        query: str | None = None,
        database_id: int | None = None,
        collection_id: int | None = None,
        result_metadata: list[dict[str, Any]] | None = None,
        visualization_settings: dict[str, Any] | None = None,
        archived: bool | None = None,
    ) -> dict[str, Any]:
        """
        Update an existing model in Metabase.
    
        Args:
            card_id: The ID of the model to update.
            name: New name for the model.
            description: New description for the model.
            query: New SQL query for the model.
            database_id: New database ID (required if changing query).
            collection_id: Move the model to a different collection.
            result_metadata: Updated column metadata list. Each dict can include
                keys like "name", "display_name", "base_type", "semantic_type",
                "description", and "field_ref".
            visualization_settings: Visualization settings to apply.
            archived: Set to true to archive the model, false to unarchive.
    
        Returns:
            The updated model object.
        """
  • The _merge_native_stage helper function is used by update_card but not by update_model. update_model does not use template-tag preservation logic like update_card does; instead it simply replaces the entire dataset_query when a query is provided.
    def _merge_native_stage(
        dataset_query: dict[str, Any],
        *,
        sql: str | None = None,
        template_tags: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        """
        Return a shallow-copy of `dataset_query` with `sql` and/or `template-tags`
        applied to the native stage, preserving all sibling fields (native/stages,
        lib/type, collection, etc.). Leaves root-level keys like database/type
        untouched — callers override those explicitly if needed.
    
        Pass only the fields you want to change: unset args keep their current values.
        """
        new_dq = dict(dataset_query)
    
        stages = dataset_query.get("stages")
        if isinstance(stages, list) and stages:
            new_stages = [dict(s) if isinstance(s, dict) else s for s in stages]
            stage0 = new_stages[0] if isinstance(new_stages[0], dict) else {}
            if sql is not None:
                stage0["native"] = sql
            if template_tags is not None:
                stage0["template-tags"] = template_tags
            new_stages[0] = stage0
            new_dq["stages"] = new_stages
            return new_dq
    
        new_native = dict(dataset_query.get("native") or {})
        if sql is not None:
            new_native["query"] = sql
        if template_tags is not None:
            new_native["template-tags"] = template_tags
        new_dq["native"] = new_native
        return new_dq
  • The _read_native_stage helper function is used by update_card but not directly by update_model.
    def _read_native_stage(
        dataset_query: dict[str, Any],
    ) -> tuple[str | None, dict[str, Any]]:
        """
        Return (sql, template_tags) from a card's dataset_query.
    
        Handles both Metabase shapes:
          - stages shape: dataset_query.stages[0] = {"native": "<SQL>", "template-tags": {...}, ...}
          - legacy shape: dataset_query.native = {"query": "<SQL>", "template-tags": {...}}
        """
        stages = dataset_query.get("stages")
        if isinstance(stages, list) and stages:
            stage = stages[0] or {}
            return stage.get("native"), dict(stage.get("template-tags") or {})
        native = dataset_query.get("native") or {}
        return native.get("query"), dict(native.get("template-tags") or {})
Behavior2/5

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

No annotations are provided, and the description does not disclose behavioral traits such as side effects, permission requirements, rate limits, or whether updates are destructive. It only lists parameters without explaining the behavior beyond that.

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

Conciseness5/5

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

The description is concise and well-structured: a one-line summary, an Args list with each parameter explained, and a Returns statement. No extraneous information.

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

Completeness3/5

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

The description covers the tool's purpose, parameters, and return value. However, it lacks usage guidelines and behavioral transparency. Given the complexity (9 parameters, mutation) and no annotations, it is adequate but has gaps.

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

Parameters4/5

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

Schema description coverage is 0%, so the description must compensate. It provides detailed explanations for each parameter, including constraints like 'database_id' being required if changing query, and notes on result_metadata keys. This adds significant meaning beyond the raw schema.

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 'Update an existing model in Metabase,' which is a specific verb+resource combination. It distinguishes itself from sibling tools like create_model, update_card, etc., by being specific to models.

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

Usage Guidelines3/5

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

Usage is implied by the tool's purpose and parameters, but there is no explicit guidance on when to use this tool versus alternatives like update_card. No prerequisites or exclusions are mentioned.

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/voducdan/matebase-mcp'

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