|
3 | 3 | import sys |
4 | 4 | from importlib.util import find_spec |
5 | 5 | from pathlib import Path |
| 6 | +from typing import Annotated |
6 | 7 |
|
7 | 8 | import typer |
8 | 9 | from loguru import logger |
@@ -31,8 +32,6 @@ def launchpad() -> None: |
31 | 32 |
|
32 | 33 |
|
33 | 34 | if find_spec("marimo"): |
34 | | - from typing import Annotated |
35 | | - |
36 | 35 | from aignostics.utils import create_marimo_app |
37 | 36 |
|
38 | 37 | @cli.command() |
@@ -68,20 +67,99 @@ def notebook( |
68 | 67 | mcp_cli = typer.Typer(name="mcp", help="MCP (Model Context Protocol) server for AI agent integration.") |
69 | 68 |
|
70 | 69 |
|
| 70 | +@mcp_cli.command("install") |
| 71 | +def mcp_install() -> None: |
| 72 | + """Configure Claude Desktop to use the Aignostics MCP server. |
| 73 | +
|
| 74 | + This command automatically adds the Aignostics MCP server configuration |
| 75 | + to your Claude Desktop config file on macOS. After running this command, |
| 76 | + restart Claude Desktop to load the MCP server. |
| 77 | +
|
| 78 | + The configuration uses uvx, so no local installation is required. |
| 79 | +
|
| 80 | + Examples: |
| 81 | + aignostics mcp install |
| 82 | +
|
| 83 | + Raises: |
| 84 | + Exit: If not on macOS, if uvx is not found, or if user cancels. |
| 85 | + """ |
| 86 | + import json # noqa: PLC0415 |
| 87 | + import platform # noqa: PLC0415 |
| 88 | + import shutil # noqa: PLC0415 |
| 89 | + |
| 90 | + if platform.system() != "Darwin": |
| 91 | + console.print("[red]Error:[/red] This command is only supported on macOS.", style="error") |
| 92 | + raise typer.Exit(1) |
| 93 | + |
| 94 | + # Claude Desktop config path on macOS |
| 95 | + config_path = Path.home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json" |
| 96 | + |
| 97 | + # Find uvx binary |
| 98 | + uvx_path = shutil.which("uvx") |
| 99 | + if not uvx_path: |
| 100 | + console.print("[red]Error:[/red] Could not find 'uvx' binary. Please install uv first.", style="error") |
| 101 | + raise typer.Exit(1) |
| 102 | + |
| 103 | + # Build the server configuration using uvx |
| 104 | + server_config = { |
| 105 | + "command": uvx_path, |
| 106 | + "args": ["aignostics", "mcp", "run"], |
| 107 | + } |
| 108 | + |
| 109 | + # Load existing config or create new one |
| 110 | + if config_path.exists(): |
| 111 | + with config_path.open() as f: |
| 112 | + config = json.load(f) |
| 113 | + console.print(f"[dim]Found existing config at {config_path}[/dim]") |
| 114 | + else: |
| 115 | + config = {} |
| 116 | + # Ensure parent directory exists |
| 117 | + config_path.parent.mkdir(parents=True, exist_ok=True) |
| 118 | + console.print(f"[dim]Creating new config at {config_path}[/dim]") |
| 119 | + |
| 120 | + # Initialize mcpServers if not present |
| 121 | + if "mcpServers" not in config: |
| 122 | + config["mcpServers"] = {} |
| 123 | + |
| 124 | + # Check if aignostics is already configured |
| 125 | + if "aignostics" in config["mcpServers"]: |
| 126 | + existing = config["mcpServers"]["aignostics"] |
| 127 | + console.print("[yellow]Warning:[/yellow] Aignostics MCP server is already configured:") |
| 128 | + console.print(f" command: {existing.get('command')}") |
| 129 | + console.print(f" args: {existing.get('args')}") |
| 130 | + if not typer.confirm("Do you want to overwrite the existing configuration?"): |
| 131 | + console.print("[dim]Configuration unchanged.[/dim]") |
| 132 | + raise typer.Exit(0) |
| 133 | + |
| 134 | + # Add or update the aignostics server config |
| 135 | + config["mcpServers"]["aignostics"] = server_config |
| 136 | + |
| 137 | + # Write the config back |
| 138 | + with config_path.open("w") as f: |
| 139 | + json.dump(config, f, indent=2) |
| 140 | + |
| 141 | + console.print("\n[green]✓[/green] Claude Desktop configured successfully!") |
| 142 | + console.print(f"\n[bold]Configuration written to:[/bold] {config_path}") |
| 143 | + console.print("\n[bold]Server configuration:[/bold]") |
| 144 | + console.print(f" command: {server_config['command']}") |
| 145 | + console.print(f" args: {server_config['args']}") |
| 146 | + console.print("\n[yellow]→[/yellow] Please restart Claude Desktop to load the MCP server.") |
| 147 | + |
| 148 | + |
71 | 149 | @mcp_cli.command("run") |
72 | 150 | def mcp_run() -> None: |
73 | | - """Run the MCP server. |
| 151 | + """Run the MCP server with all tools including MCP Apps. |
74 | 152 |
|
75 | | - Starts an MCP server using `stdio` transport that exposes SDK functionality |
76 | | - to AI agents. The server automatically discovers and mounts tools from |
77 | | - the SDK and any installed plugins. |
| 153 | + Starts an MCP server using stdio transport that exposes SDK functionality |
| 154 | + to AI agents. The server discovers and mounts all available MCP servers, |
| 155 | + including those with MCP Apps for interactive visualizations. |
78 | 156 |
|
79 | 157 | Examples: |
80 | 158 | uv run aignostics mcp run |
81 | 159 | """ |
82 | | - from aignostics.utils import mcp_run # noqa: PLC0415 |
| 160 | + from aignostics.utils import mcp_run_server # noqa: PLC0415 |
83 | 161 |
|
84 | | - mcp_run() |
| 162 | + mcp_run_server() |
85 | 163 |
|
86 | 164 |
|
87 | 165 | @mcp_cli.command("list-tools") |
|
0 commit comments