Skip to main content
Glama
amit11-ibm
by amit11-ibm
README.md
# kernel-mcp

AI-powered Linux kernel development assistant as an MCP server for IBM Bob.

`kernel-mcp` is a Python MCP server that exposes kernel development tools — symbol search, static analysis, build automation, QEMU, GDB, and more — to IBM Bob via the Model Context Protocol. It runs locally as a stdio child process and integrates with the existing `LocalLLM-MCP` server for LLM routing.

---

## Features

| Status | Tool | Description |
|---|---|---|
| ✅ **Feature 1** | `search_kernel_symbol` | Search the Linux kernel source tree for a symbol by name |
| ✅ **Feature 2** | `explain_kernel_file` | Explain a kernel source file using a local LLM |
| ✅ **Feature 3** | `build_kernel` | Build the kernel with a given config |
| ✅ **Feature 4** | `run_checkpatch` | Run `checkpatch.pl` on a patch or file |
| ✅ **Feature 5** | `run_sparse` | Run the Sparse static analyser |
| ✅ **Feature 6** | `boot_qemu` | Boot a kernel image in QEMU |
| ✅ **Feature 7** | `analyze_oops` | Analyse a kernel oops / crash log |
| ✅ **Feature 8** | `git_bisect` | Assist with `git bisect` sessions |
| ✅ **Feature 9** | `review_patch` | Review a patch with `checkpatch.pl` and optional LLM semantic review |
| ✅ **Feature 10** | `run_smatch` | Run the Smatch static analyser (`make C=1 CHECK=smatch`) |
| ✅ **Feature 11** | `run_coccinelle` | Run Coccinelle semantic patch tool (`spatch`) on a kernel source directory |
| ✅ **Feature 12** | `generate_commit_message` | Generate a kernel-style commit message from a unified diff using a local LLM |
| ✅ **Feature 13** | `route_to_best_model` | Route a kernel development prompt to the best local LLM using a capability matrix |
| ✅ **Feature 14** | `debug_kernel` | Attach GDB to a QEMU/KGDB kernel and execute debug commands (backtrace, registers, break, etc.) |
| ✅ **Feature 15** | `find_subsystem` | Identify the kernel subsystem that owns a file path, returning the MAINTAINERS entry, maintainer list, mailing list, git tree, and status |
| ✅ **Feature 16** | `list_kernel_configs` | List all available defconfig/specialconfig targets by parsing `make help` output |
| ✅ **Feature 16** | `run_menuconfig` | Return the terminal command needed to run `make menuconfig` interactively (TUI requires a real terminal) |
| ✅ **Feature 16** | `clean_kernel` | Clean the kernel build tree with `make clean` (keeps `.config`) or `make mrproper` (full reset) |
| ✅ **Feature 17** | `shutdown_qemu` | Stop a QEMU session previously started by `boot_qemu` using its session UUID |
| ✅ **Feature 18** | `generate_call_graph` | Generate a call graph for a kernel function using `cflow` (with grep fallback) |
| ✅ **Feature 19** | `search_documentation` | Search the `Documentation/` directory for `.rst`, `.txt`, and `.md` files matching a literal query |

---

## Architecture

See [docs/Architecture.md](docs/Architecture.md) for the full layered architecture diagram and design rationale.

```
IBM Bob  ──MCP STDIO──►  kernel-mcp/server.py
                              │
                         router.py  (validation)
                              │
                         tools/*.py  (business logic)
                              │
                         services/*.py  (subprocess calls)
                              │
                      ripgrep / grep / make / qemu / gdb
                              │
                      Linux kernel source tree
```

---

## Prerequisites

| Requirement | Notes |
|---|---|
| Python 3.11+ | 3.14 recommended |
| Linux kernel source tree | Any version; clone from kernel.org |
| `ripgrep` (`rg`) | Strongly recommended for speed; falls back to `grep` |
| `grep` | Standard fallback; available on all Linux/macOS systems |
| IBM Bob with MCP support | For full integration |

---

## Installation

### 1. Clone or copy the project

```bash
# kernel-mcp lives alongside LocalLLM-MCP in your workspace
git clone <your-repo> kernel-mcp
cd kernel-mcp
```

### 2. Create a virtual environment

```bash
python -m venv .venv
# Linux / macOS
source .venv/bin/activate
# Windows
.venv\Scripts\activate
```

### 3. Install dependencies

```bash
pip install -r requirements.txt
```

