mcp_on_ruby 0.3.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -28
  3. data/CODE_OF_CONDUCT.md +30 -58
  4. data/CONTRIBUTING.md +61 -67
  5. data/LICENSE.txt +2 -2
  6. data/README.md +159 -509
  7. data/bin/console +11 -0
  8. data/bin/setup +6 -0
  9. data/docs/advanced-usage.md +132 -0
  10. data/docs/api-reference.md +35 -0
  11. data/docs/testing.md +55 -0
  12. data/examples/claude/README.md +171 -0
  13. data/examples/claude/claude-bridge.js +122 -0
  14. data/lib/mcp_on_ruby/configuration.rb +74 -0
  15. data/lib/mcp_on_ruby/errors.rb +137 -0
  16. data/lib/mcp_on_ruby/generators/install_generator.rb +46 -0
  17. data/lib/mcp_on_ruby/generators/resource_generator.rb +63 -0
  18. data/lib/mcp_on_ruby/generators/templates/README +31 -0
  19. data/lib/mcp_on_ruby/generators/templates/application_resource.rb +20 -0
  20. data/lib/mcp_on_ruby/generators/templates/application_tool.rb +18 -0
  21. data/lib/mcp_on_ruby/generators/templates/initializer.rb +41 -0
  22. data/lib/mcp_on_ruby/generators/templates/resource.rb +50 -0
  23. data/lib/mcp_on_ruby/generators/templates/resource_spec.rb +67 -0
  24. data/lib/mcp_on_ruby/generators/templates/sample_resource.rb +57 -0
  25. data/lib/mcp_on_ruby/generators/templates/sample_tool.rb +59 -0
  26. data/lib/mcp_on_ruby/generators/templates/tool.rb +38 -0
  27. data/lib/mcp_on_ruby/generators/templates/tool_spec.rb +55 -0
  28. data/lib/mcp_on_ruby/generators/tool_generator.rb +51 -0
  29. data/lib/mcp_on_ruby/railtie.rb +108 -0
  30. data/lib/mcp_on_ruby/resource.rb +161 -0
  31. data/lib/mcp_on_ruby/server.rb +378 -0
  32. data/lib/mcp_on_ruby/tool.rb +134 -0
  33. data/lib/mcp_on_ruby/transport.rb +330 -0
  34. data/lib/mcp_on_ruby/version.rb +6 -0
  35. data/lib/mcp_on_ruby.rb +142 -0
  36. metadata +62 -173
  37. data/lib/ruby_mcp/client.rb +0 -43
  38. data/lib/ruby_mcp/configuration.rb +0 -90
  39. data/lib/ruby_mcp/errors.rb +0 -17
  40. data/lib/ruby_mcp/models/context.rb +0 -52
  41. data/lib/ruby_mcp/models/engine.rb +0 -31
  42. data/lib/ruby_mcp/models/message.rb +0 -60
  43. data/lib/ruby_mcp/providers/anthropic.rb +0 -269
  44. data/lib/ruby_mcp/providers/base.rb +0 -57
  45. data/lib/ruby_mcp/providers/openai.rb +0 -265
  46. data/lib/ruby_mcp/schemas.rb +0 -56
  47. data/lib/ruby_mcp/server/app.rb +0 -84
  48. data/lib/ruby_mcp/server/base_controller.rb +0 -49
  49. data/lib/ruby_mcp/server/content_controller.rb +0 -68
  50. data/lib/ruby_mcp/server/contexts_controller.rb +0 -67
  51. data/lib/ruby_mcp/server/controller.rb +0 -29
  52. data/lib/ruby_mcp/server/engines_controller.rb +0 -34
  53. data/lib/ruby_mcp/server/generate_controller.rb +0 -140
  54. data/lib/ruby_mcp/server/messages_controller.rb +0 -30
  55. data/lib/ruby_mcp/server/router.rb +0 -84
  56. data/lib/ruby_mcp/storage/active_record.rb +0 -414
  57. data/lib/ruby_mcp/storage/base.rb +0 -43
  58. data/lib/ruby_mcp/storage/error.rb +0 -8
  59. data/lib/ruby_mcp/storage/memory.rb +0 -69
  60. data/lib/ruby_mcp/storage/redis.rb +0 -197
  61. data/lib/ruby_mcp/storage_factory.rb +0 -43
  62. data/lib/ruby_mcp/validator.rb +0 -45
  63. data/lib/ruby_mcp/version.rb +0 -6
  64. data/lib/ruby_mcp.rb +0 -71
