Skip to main content
Glama
Michaelzag

Migadu MCP Server

by Michaelzag

get_rewrite

Read-onlyIdempotent

Get the full details of a rewrite rule by specifying its slug name. Returns configuration and settings for the rule.

Instructions

Get rewrite rule details by slug/name.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
nameYes
domainNo

Implementation Reference

  • The get_rewrite tool handler. Decorated with @migadu_tool(mcp, read_only=True). Accepts 'name' (slug/name of rewrite rule) and optional 'domain', resolves domain via resolve_domain(), logs via ctx, and delegates to RewriteService.get_rewrite().
    @migadu_tool(mcp, read_only=True)
    async def get_rewrite(
        name: str, ctx: Context, domain: str | None = None
    ) -> dict[str, Any]:
        """Get rewrite rule details by slug/name."""
        resolved = resolve_domain(domain)
        await ctx.info(f"📋 Getting rewrite {name} for {resolved}")
        return await get_service_factory().rewrite_service().get_rewrite(resolved, name)
  • RewriteService.get_rewrite() - core implementation that calls the Migadu API GET /domains/{domain}/rewrites/{name} endpoint to fetch a single rewrite rule.
    async def get_rewrite(self, domain: str, name: str) -> dict[str, Any]:
        return await self.client.get(f"/domains/{domain}/rewrites/{name}")
  • register_rewrite_tools(mcp) - registers all rewrite tools (list_rewrites, get_rewrite, create_rewrite, update_rewrite, delete_rewrite) with the FastMCP server. get_rewrite is registered via the @migadu_tool decorator on line 24.
    def register_rewrite_tools(mcp: FastMCP) -> None:
        @migadu_tool(mcp, read_only=True, summarize_response=True)
        async def list_rewrites(ctx: Context, domain: str | None = None) -> dict[str, Any]:
            """List rewrite rules for a domain."""
            resolved = resolve_domain(domain)
            await ctx.info(f"📋 Listing rewrites for {resolved}")
            return await get_service_factory().rewrite_service().list_rewrites(resolved)
    
        @migadu_tool(mcp, read_only=True)
        async def get_rewrite(
            name: str, ctx: Context, domain: str | None = None
        ) -> dict[str, Any]:
            """Get rewrite rule details by slug/name."""
            resolved = resolve_domain(domain)
            await ctx.info(f"📋 Getting rewrite {name} for {resolved}")
            return await get_service_factory().rewrite_service().get_rewrite(resolved, name)
    
        @migadu_bulk_tool(mcp, RewriteCreateRequest, entity="rewrite", idempotent=False)
        async def create_rewrite(
            item: RewriteCreateRequest, ctx: Context
        ) -> dict[str, Any]:
            """Create rewrite rule(s). List of dicts with: name, local_part_rule (pattern), destinations, domain (optional), order_num (optional)."""
            domain = item.domain or resolve_domain(None)
            destinations = [str(d) for d in item.destinations]
            await ctx.info(f"📋 Creating rewrite {item.name} on {domain}")
            result = (
                await get_service_factory()
                .rewrite_service()
                .create_rewrite(
                    domain, item.name, item.local_part_rule, destinations, item.order_num
                )
            )
            return {"rewrite": result, "success": True}
    
        @migadu_bulk_tool(mcp, RewriteUpdateRequest, entity="rewrite")
        async def update_rewrite(
            item: RewriteUpdateRequest, ctx: Context
        ) -> dict[str, Any]:
            """Update rewrite rule(s). List of dicts with: name (required), and any of: new_name, local_part_rule, destinations, order_num, domain."""
            domain = item.domain or resolve_domain(None)
            destinations = (
                [str(d) for d in item.destinations] if item.destinations else None
            )
            await ctx.info(f"📋 Updating rewrite {item.name} on {domain}")
            result = (
                await get_service_factory()
                .rewrite_service()
                .update_rewrite(
                    domain,
                    item.name,
                    item.new_name,
                    item.local_part_rule,
                    destinations,
                    item.order_num,
                )
            )
            return {"rewrite": result, "success": True}
    
        @migadu_bulk_tool(mcp, RewriteDeleteRequest, entity="rewrite", destructive=True)
        async def delete_rewrite(
            item: RewriteDeleteRequest, ctx: Context
        ) -> dict[str, Any]:
            """Delete rewrite rule(s). DESTRUCTIVE. List of dicts with: name, domain (optional)."""
            domain = item.domain or resolve_domain(None)
            await ctx.warning(f"🗑️ Deleting rewrite {item.name}")
            await get_service_factory().rewrite_service().delete_rewrite(domain, item.name)
            return {"deleted": item.name, "success": True}
    
        _ = (list_rewrites, get_rewrite, create_rewrite, update_rewrite, delete_rewrite)
  • Call to register_rewrite_tools(mcp) in the main server initialization sequence, which registers get_rewrite as an MCP tool.
    register_rewrite_tools(mcp)
  • The @migadu_tool decorator used by get_rewrite. Handles registration with FastMCP, error logging via ctx, optional response summarization, and annotation resolution.
    def migadu_tool(
        mcp: FastMCP,
        *,
        read_only: bool = False,
        destructive: bool = False,
        idempotent: bool = True,
        summarize_response: bool = False,
        max_tokens: int = DEFAULT_MAX_TOKENS,
    ) -> Callable[
        [Callable[..., Awaitable[dict[str, Any]]]], Callable[..., Awaitable[dict[str, Any]]]
    ]:
        """Register a single-target tool with FastMCP.
    
        The decorated function is called as-is with its original signature. Errors are
        logged via ctx and re-raised. If `summarize_response=True`, dict responses are
        passed through the static summarizer.
        """
        annotations = {
            "readOnlyHint": read_only,
            "destructiveHint": destructive,
            "idempotentHint": idempotent,
            "openWorldHint": True,
        }
    
        def decorator(
            func: Callable[..., Awaitable[dict[str, Any]]],
        ) -> Callable[..., Awaitable[dict[str, Any]]]:
            func_name = getattr(func, "__name__", "tool")
    
            @wraps(func)
            async def wrapper(*args: Any, **kwargs: Any) -> dict[str, Any]:
                ctx = _extract_ctx(args, kwargs)
                try:
                    result = await func(*args, **kwargs)
                except Exception as exc:
                    if ctx is not None:
                        await ctx.error(f"❌ {func_name} failed: {exc}")
                    raise
                if summarize_response and isinstance(result, dict):
                    return summarize(result, max_tokens=max_tokens)
                return result
    
            # Preserve the original function's signature with resolved annotations so
            # FastMCP's Pydantic-based schema builder sees real types (not strings from
            # `from __future__ import annotations`). Evaluation can fail for exotic
            # annotations; in that case FastMCP falls back to the raw (string) form.
            try:
                resolved = dict(inspect.get_annotations(func, eval_str=True))
                setattr(func, "__annotations__", resolved)
            except Exception:  # nosec B110 — best-effort annotation resolution
                pass
            setattr(wrapper, "__signature__", inspect.signature(func))
            setattr(wrapper, "__annotations__", dict(getattr(func, "__annotations__", {})))
            # Register with FastMCP as a side effect; return the wrapper so the function
            # remains directly callable (tests and internal dispatch).
            mcp.tool(annotations=annotations)(wrapper)
            return wrapper
    
        return decorator