### 4. Configure environment

```bash
cp .env.example .env
```

Edit `.env` and set `KERNEL_SOURCE_PATH`:

```env
KERNEL_SOURCE_PATH=/home/user/linux
```

### 5. Verify configuration

```bash
python config.py
```

Expected output:
```
=== kernel-mcp Configuration Smoke Test ===

kernel_source_path     : /home/user/linux
search_timeout_seconds : 30
max_results            : 50
context_lines          : 5
ripgrep_path           : rg
grep_path              : grep

=== Smoke test passed ===
```

---

## IBM Bob Integration

Add `kernel-mcp` to Bob's `mcp.json`:

**Linux / macOS:**
```json
{
  "mcpServers": {
    "kernel-mcp": {
      "command": "/path/to/kernel-mcp/.venv/bin/python",
      "args": ["/path/to/kernel-mcp/server.py"],
      "cwd": "/path/to/kernel-mcp",
      "env": {
        "KERNEL_SOURCE_PATH": "/home/user/linux",
        "LOG_LEVEL": "INFO"
      },
      "alwaysAllow": ["search_kernel_symbol", "review_patch"],
      "disabled": false
    }
  }
}
```

**Windows:**
```json
{
  "mcpServers": {
    "kernel-mcp": {
      "command": "C:\\kernel-mcp\\.venv\\Scripts\\python.exe",
      "args": ["C:\\kernel-mcp\\server.py"],
      "cwd": "C:\\kernel-mcp",
      "env": {
        "KERNEL_SOURCE_PATH": "C:\\linux",
        "LOG_LEVEL": "INFO"
      },
      "alwaysAllow": ["search_kernel_symbol", "review_patch"],
      "disabled": false
    }
  }
}
```

---

## Usage Examples

Once Bob has the server connected, ask naturally:

| Prompt | What happens |
|---|---|
| *"Find all declarations of task_struct in the kernel"* | `search_kernel_symbol("task_struct")` |
| *"Search for kmalloc only in the mm subsystem"* | `search_kernel_symbol("kmalloc", path_filter="mm/")` |
| *"Where is do_fork defined?"* | `search_kernel_symbol("do_fork", path_filter="kernel/")` |
| *"Find netif_receive_skb in the network stack"* | `search_kernel_symbol("netif_receive_skb", path_filter="net/")` |
| *"Generate a default kernel config"* | `build_kernel("defconfig")` |
| *"Build the kernel for ARM64 with 8 jobs"* | `build_kernel("all", arch="arm64", jobs=8)` |
| *"Clean the kernel build tree"* | `build_kernel("mrproper")` |
| *"Build only the e1000e driver module"* | `build_kernel("M=drivers/net/e1000e/", jobs=4)` |
| *"Check my patch for style issues"* | `run_checkpatch("0001-my-fix.patch")` |
| *"Run checkpatch on a source file"* | `run_checkpatch("drivers/net/foo.c", is_patch=False)` |
| *"Run strict checkpatch on a patch"* | `run_checkpatch("fix.patch", strict=True)` |
| *"Run sparse on the mm subsystem"* | `run_sparse("mm/")` |
| *"Sparse-check a single object file"* | `run_sparse("mm/slab.o")` |
| *"Deep sparse check for ARM64"* | `run_sparse("drivers/usb/", check_level=2, arch="arm64")` |
| *"Boot the default x86 kernel image"* | `boot_qemu("arch/x86/boot/bzImage")` |
| *"Boot with a serial console and panic timeout"* | `boot_qemu("arch/x86/boot/bzImage", append="console=ttyS0 panic=5")` |
| *"Boot an ARM64 kernel with initrd"* | `boot_qemu("arch/arm64/boot/Image", arch="arm64", machine="virt", initrd="initrd.img", memory_mb=512)` |
| *"Boot with 2 CPUs and 1 GiB RAM"* | `boot_qemu("arch/x86/boot/bzImage", memory_mb=1024, extra_args=["-smp", "2"])` |
| *"Analyse this oops log"* | `analyze_oops("<paste log text>")` |
| *"What caused this kernel panic?"* | `analyze_oops("<log>", kernel_version_hint="6.1.0-rc4")` |
| *"Analyse oops with a symbol map"* | `analyze_oops("<log>", symbol_map_path="System.map")` |
| *"Start bisecting between v6.2 (bad) and v6.1 (good)"* | `git_bisect("start", bad_commit="v6.2", good_commits=["v6.1"])` |
| *"Mark current commit as good"* | `git_bisect("good")` |
| *"Mark current commit as bad"* | `git_bisect("bad")` |
| *"Skip the current untestable commit"* | `git_bisect("skip")` |
| *"Run automated bisect with build test"* | `git_bisect("run", run_command="make defconfig && make -j4 && ./test.sh")` |
| *"Show bisect session log"* | `git_bisect("log")` |
| *"Abort bisect and restore HEAD"* | `git_bisect("reset")` |
| *"Review my patch for style issues"* | `review_patch("patches/0001-fix-mm-slab.patch")` |
| *"Strict checkpatch on a patch file"* | `review_patch("fix.patch", strict=True)` |
| *"Review inline diff text for style"* | `review_patch("<paste raw diff here>")` |
| *"Full review: checkpatch + LLM code review"* | `review_patch("fix.patch", use_llm=True)` |
| *"LLM review with Qwen Coder model"* | `review_patch("fix.patch", use_llm=True, model_name="qwen-coder")` |
| *"Run Coccinelle kzalloc-simple script on drivers/net/"* | `run_coccinelle("scripts/coccinelle/api/kzalloc-simple.cocci", "drivers/net/")` |
| *"Apply my custom .cocci script to the mm subsystem"* | `run_coccinelle("my-checks.cocci", "mm/")` |
| *"Run Coccinelle without headers"* | `run_coccinelle("scripts/coccinelle/api/foo.cocci", "drivers/", extra_args=["--no-includes"])` |
| *"Generate a commit message for my patch"* | `generate_commit_message("<paste unified diff here>")` |
| *"Write a kernel commit message for this fix.patch"* | `generate_commit_message("fix.patch")` |
| *"Use Qwen Coder to write a commit message"* | `generate_commit_message("<diff>", model_name="qwen-coder")` |

