From 0c5432b02730ba3b2e8158528024eae59f7b5b00 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Mon, 13 Apr 2026 21:02:28 +0900 Subject: [PATCH] [Doc] Update homepage with just-the-docs theme Follow-up to https://github.com/modelcontextprotocol/ruby-sdk/pull/181. The landing page at https://ruby.sdk.modelcontextprotocol.io previously only listed version links to RubyDoc.info. Add multi-page documentation using the just-the-docs Jekyll theme with sidebar navigation, search, and code highlighting. Pages: Introduction, Installation, Building Servers, and Building Clients. This PR introduces a basic set of pages. Additional content can be added incrementally in future updates. --- docs/_config.yml | 17 +++ docs/building-clients.md | 116 +++++++++++++++++ docs/building-servers.md | 275 +++++++++++++++++++++++++++++++++++++++ docs/index.md | 78 ++++++++++- docs/installation.md | 32 +++++ 5 files changed, 514 insertions(+), 4 deletions(-) create mode 100644 docs/building-clients.md create mode 100644 docs/building-servers.md create mode 100644 docs/installation.md diff --git a/docs/_config.yml b/docs/_config.yml index 3b4ca1fc..d488c9e8 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,6 +1,23 @@ # Use package name as site title title: "MCP Ruby SDK" +description: "The official Ruby SDK for Model Context Protocol servers and clients." +remote_theme: just-the-docs/just-the-docs # Include generated files and directories which may start with underscores include: - "_*" + +# Header links +aux_links: + GitHub: https://github.com/modelcontextprotocol/ruby-sdk + +# Search +search_enabled: true + +# Footer +gh_edit_link: true +gh_edit_link_text: "Edit this page on GitHub." +gh_edit_repository: "https://github.com/modelcontextprotocol/ruby-sdk" +gh_edit_branch: "main" +gh_edit_source: "docs" +gh_edit_view_mode: "edit" diff --git a/docs/building-clients.md b/docs/building-clients.md new file mode 100644 index 00000000..45c7e8dc --- /dev/null +++ b/docs/building-clients.md @@ -0,0 +1,116 @@ +--- +layout: default +title: Building Clients +nav_order: 4 +--- + +# Building an MCP Client + +The `MCP::Client` class provides an interface for interacting with MCP servers. + +**Supported operations:** + +- Tool listing (`MCP::Client#tools`) and invocation (`MCP::Client#call_tool`) +- Resource listing (`MCP::Client#resources`) and reading (`MCP::Client#read_resources`) +- Resource template listing (`MCP::Client#resource_templates`) +- Prompt listing (`MCP::Client#prompts`) and retrieval (`MCP::Client#get_prompt`) +- Completion requests (`MCP::Client#complete`) + +## Stdio Transport + +Use `MCP::Client::Stdio` to interact with MCP servers running as subprocesses: + +```ruby +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) + +tools = client.tools +tools.each do |tool| + puts "Tool: #{tool.name} - #{tool.description}" +end + +response = client.call_tool( + tool: tools.first, + arguments: { message: "Hello, world!" } +) + +stdio_transport.close +``` + +| Parameter | Required | Description | +|---|---|---| +| `command:` | Yes | The command to spawn the server process. | +| `args:` | No | An array of arguments passed to the command. Defaults to `[]`. | +| `env:` | No | A hash of environment variables for the server process. Defaults to `nil`. | +| `read_timeout:` | No | Timeout in seconds for waiting for a server response. Defaults to `nil`. | + +## HTTP Transport + +Use `MCP::Client::HTTP` to interact with MCP servers over HTTP. Requires the `faraday` gem: + +```ruby +gem 'mcp' +gem 'faraday', '>= 2.0' +``` + +```ruby +http_transport = MCP::Client::HTTP.new(url: "https://api.example.com/mcp") +client = MCP::Client.new(transport: http_transport) + +tools = client.tools +tools.each do |tool| + puts "Tool: #{tool.name} - #{tool.description}" +end + +response = client.call_tool( + tool: tools.first, + arguments: { message: "Hello, world!" } +) +``` + +### Authorization + +Provide custom headers for authentication: + +```ruby +http_transport = MCP::Client::HTTP.new( + url: "https://api.example.com/mcp", + headers: { + "Authorization" => "Bearer my_token" + } +) +client = MCP::Client.new(transport: http_transport) +``` + +### Customizing the Faraday Connection + +Pass a block to customize the underlying Faraday connection: + +```ruby +http_transport = MCP::Client::HTTP.new(url: "https://api.example.com/mcp") do |faraday| + faraday.use MyApp::Middleware::HttpRecorder + faraday.adapter :typhoeus +end +``` + +## Custom Transport + +If the built-in transports do not fit your needs, you can implement your own: + +```ruby +class CustomTransport + def send_request(request:) + # Your transport-specific logic here. + # Returns a Hash modeling a JSON-RPC response object. + end +end + +client = MCP::Client.new(transport: CustomTransport.new) +``` + +For more details, see the [full README](https://github.com/modelcontextprotocol/ruby-sdk/blob/main/README.md#building-an-mcp-client). diff --git a/docs/building-servers.md b/docs/building-servers.md new file mode 100644 index 00000000..0fd8d703 --- /dev/null +++ b/docs/building-servers.md @@ -0,0 +1,275 @@ +--- +layout: default +title: Building Servers +nav_order: 3 +--- + +# 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. + +## Supported Methods + +- `initialize` - Initializes the protocol and returns server capabilities +- `ping` - Simple health check +- `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 +- `sampling/createMessage` - Requests LLM completion from the client (server-to-client) + +## Stdio Transport + +If you want to build a local command-line application, you can use the stdio transport: + +```ruby +require "mcp" + +class ExampleTool < MCP::Tool + description "A simple example tool that echoes back its arguments" + input_schema( + properties: { + message: { type: "string" }, + }, + required: ["message"] + ) + + class << self + def call(message:, server_context:) + MCP::Tool::Response.new([{ + type: "text", + text: "Hello from example tool! Message: #{message}", + }]) + end + end +end + +server = MCP::Server.new( + name: "example_server", + tools: [ExampleTool], +) + +transport = MCP::Server::Transports::StdioTransport.new(server) +transport.open +``` + +## Streamable HTTP Transport + +`MCP::Server::Transports::StreamableHTTPTransport` is a standard Rack app, so it can be mounted in any Rack-compatible framework. +The following examples show two common integration styles in Rails. + +{: .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: + +```ruby +# 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 +``` + +`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): + +```ruby +class McpController < ActionController::API + def create + 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], + server_context: { user_id: current_user.id }, + ) + transport = MCP::Server::Transports::StreamableHTTPTransport.new(server, stateless: true) + status, headers, body = transport.handle_request(request) + + render(json: body.first, status: status, headers: headers) + end +end +``` + +## Tools + +Tools provide functionality to LLM applications. There are three ways to define tools: + +### Class Definition + +```ruby +class MyTool < MCP::Tool + title "My Tool" + description "This tool performs specific functionality..." + input_schema( + properties: { + message: { type: "string" }, + }, + required: ["message"] + ) + annotations( + read_only_hint: true, + destructive_hint: false, + ) + + def self.call(message:, server_context:) + MCP::Tool::Response.new([{ type: "text", text: "OK" }]) + end +end +``` + +### Block Definition + +```ruby +tool = MCP::Tool.define( + name: "my_tool", + description: "This tool performs specific functionality...", +) do |args, server_context:| + MCP::Tool::Response.new([{ type: "text", text: "OK" }]) +end +``` + +### Server-level Definition + +```ruby +server = MCP::Server.new +server.define_tool( + name: "my_tool", + description: "This tool performs specific functionality...", +) do |args, server_context:| + MCP::Tool::Response.new([{ type: "text", text: "OK" }]) +end +``` + +## Prompts + +Prompts are templates for LLM interactions. Like tools, they can be defined in three ways: + +### Class Definition + +```ruby +class CodeReviewPrompt < MCP::Prompt + prompt_name "code_review" + description "Review code for best practices" + arguments [ + MCP::Prompt::Argument.new(name: "code", description: "Code to review", required: true), + ] + + class << self + def template(args, server_context:) + MCP::Prompt::Result.new( + description: "Code review", + messages: [ + MCP::Prompt::Message.new( + role: "user", + content: MCP::Content::Text.new("Please review this code:\n#{args[:code]}") + ), + ] + ) + end + end +end +``` + +### Server-level Definition + +```ruby +server.define_prompt( + name: "code_review", + description: "Review code for best practices", + arguments: [ + MCP::Prompt::Argument.new(name: "code", description: "Code to review", required: true), + ] +) do |args, server_context:| + MCP::Prompt::Result.new( + description: "Code review", + messages: [ + MCP::Prompt::Message.new( + role: "user", + content: MCP::Content::Text.new("Please review this code:\n#{args[:code]}") + ), + ] + ) +end +``` + +## Resources + +Resources provide data access to LLM applications: + +```ruby +class MyResource < MCP::Resource + uri "file:///data/config.json" + resource_name "config" + description "Application configuration" + mime_type "application/json" +end + +server = MCP::Server.new( + name: "my_server", + resources: [MyResource], + resources_read_handler: ->(uri, _server_context) { + case uri + when "file:///data/config.json" + { uri: uri, text: File.read("config.json"), mimeType: "application/json" } + end + } +) +``` + +## Configuration + +```ruby +MCP.configure do |config| + config.exception_reporter = ->(exception, server_context) { + Bugsnag.notify(exception) do |report| + report.add_metadata(:model_context_protocol, server_context) + end + } + + config.instrumentation_callback = ->(data) { + puts "Got instrumentation data #{data.inspect}" + } +end +``` + +For more details on sampling, notifications, progress tracking, completions, logging, and advanced features, see the [full README](https://github.com/modelcontextprotocol/ruby-sdk/blob/main/README.md#building-an-mcp-server). diff --git a/docs/index.md b/docs/index.md index 305c3a11..564b5f0f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,7 +1,77 @@ --- -# Empty Jekyll front matter to enable Liquid templating (see {{ ... }} below) +layout: default +title: Introduction +nav_order: 1 --- -{% for version in site.data.versions -%} -- [v{{ version }}](https://rubydoc.info/gems/mcp/{{ version }}) -{% endfor %} +The official Ruby SDK for the [Model Context Protocol](https://modelcontextprotocol.io/) (MCP), implementing both server and client functionality for JSON-RPC 2.0 based communication between LLM applications and context providers. + +**Key features:** + +- JSON-RPC 2.0 message handling with protocol initialization and capability negotiation +- Tool, prompt, and resource registration and invocation +- Stdio and Streamable HTTP (including SSE) transports +- Client support for communicating with MCP servers +- Notifications, sampling, progress tracking, and completions + +## Quick Start + +Here is a minimal MCP server using the stdio transport: + +```ruby +require "mcp" + +class ExampleTool < MCP::Tool + description "A simple example tool that echoes back its arguments" + input_schema( + properties: { + message: { type: "string" }, + }, + required: ["message"] + ) + + class << self + def call(message:, server_context:) + MCP::Tool::Response.new([{ + type: "text", + text: "Hello from example tool! Message: #{message}", + }]) + end + end +end + +server = MCP::Server.new( + name: "example_server", + tools: [ExampleTool], +) + +transport = MCP::Server::Transports::StdioTransport.new(server) +transport.open +``` + +Run the script and send JSON-RPC requests via stdin: + +```console +$ ruby server.rb +{"jsonrpc":"2.0","id":"1","method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"example","version":"0.1.0"}}} +{"jsonrpc":"2.0","id":"2","method":"tools/list"} +{"jsonrpc":"2.0","id":"3","method":"tools/call","params":{"name":"example_tool","arguments":{"message":"Hello"}}} +``` + +For comprehensive documentation, see the [full README](https://github.com/modelcontextprotocol/ruby-sdk/blob/main/README.md). + +## API Documentation + +Full API reference is hosted on [RubyDoc.info](https://rubydoc.info/gems/mcp). Select a version to view: + + + +## License + +This project is transitioning from the MIT License to the Apache License 2.0. See [LICENSE](https://github.com/modelcontextprotocol/ruby-sdk/blob/main/LICENSE) for details. diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 00000000..0822994e --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,32 @@ +--- +layout: default +title: Installation +nav_order: 2 +--- + +# 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. For example, the HTTP client transport requires the `faraday` gem: + +```ruby +gem 'mcp' +gem 'faraday', '>= 2.0' +```