ruby_llm-mcp 0.8.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +144 -162
  3. data/lib/generators/ruby_llm/mcp/install/templates/initializer.rb +21 -4
  4. data/lib/generators/ruby_llm/mcp/install/templates/mcps.yml +20 -0
  5. data/lib/ruby_llm/mcp/adapters/base_adapter.rb +215 -0
  6. data/lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb +413 -0
  7. data/lib/ruby_llm/mcp/adapters/mcp_transports/coordinator_stub.rb +41 -0
  8. data/lib/ruby_llm/mcp/adapters/mcp_transports/sse.rb +56 -0
  9. data/lib/ruby_llm/mcp/adapters/mcp_transports/stdio.rb +56 -0
  10. data/lib/ruby_llm/mcp/adapters/mcp_transports/streamable_http.rb +90 -0
  11. data/lib/ruby_llm/mcp/adapters/ruby_llm_adapter.rb +216 -0
  12. data/lib/ruby_llm/mcp/auth/browser/callback_server.rb +7 -1
  13. data/lib/ruby_llm/mcp/auth/browser/http_server.rb +0 -3
  14. data/lib/ruby_llm/mcp/auth/browser/opener.rb +0 -2
  15. data/lib/ruby_llm/mcp/auth/browser/pages.rb +100 -32
  16. data/lib/ruby_llm/mcp/auth/browser_oauth_provider.rb +230 -57
  17. data/lib/ruby_llm/mcp/auth/discoverer.rb +157 -26
  18. data/lib/ruby_llm/mcp/auth/flows/authorization_code_flow.rb +19 -2
  19. data/lib/ruby_llm/mcp/auth/flows/client_credentials_flow.rb +3 -2
  20. data/lib/ruby_llm/mcp/auth/http_response_handler.rb +0 -2
  21. data/lib/ruby_llm/mcp/auth/memory_storage.rb +31 -12
  22. data/lib/ruby_llm/mcp/auth/oauth_provider.rb +124 -9
  23. data/lib/ruby_llm/mcp/auth/session_manager.rb +0 -2
  24. data/lib/ruby_llm/mcp/auth/token_manager.rb +74 -3
  25. data/lib/ruby_llm/mcp/auth/transport_oauth_helper.rb +107 -0
  26. data/lib/ruby_llm/mcp/auth/url_builder.rb +72 -15
  27. data/lib/ruby_llm/mcp/auth.rb +19 -7
  28. data/lib/ruby_llm/mcp/client.rb +267 -39
  29. data/lib/ruby_llm/mcp/configuration.rb +161 -12
  30. data/lib/ruby_llm/mcp/elicitation.rb +261 -14
  31. data/lib/ruby_llm/mcp/errors.rb +18 -0
  32. data/lib/ruby_llm/mcp/extensions/apps/constants.rb +28 -0
  33. data/lib/ruby_llm/mcp/extensions/apps/resource_metadata.rb +24 -0
  34. data/lib/ruby_llm/mcp/extensions/apps/tool_metadata.rb +45 -0
  35. data/lib/ruby_llm/mcp/extensions/configuration.rb +72 -0
  36. data/lib/ruby_llm/mcp/extensions/constants.rb +16 -0
  37. data/lib/ruby_llm/mcp/extensions/registry.rb +85 -0
  38. data/lib/ruby_llm/mcp/handlers/approval_decision.rb +90 -0
  39. data/lib/ruby_llm/mcp/handlers/async_response.rb +181 -0
  40. data/lib/ruby_llm/mcp/handlers/concerns/approval_actions.rb +42 -0
  41. data/lib/ruby_llm/mcp/handlers/concerns/async_execution.rb +80 -0
  42. data/lib/ruby_llm/mcp/handlers/concerns/elicitation_actions.rb +42 -0
  43. data/lib/ruby_llm/mcp/handlers/concerns/error_handling.rb +29 -0
  44. data/lib/ruby_llm/mcp/handlers/concerns/guard_checks.rb +72 -0
  45. data/lib/ruby_llm/mcp/handlers/concerns/lifecycle.rb +84 -0
  46. data/lib/ruby_llm/mcp/handlers/concerns/logging.rb +19 -0
  47. data/lib/ruby_llm/mcp/handlers/concerns/model_filtering.rb +36 -0
  48. data/lib/ruby_llm/mcp/handlers/concerns/options.rb +83 -0
  49. data/lib/ruby_llm/mcp/handlers/concerns/registry_integration.rb +54 -0
  50. data/lib/ruby_llm/mcp/handlers/concerns/sampling_actions.rb +84 -0
  51. data/lib/ruby_llm/mcp/handlers/concerns/timeouts.rb +52 -0
  52. data/lib/ruby_llm/mcp/handlers/concerns/tool_filtering.rb +50 -0
  53. data/lib/ruby_llm/mcp/handlers/elicitation_handler.rb +58 -0
  54. data/lib/ruby_llm/mcp/handlers/elicitation_registry.rb +203 -0
  55. data/lib/ruby_llm/mcp/handlers/human_in_the_loop_handler.rb +93 -0
  56. data/lib/ruby_llm/mcp/handlers/human_in_the_loop_registry.rb +271 -0
  57. data/lib/ruby_llm/mcp/handlers/promise.rb +192 -0
  58. data/lib/ruby_llm/mcp/handlers/sampling_handler.rb +64 -0
  59. data/lib/ruby_llm/mcp/handlers.rb +14 -0
  60. data/lib/ruby_llm/mcp/native/cancellable_operation.rb +94 -0
  61. data/lib/ruby_llm/mcp/native/client.rb +551 -0
  62. data/lib/ruby_llm/mcp/native/json_rpc.rb +170 -0
  63. data/lib/ruby_llm/mcp/native/messages/helpers.rb +39 -0
  64. data/lib/ruby_llm/mcp/native/messages/notifications.rb +60 -0
  65. data/lib/ruby_llm/mcp/native/messages/requests.rb +267 -0
  66. data/lib/ruby_llm/mcp/native/messages/responses.rb +114 -0
  67. data/lib/ruby_llm/mcp/native/messages.rb +43 -0
  68. data/lib/ruby_llm/mcp/native/notification.rb +16 -0
  69. data/lib/ruby_llm/mcp/native/protocol.rb +79 -0
  70. data/lib/ruby_llm/mcp/native/response_handler.rb +220 -0
  71. data/lib/ruby_llm/mcp/native/task_registry.rb +62 -0
  72. data/lib/ruby_llm/mcp/native/transport.rb +88 -0
  73. data/lib/ruby_llm/mcp/native/transports/sse.rb +655 -0
  74. data/lib/ruby_llm/mcp/native/transports/stdio.rb +367 -0
  75. data/lib/ruby_llm/mcp/native/transports/streamable_http.rb +1024 -0
  76. data/lib/ruby_llm/mcp/native/transports/support/http_client.rb +28 -0
  77. data/lib/ruby_llm/mcp/native/transports/support/rate_limiter.rb +49 -0
  78. data/lib/ruby_llm/mcp/native/transports/support/timeout.rb +36 -0
  79. data/lib/ruby_llm/mcp/native.rb +12 -0
  80. data/lib/ruby_llm/mcp/notification_handler.rb +43 -5
  81. data/lib/ruby_llm/mcp/prompt.rb +7 -7
  82. data/lib/ruby_llm/mcp/railtie.rb +8 -6
  83. data/lib/ruby_llm/mcp/resource.rb +17 -8
  84. data/lib/ruby_llm/mcp/resource_template.rb +8 -7
  85. data/lib/ruby_llm/mcp/result.rb +8 -4
  86. data/lib/ruby_llm/mcp/roots.rb +4 -4
  87. data/lib/ruby_llm/mcp/sample.rb +83 -13
  88. data/lib/ruby_llm/mcp/schema_validator.rb +33 -0
  89. data/lib/ruby_llm/mcp/server_capabilities.rb +41 -0
  90. data/lib/ruby_llm/mcp/task.rb +65 -0
  91. data/lib/ruby_llm/mcp/tool.rb +33 -27
  92. data/lib/ruby_llm/mcp/version.rb +1 -1
  93. data/lib/ruby_llm/mcp.rb +31 -7
  94. data/lib/tasks/smoke.rake +66 -0
  95. metadata +77 -36
  96. data/lib/ruby_llm/mcp/coordinator.rb +0 -304
  97. data/lib/ruby_llm/mcp/notifications/cancelled.rb +0 -32
  98. data/lib/ruby_llm/mcp/notifications/initialize.rb +0 -24
  99. data/lib/ruby_llm/mcp/notifications/roots_list_change.rb +0 -26
  100. data/lib/ruby_llm/mcp/protocol.rb +0 -34
  101. data/lib/ruby_llm/mcp/requests/completion_prompt.rb +0 -50
  102. data/lib/ruby_llm/mcp/requests/completion_resource.rb +0 -50
  103. data/lib/ruby_llm/mcp/requests/initialization.rb +0 -34
  104. data/lib/ruby_llm/mcp/requests/logging_set_level.rb +0 -28
  105. data/lib/ruby_llm/mcp/requests/ping.rb +0 -24
  106. data/lib/ruby_llm/mcp/requests/prompt_call.rb +0 -32
  107. data/lib/ruby_llm/mcp/requests/prompt_list.rb +0 -31
  108. data/lib/ruby_llm/mcp/requests/resource_list.rb +0 -31
  109. data/lib/ruby_llm/mcp/requests/resource_read.rb +0 -30
  110. data/lib/ruby_llm/mcp/requests/resource_template_list.rb +0 -31
  111. data/lib/ruby_llm/mcp/requests/resources_subscribe.rb +0 -30
  112. data/lib/ruby_llm/mcp/requests/shared/meta.rb +0 -32
  113. data/lib/ruby_llm/mcp/requests/shared/pagination.rb +0 -17
  114. data/lib/ruby_llm/mcp/requests/tool_call.rb +0 -35
  115. data/lib/ruby_llm/mcp/requests/tool_list.rb +0 -31
  116. data/lib/ruby_llm/mcp/response_handler.rb +0 -67
  117. data/lib/ruby_llm/mcp/responses/elicitation.rb +0 -33
  118. data/lib/ruby_llm/mcp/responses/error.rb +0 -33
  119. data/lib/ruby_llm/mcp/responses/ping.rb +0 -28
  120. data/lib/ruby_llm/mcp/responses/roots_list.rb +0 -31
  121. data/lib/ruby_llm/mcp/responses/sampling_create_message.rb +0 -50
  122. data/lib/ruby_llm/mcp/transport.rb +0 -151
  123. data/lib/ruby_llm/mcp/transports/sse.rb +0 -435
  124. data/lib/ruby_llm/mcp/transports/stdio.rb +0 -231
  125. data/lib/ruby_llm/mcp/transports/streamable_http.rb +0 -725
  126. data/lib/ruby_llm/mcp/transports/support/http_client.rb +0 -28
  127. data/lib/ruby_llm/mcp/transports/support/rate_limit.rb +0 -47
  128. data/lib/ruby_llm/mcp/transports/support/timeout.rb +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e57a30998e3b5b2e2ebc5a2166c1fd0a9501cf73f8bfe1120fedf8956ba3aaf0