---

## Running Tests

```bash
# From the kernel-mcp directory, with venv active
pytest tests/ -v
```

Test groups (`test_search_symbol.py`):
- **A** — Backend detection (ripgrep/grep/neither)
- **B** — Subprocess execution and output parsing
- **C** — MatchType classification heuristic
- **D** — Context enrichment (async file reads)
- **E** — Deduplication and result assembly
- **F** — Router input validation
- **G** — Smoke: server tool returns valid JSON
- **H** — Integration: real backend against synthetic kernel tree

Test groups (`test_build_kernel.py`):
- **A** — `locate_make`: binary detection
- **B** — `run_build`: subprocess execution (mocked)
- **C** — `_parse_diagnostics`: GCC/Clang error and warning extraction
- **D** — `_build_make_args`: argument assembly (arch, jobs, target)
- **E** — `build_kernel` tool layer (mocked service)
- **F** — Router input validation
- **G** — Server smoke: tool returns valid JSON

Integration tests (group H) are automatically skipped if neither `rg` nor `grep` is found on `PATH`.

Test groups (`test_run_checkpatch.py`):
- **A** — `locate_checkpatch`: script detection (in-tree, explicit, missing)
- **B** — `run_checkpatch` service: subprocess execution (mocked)
- **C** — `_parse_issues`: ERROR/WARNING/CHECK extraction
- **D** — `_parse_total_lines`: summary line parsing
- **E** — `run_checkpatch` tool layer (mocked service)
- **F** — Router input validation
- **G** — Server smoke: tool returns valid JSON

Test groups (`test_boot_qemu.py`):
- **A** — `locate_qemu`: binary detection (found, missing, custom path, arch aliases)
- **B** — `run_qemu` service: subprocess execution (mocked)
- **C** — `_detect_events`: boot/panic/oops detection from output text
- **D** — `_build_summary`: summary string construction
- **E** — `boot_qemu` tool layer (mocked service)
- **F** — Router input validation
- **G** — Server smoke: `boot_qemu` returns valid JSON

Test groups (`test_analyze_oops.py`):
- **A** — `_detect_oops_type`: crash-type detection (oops, bug, panic, warning, kasan, ubsan, lockdep, …)
- **B** — `_extract_metadata`: CPU / PID / comm / kernel-version extraction
- **C** — `_extract_registers`: register dump parsing (x86-64, ARM64, RISC-V)
- **D** — `_extract_call_trace`: call-trace frame extraction and indexing
- **E** — `_build_summary` / `_build_root_cause_hint` / `_infer_subsystem`
- **F** — `analyze_oops` tool layer (direct call)
- **G** — Router input validation (`route_analyze_oops`)
- **H** — Server smoke: `analyze_oops` returns valid JSON

