From argparse

Replace parser-first argparse code with typed Milo command handlers.

1 min read 289 words

argparse is parser-first: you create an ArgumentParser, attach arguments, parsesys.argv, then call your own function. Milo is handler-first: write the function contract once, then let Milo derive argv parsing, MCP schema, and llms.txt.

Official reference: argparse documentation.

Before

import argparse


def deploy(environment: str, service: str, version: str) -> dict[str, str]:
    return {"environment": environment, "service": service, "version": version}


parser = argparse.ArgumentParser(prog="deployer")
parser.add_argument("environment")
parser.add_argument("service")
parser.add_argument("--version", default="latest")
args = parser.parse_args()
print(deploy(args.environment, args.service, args.version))

After

from milo import CLI

cli = CLI(name="deployer", description="Deploy services")


@cli.command("deploy", description="Deploy a service")
def deploy(environment: str, service: str, version: str = "latest") -> dict[str, str]:
    """Deploy a service.

    Args:
        environment: Target environment.
        service: Service name.
        version: Version or image tag.
    """
    return {"environment": environment, "service": service, "version": version}


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

Run it:

uv run python app.py deploy --environment staging --service api
uv run python app.py deploy --environment staging --service api --format json
uv run python app.py --llms-txt
uv run milo verify app.py

Mapping

argparse concept Milo equivalent
ArgumentParser(prog=..., description=...) CLI(name=..., description=...)
add_argument("name") Required typed parameter:name: str
add_argument("--name", default="x") Defaulted parameter:name: str = "x"
type=int Annotation:count: int
choices=[...] Literal[...] or Enum
parser.parse_args() cli.run() for processes, cli.invoke([...])for tests
Namespace Function parameters

What To Watch

  • Positional arguments become named CLI options by default in Milo command handlers. That makes MCP calls and repair loops clearer.
  • print()output is process output. For agent-facing commands, prefer structured returns andContextoutput helpers.
  • Parser errors become structured Milo/MCP diagnostics when the command is called throughtools/call.