Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,15 @@ jobs:
with:
go-version: "1.25"

# 3.4 is the oldest series still supported: 3.2 reached end of life in
# March 2026, so the anthropic gem's own `>= 3.2.0` floor is a statement
# about what it tolerates, not a version to install here. Gems are
# installed by the smoke test rather than by `bundler-cache`, which wants
# a Gemfile at the checkout root; the two Ruby examples keep their own.
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: "3.4"

- name: Run smoke tests
run: ./tests/smoke-test.sh
3 changes: 1 addition & 2 deletions mcp-client-ruby/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

source "https://rubygems.org"

gem "anthropic"
gem "base64"
gem "anthropic", '>= 1.60.0'
gem "dotenv"
gem "mcp", '>= 0.15.0'
11 changes: 9 additions & 2 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
28 changes: 24 additions & 4 deletions tests/smoke-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,39 @@ test_mcp_client_typescript() {
node "${client_dir}/build/index.js" "${MOCK_SERVER}" >/dev/null 2>&1
}

# Test: Ruby MCP client
#
# The client connects to the server before it looks for ANTHROPIC_API_KEY and
# exits 0 when there is none, so the connection and tool listing are exercised
# without credentials -- the same shape as the Python client above.
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.
#
# The Ruby weather server is not covered either, for an upstream reason rather
# than a local one: 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. The Ruby client is covered, because it is the gem's client
# code that runs there and the server it is pointed at is the mock.
print_header "Running smoke tests"
run_test "weather-server-python" test_weather_server_python
run_test "weather-server-typescript" test_weather_server_typescript
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 ""
Expand Down
19 changes: 19 additions & 0 deletions tests/utils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,22 @@ 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.
#
# Separate from ensure_built because there is nothing on disk to test for: a
# Ruby example has no build step and Gemfile.lock is gitignored, so the checks
# ensure_built makes -- does this artefact exist yet -- have no equivalent.
# `bundle check` asks Bundler itself whether the Gemfile is already satisfied,
# which is the only reliable form of the question.
#
# Returns rather than exits, as ensure_built does, so a failure to install
# fails only the test that needed the gems.
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
}
Loading