.PHONY: help install build dev start test test-watch test-coverage clean docker-build docker-run docker-stop docker-clean deploy k8s-deploy k8s-delete logs lint format check-env setup
# Variables
IMAGE_NAME := twenty-crm-mcp-server
IMAGE_TAG := latest
PORT := 5008
DOCKER_PORT := 8080
K3D_CLUSTER := twenty-crm-cluster
NAMESPACE := default
# Default target
.DEFAULT_GOAL := help
## help: Display this help message
help:
@echo "Twenty CRM MCP Server - Available Commands:"
@echo ""
@sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /'
@echo ""
## install: Install all dependencies
install:
@echo "π¦ Installing dependencies..."
npm install
## setup: Initial setup - install dependencies and create .env file
setup: install
@if [ ! -f .env ]; then \
echo "π Creating .env file from template..."; \
cp .env.example .env; \
echo "β οΈ Please update .env with your Twenty CRM API key"; \
else \
echo "β
.env file already exists"; \
fi
## check-env: Verify environment variables are set
check-env:
@if [ ! -f .env ]; then \
echo "β .env file not found. Run 'make setup' first."; \
exit 1; \
fi
@if ! grep -q "TWENTY_API_KEY=.*[^_]" .env 2>/dev/null; then \
echo "β οΈ Warning: TWENTY_API_KEY not set in .env file"; \
echo "Please update .env with your Twenty CRM API key"; \
exit 1; \
fi
@echo "β
Environment configured"
## build: Build TypeScript project
build:
@echo "π¨ Building TypeScript project..."
npm run build
## dev: Start development server with auto-reload
dev: build
@echo "π Starting development server on port $(PORT)..."
@if [ ! -f .env ]; then \
echo "β οΈ Warning: .env file not found. Run 'make setup' first."; \
echo "Creating .env from template..."; \
cp .env.example .env; \
echo "β οΈ Please edit .env and add your TWENTY_API_KEY"; \
exit 1; \
fi
@if ! grep -q "TWENTY_API_KEY=.*[^_]" .env 2>/dev/null || grep -q "TWENTY_API_KEY=your_api_key_here" .env 2>/dev/null; then \
echo "β οΈ Warning: TWENTY_API_KEY not configured in .env"; \
echo "Please edit .env and add your Twenty CRM API key"; \
echo "Get your API key from: https://app.twenty.com/settings/developers"; \
exit 1; \
fi
@echo "π Server will be available at http://localhost:$(PORT)"
npm run mcp
## start: Start production server
start: build
@echo "π Starting production server on port $(PORT)..."
@if [ ! -f .env ]; then \
echo "β .env file not found. Run 'make setup' first."; \
exit 1; \
fi
npm start
## test: Run tests
test:
@echo "π§ͺ Running tests..."
npm test
## test-watch: Run tests in watch mode
test-watch:
@echo "π§ͺ Running tests in watch mode..."
npm run test:watch
## test-coverage: Run tests with coverage report
test-coverage:
@echo "π§ͺ Running tests with coverage..."
npm run test:coverage
## test-ui: Open test UI
test-ui:
@echo "π§ͺ Opening test UI..."
npm run test:ui
## lint: Run linter (if configured)
lint:
@echo "π Running linter..."
@if command -v eslint >/dev/null 2>&1; then \
npx eslint src/**/*.ts; \
else \
echo "β οΈ ESLint not installed, skipping..."; \
fi
## format: Format code (if prettier is installed)
format:
@echo "β¨ Formatting code..."
@if command -v prettier >/dev/null 2>&1; then \
npx prettier --write "src/**/*.ts"; \
else \
echo "β οΈ Prettier not installed, skipping..."; \
fi
## clean: Clean build artifacts
clean:
@echo "π§Ή Cleaning build artifacts..."
rm -rf build/
rm -rf coverage/
rm -rf node_modules/.cache/
## clean-all: Clean everything including node_modules
clean-all: clean
@echo "π§Ή Removing node_modules..."
rm -rf node_modules/
## docker-build: Build Docker image
docker-build:
@echo "π³ Building Docker image: $(IMAGE_NAME):$(IMAGE_TAG)..."
npm run docker-build
## docker-run: Run Docker container
docker-run: docker-build
@echo "π³ Starting Docker container..."
@if [ -f .env ]; then \
docker run -d \
-p $(PORT):$(DOCKER_PORT) \
--name $(IMAGE_NAME) \
--env-file .env \
$(IMAGE_NAME):$(IMAGE_TAG); \
else \
docker run -d \
-p $(PORT):$(DOCKER_PORT) \
--name $(IMAGE_NAME) \
$(IMAGE_NAME):$(IMAGE_TAG); \
fi
@echo "β
Container running at http://localhost:$(PORT)"
@echo "π View logs: make logs"
@echo "π Stop container: make docker-stop"
## docker-stop: Stop and remove Docker container
docker-stop:
@echo "π Stopping Docker container..."
@docker stop $(IMAGE_NAME) 2>/dev/null || true
@docker rm $(IMAGE_NAME) 2>/dev/null || true
@echo "β
Container stopped and removed"
## docker-clean: Remove Docker image
docker-clean: docker-stop
@echo "π§Ή Removing Docker image..."
@docker rmi $(IMAGE_NAME):$(IMAGE_TAG) 2>/dev/null || true
@echo "β
Docker image removed"
## docker-logs: Show Docker container logs
docker-logs:
@echo "π Showing container logs (Ctrl+C to exit)..."
docker logs -f $(IMAGE_NAME)
## logs: Alias for docker-logs
logs: docker-logs
## docker-shell: Open shell in running container
docker-shell:
@echo "π Opening shell in container..."
docker exec -it $(IMAGE_NAME) /bin/sh
## k3d-setup: Setup k3d cluster
k3d-setup:
@echo "π― Setting up k3d cluster: $(K3D_CLUSTER)..."
@if ! command -v k3d >/dev/null 2>&1; then \
echo "β k3d not installed. Please install k3d first."; \
exit 1; \
fi
@if ! k3d cluster list | grep -q $(K3D_CLUSTER); then \
k3d cluster create $(K3D_CLUSTER) \
--api-port 6550 \
--port "$(PORT):80@loadbalancer"; \
echo "β
k3d cluster created"; \
else \
echo "β
k3d cluster already exists"; \
fi
## k3d-delete: Delete k3d cluster
k3d-delete:
@echo "ποΈ Deleting k3d cluster: $(K3D_CLUSTER)..."
k3d cluster delete $(K3D_CLUSTER) 2>/dev/null || true
@echo "β
k3d cluster deleted"
## k3d-import: Build and import image to k3d
k3d-import: docker-build
@echo "π¦ Importing image to k3d cluster..."
npm run build:k3d
## k8s-secret: Create Kubernetes secret for Twenty API key
k8s-secret:
@if [ ! -f .env ]; then \
echo "β .env file not found. Run 'make setup' first."; \
exit 1; \
fi
@echo "π Creating Kubernetes secret..."
@. ./.env && kubectl create secret generic twenty-crm-secrets \
--from-literal=api-key=$$TWENTY_API_KEY \
--namespace=$(NAMESPACE) \
--dry-run=client -o yaml | kubectl apply -f -
@echo "β
Secret created/updated"
## k8s-deploy: Deploy to Kubernetes
k8s-deploy: k3d-import k8s-secret
@echo "π Deploying to Kubernetes..."
npm run deploy
@echo "β
Deployment complete"
@echo "π Check status: kubectl get pods -l app=$(IMAGE_NAME)"
## deploy: Alias for k8s-deploy
deploy: k8s-deploy
## k8s-delete: Delete Kubernetes deployment
k8s-delete:
@echo "ποΈ Deleting Kubernetes deployment..."
kubectl delete -f k8s/deployment.yaml 2>/dev/null || true
@echo "β
Deployment deleted"
## k8s-status: Check Kubernetes deployment status
k8s-status:
@echo "π Deployment status:"
@kubectl get deployments -l app=$(IMAGE_NAME) 2>/dev/null || echo "No deployment found"
@echo ""
@echo "π Pod status:"
@kubectl get pods -l app=$(IMAGE_NAME) 2>/dev/null || echo "No pods found"
@echo ""
@echo "π Service status:"
@kubectl get services -l app=$(IMAGE_NAME) 2>/dev/null || echo "No service found"
## k8s-logs: Show Kubernetes pod logs
k8s-logs:
@echo "π Showing pod logs (Ctrl+C to exit)..."
@POD=$$(kubectl get pods -l app=$(IMAGE_NAME) -o jsonpath='{.items[0].metadata.name}' 2>/dev/null); \
if [ -n "$$POD" ]; then \
kubectl logs -f $$POD; \
else \
echo "β No pods found"; \
fi
## k8s-shell: Open shell in Kubernetes pod
k8s-shell:
@echo "π Opening shell in pod..."
@POD=$$(kubectl get pods -l app=$(IMAGE_NAME) -o jsonpath='{.items[0].metadata.name}' 2>/dev/null); \
if [ -n "$$POD" ]; then \
kubectl exec -it $$POD -- /bin/sh; \
else \
echo "β No pods found"; \
fi
## k8s-restart: Restart Kubernetes deployment
k8s-restart:
@echo "π Restarting deployment..."
kubectl rollout restart deployment/$(IMAGE_NAME)
kubectl rollout status deployment/$(IMAGE_NAME)
@echo "β
Deployment restarted"
## inspect: Open MCP inspector
inspect:
@echo "π Opening MCP Inspector..."
npm run mcp-inspect
## health: Check server health
health:
@echo "π₯ Checking server health..."
@curl -s http://localhost:$(PORT)/health | jq '.' 2>/dev/null || \
curl -s http://localhost:$(PORT)/health || \
echo "β Server not responding at http://localhost:$(PORT)/health"
## info: Display project information
info:
@echo "π Twenty CRM MCP Server Information"
@echo "===================================="
@echo "Image Name: $(IMAGE_NAME)"
@echo "Image Tag: $(IMAGE_TAG)"
@echo "Local Port: $(PORT)"
@echo "Container Port: $(DOCKER_PORT)"
@echo "K3D Cluster: $(K3D_CLUSTER)"
@echo "Namespace: $(NAMESPACE)"
@echo ""
@if [ -f .env ]; then \
echo "β
Environment configured"; \
else \
echo "β Environment not configured (run 'make setup')"; \
fi
@echo ""
@echo "Node version: $$(node --version 2>/dev/null || echo 'Not installed')"
@echo "npm version: $$(npm --version 2>/dev/null || echo 'Not installed')"
@echo "Docker version: $$(docker --version 2>/dev/null || echo 'Not installed')"
@echo "k3d version: $$(k3d --version 2>/dev/null || echo 'Not installed')"
@echo "kubectl version: $$(kubectl version --client --short 2>/dev/null || echo 'Not installed')"
## all: Install, build, and run tests
all: install build test
@echo "β
All tasks completed successfully"
## ci: Run CI pipeline (install, build, lint, test)
ci: install build lint test
@echo "β
CI pipeline completed successfully"
## watch: Watch for file changes and rebuild
watch:
@echo "π Watching for changes..."
@while true; do \
inotifywait -r -e modify,create,delete src/ 2>/dev/null || \
fswatch -o src/ 2>/dev/null || \
(echo "β οΈ File watching not available. Install inotify-tools or fswatch."; exit 1); \
clear; \
echo "π¨ Rebuilding..."; \
make build; \
done
## update: Update dependencies
update:
@echo "π¦ Updating dependencies..."
npm update
@echo "β
Dependencies updated"
## audit: Run security audit
audit:
@echo "π Running security audit..."
npm audit
## audit-fix: Fix security vulnerabilities
audit-fix:
@echo "π Fixing security vulnerabilities..."
npm audit fix