initial commit
This commit is contained in:
12
services/mcp-k8s/Dockerfile
Normal file
12
services/mcp-k8s/Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY server.py .
|
||||
|
||||
EXPOSE 8012
|
||||
|
||||
CMD ["python", "/app/server.py"]
|
||||
2
services/mcp-k8s/requirements.txt
Normal file
2
services/mcp-k8s/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
mcp[cli]>=1.10.0
|
||||
kubernetes>=30.1.0
|
||||
78
services/mcp-k8s/server.py
Normal file
78
services/mcp-k8s/server.py
Normal file
@@ -0,0 +1,78 @@
|
||||
import os
|
||||
from kubernetes import client, config
|
||||
from kubernetes.client.exceptions import ApiException
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("k8s-cluster", host="0.0.0.0", port=8012)
|
||||
|
||||
|
||||
def _load_kube() -> None:
|
||||
kubeconfig = os.getenv("KUBECONFIG")
|
||||
try:
|
||||
if kubeconfig and os.path.exists(kubeconfig):
|
||||
config.load_kube_config(config_file=kubeconfig)
|
||||
else:
|
||||
config.load_incluster_config()
|
||||
except Exception as exc:
|
||||
raise RuntimeError(f"Failed to load Kubernetes config: {exc}")
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def list_namespaces() -> list[str]:
|
||||
"""List all Kubernetes namespaces visible to this server."""
|
||||
_load_kube()
|
||||
api = client.CoreV1Api()
|
||||
return [item.metadata.name for item in api.list_namespace().items]
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def list_pods(namespace: str | None = None, limit: int = 100) -> list[dict]:
|
||||
"""List pods in a namespace with status and node placement."""
|
||||
_load_kube()
|
||||
api = client.CoreV1Api()
|
||||
ns = namespace or os.getenv("K8S_DEFAULT_NAMESPACE", "default")
|
||||
|
||||
pods = api.list_namespaced_pod(ns, limit=limit).items
|
||||
return [
|
||||
{
|
||||
"name": pod.metadata.name,
|
||||
"phase": pod.status.phase,
|
||||
"node": pod.spec.node_name,
|
||||
"created": pod.metadata.creation_timestamp.isoformat() if pod.metadata.creation_timestamp else None,
|
||||
}
|
||||
for pod in pods
|
||||
]
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def get_pod_logs(namespace: str, pod_name: str, tail_lines: int = 200) -> str:
|
||||
"""Get recent logs from a specific pod."""
|
||||
_load_kube()
|
||||
api = client.CoreV1Api()
|
||||
return api.read_namespaced_pod_log(name=pod_name, namespace=namespace, tail_lines=tail_lines)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def list_deployments(namespace: str | None = None) -> list[dict]:
|
||||
"""List deployments in the namespace with availability details."""
|
||||
_load_kube()
|
||||
api = client.AppsV1Api()
|
||||
ns = namespace or os.getenv("K8S_DEFAULT_NAMESPACE", "default")
|
||||
|
||||
try:
|
||||
deps = api.list_namespaced_deployment(ns).items
|
||||
except ApiException as exc:
|
||||
raise RuntimeError(f"Kubernetes API error: {exc}")
|
||||
|
||||
return [
|
||||
{
|
||||
"name": dep.metadata.name,
|
||||
"ready_replicas": dep.status.ready_replicas or 0,
|
||||
"replicas": dep.status.replicas or 0,
|
||||
}
|
||||
for dep in deps
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run(transport="sse")
|
||||
Reference in New Issue
Block a user