Agentic Server Primer: Llama.cpp MCP Lesson 3: Adding Python Tooling Capability To your HouseLLM.
Agentic Server Primer: Llama.cpp MCP Lesson 3: Python

Environment Evolution is Powerful
- Not only will your LLM use this tool - it can self-adapt and build the environment to suite any type of python project you may work with. It literally will both update the system inside the docker image AND
pip installwhatever it needs before it runs the python execution tests.
In the previous two lessons we showed how to make a MCP calculator for your LLM, and then how to Dockerize it so that it ran in it's own container. Dockerization now will become very important because you are literally going to let your LLM execute commands.
This image can be pulled and ran easily now:
docker pull cnmcdee/mcp-python:latest
docker run -d --name mcp-python --restart unless-stopped -e "FLASH_ENV=production" -p 0.0.0.0:5015:5015 cnmcdee/mcp-python:latest- Now we will build a python docker container so it can run it's own unit tests on the code that it writes!
If you need a primer on docker / python etc consider these two guides that explain it in detail:


Let's get started!
Make a file named l_super_python.py and put inside it:
import io
import subprocess
import traceback
import sys
import os
import re
import time
from pathlib import Path
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware
import uvicorn
from fastmcp import FastMCP
# ── Configuration ────────────────────────────────────────────────────────
VENV_DIR = os.path.join(os.path.dirname(__file__), ".venv")
PYTHON_EXECUTABLE = os.path.join(VENV_DIR, "bin", "python3") if sys.platform != "win32" else os.path.join(VENV_DIR, "Scripts", "python.exe")
SUPPORT_REQUIREMENTS = os.path.join(os.path.dirname(__file__), "support_requirements.txt")
# ── Markdown Cleaning ────────────────────────────────────────────────────
def clean_markdown_code(code: str) -> str:
"""Remove markdown formatting while preserving Python indentation."""
if not code or not isinstance(code, str):
return ""
cleaned = re.sub(
r'```[\w]*\s*\n(.*?)\n```',
r'\1',
code,
flags=re.DOTALL | re.IGNORECASE
)
cleaned = re.sub(
r'```[\w]*\s*\n(.*?)(?:\n```|\Z)',
r'\1',
cleaned,
flags=re.DOTALL | re.IGNORECASE
)
cleaned = re.sub(r'`([^`]+)`', r'\1', cleaned)
lines = cleaned.split('\n')
while lines and not lines[0].strip():
lines.pop(0)
while lines and not lines[-1].strip():
lines.pop()
return '\n'.join(lines).strip('\n')
# ── VENV & Support Library Management ───────────────────────────────────
def create_venv():
"""Create virtual environment and ensure pip is installed."""
if os.path.exists(VENV_DIR):
# Ensure pip exists even if venv was previously broken
if not _pip_available():
print("Pip missing in existing venv. Repairing...")
_ensure_pip()
return
print(f"Creating venv at {VENV_DIR}...")
subprocess.run([sys.executable, "-m", "venv", VENV_DIR], check=True, capture_output=True)
_ensure_pip()
def _pip_available() -> bool:
"""Check if pip is available in the venv."""
try:
python = get_venv_python()
result = subprocess.run([python, "-m", "pip", "--version"],
capture_output=True, text=True, timeout=10)
return result.returncode == 0
except Exception:
return False
def _ensure_pip():
"""Bootstrap or upgrade pip in the venv."""
python = get_venv_python()
print("Ensuring pip is available...")
# Method 1: ensurepip
try:
subprocess.run([python, "-m", "ensurepip", "--upgrade"], check=True, capture_output=True)
print("Pip installed via ensurepip.")
return
except Exception:
pass
# Method 2: Get pip from official bootstrap
try:
subprocess.run([python, "-m", "pip", "install", "--upgrade", "pip"], check=True, capture_output=True)
print("Pip upgraded successfully.")
except Exception as e:
print(f"Warning: Could not fully bootstrap pip: {e}")
def get_venv_python() -> str:
"""Get path to venv Python interpreter."""
if os.path.exists(PYTHON_EXECUTABLE):
return PYTHON_EXECUTABLE
alt = Path(VENV_DIR) / "Scripts" / "python.exe"
if alt.exists():
return str(alt)
raise FileNotFoundError(f"Venv Python not found at {PYTHON_EXECUTABLE}")
def install_dependency(package: str) -> bool:
"""Install package in venv and update support library."""
try:
create_venv()
python = get_venv_python()
result = subprocess.run(
[python, "-m", "pip", "install", package, "--quiet"],
check=True,
capture_output=True,
text=True
)
print(f"Successfully installed {package}")
with open(SUPPORT_REQUIREMENTS, "a", encoding="utf-8") as f:
f.write(f"{package}\n")
return True
except subprocess.CalledProcessError as e:
print(f"Failed to install {package}: {e.stderr.strip() or e.stdout.strip()}")
return False
except Exception as e:
print(f"Error installing {package}: {e}")
return False
def get_installed_packages() -> set:
"""Get installed packages in venv."""
try:
python = get_venv_python()
result = subprocess.run(
[python, "-m", "pip", "list", "--format=freeze"],
capture_output=True, text=True, check=True, timeout=15
)
packages = set()
for line in result.stdout.strip().split('\n'):
if line and not line.startswith('#'):
pkg = line.split('==')[0].strip().lower()
if pkg:
packages.add(pkg)
return packages
except Exception as e:
print(f"Warning: Could not list packages: {e}")
return set()
def parse_python_imports(code: str) -> list[str]:
"""Extract top-level module names from import statements."""
imports = set()
patterns = [
r'^\s*import\s+([^\s,]+)',
r'^\s*import\s+([^\s,]+)\s+as',
r'^\s*from\s+([^\s.]+)',
]
for pattern in patterns:
for match in re.finditer(pattern, code, re.MULTILINE):
module = match.group(1).split('.')[0].strip()
if module and module.isidentifier():
imports.add(module)
return list(imports)
def ensure_dependencies(code: str):
"""Initial dependency installation from static imports."""
create_venv()
needed = set(parse_python_imports(code))
stdlib_skip = {'os', 'sys', 'io', 're', 'pathlib', 'json', 'math', 'subprocess',
'traceback', 'time', 'datetime', 'typing', 'collections', 'itertools',
'builtins', 'contextlib', 'PIL', 'pytesseract'} # common false positives
needed -= stdlib_skip
if not needed:
return
print(f"Pre-installing packages: {needed}")
installed = get_installed_packages()
for pkg in (needed - installed):
install_dependency(pkg)
def extract_missing_module(error_msg: str) -> str | None:
"""Extract missing module name from ImportError / ModuleNotFoundError."""
patterns = [
r"No module named ['\"]([^'\"]+)['\"]",
r"ModuleNotFoundError: No module named ['\"]([^'\"]+)['\"]",
]
for pattern in patterns:
match = re.search(pattern, error_msg, re.IGNORECASE)
if match:
module = match.group(1).split('.')[0].strip()
if module and module.isidentifier():
return module
return None
# ── Enhanced Python Code Execution ───────────────────────────────────────
mcp = FastMCP("Python Executor Enhanced")
@mcp.tool()
def install_python_package(package: str) -> str:
"""Install a Python package into the project's virtual environment.
Use this whenever a ModuleNotFoundError occurs or before running code that needs new libraries."""
try:
create_venv() # ensures venv exists and pip is up-to-date
python = get_venv_python()
# Upgrade pip first (fast)
subprocess.run(
[python, "-m", "pip", "install", "--upgrade", "--no-cache-dir", "pip"],
check=True, capture_output=True, timeout=30
)
# Install the package
result = subprocess.run(
[python, "-m", "pip", "install", "--no-cache-dir", package],
check=True, capture_output=True, text=True, timeout=60
)
# Update support_requirements.txt
with open(SUPPORT_REQUIREMENTS, "a", encoding="utf-8") as f:
f.write(f"{package}\n")
return f"Successfully installed {package} into venv."
except subprocess.CalledProcessError as e:
return f"Failed to install {package}: {e.stderr.strip() or e.stdout.strip()}"
except Exception as e:
return f"Error installing {package}: {str(e)}"
@mcp.tool()
def get_environment_info() -> str:
"""Returns current Python environment details."""
return f"Venv Python: {get_venv_python()}\nSupport requirements: {SUPPORT_REQUIREMENTS}"
@mcp.tool
def execute_python_enhanced(code: str) -> dict:
"""Executes Python code with automatic dependency handling using the project venv."""
start_time = time.time()
result = {
"success": False,
"stdout": "",
"stderr": "",
"exit_code": 1,
"warnings": [],
"execution_time_ms": 0,
"execution_result": None,
"install_attempts": 0
}
if not code or not isinstance(code, str):
result["stderr"] = "No code provided"
return result
# Clean the code first
cleaned_code = clean_markdown_code(code)
if cleaned_code != code:
result["warnings"].append("Markdown formatting removed")
try:
ensure_dependencies(cleaned_code) # Pre-install from static imports
python_path = get_venv_python()
max_attempts = 4 # Reduced for efficiency
for attempt in range(1, max_attempts + 1):
result["install_attempts"] = attempt
temp_file = Path("/tmp") / f"mcp_exec_{int(time.time() * 1000)}.py"
temp_file.write_text(cleaned_code, encoding="utf-8")
proc = subprocess.run(
[python_path, str(temp_file)],
capture_output=True,
text=True,
timeout=45
)
temp_file.unlink(missing_ok=True)
if proc.returncode == 0:
result.update({
"stdout": proc.stdout.strip(),
"stderr": proc.stderr.strip(),
"success": True,
"exit_code": 0,
})
break
last_error = (proc.stderr or proc.stdout).strip()
missing = extract_missing_module(last_error)
if missing:
print(f"Attempt {attempt}: Missing '{missing}'. Installing...")
install_result = install_python_package(missing)
result["warnings"].append(install_result)
time.sleep(0.5)
continue
else:
result["stderr"] = last_error
break
else:
result["stderr"] = f"Max attempts reached. Last error: {last_error}"
except Exception as e:
tb = traceback.format_exc()
result["stderr"] = f"{type(e).__name__}: {str(e)}\n{tb}"
result["execution_time_ms"] = int((time.time() - start_time) * 1000)
return result
@mcp.tool()
def install_python_package(package: str) -> str:
"""Install a Python package into the project's virtual environment.
Use this whenever you encounter a ModuleNotFoundError."""
try:
create_venv() # Ensures venv + latest pip
python = get_venv_python()
# Upgrade pip first
subprocess.run(
[python, "-m", "pip", "install", "--upgrade", "--no-cache-dir", "pip"],
check=True, capture_output=True, timeout=30
)
# Install requested package
result = subprocess.run(
[python, "-m", "pip", "install", "--no-cache-dir", package],
check=True, capture_output=True, text=True, timeout=90
)
# Log to support file
with open(SUPPORT_REQUIREMENTS, "a", encoding="utf-8") as f:
f.write(f"{package}\n")
return f"Successfully installed {package} into venv."
except subprocess.CalledProcessError as e:
return f"Failed to install {package}: {e.stderr.strip() or e.stdout.strip()}"
except Exception as e:
return f"Error installing {package}: {str(e)}"
# ...
@mcp.tool()
def install_system_dependencies(packages: list[str]) -> str:
"""Install system packages via apt (safe, non-interactive)."""
import subprocess
try:
cmd = [
"apt-get", "update",
"&&", "apt-get", "install", "-y", "--no-install-recommends"
] + packages + [
"&&", "rm", "-rf", "/var/lib/apt/lists/*"
]
result = subprocess.run(" ".join(cmd), shell=True, capture_output=True, text=True)
return f"Success: {result.stdout}" if result.returncode == 0 else f"Error: {result.stderr}"
except Exception as e:
return f"Failed: {str(e)}"
@mcp.tool
def execute_python_enhanced(code: str) -> dict:
"""Executes Python code with automatic recursive dependency installation."""
start_time = time.time()
result = {
"success": False,
"stdout": "",
"stderr": "",
"exit_code": 1,
"warnings": [],
"execution_time_ms": 0,
"execution_result": None,
"install_attempts": 0
}
if not code or not isinstance(code, str):
result["stderr"] = "No code provided"
return result
cleaned_code = clean_markdown_code(code)
if cleaned_code != code:
result["warnings"].append("Markdown formatting removed")
try:
ensure_dependencies(cleaned_code)
python_path = get_venv_python()
max_attempts = 8
attempt = 0
last_error = ""
while attempt < max_attempts:
attempt += 1
result["install_attempts"] = attempt
temp_file = Path("/tmp") / f"mcp_exec_{int(time.time() * 1000)}.py"
temp_file.write_text(cleaned_code, encoding="utf-8")
proc = subprocess.run(
[python_path, str(temp_file)],
capture_output=True,
text=True,
timeout=45
)
temp_file.unlink(missing_ok=True)
if proc.returncode == 0:
result.update({
"stdout": proc.stdout.strip(),
"stderr": proc.stderr.strip(),
"success": True,
"exit_code": 0,
})
break
last_error = (proc.stderr or proc.stdout).strip()
missing = extract_missing_module(last_error)
if missing:
print(f"Attempt {attempt}: Missing '{missing}'. Installing...")
result["warnings"].append(f"Installed missing dependency: {missing}")
if not install_dependency(missing):
result["stderr"] = f"Failed to install {missing}. {last_error}"
break
time.sleep(0.8)
continue
else:
result["stderr"] = last_error
break
else:
result["stderr"] = f"Max attempts reached. Last error: {last_error}"
except Exception as e:
tb = traceback.format_exc()
result["stderr"] = f"{type(e).__name__}: {str(e)}\n{tb}"
result["execution_time_ms"] = int((time.time() - start_time) * 1000)
return result
# ── Server Startup ───────────────────────────────────────────────────────
if __name__ == "__main__":
middleware = [
Middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["GET", "POST", "OPTIONS"],
allow_headers=["*"],
expose_headers=["*"],
)
]
app = mcp.http_app(path="/mcp", middleware=middleware)
uvicorn.run(app, host="0.0.0.0", port=5015, log_level="info")At this point you can test it temporarily just be careful not to use anything delete related - it is executing commands in your system. So run the program, it will look something like this:
- We have put this docker at port 5015..
When you specify the end point on the server it must have the /mcp url ending as:
http://192.168.1.3:5015/mcpIf you Llama.cpp detects and integrates it correctly it will look something like this:

If you notice and click on the updated 4 tools available - it will be derived from the comments, and it runs inside an isolated namespace, next we test it:

install_python_package- pretty straightforward - but it's actually more deep than that. A lot of packages will require an update to the system itself.get_environment_info- is a powerful standard method to see what is going on in the currentvenvexecute_python_enhanced- will allow it to still write and compile it's own code.install_system_dependencies- This is very import as some larger packages such asnumpymay require driver support, library support or what have you.

- Your LLM can now add its own tools, and even update the docker container to support whatever tools it needs.
Dockerization
- Pretty much everything will need to be dockerized. So stop your
l_super_python.pyand put it in it's own directory. - Add a file named Dockerfile and put into it:
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
DEBIAN_FRONTEND=noninteractive
WORKDIR /app
# System dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
python3-dev \
libx11-dev \
libxtst-dev \
libxkbcommon-dev \
xclip \
git && \
rm -rf /var/lib/apt/lists/*
# Create and activate virtual environment
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Copy and install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
COPY . .
RUN chmod +x *.py 2>/dev/null || true
EXPOSE 5015
CMD ["python", "l_super_python.py"]
- Next make a requirements.txt and put into that:
pip
fastmcp>=3.0.0
pynput==1.7.6
requests==2.32.3
# Web framework (updated for MCP compatibility)
fastapi>=0.115.0
uvicorn>=0.30.0
httpx>=0.27.0
# MCP (Model Context Protocol)
mcp>=1.0.0
# LangChain (updated to reduce conflicts)
langchain-core>=0.2.0
langchain>=0.2.0
# Data processing
pydantic>=2.8.0
pyyaml>=6.0.1
python-dotenv>=1.0.0
# Async
asyncio-mqtt==0.16.1
websockets>=12.0
# Dev tools (optional, can be in dev requirements)
# pytest==7.4.3
# etc.
Now we will build the docker image, and it will look as the following if it works:

Once that is done we can build a image for our docker ecosystem
docker build -t mcp-python:latest .And it can be run with:
docker run -d --name mcp-python --restart unless-stopped -e "FLASH_ENV=production" -p 0.0.0.0:5015:5015 mcp-python:latestConclusion
We have added agentic tooling now for python! This is powerful, not only can it write it's own code - it can safely test it. The python tool is in it's own container, that keeps it safe in case the LLM hallucinates or does whatever..
Save your Context and Come Back
This process manager is very powerful in that your LLM can now save it's work and spread it across many contexts.

Get your LLM coding all Night! LLMQP
This LLM will enable you to queue multiple prompts which will execute one after another.


