{"id":26193473,"url":"https://github.com/seuros/action_mcp","last_synced_at":"2026-01-24T18:10:03.968Z","repository":{"id":281747570,"uuid":"932751598","full_name":"seuros/action_mcp","owner":"seuros","description":"Rails Engine with MCP compliant Spec.","archived":false,"fork":false,"pushed_at":"2026-01-09T19:15:46.000Z","size":1204,"stargazers_count":85,"open_issues_count":0,"forks_count":13,"subscribers_count":3,"default_branch":"master","last_synced_at":"2026-01-18T00:22:17.100Z","etag":null,"topics":["ai","authentication","claude","gateway","gem","http","json-rpc","language-model","llm","mcp","mcp-client","mcp-server","model-context-protocol","prompt-engineering","rails","rest-api","ruby","tool-use","vscode","websocket"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/seuros.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-02-14T13:04:19.000Z","updated_at":"2026-01-13T01:13:11.000Z","dependencies_parsed_at":"2025-03-11T00:24:06.255Z","dependency_job_id":"4623e326-b93c-4b29-847d-96be3cc50de1","html_url":"https://github.com/seuros/action_mcp","commit_stats":null,"previous_names":["seuros/action_mcp"],"tags_count":172,"template":false,"template_full_name":null,"purl":"pkg:github/seuros/action_mcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seuros%2Faction_mcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seuros%2Faction_mcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seuros%2Faction_mcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seuros%2Faction_mcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seuros","download_url":"https://codeload.github.com/seuros/action_mcp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seuros%2Faction_mcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28733543,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-24T17:51:25.893Z","status":"ssl_error","status_checked_at":"2026-01-24T17:50:48.377Z","response_time":89,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","authentication","claude","gateway","gem","http","json-rpc","language-model","llm","mcp","mcp-client","mcp-server","model-context-protocol","prompt-engineering","rails","rest-api","ruby","tool-use","vscode","websocket"],"created_at":"2025-03-12T01:49:47.338Z","updated_at":"2026-01-24T18:10:03.917Z","avatar_url":"https://github.com/seuros.png","language":"Ruby","funding_links":[],"categories":["Ruby","📚 Projects (2474 total)"],"sub_categories":["MCP Servers"],"readme":"# ActionMCP\n\n**ActionMCP** is a Ruby gem focused on providing Model Context Protocol (MCP) capability to Ruby on Rails applications, specifically as a server.\n\nActionMCP is designed for production Rails environments and does **not** support STDIO transport. STDIO is not included because it is not production-ready and is only suitable for desktop or script-based use cases. Instead, ActionMCP is built for robust, network-based deployments.\n\nThe client functionality in ActionMCP is intended to connect to remote MCP servers, not to local processes via STDIO.\n\nIt offers base classes and helpers for creating MCP applications, making it easier to integrate your Ruby/Rails application with the MCP standard.\n\nWith ActionMCP, you can focus on your app's logic while it handles the boilerplate for MCP compliance.\n\n## Introduction\n\n**Model Context Protocol (MCP)** is an open protocol that standardizes how applications provide context to large language models (LLMs).\n\nThink of it as a universal interface for connecting AI assistants to external data sources and tools.\n\nMCP allows AI systems to plug into various resources in a consistent, secure way, enabling two-way integration between your data and AI-powered applications.\n\nThis means an AI (like an LLM) can request information or actions from your application through a well-defined protocol, and your app can provide context or perform tasks for the AI in return.\n\n**ActionMCP** is targeted at developers building MCP-enabled Rails applications. It simplifies the process of integrating Ruby and Rails apps with the MCP standard by providing a set of base classes and an easy-to-use server interface.\n\n## Protocol Support\n\nActionMCP supports **MCP 2025-06-18** (current) with backward compatibility for **MCP 2025-03-26**. The protocol implementation is fully compliant with the MCP specification, including:\n\n- **JSON-RPC 2.0** transport layer\n- **Capability negotiation** during initialization\n- **Error handling** with proper error codes (-32601 for method not found, -32002 for consent required)\n- **Session management** with resumable sessions\n- **Change notifications** for dynamic capability updates\n\nFor a detailed (and entertaining) breakdown of protocol versions, features, and our design decisions, see [The Hitchhiker's Guide to MCP](The_Hitchhikers_Guide_to_MCP.md).\n\n*Don't Panic: The guide contains everything you need to know about surviving MCP protocol versions.*\n\n\u003e **Note:** STDIO transport is not supported in ActionMCP. This gem is focused on production-ready, network-based deployments. STDIO is only suitable for desktop or script-based experimentation and is intentionally excluded.\n\nInstead of implementing MCP support from scratch, you can subclass and configure the provided **Prompt**, **Tool**, and **ResourceTemplate** classes to expose your app's functionality to LLMs.\n\nActionMCP handles the underlying MCP message format and routing, so you can adhere to the open standard with minimal effort.\n\nIn short, ActionMCP helps you build an MCP server (the component that exposes capabilities to AI) more quickly and with fewer mistakes.\n\n\u003e **Client connections:** The client part of ActionMCP is meant to connect to remote MCP servers only. Connecting to local processes (such as via STDIO) is not supported.\n\n## Requirements\n\n- **Ruby**: 3.4.8+ or 4.0.0+\n- **Rails**: 8.1.1+\n- **Database**: PostgreSQL, MySQL, or SQLite3\n\nActionMCP is tested against Ruby 3.4.8 and 4.0.0 with Rails 8.1.1+.\n\n## Installation\n\nTo start using ActionMCP, add it to your project:\n\n```bash\n# Add gem to your Gemfile\n$ bundle add actionmcp\n\n# Install dependencies\nbundle install\n\n# Copy migrations from the engine\nbin/rails action_mcp:install:migrations\n\n# Generate base classes and configuration\nbin/rails generate action_mcp:install\n\n# Create necessary database tables\nbin/rails db:migrate\n```\n\nThe `action_mcp:install` generator will:\n- Create base application classes (ApplicationGateway, ApplicationMCPTool, etc.)\n- Generate the MCP configuration file (`config/mcp.yml`)\n- Set up the basic directory structure for MCP components (`app/mcp/`)\n\nDatabase migrations are copied separately using `bin/rails action_mcp:install:migrations`.\n\n## Core Components\n\nActionMCP provides three core abstractions to streamline MCP server development:\n\n### ActionMCP::Prompt\n\n`ActionMCP::Prompt` enables you to create reusable prompt templates that can be discovered and used by LLMs. Each prompt is defined as a Ruby class that inherits from `ApplicationMCPPrompt`.\n\nKey features:\n- Define expected arguments with descriptions and validation rules\n- Build multi-step conversations with mixed content types\n- Support for text, images, audio, and resource attachments\n- Add messages with different roles (user/assistant)\n\n**Example:**\n\n```ruby\nclass AnalyzeCodePrompt \u003c ApplicationMCPPrompt\n  prompt_name \"analyze_code\"\n  description \"Analyze code for potential improvements\"\n\n  argument :language, description: \"Programming language\", default: \"Ruby\"\n  argument :code, description: \"Code to explain\", required: true\n\n  validates :language, inclusion: { in: %w[Ruby Python JavaScript] }\n\n  def perform\n    render(text: \"Please analyze this #{language} code for improvements:\")\n    render(text: code)\n\n    # You can add assistant messages too\n    render(text: \"Here are some things to focus on in your analysis:\", role: :assistant)\n\n    # Even add resources if needed\n    render(resource: \"file://documentation/#{language.downcase}_style_guide.pdf\",\n           mime_type: \"application/pdf\",\n           blob: get_style_guide_pdf(language))\n  end\n\n  private\n\n  def get_style_guide_pdf(language)\n    # Implementation to retrieve style guide as base64\n  end\nend\n```\n\nPrompts can be executed by instantiating them and calling the `call` method:\n\n```ruby\nanalyze_prompt = AnalyzeCodePrompt.new(language: \"Ruby\", code: \"def hello; puts 'Hello, world!'; end\")\nresult = analyze_prompt.call\n```\n\n### ActionMCP::Tool\n\n`ActionMCP::Tool` allows you to create interactive functions that LLMs can call with arguments to perform specific tasks. Each tool is a Ruby class that inherits from `ApplicationMCPTool`.\n\nKey features:\n- Define input properties with types, descriptions, and validation\n- Return multiple response types (text, images, errors)\n- Progressive responses with multiple render calls\n- Automatic input validation based on property definitions\n- **Consent management for sensitive operations**\n\n**Example:**\n\n```ruby\nclass CalculateSumTool \u003c ApplicationMCPTool\n  tool_name \"calculate_sum\"\n  description \"Calculate the sum of two numbers\"\n\n  property :a, type: \"number\", description: \"The first number\", required: true\n  property :b, type: \"number\", description: \"The second number\", required: true\n\n  def perform\n    sum = a + b\n    render(text: \"Calculating #{a} + #{b}...\")\n    render(text: \"The sum is #{sum}\")\n\n    # You can report errors if needed\n    if sum \u003e 1000\n      report_error(\"Warning: Sum exceeds recommended limit\")\n    end\n\n    # Or even images\n    render(image: generate_visualization(a, b), mime_type: \"image/png\")\n  end\n\n  private\n\n  def generate_visualization(a, b)\n    # Implementation to create a visualization as base64\n  end\nend\n```\n\n#### Consent Management\n\nFor tools that perform sensitive operations (file system access, database modifications, external API calls), you can require explicit user consent:\n\n```ruby\nclass FileSystemTool \u003c ApplicationMCPTool\n  tool_name \"read_file\"\n  description \"Read contents of a file\"\n\n  # Require explicit consent before execution\n  requires_consent!\n\n  property :file_path, type: \"string\", description: \"Path to file\", required: true\n\n  def perform\n    # This code only runs after user grants consent\n    content = File.read(file_path)\n    render(text: \"File contents: #{content}\")\n  end\nend\n```\n\n**Consent Flow:**\n1. When a consent-required tool is called without consent, it returns a JSON-RPC error with code `-32002`\n2. The client must explicitly grant consent for the specific tool\n3. Once granted, the tool can execute normally for that session\n4. Consent is session-scoped and can be revoked at any time\n\n**Managing Consent:**\n\n```ruby\n# Check if consent is granted\nsession.consent_granted_for?(\"read_file\")\n\n# Grant consent for a tool\nsession.grant_consent(\"read_file\")\n\n# Revoke consent\nsession.revoke_consent(\"read_file\")\n```\n\nTools can be executed by instantiating them and calling the `call` method:\n\n```ruby\nsum_tool = CalculateSumTool.new(a: 5, b: 10)\nresult = sum_tool.call\n```\n\n#### Structured output (output_schema)\n\nAdvertise a JSON Schema for your tool's structuredContent and return machine-validated results alongside any text output.\n\n```ruby\nclass PriceQuoteTool \u003c ApplicationMCPTool\n  tool_name \"price_quote\"\n  description \"Return a structured price quote\"\n\n  property :sku, type: \"string\", description: \"SKU to price\", required: true\n\n  output_schema do\n    string :sku, required: true, description: \"SKU that was priced\"\n    number :price_cents, required: true, description: \"Total price in cents\"\n    object :meta do\n      string :currency, required: true, enum: %w[USD EUR GBP]\n      boolean :cached, default: false\n    end\n  end\n\n  def perform\n    price_cents = lookup_price_cents(sku) # Implement your lookup\n\n    render structured: { sku: sku,\n                         price_cents: price_cents,\n                         meta: { currency: \"USD\", cached: false } }\n  end\nend\n```\n\nThe schema is included in the tool definition, and the `structured` payload is emitted as `structuredContent` in the response while remaining compatible with text/audio/image renders.\n\n#### Returning resource links from a tool\n\nWhen you want to hand back a URI instead of embedding the payload, use the built-in `render_resource_link`, which produces the MCP `resource_link` content type.\n\n```ruby\nclass ReportLinkTool \u003c ApplicationMCPTool\n  tool_name \"report_link\"\n  description \"Return a downloadable report link\"\n\n  property :report_id, type: \"string\", required: true\n\n  def perform\n    render_resource_link(\n      uri: \"reports://#{report_id}.json\",\n      name: \"Report #{report_id}\",\n      description: \"Downloadable JSON for report #{report_id}\",\n      mime_type: \"application/json\"\n    )\n  end\nend\n```\n\nClients can resolve the URI with a separate `resources/read` call, keeping tool responses lightweight while still discoverable.\n\n#### Task-augmented tools (async execution with progress)\n\nUse MCP Tasks when work might take seconds/minutes. Advertise task support with `task_required!` (or `task_optional!`) and let callers opt in by sending `_meta.task` on `tools/call`. While running as a task, you can emit progress updates with `report_progress!`.\n\n```ruby\nclass BatchIndexTool \u003c ApplicationMCPTool\n  tool_name \"batch_index\"\n  description \"Index many items asynchronously with progress updates\"\n\n  task_required! # advertise that this tool is intended to run as a task\n  property :items, type: \"array_string\", description: \"Items to index\", required: true\n\n  def perform\n    total = items.length\n    items.each_with_index do |item, idx|\n      index_item(item) # your indexing logic\n\n      percent = ((idx + 1) * 100.0 / total).round\n      report_progress!(percent: percent, message: \"Indexed #{idx + 1}/#{total}\")\n    end\n\n    render(text: \"Indexed #{total} items\")\n  end\n\n  private\n\n  def index_item(item)\n    # Implement your indexing logic here\n  end\nend\n```\n\nCall it as a task from a client by adding `_meta.task` (creates a Task record and runs the tool via `ToolExecutionJob`):\n\n```json\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"batch_index\",\n    \"arguments\": { \"items\": [\"a\", \"b\", \"c\"] },\n    \"_meta\": { \"task\": { \"ttl\": 120000, \"pollInterval\": 2000 } }\n  }\n}\n```\n\nPoll task status with `tasks/get` or fetch the result when finished with `tasks/result`. Use `tasks/cancel` to stop non-terminal tasks.\n\n### ActionMCP::ResourceTemplate\n\n`ActionMCP::ResourceTemplate` facilitates the creation of URI templates for dynamic resources that LLMs can access.\nThis allows models to request specific data using parameterized URIs.\n\n**Example:**\n\n```ruby\n\nclass ProductResourceTemplate \u003c ApplicationMCPResTemplate\n  uri_template \"product/{id}\"\n  description \"Access product information by ID\"\n\n  parameter :id, description: \"Product identifier\", required: true\n\n  validates :id, format: { with: /\\A\\d+\\z/, message: \"must be numeric\" }\n\n  def resolve\n    product = Product.find_by(id: id)\n    return unless product\n    ActionMCP::Resource.new(\n      uri: \"ecommerce://products/#{product_id}\",\n      name: \"Product #{product_id}\",\n      description: \"Product information for product #{product_id}\",\n      mime_type: \"application/json\",\n      size: product.to_json.length\n    )\n  end\nend\n```\n\n# Example of callbacks:\n\n```ruby\nbefore_resolve do |template|\n  # Starting to resolve product: #{template.product_id}\nend\n\nafter_resolve do |template|\n  # Finished resolving product resource for product: #{template.product_id}\nend\n\naround_resolve do |template, block|\n  start_time = Time.current\n  # Starting resolution for product: #{template.product_id}\n\n  resource = block.call\n\n  if resource\n    # Product #{template.product_id} resolved successfully in #{Time.current - start_time}s\n  else\n    # Product #{template.product_id} not found\n  end\n\n  resource\nend\n```\n\nResource templates are automatically registered and used when LLMs request resources matching their patterns.\n\n## 📚 Documentation\n\nActionMCP provides comprehensive documentation across multiple specialized guides. Each guide focuses on specific aspects to keep information organized and prevent context overload:\n\n### Getting Started \u0026 Setup\n- **[Installation \u0026 Configuration](README.md#installation)** - Initial setup, database migrations, and basic configuration\n- **[Authentication with Gateway](README.md#authentication-with-gateway)** - User authentication and authorization patterns\n\n### Component Development\n- **[📋 TOOLS.MD](TOOLS.MD)** - Complete guide to developing MCP tools\n  - Generator usage and best practices\n  - Property definitions, validation, and consent management\n  - Output schemas for structured responses\n  - Error handling, testing, and security considerations\n  - Advanced features like additional properties and authentication context\n\n- **[📝 PROMPTS.MD](PROMPTS.MD)** - Prompt template development guide\n  - Creating reusable prompt templates\n  - Multi-step conversations and mixed content types\n  - Argument validation and prompt chaining\n\n- **[🔗 RESOURCE_TEMPLATES.md](RESOURCE_TEMPLATES.md)** - Resource template implementation\n  - URI template patterns and parameter extraction\n  - Dynamic resource resolution and collections\n  - Callbacks and validation patterns\n\n### Client \u0026 Integration\n- **[🔌 CLIENTUSAGE.MD](CLIENTUSAGE.MD)** - Complete client implementation guide\n  - Session management and resumability\n  - Transport configuration and connection handling\n  - Tool, prompt, and resource collections\n  - Production deployment patterns\n- **[🔐 GATEWAY.md](GATEWAY.md)** - Authentication gateway guide\n  - Implementing `ApplicationGateway`\n  - Identifier handling via `ActionMCP::Current`\n  - Auth patterns, error handling, and hardening tips\n\n### Protocol \u0026 Technical Details\n- **[🚀 The Hitchhiker's Guide to MCP](The_Hitchhikers_Guide_to_MCP.md)** - Protocol versions and migration\n  - Comprehensive comparison of MCP protocol versions (2024-11-05, 2025-03-26, 2025-06-18)\n  - Design decisions and architectural rationale\n  - Migration paths and compatibility considerations\n  - Feature evolution and technical specifications (*Don't Panic!*)\n\n### Advanced Configuration\n- **[Session Storage](README.md#session-storage)** - Volatile vs ActiveRecord vs custom session stores\n- **[Thread Pool Management](README.md#thread-pool-management)** - Performance tuning and graceful shutdown\n- **[Profiles System](README.md#profiles)** - Multi-tenant capability filtering\n- **[Production Deployment](README.md#production-deployment-of-mcps0)** - Falcon, Unix sockets, and reverse proxy setup\n\n### Development \u0026 Testing\n- **[Generators](README.md#generators)** - Rails generators for scaffolding components\n- **[Testing with TestHelper](README.md#testing-with-testhelper)** - Comprehensive testing strategies\n- **[Development Commands](README.md#development-commands)** - Rake tasks for debugging and inspection\n- **[MCP Inspector Integration](README.md#inspecting-your-mcp-server)** - Interactive testing and validation\n\n### Troubleshooting \u0026 Production\n- **[Error Handling](README.md#error-handling-and-troubleshooting)** - JSON-RPC error codes and debugging\n- **[Production Considerations](README.md#production-considerations)** - Security, performance, and monitoring\n- **[Middleware Conflicts](README.md#dealing-with-middleware-conflicts)** - Using `mcp_vanilla.ru` for production\n\n\u003e **💡 Pro Tip**: Start with the component-specific guides (TOOLS.MD, PROMPTS.MD, RESOURCE_TEMPLATES.md) for hands-on development, then reference the Hitchhiker's Guide for protocol details and CLIENTUSAGE.MD for integration patterns.\n\n## Configuration\n\nActionMCP is configured via `config.action_mcp` in your Rails application.\n\nBy default, the name is set to your application's name and the version defaults to \"0.0.1\" unless your app has a version file.\n\nYou can override these settings in your configuration (e.g., in `config/application.rb`):\n\n```ruby\nmodule Tron\n  class Application \u003c Rails::Application\n    config.action_mcp.name = \"Friendly MCP (Master Control Program)\"  # defaults to Rails.application.name\n    config.action_mcp.version = \"1.2.3\"                               # defaults to \"0.0.1\"\n    config.action_mcp.logging_enabled = true                          # defaults to true\n    config.action_mcp.logging_level = :info                           # defaults to :info, can be :debug, :info, :warn, :error, :fatal\n\n    # Server instructions - helps LLMs understand the server's purpose\n    config.action_mcp.server_instructions = [\n      \"Use this server to access and control Tron system programs\",\n      \"Helpful for managing system processes and user data\"\n    ]\n  end\nend\n```\n\nFor dynamic versioning, consider adding the `rails_app_version` gem.\n\n\n### PubSub Configuration\n\nActionMCP uses a pub/sub system for real-time communication. You can choose between several adapters:\n\n1. **SolidMCP** - Database-backed pub/sub (no Redis required)\n2. **Simple** - In-memory pub/sub for development and testing\n3. **Redis** - Redis-backed pub/sub (if you prefer Redis)\n\n#### Migrating from ActionCable\n\nIf you were previously using ActionCable with ActionMCP, you will need to migrate to the new PubSub system. Here's how:\n\n1. Remove the ActionCable dependency from your Gemfile (if you don't need it for other purposes)\n2. Install one of the PubSub adapters (SolidMCP recommended)\n3. Create a configuration file at `config/mcp.yml` (you can use the generator: `bin/rails g action_mcp:config`)\n4. Run your tests to ensure everything works correctly\n\nThe new PubSub system maintains the same API as the previous ActionCable-based implementation, so your existing code should continue to work without changes.\n\nConfigure your adapter in `config/mcp.yml`:\n\n```yaml\ndevelopment:\n  adapter: solid_mcp\n  polling_interval: 0.1.seconds\n  # Thread pool configuration (optional)\n  # min_threads: 5     # Minimum number of threads in the pool\n  # max_threads: 10    # Maximum number of threads in the pool\n  # max_queue: 100     # Maximum number of tasks that can be queued\n\ntest:\n  adapter: test    # Uses the simple in-memory adapter\n\nproduction:\n  adapter: solid_mcp\n  polling_interval: 0.5.seconds\n  # Optional: connects_to: cable  # If using a separate database\n\n  # Thread pool configuration for high-traffic environments\n  min_threads: 10     # Minimum number of threads in the pool\n  max_threads: 20     # Maximum number of threads in the pool\n  max_queue: 500      # Maximum number of tasks that can be queued\n```\n\n### Server Instructions\n\nServer instructions help LLMs understand **what your server is for** and **when to use it**. They describe the server's purpose and goal, not technical details like rate limits or authentication (tools are self-documented via their own descriptions).\n\nInstructions are returned at the top level of the MCP initialization response.\n\nYou can configure server instructions in your `config/mcp.yml` file:\n\n```yaml\nshared:\n  # Describe the server's purpose - helps LLMs know when to use this server\n  server_instructions:\n    - \"Use this server to manage Fizzy project tickets and workflows\"\n    - \"Helpful for tracking bugs, features, and sprint planning\"\n\ndevelopment:\n  # Development-specific purpose description\n  server_instructions:\n    - \"Development server for testing Fizzy integration\"\n    - \"Use for prototyping ticket management workflows\"\n\nproduction:\n  # Production-specific purpose description\n  server_instructions:\n    - \"Production Fizzy server for managing live project data\"\n```\n\nInstructions are sent as a single string (joined by newlines) at the top level of the initialization response, helping LLMs understand your server's purpose.\n\n#### SolidMCP (Database-backed, Recommended)\n\nFor SolidMCP, add it to your Gemfile:\n\n```ruby\ngem \"solid_mcp\"  # Database-backed adapter optimized for MCP\n```\n\nThen install it:\n\n```bash\nbundle install\nbin/rails solid_mcp:install:migrations\nbin/rails db:migrate\n```\n\nThe installer will create the necessary database migration for message storage. Configure it in your `config/mcp.yml`.\n\n#### Redis Adapter\n\nIf you prefer Redis, add it to your Gemfile:\n\n```ruby\ngem \"redis\", \"~\u003e 5.0\"\n```\n\nThen configure the Redis adapter in your `config/mcp.yml`:\n\n```yaml\nproduction:\n  adapter: redis\n  url: \u003c%= ENV.fetch(\"REDIS_URL\") { \"redis://localhost:6379/1\" } %\u003e\n  channel_prefix: your_app_production\n\n  # Thread pool configuration for high-traffic environments\n  min_threads: 10     # Minimum number of threads in the pool\n  max_threads: 20     # Maximum number of threads in the pool\n  max_queue: 500      # Maximum number of tasks that can be queued\n```\n\n## Session Storage\n\nActionMCP provides a pluggable session storage system that allows you to choose how sessions are persisted based on your environment and requirements.\n\n### Session Store Types\n\nActionMCP includes three session store implementations:\n\n1. **`:volatile`** - In-memory storage using Concurrent::Hash\n   - Default for development and test environments\n   - Sessions are lost on server restart\n   - Fast and lightweight for local development\n   - No external dependencies\n\n2. **`:active_record`** - Database-backed storage\n   - Default for production environment\n   - Sessions persist across server restarts\n   - Supports session resumability\n   - Requires database migrations\n\n3. **`:test`** - Special store for testing\n   - Tracks notifications and method calls\n   - Provides assertion helpers\n   - Automatically used in test environment when using TestHelper\n\n### Configuration\n\nYou can configure the session store type in your Rails configuration or `config/mcp.yml`:\n\n```ruby\n# config/application.rb or environment files\nRails.application.configure do\n  config.action_mcp.session_store_type = :active_record  # or :volatile\nend\n```\n\nOr in `config/mcp.yml`:\n\n```yaml\n# Global session store type (used by both client and server)\nsession_store_type: volatile\n\n# Client-specific session store type (falls back to session_store_type if not specified)\nclient_session_store_type: volatile\n\n# Server-specific session store type (falls back to session_store_type if not specified)\nserver_session_store_type: active_record\n```\n\nThe defaults are:\n- Production: `:active_record`\n- Development: `:volatile`\n- Test: `:volatile` (or `:test` when using TestHelper)\n\n### Separate Client and Server Session Stores\n\nYou can configure different session store types for client and server operations:\n\n- **`session_store_type`**: Global setting used by both client and server when specific types aren't set\n- **`client_session_store_type`**: Session store used by ActionMCP client connections (falls back to global setting)\n- **`server_session_store_type`**: Session store used by ActionMCP server sessions (falls back to global setting)\n\nThis allows you to optimize each component separately. For example, you might use volatile storage for client sessions (faster, temporary) while using persistent storage for server sessions (maintains state across restarts).\n\n### Using Different Session Stores\n\n```ruby\n# The session store is automatically selected based on configuration\n# You can access it directly if needed:\nsession_store = ActionMCP::Server.session_store\n\n# Create a session\nsession = session_store.create_session(session_id, {\n  status: \"initialized\",\n  protocol_version: \"2025-03-26\",\n  # ... other session attributes\n})\n\n# Load a session\nsession = session_store.load_session(session_id)\n\n# Update a session\nsession_store.update_session(session_id, { status: \"active\" })\n\n# Delete a session\nsession_store.delete_session(session_id)\n```\n\n### Session Resumability\n\nWith the `:active_record` store, clients can resume sessions after disconnection:\n\n```ruby\n# Client includes session ID in request headers\n# Server automatically resumes the existing session\nheaders[\"Mcp-Session-Id\"] = \"existing-session-id\"\n\n# If the session exists, it will be resumed\n# If not, a new session will be created\n```\n\n### Custom Session Stores\n\nYou can create custom session stores by inheriting from `ActionMCP::Server::SessionStore::Base`:\n\n```ruby\nclass MyCustomSessionStore \u003c ActionMCP::Server::SessionStore::Base\n  def create_session(session_id, payload = {})\n    # Implementation\n  end\n\n  def load_session(session_id)\n    # Implementation\n  end\n\n  def update_session(session_id, updates)\n    # Implementation\n  end\n\n  def delete_session(session_id)\n    # Implementation\n  end\n\n  def exists?(session_id)\n    # Implementation\n  end\nend\n\n# Register your custom store\nActionMCP::Server.session_store = MyCustomSessionStore.new\n```\n\n## Thread Pool Management\n\nActionMCP uses thread pools to efficiently handle message callbacks. This prevents the system from being overwhelmed by too many threads under high load.\n\n### Thread Pool Configuration\n\nYou can configure the thread pool in your `config/mcp.yml`:\n\n```yaml\nproduction:\n  adapter: solid_mcp\n  # Thread pool configuration\n  min_threads: 10    # Minimum number of threads to keep in the pool\n  max_threads: 20    # Maximum number of threads the pool can grow to\n  max_queue: 500     # Maximum number of tasks that can be queued\n```\n\nThe thread pool will automatically:\n- Start with `min_threads` threads\n- Scale up to `max_threads` as needed\n- Queue tasks up to `max_queue` limit\n- Use caller's thread if queue is full (fallback policy)\n\n### Graceful Shutdown\n\nWhen your application is shutting down, you should call:\n\n```ruby\nActionMCP::Server.shutdown\n```\n\nThis ensures all thread pools are properly terminated and tasks are completed.\n\n## Engine and Mounting\n\n**ActionMCP** runs as a standalone Rack application. **Do not attempt to mount it in your application's `routes.rb`**—it is not designed to be mounted as an engine at a custom path. When you use `run ActionMCP::Engine` in your `mcp.ru`, the MCP endpoint is always available at the root path (`/`).\n\n### Installing ActionMCP\n\nActionMCP includes generators to help you set up your project quickly. The install generator creates all necessary base classes and configuration files:\n\n```bash\n# Install ActionMCP with base classes and configuration\nbin/rails generate action_mcp:install\n```\n\nThis will create:\n- `app/mcp/prompts/application_mcp_prompt.rb` - Base prompt class\n- `app/mcp/tools/application_mcp_tool.rb` - Base tool class\n- `app/mcp/resource_templates/application_mcp_res_template.rb` - Base resource template class\n- `app/mcp/application_gateway.rb` - Gateway for authentication\n- `config/mcp.yml` - Configuration file with example settings for all environments\n\n\u003e **Note:** Authentication and authorization are not included. You are responsible for securing the endpoint.\n\n## Authentication with Gateway\n\nActionMCP provides a Gateway system similar to ActionCable's Connection for handling authentication. The Gateway allows you to authenticate users and make them available throughout your MCP components.\n\nActionMCP uses a Gateway pattern with pluggable identifiers for authentication. You can implement custom authentication strategies using session-based auth, API keys, bearer tokens, or integrate with existing authentication systems like Warden, Devise, or external OAuth providers.\n\n### Creating an ApplicationGateway\n\nWhen you run the install generator, it creates an `ApplicationGateway` class:\n\n```ruby\n# app/mcp/application_gateway.rb\nclass ApplicationGateway \u003c ActionMCP::Gateway\n  # Specify what attributes identify a connection\n  identified_by :user\n\n  protected\n\n  def authenticate!\n    token = extract_bearer_token\n    raise ActionMCP::UnauthorizedError, \"Missing token\" unless token\n\n    payload = ActionMCP::JwtDecoder.decode(token)\n    user = resolve_user(payload)\n\n    raise ActionMCP::UnauthorizedError, \"Unauthorized\" unless user\n\n    # Return a hash with all identified_by attributes\n    { user: user }\n  end\n\n  private\n\n  def resolve_user(payload)\n    user_id = payload[\"user_id\"] || payload[\"sub\"]\n    User.find_by(id: user_id) if user_id\n  end\nend\n```\n\n### Using Multiple Identifiers\n\nYou can identify connections by multiple attributes:\n\n```ruby\nclass ApplicationGateway \u003c ActionMCP::Gateway\n  identified_by :user, :organization\n\n  protected\n\n  def authenticate!\n    # ... authentication logic ...\n\n    {\n      user: user,\n      organization: user.organization\n    }\n  end\nend\n```\n\n### Accessing Current User in Components\n\nOnce authenticated, the current user (and other identifiers) are available in your tools, prompts, and resource templates:\n\n```ruby\nclass MyTool \u003c ApplicationMCPTool\n  def perform\n    # Access the authenticated user\n    if current_user\n      render text: \"Hello, #{current_user.name}!\"\n    else\n      render text: \"Hi Stranger! It's been a while \"\n    end\n  end\nend\n```\n\n### Current Attributes\n\nActionMCP uses Rails' CurrentAttributes to store the authenticated context. The `ActionMCP::Current` class provides:\n- `ActionMCP::Current.user` - The authenticated user\n- `ActionMCP::Current.gateway` - The gateway instance\n- Any other attributes you define with `identified_by`\n\n### 1. Create `mcp.ru`\n\n```ruby\n# Load the full Rails environment to access models, DB, Redis, etc.\nrequire_relative \"config/environment\"\n\n# No need to set a custom endpoint path. The MCP endpoint is always served at root (\"/\")\n# when using ActionMCP::Engine directly.\nrun ActionMCP::Engine\n```\n### 2. Start the server\n```bash\nbin/rails s -c mcp.ru -p 62770 -P tmp/pids/mcps0.pid\n```\n\n### Dealing with Middleware Conflicts\n\nIf your Rails application uses middleware that interferes with MCP server operation (like Devise, Warden, Ahoy, Rack::Cors, etc.), use `mcp_vanilla.ru` instead:\n\n```ruby\n# mcp_vanilla.ru - A minimal Rack app with only essential middleware\n# This avoids conflicts with authentication, tracking, and other web-specific middleware\n# See the file for detailed documentation on when and why to use it\n\nbundle exec rails s -c mcp_vanilla.ru -p 62770\n# Or with Falcon:\nbundle exec falcon serve --bind http://0.0.0.0:62770 --config mcp_vanilla.ru\n```\n\nCommon middleware that can cause issues:\n- **Devise/Warden** - Expects cookies and sessions, throws `Devise::MissingWarden` errors\n- **Ahoy** - Analytics tracking that intercepts requests\n- **Rack::Attack** - Rate limiting designed for web traffic\n- **Rack::Cors** - CORS headers meant for browsers\n- Any middleware assuming HTML responses or cookie-based authentication\n\nAn example of a minimal `mcp_vanilla.ru` file is located in the dummy app : test/dummy/mcp_vanilla.ru.\nThis file is a minimal Rack application that only includes the essential middleware needed for MCP server operation, avoiding conflicts with web-specific middleware.\nBut remember to add any instrumentation or logging middleware you need, as the minimal setup will not include them by default.\n\n```ruby\n\n## Production Deployment of MCPS0\n\nIn production, **MCPS0** (the MCP server) is a standard Rack application. You can run it using any Rack-compatible server (such as Puma, Unicorn, or Passenger).\n\n\u003e **For best performance and concurrency, it is highly recommended to use a modern, synchronous server like [Falcon](https://github.com/socketry/falcon)**. Falcon is optimized for streaming and concurrent workloads, making it ideal for MCP servers. You can still use Puma, Unicorn, or Passenger, but Falcon will generally provide superior throughput and responsiveness for real-time and streaming use cases.\n\nYou have two main options for exposing the server:\n\n### 1. Dedicated Port\n\nRun MCPS0 on its own TCP port (commonly `62770`):\n\n**With Falcon:**\n```bash\nbundle exec falcon serve --bind http://0.0.0.0:62770 --config mcp.ru\n```\n\n**With Puma:**\n```bash\nbundle exec rails s -c mcp.ru -p 62770\n```\n\nThen, use your web server (Nginx, Apache, etc.) to reverse proxy requests to this port.\n\n### 2. Unix Socket\n\nAlternatively, you can run MCPS0 on a Unix socket for improved performance and security (especially when the web server and app server are on the same machine):\n\n**With Falcon:**\n```bash\nbundle exec falcon serve --bind unix:/tmp/mcps0.sock mcp.ru\n```\n\n**With Puma:**\n```bash\nbundle exec puma -C config/puma.rb -b unix:///tmp/mcps0.sock -c mcp.ru\n```\n\nAnd configure your web server to proxy to the socket:\n\n```nginx\nlocation /mcp/ {\n  proxy_pass http://unix:/tmp/mcps0.sock:;\n  proxy_set_header Host $host;\n  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n}\n```\n\n**Key Points:**\n- MCPS0 is a standalone Rack app—run it separately from your main Rails server.\n- You can expose it via a TCP port (e.g., 62770) or a Unix socket.\n- Use a reverse proxy (Nginx, Apache, etc.) to route requests to MCPS0 as needed.\n- This separation ensures reliability and scalability for both your main app and MCP services.\n\n## Generators\n\nActionMCP includes Rails generators to help you quickly set up your MCP server components.\n\nFirst, install ActionMCP to create base classes and configuration:\n\n```bash\nbin/rails action_mcp:install:migrations  # to copy the migrations\nbin/rails generate action_mcp:install\n```\n\nThis will create the base application classes, configuration file, and authentication gateway in your app directory.\n\n### Generate a New Prompt\n\n```bash\nbin/rails generate action_mcp:prompt AnalyzeCode\n```\n\n### Generate a New Tool\n\n```bash\nbin/rails generate action_mcp:tool CalculateSum\n```\n\n## Testing with TestHelper\n\nActionMCP provides a `TestHelper` module to simplify testing of tools and prompts:\n\n```ruby\nrequire \"test_helper\"\nrequire \"action_mcp/test_helper\"\n\nclass ToolTest \u003c ActiveSupport::TestCase\n  include ActionMCP::TestHelper\n\n  test \"CalculateSumTool returns the correct sum\" do\n    assert_tool_findable(\"calculate_sum\")\n    result = execute_tool(\"calculate_sum\", a: 5, b: 10)\n    assert_tool_output(result, \"15.0\")\n  end\n\n  test \"AnalyzeCodePrompt returns the correct analysis\" do\n    assert_prompt_findable(\"analyze_code\")\n    result = execute_prompt(\"analyze_code\", language: \"Ruby\", code: \"def hello; puts 'Hello, world!'; end\")\n    assert_equal \"Analyzing Ruby code: def hello; puts 'Hello, world!'; end\", assert_prompt_output(result)\n  end\nend\n```\n\nThe TestHelper provides several assertion methods:\n- `assert_tool_findable(name)` - Verifies a tool exists and is registered\n- `assert_prompt_findable(name)` - Verifies a prompt exists and is registered\n- `execute_tool(name, **args)` - Executes a tool with arguments\n- `execute_prompt(name, **args)` - Executes a prompt with arguments\n- `assert_tool_output(result, expected)` - Asserts tool output matches expected text\n- `assert_prompt_output(result)` - Extracts and returns prompt output for assertions\n\n## Inspecting Your MCP Server\n\nYou can use the MCP Inspector to test your server implementation:\n\n```bash\n# Start your MCP server\nbundle exec rails s -c mcp.ru -p 62770\n\n# In another terminal, run the inspector\nnpx @modelcontextprotocol/inspector --url http://localhost:62770\n```\n\nThe MCP Inspector provides an interactive interface to:\n- Test tool executions with custom arguments\n- Validate prompt responses\n- Inspect resource templates and their outputs\n- Debug protocol compliance and error handling\n\n## Development Commands\n\nActionMCP includes several rake tasks for development and debugging:\n\n```bash\n# List all MCP components\nbundle exec rails action_mcp:list\n\n# List specific component types\nbundle exec rails action_mcp:list_tools\nbundle exec rails action_mcp:list_prompts\nbundle exec rails action_mcp:list_resources\nbundle exec rails action_mcp:list_profiles\n\n# Show configuration and statistics\nbundle exec rails action_mcp:info\nbundle exec rails action_mcp:stats\n\n# Show profile configuration\nbundle exec rails action_mcp:show_profile[profile_name]\n```\n\n## Error Handling and Troubleshooting\n\nActionMCP provides comprehensive error handling following the JSON-RPC 2.0 specification:\n\n### Error Codes\n\n- **-32601**: Method not found - The requested method doesn't exist\n- **-32002**: Consent required - Tool requires user consent to execute\n- **-32603**: Internal error - Server encountered an unexpected error\n- **-32600**: Invalid request - The request is malformed\n\n### Context-Aware Error Messages\n\nTools should return clear error messages to the LLM using the `render` method:\n\n```ruby\nclass MyTool \u003c ApplicationMCPTool\n  def perform\n    # Check for error conditions and return clear messages\n    if some_error_condition?\n      report_error(\"Clear error message for the LLM\")\n      return\n    end\n\n    # Normal processing\n    render(text: \"Success message\")\n  end\nend\n```\n\n### Common Issues\n\n1. **Session not found**: Ensure sessions are properly created and saved in the session store\n2. **Tool not registered**: Verify tools are properly defined and inherit from ApplicationMCPTool\n3. **Consent required**: Grant consent using `session.grant_consent(tool_name)`\n4. **Middleware conflicts**: Use `mcp_vanilla.ru` to avoid web-specific middleware\n\n### Debugging Tips\n\n- Check server logs for detailed error information\n- Use `bundle exec rails action_mcp:info` to verify configuration\n- Test with MCP Inspector to isolate protocol issues\n- Ensure proper session management in production environments\n\n## Profiles\n\nActionMCP supports a flexible profile system that allows you to selectively expose tools, prompts, and resources based on different usage scenarios. This is particularly useful for applications that need different MCP capabilities for different contexts (e.g., public API vs. admin interface).\n\n### Understanding Profiles\n\nProfiles are named configurations that define:\n\n- Which tools are available\n- Which prompts are accessible\n- Which resources can be accessed\n- Configuration options like logging level and change notifications\n\nBy default, ActionMCP includes two profiles:\n- `primary`: Exposes all tools, prompts, and resources\n- `minimal`: Exposes no tools, prompts, or resources by default\n\n### Configuring Profiles\n\nProfiles are configured via a `config/mcp.yml` file in your Rails application. If this file doesn't exist, ActionMCP will use default settings from the gem.\n\n**Example configuration:**\n\n```yaml\ndefault:\n  tools:\n    - all  # Include all tools\n  prompts:\n    - all  # Include all prompts\n  resources:\n    - all  # Include all resources\n  options:\n    list_changed: false\n    logging_enabled: true\n    logging_level: info\n    resources_subscribe: false\n\napi_only:\n  tools:\n    - calculator\n    - weather\n  prompts: []  # No prompts for API\n  resources:\n    - user_profile\n  options:\n    list_changed: false\n    logging_level: warn\n\nadmin:\n  tools:\n    - all\n  options:\n    logging_level: debug\n    list_changed: true\n    resources_subscribe: true\n```\n\nEach profile can specify:\n- `tools`: Array of tool names to include (use `all` to include all tools)\n- `prompts`: Array of prompt names to include (use `all` to include all prompts)\n- `resources`: Array of resource names to include (use `all` to include all resources)\n- `options`: Additional configuration options:\n  - `list_changed`: Whether to send change notifications\n  - `logging_enabled`: Whether to enable logging\n  - `logging_level`: The logging level to use\n  - `resources_subscribe`: Whether to enable resource subscriptions\n\n### Switching Profiles\n\nYou can switch between profiles programmatically in your code:\n\n```ruby\n# Permanently switch to a different profile\nActionMCP.configuration.use_profile(:only_tools)  # Switch to a profile named \"only_tools\"\n\n# Temporarily use a profile for a specific operation\nActionMCP.with_profile(:minimal) do\n  # Code here uses the minimal profile\n  # After the block, reverts to the previous profile\nend\n```\n\nThis makes it easy to control which MCP capabilities are available in different contexts of your application.\n\n### Inspecting Profiles\n\nActionMCP includes rake tasks to help you manage and inspect your profiles:\n\n```bash\n# List all available profiles with their configurations\nbin/rails action_mcp:list_profiles\n\n# Show detailed information about a specific profile\nbin/rails action_mcp:show_profile[admin]\n\n# List all tools, prompts, resources, and profiles\nbin/rails action_mcp:list\n```\n\nThe profile inspection tasks will highlight any issues, such as configured tools, prompts, or resources that don't actually exist in your application.\n\n### Use Cases\n\nProfiles are particularly useful for:\n\n1. **Multi-tenant applications**: Use different profiles for different customer tiers with Dorp or other gems\n2. **Access control**: Create profiles for different user roles (admin, staff, public)\n3. **Performance optimization**: Use a minimal profile for high-traffic endpoints\n4. **Testing environments**: Use specific test profiles in your test environment\n5. **Progressive enhancement**: Start with a minimal profile and gradually add capabilities\n\nBy leveraging profiles, you can maintain a single ActionMCP codebase while providing tailored MCP capabilities for different contexts.\n\n## Client Usage\n\nActionMCP includes a client for connecting to remote MCP servers. The client handles session management, protocol negotiation, and provides a simple API for interacting with MCP servers.\n\nFor comprehensive client documentation, including examples, session management, transport configuration, and API usage, see [CLIENTUSAGE.md](CLIENTUSAGE.md).\n\n## Production Considerations\n\n### Security\n\n- **Never expose sensitive data** through MCP components\n- **Use authentication** via Gateway for production deployments\n- **Implement proper authorization** in your tools and prompts\n- **Validate all inputs** using property definitions and Rails validations\n- **Use consent management** for sensitive operations\n\n### Performance\n\n- **Configure appropriate thread pools** for high-traffic scenarios\n- **Use Redis or SolidMCP** for production pub/sub\n- **Choose ActiveRecord session store** for session persistence\n- **Monitor session cleanup** to prevent memory leaks\n- **Use profiles** to limit exposed capabilities\n\n### Monitoring\n\n- **Enable logging** and configure appropriate log levels\n- **Monitor session statistics** using `action_mcp:stats`\n- **Track tool usage** and performance metrics\n- **Set up alerts** for error rates and response times\n\n### Deployment\n\n- **Use Falcon** for optimal performance with streaming workloads\n- **Deploy on dedicated ports** or Unix sockets\n- **Use reverse proxies** (Nginx, Apache) for SSL termination\n- **Implement health checks** for your MCP endpoints\n- **Use `mcp_vanilla.ru`** to avoid middleware conflicts\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseuros%2Faction_mcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseuros%2Faction_mcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseuros%2Faction_mcp/lists"}