debugpy_context
Attach debugpy to Python processes in Docker containers for debugging and inspection. Enables process injection and breakpoint planning based on logs.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| container | Yes | ||
| port | No | ||
| python_bin | No | python |
Output Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/debugpy_mcp/server.py:503-520 (handler)The main handler function for the 'debugpy_context' tool. It gathers comprehensive context about a container including process information, debugpy status, Python version, working directory, port mappings, and environment variables. The function is decorated with @mcp.tool() which registers it as an MCP tool.
@mcp.tool() def debugpy_context(container: str, port: int = DEFAULT_PORT, python_bin: str = "python") -> dict[str, Any]: if not docker_inspect_running(container): return DebugContextResult(ok=False, container=container, notes=["Container is not running or does not exist."]).model_dump() installed, version = detect_debugpy_installed(container, python_bin=python_bin) processes = get_process_table(container) suggested_pid, pid_notes = choose_pid(processes) working_dir = get_working_dir(container, suggested_pid) if suggested_pid else None listening = port_is_listening(container, port) mapped_port = docker_port_mapping(container, port) py_ver = get_python_version(container, python_bin=python_bin) ports_snapshot = capture_ports_snapshot(container) env_subset = get_env_subset(container) notes = list(pid_notes) if not installed: notes.append("debugpy is not currently installed inside the container.") notes.append(f"Port {port} is {'already' if listening else 'not'} listening inside the container.") return DebugContextResult(ok=True, container=container, working_dir=working_dir, python_version=py_ver, debugpy_version=version, debugpy_listening=listening, mapped_port=mapped_port, processes=processes, suggested_pid=suggested_pid, ports_snapshot=ports_snapshot, env_subset=env_subset, notes=notes).model_dump() - src/debugpy_mcp/server.py:68-80 (schema)The DebugContextResult Pydantic model that defines the output schema for the debugpy_context tool. It includes fields for container info, working directory, Python/debugpy versions, listening status, port mappings, process list, and diagnostic notes.
class DebugContextResult(BaseModel): ok: bool container: str working_dir: str | None = None python_version: str | None = None debugpy_version: str | None = None debugpy_listening: bool = False mapped_port: str | None = None processes: list[ProcessInfo] = Field(default_factory=list) suggested_pid: int | None = None ports_snapshot: str | None = None env_subset: dict[str, str] = Field(default_factory=dict) notes: list[str] = Field(default_factory=list) - src/debugpy_mcp/server.py:503-503 (registration)The @mcp.tool() decorator registers the debugpy_context function as an MCP tool, making it available for invocation by MCP clients.
@mcp.tool() - src/debugpy_mcp/server.py:220-250 (helper)Helper function get_process_table that retrieves the process table from a Docker container and classifies processes by type (uvicorn, gunicorn-master, gunicorn-worker, python, other). This is used by debugpy_context to identify candidate processes for debugging.
def get_process_table(container: str) -> list[ProcessInfo]: proc = docker_exec(container, "ps -eo pid,ppid,args", timeout=20, check=False) if proc.returncode != 0: raise ToolError(f"Unable to read process table in container {container}:\n{proc.stderr}") results: list[ProcessInfo] = [] for line in proc.stdout.splitlines()[1:]: line = line.strip() if not line: continue parts = line.split(None, 2) if len(parts) < 3: continue pid_s, ppid_s, cmd = parts try: pid = int(pid_s) ppid = int(ppid_s) except ValueError: continue lowered = cmd.lower() kind: Literal["uvicorn", "gunicorn-master", "gunicorn-worker", "python", "other"] = "other" if "gunicorn" in lowered and "master" in lowered: kind = "gunicorn-master" elif "gunicorn" in lowered and "worker" in lowered: kind = "gunicorn-worker" elif "uvicorn" in lowered: kind = "uvicorn" elif "python" in lowered: kind = "python" if any(token in lowered for token in ["python", "uvicorn", "gunicorn", "fastapi"]): results.append(ProcessInfo(pid=pid, ppid=ppid, cmd=cmd, kind=kind)) return results - src/debugpy_mcp/server.py:195-217 (helper)Helper function detect_debugpy_installed that checks if debugpy is installed in the container and retrieves its version. This is called by debugpy_context to determine if the debugger is available.
def detect_debugpy_installed(container: str, python_bin: str = "python") -> tuple[bool, str | None]: cmd = ( f"{shlex.quote(python_bin)} - <<'PY'\n" "import importlib.util\n" "spec = importlib.util.find_spec('debugpy')\n" "print('YES' if spec else 'NO')\n" "PY" ) proc = docker_exec(container, cmd, timeout=20, check=False) if proc.returncode != 0: return False, None installed = proc.stdout.strip() == "YES" if not installed: return False, None ver_cmd = ( f"{shlex.quote(python_bin)} - <<'PY'\n" "import debugpy\n" "print(getattr(debugpy, '__version__', 'unknown'))\n" "PY" ) ver_proc = docker_exec(container, ver_cmd, timeout=20, check=False) version = ver_proc.stdout.strip() if ver_proc.returncode == 0 else None return True, version