Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f56b9ee
feat(network): add ip route tool
Albe83 Feb 7, 2026
0baea07
docs(network): document ip route tool
Albe83 Feb 7, 2026
65e1513
test(storage): skip restricted path test when root
Albe83 Feb 7, 2026
74b8fb0
test(network): cover partial failure for ip route table
Albe83 Feb 7, 2026
73fbe45
feat(dnf): add read-only dnf tools
Albe83 Feb 7, 2026
435f647
feat(dnf): add repo, group, module, provides tools
Albe83 Feb 7, 2026
8020319
feat(dnf): add list output limits
Albe83 Feb 7, 2026
9a20b1e
Add transport options to documentation
samdoran Feb 4, 2026
b227e24
Add transport options
samdoran Feb 4, 2026
abc0292
Update default values in docs
samdoran Feb 4, 2026
f8f293c
Add tests
samdoran Feb 4, 2026
553416b
Update environment variable documentation
samdoran Feb 4, 2026
eff4842
Correct formatting of JSON example
samdoran Feb 4, 2026
39dff15
Add documentation about how to run using HTTP in a container
samdoran Feb 4, 2026
49174d4
Set transport default values
samdoran Feb 5, 2026
c2f4b8c
Always pass the log_level
samdoran Feb 5, 2026
161f2fa
Only ignore errors under test
samdoran Feb 5, 2026
a9c9c25
Be more explicit about pytest being the called executable
samdoran Feb 5, 2026
c49aa78
feat(network): add ip route tool
Albe83 Feb 7, 2026
5982718
docs(network): document ip route tool
Albe83 Feb 7, 2026
0c6a341
test(storage): skip restricted path test when root
Albe83 Feb 7, 2026
c20af9b
test(network): cover partial failure for ip route table
Albe83 Feb 7, 2026
366729f
feat(dnf): add read-only dnf tools
Albe83 Feb 7, 2026
9c77ff5
feat(dnf): add repo, group, module, provides tools
Albe83 Feb 7, 2026
af22451
feat(dnf): add list output limits
Albe83 Feb 7, 2026
46327b5
fix(containerfile): align wheelhouse source path with hatch package l…
Albe83 Feb 20, 2026
2dc21f5
Merge branch 'rhel-lightspeed:main' into private/main
Albe83 Feb 20, 2026
3d993ab
Merge pull request #4 from Albe83/chore/devops-containerfile-cicd
Albe83 Feb 20, 2026
945176b
Merge pull request #5 from Albe83/feat/dnf-tools
Albe83 Feb 20, 2026
e9b576a
Merge branch 'private/main' into feat/ip-route-tool
Albe83 Feb 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions Containerfile.wheelhouse
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
ARG APP_VERSION=0.0.0

ARG BASE_IMAGE=python:3.12-slim
# ARG BASE_IMAGE=python:3.12-alpine

ARG PIP_OPTS="--prefer-binary"
ARG UV_OPTS="--no-dev --no-editable --frozen --no-install-project"
ARG PIP_EXTRA_OPTS=""
ARG UV_EXTRA_OPTS=""
ARG UV_EXTRA_OPTS="--allow-insecure-host pypi.org --allow-insecure-host files.pythonhosted.org"
ARG PIP_EXTRA_OPTS="--trusted-host pypi.org --trusted-host files.pythonhosted.org"

FROM ${BASE_IMAGE} as builder
ARG APP_VERSION
ARG PIP_OPTS
ARG PIP_EXTRA_OPTS
ARG UV_OPTS
ARG UV_EXTRA_OPTS

ENV SETUPTOOLS_SCM_PRETEND_VERSION=${APP_VERSION}

RUN --mount=type=cache,id=linux-mcp-server-wheelhouse-pip,target=/root/.cache/pip\
pip install ${PIP_OPTS} ${PIP_EXTRA_OPTS} uv build hatchling hatch-vcs
WORKDIR /src
COPY pyproject.toml uv.lock README.md /src/
RUN uv export ${UV_OPTS} ${UV_EXTRA_OPTS} -o requirements.lock.txt
RUN --mount=type=cache,id=linux-mcp-server-wheelhouse-pip,target=/root/.cache/pip\
mkdir -p /wheelhouse && \
pip wheel ${PIP_OPTS} ${PIP_EXTRA_OPTS} \
--wheel-dir /wheelhouse \
-r requirements.lock.txt
COPY src/ /src/src/
RUN test -d /src/src/linux_mcp_server
RUN --mount=type=cache,id=linux-mcp-server-wheelhouse-pip,target=/root/.cache/pip\
python -m build --wheel --outdir /wheelhouse --no-isolation

