diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 93fc9e8..4d6c754 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -13,6 +13,7 @@ concurrency: env: GO_VERSION: 1.23 NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + UV_PUBLISH_TOKEN: ${{ secrets.UV_PUBLISH_TOKEN }} permissions: contents: write @@ -41,3 +42,13 @@ jobs: - name: Publish npm run: make npm-publish + python: + name: Release Python + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v5 + - name: Publish Python + run: + make python-publish diff --git a/.gitignore b/.gitignore index 9e6f4f5..1d4fb59 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,9 @@ kubernetes-mcp-server-linux-arm64 !npm/kubernetes-mcp-server-linux-arm64 kubernetes-mcp-server-windows-amd64.exe kubernetes-mcp-server-windows-arm64.exe + +python/.venv/ +python/build/ +python/dist/ +python/kubernetes_mcp_server.egg-info/ +!python/kubernetes-mcp-server diff --git a/Makefile b/Makefile index 0e50dbe..1315b73 100644 --- a/Makefile +++ b/Makefile @@ -79,6 +79,15 @@ npm-publish: npm-copy-binaries ## Publish the npm packages jq '.optionalDependencies |= with_entries(.value = "$(NPM_VERSION)")' ./npm/kubernetes-mcp-server/package.json > tmp.json && mv tmp.json ./npm/kubernetes-mcp-server/package.json; \ cd npm/kubernetes-mcp-server && npm publish +.PHONY: python-publish +python-publish: ## Publish the python packages + cd ./python && \ + sed -i "s/version = \".*\"/version = \"$(NPM_VERSION)\"/" pyproject.toml && \ + uv build && \ + uv push + + + .PHONY: test test: ## Run the tests go test -count=1 -v ./... diff --git a/python/README.md b/python/README.md new file mode 120000 index 0000000..32d46ee --- /dev/null +++ b/python/README.md @@ -0,0 +1 @@ +../README.md \ No newline at end of file diff --git a/python/kubernetes_mcp_server/__init__.py b/python/kubernetes_mcp_server/__init__.py new file mode 100644 index 0000000..4a73143 --- /dev/null +++ b/python/kubernetes_mcp_server/__init__.py @@ -0,0 +1,7 @@ +""" +Kubernetes MCP Server (Model Context Protocol) with special support for OpenShift. +""" +from .kubernetes_mcp_server import main + +__all__ = ['main'] + diff --git a/python/kubernetes_mcp_server/__main__.py b/python/kubernetes_mcp_server/__main__.py new file mode 100644 index 0000000..40171c9 --- /dev/null +++ b/python/kubernetes_mcp_server/__main__.py @@ -0,0 +1,4 @@ +from .kubernetes_mcp_server import main + +if __name__ == "__main__": + main() diff --git a/python/kubernetes_mcp_server/kubernetes_mcp_server.py b/python/kubernetes_mcp_server/kubernetes_mcp_server.py new file mode 100644 index 0000000..b1d1f24 --- /dev/null +++ b/python/kubernetes_mcp_server/kubernetes_mcp_server.py @@ -0,0 +1,91 @@ +import os +import platform +import subprocess +import sys +from pathlib import Path +import shutil +import tempfile +import urllib.request + +def get_platform_binary(): + """Determine the correct binary for the current platform.""" + system = platform.system().lower() + arch = platform.machine().lower() + + # Normalize architecture names + if arch in ["x86_64", "amd64"]: + arch = "amd64" + elif arch in ["arm64", "aarch64"]: + arch = "arm64" + else: + raise RuntimeError(f"Unsupported architecture: {arch}") + + if system == "darwin": + return f"kubernetes-mcp-server-darwin-{arch}" + elif system == "linux": + return f"kubernetes-mcp-server-linux-{arch}" + elif system == "windows": + return f"kubernetes-mcp-server-windows-{arch}.exe" + else: + raise RuntimeError(f"Unsupported operating system: {system}") + +def download_binary(version="latest", destination=None): + """Download the correct binary for the current platform.""" + binary_name = get_platform_binary() + if destination is None: + destination = Path.home() / ".kubernetes-mcp-server" / "bin" + + destination = Path(destination) + destination.mkdir(parents=True, exist_ok=True) + binary_path = destination / binary_name + + if binary_path.exists(): + return binary_path + + base_url = "https://github.com/manusa/kubernetes-mcp-server/releases" + if version == "latest": + release_url = f"{base_url}/latest/download/{binary_name}" + else: + release_url = f"{base_url}/download/{version}/{binary_name}" + + # Download the binary + print(f"Downloading {binary_name} from {release_url}") + with tempfile.NamedTemporaryFile(delete=False) as temp_file: + try: + with urllib.request.urlopen(release_url) as response: + shutil.copyfileobj(response, temp_file) + temp_file.close() + + # Move to destination and make executable + shutil.move(temp_file.name, binary_path) + binary_path.chmod(binary_path.stat().st_mode | 0o755) # Make executable + + return binary_path + except Exception as e: + os.unlink(temp_file.name) + raise RuntimeError(f"Failed to download binary: {e}") + +def execute(args=None): + """Download and execute the kubernetes-mcp-server binary.""" + if args is None: + args = [] + + try: + binary_path = download_binary() + cmd = [str(binary_path)] + args + + # Execute the binary with the provided arguments + process = subprocess.run(cmd) + return process.returncode + except Exception as e: + print(f"Error executing kubernetes-mcp-server: {e}", file=sys.stderr) + return 1 + +if __name__ == "__main__": + sys.exit(execute(sys.argv[1:])) + + +def main(): + """Main function to execute the kubernetes-mcp-server binary.""" + args = sys.argv[1:] if len(sys.argv) > 1 else [] + return execute(args) diff --git a/python/pyproject.toml b/python/pyproject.toml new file mode 100644 index 0000000..ba6467c --- /dev/null +++ b/python/pyproject.toml @@ -0,0 +1,25 @@ +[build-system] +requires = ["setuptools>=42", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "kubernetes-mcp-server" +version = "0.0.21-7-g869ebc2" +description = "Kubernetes MCP Server (Model Context Protocol) with special support for OpenShift" +readme = {file="README.md", content-type="text/markdown"} +requires-python = ">=3.6" +license = "Apache-2.0" +authors = [ + { name = "Marc Nuri", email = "marc@marcnuri.com" } +] +classifiers = [ + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", +] + +[project.urls] +Homepage = "https://github.com/manusa/kubernetes-mcp-server" +Repository = "https://github.com/manusa/kubernetes-mcp-server" + +[project.scripts] +kubernetes-mcp-server = "kubernetes_mcp_server:main" diff --git a/python/uv.lock b/python/uv.lock new file mode 100644 index 0000000..6211758 --- /dev/null +++ b/python/uv.lock @@ -0,0 +1,8 @@ +version = 1 +revision = 1 +requires-python = ">=3.6" + +[[package]] +name = "kubernetes-mcp-server" +version = "0.0.0.dev3" +source = { editable = "." }