Commit graph quickstart-resources/tests/smoke-test.sh
Author SHA1 Message Date
Alex Akimov
470d1ba328
Fix the tool-use loop in the Go, Rust, and Ruby clients and add a tool-loop smoke test (#200)
Some checks failed
Smoke Tests / test (push) Has been cancelled
* Fix the tool-use loop in the Go, Rust, and Ruby clients and add a tool-loop smoke test

## Motivation and Context

Only the Python and TypeScript clients implemented a correct tool-use loop. The other three
had bugs that break ordinary multi-step requests:

- **Go, Rust, Ruby** did a single tool round and then called Claude once more *without*
  `tools`, so any request that needs a second tool call stalled. Go silently dropped
  `tool_use` blocks from that final response.
- **Ruby** called Claude once per `tool_use` block, each time with a single `tool_result`.
  Parallel tool calls produced a malformed message sequence and an API 400.
- **max_tokens** was 1000 in TypeScript and Ruby, 1024 in Go, and unset in Rust. Sonnet 5
  thinks adaptively, so 1000 tokens is regularly exhausted mid-thought. Python already used
  10000 with a comment explaining why.
- **Ruby** never forwarded MCP `isError` as `is_error`, so the model could not tell a failed
  tool call from a successful one. Rust cannot set the flag either (see below).
- **TypeScript** forwarded raw MCP content blocks to Anthropic. The two block shapes differ,
  so a non-text result would have failed.

The existing smoke test never noticed because it only checks that each client calls
`tools/list`. None of these bugs was reachable without an API key.

### What changed

All five clients now run the same loop: call tools until Claude answers without one, send
every `tool_result` of a turn in one user message, pass `tools` on every call, and stop
after `MAX_TOOL_TURNS = 10`. `max_tokens` is 10000 everywhere, with the same comment.
Python and TypeScript also keep the last response's text when the turn cap is hit; it used
to be dropped.

Rust keeps the `genai` crate. Its tool-response type has no `is_error` field, so an error
result is marked with an `Error:` prefix in the text and the limitation is commented.

### New smoke test

`tests/helpers/tool-loop-test.ts` starts a fake Anthropic Messages API on a loopback port,
points a client at it through `ANTHROPIC_BASE_URL`, pipes in one query, and checks every
request the client sends. It scripts two parallel tool calls, then a call to a tool the mock
server lacks (an `isError` result), then a final answer, and asserts that tools are passed
each time, `max_tokens` is 10000, all `tool_result`s arrive in one message with matching
ids, and the error is forwarded. `smoke-test.sh` runs it for Python, TypeScript, Go, and
Ruby.

The Rust client is excluded for now: `genai` reads no endpoint variable, and the client
negotiates protocol `2025-11-25`, under which the mock's array-rooted tool is refused at call
time. Both will be addressed in a follow-up that brings all examples to current SDK versions.

## How Has This Been Tested?

- `./tests/smoke-test.sh` passes for every example.
- The new tool-loop test passes all 19 checks for Python, TypeScript, Go, and Ruby.
- The Rust client was driven through the same scripted conversation with a temporary
  endpoint resolver (not committed) and passes.
- As a negative check, the Ruby client from `main` was run through the new test. It fails 10
  of 19 checks, on exactly the bugs listed above.
- Go and Ruby were built and run in `golang:1.25` and `ruby:3.4` containers.

## Breaking Changes

None. The clients' command lines, output format, and no-key behavior are unchanged.

## Types of changes

- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [x] Documentation update

## Checklist

- [x] I have read the [MCP Documentation](https://modelcontextprotocol.io)
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [x] I have added appropriate error handling
- [x] I have added or updated documentation as needed

## Additional context

Two pre-existing issues surfaced while testing and are left for a follow-up:

- The Rust client (rmcp 3.1.2) negotiates `2025-11-25`. The TypeScript mock server and the
  Python weather server both reject its calls to array-rooted tools under that revision.
  Even current rmcp defaults to `2025-11-25`, so the client must request `2026-07-28`
  explicitly.
- The Go SDK's tool-result helper sends content as a list of text blocks rather than a
  string. The API accepts both, so it is unchanged.

AI assistance: the analysis, code changes, test helper, and this description were prepared
partially manually, partially with Claude Code and reviewed by me.

* Improvements

* Address comments
2026-09-19 18:25:02 -07:00
Alex Akimov
3844c5b45e
Add smoke tests for Go and Rust clients (#196)
* Add smoke tests for Go and Rush clients

* Add smoke tests for Go and Rush clients
2026-09-15 15:14:06 -07:00
olaservo
cfda589119 Update the tests README for Ruby server coverage
The README still said the Ruby weather server was uncovered and described
the resultType gap as unfixable from an example. Both stopped being true
when the server test landed.

Also move the no-API-key comment below the Ruby server test. It describes
the client tests, and sat where it read as describing the server one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 18:03:57 -07:00
olaservo
d9005ebe03 Cover the Ruby weather server in the smoke tests
The server was excluded because the mcp gem did not stamp the resultType
field that 2026-07-28 requires, so the test client rejected its responses.
mcp 1.2.0 stamps it. Gemfile.lock is gitignored, so a fresh bundle install
resolves the existing ">= 1.1.0" to 1.2.0 with no manifest change.

Observed against the built test client: on 1.1.0 the run fails with
"missing required resultType"; on 1.2.0 it connects and lists both tools.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 17:11:37 -07:00
olaservo
ecc1138647 Pin ANTHROPIC_API_KEY empty in the client smoke tests
The client tests drive the path where no key is set: each client prints a
notice and exits. They did not isolate the variable, so a key exported in
the environment or sitting in a local .env sent the client into its
interactive chat loop instead. Observed with a .env in mcp-client-ruby: the
run reached "Query:" and waited on stdin.

Empty rather than unset, because dotenv skips a name already present in ENV.
All three clients check `nil? || empty?`, so an empty value takes the early
exit.

CI never hit this, having no key set. It affects local runs, and a run fed
input rather than /dev/null would issue real API requests.

Applied to the Python and TypeScript client tests too; all three share the
pattern.

Also correct the Ruby version comment: 3.3 is supported until March 2027, so
3.4 is not the oldest supported series. The tested version is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 19:44:09 -07:00
olaservo
75796e0c98 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 <noreply@anthropic.com>
2026-08-09 21:11:42 -07:00
Ola Hungerford
f587167452
Assert on structured output in the smoke tests and update to v2 (#163)
* Assert on structured output in the smoke tests

The smoke tests connected to each server, listed its tools and stopped there,
so they could not catch a broken structured result.

mcp-test-client now calls every tool that declares an outputSchema and checks
the answer against it: the result must carry structuredContent, and a tool
with an array-rooted schema must return a top-level JSON array rather than an
object wrapping one. Tools with no output schema are left alone, so this is
not a new requirement on any example. The client negotiates with mode "auto",
so it exercises servers of either protocol era. Tool calls reach the live NWS
API; an error result is reported as a skip rather than a failure.

mock-mcp-server moves to the low-level Server and advertises one array-rooted
and one object-rooted tool, with the schemas written out literally. A client
that compiles every declared outputSchema at connect time fails against the
array-rooted one if it assumes output schemas are always objects.

utils.sh no longer swallows build output. A failed build left no binary behind
and the test reported a downstream spawn ENOENT instead of the compiler error,
which is what makes a broken build hard to diagnose from CI logs; #143 is the
current example. check_dependency returns instead of exiting, so a missing
toolchain fails its own test and the suite still prints its summary. Binaries
resolve through a helper that accounts for the .exe suffix.

The test helpers move to the 2.0 MCP SDK packages, where the 2026-07-28
support lives; the examples themselves are untouched. Those require Node 20 or
newer, so CI moves off Node 18 to Node 24, the Active LTS - Node 20 reached
end of life in April 2026.

The Go examples and the Rust client stay uncovered here. Both clients abort
when no .env file is present, so that coverage lands with those examples.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Update to the stable 2.0 SDK packages and address review feedback

@modelcontextprotocol/{client,server} 2.0.0 shipped on 2026-07-27, so the
helpers move off 2.0.0-beta.5.

Review fixes:
- strip the UTF-8 BOM that preceded smoke-test.sh's shebang
- ensure_built and ensure_helpers_built return rather than exit, so one
  project failing to build no longer aborts the suite before the summary
- engines.node and the documented Node requirement go to 20, matching what
  the 2.0 SDK packages actually require
- close the client on the failure path, so a failing test cannot leave the
  spawned server running
- the README claimed Go and Rust client coverage that did not exist

Adds the Go weather server to the smoke test, with the Go toolchain CI was
missing. The Go and Rust clients stay uncovered: on main both abort without
a .env, so credential-free startup has to land in their own directories.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 19:42:57 +02:00
Jonathan Hefner
bd27d306c0
Implement smoke test for clients and servers (#76)
This commit adds `tests/smoke-test.sh`, which performs a smoke test of
all clients and servers. The smoke test tests that the clients and
servers run and that list tools is called.

This commit also adds a GitHub Actions workflow to run the smoke test on
all pull requests and pushes to main.

For practicality, this commit modifies the TypeScript and Python clients
to lazily initialize the Anthropic client, thus allowing tests to skip
setting an API key. This change also provides a friendly error when
users forget to set an API key.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alex Akimov <alexeyakimov@gmail.com>
2026-02-01 10:57:45 +01:00