ruby-sdk/test/mcp
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Koichi ITO a407cea086 Re-run OAuth flow on 403 insufficient_scope (step-up)
## Motivation and Context

The MCP authorization specification (2025-11-25 scope-selection-strategy)
and RFC 6750 Section 3.1 describe OAuth 2.0 step-up authentication:
the client already holds a valid access token, but the server returns a 403
with `WWW-Authenticate: Bearer error="insufficient_scope", scope="..."`
to demand a higher-privileged scope for a specific operation.
The client is expected to run a fresh authorization request with
the escalated scope and retry.

Until now the Ruby SDK only ran the OAuth flow on 401 responses.
A 403 surfaced unchanged as a `RequestHandlerError` and the operation
that needed the escalated scope failed permanently, even with a valid token
in storage. This lined up with the `auth/scope-step-up` conformance
scenario being listed in `conformance/expected_failures.yml`.

The change adds:

- `MCP::Client::HTTP#send_request` recognises a 403 carrying
  an `insufficient_scope` Bearer challenge as a step-up signal,
  re-runs a full authorization request with the scope from the challenge,
  and retries the original request. Retries are capped at
  one per `send_request` call so a server that repeatedly demands more
  scope cannot loop the transport.
- `run_step_up_flow!` deliberately bypasses the refresh-token path that
  `run_oauth_flow!` uses on 401. Refreshing would re-issue the same
  scope set the existing token already has and the server already
  rejected. Only a fresh `authorization_code` grant with the escalated
  scope can succeed.
- A plain 403 with no `insufficient_scope` challenge continues to
  surface as `RequestHandlerError` with `error_type: :forbidden`.
- `conformance/client.rb` invokes a tool for the `auth/scope-step-up`
  scenario; the scenario's authorization middleware only requires
  the escalated scope on `tools/call`, so the previous `tools/list`-only
  client could never trigger the second authorization.
- `auth/scope-step-up` is removed from `conformance/expected_failures.yml`.

Resolves #383.

## How Has This Been Tested?

New `HTTPOAuthTest` cases cover: the 403 insufficient_scope path
re-authorizes with the escalated scope from the challenge and retries
the original request with the new bearer token; a plain 403 without
the `insufficient_scope` challenge is surfaced as
`error_type: :forbidden` with no OAuth side effects; repeated
`insufficient_scope` responses retry at most once and then surface the 403;
a provider with a saved `refresh_token` still goes through
the full authorization request rather than exchanging the refresh token,
because the existing scope set is precisely what was rejected.

Conformance: `auth/scope-step-up` now passes 22/22 with no warnings.
`bundle exec rake test`, `bundle exec rake rubocop`, and
`bundle exec rake conformance` are all green.

## Breaking Changes

None. Behaviour on 401 is unchanged. A 403 without the `insufficient_scope`
Bearer challenge still raises the same `RequestHandlerError` as before.
The new retry only fires when both the WWW-Authenticate header signals step-up
and the transport was constructed with an OAuth provider.
2026-06-12 11:59:02 +09:00
..
client Re-run OAuth flow on 403 insufficient_scope (step-up) 2026-06-12 11:59:02 +09:00
prompt Add _meta field to resource, content, and result classes 2026-04-11 15:08:55 +09:00
resource Add _meta field to resource, content, and result classes 2026-04-11 15:08:55 +09:00
server Fix case-sensitive Accept header comparison 2026-05-23 21:36:27 +09:00
tool Cache Tool::Schema Validation to Avoid Re-validating Identical Schemas 2026-05-27 12:38:28 +09:00
annotations_test.rb Replace assert_empty with assert_equal using explicit literals 2026-02-24 16:09:34 +09:00
cancellation_test.rb Support notifications/cancelled per MCP specification 2026-05-03 04:02:22 +09:00
client_test.rb fix: preserve client tool output schemas 2026-05-11 21:44:26 -04:00
configuration_test.rb feat: add opt-in tool output validation 2026-05-12 16:20:43 -04:00
content_test.rb Add _meta field to resource, content, and result classes 2026-04-11 15:08:55 +09:00
icon_test.rb Replace assert_empty with assert_equal using explicit literals 2026-02-24 16:09:34 +09:00
instrumentation_test.rb Add around_request hook for request instrumentation 2026-04-11 13:54:47 +09:00
logging_message_notification_test.rb Add logging support 2026-01-26 22:23:46 +09:00
methods_test.rb Add roots/list and notifications/roots/list_changed support 2026-04-15 02:39:14 +09:00
progress_test.rb Auto-set server.transport in Transport#initialize 2026-04-09 12:08:55 +09:00
prompt_test.rb Fix Prompt#validate_arguments! crash when arguments are nil 2026-03-01 17:21:41 +09:00
resource_template_test.rb Add _meta field to resource, content, and result classes 2026-04-11 15:08:55 +09:00
resource_test.rb Add size field to MCP::Resource per MCP specification 2026-06-07 21:10:41 +09:00
server_cancellation_test.rb Support notifications/cancelled per MCP specification 2026-05-03 04:02:22 +09:00
server_context_test.rb Support server-to-client ping per MCP specification 2026-05-22 11:28:51 +09:00
server_elicitation_test.rb Support notifications/cancelled per MCP specification 2026-05-03 04:02:22 +09:00
server_notification_test.rb Auto-set server.transport in Transport#initialize 2026-04-09 12:08:55 +09:00
server_progress_test.rb Auto-set server.transport in Transport#initialize 2026-04-09 12:08:55 +09:00
server_roots_test.rb Add roots/list and notifications/roots/list_changed support 2026-04-15 02:39:14 +09:00
server_sampling_test.rb Support notifications/cancelled per MCP specification 2026-05-03 04:02:22 +09:00
server_session_ping_test.rb Support server-to-client ping per MCP specification 2026-05-22 11:28:51 +09:00
server_test.rb Reject duplicate initialize requests (#350) 2026-05-18 00:00:23 -04:00
string_utils_test.rb Fix ReDoS vulnerability in StringUtils#underscore 2026-01-09 01:14:52 +09:00
tool_test.rb Advertise JSON Schema 2020-12 dialect on emitted tool schemas 2026-05-11 13:44:30 +09:00
transport_test.rb Remove redundant require statements 2025-08-02 00:57:31 +09:00