FROM scratch AS wheelhouse
COPY --from=builder --chmod=444 /wheelhouse /
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ A Model Context Protocol (MCP) server for read-only Linux system administration,
- **Remote SSH Execution**: Execute commands on remote systems via SSH with key-based authentication
- **Multi-Host Management**: Connect to different remote hosts in the same session
- **Comprehensive Diagnostics**: System info, services, processes, logs, network, and storage
- **Package Insights (DNF)**: Query packages, repositories, provides, groups, and modules
- **Configurable Log Access**: Control which log files can be accessed via environment variables
- **RHEL/systemd Focused**: Optimized for Red Hat Enterprise Linux systems

Expand Down
3 changes: 2 additions & 1 deletion docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ MCP tools organized by category:
- **[Services](tools/services.md)** - Systemd service management
- **[Processes](tools/processes.md)** - Process listing and details
- **[Logs](tools/logs.md)** - Journal, audit, and log file access
- **[Network](tools/network.md)** - Network interfaces, connections, ports
- **[Network](tools/network.md)** - Network interfaces, connections, ports, routes
- **[Storage](tools/storage.md)** - Block devices, directory and file listing
- **[DNF](tools/dnf.md)** - Package and repository information

### Utilities

Expand Down
6 changes: 6 additions & 0 deletions docs/api/tools/dnf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# DNF Tools

::: linux_mcp_server.tools.dnf
options:
show_root_heading: true
show_root_full_path: false
17 changes: 17 additions & 0 deletions docs/cheatsheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,30 @@ A quick reference guide for common tasks and the tools to use.
| **Service Logs** | `get_service_logs` | "Show recent logs for sshd." |
| **Specific Log File** | `read_log_file` | "Read the last 50 lines of /var/log/messages." |

## 📦 Packages (DNF)

| I want to check... | Use this tool | Example Prompt |
|-------------------|---------------|----------------|
| **Installed Packages** | `list_dnf_installed_packages` | "List all installed packages." |
| **Available Packages** | `list_dnf_available_packages` | "What packages are available in repos?" |
| **Package Details** | `get_dnf_package_info` | "Show details for bash." |
| **Repositories** | `list_dnf_repositories` | "Which repositories are enabled?" |
| **File Provides** | `dnf_provides` | "Which package provides /usr/bin/python3?" |
| **Repository Info** | `get_dnf_repo_info` | "Show details for baseos." |
| **Group List** | `list_dnf_groups` | "List all package groups." |
| **Group Info** | `get_dnf_group_info` | "Show details for Development Tools." |
| **Group Summary** | `get_dnf_group_summary` | "Summarize installed groups." |
| **Module List** | `list_dnf_modules` | "List nodejs module streams." |
| **Module Provides** | `dnf_module_provides` | "Which module provides python3?" |

## 🌐 Network

| I want to check... | Use this tool | Example Prompt |
|-------------------|---------------|----------------|
| **IP Addresses** | `get_network_interfaces` | "What is my IP address?" |
| **Open Ports** | `get_listening_ports` | "What ports are open?" |
| **Connections** | `get_network_connections` | "Who is connected to port 22?" |
| **Routes** | `get_ip_route_table` | "Show me the routing table" |

## 📂 Files & Storage

Expand Down
1 change: 1 addition & 0 deletions docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ pytest
linux-mcp-server/
├── src/linux_mcp_server/
│ ├── tools/ # MCP tool implementations
│ │ ├── dnf.py # DNF package manager tools
│ │ ├── logs.py # Log reading tools
│ │ ├── network.py # Network diagnostic tools
│ │ ├── processes.py # Process management tools
Expand Down
125 changes: 124 additions & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,120 @@ Reads a specific log file (must be in the allowed list).

**Security Note:** This tool respects the `LINUX_MCP_ALLOWED_LOG_PATHS` environment variable whitelist.

### Package Management (DNF)

#### `list_dnf_installed_packages`
Lists installed packages via `dnf`.

**Parameters:**
- `host` (string, optional): Remote host identifier
- `limit` (number, optional): Maximum number of output lines to return (default: 500)
- `offset` (number, optional): Number of output lines to skip (default: 0)
- `no_limit` (boolean, optional): Disable output truncation (default: false)

**Example use case:** "Show me all installed packages."

#### `list_dnf_available_packages`
Lists packages available in configured repositories.

