diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd883ef6..0706ae3a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,5 +41,12 @@ jobs: with: go-version: "1.25" + # 3.4 is the oldest series still supported; 3.2 reached end of life in + # March 2026. + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.4" + - name: Run smoke tests run: ./tests/smoke-test.sh diff --git a/tests/README.md b/tests/README.md index 1a769729..2f0fa396 100644 --- a/tests/README.md +++ b/tests/README.md @@ -7,9 +7,11 @@ This directory contains smoke tests for the MCP quickstart examples. These tests The smoke tests verify: - **Servers**: Each weather server (Python, TypeScript, Rust, Go) can start, respond to MCP protocol requests, and honour the output schemas it advertises -- **Clients**: The Python and TypeScript MCP clients can connect to a mock server and list tools +- **Clients**: The Python, TypeScript and Ruby MCP clients can connect to a mock server and list tools -The Go and Rust clients are not covered here: on `main` both abort when no `.env` file is present, so they cannot be driven without credentials. Making them start credential-free is a change in their own directories, so their coverage lands with those changes rather than here. The Ruby examples are not covered either — the `mcp` gem cannot negotiate protocol revision `2026-07-28`. +The Go and Rust clients are not covered here: on `main` both abort when no `.env` file is present, so they cannot be driven without credentials. Making them start credential-free is a change in their own directories, so their coverage lands with those changes rather than here. + +The Ruby **weather server** is not covered, for an upstream reason rather than a local one. The `mcp` gem negotiates `2026-07-28`, but its server never emits the `resultType` field that revision makes mandatory, so the test client rejects its responses — including `tools/list`. That is a fix for the gem, not something an example can work around. The Ruby **client** is covered: there it is the gem's client code that runs, and the server it is pointed at is the mock. ## Structured content @@ -37,6 +39,8 @@ Tool calls reach the live NWS API. When it is unreachable the tools return an er - **Rust** stable - **Cargo** (for Rust builds) - **Go** 1.25+ +- **Ruby** 3.4+ (3.2 and 3.3 satisfy the gems, but 3.2 is past end of life) +- **Bundler** (ships with Ruby 3.x) ## How It Works @@ -111,6 +115,9 @@ nvm install 24 # Rust curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# Ruby (via rbenv) +rbenv install 3.4 ``` ## Adding New Tests diff --git a/tests/smoke-test.sh b/tests/smoke-test.sh index acb039b5..bb60d788 100755 --- a/tests/smoke-test.sh +++ b/tests/smoke-test.sh @@ -100,12 +100,24 @@ test_mcp_client_typescript() { node "${client_dir}/build/index.js" "${MOCK_SERVER}" >/dev/null 2>&1 } +# Test: Ruby MCP client +test_mcp_client_ruby() { + check_dependency ruby || return 1 + check_dependency bundle || return 1 + local client_dir="${PROJECT_ROOT}/mcp-client-ruby" + ensure_bundled "${client_dir}" || return 1 + (cd "${client_dir}" && bundle exec ruby client.rb "${MOCK_SERVER}") >/dev/null 2>&1 +} + # Run all tests # -# All four servers are covered. The Go and Rust clients are not: on main both -# abort when no .env file is present, so they cannot be driven without -# credentials. Making them start credential-free is a change in their own -# directories, so their coverage lands with those changes rather than here. +# The Go and Rust clients are not covered: on main both abort when no .env file +# is present, so they cannot be driven without credentials. Making them start +# credential-free is a change in their own directories, so their coverage lands +# with those changes rather than here. +# +# Nor is the Ruby weather server: the mcp gem does not stamp the resultType +# field that 2026-07-28 requires, so the test client rejects its responses. print_header "Running smoke tests" run_test "weather-server-python" test_weather_server_python run_test "weather-server-typescript" test_weather_server_typescript @@ -113,6 +125,7 @@ run_test "weather-server-rust" test_weather_server_rust run_test "weather-server-go" test_weather_server_go run_test "mcp-client-python" test_mcp_client_python run_test "mcp-client-typescript" test_mcp_client_typescript +run_test "mcp-client-ruby" test_mcp_client_ruby # Print summary echo "" diff --git a/tests/utils.sh b/tests/utils.sh index 7423eae7..89081f68 100644 --- a/tests/utils.sh +++ b/tests/utils.sh @@ -138,3 +138,16 @@ ensure_built() { run_build "go build in ${dir}" go build -o "server$(exe_suffix)" . || return 1 fi } + +# Ensure a Ruby project directory has its gems installed. +# +# Unlike ensure_built there is no artefact to test for -- a Ruby example has no +# build step and Gemfile.lock is gitignored -- so ask Bundler directly. +ensure_bundled() { + local dir=$1 + cd "${dir}" || return 1 + + if ! bundle check >/dev/null 2>&1; then + run_build "bundle install in ${dir}" bundle install || return 1 + fi +}