Agentic Server Primer: Llama.cpp MCP Lesson 10: mcp-coder (Cuda Version)

We build a MCP Coding Agent that will allow your LLM to specifically work on and debug it's own code with nvcc, or really any language!

Agentic Server Primer: Llama.cpp MCP Lesson 10: mcp-coder (Cuda Version)
We give our houseLLM an Agentic Coding Function

This is a specialized MCP agent in that it is designed for your LLM to work with, pull, compile and develop it's own software - specifically for Nvidia nvcc Cuda!   It is effectively its entire own build agent! However the potential is utterly amazing, it can effectively have your LLM writing advanced GPU code (aka configuring it's own Llama.cpp!)

MTP / TurboQuant Forked Llama.cpp
We hot compile one of the first combo MTP / TurboQuant forks in the world!
  • In the above link we looked at one of the worlds first MTP/TurboQuant forks of Llama.cpp but stopped because it was not specifically for a Qwen3.6 which because of it's advanced nature we really wanted to keep.
  • Our goal is to see if a HouseLLM can compile a MTP (Multiple-Token-Prediction) cross-blend of TurboQuant forked Llama.cpp and get it to work with Qwen3.6!

If you want to simply pull and run this mcp-coder:

docker pull docker.io/cnmcdee/mcp-coder
docker run -d \
  --name mcp-coder \
  --restart unless-stopped \
  -p 5011:5011 \
  -v "$(pwd)/target-project:/app/project" \
  -e PYTHONUNBUFFERED=1 \
  --user 1000:1000 \
  --gpus all \
  mcp-coder

We used  python as a pass-through, with it's own CORS http point.  There are a couple paths one can take when you are making these, namely:

  • You give  it a number of guided mcp command end-points representing system operations like 'git push' , 'git pull'  - but then you need to do it for all of them, or:
  • Or you simply give it the ability to read, write, open files, and execute system commands knowing it has the agentic ability to conceptually understand what it is doing.  We choose this path because these LLM's are simply that good.

Source Code

from fastmcp import FastMCP
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware
import uvicorn
import subprocess
import os
from pathlib import Path
import re
import json

# =============================================================================
# Enhanced MCP Server - Restricted to Specific Target Directory
# =============================================================================

mcp = FastMCP(name="Target Directory MCP Server")

# ── Configuration ────────────────────────────────────────────────────────────

# === CHANGE THIS TO YOUR DESIRED TARGET DIRECTORY ===
BASE_DIRECTORY = "/work_project"   # ←←← UPDATE THIS PATH

# Security limits
MAX_FILE_SIZE = 10 * 1024 * 1024  # 10MB limit
MAX_OUTPUT_LENGTH = 50000         # 50KB output limit

# All operations are forced inside BASE_DIRECTORY
ALLOWED_DIRECTORIES = [BASE_DIRECTORY]


# ── Helper Functions ─────────────────────────────────────────────────────────

def validate_path(file_path):
    """
    Force all paths to be inside the BASE_DIRECTORY.
    """
    # Convert to absolute path relative to BASE_DIRECTORY if relative
    if not os.path.isabs(file_path):
        file_path = os.path.join(BASE_DIRECTORY, file_path)
    
    path = Path(file_path).resolve()

    # Enforce BASE_DIRECTORY restriction
    base_path = Path(BASE_DIRECTORY).resolve()
    if not str(path).startswith(str(base_path)):
        raise ValueError(f"Path must be inside the target directory: {BASE_DIRECTORY}")

    # Prevent path traversal
    if '..' in path.parts:
        raise ValueError("Path traversal detected")

    return path


def truncate_output(output, max_length=MAX_OUTPUT_LENGTH):
    """Truncate output if it exceeds the maximum length."""
    if len(output) <= max_length:
        return output

    truncated = output[:max_length]
    last_newline = truncated.rfind('\n')
    if last_newline > max_length * 0.8:
        truncated = truncated[:last_newline + 1]

    return truncated + f"\n\n[Output truncated to {max_length} characters]"


# ── File System Tools ────────────────────────────────────────────────────────

@mcp.tool
def read_file(file_path):
    """Read the entire content of a file (restricted to target directory)."""
    try:
        path = validate_path(file_path)

        if not path.is_file():
            raise ValueError(f"File not found: {file_path}")

        file_size = path.stat().st_size
        if file_size > MAX_FILE_SIZE:
            raise ValueError(f"File too large: {file_size} bytes (max: {MAX_FILE_SIZE})")

        return path.read_text(encoding="utf-8")

    except ValueError as e:
        return f"Error: {str(e)}"
    except Exception as e:
        return f"Error reading file: {str(e)}"