**Parameters:**
- `host` (string, optional): Remote host identifier
- `limit` (number, optional): Maximum number of output lines to return (default: 500)
- `offset` (number, optional): Number of output lines to skip (default: 0)
- `no_limit` (boolean, optional): Disable output truncation (default: false)

**Example use case:** "Which packages are available from enabled repos?"

#### `get_dnf_package_info`
Returns detailed information for a specific package.

**Parameters:**
- `package` (string, required): Package name (e.g., "bash", "openssl")
- `host` (string, optional): Remote host identifier

**Example use case:** "Get details for the bash package."

#### `list_dnf_repositories`
Lists configured repositories and their status.

**Parameters:**
- `host` (string, optional): Remote host identifier
- `limit` (number, optional): Maximum number of output lines to return (default: 500)
- `offset` (number, optional): Number of output lines to skip (default: 0)
- `no_limit` (boolean, optional): Disable output truncation (default: false)

**Example use case:** "Show me all configured repositories and whether they are enabled."

#### `dnf_provides`
Finds packages that provide a file or binary.

**Parameters:**
- `query` (string, required): File path or binary name (e.g., "/usr/bin/python3", "libssl.so.3")
- `host` (string, optional): Remote host identifier

**Example use case:** "Which package provides /usr/bin/python3?"

#### `get_dnf_repo_info`
Shows detailed information for a specific repository.

**Parameters:**
- `repo_id` (string, required): Repository id (e.g., "baseos", "appstream")
- `host` (string, optional): Remote host identifier

**Example use case:** "Show details for the baseos repository."

#### `list_dnf_groups`
Lists available and installed package groups.

**Parameters:**
- `host` (string, optional): Remote host identifier
- `limit` (number, optional): Maximum number of output lines to return (default: 500)
- `offset` (number, optional): Number of output lines to skip (default: 0)
- `no_limit` (boolean, optional): Disable output truncation (default: false)

**Example use case:** "List all package groups."

#### `get_dnf_group_info`
Shows details for a specific package group.

**Parameters:**
- `group` (string, required): Group name (e.g., "Development Tools")
- `host` (string, optional): Remote host identifier

**Example use case:** "Show details for the Development Tools group."

#### `get_dnf_group_summary`
Shows a summary of installed and available groups.

**Parameters:**
- `host` (string, optional): Remote host identifier
- `limit` (number, optional): Maximum number of output lines to return (default: 500)
- `offset` (number, optional): Number of output lines to skip (default: 0)
- `no_limit` (boolean, optional): Disable output truncation (default: false)

**Example use case:** "Summarize installed and available groups."

#### `list_dnf_modules`
Lists modules (optionally filtered by module name).

**Parameters:**
- `module` (string, optional): Module name filter (e.g., "nodejs")
- `host` (string, optional): Remote host identifier
- `limit` (number, optional): Maximum number of output lines to return (default: 500)
- `offset` (number, optional): Number of output lines to skip (default: 0)
- `no_limit` (boolean, optional): Disable output truncation (default: false)

**Example use case:** "List available nodejs module streams."

#### `dnf_module_provides`
Shows modules that provide a specific package.

**Parameters:**
- `package` (string, required): Package name (e.g., "python3")
- `host` (string, optional): Remote host identifier

**Example use case:** "Which module provides python3?"

### Network Diagnostics

#### `get_network_interfaces`
Expand All @@ -212,6 +326,15 @@ Returns ports that are listening on the system.

**Example use case:** "What services are listening on network ports?"

#### `get_ip_route_table`
Returns IPv4/IPv6 routing table entries using `ip route`.

**Parameters:**
- `family` (string, optional): `ipv4`, `ipv6`, or `all` (default: `ipv4`)
- `host` (string, optional): Remote host identifier

**Example use case:** "Show me the IPv4 routing table."

### Storage & Disk Analysis

#### `list_block_devices`
Expand Down Expand Up @@ -266,6 +389,7 @@ See [Client Configuration](clients.md) for environment variables and AI agent in
1. "Show me all network interfaces and their status" → `get_network_interfaces`
2. "What ports are listening on this system?" → `get_listening_ports`
3. "Show me active network connections" → `get_network_connections`
4. "Show me the routing table" → `get_ip_route_table`

### Disk Space Problems
1. "Show me disk usage for all filesystems" → `get_disk_usage`
Expand Down Expand Up @@ -318,4 +442,3 @@ See the [Troubleshooting Guide](troubleshooting.md) for detailed solutions, debu
4. **Security First**: Only whitelist log files that are necessary for diagnostics.

