Track MCP LogoTrack MCP
Track MCP LogoTrack MCP

The world's largest repository of Model Context Protocol servers. Discover, explore, and submit MCP tools.

Product

  • Categories
  • Top MCP
  • New & Updated
  • Submit MCP

Company

  • About

Legal

  • Privacy Policy
  • Terms of Service
  • Cookie Policy

© 2026 TrackMCP. All rights reserved.

Built with ❤️ by Krishna Goyal

    Ruby Sdk

    The official Ruby SDK for the Model Context Protocol. Maintained in collaboration with Shopify. Trusted by 600+ developers.

    604 stars
    Ruby
    Updated Nov 4, 2025

    Table of Contents

    • Installation
    • Building an MCP Server
    • Key Features
    • Supported Methods
    • Usage
    • Stdio Transport
    • Streamable HTTP Transport
    • Configuration
    • Server Context and Configuration Block Data
    • server_context
    • Request-specific _meta Parameter
    • Configuration Block Data
    • Server Protocol Version
    • Exception Reporting
    • Tools
    • Tool Annotations
    • Tool Output Schemas
    • Tool Responses with Structured Content
    • Tool Responses with Errors
    • Prompts
    • Key Components
    • Usage
    • Resources
    • Reading Resources
    • Resource Templates
    • Roots
    • Resource Subscriptions
    • Sampling
    • Notifications
    • Notification Methods
    • Session Scoping
    • Notification Format
    • Cancellation
    • Server-Side: Handlers that Check for Cancellation
    • Nested Server-to-Client Requests Are Cancelled Automatically
    • Ping
    • Server-Side
    • Client-Side
    • Progress
    • How Progress Works
    • Server-Side: Tool with Progress
    • Completions
    • Elicitation
    • Capabilities
    • Using Elicitation in Tools
    • Form Mode
    • URL Mode
    • URLElicitationRequiredError
    • Logging
    • Log Levels
    • How Logging Works
    • Transport Support
    • Usage Example
    • Pagination
    • Server-Side: Enabling Pagination
    • Client-Side: Iterating Pages
    • Fetching the Complete Collection
    • Advanced
    • Custom Methods
    • Building an MCP Client
    • Transport Layer Interface
    • Stdio Transport Layer
    • HTTP Transport Layer
    • HTTP Authorization
    • OAuth 2.1 Authorization
    • Customizing the Faraday Connection
    • Tool Objects
    • Conformance Testing
    • Documentation

    Table of Contents

    • Installation
    • Building an MCP Server
    • Key Features
    • Supported Methods
    • Usage
    • Stdio Transport
    • Streamable HTTP Transport
    • Configuration
    • Server Context and Configuration Block Data
    • server_context
    • Request-specific _meta Parameter
    • Configuration Block Data
    • Server Protocol Version
    • Exception Reporting
    • Tools
    • Tool Annotations
    • Tool Output Schemas
    • Tool Responses with Structured Content
    • Tool Responses with Errors
    • Prompts
    • Key Components
    • Usage
    • Resources
    • Reading Resources
    • Resource Templates
    • Roots
    • Resource Subscriptions
    • Sampling
    • Notifications
    • Notification Methods
    • Session Scoping
    • Notification Format
    • Cancellation
    • Server-Side: Handlers that Check for Cancellation
    • Nested Server-to-Client Requests Are Cancelled Automatically
    • Ping
    • Server-Side
    • Client-Side
    • Progress
    • How Progress Works
    • Server-Side: Tool with Progress
    • Completions
    • Elicitation
    • Capabilities
    • Using Elicitation in Tools
    • Form Mode
    • URL Mode
    • URLElicitationRequiredError
    • Logging
    • Log Levels
    • How Logging Works
    • Transport Support
    • Usage Example
    • Pagination
    • Server-Side: Enabling Pagination
    • Client-Side: Iterating Pages
    • Fetching the Complete Collection
    • Advanced
    • Custom Methods
    • Building an MCP Client
    • Transport Layer Interface
    • Stdio Transport Layer
    • HTTP Transport Layer
    • HTTP Authorization
    • OAuth 2.1 Authorization
    • Customizing the Faraday Connection
    • Tool Objects
    • Conformance Testing
    • Documentation

    Documentation

    MCP Ruby SDK Gem Version Apache 2.0 licensed CI

    The official Ruby SDK for Model Context Protocol servers and clients.

    Installation

    Add this line to your application's Gemfile:

    ruby
    gem 'mcp'

    And then execute:

    console
    $ bundle install

    Or install it yourself as:

    console
    $ gem install mcp

    You may need to add additional dependencies depending on which features you wish to access.

    Building an MCP Server

    The MCP::Server class is the core component that handles JSON-RPC requests and responses.

    It implements the Model Context Protocol specification, handling model context requests and responses.

    Key Features

    • Implements JSON-RPC 2.0 message handling
    • Supports protocol initialization and capability negotiation
    • Manages tool registration and invocation
    • Supports prompt registration and execution
    • Supports resource registration and retrieval
    • Supports stdio & Streamable HTTP (including SSE) transports
    • Supports notifications for list changes (tools, prompts, resources)
    • Supports roots (server-to-client filesystem boundary queries)
    • Supports sampling (server-to-client LLM completion requests)
    • Supports cursor-based pagination for list operations
    • Supports server-side cancellation of in-flight requests (notifications/cancelled)

    Supported Methods

    • initialize - Initializes the protocol and returns server capabilities
    • ping - Simple health check
    • logging/setLevel - Configures the minimum log level for the server
    • tools/list - Lists all registered tools and their schemas
    • tools/call - Invokes a specific tool with provided arguments
    • prompts/list - Lists all registered prompts and their schemas
    • prompts/get - Retrieves a specific prompt by name
    • resources/list - Lists all registered resources and their schemas
    • resources/read - Retrieves a specific resource by name
    • resources/templates/list - Lists all registered resource templates and their schemas
    • resources/subscribe - Subscribes to updates for a specific resource
    • resources/unsubscribe - Unsubscribes from updates for a specific resource
    • completion/complete - Returns autocompletion suggestions for prompt arguments and resource URIs
    • roots/list - Requests filesystem roots from the client (server-to-client)
    • sampling/createMessage - Requests LLM completion from the client (server-to-client)
    • elicitation/create - Requests user input from the client (server-to-client)

    Usage

    Stdio Transport

    If you want to build a local command-line application, you can use the stdio transport:

    ruby
    require "mcp"
    
    # Create a simple tool
    class ExampleTool  [!IMPORTANT]
    > `MCP::Server::Transports::StreamableHTTPTransport` stores session and SSE stream state in memory,
    > so it must run in a single process. Use a single-process server (e.g., Puma with `workers 0`).
    > Multi-process configurations (Unicorn, or Puma with `workers > 0`) fork separate processes that
    > do not share memory, which breaks session management and SSE connections.
    >
    > When running multiple server instances behind a load balancer, configure your load balancer to use
    > sticky sessions (session affinity) so that requests with the same `Mcp-Session-Id` header are always
    > routed to the same instance.
    >
    > Stateless mode (`stateless: true`) does not use sessions and works with any server configuration.
    
    ##### Rails (mount)
    
    `StreamableHTTPTransport` is a Rack app that can be mounted directly in Rails routes:

    config/routes.rb

    server = MCP::Server.new(

    name: "my_server",

    title: "Example Server Display Name",

    version: "1.0.0",

    instructions: "Use the tools of this server as a last resort",

    tools: [SomeTool, AnotherTool],

    prompts: [MyPrompt],

    )

    transport = MCP::Server::Transports::StreamableHTTPTransport.new(server)

    Rails.application.routes.draw do

    mount transport => "/mcp"

    end

    code
    `mount` directs all HTTP methods on `/mcp` to the transport. `StreamableHTTPTransport` internally dispatches
    `POST` (client-to-server JSON-RPC messages, with responses optionally streamed via SSE),
    `GET` (optional standalone SSE stream for server-to-client messages), and `DELETE` (session termination) per
    the [MCP Streamable HTTP transport spec](https://modelcontextprotocol.io/specification/latest/basic/transports#streamable-http),
    so no additional route configuration is needed.
    
    ##### Rails (controller)
    
    While the mount approach creates a single server at boot time, the controller approach creates a new server per request.
    This allows you to customize tools, prompts, or configuration based on the request (e.g., different tools per route).
    
    `StreamableHTTPTransport#handle_request` returns proper HTTP status codes (e.g., 202 Accepted for notifications):

    class McpController (exception, server_context) {

    # Your exception reporting logic here

    # For example with Bugsnag:

    Bugsnag.notify(exception) do |report|

    report.add_metadata(:model_context_protocol, server_context)

    end

    }

    config.around_request = ->(data, &request_handler) {

    logger.info("Start: #{data[:method]}")

    request_handler.call

    logger.info("Done: #{data[:method]}, tool: #{data[:tool_name]}")

    }

    end

    code
    or by creating an explicit configuration and passing it into the server.
    This is useful for systems where an application hosts more than one MCP server but
    they might require different configurations.

    configuration = MCP::Configuration.new

    configuration.exception_reporter = ->(exception, server_context) {

    # Your exception reporting logic here

    # For example with Bugsnag:

    Bugsnag.notify(exception) do |report|

    report.add_metadata(:model_context_protocol, server_context)

    end

    }

    configuration.around_request = ->(data, &request_handler) {

    logger.info("Start: #{data[:method]}")

    request_handler.call

    logger.info("Done: #{data[:method]}, tool: #{data[:tool_name]}")

    }

    server = MCP::Server.new(

    # ... all other options

    configuration:,

    )

    code
    ### Server Context and Configuration Block Data
    
    #### `server_context`
    
    The `server_context` is a user-defined hash that is passed into the server instance and made available to tool and prompt calls.
    It can be used to provide contextual information such as authentication state, user IDs, or request-specific data.
    
    **Type:**

    server_context: { [String, Symbol] => Any }

    code
    **Example:**

    server = MCP::Server.new(

    name: "my_server",

    server_context: { user_id: current_user.id, request_id: request.uuid }

    )

    code
    This hash is then passed as the `server_context` keyword argument to tool and prompt calls.
    Note that exception and instrumentation callbacks do not receive this user-defined hash.
    See the relevant sections below for the arguments they receive.
    
    #### Request-specific `_meta` Parameter
    
    The MCP protocol supports a special [`_meta` parameter](https://modelcontextprotocol.io/specification/2025-06-18/basic#general-fields) in requests that allows clients to pass request-specific metadata. The server automatically extracts this parameter and makes it available to tools and prompts as a nested field within the `server_context`.
    
    > [!NOTE]
    > `_meta` is only merged when `server_context` is a `Hash` (or `nil`, in which case a new `{ _meta: ... }` hash is synthesized).
    > If you assign a non-`Hash` value to `server_context`, `_meta` is not merged and tools will not see it
    > under `server_context[:_meta]`. Keep `server_context` as a `Hash` if your tools need access to `_meta`.
    
    **Access Pattern:**
    
    When a client includes `_meta` in the request params, it becomes available as `server_context[:_meta]`:

    class MyTool "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01" }

    client.call_tool(tool: tool, arguments: { message: "Hello" }, meta: meta)

    client.read_resource(uri: "file:///report.txt", meta: meta)

    code
    #### Configuration Block Data
    
    ##### Exception Reporter
    
    The exception reporter receives:
    
    - `exception`: The Ruby exception object that was raised
    - `server_context`: A hash describing where the failure occurred (e.g., `{ request:  }`
      for request handling, `{ notification: "tools_list_changed" }` for notification delivery).
      This is not the user-defined `server_context` passed to `Server.new`.
    
    **Signature:**

    exception_reporter = ->(exception, server_context) { ... }

    code
    ##### Around Request
    
    The `around_request` hook wraps request handling, allowing you to execute code before and after each request.
    This is useful for Application Performance Monitoring (APM) tracing, logging, or other observability needs.
    
    The hook receives a `data` hash and a `request_handler` block. You must call `request_handler.call` to execute the request:
    
    **Signature:**

    around_request = ->(data, &request_handler) { request_handler.call }

    code
    **`data` availability by timing:**
    
    - Before `request_handler.call`: `method`
    - After `request_handler.call`: `tool_name`, `tool_arguments`, `prompt_name`, `resource_uri`, `error`, `client`
    - Not available inside `around_request`: `duration` (added after `around_request` returns)
    
    > [!NOTE]
    > `tool_name`, `prompt_name` and `resource_uri` may only be populated for the corresponding request methods
    > (`tools/call`, `prompts/get`, `resources/read`), and may not be set depending on how the request is handled
    > (for example, `prompt_name` is not recorded when the prompt is not found).
    > `duration` is added after `around_request` returns, so it is not visible from within the hook.
    
    **Example:**

    MCP.configure do |config|

    config.around_request = ->(data, &request_handler) {

    logger.info("Start: #{data[:method]}")

    request_handler.call

    logger.info("Done: #{data[:method]}, tool: #{data[:tool_name]}")

    }

    end

    code
    ##### Instrumentation Callback (soft-deprecated)
    
    > [!NOTE]
    > `instrumentation_callback` is soft-deprecated. Use `around_request` instead.
    >
    > To migrate, wrap the call in `begin/ensure` so the callback still runs when the request fails:
    >
    > ```ruby
    > # Before
    > config.instrumentation_callback = ->(data) { log(data) }
    >
    > # After
    > config.around_request = ->(data, &request_handler) do
    >   request_handler.call
    > ensure
    >   log(data)
    > end
    > ```
    >
    > Note that `data[:duration]` is not available inside `around_request`.
    > If you need it, measure elapsed time yourself within the hook, or keep using `instrumentation_callback`.
    
    The instrumentation callback is called after each request finishes, whether successfully or with an error.
    It receives a hash with the following possible keys:
    
    - `method`: (String) The protocol method called (e.g., "ping", "tools/list")
    - `tool_name`: (String, optional) The name of the tool called
    - `tool_arguments`: (Hash, optional) The arguments passed to the tool
    - `prompt_name`: (String, optional) The name of the prompt called
    - `resource_uri`: (String, optional) The URI of the resource called
    - `error`: (String, optional) Error code if a lookup failed
    - `duration`: (Float) Duration of the call in seconds
    - `client`: (Hash, optional) Client information with `name` and `version` keys, from the initialize request
    
    **Signature:**

    instrumentation_callback = ->(data) { ... }

    code
    ### Server Protocol Version
    
    The server's protocol version can be overridden using the `protocol_version` keyword argument:

    configuration = MCP::Configuration.new(protocol_version: "2024-11-05")

    MCP::Server.new(name: "test_server", configuration: configuration)

    code
    If no protocol version is specified, the latest stable version will be applied by default.
    The latest stable version includes new features from the [draft version](https://modelcontextprotocol.io/specification/draft).
    
    This will make all new server instances use the specified protocol version instead of the default version. The protocol version can be reset to the default by setting it to `nil`:

    MCP::Configuration.new(protocol_version: nil)

    code
    If an invalid `protocol_version` value is set, an `ArgumentError` is raised.
    
    Be sure to check the [MCP spec](https://modelcontextprotocol.io/specification/versioning) for the protocol version to understand the supported features for the version being set.
    
    ### Exception Reporting
    
    The exception reporter receives two arguments:
    
    - `exception`: The Ruby exception object that was raised
    - `server_context`: A hash containing contextual information about where the error occurred
    
    The `server_context` hash includes:
    
    - For request handling failures: `{ request: { ... } }` (the raw JSON-RPC request hash)
    - For notification delivery failures: `{ notification: "tools_list_changed" }` (or the relevant notification name)
    
    When an exception occurs:
    
    1. The exception is reported via the configured reporter
    2. For tool calls, a generic error response is returned to the client: `{ error: "Internal error occurred", isError: true }`
    3. For other requests, the exception is re-raised after reporting
    
    If no exception reporter is configured, a default no-op reporter is used that silently ignores exceptions.
    
    ### Tools
    
    MCP spec includes [Tools](https://modelcontextprotocol.io/specification/latest/server/tools) which provide functionality to LLM apps.
    
    This gem provides a `MCP::Tool` class that can be used to create tools in three ways:
    
    1. As a class definition:

    class MyTool [!NOTE]

    This Tool Annotations feature is supported starting from protocol_version: '2025-03-26'.

    Tool Output Schemas

    Tools can optionally define an output_schema to specify the expected structure of their results. This works similarly to how input_schema is defined and can be used in three ways:

    1. Class definition with output_schema:

    ruby
    class WeatherTool  [!NOTE]
    > Client-initiated cancellation (`Client#cancel` equivalent that would also abort
    > the calling thread's wait) is not yet implemented. Sending `notifications/cancelled`
    > from the client side can be done by constructing the notification payload and writing it
    > directly through the transport, but the calling thread does not yet unwind automatically.
    > This is tracked as a follow-up.
    
    #### Server-Side: Handlers that Check for Cancellation
    
    Any handler that opts in to `server_context:` - tools (`Tool.call`), prompt templates,
    `resources_read_handler`, `completion_handler`, `resources_subscribe_handler`,
    `resources_unsubscribe_handler`, and `define_custom_method` blocks - receives
    an `MCP::ServerContext` wired to the in-flight request's cancellation token.
    Handlers check `cancelled?` in their work loop, or call `raise_if_cancelled!` to raise
    `MCP::CancelledError` at a safe point:

    class LongRunningTool {} on success

    MCP::Tool::Response.new([{ type: "text", text: "client is alive" }])

    end

    end

    code
    `#ping` raises `MCP::Server::ValidationError` when the client returns a `result`
    that is not a Hash. Transport-level errors (e.g., the client returning a JSON-RPC error)
    propagate as exceptions raised by the transport layer.
    
    #### Client-Side
    
    `MCP::Client` exposes `ping` to send a ping to the server:

    client = MCP::Client.new(transport: transport)

    client.ping # => {} on success

    code
    `#ping` raises `MCP::Client::ServerError` when the server returns a JSON-RPC error.
    It raises `MCP::Client::ValidationError` when the response `result` is missing or
    is not a Hash (matching the spec requirement that `result` be an object).
    Transport-level errors (for example, `MCP::Client::Stdio`'s `read_timeout:` firing)
    propagate as exceptions raised by the transport layer.
    
    ### Progress
    
    The MCP Ruby SDK supports progress tracking for long-running tool operations,
    following the [MCP Progress specification](https://modelcontextprotocol.io/specification/latest/server/utilities/progress).
    
    #### How Progress Works
    
    1. **Client Request**: The client sends a `progressToken` in the `_meta` field when calling a tool
    2. **Server Notification**: The server sends `notifications/progress` messages back to the client during tool execution
    3. **Tool Integration**: Tools call `server_context.report_progress` to report incremental progress
    
    #### Server-Side: Tool with Progress
    
    Tools that accept a `server_context:` parameter can call `report_progress` on it.
    The server automatically wraps the context in an `MCP::ServerContext` instance that provides this method:

    class LongRunningTool Array of every tool on the server.

    code
    Use these when you want the complete list; use `list_tools(cursor:)` etc. when you need
    fine-grained iteration (e.g. to stream-process pages without loading everything into memory).
    
    ### Advanced
    
    #### Custom Methods
    
    The server allows you to define custom JSON-RPC methods beyond the standard MCP protocol methods using the `define_custom_method` method:

    server = MCP::Server.new(name: "my_server")

    Define a custom method that returns a result

    server.define_custom_method(method_name: "add") do |params|

    params[:a] + params[:b]

    end

    Define a custom notification method (returns nil)

    server.define_custom_method(method_name: "notify") do |params|

    # Process notification

    nil

    end

    code
    **Key Features:**
    
    - Accepts any method name as a string
    - Block receives the request parameters as a hash
    - Can handle both regular methods (with responses) and notifications
    - Prevents overriding existing MCP protocol methods
    - Supports instrumentation callbacks for monitoring
    
    **Usage Example:**

    Client request

    {

    "jsonrpc": "2.0",

    "id": 1,

    "method": "add",

    "params": { "a": 5, "b": 3 }

    }

    Server response

    {

    "jsonrpc": "2.0",

    "id": 1,

    "result": 8

    }

    code
    **Error Handling:**
    
    - Raises `MCP::Server::MethodAlreadyDefinedError` if trying to override an existing method
    - Supports the same exception reporting and instrumentation as standard methods
    
    ## Building an MCP Client
    
    The `MCP::Client` class provides an interface for interacting with MCP servers.
    
    This class supports:
    
    - Liveness check via the `ping` method (`MCP::Client#ping`)
    - Tool listing via the `tools/list` method (`MCP::Client#tools`)
    - Tool invocation via the `tools/call` method (`MCP::Client#call_tool`)
    - Resource listing via the `resources/list` method (`MCP::Client#resources`)
    - Resource template listing via the `resources/templates/list` method (`MCP::Client#resource_templates`)
    - Resource reading via the `resources/read` method (`MCP::Client#read_resource`)
    - Prompt listing via the `prompts/list` method (`MCP::Client#prompts`)
    - Prompt retrieval via the `prompts/get` method (`MCP::Client#get_prompt`)
    - Completion requests via the `completion/complete` method (`MCP::Client#complete`)
    - Automatic JSON-RPC 2.0 message formatting
    - UUID request ID generation
    
    Clients are initialized with a transport layer instance that handles the low-level communication mechanics.
    Authorization is handled by the transport layer.
    
    ## Transport Layer Interface
    
    If the transport layer you need is not included in the gem, you can build and pass your own instances so long as they conform to the following interface:

    class CustomTransport

    # Sends a JSON-RPC request to the server and returns the raw response.

    #

    # @param request [Hash] A complete JSON-RPC request object.

    # https://www.jsonrpc.org/specification#request_object

    # @return [Hash] A hash modeling a JSON-RPC response object.

    # https://www.jsonrpc.org/specification#response_object

    def send_request(request:)

    # Your transport-specific logic here

    # - HTTP: POST to endpoint with JSON body

    # - WebSocket: Send message over WebSocket

    # - stdio: Write to stdout, read from stdin

    # - etc.

    end

    end

    code
    ### Stdio Transport Layer
    
    Use the `MCP::Client::Stdio` transport to interact with MCP servers running as subprocesses over standard input/output.
    
    `MCP::Client::Stdio.new` accepts the following keyword arguments:
    
    | Parameter | Required | Description |
    |---|---|---|
    | `command:` | Yes | The command to spawn the server process (e.g., `"ruby"`, `"bundle"`, `"npx"`). |
    | `args:` | No | An array of arguments passed to the command. Defaults to `[]`. |
    | `env:` | No | A hash of environment variables to set for the server process. Defaults to `nil`. |
    | `read_timeout:` | No | Timeout in seconds for waiting for a server response. Defaults to `nil` (no timeout). |
    
    Example usage:

    stdio_transport = MCP::Client::Stdio.new(

    command: "bundle",

    args: ["exec", "ruby", "path/to/server.rb"],

    env: { "API_KEY" => "my_secret_key" },

    read_timeout: 30

    )

    client = MCP::Client.new(transport: stdio_transport)

    Perform the MCP initialization handshake before sending any requests.

    client.connect

    List available tools.

    tools = client.tools

    tools.each do |tool|

    puts "Tool: #{tool.name} - #{tool.description}"

    end

    Call a specific tool.

    response = client.call_tool(

    tool: tools.first,

    arguments: { message: "Hello, world!" }

    )

    Close the transport when done.

    stdio_transport.close

    code
    The stdio transport automatically handles:
    
    - Spawning the server process with `Open3.popen3`
    - MCP protocol initialization handshake (`initialize` request + `notifications/initialized`)
    - JSON-RPC 2.0 message framing over newline-delimited JSON
    
    ### HTTP Transport Layer
    
    Use the `MCP::Client::HTTP` transport to interact with MCP servers using simple HTTP requests.
    
    You'll need to add `faraday` as a dependency in order to use the HTTP transport layer. Add `event_stream_parser` as well if the server uses SSE (`text/event-stream`) responses:

    gem 'mcp'

    gem 'faraday', '>= 2.0'

    gem 'event_stream_parser', '>= 1.0' # optional, required only for SSE responses

    code
    Example usage:

    http_transport = MCP::Client::HTTP.new(url: "https://api.example.com/mcp")

    client = MCP::Client.new(transport: http_transport)

    Perform the MCP initialization handshake before sending any requests.

    client.connect

    List available tools

    tools = client.tools

    tools.each do |tool|

    puts "Bearer my_token"

    }

    )

    client = MCP::Client.new(transport: http_transport)

    client.tools # will make the call using Bearer auth

    code
    You can add any custom headers needed for your authentication scheme, or for any other purpose. The client will include these headers on every request.
    
    #### OAuth 2.1 Authorization
    
    When an MCP server enforces the [MCP Authorization spec](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization),
    pass an `MCP::Client::OAuth::Provider` to the transport instead of a static `Authorization` header. The transport will:
    
    - Send `Authorization: Bearer ` on every request when a token is available.
    - On a `401 Unauthorized`, parse the `WWW-Authenticate` header, discover the authorization server (Protected Resource Metadata + RFC 8414 Authorization Server Metadata),
      perform Dynamic Client Registration if needed, run the OAuth 2.1 Authorization Code flow with PKCE (S256), and retry the failed request with the acquired token.
    - On subsequent 401s with a saved `refresh_token`, exchange it at the token endpoint before falling back to the full interactive flow (RFC 6749 Section 6).
    - On a `403 Forbidden` whose `WWW-Authenticate` header carries `error="insufficient_scope"` (OAuth 2.0 step-up, RFC 6750 Section 3.1 and the MCP scope-selection-strategy),
      run a fresh authorization request for the union of the currently granted scope and the scope named in the challenge, then retry the failed request once.
      The refresh path is bypassed because refreshing would re-issue the same scope set the server just rejected. A `403` without that challenge is surfaced unchanged.
    - Request the `offline_access` scope when `client_metadata[:grant_types]` includes `refresh_token` and the authorization server advertises `offline_access` in its metadata
      `scopes_supported` (SEP-2207). This is what lets the server issue the `refresh_token` used above. As an SDK-level safeguard, when the authorization server does not advertise
      `offline_access` the scope is also stripped from any other source (challenge, PRM, or provider-supplied scope) so a server that does not support it never receives it.

    require "mcp"

    provider = MCP::Client::OAuth::Provider.new(

    client_metadata: {

    client_name: "My MCP App",

    redirect_uris: ["http://localhost:3030/callback"],

    grant_types: ["authorization_code", "refresh_token"],

    response_types: ["code"],

    token_endpoint_auth_method: "none",

    },

    redirect_uri: "http://localhost:3030/callback",

    redirect_handler: ->(authorization_url) {

    # Send the user to the authorization URL - typically Launchy.open(authorization_url)

    # or a manual puts authorization_url in CLI tools.

    },

    callback_handler: -> {

    # Capture the redirect (for example, by running a small HTTP listener on

    # redirect_uri) and return [code, state] from the query string.

    },

    )

    transport = MCP::Client::HTTP.new(

    url: "https://api.example.com/mcp",

    oauth: provider,

    )

    client = MCP::Client.new(transport: transport)

    client.connect # initialize is sent here; if the server replies 401 the OAuth flow runs and the handshake is retried with the acquired token

    client.tools

    code
    Required keyword arguments to `Provider.new`:
    
    - `client_metadata`: Hash sent to the authorization server's Dynamic Client Registration endpoint. Must include `redirect_uris`, `grant_types`, `response_types`,
      `token_endpoint_auth_method`. `redirect_uri` (below) must appear in this list, otherwise the constructor raises `Provider::UnregisteredRedirectURIError`.
    - `redirect_uri`: String. Must use HTTPS or be a loopback URL (`localhost`, `127.0.0.0/8`, `::1`); other values raise `Provider::InsecureRedirectURIError`.
    - `redirect_handler`: Callable invoked with the fully-built authorization `URI`. Typically opens the user's browser.
    - `callback_handler`: Callable that returns `[code, state]` after the user is redirected back to `redirect_uri`.
    
    Optional keyword arguments:
    
    - `scope`: Space-separated scopes to request when the server's `WWW-Authenticate` does not specify one.
    - `storage`: Object responding to `tokens`, `save_tokens(t)`, `client_information`, `save_client_information(info)`. Defaults to `MCP::Client::OAuth::InMemoryStorage`,
      which keeps credentials in process memory only.
    - `client_id_metadata_document_url`: URL where you publish a Client ID Metadata Document
      (`draft-ietf-oauth-client-id-metadata-document` and the MCP authorization specification).
      When the authorization server advertises `client_id_metadata_document_supported: true`,
      the SDK uses this URL as the OAuth `client_id` and skips Dynamic Client Registration.
      Spec-required: the URL MUST be `https://` with a non-root path and MUST NOT include a fragment,
      userinfo, or `.`/`..` segments. The SDK additionally rejects query strings (the draft only marks
      them SHOULD NOT include, but the SDK refuses to send any) for `client_id` stability.
      Any of these failures raise `Provider::InvalidClientIDMetadataDocumentURLError`. The CIMD document
      served at the URL is a separate JSON artifact from the `client_metadata` keyword above:
      the DCR `client_metadata` MUST NOT include `client_id`, while the CIMD document MUST include
      `client_id` set to the document URL, `client_name`, and `redirect_uris` covering `redirect_uri`.
    
    To persist credentials across restarts, supply your own storage:

    class FileTokenStorage

    def initialize(path)

    @path = path

    end

    def tokens

    read["tokens"]

    end

    def save_tokens(value)

    write("tokens" => value)

    end

    def client_information

    read["client"]

    end

    def save_client_information(value)

    write("client" => value)

    end

    private

    def read

    File.exist?(@path) ? JSON.parse(File.read(@path)) : {}

    end

    def write(updates)

    File.write(@path, JSON.dump(read.merge(updates)))

    end

    end

    provider = MCP::Client::OAuth::Provider.new(

    # ... required keywords ...

    storage: FileTokenStorage.new(File.expand_path("~/.config/my-app/oauth.json")),

    )

    code
    ##### Client Credentials Grant
    
    For a confidential machine-to-machine client (no user, no browser redirect), use `MCP::Client::OAuth::ClientCredentialsProvider` instead of `Provider`.
    The transport discovers the authorization server the same way, then exchanges the OAuth 2.1 `client_credentials` grant (RFC 6749 Section 4.4) at
    the token endpoint. There is no authorization request, PKCE, or `offline_access`, because the grant does not issue a refresh token.

    provider = MCP::Client::OAuth::ClientCredentialsProvider.new(

    client_id: "my-service",

    client_secret: ENV.fetch("MCP_CLIENT_SECRET"),

    # token_endpoint_auth_method: "client_secret_basic" (default) or "client_secret_post"

    # scope: "mcp:read mcp:write" (optional; used when the server does not advertise scopes)

    )

    transport = MCP::Client::HTTP.new(url: "https://api.example.com/mcp", oauth: provider)

    code
    Keyword arguments:
    
    - `client_id`, `client_secret`: Required. The grant is for confidential clients, so a credential is mandatory.
    - `token_endpoint_auth_method`: `"client_secret_basic"` (default) or `"client_secret_post"`. `"none"` is rejected with `ClientCredentialsProvider::InvalidCredentialsError`.
    - `scope`, `storage`: Optional, same meaning as on `Provider`.
    
    ##### Communication Security
    
    When `oauth:` is set, the MCP transport URL and every OAuth-facing URL (PRM, Authorization Server metadata, `authorization_endpoint`, `token_endpoint`, `registration_endpoint`,
    `redirect_uri`) must use HTTPS or a loopback host. Non-loopback `http://` URLs are rejected at the SDK boundary so a bearer token is never sent over plain HTTP to a remote host.
    
    The transport also snapshots the canonicalized origin, path, and query string of the MCP URL at `initialize` time and re-checks them on every outgoing request through
    a Faraday middleware that runs after any user-supplied customizer. That means any URL swap raises `MCP::Client::HTTP::InsecureURLError` before the request reaches the adapter,
    whether the swap was triggered by
    `instance_variable_set(:@url, ...)`, by a Faraday customizer rewriting `url_prefix`, or by a custom middleware rewriting `env.url` (including just `env.url.query`) at request time,
    and whether the new URL is `http://` *or* `https://` to a different host or tenant.
    
    #### Customizing the Faraday Connection
    
    You can pass a block to `MCP::Client::HTTP.new` to customize the underlying Faraday connection.
    The block is called after the default middleware is configured, so you can add middleware or swap the HTTP adapter:

    http_transport = MCP::Client::HTTP.new(url: "https://api.example.com/mcp") do |faraday|

    faraday.use MyApp::Middleware::HttpRecorder

    faraday.adapter :typhoeus

    end

    code
    ### Tool Objects
    
    The client provides a wrapper class for tools returned by the server:
    
    - `MCP::Client::Tool` - Represents a single tool with its metadata
    
    This class provides easy access to tool properties like name, description, input schema, and output schema.
    
    ## Conformance Testing
    
    The `conformance/` directory contains a test server and runner that validate the SDK against the MCP specification using [`@modelcontextprotocol/conformance`](https://github.com/modelcontextprotocol/conformance).
    
    See [conformance/README.md](conformance/README.md) for usage instructions.
    
    ## Documentation
    
    - [SDK API documentation](https://rubydoc.info/gems/mcp)
    - [Model Context Protocol documentation](https://modelcontextprotocol.io)

    Similar MCP

    Based on tags & features

    • MC

      Mcpjungle

      Go·
      617
    • MA

      Manim Mcp Server

      Python·
      490
    • DA

      Davinci Resolve Mcp

      Python·
      327
    • YU

      Yutu

      Go·
      317

    Trending MCP

    Most active this week

    • PL

      Playwright Mcp

      TypeScript·
      22.1k
    • SE

      Serena

      Python·
      14.5k
    • MC

      Mcp Playwright

      TypeScript·
      4.9k
    • MC

      Mcp Server Cloudflare

      TypeScript·
      3.0k
    View All MCP Servers

    Similar MCP

    Based on tags & features

    • MC

      Mcpjungle

      Go·
      617
    • MA

      Manim Mcp Server

      Python·
      490
    • DA

      Davinci Resolve Mcp

      Python·
      327
    • YU

      Yutu

      Go·
      317

    Trending MCP

    Most active this week

    • PL

      Playwright Mcp

      TypeScript·
      22.1k
    • SE

      Serena

      Python·
      14.5k
    • MC

      Mcp Playwright

      TypeScript·
      4.9k
    • MC

      Mcp Server Cloudflare

      TypeScript·
      3.0k