fix: server.json top-level version now uses PyPI version (2.0.1) #275
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Quality Control | |
| on: | |
| push: | |
| branches: [ main, dev ] | |
| pull_request: | |
| branches: [ main, dev ] | |
| # Chronolog MCP tests are handled in dedicated workflow | |
| jobs: | |
| discover-mcps: | |
| name: Discover MCP Directories | |
| runs-on: ubuntu-latest | |
| outputs: | |
| mcps: ${{ steps.discover.outputs.mcps }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Discover MCP directories | |
| id: discover | |
| run: | | |
| # Find all directories in clio-kit-mcp-servers/ that have a pyproject.toml file | |
| # Exclude Chronolog (has dedicated test-chronomcp.yml workflow) | |
| blacklisted_mcps="chronolog|Chronolog" | |
| mcps_list=$(find clio-kit-mcp-servers -maxdepth 1 -type d -exec test -f {}/pyproject.toml \; -print | sed 's|clio-kit-mcp-servers/||' | sort) | |
| # Filter out blacklisted MCPs | |
| mcps_list=$(echo "$mcps_list" | grep -v -E "^($blacklisted_mcps)$") | |
| mcps=$(echo "$mcps_list" | jq -R -s -c 'split("\n")[:-1]') | |
| echo "mcps=$mcps" >> $GITHUB_OUTPUT | |
| echo "Found MCPs: $mcps" | |
| ruff: | |
| name: Ruff - ${{ matrix.mcp }} | |
| runs-on: ubuntu-latest | |
| needs: discover-mcps | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| mcp: ${{ fromJson(needs.discover-mcps.outputs.mcps) }} | |
| max-parallel: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: 'recursive' | |
| - uses: ./.github/actions/setup-mcp | |
| with: | |
| mcp: ${{ matrix.mcp }} | |
| dev-packages: ruff | |
| - name: Run Ruff linter on ${{ matrix.mcp }} | |
| run: | | |
| cd clio-kit-mcp-servers/${{ matrix.mcp }} | |
| uv run ruff check --output-format=github . | |
| - name: Run Ruff formatter check on ${{ matrix.mcp }} | |
| run: | | |
| cd clio-kit-mcp-servers/${{ matrix.mcp }} | |
| uv run ruff format --check . | |
| mypy: | |
| name: MyPy - ${{ matrix.mcp }} | |
| runs-on: ubuntu-latest | |
| needs: discover-mcps | |
| continue-on-error: true # Don't fail workflow on type errors | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| mcp: ${{ fromJson(needs.discover-mcps.outputs.mcps) }} | |
| max-parallel: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: 'recursive' | |
| - uses: ./.github/actions/setup-mcp | |
| with: | |
| mcp: ${{ matrix.mcp }} | |
| dev-packages: mypy | |
| - name: Run MyPy on ${{ matrix.mcp }} | |
| run: | | |
| cd clio-kit-mcp-servers/${{ matrix.mcp }} | |
| if [ -d src ]; then | |
| uv run mypy src/ --ignore-missing-imports --show-error-codes --no-error-summary | |
| else | |
| echo "No src directory found for ${{ matrix.mcp }}" | |
| fi | |
| test: | |
| name: Test - ${{ matrix.mcp }} (py${{ matrix.python-version }}) | |
| runs-on: ubuntu-latest | |
| needs: discover-mcps | |
| continue-on-error: ${{ matrix.python-version != '3.12' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.10", "3.11", "3.12"] | |
| mcp: ${{ fromJson(needs.discover-mcps.outputs.mcps) }} | |
| max-parallel: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: 'recursive' | |
| - uses: ./.github/actions/setup-mcp | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| mcp: ${{ matrix.mcp }} | |
| dev-packages: "pytest pytest-cov" | |
| - name: Run tests for ${{ matrix.mcp }} | |
| run: | | |
| cd clio-kit-mcp-servers/${{ matrix.mcp }} | |
| if [ -d tests ]; then | |
| uv run pytest tests/ -v --tb=short \ | |
| --cov=src --cov-report=xml --cov-report=html --cov-report=term \ | |
| --junitxml=junit.xml -o junit_family=legacy | |
| else | |
| echo "No tests directory found for ${{ matrix.mcp }}" | |
| fi | |
| - name: Upload coverage to Codecov | |
| if: matrix.python-version == '3.12' | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| file: clio-kit-mcp-servers/${{ matrix.mcp }}/coverage.xml | |
| flags: ${{ matrix.mcp }} | |
| name: codecov-${{ matrix.mcp }} | |
| fail_ci_if_error: false | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| - name: Upload test results to Codecov | |
| if: matrix.python-version == '3.12' && !cancelled() | |
| uses: codecov/test-results-action@v1 | |
| with: | |
| file: clio-kit-mcp-servers/${{ matrix.mcp }}/junit.xml | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| - name: Upload coverage HTML report | |
| if: matrix.python-version == '3.12' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-html-${{ matrix.mcp }} | |
| path: clio-kit-mcp-servers/${{ matrix.mcp }}/htmlcov/ | |
| if-no-files-found: ignore | |
| security: | |
| name: Security Audit - ${{ matrix.mcp }} | |
| runs-on: ubuntu-latest | |
| needs: discover-mcps | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| mcp: ${{ fromJson(needs.discover-mcps.outputs.mcps) }} | |
| max-parallel: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: 'recursive' | |
| - uses: ./.github/actions/setup-mcp | |
| with: | |
| mcp: ${{ matrix.mcp }} | |
| dev-packages: pip-audit | |
| - name: Run security audit for ${{ matrix.mcp }} | |
| run: | | |
| cd clio-kit-mcp-servers/${{ matrix.mcp }} | |
| uv run pip-audit | |
| validate-fastmcp: | |
| name: Validate FastMCP - ${{ matrix.mcp }} | |
| runs-on: ubuntu-latest | |
| needs: discover-mcps | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| mcp: ${{ fromJson(needs.discover-mcps.outputs.mcps) }} | |
| max-parallel: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: 'recursive' | |
| - uses: ./.github/actions/setup-mcp | |
| with: | |
| mcp: ${{ matrix.mcp }} | |
| - name: Validate FastMCP 3.0 compliance for ${{ matrix.mcp }} | |
| run: | | |
| cd clio-kit-mcp-servers/${{ matrix.mcp }} | |
| uv run python ../../scripts/validate_fastmcp.py | |
| validate-server-json: | |
| name: Validate server.json - ${{ matrix.mcp }} | |
| runs-on: ubuntu-latest | |
| needs: discover-mcps | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| mcp: ${{ fromJson(needs.discover-mcps.outputs.mcps) }} | |
| max-parallel: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: 'recursive' | |
| - uses: ./.github/actions/setup-mcp | |
| with: | |
| mcp: ${{ matrix.mcp }} | |
| - name: Regenerate and diff server.json for ${{ matrix.mcp }} | |
| run: | | |
| cd clio-kit-mcp-servers/${{ matrix.mcp }} | |
| # Extract live metadata | |
| LIVE=$(uv run python ../../scripts/extract_mcp_metadata.py) | |
| # Compare tool/resource/prompt counts from live metadata vs committed server.json | |
| if [ ! -f server.json ]; then | |
| echo "FAIL: server.json not found for ${{ matrix.mcp }}" | |
| exit 1 | |
| fi | |
| # Validate JSON is well-formed | |
| python3 -c "import json, sys; json.load(open('server.json'))" || { | |
| echo "FAIL: server.json is not valid JSON" | |
| exit 1 | |
| } | |
| # Verify tool names match between live metadata and server.json | |
| LIVE_TOOLS=$(echo "$LIVE" | python3 -c "import json,sys; d=json.load(sys.stdin); print('\n'.join(sorted(t['name'] for t in d.get('tools',[]))))") | |
| JSON_TOOLS=$(python3 -c "import json; d=json.load(open('server.json')); print('\n'.join(sorted(t['name'] for t in d.get('tools',[]))))") | |
| if [ "$LIVE_TOOLS" != "$JSON_TOOLS" ]; then | |
| echo "FAIL: server.json tools out of sync with live metadata" | |
| echo "--- Live tools ---" | |
| echo "$LIVE_TOOLS" | |
| echo "--- server.json tools ---" | |
| echo "$JSON_TOOLS" | |
| exit 1 | |
| fi | |
| echo "PASS: server.json is in sync for ${{ matrix.mcp }}" |