5. **Regular Updates**: Keep the MCP server and its dependencies updated for security and compatibility.

62 changes: 62 additions & 0 deletions src/linux_mcp_server/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ class CommandGroup(BaseModel):
"stats": CommandSpec(args=("cat", "/proc/net/dev")),
}
),
"ip_route": CommandGroup(
commands={
"ipv4": CommandSpec(args=("ip", "route", "show")),
"ipv6": CommandSpec(args=("ip", "-6", "route", "show")),
}
),
# === Logs ===
"journal_logs": CommandGroup(
commands={
Expand Down Expand Up @@ -233,6 +239,62 @@ class CommandGroup(BaseModel):
"default": CommandSpec(args=("cat", "{path}")),
}
),
# === Packages (dnf) ===
"dnf_list_installed": CommandGroup(
commands={
"default": CommandSpec(args=("dnf", "list", "installed")),
}
),
"dnf_list_available": CommandGroup(
commands={
"default": CommandSpec(args=("dnf", "list", "available")),
}
),
"dnf_package_info": CommandGroup(
commands={
"default": CommandSpec(args=("dnf", "info", "{package}")),
}
),
"dnf_repolist": CommandGroup(
commands={
"default": CommandSpec(args=("dnf", "repolist", "--all")),
}
),
"dnf_provides": CommandGroup(
commands={
"default": CommandSpec(args=("dnf", "provides", "{query}")),
}
),
"dnf_repo_info": CommandGroup(
commands={
"default": CommandSpec(args=("dnf", "repoinfo", "{repo_id}")),
}
),
"dnf_group_list": CommandGroup(
commands={
"default": CommandSpec(args=("dnf", "group", "list")),
}
),
"dnf_group_info": CommandGroup(
commands={
"default": CommandSpec(args=("dnf", "group", "info", "{group}")),
}
),
"dnf_group_summary": CommandGroup(
commands={
"default": CommandSpec(args=("dnf", "group", "summary")),
}
),
"dnf_module_list": CommandGroup(
commands={
"default": CommandSpec(args=("dnf", "module", "list"), optional_flags={"module": ("{module}",)}),
}
),
"dnf_module_provides": CommandGroup(
commands={
"default": CommandSpec(args=("dnf", "module", "provides", "{package}")),
}
),
# === System Info ===
"system_info": CommandGroup(
commands={
Expand Down
26 changes: 26 additions & 0 deletions src/linux_mcp_server/tools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
# packages (dnf)
from linux_mcp_server.tools.dnf import dnf_module_provides
from linux_mcp_server.tools.dnf import dnf_provides
from linux_mcp_server.tools.dnf import get_dnf_group_info
from linux_mcp_server.tools.dnf import get_dnf_group_summary
from linux_mcp_server.tools.dnf import get_dnf_package_info
from linux_mcp_server.tools.dnf import get_dnf_repo_info
from linux_mcp_server.tools.dnf import list_dnf_available_packages
from linux_mcp_server.tools.dnf import list_dnf_groups
from linux_mcp_server.tools.dnf import list_dnf_installed_packages
from linux_mcp_server.tools.dnf import list_dnf_modules
from linux_mcp_server.tools.dnf import list_dnf_repositories

# logs
from linux_mcp_server.tools.logs import get_journal_logs
from linux_mcp_server.tools.logs import read_log_file

# network
from linux_mcp_server.tools.network import get_ip_route_table
from linux_mcp_server.tools.network import get_listening_ports
from linux_mcp_server.tools.network import get_network_connections
from linux_mcp_server.tools.network import get_network_interfaces
Expand Down Expand Up @@ -34,6 +48,7 @@
"get_cpu_information",
"get_disk_usage",
"get_hardware_information",
"get_ip_route_table",
"get_journal_logs",
"get_listening_ports",
"get_memory_information",
Expand All @@ -43,11 +58,22 @@
"get_service_logs",
"get_service_status",
"get_system_information",
"get_dnf_package_info",
"get_dnf_group_info",
"get_dnf_group_summary",
"get_dnf_repo_info",
"list_block_devices",
"list_directories",
"list_dnf_available_packages",
"list_dnf_installed_packages",
"list_dnf_groups",
"list_dnf_modules",
"list_dnf_repositories",
"list_files",
"list_processes",
"list_services",
"dnf_module_provides",
"dnf_provides",
"read_file",
"read_log_file",
]
Loading