1- name : Build CodeAlive MCP Server docker image
1+ name : Build and Publish CodeAlive MCP Server
22
33on :
44 push :
5+ branches : [main]
6+ pull_request :
7+ branches : [main]
58
69env :
710 DOCKER_REGISTRY : ghcr.io
811 DOCKER_USERNAME : ${{ github.actor }}
912 DOCKER_PASSWORD : ${{ secrets.GITHUB_TOKEN }}
1013 IMAGE_NAME : ghcr.io/codealive-ai/codealive-mcp
1114
15+ permissions :
16+ id-token : write # Required for MCP Registry OIDC authentication
17+ contents : write # For creating tags and releases
18+ packages : write
19+
1220jobs :
13- build-mcp-server :
14- name : MCP Server docker image
21+ build-and-publish :
22+ name : Build, Test, and Publish MCP Server
1523 runs-on : ubuntu-latest
16- permissions :
17- contents : read
18- packages : write
1924 steps :
2025 - name : Checkout repository
2126 uses : actions/checkout@v4
27+ with :
28+ fetch-depth : 0 # Fetch all history for version detection
2229
2330 - name : Set up Python
2431 uses : actions/setup-python@v5
3037 run : |
3138 python -m pip install --upgrade pip
3239 pip install -e .
33- pip install pytest pytest-asyncio pytest-mock pytest-cov
40+ pip install pytest pytest-asyncio pytest-mock pytest-cov jsonschema
3441
3542 - name : Run tests
3643 run : |
@@ -45,26 +52,202 @@ jobs:
4552 junit/test-results.xml
4653 coverage.xml
4754
55+ - name : Check for version change
56+ id : version-check
57+ run : |
58+ # Get current version from pyproject.toml
59+ CURRENT_VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
60+ echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
61+
62+ # Check if tag already exists
63+ if git tag -l "v$CURRENT_VERSION" | grep -q "v$CURRENT_VERSION"; then
64+ echo "version_changed=false" >> $GITHUB_OUTPUT
65+ echo "Tag v$CURRENT_VERSION already exists"
66+ else
67+ echo "version_changed=true" >> $GITHUB_OUTPUT
68+ echo "New version detected: $CURRENT_VERSION"
69+ fi
70+
71+ - name : Update server.json version
72+ if : steps.version-check.outputs.version_changed == 'true'
73+ run : |
74+ python -c "
75+ import json
76+ with open('server.json', 'r') as f:
77+ data = json.load(f)
78+ version = '${{ steps.version-check.outputs.current_version }}'
79+ data['version'] = version
80+ if 'packages' in data:
81+ for package in data['packages']:
82+ package['version'] = version
83+ transport = package.get('transport', {})
84+ args = transport.get('args') if isinstance(transport, dict) else None
85+ if isinstance(args, list):
86+ for idx, arg in enumerate(args):
87+ if isinstance(arg, str) and 'ghcr.io/codealive-ai/codealive-mcp:' in arg:
88+ args[idx] = f'ghcr.io/codealive-ai/codealive-mcp:v{version}'
89+ with open('server.json', 'w') as f:
90+ json.dump(data, f, indent=2)
91+ "
92+
4893 - name : Set up Docker Buildx
49- uses : docker/setup-buildx-action@v2
94+ uses : docker/setup-buildx-action@v3
95+
5096 - name : Docker meta
5197 id : meta
52- uses : docker/metadata-action@v3
98+ uses : docker/metadata-action@v5
5399 with :
54100 images : ${{ env.IMAGE_NAME }}
101+ tags : |
102+ type=ref,event=branch
103+ type=ref,event=pr
104+ type=semver,pattern={{version}},value=${{ steps.version-check.outputs.current_version }},enable=${{ github.ref == 'refs/heads/main' && steps.version-check.outputs.version_changed == 'true' }}
105+ type=semver,pattern=v{{version}},value=${{ steps.version-check.outputs.current_version }},enable=${{ github.ref == 'refs/heads/main' && steps.version-check.outputs.version_changed == 'true' }}
106+ type=raw,value=latest,enable={{is_default_branch}}
107+
55108 - name : Login to GitHub Container Registry
56- uses : docker/login-action@v2
109+ uses : docker/login-action@v3
57110 with :
58111 registry : ${{ env.DOCKER_REGISTRY }}
59112 username : ${{ env.DOCKER_USERNAME }}
60113 password : ${{ env.DOCKER_PASSWORD }}
61- - name : Build and push
62- uses : docker/build-push-action@v3
114+
115+ - name : Build and push Docker image
116+ uses : docker/build-push-action@v5
63117 with :
64118 push : true
65119 platforms : linux/amd64,linux/arm64
66120 file : ./Dockerfile
67121 tags : ${{ steps.meta.outputs.tags }}
68- labels : ${{ steps.meta.outputs.labels }}
122+ labels : |
123+ ${{ steps.meta.outputs.labels }}
124+ io.modelcontextprotocol.server.name=io.github.codealive-ai/codealive-mcp
69125 cache-from : type=gha
70126 cache-to : type=gha
127+
128+ - name : Create git tag
129+ if : steps.version-check.outputs.version_changed == 'true' && github.ref == 'refs/heads/main'
130+ run : |
131+ git config user.name "github-actions[bot]"
132+ git config user.email "github-actions[bot]@users.noreply.github.com"
133+ git tag "v${{ steps.version-check.outputs.current_version }}"
134+ git push origin "v${{ steps.version-check.outputs.current_version }}"
135+
136+ - name : Validate server.json
137+ if : steps.version-check.outputs.version_changed == 'true' && github.ref == 'refs/heads/main'
138+ run : |
139+ python - <<'PY'
140+ import json
141+ import sys
142+ import urllib.request
143+ from jsonschema import ValidationError, validate
144+
145+ schema_url = "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json"
146+
147+ try :
148+ with urllib.request.urlopen(schema_url, timeout=30) as response :
149+ schema = json.load(response)
150+ except Exception as exc :
151+ print(f"✗ Unable to download server.json schema : {exc}")
152+ sys.exit(1)
153+
154+ try :
155+ with open('server.json', 'r') as f :
156+ data = json.load(f)
157+ except Exception as exc :
158+ print(f"✗ Failed to load server.json : {exc}")
159+ sys.exit(1)
160+
161+ try :
162+ validate(instance=data, schema=schema)
163+ print('✓ server.json schema validation passed')
164+ except ValidationError as exc :
165+ print(f"✗ server.json schema validation failed : {exc.message}")
166+ sys.exit(1)
167+
168+ has_packages = 'packages' in data and len(data['packages']) > 0
169+ has_remotes = 'remotes' in data and len(data['remotes']) > 0
170+
171+ if not (has_packages or has_remotes) :
172+ print('✗ Must have either packages or remotes configured')
173+ sys.exit(1)
174+
175+ if has_packages :
176+ for idx, pkg in enumerate(data['packages']) :
177+ registry_type = pkg.get('registryType')
178+ identifier = pkg.get('identifier')
179+ if registry_type not in ['npm', 'pypi', 'nuget', 'oci', 'mcpb'] :
180+ print(f"✗ Package {idx} : Invalid registry type '{registry_type}'")
181+ sys.exit(1)
182+ print(f"✓ Package {idx} : {registry_type.upper()} -> {identifier}")
183+
184+ if has_remotes :
185+ for idx, remote in enumerate(data['remotes']) :
186+ remote_type = remote.get('type')
187+ url = remote.get('url')
188+ if remote_type not in ['sse', 'streamable-http'] :
189+ print(f"✗ Remote {idx} : Invalid transport type '{remote_type}'")
190+ sys.exit(1)
191+ print(f"✓ Remote {idx} : {remote_type} -> {url}")
192+
193+ print('✓ server.json validation passed (hybrid deployment)')
194+ PY
195+
196+ - name : Install MCP Publisher CLI
197+ if : steps.version-check.outputs.version_changed == 'true' && github.ref == 'refs/heads/main'
198+ run : |
199+ curl -L "https://github.com/modelcontextprotocol/registry/releases/download/v1.0.0/mcp-publisher_1.0.0_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
200+ chmod +x mcp-publisher
201+
202+ - name : Login to MCP Registry (GitHub OIDC)
203+ if : steps.version-check.outputs.version_changed == 'true' && github.ref == 'refs/heads/main'
204+ run : |
205+ ./mcp-publisher login github-oidc
206+
207+ - name : Publish to MCP Registry
208+ if : steps.version-check.outputs.version_changed == 'true' && github.ref == 'refs/heads/main'
209+ run : |
210+ ./mcp-publisher publish server.json
211+
212+ - name : Create GitHub Release
213+ if : steps.version-check.outputs.version_changed == 'true' && github.ref == 'refs/heads/main'
214+ uses : softprops/action-gh-release@v1
215+ with :
216+ tag_name : v${{ steps.version-check.outputs.current_version }}
217+ name : CodeAlive MCP v${{ steps.version-check.outputs.current_version }}
218+ body : |
219+ ## CodeAlive MCP Server v${{ steps.version-check.outputs.current_version }}
220+
221+ ### 🚀 Hybrid Deployment Options
222+
223+ **Docker Container (Local)**
224+ ```bash
225+ docker run --rm -i -e CODEALIVE_API_KEY=your-key ghcr.io/codealive-ai/codealive-mcp:v${{ steps.version-check.outputs.current_version }}
226+ ```
227+
228+ **MCP Registry**
229+ ```json
230+ {
231+ "name": "io.github.codealive-ai/codealive-mcp",
232+ "transport": {
233+ "type": "stdio",
234+ "command": "docker",
235+ "args": ["run", "--rm", "-i", "-e", "CODEALIVE_API_KEY=YOUR_API_KEY_HERE", "ghcr.io/codealive-ai/codealive-mcp:v${{ steps.version-check.outputs.current_version }}"]
236+ }
237+ }
238+ ```
239+
240+ **Remote HTTP (Zero Setup)**
241+ ```json
242+ {
243+ "transport": {
244+ "type": "http",
245+ "url": "https://mcp.codealive.ai/api"
246+ },
247+ "headers": {
248+ "Authorization": "Bearer your-codealive-api-key"
249+ }
250+ }
251+ ```
252+ draft : false
253+ prerelease : false
0 commit comments