From Python Fire

Narrow automatic Python object exposure into explicit Milo command contracts.

1 min read 272 words

Python Fire is optimized for turning Python objects into CLIs with very little code. Milo is more explicit: only registered commands become public, and every public command should have typed parameters, documentation, tests, and an MCP schema.

Official reference: Python Fire guide.

Before

import fire


class Tasks:
    def add(self, title, priority="medium"):
        return {"title": title, "priority": priority}

    def list(self):
        return []


if __name__ == "__main__":
    fire.Fire(Tasks)

After

from typing import Literal

from milo import CLI

cli = CLI(name="tasks", description="Task commands")


@cli.command("add", description="Add a task")
def add(title: str, priority: Literal["low", "medium", "high"] = "medium") -> dict[str, str]:
    """Add a task.

    Args:
        title: Task title.
        priority: Task priority.
    """
    return {"title": title, "priority": priority}


@cli.command("list", description="List tasks", annotations={"readOnlyHint": True})
def list_tasks() -> list[dict[str, str]]:
    return []


if __name__ == "__main__":
    cli.run()

Mapping

Fire concept Milo equivalent
fire.Fire()exposing module contents Register only the intended public commands
fire.Fire(component) CLI(...)plus explicit command decorators
Object methods as commands Top-level functions or grouped commands
Runtime value parsing Type annotations andAnnotated[...]schema constraints
Printed or stringified objects JSON-serializable return values

What To Watch

  • Fire can expose more than you intended. Treat migration as an allowlist: only decorate commands that humans and agents should call.
  • Fire's value parsing is convenient for local exploration. For agent-facing tools, prefer explicit annotations so schemas can be validated before calls.
  • Rename ambiguous methods before exposing them. Agents do better with command names and parameter descriptions that say what to do next.