4
- data.tar.gz: 44ff135d42ca29a24175e33f12dedd24297d86dffe3cb06a640491f300c9fa1b
3
+ metadata.gz: a516cd88de84929d95dbaaea467780ba499dc8362001a02d00ac0c8672107506
4
+ data.tar.gz: b852e278be156403a32454a00fa4ad450491d219d6243d9629098680576056d2
5
5
  SHA512:
6
- metadata.gz: fc2325d76bfbca79d8f99b304b05816a80e4414dc6ea359d92556abeb391019ff02cdd6380fe84f0d2767ad82632c434424b2fdee77c5ff2d1cc78dce879a2ac
7
- data.tar.gz: ba9b9d1bff3f0ab545a22ae27075a5b6d63a40393c3da5daa437c25340019622b258e4f621cc08f3f8d90565bd720c234a55d502df568b5e5bc6abcce5b4662e
6
+ metadata.gz: a63c3650a2db47782d7db7eae32feec794302e63f2fe9967a584d1253fb42de75dfe004c87ba8758e9c09d06be856dc0d645183d0fc5b80e18a0cb294c5cda68
7
+ data.tar.gz: a89b6bffe3d03b95a9b2734c39f8b6152c7a07f945a9fe70f6eb3f3cb5423939a4fa7e16de2db94e5b01acdc82fde6b0d0d7eadcf4ce94ebd91a6654fa65565f
data/README.md CHANGED
@@ -1,235 +1,217 @@
1
- <img src="/docs/assets/images/rubyllm-mcp-logo-text.svg" alt="RubyLLM" height="120" width="250">
1
+ <div align="center">
2
2
 
