Skip to content

Commit 21aa081

Browse files
thuongvh2claude
andcommitted
MCP server logging and CLI enhancement for v1.0.0
Add comprehensive logging infrastructure for MCP server: - New mcp/logger.go with thread-safe file logging to ~/.mimir/mcp.log - Request/response logging with microsecond timestamps and timing info - Debug logging in all 7 MCP tools (query, context, impact, detect_changes, rename, cypher) - New 'mimir daemon logs [lines]' CLI command to view daemon logs - Updated README documentation with logging features This release marks v1.0.0 with complete MCP observability. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ca1eb4c commit 21aa081

18 files changed

Lines changed: 2648 additions & 17 deletions

README.md

Lines changed: 122 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,23 @@ The result is a single static binary with zero external dependencies.
9191

9292
```bash
9393
# Index with full features (requires ollama or OpenAI for embeddings)
94-
mimir analyze /path/to/your/project
94+
mimir analyze /path/to/your-project
9595

9696
# Index without embeddings (faster, graph + BM25 search only)
97-
mimir analyze --skip-embeddings /path/to/your/project
97+
mimir analyze --skip-embeddings /path/to/your-project
98+
99+
# Skip auto-setup (daemon, hooks, skills) for manual control
100+
mimir analyze --skip-daemon --skip-hooks --skip-skills /path/to/your-project
98101
```
99102

103+
When you run `mimir analyze`, it automatically:
104+
1. **Indexes your code** into a knowledge graph
105+
2. **Starts the MCP daemon** for persistent background operation
106+
3. **Configures your editors** (Claude Code, VS Code, Cursor, Windsurf, Zed, OpenCode)
107+
4. **Installs Claude Code hooks** for auto-reindexing on file changes
108+
5. **Generates agent skills** — 4 static methodology skills + dynamic module skills per cluster
109+
6. **Creates context files** — AGENTS.md and CLAUDE.md with live index statistics
110+
100111
### 2. Explore via Web UI
101112

