diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..0f8d64c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,72 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Language Conventions + +- **Output / explanations:** Russian +- **Code:** English +- **Comments, commit messages:** Russian + +## Commands + +```bash +# Build +dotnet build + +# Run (starts TUI + HTTP MCP endpoint on port 5000) +dotnet run --project LazyBear.MCP + +# Test MCP tool wiring (only needed after changing transport or tool registration) +npx @modelcontextprotocol/inspector dotnet run --project LazyBear.MCP +``` + +There are **no test projects**. After making changes, run `dotnet build`. If MCP wiring changed (new tool, new module, transport changes), also run the inspector. + +Port is controlled via the `ASPNETCORE_URLS` environment variable (default `http://localhost:5000`). Ignore `launchSettings.json` — it shows a different port that is not used. + +## Architecture + +LazyBear is a .NET 10 MCP server exposing Jira, Confluence, and Kubernetes integrations to AI clients. It runs in two simultaneous modes that share a single DI container: + +1. **TUI (foreground)** — RazorConsole terminal UI (`App.razor`), owns the console, handles keyboard navigation +2. **HTTP MCP endpoint (background)** — `McpWebHostedService` runs as a hosted service, serves MCP tool calls on port 5000 + +Both share the same singletons: `ToolRegistryService`, `InMemoryLogSink`, and the three client providers. + +### Plugin System (IToolModule) + +Each integration implements `IToolModule` (in `Services/ToolRegistry/IToolModule.cs`) and declares its `ModuleName`, `ToolNames[]`, and `Description`. Tool classes are auto-discovered at startup via reflection using `WithToolsFromAssembly()` — classes are annotated with `[McpServerToolType]`, methods with `[McpServerTool]`. Registering a new module requires only: implement `IToolModule`, create tool classes with attributes, and add DI registration in `Program.cs`. + +### ToolRegistryService + +Singleton that tracks enabled/disabled state for both modules and individual tools at runtime (no restart needed). Uses `ConcurrentDictionary` for thread safety. Fires `StateChanged` event on toggles; TUI components subscribe to re-render. Tool keys use the format `"ModuleName::ToolName"`. + +### Provider Pattern + +Each client (`K8sClientProvider`, `JiraClientProvider`, `ConfluenceClientProvider`) is a lazy singleton. If initialization fails (missing config, unreachable endpoint), it captures an `InitializationError` string. **Tools return error strings instead of throwing exceptions** — this is intentional so MCP clients see the configuration issue gracefully. + +### Logging + +`InMemoryLogSink` maintains a 500-entry circular `ConcurrentQueue`. All .NET logs flow through `InMemoryLoggerProvider` → sink → `OnLog` event → TUI Logs tab live view. + +### TUI Navigation + +Tabs: Overview → Logs → Settings (switch with `Tab`/`Shift+Tab`). In Settings: arrow keys navigate the module→tool tree, `Space` toggles enable/disable, `Enter` expands/collapses. In Overview, `Enter` on a module jumps to its Settings entry. + +## Configuration Gotchas + +- **`Jira:Url`** is required; if missing, all Jira tools return string errors +- **K8s kubeconfig fallback order:** explicit `Kubernetes:KubeconfigPath` → `~/.kube/config` → in-cluster config +- **Source of truth:** `Program.cs`, not `README.md` (README is aspirational) +- `Pages/` directory exists but Razor Pages are **not enabled** in `Program.cs` — do not use them + +## Key Dependencies + +| Package | Role | +|---------|------| +| `ModelContextProtocol.AspNetCore` 1.2.0 | HTTP MCP transport | +| `KubernetesClient` 19.0.2 | K8s API | +| `RestSharp` 112.0.0 | Jira / Confluence HTTP | +| `RazorConsole.Core` 0.5.0 | Terminal UI framework | +| `Polly` 8.4.2 | Retry/resilience policies |