3
- **Aiming to make using MCPs with RubyLLM and Ruby as easy as possible.**
3
+ <img src="docs/assets/images/rubyllm-mcp-logo-text.svg#gh-light-mode-only" alt="RubyLLM::MCP" height="120" width="250">
4
+ <img src="docs/assets/images/rubyllm-mcp-logo-text-white.svg#gh-dark-mode-only" alt="RubyLLM::MCP" height="120" width="250">
4
5
 
5
- This project is a Ruby client for the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/), designed to work seamlessly with [RubyLLM](https://github.com/crmne/ruby_llm). This gem enables Ruby applications to connect to MCP servers and use their tools, resources and prompts as part of LLM conversations.
6
+ <strong>MCP for Ruby and RubyLLM, as easy as possible.</strong>
6
7
 
7
- For a more detailed guide, see the [RubyLLM::MCP docs](https://rubyllm-mcp.com/).
8
+ [![Gem Version](https://badge.fury.io/rb/ruby_llm-mcp.svg)](https://badge.fury.io/rb/ruby_llm-mcp)
9
+ [![Gem Downloads](https://img.shields.io/gem/dt/ruby_llm-mcp)](https://rubygems.org/gems/ruby_llm-mcp)
8
10
 
9
- Currently full support for MCP protocol version up to `2025-06-18`.
10
-
11
- <div class="badge-container">
12
- <a href="https://badge.fury.io/rb/ruby_llm-mcp"><img src="https://badge.fury.io/rb/ruby_llm-mcp.svg" alt="Gem Version" /></a>
13
- <a href="https://rubygems.org/gems/ruby_llm-mcp"><img alt="Gem Downloads" src="https://img.shields.io/gem/dt/ruby_llm-mcp"></a>
14
11
  </div>
15
12
 
16
- ## RubyLLM::MCP Features
17
-
18
- - 🔌 **Multiple Transport Types**: Streamable HTTP, and STDIO and legacy SSE transports
19
- - 🛠️ **Tool Integration**: Automatically converts MCP tools into RubyLLM-compatible tools
20
- - 📄 **Resource Management**: Access and include MCP resources (files, data) and resource templates in conversations
21
- - 🎯 **Prompt Integration**: Use predefined MCP prompts with arguments for consistent interactions
22
- - 🎛️ **Client Features**: Support for sampling, roots, and elicitation
23
- - 🎨 **Enhanced Chat Interface**: Extended RubyLLM chat methods for seamless MCP integration
24
- - 🔄 **Multiple Client Management**: Create and manage multiple MCP clients simultaneously for different servers and purposes
25
- - 📚 **Simple API**: Easy-to-use interface that integrates seamlessly with RubyLLM
13
+ RubyLLM::MCP is a Ruby client for the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/), built to work cleanly with [RubyLLM](https://github.com/crmne/ruby_llm). Aiming to be completely spec compliant.
26
14
 
27
- ## Installation
15
+ Use MCP tools, resources, and prompts from your RubyLLM chats over `stdio`, streamable HTTP, or SSE.
28
16
 
29
- ```bash
30
- bundle add ruby_llm-mcp
31
- ```
17
+ **Protocol support:** Fully supports MCP spec `2025-06-18` (stable), with draft spec `2026-01-26` available.
32
18
 
33
- or add this line to your application's Gemfile:
19
+ ## RubyLLM::MCP Out of the Box
34
20
 
35
- ```ruby
36
- gem 'ruby_llm-mcp'
37
- ```
21
+ Our goal is to be able to plug MCP into Ruby/RubyLLM apps as easily as possible.
38
22
 
39
- And then execute:
23
+ RubyLLM::MCP gives you that:
40
24
 
41
- ```bash
42
- bundle install
43
- ```
25
+ - Ruby-first API for using MCP tools, resources, and prompts directly in RubyLLM chat workflows
26
+ - Stable protocol track by default (`2025-06-18`), with opt-in draft track (`2026-01-26`)
27
+ - Built-in notification and response handlers for real-time and interactive workflows
28
+ - MCP OAuth 2.1 authentication support (PKCE, dynamic registration, discovery, and automatic token refresh)
29
+ - OAuth setup paths for Rails apps (per-user connections) and browser-based CLI flows
30
+ - Straightforward integration for any Ruby app, background job, or Rails project using RubyLLM
44
31
 
45
- Or install it yourself as:
46
-
47
- ```bash
48
- gem install ruby_llm-mcp
49
- ```
50
-
51
- ## Usage
52
-
53
- ### Basic Setup
54
-
55
- First, configure your RubyLLM client and create an MCP connection:
32
+ ## Show me the code
56
33
 
57
34
  ```ruby
58
- require 'ruby_llm/mcp'
35
+ # Basic setup
36
+ require "ruby_llm/mcp"
59
37
 
60
- # Configure RubyLLM
61
38
  RubyLLM.configure do |config|
62
- config.openai_api_key = "your-api-key"
39
+ config.openai_api_key = ENV.fetch("OPENAI_API_KEY")
63
40
  end
64
41
 
65
- # Connect to an MCP server via SSE
66
42
  client = RubyLLM::MCP.client(
67
- name: "my-mcp-server",
68
- transport_type: :sse,
69
- config: {
70
- url: "http://localhost:9292/mcp/sse"
71
- }
72
- )
73
-
74
- # Or connect via stdio
75
- client = RubyLLM::MCP.client(
76
- name: "my-mcp-server",
43
+ name: "filesystem",
77
44
  transport_type: :stdio,
78
45
  config: {
79
- command: "node",
80
- args: ["path/to/mcp-server.js"],
81
- env: { "NODE_ENV" => "production" }
46
+ command: "bunx",
47
+ args: ["@modelcontextprotocol/server-filesystem", Dir.pwd]
82
48
  }
83
49
  )
84
50
 
85
- # Or connect via streamable HTTP
86
- client = RubyLLM::MCP.client(
87
- name: "my-mcp-server",
88
- transport_type: :streamable,
89
- config: {
90
- url: "http://localhost:8080/mcp",
91
- headers: { "Authorization" => "Bearer your-token" }
51
+ chat = RubyLLM.chat(model: "gpt-4.1-mini")
52
+ chat.with_tools(*client.tools)
53
+
54
+ puts chat.ask("Find Ruby files modified today and summarize what changed.")
55
+ ```
56
+
57
+ ```ruby
58
+ # Resources
59
+ resource = client.resource("release_notes")
60
+ chat = RubyLLM.chat(model: "gpt-4.1-mini")
61
+ chat.with_resource(resource)
62
+
63
+ puts chat.ask("Summarize release notes for the team in 5 bullet points.")
64
+ ```
65
+
66
+ ```ruby
67
+ # Prompts
68
+ prompt = client.prompt("code_review")
69
+ chat = RubyLLM.chat(model: "gpt-4.1-mini")
70
+
71
+ response = chat.ask_prompt(
72
+ prompt,
73
+ arguments: {
74
+ language: "ruby",
75
+ focus: "security"
92
76
  }
93
77
  )
94
- ```
95
78
 
96
- ### Using MCP Tools with RubyLLM
79
+ puts response
80
+ ```
97
81
 
98
82
  ```ruby
99
- # Get available tools from the MCP server
100
- tools = client.tools
101
- puts "Available tools:"
102
- tools.each do |tool|
103
- puts "- #{tool.name}: #{tool.description}"
83
+ # Handlers (response + notifications)
84
+ client.on_progress do |progress|
85
+ puts "Progress: #{progress.progress}% - #{progress.message}"
104
86
  end
105
87
 
106
- # Create a chat session with MCP tools
107
- chat = RubyLLM.chat(model: "gpt-4")
88
+ client.on_logging do |logging|
89
+ puts "[#{logging.level}] #{logging.message}"
90
+ end
91
+
92
+ chat = RubyLLM.chat(model: "gpt-4.1-mini")
108
93
  chat.with_tools(*client.tools)
109
94
 
110
- # Ask a question that will use the MCP tools
111
- response = chat.ask("Can you help me search for recent files in my project?")
112
- puts response
95
+ chat.ask("Run a repository scan and summarize risks.") do |chunk|
96
+ print chunk.content
97
+ end
113
98
  ```
114
99
 
115
- ### Manual Tool Execution
116
-
117
- You can also execute MCP tools directly:
118
-
119
100
  ```ruby
120
- # Tools Execution
121
- tool = client.tool("search_files")
122
-
123
- # Execute a specific tool
124
- result = tool.execute(
125
- name: "search_files",
126
- parameters: {
127
- query: "*.rb",
128
- directory: "/path/to/search"
101
+ # OAuth setup (Rails and CLI)
102
+ # Rails: per-user OAuth client (after running rails generate ruby_llm:mcp:oauth:install User)
103
+ client = current_user.mcp_client
104
+ chat = RubyLLM.chat(model: "gpt-4.1-mini")
105
+ chat.with_tools(*client.tools)
106
+ puts chat.ask("What changed in my connected repos this week?")
107
+
108
+ # CLI: browser-based OAuth flow
109
+ cli_client = RubyLLM::MCP.client(
110
+ name: "oauth-server",
111
+ transport_type: :streamable,
112
+ start: false,
113
+ config: {
114
+ url: ENV.fetch("MCP_SERVER_URL"),
115
+ oauth: { scope: "mcp:read mcp:write" }
129
116
  }
130
117
  )
131
118
 
132
- puts result
119
+ cli_client.oauth(type: :browser).authenticate
120
+ cli_client.start
121
+ puts RubyLLM.chat(model: "gpt-4.1-mini").with_tools(*cli_client.tools).ask("List my open tasks.")
122
+ cli_client.stop
133
123
  ```
134
124
 
135
- ### Working with Resources
125
+ ## Features
126
+
127
+ - **Tools:** Convert MCP tools into RubyLLM-compatible tools
128
+ - **Resources:** Work with resources and resource templates in chat context
129
+ - **Prompts:** Execute server prompts with typed arguments
130
+ - **Transports:** `:stdio`, `:streamable`, and `:sse`
131
+ - **Client capabilities:** Sampling, roots, progress tracking, and elicitation
132
+ - **Handlers:** Built-in notification and response handlers for real-time and interactive workflows
133
+ - **MCP Authentication:** OAuth 2.1 support with PKCE, dynamic registration, discovery, and automatic token refresh
134
+ - **OAuth setup paths:** Rails per-user OAuth setup and browser-based OAuth for CLI tools
135
+ - **Extensions:** Global/per-client extension negotiation, including MCP Apps
136
+ - **Multi-client support:** Manage multiple MCP servers in one workflow
137
+ - **Protocol control:** Stable default with explicit draft opt-in
138
+ - **Adapters:** Native `:ruby_llm` adapter (full feature set) and optional `:mcp_sdk`
136
139
 
137
- MCP servers can provide access to resources - structured data that can be included in conversations. Resources come in two types: normal resources and resource templates.
140
+ ## Installation
138
141
 
139
- #### Normal Resources
142
+ Add to your Gemfile:
140
143
 
141
144
  ```ruby
142
- # Get available resources from the MCP server
143
- resources = client.resources
144
- puts "Available resources:"
145
- resources.each do |resource|
146
- puts "- #{resource.name}: #{resource.description}"
147
- end
145
+ gem "ruby_llm-mcp"
146
+ ```
148
147
 
149
- # Access a specific resource by name
150
- file_resource = client.resource("project_readme")
151
- content = file_resource.content
152
- puts "Resource content: #{content}"
148
+ Then run:
153
149
 
154
- # Include a resource in a chat conversation for reference with an LLM
155
- chat = RubyLLM.chat(model: "gpt-4")
156
- chat.with_resource(file_resource)
150
+ ```bash
151
+ bundle install
152
+ ```
157
153
 
158
- # Or add a resource directly to the conversation
159
- file_resource.include(chat)
154
+ If you want the official SDK adapter, also add:
160
155
 
161
- response = chat.ask("Can you summarize this README file?")
162
- puts response
156
+ ```ruby
157
+ gem "mcp", "~> 0.7"
163
158
  ```
164
159
 
165
- #### Resource Templates
160
+ ## Rails
166
161
 
167
- Resource templates are parameterized resources that can be dynamically configured:
162
+ ```bash
163
+ rails generate ruby_llm:mcp:install
164
+ ```
168
165
 
169
- ```ruby
170
- # Get available resource templates
171
- templates = client.resource_templates
172
- log_template = client.resource_template("application_logs")
173
-
174
- # Use a template with parameters
175
- chat = RubyLLM.chat(model: "gpt-4")
176
- chat.with_resource_template(log_template, arguments: {
177
- date: "2024-01-15",
178
- level: "error"
179
- })
180
-
181
- response = chat.ask("What errors occurred on this date?")
182
- puts response
166
+ For OAuth-based user connections:
183
167
 
184
- # You can also get templated content directly
185
- content = log_template.to_content(arguments: {
186
- date: "2024-01-15",
187
- level: "error"
188
- })
189
- puts content
168
+ ```bash
169
+ rails generate ruby_llm:mcp:oauth:install User
190
170
  ```
191
171
 
192
- ### Working with Prompts
193
-
194
- MCP servers can provide predefined prompts that can be used in conversations:
172
+ OAuth quick example:
195
173
 
196
174
  ```ruby
197
- # Get available prompts from the MCP server
198
- prompts = client.prompts
199
- puts "Available prompts:"
200
- prompts.each do |prompt|
201
- puts "- #{prompt.name}: #{prompt.description}"
202
- prompt.arguments.each do |arg|
203
- puts " - #{arg.name}: #{arg.description} (required: #{arg.required})"
204
- end
205
- end
175
+ client = RubyLLM::MCP.client(
176
+ name: "oauth-server",
177
+ transport_type: :streamable,
178
+ start: false,
179
+ config: {
180
+ url: ENV.fetch("MCP_SERVER_URL"),
181
+ oauth: { scope: "mcp:read mcp:write" }
182
+ }
183
+ )
206
184
 
207
- # Use a prompt in a conversation
208
- greeting_prompt = client.prompt("daily_greeting")
209
- chat = RubyLLM.chat(model: "gpt-4")
185
+ client.oauth(type: :browser).authenticate
186
+ client.start
210
187
 
211
- # Method 1: Ask prompt directly
212
- response = chat.ask_prompt(greeting_prompt, arguments: { name: "Alice", time: "morning" })
213
- puts response
188
+ chat = RubyLLM.chat(model: "gpt-4.1-mini")
189
+ chat.with_tools(*client.tools)
190
+ puts chat.ask("What should I prioritize today?")
214
191
 
215
- # Method 2: Add prompt to chat and then ask
216
- chat.with_prompt(greeting_prompt, arguments: { name: "Alice", time: "morning" })
217
- response = chat.ask("Continue with the greeting")
192
+ client.stop
218
193
  ```
219
194
 
220
- ## Development
195
+ Then use explicit connection blocks in jobs/controllers/services:
221
196
 
222
- After checking out the repo, run `bundle` to install dependencies. Then, run `bundle exec rake` to run the tests. Tests currently use `bun` to run test MCP servers You can also run `bin/console` for an interactive prompt that will allow you to experiment.
197
+ ```ruby
198
+ RubyLLM::MCP.establish_connection do |clients|
199
+ chat = RubyLLM.chat(model: "gpt-4.1-mini")
200
+ chat.with_tools(*clients.tools)
201
+ chat.ask("Analyze this pull request and list risks.")
202
+ end
203
+ ```
223
204
 
224
- There are also examples you you can run to verify the gem is working as expected.
205
+ ## Documentation
225
206
 
226
- ```bash
227
- bundle exec ruby examples/tools/local_mcp.rb
228
- ```
207
+ - [rubyllm-mcp.com](https://rubyllm-mcp.com/)
208
+ - [Getting Started](https://rubyllm-mcp.com/getting-started/getting-started.html)
209
+ - [Rails Integration](https://rubyllm-mcp.com/guides/rails-integration.html)
210
+ - [Adapters and transports](https://rubyllm-mcp.com/guides/adapters.html)
229
211
 
230
212
  ## Contributing
231
213
 
232
- We welcome contributions! Bug reports and pull requests are welcome on GitHub at https://github.com/patvice/ruby_llm-mcp.
214
+ Issues and pull requests are welcome at [patvice/ruby_llm-mcp](https://github.com/patvice/ruby_llm-mcp).
233
215
 
234
216
  ## License
235
217
 
@@ -2,6 +2,11 @@
2
2
 
3
3
  # Configure RubyLLM MCP
4
4
  RubyLLM::MCP.configure do |config|
5
+ # Default SDK adapter to use (:ruby_llm or :mcp_sdk)
6
+ # - :ruby_llm: Full-featured, supports all MCP features + extensions
7
+ # - :mcp_sdk: Official SDK, limited features but maintained by Anthropic
8
+ config.default_adapter = :ruby_llm
9
+
5
10
  # Request timeout in milliseconds
6
11
  config.request_timeout = 8000
7
12
 
@@ -17,23 +22,35 @@ RubyLLM::MCP.configure do |config|
17
22
  # Launch MCPs (:automatic, :manual)
18
23
  config.launch_control = :automatic
19
24
 
20
- # Configure roots for file system access
25
+ # Configure roots for file system access (RubyLLM adapter only)
21
26
  # config.roots = [
22
27
  # Rails.root.to_s
23
28
  # ]
24
29
 
25
- # Configure sampling (optional)
30
+ # Configure sampling (RubyLLM adapter only)
26
31
  config.sampling.enabled = false
27
32
 
28
33
  # Set preferred model for sampling
29
34
  # config.sampling.preferred_model do
30
- # # Return the preferred model name
31
35
  # "claude-sonnet-4"
32
36
  # end
33
37
 
34
38
  # Set a guard for sampling
35
39
  # config.sampling.guard do
36
- # # Return true to enable sampling, false to disable
37
40
  # Rails.env.development?
38
41
  # end
42
+
43
+ # Event handlers (RubyLLM adapter only)
44
+ # config.on_progress do |progress_token, progress, total|
45
+ # # Handle progress updates
46
+ # end
47
+
48
+ # config.on_human_in_the_loop do |tool_name, arguments|
49
+ # # Return true to allow, false to deny
50
+ # true
51
+ # end
52
+
53
+ # config.on_logging do |level, logger_name, data|
54
+ # # Handle logging from MCP servers
55
+ # end
39
56
  end
@@ -1,5 +1,9 @@
1
1
  mcp_servers:
2
2
  filesystem:
3
+ # SDK adapter to use (optional, defaults to config.default_adapter)
4
+ # Options: ruby_llm, mcp_sdk
5
+ # adapter: ruby_llm
6
+
3
7
  transport_type: stdio
4
8
  command: npx
5
9
  args:
@@ -7,3 +11,19 @@ mcp_servers:
7
11
  - "<%%= Rails.root %>"
8
12
  env: {}
9
13
  with_prefix: true
14
+
15
+ # Example with MCP SDK (official)
16
+ # weather:
17
+ # adapter: mcp_sdk
18
+ # transport_type: http
19
+ # url: "https://api.example.com/mcp"
20
+ # headers:
21
+ # Authorization: "Bearer <%%= ENV['WEATHER_API_KEY'] %>"
22
+
23
+ # Example with SSE (RubyLLM adapter only)
24
+ # notifications:
25
+ # adapter: ruby_llm
26
+ # transport_type: sse
27
+ # url: "https://api.example.com/mcp/sse"
28
+ # headers:
29
+ # Authorization: "Bearer <%%= ENV['API_KEY'] %>"