From 75796e0c9880fd963e999400f3a18846d87900a2 Mon Sep 17 00:00:00 2001 From: olaservo Date: Sun, 9 Aug 2026 20:42:32 -0700 Subject: [PATCH] Cover the Ruby MCP client in the smoke tests CI installed no Ruby toolchain and the suite ran no Ruby example, so nothing under mcp-client-ruby or weather-server-ruby was exercised by a green build. A dependency change there -- #177 removes the base64 gem now that the anthropic gem declares it -- merges on the strength of a check that never resolved its Gemfile. The client is drivable without credentials: it connects before it looks for ANTHROPIC_API_KEY and exits 0 when there is none, the same shape the Python and TypeScript clients already rely on for their coverage. The Ruby weather server stays uncovered. The mcp gem's server does not emit the resultType field that protocol revision 2026-07-28 makes mandatory, so the test client rejects its responses; that is a fix for the gem rather than something an example can work around. The README said the gem could not negotiate the revision at all, which stopped being true at 1.1.0 -- it negotiates, then answers incompletely. Ruby 3.4 rather than the 3.2.0 floor the anthropic gem declares: 3.2 reached end of life in March 2026, and a gem's floor says what it tolerates, not what an example should tell people to run. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 7 +++++++ tests/README.md | 11 +++++++++-- tests/smoke-test.sh | 21 +++++++++++++++++---- tests/utils.sh | 13 +++++++++++++ 4 files changed, 46 insertions(+), 6 deletions(-) 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 +}