diff --git a/Dockerfile b/Dockerfile index 99ef59e..ec0ea77 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,9 +27,19 @@ RUN m3 init mimic-iv-demo # Lite: SQLite only FROM base AS lite +ENV MCP_TRANSPORT=http \ + MCP_HOST=0.0.0.0 \ + MCP_PORT=3000 \ + MCP_PATH=/sse +EXPOSE 3000 CMD ["python", "-m", "m3.mcp_server"] # BigQuery: add GCP client FROM base AS bigquery RUN pip install --no-cache-dir google-cloud-bigquery +ENV MCP_TRANSPORT=http \ + MCP_HOST=0.0.0.0 \ + MCP_PORT=3000 \ + MCP_PATH=/sse +EXPOSE 3000 CMD ["python", "-m", "m3.mcp_server"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..25d8f4e --- /dev/null +++ b/Makefile @@ -0,0 +1,58 @@ +# Makefile for M3 Docker Image Build and Push +DOCKER ?= docker +IMAGE_NAME := m3-mimic-demo +IMAGE_TAG ?= 0.4.0 + +# Prompt for registry only if not set +ifndef DOCKER_REGISTRY +DOCKER_REGISTRY := $(shell bash -c 'read -p "Enter Docker registry/username: " registry; echo $${registry}') +endif + +DOCKER_IMAGE := $(DOCKER_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG) + +DB_FILE := m3_data/databases/mimic_iv_demo.db + +.PHONY: help +help: ## Show help + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " %-20s %s\n", $$1, $$2}' + +.PHONY: all +all: download-db build push ## Complete workflow: download DB, build and push + +.PHONY: login +login: ## Login to Docker Hub + @$(DOCKER) login docker.io + +.PHONY: download-db +download-db: ## Download MIMIC-IV demo database + @uv sync + @uv run m3 init mimic-iv-demo + +.PHONY: build +build: ## Build Docker image (lite version) + @test -f $(DB_FILE) || { echo "Run 'make download-db' first"; exit 1; } + @$(DOCKER) build --target lite -t $(DOCKER_IMAGE) -t $(DOCKER_REGISTRY)/$(IMAGE_NAME):lite . + +.PHONY: build-bigquery +build-bigquery: ## Build BigQuery version + @test -f $(DB_FILE) || { echo "Run 'make download-db' first"; exit 1; } + @$(DOCKER) build --target bigquery -t $(DOCKER_REGISTRY)/$(IMAGE_NAME):bigquery . + +.PHONY: push +push: ## Push Docker image to registry (run 'make login' first) + @$(DOCKER) push $(DOCKER_IMAGE) + @$(DOCKER) push $(DOCKER_REGISTRY)/$(IMAGE_NAME):lite + +.PHONY: push-bigquery +push-bigquery: ## Push BigQuery image (run 'make login' first) + @$(DOCKER) push $(DOCKER_REGISTRY)/$(IMAGE_NAME):bigquery + +.PHONY: test-image +test-image: ## Test the built Docker image + @$(DOCKER) run --rm $(DOCKER_IMAGE) python -c "import m3; print(f'M3 version: {m3.__version__}')" + +.PHONY: clean +clean: ## Remove database and raw files + @rm -rf m3_data + +.DEFAULT_GOAL := help diff --git a/README.md b/README.md index f11204d..dc62365 100644 --- a/README.md +++ b/README.md @@ -548,7 +548,23 @@ m3-mcp-server - 🔐 **Enhanced Security**: Role-based access control, audit logging - 🌐 **Multi-tenant Support**: Organization-level data isolation -## Contributing +## 🐳 Kubernetes Deployment + +Deploy M3 on Kubernetes using Docker images with pre-loaded MIMIC-IV demo database: + +```bash +# Build and push Docker image +make all # Will prompt for Docker registry/username + +# Or specify registry directly +make all DOCKER_REGISTRY=your-username DOCKER=podman +``` + +The container uses StreamableHTTP transport on port 3000 with path `/sse`. Configure your MCP client to connect to the service endpoint (e.g., `http://m3.kagent.svc.cluster.local:3000/sse` for intra-cluster access). + +Helm charts for deploying M3 are available in a separate repository. + +## 🤝 Contributing We welcome contributions! Please: diff --git a/src/m3/mcp_server.py b/src/m3/mcp_server.py index 1a7ad6f..ebc870d 100644 --- a/src/m3/mcp_server.py +++ b/src/m3/mcp_server.py @@ -682,9 +682,30 @@ def get_race_distribution(limit: int = 10) -> str: def main(): - """Main entry point for MCP server.""" - # Run the FastMCP server - mcp.run() + """Main entry point for MCP server. + + Runs FastMCP server in either STDIO mode (desktop clients) or HTTP mode + (Kubernetes/web clients). Transport mode configured via environment variables. + + Environment Variables: + MCP_TRANSPORT: "stdio" (default), "sse", or "http" + MCP_HOST: Host binding for HTTP mode (default: "0.0.0.0") + MCP_PORT: Port for HTTP mode (default: 3000) + MCP_PATH: SSE endpoint path for HTTP mode (default: "/sse") + + Notes: + HTTP/SSE mode uses streamable-http transport for containerized deployments + where STDIO is unavailable. Binds to 0.0.0.0 for Kubernetes service mesh access. + """ + transport = os.getenv("MCP_TRANSPORT", "stdio").lower() + + if transport in ("sse", "http"): + host = os.getenv("MCP_HOST", "0.0.0.0") + port = int(os.getenv("MCP_PORT", "3000")) + path = os.getenv("MCP_PATH", "/sse") + mcp.run(transport="streamable-http", host=host, port=port, path=path) + else: + mcp.run() if __name__ == "__main__":