Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 11 additions & 17 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ dependencies = [
]

[project.scripts]
mcp-github-pr-review = "mcp_github_pr_review.cli:main"
mcp-github-pr-review = "mcp_github_pr_review.server_runner:main"
mcp-github-pr = "mcp_github_pr_review.cli:cli_main"

[project.optional-dependencies]
dev = [
Expand All @@ -38,6 +39,10 @@ dev = [
"mkdocstrings[python]>=0.24.1",
"hatch>=1.9.0",
]
cli = [
"typer>=0.12.0",
"rich>=13.7.0",
]

[project.urls]
Homepage = "https://github.com/cool-kids-inc/github-pr-review-mcp-server"
Expand Down Expand Up @@ -173,20 +178,9 @@ module = [
]
ignore_missing_imports = true

[dependency-groups]
dev = [
"pytest>=8.4.1",
"pytest-asyncio>=1.1.0",
"pytest-timeout>=2.3.1",
"pytest-cov>=6.0.0",
"pytest-httpx>=0.32.0",
"hypothesis>=6.100.0",
"mypy>=1.8.0",
"respx>=0.21.0",
"ruff>=0.12.10",
"pre-commit>=3.5.0",
"mkdocs>=1.6.0",
"mkdocs-material>=9.5.0",
"mkdocstrings[python]>=0.24.1",
"hatch>=1.9.0",
[[tool.mypy.overrides]]
module = [
"typer.*",
"rich.*",
]
ignore_missing_imports = true
837 changes: 837 additions & 0 deletions specs/CLI_CLI_UX_REDESIGN.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/mcp_github_pr_review/__main__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .cli import main
from .server_runner import main

if __name__ == "__main__": # pragma: no cover
raise SystemExit(main())
78 changes: 78 additions & 0 deletions src/mcp_github_pr_review/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""Command-line interface for GitHub PR Review MCP server."""

from __future__ import annotations

import importlib.metadata
import sys

try:
import typer
from rich.console import Console
except ImportError as e:
print(
"CLI dependencies not installed. Install with: uv sync --extra cli",
file=sys.stderr,
)
raise SystemExit(1) from e

# Initialize Typer app and Rich console
app = typer.Typer(
name="mcp-github-pr",
help="GitHub PR Review MCP Server - CLI for configuration and management",
add_completion=True,
no_args_is_help=True,
)
console = Console()

# Import and register command groups
from . import agents, config, doctor, quickstart, server # noqa: E402

app.add_typer(config.app, name="config")
app.add_typer(agents.app, name="agents")
app.add_typer(server.app, name="server")
app.add_typer(doctor.app, name="doctor")
app.add_typer(quickstart.app, name="quickstart")


def version_callback(show_version: bool) -> None:
"""Show version and exit."""
if show_version:
try:
version = importlib.metadata.version("mcp-github-pr-review")
console.print(f"mcp-github-pr version {version}")
except importlib.metadata.PackageNotFoundError:
console.print(
"mcp-github-pr version: [yellow]unknown[/yellow] (development)"
)
raise typer.Exit(0)


@app.callback()
def main(
version: bool | None = typer.Option(
None,
"--version",
"-v",
help="Show version and exit",
callback=version_callback,
is_eager=True,
),
) -> None:
"""
GitHub PR Review MCP Server CLI.

A Model Context Protocol server that fetches and formats GitHub PR review
comments with resolution status and diff context.

Use --help on any command for detailed usage information.
"""
pass


def cli_main() -> None:
"""Entry point for the CLI."""
app()


if __name__ == "__main__":
cli_main()
36 changes: 36 additions & 0 deletions src/mcp_github_pr_review/cli/agents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Agent integration management commands."""

from __future__ import annotations

import typer
from rich.console import Console

app = typer.Typer(
name="agents",
help="Manage agent client integrations",
no_args_is_help=True,
)
console = Console()


@app.command(name="list")
def list_agents() -> None:
"""Show supported agent clients with integration status."""
console.print("[yellow]agents list - Not yet implemented[/yellow]")
console.print("This will show available agents: Claude CLI/Desktop, Codex, Gemini")


@app.command()
def snippet() -> None:
"""Generate configuration snippet for an agent."""
console.print("[yellow]agents snippet - Not yet implemented[/yellow]")
console.print("Usage: mcp-github-pr agents snippet <agent-name>")
console.print("Example: mcp-github-pr agents snippet claude-cli")


@app.command()
def verify() -> None:
"""Test if specified agent can reach the MCP server."""
console.print("[yellow]agents verify - Not yet implemented[/yellow]")
console.print("Usage: mcp-github-pr agents verify <agent-name>")
console.print("This will test connectivity between agent and MCP server.")
73 changes: 73 additions & 0 deletions src/mcp_github_pr_review/cli/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""Configuration management commands."""

from __future__ import annotations

import typer
from rich.console import Console

app = typer.Typer(
name="config",
help="Manage configuration files and settings",
no_args_is_help=True,
)
console = Console()


@app.command()
def init() -> None:
"""Initialize configuration files with guided prompts."""
console.print("[yellow]config init - Not yet implemented[/yellow]")
console.print("This will create config.toml and .env files with proper structure.")


@app.command(name="set")
def set_value() -> None:
"""Set a configuration value."""
console.print("[yellow]config set - Not yet implemented[/yellow]")
console.print("Usage: mcp-github-pr config set <key> <value>")


@app.command()
def unset() -> None:
"""Unset a configuration value."""
console.print("[yellow]config unset - Not yet implemented[/yellow]")
console.print("Usage: mcp-github-pr config unset <key>")


@app.command()
def show() -> None:
"""Display current configuration with secrets masked."""
console.print("[yellow]config show - Not yet implemented[/yellow]")
console.print("This will display effective configuration from all sources.")


@app.command()
def validate() -> None:
"""Validate GitHub token and configuration."""
console.print("[yellow]config validate - Not yet implemented[/yellow]")
console.print(
"This will check token format, API connectivity, and required scopes."
)


@app.command()
def migrate() -> None:
"""Migrate from legacy .env-only setup to new TOML + .env structure."""
console.print("[yellow]config migrate - Not yet implemented[/yellow]")
console.print(
"This will migrate existing .env configuration with automatic backup."
)


@app.command()
def edit() -> None:
"""Open config file in $EDITOR for manual editing."""
console.print("[yellow]config edit - Not yet implemented[/yellow]")
console.print("This will open config.toml in your default editor.")


@app.command()
def reset() -> None:
"""Reset configuration to defaults with confirmation prompt."""
console.print("[yellow]config reset - Not yet implemented[/yellow]")
console.print("This will reset all settings to default values.")
45 changes: 45 additions & 0 deletions src/mcp_github_pr_review/cli/doctor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""Health check and diagnostic commands."""

from __future__ import annotations

import typer
from rich.console import Console

app = typer.Typer(
name="doctor",
help="Run comprehensive health checks",
no_args_is_help=False,
)
console = Console()


@app.callback(invoke_without_command=True)
def doctor(
ctx: typer.Context,
ci: bool = typer.Option(False, "--ci", help="CI mode: treat warnings as errors"),
json_output: bool = typer.Option(False, "--json", help="Output results as JSON"),
) -> None:
"""
Run comprehensive health checks on the MCP server setup.

Checks:
- Python version (>= 3.10)
- uv binary availability
- GitHub token validity
- GitHub API connectivity
- Token scopes
- Config file validity
- MCP server module
- Git repository detection
"""
if ctx.invoked_subcommand is None:
console.print("[yellow]doctor - Not yet implemented[/yellow]")
console.print("\nThis will perform comprehensive health checks:")
console.print(" - Python version")
console.print(" - uv binary")
console.print(" - GitHub token")
console.print(" - GitHub API connectivity")
console.print(" - Token scopes")
console.print(" - Configuration validity")
console.print(" - MCP server smoke test")
console.print(" - Git repository detection")
31 changes: 31 additions & 0 deletions src/mcp_github_pr_review/cli/quickstart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Interactive quickstart and setup commands."""

from __future__ import annotations

import typer
from rich.console import Console

app = typer.Typer(
name="quickstart",
help="Interactive first-time setup wizard",
no_args_is_help=False,
)
console = Console()


@app.callback(invoke_without_command=True)
def quickstart(ctx: typer.Context) -> None:
"""
Interactive first-time setup wizard.

Combines init, validation, and agent snippet generation in one flow.
Perfect for new users to get started quickly.
"""
if ctx.invoked_subcommand is None:
console.print("[yellow]quickstart - Not yet implemented[/yellow]")
console.print("\nThis interactive wizard will guide you through:")
console.print(" 1. Creating configuration files")
console.print(" 2. Setting up GitHub token")
console.print(" 3. Validating connectivity")
console.print(" 4. Generating agent snippets")
console.print(" 5. Testing the server")
35 changes: 35 additions & 0 deletions src/mcp_github_pr_review/cli/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Server management commands."""

from __future__ import annotations

import typer
from rich.console import Console

app = typer.Typer(
name="server",
help="Manage MCP server lifecycle",
no_args_is_help=True,
)
console = Console()


@app.command()
def run() -> None:
"""Spawn MCP server as subprocess with optional logging."""
console.print("[yellow]server run - Not yet implemented[/yellow]")
console.print("This will start the MCP server with stdio protocol.")
console.print("Options: --log, --no-sync, --follow")


@app.command()
def debug() -> None:
"""Run server with verbose logging to stderr for development."""
console.print("[yellow]server debug - Not yet implemented[/yellow]")
console.print("This will start the server with debug-level logging enabled.")


@app.command()
def logs() -> None:
"""Tail server logs if --log was used previously."""
console.print("[yellow]server logs - Not yet implemented[/yellow]")
console.print("This will display recent server logs.")
Loading
Loading