Skip to main content
Glama
Michaelzag

Migadu MCP Server

by Michaelzag

update_domain

Idempotent

Update domain fields such as name, description, and tags in Migadu email hosting. Provide a list of dictionaries to modify specified domain attributes.

Instructions

Update domain field(s). List of dicts with: name (required), description (optional), tags (optional).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
itemsYes

Implementation Reference

  • The `update_domain` tool handler — a bulk tool decorated with @migadu_bulk_tool that receives a DomainUpdateRequest, calls domain_service().update_domain(), and returns a result envelope.
    @migadu_bulk_tool(mcp, DomainUpdateRequest, entity="domain")
    async def update_domain(item: DomainUpdateRequest, ctx: Context) -> dict[str, Any]:
        """Update domain field(s). List of dicts with: name (required), description (optional), tags (optional)."""
        await ctx.info(f"📋 Updating domain {item.name}")
        result = (
            await get_service_factory()
            .domain_service()
            .update_domain(item.name, item.description, item.tags)
        )
        return {"domain": result, "name": item.name, "success": True}
  • The `DomainService.update_domain()` method that builds a PATCH request body with only non-None fields (description, tags) and sends it to /domains/{name}.
    async def update_domain(
        self,
        name: str,
        description: str | None = None,
        tags: str | None = None,
    ) -> dict[str, Any]:
        data: dict[str, Any] = {}
        if description is not None:
            data["description"] = description
        if tags is not None:
            data["tags"] = tags
        return await self.client.patch(f"/domains/{name}", json=data)
  • The `DomainUpdateRequest` Pydantic schema defining the input: name (required), description (optional), tags (optional).
    class DomainUpdateRequest(BaseModel):
        name: str = Field(..., description="Domain name")
        description: str | None = None
        tags: str | None = None
  • The `@migadu_bulk_tool` decorator that wraps the handler, validates items against the schema, registers the bulk_wrapper with FastMCP, and returns bulk results.
    def migadu_bulk_tool(
        mcp: FastMCP,
        schema: Type[BaseModel],
        *,
        entity: str,
        destructive: bool = False,
        idempotent: bool = True,
    ) -> Callable[..., Any]:
        """Register a bulk mutation tool.
    
        The decorated function processes ONE validated item at a time. The outer tool
        accepts `list[dict]`, validates each, calls the inner function, and returns a
        bulk-result envelope with per-item success/failure.
        """
        annotations = {
            "readOnlyHint": False,
            "destructiveHint": destructive,
            "idempotentHint": idempotent,
            "openWorldHint": True,
        }
    
        def decorator(
            process_one: Callable[..., Awaitable[dict[str, Any]]],
        ) -> Callable[..., Awaitable[dict[str, Any]]]:
            async def bulk_wrapper(items: list, ctx: Context) -> dict:
                normalized: list[dict[str, Any]] = (
                    [items] if isinstance(items, dict) else list(items)
                )
                total = len(normalized)
                plural = entity if total == 1 else f"{entity}s"
                await ctx.info(f"🔄 Processing {total} {plural}")
    
                results: list[dict[str, Any]] = []
                for item in normalized:
                    try:
                        validated = validate_with_schema(item, schema)
                        result = await process_one(validated, ctx)
                        results.append(result)
                    except Exception as exc:
                        results.append({"error": str(exc), "item": item, "success": False})
    
                successful = sum(1 for r in results if r.get("success", True))
                failed = total - successful
    
                if failed == 0:
                    await ctx.info(f"✅ Processed {successful}/{total} {plural}")
                else:
                    await ctx.warning(
                        f"⚠️ Processed {successful}/{total} {plural}; {failed} failed"
                    )
    
                return {
                    "items": results,
                    "total_requested": total,
                    "total_successful": successful,
                    "total_failed": failed,
                    "success": failed == 0,
                }
    
            bulk_wrapper.__name__ = getattr(process_one, "__name__", "bulk_tool")
            bulk_wrapper.__qualname__ = getattr(
                process_one, "__qualname__", bulk_wrapper.__name__
            )
            bulk_wrapper.__doc__ = getattr(process_one, "__doc__", None)
            mcp.tool(annotations=annotations)(bulk_wrapper)
            return bulk_wrapper
    
        return decorator
  • Top-level registration call: `register_domain_tools(mcp)` is invoked during server initialization.
    def initialize_server() -> None:
        register_domain_tools(mcp)
Behavior2/5

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

Annotations indicate idempotentHint true, readOnlyHint false, and destructiveHint false, but the description adds no behavioral details beyond this. It fails to explain effects like whether updates are reversible or what happens if the domain does not exist, leaving the agent with only annotation-provided information.

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 a single concise sentence followed by a clear list of fields. It is front-loaded with the core action ('Update domain field(s)') and immediately provides necessary structure, with no extraneous words.

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

Completeness4/5

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

For a simple update tool with one parameter and no output schema, the description adequately covers the input structure. It hints at batch updates via 'list of dicts' but does not clarify if 'name' is the domain identifier or mention error cases. Overall, sufficient for the low complexity.

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?

The input schema provides no description for the 'items' parameter (0% coverage), but the description compensates by defining it as a list of dicts with required 'name' and optional 'description' and 'tags'. This adds essential structure missing from the schema, though it does not explain what 'name' refers to (e.g., domain identifier).

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

Purpose4/5

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

The description clearly states 'Update domain field(s)' indicating the tool modifies domain settings. It specifies the required field 'name' and optional fields 'description' and 'tags', providing a clear purpose. However, it does not differentiate from sibling tools like update_alias or create_domain, missing explicit scoping.

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?

The description implies usage for updating domain fields, but offers no guidance on when to use this tool versus alternatives like create_domain or get_domain. No prerequisites or exclusions are mentioned, relying on the tool name for context.

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/Michaelzag/migadu-mcp'

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