Test groups (`test_run_sparse.py`):
- **A** — `locate_sparse`: binary detection (found, missing)
- **B** — `run_sparse_via_make` service: subprocess execution (mocked)
- **C** — `_parse_findings`: sparse diagnostic extraction from stderr
- **D** — `_build_summary`: summary string construction
- **E** — `run_sparse` tool layer (mocked service)
- **F** — Router input validation
- **G** — Server smoke: tool returns valid JSON

Test groups (`test_git_bisect.py`):
- **A** — `locate_git`: binary detection (found, missing, custom path)
- **B** — `run_git_command` service: subprocess execution (mocked)
- **C** — `_build_git_args`: argument assembly for each action
- **D** — `_parse_bisect_output`: state detection from git output
- **E** — `_build_summary`: summary string construction
- **F** — `git_bisect` tool layer (mocked service)
- **G** — Router input validation (`route_git_bisect`)
- **H** — Server smoke: `git_bisect` returns valid JSON

Test groups (`test_review_patch.py`):
- **A** — `normalise_patch_input`: file-path detection vs inline-text detection
- **B** — `run_patch_checkpatch` service: subprocess delegation (mocked)
- **C** — `_parse_diff_metadata`: changed-file extraction and statistics
- **D** — `_infer_subsystem`: top-level subsystem detection
- **E** — `review_patch` tool layer (mocked service + mocked LLM)
- **F** — Router input validation (`route_review_patch`)
- **G** — Server smoke: `review_patch` returns valid JSON

Test groups (`test_run_smatch.py`):
- **A** — `locate_smatch`: binary detection (found, missing, custom path)
- **B** — `run_smatch_via_make` service: subprocess execution (mocked)
- **C** — `_parse_findings`: smatch diagnostic extraction from stderr
- **D** — `_build_summary`: summary string construction
- **E** — `run_smatch` tool layer (mocked service)
- **F** — Router input validation
- **G** — Server smoke: tool returns valid JSON

Test groups (`test_run_coccinelle.py`):
- **A** — `locate_spatch`: binary detection (found, missing, custom path)
- **B** — `run_spatch` service: subprocess execution (mocked)
- **C** — `_parse_transformations`: unified diff parsing from stdout
- **D** — `_parse_findings`: Coccinelle diagnostic extraction from stderr
- **E** — `run_coccinelle` tool layer (mocked service)
- **F** — Router input validation
- **G** — Server smoke: tool returns valid JSON

Test groups (`test_generate_commit_message.py`):
- **A** — `_parse_diff_metadata`: file path extraction and line counts
- **B** — `_infer_subsystem`: top-level subsystem detection
- **C** — `_build_prompt` / `_extract_commit_message` / `_split_subject_body`: prompt + response post-processing
- **D** — `generate_commit_message` tool layer (LLM mocked)
- **E** — Router input validation (`route_generate_commit_message`)
- **F** — Server smoke: tool returns valid JSON

---

## Configuration Reference

### `kernel_mcp_config.json`

| Key | Type | Default | Description |
|---|---|---|---|
| `search_timeout_seconds` | int | `30` | Max seconds for a search subprocess |
| `max_results` | int | `50` | Hard cap on total matches returned |
| `context_lines` | int | `5` | Lines above/below each match |
| `ripgrep_path` | string | `"rg"` | Name or absolute path of ripgrep binary |
| `grep_path` | string | `"grep"` | Name or absolute path of grep binary |
| `make_path` | string | `"make"` | Name or absolute path of the make binary |
| `build_timeout_seconds` | int | `1800` | Max seconds for a kernel build (30 min) |
| `build_jobs` | int | `0` | Default `-j N` for make. `0` = no `-j` flag |
| `checkpatch_path` | string | `""` | Path to `checkpatch.pl`. Empty = auto-detect at `scripts/checkpatch.pl` |
| `perl_path` | string | `"perl"` | Name or absolute path of the Perl interpreter |
| `checkpatch_timeout_seconds` | int | `60` | Max seconds for a `checkpatch.pl` run |
| `sparse_path` | string | `"sparse"` | Name or absolute path of the `sparse` binary |
| `sparse_timeout_seconds` | int | `300` | Max seconds for a `make C=` sparse run (5 min) |
| `smatch_path` | string | `"smatch"` | Name or absolute path of the `smatch` binary |
| `smatch_timeout_seconds` | int | `300` | Max seconds for a `make C=` smatch run (5 min) |
| `spatch_path` | string | `"spatch"` | Name or absolute path of the `spatch` (Coccinelle) binary |
| `coccinelle_timeout_seconds` | int | `600` | Max seconds for a `spatch` run (10 min) |
| `qemu_path` | string | `""` | Name or absolute path of the qemu-system binary. Empty = auto-detect `qemu-system-<arch>` on PATH |
| `qemu_timeout_seconds` | int | `60` | Max seconds for a QEMU boot session |
| `qemu_memory_mb` | int | `256` | Default guest RAM in MiB (`-m <N>M`) |
| `oops_log_max_bytes` | int | `131072` | Max byte length of a kernel oops log accepted by `analyze_oops` (default 128 KiB) |
| `generate_commit_message_max_bytes` | int | `131072` | Max byte length of a unified diff accepted by `generate_commit_message` (default 128 KiB) |

