Skip to content

feat: integrate clio-agentic-search into monorepo #276

feat: integrate clio-agentic-search into monorepo

feat: integrate clio-agentic-search into monorepo #276

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 }}"
# ── clio-agentic-search (standalone service, not an MCP server) ──
agentic-search-ruff:
name: Ruff - clio-agentic-search
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
with:
version: "latest"
- run: uv python install 3.12
- name: Install dependencies
run: |
cd clio-agentic-search
uv sync --all-extras --dev
- name: Ruff lint
run: |
cd clio-agentic-search
uv run ruff check --output-format=github .
- name: Ruff format check
run: |
cd clio-agentic-search
uv run ruff format --check .
agentic-search-mypy:
name: MyPy - clio-agentic-search
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
with:
version: "latest"
- run: uv python install 3.12
- name: Install dependencies
run: |
cd clio-agentic-search
uv sync --all-extras --dev
- name: Run MyPy
run: |
cd clio-agentic-search
uv run mypy src/ --ignore-missing-imports --show-error-codes --no-error-summary
agentic-search-test:
name: Test - clio-agentic-search (py${{ matrix.python-version }})
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.python-version != '3.12' }}
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
with:
version: "latest"
- run: uv python install ${{ matrix.python-version }}
- name: Install dependencies
run: |
cd clio-agentic-search
uv sync --all-extras --dev
- name: Run tests
run: |
cd clio-agentic-search
uv run pytest --ignore=tests/benchmarks -v --tb=short