102113
```bash
@@ -115,8 +126,20 @@ The web UI provides:
115126
### 3. Use with AI Agents (MCP)
116127

117128
```bash
118-
# Start the MCP stdio server
119-
mimir mcp
129+
# Start the MCP stdio server (or let analyze start it for you)
130+
mimir daemon start
131+
132+
# Check daemon status
133+
mimir daemon status
134+
135+
# View MCP logs (last 30 lines)
136+
mimir daemon logs
137+
138+
# View last 100 lines
139+
mimir daemon logs 100
140+
141+
# Stop the daemon
142+
mimir daemon stop
120143
```
121144

122145
Or configure in your editor's MCP settings:
@@ -144,19 +167,35 @@ mimir setup
144167

145168
| Command | Description |
146169
|---|---|
147-
| `mimir analyze <path>` | Index a repository into the knowledge graph |
170+
| `mimir analyze <path>` | Index a repository into the knowledge graph with auto-setup |
148171
| `mimir serve` | Start the HTTP server + web UI (default: port 7842) |
149172
| `mimir mcp` | Start the MCP stdio server for AI agent integration |
173+
| `mimir daemon start` | Start the MCP daemon in the background |
174+
| `mimir daemon stop` | Stop the MCP daemon |
175+
| `mimir daemon status` | Show daemon status and uptime |
176+
| `mimir daemon restart` | Restart the MCP daemon |
177+
| `mimir daemon logs [lines]` | View MCP daemon logs (default: 30 lines) |
150178
| `mimir list` | List all indexed repositories |
151179
| `mimir status [name]` | Show index status for a repository |
152180
| `mimir clean <name>` | Remove the index for a repository |
153-
| `mimir setup` | Configure MCP settings in supported editors |
181+
| `mimir setup` | Configure MCP settings in all supported editors |
154182
| `mimir wiki [name]` | Generate a wiki from the knowledge graph |
155183

156-
### Flags
184+
### Analyze Flags
157185

158186
```bash
159187
mimir analyze --skip-embeddings <path> # Skip embedding generation
188+
mimir analyze --skip-daemon <path> # Don't start MCP daemon
189+
mimir analyze --skip-hooks <path> # Don't install Claude Code hooks
190+
mimir analyze --skip-skills <path> # Don't install agent skills
191+
mimir analyze --force <path> # Force full re-index even if up to date
192+
mimir analyze --incremental <path> # Force incremental mode
193+
mimir analyze --hint <file> <path> # Hint for faster patch planning
194+
```
195+
196+
### Other Flags
197+
198+
```bash
160199
mimir serve --port 8080 # Custom port (default: 7842)
161200
```
162201

@@ -192,7 +231,82 @@ When connected via MCP, AI agents get access to 7 tools:
192231
| `detect_changes` | Detect uncommitted/recent git changes and their impact |
193232
| `rename` | Plan a coordinated multi-file rename |
194233
| `cypher` | Execute raw graph queries |
195-
| `reindex` | Trigger incremental re-indexing |
234+
| `list_repos` | List all indexed repositories |
235+
236+
---
237+
238+
## Auto-Analyze Features
239+
240+
When you run `mimir analyze`, Mimir sets up a complete AI-powered development environment automatically:
241+
242+
### 1. MCP Daemon
243+
244+
The daemon runs in the background, keeping the MCP server always available:
245+
246+
```bash
247+
mimir daemon start # Start background daemon
248+
mimir daemon status # Check status and uptime
249+
mimir daemon logs # View last 30 log lines
250+
mimir daemon logs 100 # View last 100 log lines
251+
mimir daemon stop # Stop daemon
252+
```
253+
254+
**Log file location**: `~/.mimir/mimir-mcp.log`
255+
256+
The daemon logs all MCP requests, tool calls, errors, and timing information for debugging.
257+
258+
### 2. Editor Auto-Configuration
259+
260+
Mimir detects and configures MCP settings for:
261+
- Claude Code
262+
- VS Code (Copilot)
263+
- Cursor
264+
- Windsurf
265+
- Zed
266+
- OpenCode
267+
268+
### 3. Claude Code Hooks
269+
270+
Two bash hooks enhance your Claude Code experience:
271+
272+
**Pre-Search Hook** (`mimir-pre-search.sh`):
273+
- Intercepts `grep`, `rg`, `find`, and `Glob` tool calls
274+
- Automatically queries the Mimir graph for context
275+
- Augments search results with graph intelligence
276+
277+
**Post-Write Hook** (`mimir-post-write.sh`):
278+
- Triggers after file writes or git commits
279+
- Runs incremental re-indexing automatically
280+
- Fire-and-forget (never blocks Claude)
281+
282+
Hooks are installed to `.claude/hooks/` and configured in `.claude/settings.json`.
283+
284+
### 4. Agent Skills
285+
286+
Mimir installs specialized skills for AI agents:
287+
288+
**Static Skills** (always installed):
289+
- `exploring.md` — Navigate code using the knowledge graph
290+
- `debugging.md` — Trace bugs through call chains
291+
- `impact-analysis.md` — Analyze blast radius before edits
292+
- `refactoring.md` — Plan safe multi-file refactors
293+
294+
**Dynamic Module Skills** (generated per project):
295+
- One skill per detected code cluster/module
296+
- Includes key files, entry points, and dependencies
297+
- Skips low-cohesion clusters (< 0.3 score)
298+
299+
Skills are installed to `.claude/skills/mimir/`.
300+
301+
### 5. Context Files
302+
303+
**AGENTS.md** and **CLAUDE.md** are generated with:
304+
- Live index statistics (symbols, edges, clusters, processes)
305+
- Functional cluster list with cohesion scores
306+
- Execution flow traces
307+
- MCP tool usage guidelines
308+
309+
Read these files before starting work to understand the codebase structure.
196310

197311
---
198312

@@ -265,7 +379,3 @@ mimir/
265379
```
266380

267381
---
268-
269-
## License
270-
271-
MIT

