MCP server that exposes Podman container management as tools for AI assistants
Project description
podman-mcp
A Model Context Protocol (MCP) server that exposes Podman container management as tools for AI assistants such as Claude, GitHub Copilot, ChatGPT, Cursor and Windsurf.
With podman-mcp you can manage containers and images through natural language — no need to remember CLI flags.
"Show me all running containers"
"Pull the nginx image and run it on port 8080"
"Show me the last 100 log lines from myapp"
Table of Contents
- Requirements
- Installation
- Client Configuration
- Using from the Terminal
- Available Tools
- Usage Examples
- Project Structure
- Testing
- Adding New Tools
- Releasing a New Version
- Contributing
- License
Requirements
- Python 3.10+
- Podman installed and available in
$PATH - Claude Code CLI, GitHub Copilot, Cursor, Windsurf, ChatGPT or any MCP-compatible client
Installation
Using pip (recommended)
pip install podman-mcp
From source
git clone https://github.com/edtroleis/podman-mcp.git
cd podman-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Client Configuration
Claude Code
Via pip:
claude mcp add --scope user podman-mcp -- podman-mcp
From source:
claude mcp add --scope user podman-mcp -- /absolute/path/to/podman-mcp/.venv/bin/podman-mcp
The --scope user flag makes the server available across all projects.
Verify:
claude mcp list
Skip confirmation prompts
By default, Claude asks for confirmation before calling each tool. To allow all podman-mcp tools to run without prompts, add the following to ~/.claude/settings.json:
{
"permissions": {
"allow": [
"mcp__podman-mcp__*"
]
}
}
GitHub Copilot (VS Code)
Create or edit .vscode/mcp.json in your workspace.
Via pip:
{
"servers": {
"podman-mcp": {
"type": "stdio",
"command": "podman-mcp"
}
}
}
From source:
{
"servers": {
"podman-mcp": {
"type": "stdio",
"command": "/absolute/path/to/podman-mcp/.venv/bin/podman-mcp"
}
}
}
Requires VS Code 1.99+ with GitHub Copilot agent mode enabled.
Note: VS Code does not auto-start MCP servers. After opening Copilot Chat, click the Start button next to
podman-mcpin the Tools section once per VS Code session. This is a VS Code security design decision and cannot be configured away.
Cursor
Create or edit ~/.cursor/mcp.json.
Via pip:
{
"mcpServers": {
"podman-mcp": {
"command": "podman-mcp"
}
}
}
From source:
{
"mcpServers": {
"podman-mcp": {
"command": "/absolute/path/to/podman-mcp/.venv/bin/podman-mcp"
}
}
}
Windsurf
Create or edit ~/.codeium/windsurf/mcp_config.json.
Via pip:
{
"mcpServers": {
"podman-mcp": {
"command": "podman-mcp"
}
}
}
From source:
{
"mcpServers": {
"podman-mcp": {
"command": "/absolute/path/to/podman-mcp/.venv/bin/podman-mcp"
}
}
}
ChatGPT and other HTTP clients
Start the server in SSE mode:
Via pip:
podman-mcp --transport sse --host 127.0.0.1 --port 8000
From source:
/absolute/path/to/podman-mcp/.venv/bin/podman-mcp --transport sse --host 127.0.0.1 --port 8000
The MCP endpoint will be available at:
http://127.0.0.1:8000/sse
Configure your client to connect to that URL as a remote MCP server.
Using from the Terminal
You can interact with podman-mcp directly from the terminal using the Claude Code CLI — no IDE required.
Claude Code CLI
Once podman-mcp is registered with --scope user, start an interactive session:
claude
Then ask naturally:
How many images do I have locally?
Pull nginx:latest
Show logs from my api container
List all running containers
Remove the alpine image
Other AI CLIs (HTTP mode)
For AI tools that support remote MCP over HTTP, start the server in SSE mode first:
podman-mcp --transport sse --host 127.0.0.1 --port 8000
Then point your AI CLI client to http://127.0.0.1:8000/sse.
Available Tools
Images
| Tool | Description | Parameters |
|---|---|---|
list_images |
List all local images | — |
pull_image |
Pull an image from a registry | image: str |
remove_image |
Remove an image | image: str, force: bool |
build_image |
Build an image from a Dockerfile | tag: str, dockerfile: str, context: str |
tag_image |
Tag an image with a new name | source: str, target: str |
push_image |
Push an image to a registry | image: str |
image_history |
Show layer history of an image | image: str |
search_image |
Search for images in registries | term: str |
save_image |
Save an image to a tar archive | image: str, output: str |
load_image |
Load an image from a tar archive | path: str |
prune_images |
Remove unused images | all: bool |
Containers
| Tool | Description | Parameters |
|---|---|---|
list_containers |
List containers | all: bool (include stopped) |
run_container |
Run a container | image: str, args: str |
start_container |
Start a stopped container | name: str |
stop_container |
Stop a running container | name: str |
restart_container |
Restart a container | name: str |
pause_container |
Pause all processes in a container | name: str |
unpause_container |
Resume a paused container | name: str |
rename_container |
Rename a container | name: str, new_name: str |
remove_container |
Remove a container | name: str, force: bool |
container_logs |
Fetch container logs | name: str, tail: int |
exec_in_container |
Run a command inside a container | name: str, command: str |
inspect_container |
Inspect container configuration | name: str |
container_stats |
Show resource usage for running containers | name: str (empty for all) |
container_top |
Show running processes inside a container | name: str |
container_port |
List port mappings of a container | name: str |
container_diff |
Show filesystem changes in a container | name: str |
copy_to_container |
Copy files between host and container | src: str, dest: str |
Networks
| Tool | Description | Parameters |
|---|---|---|
network_list |
List all networks | — |
network_create |
Create a new network | name: str |
network_remove |
Remove a network | name: str |
network_inspect |
Inspect a network | name: str |
network_connect |
Connect a container to a network | network: str, container: str |
network_disconnect |
Disconnect a container from a network | network: str, container: str |
network_prune |
Remove all unused networks | — |
Volumes
| Tool | Description | Parameters |
|---|---|---|
volume_list |
List all volumes | — |
volume_create |
Create a new volume | name: str |
volume_remove |
Remove a volume | name: str |
volume_inspect |
Inspect a volume | name: str |
volume_prune |
Remove all unused volumes | — |
Pods
| Tool | Description | Parameters |
|---|---|---|
pod_list |
List pods | all: bool (include stopped) |
pod_create |
Create a new pod | name: str |
pod_remove |
Remove a pod | name: str, force: bool |
pod_start |
Start a pod and all its containers | name: str |
pod_stop |
Stop a pod and all its containers | name: str |
pod_restart |
Restart a pod and all its containers | name: str |
pod_inspect |
Inspect a pod | name: str |
pod_stats |
Show resource usage stats for pods | name: str (empty for all) |
Secrets
| Tool | Description | Parameters |
|---|---|---|
secret_list |
List all secrets | — |
secret_create |
Create a secret from a literal value | name: str, value: str |
secret_remove |
Remove a secret | name: str |
secret_inspect |
Inspect a secret (value is never revealed) | name: str |
Generate
| Tool | Description | Parameters |
|---|---|---|
generate_kube |
Generate Kubernetes YAML from a pod or container | name: str |
generate_systemd |
Generate a systemd unit file for a container or pod | name: str |
System
| Tool | Description | Parameters |
|---|---|---|
system_info |
Podman disk usage and system stats | — |
system_prune |
Remove all unused resources | all: bool |
system_events |
Show recent Podman events | since: str, until: str, filter: str |
podman_version |
Show Podman version information | — |
podman_info |
Show detailed host and runtime information | — |
login_registry |
Login to a container registry | registry: str, username: str, password: str |
Usage Examples
Once registered, interact with Podman using natural language. Examples by category:
Images
How many images do I have locally?
Pull the python:3.12-slim image from Docker Hub
Build an image tagged myapp:latest from the Dockerfile in the current directory
Show the layer history of the debian:latest image
Tag myapp:latest as localhost:8082/myapp:1.0
Push localhost:8082/myapp:1.0 to the registry
Search for postgres images in Docker Hub
Save the myapp:latest image to /tmp/myapp.tar
Remove the alpine image
Containers
List all running containers
Show me all containers, including stopped ones
Run nginx in detached mode, exposing port 8080 on the host
Start the stopped api container
Restart the db container
Show the last 200 log lines from the api container
What is the IP address of the db container?
Show CPU and memory usage for all running containers
Execute the command "df -h" inside the api container
Show all port mappings of the web container
Remove all stopped containers
Networks
List all networks
Create a network called backend-net
Connect the api container to the backend-net network
Remove unused networks
Volumes
List all volumes
Create a volume called postgres-data
Remove unused volumes
Pods
List all pods, including stopped ones
Create a pod called my-pod
Start the my-pod pod
Stop the my-pod pod
Remove the my-pod pod and all its containers
Show resource usage for all pods
Secrets
List all secrets
Create a secret called db-password with value s3cr3t
Show metadata for the db-password secret
Remove the db-password secret
Generate
Generate Kubernetes YAML for the my-pod pod
Generate a systemd unit file for the api container
System & Registry
Show Podman disk usage and system stats
Show detailed Podman host information
Show the Podman version
Show recent container start events from the last hour
Remove all unused containers, images, networks and volumes
Login to localhost:8082 with my credentials
Project Structure
podman-mcp/
├── .github/
│ └── workflows/
│ └── publish.yml # GitHub Actions — publishes to PyPI on tag push
├── podman_mcp/
│ ├── __init__.py
│ └── server.py # MCP server — all tools are defined here
├── tests/
│ └── test_server.py # pytest test suite
├── pyproject.toml # Project metadata and build config
├── requirements.txt # Runtime dependencies
├── CONTRIBUTING.md # How to contribute
├── LICENSE # MIT License
└── README.md
Testing
Tests use pytest and mock subprocess.run so no Podman installation is required to run them.
Install dev dependencies and run the suite:
pip install -e ".[dev]"
pytest tests/
Run with verbose output:
pytest tests/ -v
Adding New Tools
Open podman_mcp/server.py and add a new function decorated with @mcp.tool():
@mcp.tool()
def your_tool_name(param: str) -> str:
"""Clear description — the AI assistant uses this to decide when to call the tool."""
return run(f"<podman subcommand> {param}")
No re-registration is needed. Restart your AI client session to pick up the new tool.
Releasing a New Version
- Bump the version in
pyproject.toml:
[project]
version = "1.2.3"
- Commit and push:
git add pyproject.toml
git commit -m "bump version to 1.2.3"
git push
- Create and push a tag — this triggers the publish pipeline:
git tag v1.2.3
git push origin v1.2.3
The GitHub Actions workflow will run tests, publish to PyPI and create a GitHub Release automatically.
Note: PyPI does not allow re-uploading the same version. Always bump the version before tagging.
Release Notes
Release notes are generated automatically from commits and PRs merged since the last tag, grouped by label:
| Label | Section |
|---|---|
enhancement, feature |
New Features |
bug, fix |
Bug Fixes |
documentation, docs |
Documentation |
| anything else | Other Changes |
No conventional commit format required — just label your PRs on GitHub before merging.
Contributing
Contributions are welcome! See CONTRIBUTING.md for guidelines.
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file podman_mcp-0.3.0.tar.gz.
File metadata
- Download URL: podman_mcp-0.3.0.tar.gz
- Upload date:
- Size: 15.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb03c25a87d6844a0d27f8f54d6e74c199a8ed8a9b0be33cd3fdcff50ee578fc
|
|
| MD5 |
0cbe63c773648fc041c88675abfb2f8e
|
|
| BLAKE2b-256 |
032628dd379382272b19241eb04c7caacb3024f0bc35fcec2ce7cbfe0f3c701e
|
Provenance
The following attestation bundles were made for podman_mcp-0.3.0.tar.gz:
Publisher:
publish.yml on edtroleis/podman-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
podman_mcp-0.3.0.tar.gz -
Subject digest:
fb03c25a87d6844a0d27f8f54d6e74c199a8ed8a9b0be33cd3fdcff50ee578fc - Sigstore transparency entry: 1916285580
- Sigstore integration time:
-
Permalink:
edtroleis/podman-mcp@667607a24787d9216d51642f90ac25138091a39a -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/edtroleis
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@667607a24787d9216d51642f90ac25138091a39a -
Trigger Event:
push
-
Statement type:
File details
Details for the file podman_mcp-0.3.0-py3-none-any.whl.
File metadata
- Download URL: podman_mcp-0.3.0-py3-none-any.whl
- Upload date:
- Size: 10.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9c7187c156609632c8d420cd9ed2230208051889fc344075e2b272f4a40ad4e2
|
|
| MD5 |
a05d6c3e6c88605f6fb282f9e544688e
|
|
| BLAKE2b-256 |
cba150769a53651ae748b56ec9e3744dd83d6d6fa09d594c8e40d1e0df1137d9
|
Provenance
The following attestation bundles were made for podman_mcp-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on edtroleis/podman-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
podman_mcp-0.3.0-py3-none-any.whl -
Subject digest:
9c7187c156609632c8d420cd9ed2230208051889fc344075e2b272f4a40ad4e2 - Sigstore transparency entry: 1916285736
- Sigstore integration time:
-
Permalink:
edtroleis/podman-mcp@667607a24787d9216d51642f90ac25138091a39a -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/edtroleis
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@667607a24787d9216d51642f90ac25138091a39a -
Trigger Event:
push
-
Statement type: