3.7 KiB
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
# 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:
- TUI (foreground) — RazorConsole terminal UI (
App.razor), owns the console, handles keyboard navigation - HTTP MCP endpoint (background) —
McpWebHostedServiceruns 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:Urlis 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, notREADME.md(README is aspirational) Pages/directory exists but Razor Pages are not enabled inProgram.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 |