|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +# This example demonstrates how to use ActiveRecord storage with RubyMCP in a Rails application |
| 4 | + |
| 5 | +# Step 1: Add gems to your Gemfile |
| 6 | +# gem 'mcp_on_ruby', '~> 0.3.0' |
| 7 | +# gem 'activerecord', '~> 6.1' # Usually already included in Rails |
| 8 | +# gem 'sqlite3', '~> 1.4' # Or your preferred database adapter |
| 9 | + |
| 10 | +############################################### |
| 11 | +# Step 2: Create an initializer |
| 12 | +# config/initializers/ruby_mcp.rb |
| 13 | +############################################### |
| 14 | + |
| 15 | +require 'ruby_mcp' |
| 16 | + |
| 17 | +Rails.application.config.to_prepare do |
| 18 | + RubyMCP.configure do |config| |
| 19 | + # Configure LLM providers |
| 20 | + config.providers = { |
| 21 | + openai: { api_key: ENV['OPENAI_API_KEY'] }, |
| 22 | + anthropic: { api_key: ENV['ANTHROPIC_API_KEY'] } |
| 23 | + } |
| 24 | + |
| 25 | + # Use ActiveRecord storage with Rails database |
| 26 | + config.storage = :active_record |
| 27 | + config.active_record = { |
| 28 | + # Uses Rails database connection automatically |
| 29 | + table_prefix: "mcp_#{Rails.env}_" # Environment-specific prefix |
| 30 | + } |
| 31 | + |
| 32 | + # Enable authentication in production |
| 33 | + if Rails.env.production? |
| 34 | + config.auth_required = true |
| 35 | + config.jwt_secret = ENV['JWT_SECRET'] |
| 36 | + end |
| 37 | + end |
| 38 | + |
| 39 | + # Log configuration |
| 40 | + Rails.logger.info "Configured RubyMCP with providers: #{RubyMCP.configuration.providers.keys.join(', ')}" |
| 41 | + Rails.logger.info "Using ActiveRecord storage with prefix: #{RubyMCP.configuration.active_record[:table_prefix]}" |
| 42 | +end |
| 43 | + |
| 44 | +############################################### |
| 45 | +# Step 3: Mount the MCP server in routes.rb |
| 46 | +# config/routes.rb |
| 47 | +############################################### |
| 48 | + |
| 49 | +Rails.application.routes.draw do |
| 50 | + # Mount RubyMCP at /api/mcp |
| 51 | + mount_mcp_at = '/api/mcp' |
| 52 | + |
| 53 | + Rails.application.config.middleware.use Rack::Config do |env| |
| 54 | + env['SCRIPT_NAME'] = mount_mcp_at if env['PATH_INFO'].start_with?(mount_mcp_at) |
| 55 | + end |
| 56 | + |
| 57 | + mount RubyMCP::Server::App.new.rack_app, at: mount_mcp_at |
| 58 | + |
| 59 | + # Rest of your routes... |
| 60 | +end |
| 61 | + |
| 62 | +############################################### |
| 63 | +# Step 4: (Optional) Create a migration |
| 64 | +# This is only needed if you prefer migration-based setup |
| 65 | +# instead of automatic table creation |
| 66 | +############################################### |
| 67 | + |
| 68 | +# Run: rails generate migration CreateMcpTables |
| 69 | +# Then edit the migration file: |
| 70 | + |
| 71 | +class CreateMcpTables < ActiveRecord::Migration[6.1] |
| 72 | + def change |
| 73 | + prefix = "mcp_#{Rails.env}_" |
| 74 | + |
| 75 | + create_table "#{prefix}contexts" do |t| |
| 76 | + t.string :external_id, null: false |
| 77 | + t.text :metadata, default: '{}' |
| 78 | + t.timestamps |
| 79 | + |
| 80 | + t.index :external_id, unique: true |
| 81 | + t.index :updated_at |
| 82 | + end |
| 83 | + |
| 84 | + create_table "#{prefix}messages" do |t| |
| 85 | + t.references :context, null: false, foreign_key: { to_table: "#{prefix}contexts", on_delete: :cascade } |
| 86 | + t.string :external_id, null: false |
| 87 | + t.string :role, null: false |
| 88 | + t.text :content, null: false |
| 89 | + t.text :metadata, default: '{}' |
| 90 | + t.timestamps |
| 91 | + |
| 92 | + t.index %i[context_id external_id], unique: true |
| 93 | + end |
| 94 | + |
| 95 | + create_table "#{prefix}contents" do |t| |
| 96 | + t.references :context, null: false, foreign_key: { to_table: "#{prefix}contexts", on_delete: :cascade } |
| 97 | + t.string :external_id, null: false |
| 98 | + t.binary :data_binary |
| 99 | + t.text :data_json |
| 100 | + t.string :content_type |
| 101 | + t.timestamps |
| 102 | + |
| 103 | + t.index %i[context_id external_id], unique: true |
| 104 | + end |
| 105 | + end |
| 106 | +end |
| 107 | + |
| 108 | +############################################### |
| 109 | +# Step 5: Using RubyMCP with ActiveRecord in your application |
| 110 | +############################################### |
| 111 | + |
| 112 | +# app/controllers/conversations_controller.rb |
| 113 | +class ConversationsController < ApplicationController |
| 114 | + def create |
| 115 | + # Create a new MCP context |
| 116 | + client = RubyMCP.client |
| 117 | + |
| 118 | + # Create a context with a system message |
| 119 | + context = client.create_context( |
| 120 | + [{ role: 'system', content: 'You are a helpful assistant.' }], |
| 121 | + { user_id: current_user.id } # Store metadata |
| 122 | + ) |
| 123 | + |
| 124 | + # Store the context ID in your database |
| 125 | + conversation = current_user.conversations.create( |
| 126 | + title: 'New Conversation', |
| 127 | + mcp_context_id: context.id |
| 128 | + ) |
| 129 | + |
| 130 | + redirect_to conversation_path(conversation) |
| 131 | + end |
| 132 | + |
| 133 | + def show |
| 134 | + @conversation = current_user.conversations.find(params[:id]) |
| 135 | + |
| 136 | + # Get the MCP context |
| 137 | + client = RubyMCP.client |
| 138 | + @context = client.get_context(@conversation.mcp_context_id) |
| 139 | + |
| 140 | + # Render the conversation UI |
| 141 | + end |
| 142 | + |
| 143 | + def message |
| 144 | + @conversation = current_user.conversations.find(params[:id]) |
| 145 | + |
| 146 | + # Add user message to context |
| 147 | + client = RubyMCP.client |
| 148 | + client.add_message( |
| 149 | + @conversation.mcp_context_id, |
| 150 | + 'user', |
| 151 | + params[:content] |
| 152 | + ) |
| 153 | + |
| 154 | + # Generate AI response |
| 155 | + response = client.generate( |
| 156 | + @conversation.mcp_context_id, |
| 157 | + 'openai/gpt-4', |
| 158 | + temperature: 0.7 |
| 159 | + ) |
| 160 | + |
| 161 | + # Response is automatically added to the context |
| 162 | + |
| 163 | + # Return the response |
| 164 | + render json: { content: response[:content] } |
| 165 | + end |
| 166 | +end |
0 commit comments