mcp_on_ruby 1.0.0 → 2.0.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +42 -4
- data/LICENSE.txt +1 -1
- data/README.md +350 -163
- data/config/routes.rb +5 -0
- data/lib/generators/mcp_on_ruby/expose/expose_generator.rb +108 -0
- data/lib/generators/mcp_on_ruby/install/install_generator.rb +37 -0
- data/lib/generators/mcp_on_ruby/install/templates/initializer.rb +22 -0
- data/lib/mcp_on_ruby/call_context.rb +29 -0
- data/lib/mcp_on_ruby/configuration.rb +63 -59
- data/lib/mcp_on_ruby/controller.rb +60 -0
- data/lib/mcp_on_ruby/declaration.rb +24 -0
- data/lib/mcp_on_ruby/dispatcher.rb +97 -0
- data/lib/mcp_on_ruby/doctor.rb +67 -0
- data/lib/mcp_on_ruby/endpoint.rb +43 -0
- data/lib/mcp_on_ruby/engine.rb +34 -0
- data/lib/mcp_on_ruby/registry.rb +146 -0
- data/lib/mcp_on_ruby/result.rb +83 -0
- data/lib/mcp_on_ruby/rspec.rb +8 -0
- data/lib/mcp_on_ruby/schema.rb +149 -0
- data/lib/mcp_on_ruby/server_factory.rb +70 -0
- data/lib/mcp_on_ruby/stdio.rb +42 -0
- data/lib/mcp_on_ruby/test_helper.rb +86 -0
- data/lib/mcp_on_ruby/tool_spec.rb +87 -0
- data/lib/mcp_on_ruby/tool_table.rb +31 -0
- data/lib/mcp_on_ruby/version.rb +2 -3
- data/lib/mcp_on_ruby/warden_hook.rb +21 -0
- data/lib/mcp_on_ruby.rb +44 -122
- data/lib/tasks/mcp_on_ruby.rake +20 -0
- metadata +65 -136
- data/CODE_OF_CONDUCT.md +0 -56
- data/CONTRIBUTING.md +0 -86
- data/bin/console +0 -11
- data/bin/setup +0 -6
- data/docs/advanced-usage.md +0 -132
- data/docs/api-reference.md +0 -35
- data/docs/testing.md +0 -55
- data/examples/claude/README.md +0 -171
- data/examples/claude/claude-bridge.js +0 -122
- data/lib/mcp_on_ruby/errors.rb +0 -137
- data/lib/mcp_on_ruby/generators/install_generator.rb +0 -46
- data/lib/mcp_on_ruby/generators/resource_generator.rb +0 -63
- data/lib/mcp_on_ruby/generators/templates/README +0 -31
- data/lib/mcp_on_ruby/generators/templates/application_resource.rb +0 -20
- data/lib/mcp_on_ruby/generators/templates/application_tool.rb +0 -18
- data/lib/mcp_on_ruby/generators/templates/initializer.rb +0 -41
- data/lib/mcp_on_ruby/generators/templates/resource.rb +0 -50
- data/lib/mcp_on_ruby/generators/templates/resource_spec.rb +0 -67
- data/lib/mcp_on_ruby/generators/templates/sample_resource.rb +0 -57
- data/lib/mcp_on_ruby/generators/templates/sample_tool.rb +0 -59
- data/lib/mcp_on_ruby/generators/templates/tool.rb +0 -38
- data/lib/mcp_on_ruby/generators/templates/tool_spec.rb +0 -55
- data/lib/mcp_on_ruby/generators/tool_generator.rb +0 -51
- data/lib/mcp_on_ruby/railtie.rb +0 -108
- data/lib/mcp_on_ruby/resource.rb +0 -161
- data/lib/mcp_on_ruby/server.rb +0 -378
- data/lib/mcp_on_ruby/tool.rb +0 -134
- data/lib/mcp_on_ruby/transport.rb +0 -330
data/CONTRIBUTING.md
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
# Contributing to MCP on Ruby
|
|
2
|
-
|
|
3
|
-
We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
|
|
4
|
-
|
|
5
|
-
- Reporting a bug
|
|
6
|
-
- Discussing the current state of the code
|
|
7
|
-
- Submitting a fix
|
|
8
|
-
- Proposing new features
|
|
9
|
-
- Becoming a maintainer
|
|
10
|
-
|
|
11
|
-
## Development Process
|
|
12
|
-
|
|
13
|
-
We use GitHub to host code, to track issues and feature requests, as well as accept pull requests.
|
|
14
|
-
|
|
15
|
-
## Pull Requests
|
|
16
|
-
|
|
17
|
-
1. Fork the repo and create your branch from `main`.
|
|
18
|
-
2. If you've added code that should be tested, add tests.
|
|
19
|
-
3. If you've changed APIs, update the documentation.
|
|
20
|
-
4. Ensure the test suite passes.
|
|
21
|
-
5. Make sure your code lints.
|
|
22
|
-
6. Issue that pull request!
|
|
23
|
-
|
|
24
|
-
## Development Setup
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
# Clone your fork
|
|
28
|
-
git clone https://github.com/nagstler/mcp_on_ruby.git
|
|
29
|
-
cd mcp_on_ruby
|
|
30
|
-
|
|
31
|
-
# Install dependencies
|
|
32
|
-
bundle install
|
|
33
|
-
|
|
34
|
-
# Run the generators to test Rails integration
|
|
35
|
-
mkdir test_app && cd test_app
|
|
36
|
-
rails new . --skip-git
|
|
37
|
-
echo "gem 'mcp_on_ruby', path: '..' " >> Gemfile
|
|
38
|
-
bundle install
|
|
39
|
-
rails generate mcp_on_ruby:install
|
|
40
|
-
|
|
41
|
-
# Test the gem
|
|
42
|
-
cd .. && ruby -e "require './lib/mcp_on_ruby'; puts 'Gem loads successfully'"
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Code Style
|
|
46
|
-
|
|
47
|
-
- Follow standard Ruby conventions
|
|
48
|
-
- Use meaningful variable and method names
|
|
49
|
-
- Add documentation for public APIs
|
|
50
|
-
- Keep methods focused and small
|
|
51
|
-
|
|
52
|
-
## Testing
|
|
53
|
-
|
|
54
|
-
Currently, we use manual testing and examples. In the future, we plan to add:
|
|
55
|
-
- RSpec test suite
|
|
56
|
-
- Integration tests with Rails
|
|
57
|
-
- CI/CD pipeline
|
|
58
|
-
|
|
59
|
-
## Bug Reports
|
|
60
|
-
|
|
61
|
-
We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/nagstler/mcp_on_ruby/issues).
|
|
62
|
-
|
|
63
|
-
**Great Bug Reports** tend to have:
|
|
64
|
-
|
|
65
|
-
- A quick summary and/or background
|
|
66
|
-
- Steps to reproduce
|
|
67
|
-
- Be specific!
|
|
68
|
-
- Give sample code if you can
|
|
69
|
-
- What you expected would happen
|
|
70
|
-
- What actually happens
|
|
71
|
-
- Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
|
|
72
|
-
|
|
73
|
-
## Feature Requests
|
|
74
|
-
|
|
75
|
-
We use GitHub issues to track feature requests. Propose a feature by [opening a new issue](https://github.com/nagstler/mcp_on_ruby/issues).
|
|
76
|
-
|
|
77
|
-
**Great Feature Requests** include:
|
|
78
|
-
|
|
79
|
-
- Clear use case description
|
|
80
|
-
- Why this feature would be useful
|
|
81
|
-
- Proposed API or implementation approach
|
|
82
|
-
- Examples of how it would be used
|
|
83
|
-
|
|
84
|
-
## License
|
|
85
|
-
|
|
86
|
-
By contributing, you agree that your contributions will be licensed under the same MIT License that covers the project.
|
data/bin/console
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "bundler/setup"
|
|
5
|
-
require "mcp_on_ruby"
|
|
6
|
-
|
|
7
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
|
-
# with your gem easier. You can also use a different console, if you like.
|
|
9
|
-
|
|
10
|
-
require "irb"
|
|
11
|
-
IRB.start(__FILE__)
|
data/bin/setup
DELETED
data/docs/advanced-usage.md
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
# Advanced Usage
|
|
2
|
-
|
|
3
|
-
## Custom Authorization
|
|
4
|
-
|
|
5
|
-
```ruby
|
|
6
|
-
class ApplicationTool < McpOnRuby::Tool
|
|
7
|
-
def authorize(context)
|
|
8
|
-
token = context[:auth_token]
|
|
9
|
-
user = authenticate_token(token)
|
|
10
|
-
user&.has_permission?(:mcp_access)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
private
|
|
14
|
-
|
|
15
|
-
def authenticate_token(token)
|
|
16
|
-
# Your authentication logic
|
|
17
|
-
JWT.decode(token, Rails.application.secret_key_base).first
|
|
18
|
-
rescue JWT::DecodeError
|
|
19
|
-
nil
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Resource Caching
|
|
25
|
-
|
|
26
|
-
```ruby
|
|
27
|
-
class ApplicationResource < McpOnRuby::Resource
|
|
28
|
-
def read(params = {}, context = {})
|
|
29
|
-
cache_key = "mcp:#{uri}:#{params.hash}"
|
|
30
|
-
Rails.cache.fetch(cache_key, expires_in: 5.minutes) do
|
|
31
|
-
super
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Manual Server Configuration
|
|
38
|
-
|
|
39
|
-
```ruby
|
|
40
|
-
# For advanced scenarios where auto-registration isn't sufficient
|
|
41
|
-
McpOnRuby.mount_in_rails(Rails.application) do |server|
|
|
42
|
-
# Register tools manually
|
|
43
|
-
server.register_tool(CustomTool.new)
|
|
44
|
-
|
|
45
|
-
# Define tools with DSL
|
|
46
|
-
server.tool 'database_query', 'Execute read-only database queries' do |args|
|
|
47
|
-
query = args['query']
|
|
48
|
-
raise 'Only SELECT allowed' unless query.strip.upcase.start_with?('SELECT')
|
|
49
|
-
|
|
50
|
-
result = ActiveRecord::Base.connection.execute(query)
|
|
51
|
-
{ rows: result.to_a }
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Define resources with DSL
|
|
55
|
-
server.resource 'health' do
|
|
56
|
-
{
|
|
57
|
-
status: 'healthy',
|
|
58
|
-
database: database_healthy?,
|
|
59
|
-
redis: redis_healthy?,
|
|
60
|
-
timestamp: Time.current
|
|
61
|
-
}
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Production Configuration
|
|
67
|
-
|
|
68
|
-
### Security Setup
|
|
69
|
-
|
|
70
|
-
```ruby
|
|
71
|
-
# config/initializers/mcp_on_ruby.rb
|
|
72
|
-
McpOnRuby.configure do |config|
|
|
73
|
-
# Authentication
|
|
74
|
-
config.authentication_required = true
|
|
75
|
-
config.authentication_token = ENV['MCP_AUTH_TOKEN']
|
|
76
|
-
|
|
77
|
-
# Security
|
|
78
|
-
config.dns_rebinding_protection = true
|
|
79
|
-
config.allowed_origins = [
|
|
80
|
-
ENV['ALLOWED_ORIGIN'],
|
|
81
|
-
/\A#{Regexp.escape(ENV['DOMAIN'])}\z/
|
|
82
|
-
]
|
|
83
|
-
config.localhost_only = false
|
|
84
|
-
|
|
85
|
-
# Rate limiting
|
|
86
|
-
config.rate_limit_per_minute = 100
|
|
87
|
-
|
|
88
|
-
# Features
|
|
89
|
-
config.cors_enabled = true
|
|
90
|
-
end
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### Monitoring & Logging
|
|
94
|
-
|
|
95
|
-
```ruby
|
|
96
|
-
class ApplicationTool < McpOnRuby::Tool
|
|
97
|
-
def call(arguments = {}, context = {})
|
|
98
|
-
start_time = Time.current
|
|
99
|
-
result = super
|
|
100
|
-
duration = Time.current - start_time
|
|
101
|
-
|
|
102
|
-
Rails.logger.info("MCP Tool executed", {
|
|
103
|
-
tool: name,
|
|
104
|
-
duration: duration,
|
|
105
|
-
success: !result.key?(:error),
|
|
106
|
-
user_ip: context[:remote_ip]
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
result
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### Error Monitoring
|
|
115
|
-
|
|
116
|
-
```ruby
|
|
117
|
-
# config/initializers/mcp_on_ruby.rb
|
|
118
|
-
class CustomTool < ApplicationTool
|
|
119
|
-
def execute(arguments, context)
|
|
120
|
-
# Your tool logic
|
|
121
|
-
rescue => error
|
|
122
|
-
# Report to error monitoring service
|
|
123
|
-
Bugsnag.notify(error, {
|
|
124
|
-
tool: name,
|
|
125
|
-
arguments: arguments,
|
|
126
|
-
context: context
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
raise
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
```
|
data/docs/api-reference.md
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# API Reference
|
|
2
|
-
|
|
3
|
-
## Server Methods
|
|
4
|
-
|
|
5
|
-
```ruby
|
|
6
|
-
server = McpOnRuby.server do |s|
|
|
7
|
-
s.tool(name, description, input_schema, **options, &block)
|
|
8
|
-
s.resource(uri, **options, &block)
|
|
9
|
-
s.register_tool(tool_instance)
|
|
10
|
-
s.register_resource(resource_instance)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# Handle requests
|
|
14
|
-
server.handle_request(json_string, context)
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Tool Class
|
|
18
|
-
|
|
19
|
-
```ruby
|
|
20
|
-
class MyTool < McpOnRuby::Tool
|
|
21
|
-
def initialize(name:, description: '', input_schema: {}, **options)
|
|
22
|
-
def execute(arguments, context) # Override this
|
|
23
|
-
def authorize(context) # Optional override
|
|
24
|
-
end
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Resource Class
|
|
28
|
-
|
|
29
|
-
```ruby
|
|
30
|
-
class MyResource < McpOnRuby::Resource
|
|
31
|
-
def initialize(uri:, name: nil, description: '', mime_type: 'application/json', **options)
|
|
32
|
-
def fetch_content(params, context) # Override this
|
|
33
|
-
def authorize(context) # Optional override
|
|
34
|
-
end
|
|
35
|
-
```
|
data/docs/testing.md
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# Testing Guide
|
|
2
|
-
|
|
3
|
-
## RSpec Integration
|
|
4
|
-
|
|
5
|
-
```ruby
|
|
6
|
-
# spec/tools/user_manager_tool_spec.rb
|
|
7
|
-
require 'rails_helper'
|
|
8
|
-
|
|
9
|
-
RSpec.describe UserManagerTool do
|
|
10
|
-
subject(:tool) { described_class.new }
|
|
11
|
-
|
|
12
|
-
describe '#execute' do
|
|
13
|
-
context 'creating a user' do
|
|
14
|
-
let(:arguments) do
|
|
15
|
-
{
|
|
16
|
-
'action' => 'create',
|
|
17
|
-
'attributes' => { 'name' => 'John Doe', 'email' => 'john@example.com' }
|
|
18
|
-
}
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
it 'creates user successfully' do
|
|
22
|
-
result = tool.call(arguments, { authenticated: true })
|
|
23
|
-
|
|
24
|
-
expect(result[:success]).to be true
|
|
25
|
-
expect(result[:user]['name']).to eq 'John Doe'
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Integration Testing
|
|
33
|
-
|
|
34
|
-
```ruby
|
|
35
|
-
# spec/integration/mcp_server_spec.rb
|
|
36
|
-
require 'rails_helper'
|
|
37
|
-
|
|
38
|
-
RSpec.describe 'MCP Server Integration' do
|
|
39
|
-
let(:server) { Rails.application.config.mcp_server }
|
|
40
|
-
|
|
41
|
-
it 'handles tool calls' do
|
|
42
|
-
request = {
|
|
43
|
-
jsonrpc: '2.0',
|
|
44
|
-
method: 'tools/call',
|
|
45
|
-
params: { name: 'user_manager', arguments: { action: 'create' } },
|
|
46
|
-
id: 1
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
response = server.handle_request(request.to_json)
|
|
50
|
-
parsed = JSON.parse(response)
|
|
51
|
-
|
|
52
|
-
expect(parsed['result']).to be_present
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
```
|
data/examples/claude/README.md
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
# Claude Desktop Integration with MCP on Ruby
|
|
2
|
-
|
|
3
|
-
This example demonstrates how to connect Claude Desktop to your Rails application using MCP on Ruby. We'll walk through the complete setup process that bridges the protocol difference between Claude Desktop (stdio) and Rails (HTTP).
|
|
4
|
-
|
|
5
|
-
## The Challenge
|
|
6
|
-
|
|
7
|
-
Claude Desktop communicates with MCP servers using **stdio** (standard input/output), but MCP on Ruby runs as an **HTTP server** within your Rails application. We need a bridge to convert between these two protocols.
|
|
8
|
-
|
|
9
|
-
## Complete Setup Guide
|
|
10
|
-
|
|
11
|
-
### Step 1: Prepare Your Rails Application
|
|
12
|
-
|
|
13
|
-
1. **Add MCP on Ruby to your Rails app:**
|
|
14
|
-
```bash
|
|
15
|
-
# In your Rails app directory
|
|
16
|
-
bundle add mcp_on_ruby
|
|
17
|
-
rails generate mcp_on_ruby:install
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
2. **Configure MCP settings:**
|
|
21
|
-
```ruby
|
|
22
|
-
# config/initializers/mcp_on_ruby.rb
|
|
23
|
-
McpOnRuby.configure do |config|
|
|
24
|
-
config.authentication_required = true
|
|
25
|
-
config.authentication_token = 'my-secure-token'
|
|
26
|
-
config.rate_limit_per_minute = 60
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
Rails.application.configure do
|
|
30
|
-
config.mcp.enabled = true
|
|
31
|
-
config.mcp.auto_register_tools = true
|
|
32
|
-
end
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
3. **Create a sample tool for testing:**
|
|
36
|
-
```bash
|
|
37
|
-
rails generate mcp_on_ruby:tool UserManager --description "Manage application users"
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
This creates `app/tools/user_manager_tool.rb`. You can customize it or use the default implementation to test the connection.
|
|
41
|
-
|
|
42
|
-
### Step 2: Set Up the Bridge Script
|
|
43
|
-
|
|
44
|
-
1. **Copy the bridge script:**
|
|
45
|
-
Copy `claude-bridge.js` from this directory to your preferred location (e.g., `~/mcp-bridge/claude-bridge.js`)
|
|
46
|
-
|
|
47
|
-
2. **Make it executable:**
|
|
48
|
-
```bash
|
|
49
|
-
chmod +x ~/mcp-bridge/claude-bridge.js
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
3. **Update the bridge script configuration:**
|
|
53
|
-
Edit the top of `claude-bridge.js` to match your setup:
|
|
54
|
-
```javascript
|
|
55
|
-
const RAILS_PORT = 3001; // Your Rails server port
|
|
56
|
-
const AUTH_TOKEN = 'my-secure-token'; // Must match your Rails config
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Step 3: Configure Claude Desktop
|
|
60
|
-
|
|
61
|
-
1. **Locate Claude Desktop config file:**
|
|
62
|
-
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
63
|
-
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
64
|
-
|
|
65
|
-
2. **Add your MCP server configuration:**
|
|
66
|
-
```json
|
|
67
|
-
{
|
|
68
|
-
"mcpServers": {
|
|
69
|
-
"my-rails-app": {
|
|
70
|
-
"command": "node",
|
|
71
|
-
"args": ["/Users/yourusername/mcp-bridge/claude-bridge.js"]
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
Replace `/Users/yourusername/mcp-bridge/claude-bridge.js` with the actual path to your bridge script.
|
|
78
|
-
|
|
79
|
-
### Step 4: Start Everything
|
|
80
|
-
|
|
81
|
-
1. **Start your Rails server:**
|
|
82
|
-
```bash
|
|
83
|
-
cd your-rails-app
|
|
84
|
-
rails server -p 3001
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
2. **Restart Claude Desktop** to load the new MCP server configuration
|
|
88
|
-
|
|
89
|
-
### Step 5: Test the Integration
|
|
90
|
-
|
|
91
|
-
1. Open Claude Desktop
|
|
92
|
-
2. Look for your MCP tools in the available tools (they should appear automatically)
|
|
93
|
-
3. Try asking Claude to use your tools:
|
|
94
|
-
- "Use the user manager tool to help me understand what it does"
|
|
95
|
-
- "What tools are available from my Rails application?"
|
|
96
|
-
- Test any specific functionality you've implemented in your tools
|
|
97
|
-
|
|
98
|
-
## How the Bridge Works
|
|
99
|
-
|
|
100
|
-
```
|
|
101
|
-
┌─────────────────┐ stdio ┌─────────────────┐ HTTP ┌─────────────────┐
|
|
102
|
-
│ Claude │ ←──────────→ │ Bridge │ ←─────────→ │ Rails MCP │
|
|
103
|
-
│ Desktop │ │ Script │ │ Server │
|
|
104
|
-
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
The bridge script (`claude-bridge.js`):
|
|
108
|
-
1. Receives JSON-RPC messages from Claude Desktop via stdin
|
|
109
|
-
2. Adds authentication headers (Bearer token from your configuration)
|
|
110
|
-
3. Forwards requests to Rails at `http://localhost:3001/mcp`
|
|
111
|
-
4. Returns responses to Claude Desktop via stdout
|
|
112
|
-
5. Handles errors and ensures proper JSON-RPC formatting
|
|
113
|
-
|
|
114
|
-
## Directory Structure
|
|
115
|
-
|
|
116
|
-
```
|
|
117
|
-
your-rails-app/
|
|
118
|
-
├── app/
|
|
119
|
-
│ └── tools/
|
|
120
|
-
│ └── user_manager_tool.rb # Your generated tool
|
|
121
|
-
├── config/
|
|
122
|
-
│ └── initializers/
|
|
123
|
-
│ └── mcp_on_ruby.rb # MCP configuration
|
|
124
|
-
└── config/routes.rb # MCP route added automatically
|
|
125
|
-
|
|
126
|
-
~/mcp-bridge/ # Or your preferred location
|
|
127
|
-
└── claude-bridge.js # Bridge script
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## Troubleshooting
|
|
131
|
-
|
|
132
|
-
**Claude Desktop shows "Connection failed":**
|
|
133
|
-
- Ensure Rails server is running on port 3001
|
|
134
|
-
- Check that the bridge script path in Claude config is correct
|
|
135
|
-
|
|
136
|
-
**"Unexpected end of JSON input":**
|
|
137
|
-
- Verify authentication token matches exactly in both Rails config (`my-secure-token`) and bridge script (`AUTH_TOKEN`)
|
|
138
|
-
- Check Rails server logs for authentication errors
|
|
139
|
-
|
|
140
|
-
**Tools not appearing in Claude:**
|
|
141
|
-
- Restart Claude Desktop after configuration changes
|
|
142
|
-
- Verify tools are being auto-registered (check Rails logs)
|
|
143
|
-
|
|
144
|
-
**Bridge script not executing:**
|
|
145
|
-
- Ensure Node.js is installed and accessible
|
|
146
|
-
- Make sure the script has execute permissions
|
|
147
|
-
|
|
148
|
-
## Customizing the Setup
|
|
149
|
-
|
|
150
|
-
To modify ports or authentication:
|
|
151
|
-
|
|
152
|
-
1. **Change Rails port:**
|
|
153
|
-
```bash
|
|
154
|
-
rails server -p 3002 # Use different port
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
2. **Update bridge script:**
|
|
158
|
-
Edit `RAILS_PORT` in `claude-bridge.js`
|
|
159
|
-
|
|
160
|
-
3. **Change authentication token:**
|
|
161
|
-
Update both Rails config (`config.authentication_token`) and `AUTH_TOKEN` in bridge script
|
|
162
|
-
|
|
163
|
-
## What You Get
|
|
164
|
-
|
|
165
|
-
Once connected, Claude Desktop can:
|
|
166
|
-
- Discover and use any tools you create in `app/tools/`
|
|
167
|
-
- Access resources you define in `app/resources/`
|
|
168
|
-
- Interact with your Rails application's data and functionality
|
|
169
|
-
- Provide a natural language interface to your application's capabilities
|
|
170
|
-
|
|
171
|
-
This setup provides a secure, production-ready connection between Claude Desktop and your Rails application via MCP on Ruby.
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const http = require('http');
|
|
4
|
-
const { stdin, stdout, stderr } = process;
|
|
5
|
-
|
|
6
|
-
// MCP server configuration
|
|
7
|
-
const RAILS_HOST = 'localhost';
|
|
8
|
-
const RAILS_PORT = 3001;
|
|
9
|
-
const RAILS_PATH = '/mcp';
|
|
10
|
-
const AUTH_TOKEN = 'test-db-inspector-token';
|
|
11
|
-
|
|
12
|
-
// Set up stdio
|
|
13
|
-
stdin.setEncoding('utf8');
|
|
14
|
-
process.on('SIGTERM', () => process.exit(0));
|
|
15
|
-
process.on('SIGINT', () => process.exit(0));
|
|
16
|
-
|
|
17
|
-
let buffer = '';
|
|
18
|
-
|
|
19
|
-
stdin.on('data', (chunk) => {
|
|
20
|
-
buffer += chunk;
|
|
21
|
-
|
|
22
|
-
// Process complete JSON-RPC messages
|
|
23
|
-
const lines = buffer.split('\n');
|
|
24
|
-
buffer = lines.pop() || ''; // Keep incomplete line
|
|
25
|
-
|
|
26
|
-
for (const line of lines) {
|
|
27
|
-
if (line.trim()) {
|
|
28
|
-
handleRequest(line.trim());
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
stdin.on('end', () => {
|
|
34
|
-
if (buffer.trim()) {
|
|
35
|
-
handleRequest(buffer.trim());
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
async function handleRequest(requestLine) {
|
|
40
|
-
let requestId = 1; // Default ID
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
// Parse and validate JSON-RPC request
|
|
44
|
-
const request = JSON.parse(requestLine);
|
|
45
|
-
requestId = request.id || 1; // Use request ID or default to 1
|
|
46
|
-
|
|
47
|
-
// Forward to Rails MCP server
|
|
48
|
-
const response = await forwardToRails(requestLine);
|
|
49
|
-
|
|
50
|
-
// Parse response to ensure it has correct ID
|
|
51
|
-
try {
|
|
52
|
-
const parsedResponse = JSON.parse(response);
|
|
53
|
-
if (parsedResponse.id === null || parsedResponse.id === undefined) {
|
|
54
|
-
parsedResponse.id = requestId;
|
|
55
|
-
}
|
|
56
|
-
stdout.write(JSON.stringify(parsedResponse) + '\n');
|
|
57
|
-
} catch {
|
|
58
|
-
// If response is not valid JSON, create proper response
|
|
59
|
-
const successResponse = {
|
|
60
|
-
jsonrpc: "2.0",
|
|
61
|
-
id: requestId,
|
|
62
|
-
result: { message: response }
|
|
63
|
-
};
|
|
64
|
-
stdout.write(JSON.stringify(successResponse) + '\n');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
} catch (error) {
|
|
68
|
-
// Send JSON-RPC error response with proper ID
|
|
69
|
-
const errorResponse = {
|
|
70
|
-
jsonrpc: "2.0",
|
|
71
|
-
id: requestId,
|
|
72
|
-
error: {
|
|
73
|
-
code: -32700,
|
|
74
|
-
message: "Parse error",
|
|
75
|
-
data: error.message
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
stdout.write(JSON.stringify(errorResponse) + '\n');
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function forwardToRails(requestData) {
|
|
83
|
-
return new Promise((resolve, reject) => {
|
|
84
|
-
const options = {
|
|
85
|
-
hostname: RAILS_HOST,
|
|
86
|
-
port: RAILS_PORT,
|
|
87
|
-
path: RAILS_PATH,
|
|
88
|
-
method: 'POST',
|
|
89
|
-
headers: {
|
|
90
|
-
'Content-Type': 'application/json',
|
|
91
|
-
'Authorization': `Bearer ${AUTH_TOKEN}`,
|
|
92
|
-
'Content-Length': Buffer.byteLength(requestData)
|
|
93
|
-
},
|
|
94
|
-
timeout: 30000
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const req = http.request(options, (res) => {
|
|
98
|
-
let responseData = '';
|
|
99
|
-
|
|
100
|
-
res.on('data', (chunk) => {
|
|
101
|
-
responseData += chunk;
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
res.on('end', () => {
|
|
105
|
-
resolve(responseData);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
req.on('error', (error) => {
|
|
110
|
-
// Return error as string to be wrapped in proper JSON-RPC format
|
|
111
|
-
resolve(`Connection failed: ${error.message}`);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
req.on('timeout', () => {
|
|
115
|
-
req.destroy();
|
|
116
|
-
resolve('Request timeout');
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
req.write(requestData);
|
|
120
|
-
req.end();
|
|
121
|
-
});
|
|
122
|
-
}
|