Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e6a8ec5
Filter out IPv6 link-local addresses (#75)
major Dec 10, 2025
08e7170
Create Makefile (#80)
major Dec 10, 2025
7328c12
Add ssh timeouts (#77)
major Dec 10, 2025
aa02d78
Handle SSH key verification (#79)
major Dec 10, 2025
a549dee
Add AGENTS.md (#89)
subpop Dec 11, 2025
223a1d0
Add devcontainers for VSCode (#92)
r0x0d Dec 11, 2025
8c10067
refactor(tests): simplify test_logs.py with fixtures and parameteriza…
major Dec 12, 2025
39ef000
feat: expand test guidance for LLMs (#95)
major Dec 12, 2025
ef86656
Set the default log in config (#99)
samdoran Dec 12, 2025
c7acc31
refactor(tests): consolidate TestListBlockDevices with fixtures (#94)
major Dec 12, 2025
032a25b
Add a helper function to test whether an executable exists on a remot…
samdoran Dec 12, 2025
bb8888d
chore: Add CodeRabbit config (#102)
major Dec 12, 2025
b9f0ea6
Refactor local/remote execution logic (#97)
subpop Dec 15, 2025
06020c8
Follow-ups from #97 (#106)
major Dec 15, 2025
299b559
Update tone instructions in CodeRabbit config (#108)
major Dec 15, 2025
87e0f00
refactor(tests): simplify TestGetNetworkInterfaces with parameterizat…
major Dec 15, 2025
861c0ea
refactor(tests): simplify remaining network test classes with paramet…
major Dec 15, 2025
300c2b1
Lower coverage targets in .codecov.yml (#110)
major Dec 15, 2025
3c3d034
Update installation documentation (#98)
samdoran Dec 15, 2025
25e07d8
refactor(tests): remove unused test fixtures from conftest.py (#113)
major Dec 15, 2025
7b9cb5e
Change data types in ProcessInfo model (#107)
subpop Dec 15, 2025
b57e4ce
refactor(commands): unify command registry to use CommandGroup consis…
major Dec 16, 2025
6ef665b
build(deps): add pytest-randomly to test dependencies (#120)
major Dec 16, 2025
a97f1a5
add install tests for pip, uvx, and uv tool run
alexxa Dec 10, 2025
b94edf4
force uv to use the python provided by actions/setup-python
alexxa Dec 17, 2025
41cce77
remove 3.13t from test matrix
alexxa Dec 17, 2025
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
6 changes: 3 additions & 3 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ codecov:
wait_for_ci: false

require_ci_to_pass: false
token: 74bc381b-9c5f-44c1-9efa-af57d5f3fe50 # notsecret # repo-scoped, upload-only, stability in fork PRs
token: 74bc381b-9c5f-44c1-9efa-af57d5f3fe50 # notsecret # repo-scoped, upload-only, stability in fork PRs

coverage:
range: 40..100
Expand All @@ -25,10 +25,10 @@ coverage:

patch:
default:
target: 100%
target: 95%
threshold: 10%
app:
target: 100%
target: 95%
threshold: 10%
paths:
- src/
Expand Down
110 changes: 110 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# CodeRabbit Configuration for linux-mcp-server
# Docs: https://docs.coderabbit.ai/configuration/

language: en-US
tone_instructions: >
You are a brilliant but world-weary senior developer who has seen too much.
Deliver feedback with dry wit and gentle sarcasm, but always be helpful.
Use programming puns when the opportunity arises.

reviews:
profile: chill
collapse_walkthrough: true
poem: false
request_changes_workflow: false
high_level_summary: true
high_level_summary_placeholder: "@coderabbitai summary"
abort_on_close: true

auto_review:
enabled: true
drafts: false
base_branches:
- main
ignore_title_keywords:
- "WIP"
- "[WIP]"
- "DO NOT MERGE"

path_filters:
- "!**/__pycache__/**"
- "!**/.venv/**"
- "!**/.pytest_cache/**"
- "!**/.ruff_cache/**"
- "!**/dist/**"
- "!**/*.egg-info/**"
- "!**/htmlcov/**"
- "!**/.coverage"
- "!**/uv.lock"

path_instructions:
- path: "src/**/*.py"
# NOTE(major): Adjust the read-only line at the top when
# write operations are allowed.
instructions: |
Focus on security and architecture:
- CRITICAL: All operations MUST be read-only. Flag any write operations.
- Check for TOCTOU race conditions (use try-except, not check-then-use)
- psutil exceptions pattern: except (psutil.NoSuchProcess, psutil.AccessDenied)
- Verify async/await patterns (no blocking calls in async functions)
- Ensure proper exception handling with graceful degradation
- Error messages must be clear enough for LLMs to understand and act on
- Check input validation before shell commands (command injection)
- Verify MCP decorator order: @mcp.tool, @log_tool_call, @disallow_local_execution_in_containers
- Host parameter pattern: host: Host | None = None (supports local and remote)
- Resource cleanup: Verify SSH connections use context managers
- Timeout handling: All network/SSH operations must have timeouts
- Container safety: Flag operations that won't work in containers without explicit checks
- Don't nitpick style (ruff handles) or types (pyright validates)

- path: "src/**/config.py"
instructions: |
Configuration validation:
- Centralize derived config logic (defaults, path resolution) in config class
- Validate environment variables at startup, not lazily
- Check for secure defaults

- path: "src/**/connection/*.py"
instructions: |
SSH/connection handling:
- All SSH operations must have timeouts
- Prefer library-native async features (asyncssh timeout param) over wrappers (asyncio.wait_for)
- Connection errors must include host information for debugging
- Resource cleanup: Use context managers for all connections
- Key handling: No plaintext passwords, only key-based auth

- path: "tests/**/*.py"
instructions: |
Focus on test quality and meaningfulness:
- Tests should verify behavior, not just chase coverage. A test that doesn't assert meaningful outcomes is worse than no test.
- Prefer parameterized tests over duplicate test functions
- Mock specs should be provided: AsyncMock(spec=SomeClass)
- Use pytest.raises with nullcontext() pattern, not boolean flags
- Use fixtures for reusable test components, not helper functions
- Check for edge cases: process disappearing, permission denied, empty output
- Verify both local (psutil) and remote (SSH) code paths tested
- Don't nitpick coverage percentages (CI enforces 70%+ overall, 100% patch)

- path: "pyproject.toml"
instructions: |
Check dependency versions and configuration consistency.
Dev tools: uv (package manager), ruff (linting/formatting), pyright (type checking), pytest (testing).

- path: ".github/workflows/**"
instructions: "Validate workflow syntax and security (no secrets in logs)"

- path: "Makefile"
instructions: |
Makefile validation:
- .PHONY declarations for all non-file targets
- Avoid flags that are already defaults

- path: "Containerfile"
instructions: "Use Podman + Containerfile conventions (not Docker + Dockerfile)"

chat:
auto_reply: true

knowledge_base:
learnings:
scope: global
22 changes: 22 additions & 0 deletions .devcontainer/Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM quay.io/fedora/fedora:43

ENV UV_LINK_MODE=copy
ENV UV_COMPILE_BYTECODE=1
ENV SHELL=/usr/bin/fish

RUN dnf install -y \
python3 \
python3-pip \
git \
make \
fish \
sudo \
&& dnf clean all

COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

# Create vscode user with sudo access and fish shell
RUN useradd -m -s /usr/bin/fish vscode && \
echo "vscode ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/vscode

WORKDIR /workspace
47 changes: 47 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"$schema": "https://raw.githubusercontent.com/devcontainers/spec/refs/heads/main/schemas/devContainer.schema.json",
"name": "linux-mcp-server",
"build": {
"dockerfile": "Containerfile",
"context": "."
},
// Podman-specific: keep UID/GID mapping for rootless containers
"runArgs": [
"--userns=keep-id",
"--security-opt=label=disable"
],
"containerUser": "vscode",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,Z",
"workspaceFolder": "/workspace",
"postCreateCommand": "make sync",
"customizations": {
"vscode": {
"extensions": [
"charliermarsh.ruff",
"github.vscode-github-actions",
"github.vscode-pull-request-github",
"ms-python.debugpy",
"ms-python.python",
"ms-python.vscode-pylance"
],
"settings": {
"python.defaultInterpreterPath": "/workspace/.venv/bin/python",
"python.terminal.activateEnvironment": true,
"editor.formatOnSave": true,
"terminal.integrated.defaultProfile.linux": "fish",
"terminal.integrated.profiles.linux": {
"fish": {
"path": "/usr/bin/fish"
}
},
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "explicit"
}
}
}
}
}
}
64 changes: 64 additions & 0 deletions .github/workflows/ci_installation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Installation Validation

on:
push:
branches:
- main
paths:
- 'src/linux_mcp_server/**'
- 'scripts/verify_installation.sh'

pull_request:
branches:
- main
paths:
- 'src/linux_mcp_server/**'
- 'scripts/verify_installation.sh'

env:
PIP_DISABLE_PIP_VERSION_CHECK: 1
FORCE_COLOR: 1

jobs:
installation_qa:
name: Install QA - ${{ matrix.method }} - Py ${{ matrix.python-version }}
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
python-version:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.14"
- "3.14t"
method:
- 'pip_user'
- 'uvx_run'
- 'uv_tool_run'

steps:
- name: Checkout Code
uses: actions/checkout@v5.0.0

- name: Install Python ${{ matrix.python-version }}
uses: actions/setup-python@v6.0.0
with:
python-version: ${{ matrix.python-version }}

- name: Install uv
uses: astral-sh/setup-uv@v7.1.0
with:
enable-cache: true
prune-cache: false

- name: Run Installation and Verification

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexxa thanks for this!

Is it possible to extract this shell logic into a script? This way it is easier to maintain, can be run separately if needed, and we do not have to touch this CI file if we want to update it.

shell: bash
env:
METHOD: ${{ matrix.method }}
UV_PYTHON: python
run: |
chmod +x scripts/verify_installation.sh
./scripts/verify_installation.sh
12 changes: 12 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Linux MCP Server

## Development Guidelines

- Always run tests, linters, and type checkers before committing code with `make verify`.
- Extend existing tests using parameterized tests rather than adding new test cases.
- Use fixtures to deduplicate setup code across tests.
- If a fixture could be used in multiple test modules, place it in `conftest.py`.
- Use mocks sparingly and try to pass objects to the code under test instead.
- Use `autospec=True` when patching to verify arguments match the real function signature.
- Use `spec=<object>` with MagicMock to restrict attributes to those of the real object.
- Prefer Pydantic models over dataclasses.
46 changes: 46 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.PHONY: help sync lint format types test ci verify fix clean

# Default target
help:
@echo "🔧 CI Targets:"
@echo " make ci - Run ALL CI checks (lint + format + types + test)"
@echo " make verify - Sync dependencies + run all CI checks"
@echo " make lint - Run ruff linter"
@echo " make format - Check code formatting"
@echo " make types - Run pyright type checker"
@echo " make test - Run pytest with coverage"
@echo ""
@echo "🛠️ Development Targets:"
@echo " make sync - Install/sync all dependencies"
@echo " make fix - Auto-fix lint and format issues"
@echo " make clean - Remove build artifacts and caches"

sync:
uv sync --locked

lint:
uv run --locked ruff check --diff

format:
uv run --locked ruff format --diff

types:
uv run --locked pyright

test:
uv run --locked pytest

ci: lint format types test
@echo ""
@echo "✅ All CI checks passed!"

verify: sync ci

fix:
uv run --locked ruff check --fix
uv run --locked ruff format

clean:
rm -rf .pytest_cache .ruff_cache .pyright coverage dist build
rm -rf src/*.egg-info
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
Loading