Metadata-Version: 2.4
Name: click-clop
Version: 0.3.0
Summary: A reusable framework and scaffold for Python CLI applications with MCP, REST, and service-layer architecture
Author: bpayne
License: MIT
Keywords: cli,click,framework,mcp,scaffold
Requires-Python: >=3.14
Requires-Dist: click>=8.1.0
Requires-Dist: copier>=9.0.0
Requires-Dist: fastapi>=0.115.0
Requires-Dist: fastmcp>=2.0.0
Requires-Dist: opentelemetry-api>=1.20.0
Requires-Dist: opentelemetry-exporter-otlp>=1.20.0
Requires-Dist: opentelemetry-sdk>=1.20.0
Requires-Dist: questionary>=2.0.0
Requires-Dist: rich>=13.0.0
Requires-Dist: structlog>=24.0.0
Requires-Dist: tomli>=2.0.0; python_version < '3.11'
Requires-Dist: uvicorn>=0.34.0
Provides-Extra: dev
Requires-Dist: behave>=1.2.6; extra == 'dev'
Requires-Dist: httpx>=0.27.0; extra == 'dev'
Requires-Dist: mypy>=1.0; extra == 'dev'
Requires-Dist: pytest-bdd>=7.0; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.8.0; extra == 'dev'
Requires-Dist: sphinx-click>=6.0; extra == 'dev'
Requires-Dist: sphinx-rtd-theme>=2.0; extra == 'dev'
Requires-Dist: sphinx>=7.0; extra == 'dev'
Description-Content-Type: text/markdown

# click-clop

A Python framework and project generator where every feature is simultaneously a **CLI command** (Click), **MCP tool** (FastMCP), and **REST endpoint** (FastAPI).

Write your business logic once as a service class — click-clop exposes it everywhere.

```
Service method  ──►  CLI command  (Click)
                ──►  MCP tool     (FastMCP)
                ──►  REST endpoint (FastAPI)
```

## Quick Start

### Install

```bash
pip install click-clop
```

### Generate a new project

```bash
click-clop new my-app --description "My awesome CLI"
cd my-app
make install-dev
```

This scaffolds a complete project with a Makefile, Dockerfile, Helm charts, telemetry config, BDD tests, and documentation — with intelligent defaults based on your system.

### Write a service

```python
# src/my_app/services/greet.py
from click_clop import Service

class GreetService(Service):
    """Greeting service."""

    name = "greet"
    description = "Say hello"

    def hello(self, name: str = "world") -> str:
        """Say hello to someone."""
        return f"Hello, {name}!"

    def farewell(self, name: str = "world") -> str:
        """Say goodbye to someone."""
        return f"Goodbye, {name}!"

_service = GreetService()  # auto-registers
```

### Expose as CLI

```python
import click
from click_clop import expose_cli

@click.group()
def main():
    """My CLI app."""

expose_cli(main)  # all registered services become subcommands
```

```bash
$ my-app greet hello --name Claude
Hello, Claude!
```

### Expose as REST

```python
from click_clop import expose_rest

app = expose_rest()  # creates /api/greet/hello, /api/greet/farewell
```

### Expose as MCP tools

```python
from fastmcp import FastMCP
from click_clop import expose_mcp

mcp = FastMCP("my-service")
expose_mcp(mcp)  # registers greet_hello, greet_farewell tools
```

## CLI

```
click-clop new <PROJECT_NAME>   Create a new project from template
  -d, --description TEXT         Project description
  -a, --author TEXT              Author name (auto-detected from git)
  -o, --output-dir PATH          Parent directory (default: cwd)

click-clop detect                Show detected system capabilities
```

## Library API

```python
from click_clop import (
    Service,            # Base class for services
    ServiceRegistry,    # Global registry of all services
    load_config,        # Load TOML config with env overrides
    expose_cli,         # Add services to a Click group
    expose_mcp,         # Add services as MCP tools
    expose_rest,        # Add services as FastAPI endpoints
)
```

### Configuration

`load_config()` merges TOML files in order (later wins):

1. `config.toml` — base defaults
2. `config.dev.toml` — development overrides
3. `config.local.toml` — local/gitignored overrides
4. Environment variables with prefix

```python
from click_clop import load_config

config = load_config(prefix="APP")
```

Environment variables map with double-underscore nesting:

```
APP_SERVER__HOST=0.0.0.0  →  config["server"]["host"]
APP_DATABASE__PORT=5432   →  config["database"]["port"]
```

### Secrets (1Password)

```python
from click_clop.secrets import get_secret_field, ensure_secret

# Read a secret
api_key = get_secret_field("vault-name", "item-name", "api_key")

# Create or update a secret
ensure_secret("vault-name", "item-name", {"api_key": "sk-..."})
```

Requires the 1Password CLI (`op`) installed and authenticated.

### Telemetry (OpenTelemetry)

```python
from click_clop.telemetry import setup_telemetry, get_tracer

setup_telemetry(service_name="my-service", otlp_endpoint="http://localhost:4317")

tracer = get_tracer("my_module")
with tracer.start_as_current_span("operation"):
    ...
```

### Logging (structlog)

```python
from click_clop.logging import setup_logging, get_logger

setup_logging(level="INFO", json_output=True, service_name="my-service")
log = get_logger("my_module")
log.info("event", key="value")
```

### System Detection

```python
from click_clop.system_detect import detect_system

info = detect_system()
print(info.summary())
```

Detects Docker/Podman, Helm, 1Password CLI, Python, uv, Git, OS, and architecture.

## Generated Project Structure

```
my_app/
├── src/my_app/
│   ├── __main__.py          # Entry point
│   ├── cli.py               # CLI main group
│   ├── server.py            # MCP + REST server
│   └── services/
│       └── hello.py         # Example service
├── tests/
│   └── unit/
│       └── test_hello.py
├── features/                # BDD/Gherkin tests
├── helm/my_app/             # Kubernetes Helm charts
├── alloy/config.alloy       # Grafana Alloy telemetry config
├── docs/                    # Sphinx documentation
├── config.toml              # Application config
├── Makefile                 # Development tasks
├── Dockerfile
└── pyproject.toml
```

### Generated project commands

```bash
make install-dev    # Install with dev dependencies
make test           # Run pytest
make test-bdd       # Run BDD tests
make lint           # Lint with ruff + mypy
make format         # Format with ruff
make serve          # Run REST + MCP server (Swagger at localhost:8000/docs)
make build-image    # Build container image
make helm-install   # Deploy via Helm
make docs           # Build Sphinx docs
```

## Development

```bash
git clone https://github.com/bpayne/click-clop.git
cd click-clop
make install-dev
make test
make lint
```

## Type Mapping

Service method parameters are automatically mapped:

| Python type | CLI (Click) | REST (FastAPI) |
|-------------|-------------|----------------|
| `str` | `STRING` | `str` |
| `int` | `INT` | `int` |
| `float` | `FLOAT` | `float` |
| `bool` | `--flag/--no-flag` | `bool` |

Parameters without type annotations default to `str`.

## License

MIT