### Environment Variables

| Variable | Required | Description |
|---|---|---|
| `KERNEL_SOURCE_PATH` | **Yes** | Absolute path to the Linux kernel source tree |
| `SEARCH_TIMEOUT_SECONDS` | No | Override `search_timeout_seconds` |
| `MAX_RESULTS` | No | Override `max_results` |
| `CONTEXT_LINES` | No | Override `context_lines` |
| `RIPGREP_PATH` | No | Override ripgrep binary path |
| `GREP_PATH` | No | Override grep binary path |
| `MAKE_PATH` | No | Override make binary path |
| `BUILD_TIMEOUT_SECONDS` | No | Override `build_timeout_seconds` |
| `BUILD_JOBS` | No | Override `build_jobs` |
| `CHECKPATCH_PATH` | No | Override path to `checkpatch.pl` |
| `PERL_PATH` | No | Override Perl interpreter path |
| `CHECKPATCH_TIMEOUT_SECONDS` | No | Override `checkpatch_timeout_seconds` |
| `SPARSE_PATH` | No | Override `sparse` binary path |
| `SPARSE_TIMEOUT_SECONDS` | No | Override `sparse_timeout_seconds` |
| `QEMU_PATH` | No | Override qemu-system binary path |
| `QEMU_TIMEOUT_SECONDS` | No | Override `qemu_timeout_seconds` |
| `QEMU_MEMORY_MB` | No | Override `qemu_memory_mb` |
| `OOPS_LOG_MAX_BYTES` | No | Override `oops_log_max_bytes` |
| `SMATCH_PATH` | No | Override `smatch` binary path |
| `SMATCH_TIMEOUT_SECONDS` | No | Override `smatch_timeout_seconds` |
| `SPATCH_PATH` | No | Override `spatch` binary path |
| `COCCINELLE_TIMEOUT_SECONDS` | No | Override `coccinelle_timeout_seconds` |
| `GENERATE_COMMIT_MESSAGE_MAX_BYTES` | No | Override `generate_commit_message_max_bytes` |
| `LOG_LEVEL` | No | `DEBUG`, `INFO`, `WARNING`, `ERROR` (default `INFO`) |

---

## Tool Reference

- [search_kernel_symbol](docs/tools/search_kernel_symbol.md)
- [explain_kernel_file](docs/tools/explain_kernel_file.md)
- [build_kernel](docs/tools/build_kernel.md)
- [run_checkpatch](docs/tools/run_checkpatch.md)
- [run_sparse](docs/tools/run_sparse.md)
- [boot_qemu](docs/tools/boot_qemu.md)
- [analyze_oops](docs/tools/analyze_oops.md)
- [git_bisect](docs/tools/git_bisect.md)
- [review_patch](docs/tools/review_patch.md)
- [run_smatch](docs/tools/run_smatch.md)
- [run_coccinelle](docs/tools/run_coccinelle.md)
- [generate_commit_message](docs/tools/generate_commit_message.md)

---

## Relationship to LocalLLM-MCP

`kernel-mcp` is a **sibling project** to `LocalLLM-MCP`. They run as separate MCP servers registered independently in Bob's `mcp.json`. Kernel MCP does not modify or depend on LocalLLM-MCP's code. Future features will call LocalLLM-MCP tools via Bob's tool-routing to send kernel source code to local LLMs for explanation and review.

---

## License

MIT