data/README.md CHANGED
@@ -1,590 +1,240 @@
1
+ # MCP on Ruby
2
+
1
3
  <div align="center">
2
4
 
3
- # MCP on Ruby
4
5
  [![Gem Version](https://badge.fury.io/rb/mcp_on_ruby.svg)](https://badge.fury.io/rb/mcp_on_ruby)
5
- [![Build](https://github.com/nagstler/ruby_mcp/actions/workflows/build.yml/badge.svg)](https://github.com/nagstler/ruby_mcp/actions/workflows/build.yml)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
- [![RuboCop](https://github.com/nagstler/ruby_mcp/actions/workflows/rubocop.yml/badge.svg)](https://github.com/nagstler/ruby_mcp/actions/workflows/rubocop.yml)
8
- [![Test](https://github.com/nagstler/ruby_mcp/actions/workflows/test.yml/badge.svg)](https://github.com/nagstler/ruby_mcp/actions/workflows/test.yml)
9
- [![codecov](https://codecov.io/github/nagstler/ruby_mcp/graph/badge.svg?token=SG4EJEIHW3)](https://codecov.io/github/nagstler/ruby_mcp)
10
-
11
- <strong> **Turn your Rails APIs into an MCP server.**</strong>
12
-
13
- </div>
14
-
15
- ## ๐Ÿ” Introduction
16
- The [Model Context Protocol](https://modelcontextprotocol.io) standardizes how applications interact with AI models, serving as the "REST for LLMs." **MCP on Ruby** brings this standard to the Ruby ecosystem. Create contexts, manage conversations, connect to multiple providers, and handle streaming responses with clean, Ruby code.
17
-
18
- ![System Component Flow (Horizontal)](https://github.com/user-attachments/assets/085ad9b8-bee0-4d60-a4b7-ecf02d07f53c)
7
+ [![Ruby Version](https://img.shields.io/badge/Ruby-2.7%2B-red.svg)](https://www.ruby-lang.org/)
19
8
 
20
- > ๐Ÿ“Œ If you find this useful, **give it a โญ on GitHub**
9
+ **Model Context Protocol (MCP) server for Rails applications**
21
10
 
22
- ## ๐Ÿ“‹ Table of Contents
11
+ Expose your Rails app as an AI accessible interface โ€” define tools and resources the Rails way.
23
12
 
24
- - [๐Ÿ” Introduction](#-introduction)
25
- - [๐ŸŒŸ Why MCP on Ruby?](#-why-mcp-on-ruby)
26
- - [๐Ÿ“ฆ Installation](#-installation)
27
- - [๐Ÿš€ Quick Start](#-quick-start)
28
- - [๐ŸŽฎ Interactive Demo](#-interactive-demo)
29
- - [โš™๏ธ Configuration Options](#๏ธ-configuration-options)
30
- - [๐Ÿ›ฃ๏ธ Server Endpoints](#๏ธ-server-endpoints)
31
- - [๐Ÿ“š Detailed Usage](#-detailed-usage)
32
- - [Creating a Context](#creating-a-context)
33
- - [Adding a Message](#adding-a-message)
34
- - [Generating a Response](#generating-a-response)
35
- - [Streaming a Response](#streaming-a-response)
36
- - [Uploading Content](#uploading-content)
37
- - [Using Tool Calls](#using-tool-calls)
38
- - [๐Ÿš„ Rails Integration](#-rails-integration)
39
- - [๐Ÿ’พ Storage Backend](#-storage-backends)
40
- - [๐Ÿ”’ Authentication](#-authentication)
41
- - [๐Ÿ› ๏ธ Development](#๏ธ-development)
42
- - [๐Ÿ—บ๏ธ Roadmap](#๏ธ-roadmap)
43
- - [๐Ÿ‘ฅ Contributing](#-contributing)
44
- - [๐Ÿ“„ License](#-license)
13
+ [Documentation](https://rubydoc.info/gems/mcp_on_ruby) | [Contributing](#contributing)
45
14
 
46
- ## ๐ŸŒŸ Why MCP on Ruby?
15
+ </div>
47
16
 
48
- **MCP on Ruby** provides a comprehensive implementation of the Model Context Protocol with these features:
17
+ ---
49
18
 
50
- - **Provider-Ready:** Pre-built adapters for OpenAI and Anthropic - just add your API key
51
- - **Complete Protocol Implementation:** Fully implements the MCP specification for compatibility
52
- - **Conversation Management:** Context handling for multi-turn conversations
53
- - **Flexible Storage:** Extensible storage backends
54
- - **Streaming Support:** Real-time response streaming for dynamic UIs
55
- - **File Handling:** Upload and reference files in conversations
56
- - **Tool Calling:** Support for LLM function calling capabilities
57
- - **Battle-Tested:** Comprehensive test suite ensures reliability
19
+ ## Features
58
20
 
59
- The library is designed to be straightforward to use while maintaining full compatibility with the MCP specification.
21
+ ๐Ÿš€ **Production-Ready** - Authentication, rate limiting, error handling, security
22
+ ๐Ÿ”ง **Rails Integration** - Generators, autoloading, middleware, Railtie
23
+ ๐Ÿ› ๏ธ **Tools System** - Callable functions with JSON Schema validation
24
+ ๐Ÿ“Š **Resources System** - Data exposure with URI templating
25
+ ๐Ÿ”’ **Security** - DNS rebinding protection, CORS, token authentication
26
+ โšก **Real-time** - Server Events (SSE) foundation (full implementation coming soon)
27
+ ๐ŸŽฏ **Developer-Friendly** - Clean DSL, generators, testing support
60
28
 
61
- ## ๐Ÿ“ฆ Installation
29
+ ## Installation
62
30
 
63
- Add this line to your application's Gemfile:
31
+ Add to your `Gemfile`:
64
32
 
65
33
  ```ruby
66
34
  gem 'mcp_on_ruby'
67
35
  ```
68
36
 
69
- And then execute:
70
-
71
- ```
72
- $ bundle install
73
- ```
74
-
75
- Or install it yourself as:
76
-
77
- ```
78
- $ gem install mcp_on_ruby
79
- ```
80
-
81
- ## ๐Ÿš€ Quick Start
82
-
83
- Here's how to get a basic MCP server running:
84
-
85
- ```ruby
86
- require 'ruby_mcp'
87
-
88
- # Configure RubyMCP
89
- RubyMCP.configure do |config|
90
- config.providers = {
91
- openai: { api_key: ENV['OPENAI_API_KEY'] },
92
- anthropic: { api_key: ENV['ANTHROPIC_API_KEY'] }
93
- }
94
- end
95
-
96
- # Start the MCP server
97
- server = RubyMCP::Server::Controller.new
98
- server.start
99
- ```
100
-
101
- ### ๐ŸŽฎ Interactive Demo
102
-
103
- The repository includes an interactive demo that walks through all the key MCP concepts:
37
+ Then run:
104
38
 
105
39
  ```bash
106
- # Terminal 1: Start the server
107
- cd examples/simple_server
108
- ruby server.rb
109
-
110
- # Terminal 2: Run the client
111
- cd examples/simple_server
112
- ruby client.rb
113
-
114
- # ActiveRecord Storage Demo
115
- # Demonstrates database storage with SQLite
116
- cd examples/simple_server
117
- ruby activerecord_demo.rb
40
+ bundle install
41
+ rails generate mcp_on_ruby:install
118
42
  ```
119
43
 
120
- This demo provides a guided tour of the MCP functionality, showing each step of creating contexts, adding messages, and generating responses with detailed explanations.
44
+ ## Quick Start
121
45
 
122
- ## โš™๏ธ Configuration Options
46
+ ### 1. Install and Configure
123
47
 
124
- RubyMCP offers several configuration options:
125
-
126
- ```ruby
127
- RubyMCP.configure do |config|
128
- # LLM Provider configurations
129
- config.providers = {
130
- openai: {
131
- api_key: ENV['OPENAI_API_KEY'],
132
- api_base: 'https://api.openai.com/v1' # Optional
133
- },
134
- anthropic: {
135
- api_key: ENV['ANTHROPIC_API_KEY']
136
- }
137
- }
138
-
139
- # Storage backend
140
-
141
- # Option 1: Memory storage (default)
142
- config.storage = :memory
143
-
144
- # Option 2: Redis storage
145
- config.storage = :redis
146
- config.redis = {
147
- url: ENV.fetch('REDIS_URL', 'redis://localhost:6379/0'),
148
- namespace: 'my_app_mcp',
149
- ttl: 86400 # 1 day in seconds
150
- }
151
-
152
- # Option 3: ActiveRecord storage
153
- config.storage = :active_record
154
- config.active_record = {
155
- # Connection settings (not needed in Rails)
156
- connection: {
157
- adapter: 'sqlite3',
158
- database: 'db/mcp.sqlite3'
159
- },
160
- # Table prefix to avoid name collisions
161
- table_prefix: 'mcp_'
162
- }
163
-
164
- # Server settings
165
- config.server_port = 3000
166
- config.server_host = "0.0.0.0"
167
-
168
- # Authentication settings
169
- config.auth_required = false
170
- config.jwt_secret = ENV['JWT_SECRET']
171
- config.token_expiry = 3600 # 1 hour
172
-
173
- # Limits
174
- config.max_contexts = 1000
175
- end
176
- ```
177
-
178
- ## ๐Ÿ›ฃ๏ธ Server Endpoints
179
-
180
- The MCP server provides the following RESTful endpoints:
181
-
182
- ### Engines
183
- - `GET /engines` - List available language models
184
-
185
- ### Contexts
186
- - `POST /contexts` - Create a new conversation context
187
- - `GET /contexts` - List existing contexts
188
- - `GET /contexts/:id` - Get details of a specific context
189
- - `DELETE /contexts/:id` - Delete a context
190
-
191
- ### Messages
192
- - `POST /messages` - Add a message to a context
193
-
194
- ### Generation
195
- - `POST /generate` - Generate a response from a language model
196
- - `POST /generate/stream` - Stream a response with incremental updates
197
-
198
- ### Content
199
- - `POST /content` - Upload content (files)
200
- - `GET /content/:context_id/:id` - Retrieve uploaded content
201
-
202
- ## ๐Ÿ“š Detailed Usage
203
-
204
- ### Creating a Context
205
-
206
- ```ruby
207
- # Using the HTTP API
208
- response = Faraday.post(
209
- "http://localhost:3000/contexts",
210
- {
211
- messages: [
212
- {
213
- role: "system",
214
- content: "You are a helpful assistant."
215
- }
216
- ],
217
- metadata: {
218
- user_id: "user_123",
219
- conversation_name: "Technical Support"
220
- }
221
- }.to_json,
222
- "Content-Type" => "application/json"
223
- )
224
-
225
- context_id = JSON.parse(response.body)["id"]
226
- ```
227
-
228
- ### Adding a Message
229
-
230
- ```ruby
231
- Faraday.post(
232
- "http://localhost:3000/messages",
233
- {
234
- context_id: context_id,
235
- role: "user",
236
- content: "What is the capital of France?"
237
- }.to_json,
238
- "Content-Type" => "application/json"
239
- )
240
- ```
48
+ ```bash
49
+ # Generate MCP server files
50
+ rails generate mcp_on_ruby:install
241
51
 
242
- ### Generating a Response
52
+ # Create a tool
53
+ rails generate mcp_on_ruby:tool UserManager --description "Manage application users"
243
54
 
244
- ```ruby
245
- response = Faraday.post(
246
- "http://localhost:3000/generate",
247
- {
248
- context_id: context_id,
249
- engine_id: "anthropic/claude-3-sonnet-20240229",
250
- max_tokens: 1000,
251
- temperature: 0.7
252
- }.to_json,
253
- "Content-Type" => "application/json"
254
- )
255
-
256
- assistant_response = JSON.parse(response.body)["content"]
55
+ # Create a resource
56
+ rails generate mcp_on_ruby:resource UserStats --uri "users/{id}/stats" --template
257
57
  ```
258
58
 
259
- ### Streaming a Response
59
+ ### 2. Configure MCP Server
260
60
 
261
61
  ```ruby
262
- conn = Faraday.new do |f|
263
- f.request :json
264
- f.response :json
265
- f.adapter :net_http
62
+ # config/initializers/mcp_on_ruby.rb
63
+ McpOnRuby.configure do |config|
64
+ config.authentication_required = true
65
+ config.authentication_token = ENV['MCP_AUTH_TOKEN']
66
+ config.rate_limit_per_minute = 60
67
+ config.allowed_origins = [/\.yourdomain\.com$/]
266
68
  end
267
69
 
268
- conn.post("http://localhost:3000/generate/stream") do |req|
269
- req.headers["Content-Type"] = "application/json"
270
- req.body = {
271
- context_id: context_id,
272
- engine_id: "openai/gpt-4",
273
- temperature: 0.7
274
- }.to_json
275
-
276
- req.options.on_data = Proc.new do |chunk, size, total|
277
- event_data = chunk.split("data: ").last.strip
278
- next if event_data.empty? || event_data == "[DONE]"
279
-
280
- event = JSON.parse(event_data)
281
- if event["event"] == "generation.content" && event["content"]
282
- print event["content"]
283
- end
284
- end
70
+ Rails.application.configure do
71
+ config.mcp.enabled = true
72
+ config.mcp.auto_register_tools = true
73
+ config.mcp.auto_register_resources = true
285
74
  end
286
75
  ```
287
76
 
288
- ### Uploading Content
77
+ ### 3. Create Tools
289
78
 
290
79
  ```ruby
291
- file_data = Base64.strict_encode64(File.read("example.pdf"))
292
-
293
- Faraday.post(
294
- "http://localhost:3000/content",
295
- {
296
- context_id: context_id,
297
- type: "file",
298
- filename: "example.pdf",
299
- content_type: "application/pdf",
300
- file_data: file_data
301
- }.to_json,
302
- "Content-Type" => "application/json"
303
- )
304
- ```
305
-
306
- ### Using Tool Calls
307
-
308
- ```ruby
309
- tools = [
310
- {
311
- type: "function",
312
- function: {
313
- name: "get_weather",
314
- description: "Get the current weather for a location",
315
- parameters: {
316
- type: "object",
80
+ # app/tools/user_manager_tool.rb
81
+ class UserManagerTool < ApplicationTool
82
+ def initialize
83
+ super(
84
+ name: 'user_manager',
85
+ description: 'Manage application users',
86
+ input_schema: {
87
+ type: 'object',
317
88
  properties: {
318
- location: {
319
- type: "string",
320
- description: "City and state, e.g., San Francisco, CA"
321
- }
89
+ action: { type: 'string', enum: ['create', 'update', 'delete'] },
90
+ user_id: { type: 'integer' },
91
+ attributes: { type: 'object' }
322
92
  },
323
- required: ["location"]
93
+ required: ['action']
324
94
  }
325
- }
326
- }
327
- ]
328
-
329
- response = Faraday.post(
330
- "http://localhost:3000/generate",
331
- {
332
- context_id: context_id,
333
- engine_id: "openai/gpt-4",
334
- tools: tools
335
- }.to_json,
336
- "Content-Type" => "application/json"
337
- )
338
-
339
- if response.body["tool_calls"]
340
- # Handle tool calls
341
- tool_calls = response.body["tool_calls"]
342
- # Process tool calls and add tool response message
343
- end
344
- ```
345
-
346
- ## ๐Ÿš„ Rails Integration
347
-
348
- For Rails applications, create an initializer at `config/initializers/ruby_mcp.rb`:
95
+ )
96
+ end
349
97
 
350
- ```ruby
351
- RubyMCP.configure do |config|
352
- config.providers = {
353
- openai: { api_key: ENV['OPENAI_API_KEY'] },
354
- anthropic: { api_key: ENV['ANTHROPIC_API_KEY'] }
355
- }
356
-
357
- # Use memory storage in development, consider persistent storage in production
358
- if Rails.env.development? || Rails.env.test?
359
- config.storage = :memory
360
- else
361
- # Use ActiveRecord for production (uses your Rails database)
362
- config.storage = :active_record
363
- config.active_record = {
364
- table_prefix: "mcp_#{Rails.env}_" # Environment-specific prefix
365
- }
98
+ protected
99
+
100
+ def execute(arguments, context)
101
+ case arguments['action']
102
+ when 'create'
103
+ user = User.create!(arguments['attributes'])
104
+ { success: true, user: user.as_json }
105
+ when 'update'
106
+ user = User.find(arguments['user_id'])
107
+ user.update!(arguments['attributes'])
108
+ { success: true, user: user.as_json }
109
+ else
110
+ { error: 'Unsupported action' }
111
+ end
366
112
  end
367
-
368
- # Enable authentication in production
369
- if Rails.env.production?
370
- config.auth_required = true
371
- config.jwt_secret = ENV["JWT_SECRET"]
113
+
114
+ def authorize(context)
115
+ # Add your authorization logic
116
+ context[:authenticated] == true
372
117
  end
373
118
  end
374
119
  ```
375
120
 
376
- And mount the server in your `config/routes.rb` file:
121
+ ### 4. Create Resources
377
122
 
378
123
  ```ruby
379
- Rails.application.routes.draw do
380
- # Mount RubyMCP at /api/mcp
381
- mount_mcp_at = "/api/mcp"
382
-
383
- Rails.application.config.middleware.use Rack::Config do |env|
384
- env["SCRIPT_NAME"] = mount_mcp_at if env["PATH_INFO"].start_with?(mount_mcp_at)
124
+ # app/resources/user_stats_resource.rb
125
+ class UserStatsResource < ApplicationResource
126
+ def initialize
127
+ super(
128
+ uri: 'users/{id}/stats',
129
+ name: 'User Statistics',
130
+ description: 'Get detailed user statistics',
131
+ mime_type: 'application/json'
132
+ )
385
133
  end
386
-
387
- mount RubyMCP::Server::App.new.rack_app, at: mount_mcp_at
388
-
389
- # Rest of your routes
390
- # ...
391
- end
392
- ```
393
-
394
- ## ๐Ÿ’พ Storage Backends
395
-
396
- ### Redis Storage
397
134
 
398
- MCP on Ruby supports Redis as a persistent storage backend:
135
+ protected
399
136
 
400
- 1. Add the Redis gem to your Gemfile:
401
- ```ruby
402
- gem 'redis', '~> 5.0'
403
- ```
404
-
405
- 2. Configure Redis storage:
406
- ```ruby
407
- RubyMCP.configure do |config|
408
- config.storage = :redis
409
- config.redis = {
410
- url: ENV.fetch('REDIS_URL', 'redis://localhost:6379/0'),
411
- namespace: "app_mcp_#{Rails.env}",
412
- ttl: 86400 # 1 day in seconds
413
- }
414
- end
415
- ```
416
-
417
- 3. Access the configured client:
418
- ```ruby
419
- client = RubyMCP.client
420
- ```
421
-
422
- For detailed integration examples, see the [[Redis Storage](https://github.com/nagstler/mcp_on_ruby/wiki/Redis-Storage)] wiki page.
423
-
424
- ### ActiveRecord Storage
425
-
426
- For integration with Rails or any app needing database storage:
137
+ def fetch_content(params, context)
138
+ user = User.find(params['id'])
139
+
140
+ {
141
+ user_id: user.id,
142
+ statistics: {
143
+ posts_count: user.posts.count,
144
+ comments_count: user.comments.count,
145
+ last_login: user.last_login_at,
146
+ account_created: user.created_at
147
+ },
148
+ generated_at: Time.current.iso8601
149
+ }
150
+ end
427
151
 
428
- ```ruby
429
- # Add to Gemfile
430
- gem 'activerecord', '~> 6.1'
431
- gem 'sqlite3', '~> 1.4' # or pg, mysql2, etc.
432
-
433
- # Configure RubyMCP
434
- RubyMCP.configure do |config|
435
- config.storage = :active_record
436
- config.active_record = {
437
- # Connection (not needed in Rails)
438
- connection: {
439
- adapter: 'sqlite3',
440
- database: 'db/mcp.sqlite3'
441
- },
442
- # Table prefix to avoid name collisions
443
- table_prefix: 'mcp_'
444
- }
152
+ def authorize(context)
153
+ # Check if user can access this data
154
+ context[:authenticated] == true
155
+ end
445
156
  end
446
157
  ```
447
158
 
448
- In Rails applications, it uses your app's database connection automatically:
159
+ ### 5. Start Your Server
449
160
 
450
- ```ruby
451
- # config/initializers/ruby_mcp.rb
452
- RubyMCP.configure do |config|
453
- config.storage = :active_record
454
- config.active_record = {
455
- table_prefix: "mcp_#{Rails.env}_" # Environment-specific prefix
456
- }
457
- end
161
+ ```bash
162
+ rails server
163
+ # MCP server available at http://localhost:3000/mcp
458
164
  ```
459
165
 
460
- The ActiveRecord adapter automatically creates the necessary tables with appropriate indexes, and handles different types of data (text, binary, JSON) appropriately.
461
-
462
- ### Custom storage
463
- You can implement custom storage backends by extending the base storage class:
166
+ ## Architecture
464
167
 
465
- ```ruby
466
- class RedisStorage < RubyMCP::Storage::Base
467
- def initialize(options = {})
468
- super
469
- @redis = Redis.new(options)
470
- end
471
-
472
- def create_context(context)
473
- @redis.set("context:#{context.id}", JSON.dump(context.to_h))
474
- context
475
- end
476
-
477
- def get_context(context_id)
478
- data = @redis.get("context:#{context_id}")
479
- raise RubyMCP::Errors::ContextError, "Context not found: #{context_id}" unless data
480
-
481
- hash = JSON.parse(data, symbolize_names: true)
482
-
483
- # Create message objects
484
- messages = hash[:messages].map do |msg|
485
- RubyMCP::Models::Message.new(
486
- role: msg[:role],
487
- content: msg[:content],
488
- id: msg[:id],
489
- metadata: msg[:metadata]
490
- )
491
- end
492
-
493
- # Create the context
494
- RubyMCP::Models::Context.new(
495
- id: hash[:id],
496
- messages: messages,
497
- metadata: hash[:metadata]
498
- )
499
- end
500
-
501
- # Implement other required methods...
502
- end
503
-
504
- # Configure RubyMCP to use your custom storage
505
- RubyMCP.configure do |config|
506
- config.storage = RedisStorage.new(url: ENV["REDIS_URL"])
507
- end
508
168
  ```
509
-
510
- ## ๐Ÿ”’ Authentication
511
-
512
- To enable JWT authentication:
513
-
514
- ```ruby
515
- RubyMCP.configure do |config|
516
- config.auth_required = true
517
- config.jwt_secret = ENV['JWT_SECRET']
518
- config.token_expiry = 3600 # 1 hour
519
- end
169
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
170
+ โ”‚ Rails App โ”‚
171
+ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
172
+ โ”‚ app/tools/ โ”‚ app/resources/ โ”‚
173
+ โ”‚ โ”œโ”€โ”€ application_tool.rb โ”œโ”€โ”€ application_resource.rb โ”‚
174
+ โ”‚ โ”œโ”€โ”€ user_manager_tool.rb โ”œโ”€โ”€ user_stats_resource.rb โ”‚
175
+ โ”‚ โ””โ”€โ”€ ... โ””โ”€โ”€ ... โ”‚
176
+ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
177
+ โ”‚ MCP Server Core โ”‚
178
+ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
179
+ โ”‚ โ”‚ Tools โ”‚ โ”‚ Resources โ”‚ โ”‚ Transport โ”‚ โ”‚
180
+ โ”‚ โ”‚ - Validation โ”‚ โ”‚ - Templating โ”‚ โ”‚ - HTTP โ”‚ โ”‚
181
+ โ”‚ โ”‚ - Authorizationโ”‚ โ”‚ - Authorizationโ”‚ โ”‚ - SSE โ”‚ โ”‚
182
+ โ”‚ โ”‚ - Execution โ”‚ โ”‚ - Content โ”‚ โ”‚ - Security โ”‚ โ”‚
183
+ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
184
+ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
185
+ โ”‚ JSON-RPC Protocol โ”‚
186
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
520
187
  ```
521
188
 
522
- Then, create and use JWT tokens:
189
+ ## Examples
523
190
 
524
- ```ruby
525
- # Generate a token
526
- require 'jwt'
191
+ Connect your Rails MCP server with different AI clients:
527
192
 
528
- payload = {
529
- sub: "user_123",
530
- exp: Time.now.to_i + 3600
531
- }
193
+ ๐Ÿ‘‰ **[Claude Desktop](examples/claude/)** - Complete setup guide with bridge script
532
194
 
533
- token = JWT.encode(payload, ENV['JWT_SECRET'], 'HS256')
195
+ ## Documentation
534
196
 
535
- # Use the token in requests
536
- conn = Faraday.new do |f|
537
- f.request :json
538
- f.response :json
539
- f.adapter :net_http
540
- end
541
-
542
- conn.get("http://localhost:3000/contexts") do |req|
543
- req.headers["Authorization"] = "Bearer #{token}"
544
- end
545
- ```
197
+ ๐Ÿ‘‰ **[Advanced Usage](docs/advanced-usage.md)** - Custom authorization, caching, manual configuration
198
+ ๐Ÿ‘‰ **[Testing Guide](docs/testing.md)** - RSpec integration and testing patterns
199
+ ๐Ÿ‘‰ **[API Reference](docs/api-reference.md)** - Complete API documentation
546
200
 
547
- ## ๐Ÿ› ๏ธ Development
548
201
 
549
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
202
+ ## Requirements
550
203
 
551
- ### Running Tests
204
+ - Ruby 2.7.0 or higher
205
+ - Rails 6.0 or higher (for full integration)
206
+ - JSON Schema validation support
552
207
 
553
- ```
554
- bundle exec rspec
555
- ```
208
+ ## Dependencies
556
209
 
557
- ### Local Development Server
210
+ Production dependencies (minimal footprint):
211
+ - `json-schema` (~> 3.0) - JSON Schema validation
212
+ - `rack` (~> 2.2) - HTTP transport layer
213
+ - `webrick` (~> 1.7) - HTTP server
558
214
 
559
- ```
560
- bundle exec ruby examples/simple_server/server.rb
561
- ```
215
+ ## Contributing
562
216
 
563
- ## ๐Ÿ—บ๏ธ Roadmap
217
+ 1. Fork the repository
218
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
219
+ 3. Write tests for your changes
220
+ 4. Ensure all tests pass (`bundle exec rspec`)
221
+ 5. Commit your changes (`git commit -am 'Add some feature'`)
222
+ 6. Push to the branch (`git push origin my-new-feature`)
223
+ 7. Create a Pull Request
564
224
 
565
- While RubyMCP is functional for basic use cases, there are several areas planned for improvement:
225
+ ## License
566
226
 
567
- - [x] Redis persistent storage backend
568
- - [x] ActiveRecord storage backend
569
- - [ ] Complete test coverage, including integration tests
570
- - [ ] Improved error handling and recovery strategies
571
- - [ ] Rate limiting for provider APIs
572
- - [ ] Proper tokenization for context window management
573
- - [ ] More robust streaming implementation
574
- - [ ] Additional provider integrations
227
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE.txt) file for details.
575
228
 
576
- :heart: Contributions in any of these areas are welcome!
229
+ ## ๐Ÿ™ Acknowledgments
577
230
 
578
- ## ๐Ÿ‘ฅ Contributing
231
+ - The [Model Context Protocol](https://modelcontextprotocol.io) team at Anthropic for creating the specification
232
+ - The Ruby on Rails community for inspiration and conventions
579
233
 
580
- 1. Fork the repository
581
- 2. Create your feature branch (`git checkout -b feature/my-new-feature`)
582
- 3. Commit your changes (`git commit -am 'Add some feature'`)
583
- 4. Push to the branch (`git push origin feature/my-new-feature`)
584
- 5. Create a new Pull Request
234
+ ---
585
235
 
586
- Bug reports and pull requests are welcome on GitHub at https://github.com/nagstler/mcp_on_ruby.
236
+ <div align="center">
587
237
 
588
- ## ๐Ÿ“„ License
238
+ Made with โค๏ธ for the Ruby community
589
239
 
590
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
240
+ </div>