Behavior3/5

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

Annotations already declare readOnlyHint=true, destructiveHint=false, and idempotentHint=true, so the description adds little beyond stating the operation. It does clarify that the tool returns 'details', but this is minimal. No contradiction with annotations.

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

Conciseness4/5

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

The description is very concise at a single sentence with no wasted words. However, it could be slightly more informative by including the domain parameter without significantly increasing length. It is front-loaded with the key action.

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?

For a simple tool with 2 parameters and no output schema, the description covers the primary intent but fails to explain the optional 'domain' parameter or what the returned 'details' include. Given the available annotations, the description is adequate but has clear gaps.

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

Parameters2/5

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

With 0% schema description coverage, the description must compensate. It partially explains the 'name' parameter as 'slug/name', but entirely omits the 'domain' parameter. This leaves users uncertain about its purpose and how to use it.

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 the verb 'Get' and resource 'rewrite rule details' and the identifier type 'by slug/name'. It effectively distinguishes from sibling tools like list_rewrites and create_rewrite. However, it does not mention the optional 'domain' parameter, which could create ambiguity about the tool's scope.

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?

No guidance is provided on when to use this tool versus alternatives (e.g., list_rewrites for listing all rules, or get_alias for alias details). The description implies it is for fetching a single rule's details but does not explicitly state when not to use it or compare with siblings.

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