internal/daemon/daemon.go

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package daemon
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"path/filepath"
8+
"strconv"
9+
"strings"
10+
"syscall"
11+
"time"
12+
)
13+
14+
const (
15+
pidFile = "mimir-mcp.pid"
16+
logFile = "mimir-mcp.log"
17+
dataDir = ".mimir"
18+
)
19+
20+
// PIDPath returns the path to the PID file in ~/.mimir/
21+
func PIDPath() string {
22+
home, _ := os.UserHomeDir()
23+
return filepath.Join(home, dataDir, pidFile)
24+
}
25+
26+
// LogPath returns the path to the log file in ~/.mimir/
27+
func LogPath() string {
28+
home, _ := os.UserHomeDir()
29+
return filepath.Join(home, dataDir, logFile)
30+
}
31+
32+
// DataDir returns the path to the ~/.mimir directory
33+
func DataDir() string {
34+
home, _ := os.UserHomeDir()
35+
return filepath.Join(home, dataDir)
36+
}
37+
38+
// ensureDir creates the ~/.mimir directory if it doesn't exist
39+
func ensureDir() error {
40+
return os.MkdirAll(DataDir(), 0755)
41+
}
42+
43+
// Start launches mimir mcp as a detached background process.
44+
// Returns early if already running.
45+
func Start(mimirBin string) error {
46+
if IsRunning() {
47+
return nil // already up
48+
}
49+
50+
if err := ensureDir(); err != nil {
51+
return fmt.Errorf("create data dir: %w", err)
52+
}
53+
54+
logF, err := os.OpenFile(LogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
55+
if err != nil {
56+
return fmt.Errorf("open log: %w", err)
57+
}
58+
59+
cmd := exec.Command(mimirBin, "mcp")
60+
cmd.Stdout = logF
61+
cmd.Stderr = logF
62+
// Detach from parent process group so it survives terminal close
63+
cmd.SysProcAttr = sysProcDetach()
64+
65+
if err := cmd.Start(); err != nil {
66+
logF.Close()
67+
return fmt.Errorf("start daemon: %w", err)
68+
}
69+
70+
// Write PID
71+
pidStr := strconv.Itoa(cmd.Process.Pid)
72+
if err := os.WriteFile(PIDPath(), []byte(pidStr), 0644); err != nil {
73+
logF.Close()
74+
return fmt.Errorf("write pid: %w", err)
75+
}
76+
77+
return nil
78+
}
79+
80+
// Stop sends SIGINT to the daemon process.
81+
func Stop() error {
82+
pid, err := readPID()
83+
if err != nil {
84+
return nil // not running
85+
}
86+
87+
proc, err := os.FindProcess(pid)
88+
if err != nil {
89+
os.Remove(PIDPath())
90+
return nil
91+
}
92+
93+
os.Remove(PIDPath())
94+
return proc.Signal(os.Interrupt)
95+
}
96+
97+
// Restart stops and then starts the daemon.
98+
func Restart(mimirBin string) error {
99+
Stop()
100+
// Give it a moment to fully stop
101+
time.Sleep(100 * time.Millisecond)
102+
return Start(mimirBin)
103+
}
104+
105+
// IsRunning checks whether the daemon PID is alive.
106+
func IsRunning() bool {
107+
pid, err := readPID()
108+
if err != nil {
109+
return false
110+
}
111+
112+
proc, err := os.FindProcess(pid)
113+
if err != nil {
114+
return false
115+
}
116+
117+
// Signal 0 = check existence only
118+
return proc.Signal(syscall.Signal(0)) == nil
119+
}
120+
121+
// WatchAndRestart keeps the daemon alive — call from a supervisor goroutine.
122+
func WatchAndRestart(mimirBin string, interval time.Duration) {
123+
for {
124+
time.Sleep(interval)
125+
if !IsRunning() {
126+
if err := Start(mimirBin); err != nil {
127+
// Log error but keep trying
128+
fmt.Fprintf(os.Stderr, "daemon restart failed: %v\n", err)
129+
}
130+
}
131+
}
132+
}
133+
134+
// ReadPID returns the current daemon PID.
135+
func ReadPID() (int, error) {
136+
return readPID()
137+
}
138+
139+
// GetUptime returns how long the daemon has been running.
140+
func GetUptime() (time.Duration, error) {
141+
if _, err := readPID(); err != nil {
142+
return 0, fmt.Errorf("not running")
143+
}
144+
145+
// Use PID file modification time as proxy for process start time
146+
if stat, err := os.Stat(PIDPath()); err == nil {
147+
return time.Since(stat.ModTime()), nil
148+
}
149+
150+
return 0, fmt.Errorf("could not determine uptime")
151+
}
152+
153+
// LogTail returns the last N lines of the daemon log.
154+
func LogTail(lines int) ([]string, error) {
155+
logPath := LogPath()
156+
data, err := os.ReadFile(logPath)
157+
if err != nil {
158+
if os.IsNotExist(err) {
159+
return []string{}, nil
160+
}
161+
return nil, err
162+
}
163+
164+
allLines := strings.Split(strings.TrimSpace(string(data)), "\n")
165+
if len(allLines) <= lines {
166+
return allLines, nil
167+
}
168+
169+
return allLines[len(allLines)-lines:], nil
170+
}
171+
172+
func readPID() (int, error) {
173+
b, err := os.ReadFile(PIDPath())
174+
if err != nil {
175+
return 0, err
176+
}
177+
return strconv.Atoi(strings.TrimSpace(string(b)))
178+
}

0 commit comments

Comments
 (0)