pmcp 2.2.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: ""

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: |
        for example in examples/*.rs; do
          if [[ -f "$example" ]]; then
            example_name=$(basename "$example" .rs)
            # Skip examples that require special feature flags
            if [[ "$example_name" == "09_authentication" ]]; then
              echo "Skipping example: $example_name (requires authentication_example feature)"
              continue
            fi
            if [[ "$example_name" == "10_progress_notifications" ]]; then
              echo "Skipping example: $example_name (requires progress_example feature)"
              continue
            fi
            if [[ "$example_name" == "11_request_cancellation" ]]; then
              echo "Skipping example: $example_name (requires cancellation_example feature)"
              continue
            fi
            if [[ "$example_name" == "13_websocket_transport" ]] || \
               [[ "$example_name" == "27_websocket_server_enhanced" ]]; then
              echo "Skipping example: $example_name (requires websocket feature)"
              continue
            fi
            if [[ "$example_name" == "18_resource_watcher" ]]; then
              echo "Skipping example: $example_name (requires resource-watcher feature)"
              continue
            fi
            if [[ "$example_name" == "22_streamable_http_server_stateful" ]] || \
               [[ "$example_name" == "23_streamable_http_server_stateless" ]] || \
               [[ "$example_name" == "24_streamable_http_client" ]] || \
               [[ "$example_name" == "55_server_middleware" ]]; then
              echo "Skipping example: $example_name (requires streamable-http feature)"
              continue
            fi
            if [[ "$example_name" == "32_typed_tools" ]] || \
               [[ "$example_name" == "33_advanced_typed_tools" ]] || \
               [[ "$example_name" == "34_serverbuilder_typed" ]] || \
               [[ "$example_name" == "35_wasm_typed_tools" ]] || \
               [[ "$example_name" == "36_typed_tool_v2_example" ]] || \
               [[ "$example_name" == "37_description_variants_example" ]] || \
               [[ "$example_name" == "53_typed_tools_workflow_integration" ]] || \
               [[ "$example_name" == "conference_venue_map" ]] || \
               [[ "$example_name" == "hotel_gallery" ]]; then
              echo "Skipping example: $example_name (requires schema-generation feature)"
              continue
            fi
            if [[ "$example_name" == "20_oidc_discovery" ]]; then
              echo "Skipping example: $example_name (requires http-client feature)"
              continue
            fi
            if [[ "$example_name" == "28_sse_optimized" ]]; then
              echo "Skipping example: $example_name (requires sse feature)"
              continue
            fi
            if [[ "$example_name" == "29_connection_pool" ]] || \
               [[ "$example_name" == "30_enhanced_middleware" ]] || \
               [[ "$example_name" == "31_advanced_error_recovery" ]] || \
               [[ "$example_name" == "49_tool_with_sampling_server" ]] || \
               [[ "$example_name" == "63_mcp_tool_macro" ]] || \
               [[ "$example_name" == "64_mcp_prompt_macro" ]]; then
              echo "Skipping example: $example_name (requires full feature)"
              continue
            fi
            echo "Checking example: $example_name"
            # Most examples need at least http feature for tokio
            cargo check --example "$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
    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
          cargo install 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: Run quality gate
      run: make quality-gate

    - 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.82)
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6
    
    - name: Install Rust 1.82
      uses: dtolnay/rust-toolchain@1.82
    
    - 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."