@mcp.tool
def write_file(file_path, content, mode="w"):
    """Write content to a file (restricted to target directory)."""
    if mode not in ["w", "a"]:
        return "Error: Invalid mode. Use 'w' for write or 'a' for append"

    try:
        path = validate_path(file_path)
        path.parent.mkdir(parents=True, exist_ok=True)

        with open(path, mode, encoding="utf-8") as f:
            f.write(content)

        return f"Successfully wrote to {file_path} (mode: {mode})"

    except ValueError as e:
        return f"Error: {str(e)}"
    except Exception as e:
        return f"Error writing file: {str(e)}"


@mcp.tool
def delete_file(file_path):
    """Delete a file (restricted to target directory)."""
    try:
        path = validate_path(file_path)

        if not path.is_file():
            return f"Error: File not found: {file_path}"

        path.unlink()
        return f"Successfully deleted {file_path}"

    except ValueError as e:
        return f"Error: {str(e)}"
    except Exception as e:
        return f"Error deleting file: {str(e)}"


@mcp.tool
def replace_line(file_path, line_number, new_content):
    """Replace a specific line in a file by line number (restricted to target directory)."""
    try:
        path = validate_path(file_path)

        if not path.is_file():
            return f"Error: File not found: {file_path}"

        lines = path.read_text(encoding="utf-8").splitlines(keepends=True)

        if line_number < 1 or line_number > len(lines):
            return f"Error: Line number {line_number} is out of range. File has {len(lines)} lines."

        original_ending = lines[line_number - 1][-1:] if lines[line_number - 1] else '\n'
        lines[line_number - 1] = new_content.rstrip() + original_ending

        path.write_text(''.join(lines), encoding="utf-8")

        return f"Successfully replaced line {line_number} in {file_path}"

    except ValueError as e:
        return f"Error: {str(e)}"
    except Exception as e:
        return f"Error replacing line: {str(e)}"


@mcp.tool
def list_directory(directory="."):
    """List files and directories (restricted to target directory)."""
    try:
        path = validate_path(directory)

        if not path.is_dir():
            return ["E/Not a directory"]

        items = []
        for item in sorted(path.iterdir()):
            prefix = "D/" if item.is_dir() else "F/"
            items.append(f"{prefix}{item.name}")

        return items

    except ValueError as e:
        return [f"E/{str(e)}"]
    except Exception as e:
        return [f"E/Error listing directory: {str(e)}"]


@mcp.tool
def get_file_info(file_path):
    """Get metadata about a file (restricted to target directory)."""
    try:
        path = validate_path(file_path)

        if not path.exists():
            return {"error": f"File not found: {file_path}"}

        stat = path.stat()
        return {
            "path": str(path),
            "exists": True,
            "is_file": path.is_file(),
            "is_dir": path.is_dir(),
            "size": stat.st_size,
            "modified": stat.st_mtime,
            "permissions": oct(stat.st_mode)[-3:],
            "name": path.name
        }

    except ValueError as e:
        return {"error": str(e)}
    except Exception as e:
        return {"error": f"Error getting file info: {str(e)}"}


@mcp.tool
def search_files(directory=".", pattern="*"):
    """Search for files matching a pattern (restricted to target directory)."""
    try:
        path = validate_path(directory)

        if not path.is_dir():
            return [f"Error: Not a directory: {directory}"]

        matches = []
        for item in path.rglob(pattern):
            if item.is_file():
                matches.append(str(item))

        return sorted(matches)

    except ValueError as e:
        return [f"Error: {str(e)}"]
    except Exception as e:
        return [f"Error searching files: {str(e)}"]


# ── Raw Console / Shell Tool ─────────────────────────────────────────────────

@mcp.tool
def run_command(command, cwd=None, timeout=180, shell=True):
    """Execute a shell command. Working directory is forced inside target directory."""
    try:
        # Force cwd to be inside BASE_DIRECTORY
        if cwd is None:
            cwd = BASE_DIRECTORY
        else:
            cwd_path = validate_path(cwd)
            cwd = str(cwd_path)

        # Basic dangerous command protection
        dangerous_patterns = ["&&", ";", "|", ">", "<", ">>", ">>>", "sudo", "su ", "chmod 777", "rm -rf /", "mkfs"]
        for pattern in dangerous_patterns:
            if pattern in command:
                return f"Error: Potentially dangerous command detected: '{pattern}'"

        result = subprocess.run(
            command,
            shell=shell,
            cwd=cwd,
            capture_output=True,
            text=True,
            timeout=timeout
        )

        stdout = truncate_output(result.stdout)
        stderr = truncate_output(result.stderr)

        response = [f"Command: {command} (cwd: {cwd})"]
        if stdout:
            response.append(f"STDOUT:\n{stdout}")
        if stderr:
            response.append(f"STDERR:\n{stderr}")
        response.append(f"Return code: {result.returncode}")

        return "\n\n".join(response)

    except subprocess.TimeoutExpired:
        return f"Command timed out after {timeout} seconds."
    except ValueError as e:
        return f"Error: {str(e)}"
    except Exception as e:
        return f"Error executing command: {str(e)}"


