pmcp 2.9.0

High-quality Rust SDK for Model Context Protocol (MCP) with full TypeScript SDK compatibility
Documentation
name: CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1
  # Override .cargo/config.toml target-cpu=native to avoid SIGILL on CI runners
  RUSTFLAGS: ""
  # Pin matches .github/workflows/quality-badges.yml so badge and gate stay aligned (Phase 75 D-09).
  PMAT_VERSION: "3.15.0"

jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6

    - name: Free Disk Space
      uses: jlumbroso/free-disk-space@main
      with:
        # Remove large packages we don't need
        tool-cache: false
        android: true
        dotnet: true
        haskell: true
        large-packages: true
        docker-images: true
        swap-storage: true

    - name: Install Rust
      uses: dtolnay/rust-toolchain@stable
      with:
        components: rustfmt, clippy, llvm-tools-preview
    
    - name: Cache cargo
      uses: actions/cache@v5
      with:
        path: |
          ~/.cargo/bin/
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
          target/
        key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
    
    - name: Install cargo-llvm-cov
      run: |
        if ! command -v cargo-llvm-cov &> /dev/null; then
          cargo install cargo-llvm-cov
        fi
    
    - name: Format check
      run: cargo fmt --all -- --check
    
    - name: Clippy
      run: |
        cargo clippy --all-targets --all-features -- \
          -D warnings \
          -A clippy::module_name_repetitions \
          -A clippy::must_use_candidate \
          -A clippy::missing_errors_doc \
          -A clippy::missing_const_for_fn \
          -A clippy::return_self_not_must_use \
          -A clippy::missing_fields_in_debug \
          -A clippy::uninlined_format_args \
          -A clippy::if_not_else \
          -A clippy::result_large_err \
          -A clippy::multiple_crate_versions \
          -A clippy::implicit_hasher \
          -A clippy::unused_async \
          -A clippy::cast_lossless \
          -A clippy::redundant_clone \
          -A clippy::redundant_closure_for_method_calls \
          -A clippy::significant_drop_tightening \
          -A clippy::missing_panics_doc \
          -A clippy::cast_possible_truncation \
          -A clippy::cast_precision_loss \
          -A clippy::option_if_let_else \
          -A clippy::derive_partial_eq_without_eq \
          -A clippy::redundant_else \
          -A clippy::match_same_arms
    
    - name: Build
      run: cargo build --all-features --verbose
    
    - name: Run tests
      run: cargo test --all-features --verbose -- --test-threads=1
    
    - name: Run doctests
      run: cargo test --doc --all-features --verbose
    
    - name: Check examples
      run: |
        # Build every registered example with its declared required-features.
        # Reading required-features from cargo metadata keeps this in sync with
        # Cargo.toml automatically, so future renames or feature changes don't
        # require workflow edits.
        cargo metadata --no-deps --format-version 1 \
          | python3 -c '
        import json, sys
        meta = json.load(sys.stdin)
        pkg = next(p for p in meta["packages"] if p["name"] == "pmcp")
        for t in pkg["targets"]:
            if "example" in t["kind"]:
                feats = ",".join(t.get("required-features") or [])
                print(f"{t[\"name\"]}|{feats}")
        ' | while IFS='|' read -r name feats; do
            if [[ -n "$feats" ]]; then
              echo "Checking example: $name (features: $feats)"
              cargo check --example "$name" --features "$feats"
            else
              echo "Checking example: $name (no required features)"
              cargo check --example "$name" --features http
            fi
          done

    - name: Clean up before coverage
      run: |
        echo "Disk usage before cleanup:"
        df -h
        echo "Cleaning cargo build artifacts..."
        cargo clean
        echo "Disk usage after cleanup:"
        df -h

    - name: Run coverage
      run: cargo llvm-cov --all-features --lcov --output-path lcov.info

  feature-flags:
    name: Feature Flag Verification
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6

    - name: Install Rust
      uses: dtolnay/rust-toolchain@stable
      with:
        components: clippy

    - name: Cache cargo
      uses: actions/cache@v5
      with:
        path: |
          ~/.cargo/bin/
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
          target/
        key: ${{ runner.os }}-cargo-feature-flags-${{ hashFiles('**/Cargo.lock') }}

    - name: Verify feature flag combinations
      run: make test-feature-flags

  quality-gate:
    name: Quality Gate
    runs-on: ubuntu-latest
    # Phase 75 Wave 5 / D-07: when adding PMAT here, pin via `cargo install pmat --version =3.15.0 --locked`
    # The pin matches .github/workflows/quality-badges.yml so badge and gate
    # semantics stay aligned. See .planning/phases/75-fix-pmat-issues/
    # 75-W0-SPIKE-RESULTS.md for the empirical justification (D-09).
    steps:
    - uses: actions/checkout@v6

    - name: Free Disk Space
      uses: jlumbroso/free-disk-space@main
      with:
        # Remove large packages we don't need
        tool-cache: false
        android: true
        dotnet: true
        haskell: true
        large-packages: true
        docker-images: true
        swap-storage: true

    - name: Install Rust
      uses: dtolnay/rust-toolchain@stable
      with:
        components: rustfmt, clippy
    
    - name: Cache cargo
      uses: actions/cache@v5
      with:
        path: |
          ~/.cargo/bin/
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
        key: ${{ runner.os }}-cargo-quality-${{ hashFiles('**/Cargo.lock') }}

    - name: Install quality tools
      run: |
        if ! command -v cargo-llvm-cov &> /dev/null; then
          cargo install cargo-llvm-cov
        fi
        if ! command -v cargo-nextest &> /dev/null; then
          # Why: cargo-nextest 0.9.133+ embeds a `locked-tripwire` build-time
          # compile_error! that aborts unless `--locked` is passed. Without
          # this flag CI fails with "Nextest does not support being installed
          # without --locked." See https://nexte.st/docs/installation/from-source/
          cargo install --locked cargo-nextest
        fi
        # Force install latest cargo-audit to support CVSS 4.0
        cargo install cargo-audit --force

    - name: Check disk space before quality gate
      run: df -h

    - name: Check rustdoc zero-warnings
      run: make doc-check

    - name: Run quality gate
      run: make quality-gate

    - name: Install PMAT (cache-aware; reused across runs via ~/.cargo/bin)
      run: |
        if ! command -v pmat &>/dev/null || ! pmat --version | grep -qE "^pmat ${PMAT_VERSION}$"; then
          cargo install pmat --version "=${PMAT_VERSION}" --locked
        fi

    - name: Run PMAT quality gate (complexity only — see CONTEXT.md D-01 / D-11-B)
      run: pmat quality-gate --fail-on-violation --checks complexity

    - name: Check disk space after quality gate
      run: df -h

  benchmarks:
    name: Benchmarks
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6
    
    - name: Install Rust
      uses: dtolnay/rust-toolchain@stable
    
    - name: Cache cargo
      uses: actions/cache@v5
      with:
        path: |
          ~/.cargo/bin/
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
          target/
        key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
    
    - name: Run benchmarks
      run: cargo bench --all-features --no-run

  msrv:
    name: Minimum Supported Rust Version (1.91)
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6

    - name: Install Rust 1.91
      uses: dtolnay/rust-toolchain@1.91
    
    - name: Cache cargo
      uses: actions/cache@v5
      with:
        path: |
          ~/.cargo/bin/
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
          target/
        key: ${{ runner.os }}-cargo-msrv-${{ hashFiles('**/Cargo.lock') }}
    
    - name: Check MSRV
      run: cargo check --all-features

  # Unified gate — single required check for org ruleset.
  # Reports as "gate" to match the org ruleset's required status check.
  gate:
    runs-on: ubuntu-latest
    needs: [test, quality-gate]
    if: always()
    steps:
      - name: Evaluate required checks
        env:
          TEST_RESULT: ${{ needs.test.result }}
          QG_RESULT: ${{ needs.quality-gate.result }}
        run: |
          if [[ "$TEST_RESULT" != "success" ]] || \
             [[ "$QG_RESULT" != "success" ]]; then
            echo "Required checks failed: test=$TEST_RESULT, quality-gate=$QG_RESULT"
            exit 1
          fi
          echo "All required checks passed."