# ── Server Setup ─────────────────────────────────────────────────────────────

if __name__ == "__main__":
    middleware = [
        Middleware(
            CORSMiddleware,
            allow_origins=["*"],
            allow_credentials=False,
            allow_methods=["GET", "POST", "OPTIONS"],
            allow_headers=["*"],
            expose_headers=["*"],
            max_age=3600,
        )
    ]

    app = mcp.http_app(
        path="/mcp",
        middleware=middleware
    )

    print("🚀 Starting Target Directory Restricted MCP Server")
    print(f"→ All operations restricted to: {BASE_DIRECTORY}")
    print("→ Tools: read_file, write_file, delete_file, replace_line, list_directory,")
    print("         get_file_info, search_files, run_command")
    
    uvicorn.run(app, host="0.0.0.0", port=5011, log_level="info")

Here are the complete files you need to run the MCP Server in Docker:

1. requirements.txt

fastmcp
uvicorn[standard]
starlette

2. Dockerfile

FROM nvidia/cuda:13.1.2-devel-ubuntu22.04

WORKDIR /app

# Environment settings
ENV DEBIAN_FRONTEND=noninteractive \
    TZ=UTC

# ── 1. Initial apt update ─────────────────────────────────────
RUN apt-get update

# ── Individual Package Installations ───────────────────────────

RUN apt-get install -y --no-install-recommends software-properties-common
RUN apt-get install -y --no-install-recommends sudo
RUN apt-get install -y --no-install-recommends git
RUN apt-get install -y --no-install-recommends build-essential
RUN apt-get install -y --no-install-recommends cmake
RUN apt-get install -y --no-install-recommends ninja-build
RUN apt-get install -y --no-install-recommends curl
RUN apt-get install -y --no-install-recommends wget
RUN apt-get install -y --no-install-recommends ca-certificates

# ── Add deadsnakes PPA for Python 3.11 ────────────────────────
RUN add-apt-repository ppa:deadsnakes/ppa -y

# ── Update after adding PPA ───────────────────────────────────
RUN apt-get update

# ── Python 3.11 packages (one per RUN) ────────────────────────
RUN apt-get install -y --no-install-recommends python3.11
RUN apt-get install -y --no-install-recommends python3.11-venv
RUN apt-get install -y --no-install-recommends python3.11-dev
RUN apt-get install -y --no-install-recommends python3-pip
RUN apt-get install -y --no-install-recommends tzdata

# ── Timezone configuration ────────────────────────────────────
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
    echo $TZ > /etc/timezone && \
    dpkg-reconfigure --frontend noninteractive tzdata

# ── Clean apt cache ───────────────────────────────────────────
RUN rm -rf /var/lib/apt/lists/*

# ── Set Python 3.11 as default ────────────────────────────────
RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.11 1
RUN update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1

# ── Python dependencies ───────────────────────────────────────
COPY requirements.txt .
RUN python -m pip install --no-cache-dir -r requirements.txt

# ── Copy server code ──────────────────────────────────────────
COPY server.py .

# ── Create privileged user ────────────────────────────────────
RUN useradd -m -u 1000 -s /bin/bash mcpuser
RUN echo "mcpuser ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

# ── Permissions ───────────────────────────────────────────────
RUN chown -R mcpuser:mcpuser /app

USER mcpuser

EXPOSE 5011

CMD ["python", "server.py"]

3. docker-compose.yml

version: '3.9'

services:
  mcp-server:
    build: .
    container_name: mcp-server
    restart: unless-stopped
    ports:
      - "5011:5011"
    volumes:
      # Mount your target project directory into the container
      - ./target-project:/app/project
    environment:
      - PYTHONUNBUFFERED=1
    # Optional: Add GPU support if you need CUDA/nvcc inside the container
    # deploy:
    #   resources:
    #     reservations:
    #       devices:
    #         - driver: nvidia
    #           count: all
    #           capabilities: [gpu]

How to Use

Save the files:

  • requirements.txt
  • Dockerfile
  • docker-compose.yml

Rename your server code to server.py (or update the Dockerfile accordingly).

Update the target directory in server.py:

BASE_DIRECTORY = "/app/project"   # This matches the volume mount

Create the project folder (next to docker-compose.yml):

mkdir target-project

Start the server:

docker-compose up --build

Or in detached mode:

docker-compose up -d --build

Once its Working the Fun Really Starts!

Linux Rocks Every Day