Fast MCP

repository·main·Indexed 22 days ago

https://github.com/yjacquin/fast-mcp

A Ruby implementation of the Model Context Protocol (MCP) for connecting AI models to Ruby applications. It provides high-level abstractions for defining Tools and Resources with built-in Dry-Schema validation and supports multiple transport options including STDIO, HTTP, and SSE. Fast MCP integrates with Ruby on Rails, Sinatra, and Hanami, and includes features for dynamic tool/resource filtering, token-based authentication, and DNS rebinding protection.

Tokens
20.1K
Snippets
68
Records
84
Agent score
78%

What's inside fast-mcp

  1. Review Supported Specifications

    main

    Fast MCP provides a full implementation of the following Model Context Protocol features:

    • JSON-RPC 2.0: Full implementation for communication.
    • Tool Definition & Calling: Define and call tools with rich argument types.
    • Resource & Resource Templates Management: Create, read, update, and subscribe to resources.
    • Transport Options: Supports STDIO, HTTP, and SSE.
    • Framework Integration: Compatible with Rails, Sinatra, Hanami, and any Rack-compatible framework.
    • Authentication: Token-based security.
    • Schema Support: Full JSON Schema for tool arguments with validation.
  2. Define and manage MCP Resources

    main

    Resources provide data to clients. To create one, inherit from FastMcp::Resource and define its uri, name, description, and mime_type. The content method must return the data.

    Because resources are stateless, you can use a Tool to update the underlying data and then call notify_resource_updated("your/uri") to signal clients that the resource has changed.

    # Define a resource
    class Counter < FastMcp::Resource
      uri "example/counter"
      resource_name "Counter"
      description "A simple counter resource"
      mime_type "application/json"
    
      def content
        JSON.generate({ count: 10 })
      end
    end
    
    # Register it
    server.register_resource(Counter)
    
    # Update pattern using a tool
    class IncrementTool < FastMcp::Tool
      def call
        # ... update logic ...
        notify_resource_updated("example/counter")
        { success: true }
      end
    end
  3. How dynamic tool and resource filtering works

    main

    Fast MCP uses a request-scoped filtering system to dynamically control which tools and resources are exposed to an LLM based on the incoming request context. This is ideal for implementing permission-based access control, multi-tenancy, feature flags, or API versioning.

    The workflow follows three steps:

    1. Define filters on the server that inspect the request context.
    2. Generate request-scoped server instances that contain only the allowed tools/resources.
    3. Handle requests using these filtered instances.

    This system is completely thread-safe because each request receives its own isolated server instance, ensuring that concurrent requests with different permissions do not interfere with each other.

  4. Implement tool authentication and authorization

    main

    Fast MCP provides mechanisms to secure tools based on request context:

    1. Accessing Headers: Use the headers method within your tool to access HTTP-style headers (e.g., headers["AUTHORIZATION"]). This is useful for manual token validation.
    2. The authorize block: Use the authorize class method to define a block that must return a truthy value before the call method is executed. This block has access to the tool's arguments.

    Authorization is inherited. If a parent class defines an authorize block, all child tools will run that check. If a child also defines an authorize block, both the parent and child checks must pass.

    class PerformAuthenticatedActionTool < FastMcp::Tool
      description "Perform an action which requires an authenticated user"
    
      arguments do
        required(:item_id).filled(:integer).description('ID of item to affect')
      end
    
      authorize do |item_id:|
        current_user&.is_admin? &&
          get_item(item_id).user_id == current_user.id
      end
    
      def call(item_id:)
        # Perform action
        # ...
      end
    end
  5. Update resources and notify clients of changes

    main

    Because FastMcp::Resource objects are stateless, you do not 'update' the resource object itself. Instead, you use an FastMcp::Tool to modify the underlying external state (like a file or database) and then call notify_resource_updated(uri) to inform the MCP client that the resource content has changed.

    # Example tool that updates the counter
    class IncrementCounterTool < FastMcp::Tool
      description 'Increment the counter'
    
      def call
        # Read current value
        current_count = File.exist?('counter.txt') ? File.read('counter.txt').to_i : 0
        
        # Increment and save
        new_count = current_count + 1
        File.write('counter.txt', new_count.to_s)
    
        # Notify that the resource has been updated
        notify_resource_updated("example/counter")
    
        { count: new_count }
      end
    end
  6. Filter tools and resources dynamically based on request context

    main

    You can control which tools or resources are visible to a client by using server.filter_tools. This block receives the current request and the list of available tools. You can use tags assigned to tools via the tags method to implement permission-based filtering.

    class AdminTool < FastMcp::Tool
      tags :admin, :dangerous
      description "Perform admin operations"
    
      def call
        # Admin only functionality
      end
    end
    
    server.filter_tools do |request, tools|
      user_role = request.params['role']
    
      case user_role
      when 'admin'
        tools # Admins see all tools
      when 'user'
        tools.reject { |t| t.tags.include?(:admin) }
      else
        tools.select { |t| t.tags.include?(:public) }
      end
    end
  7. Install and configure Fast MCP in Ruby on Rails

    main

    To integrate Fast MCP into a Rails application:

    1. Add the gem: bundle add fast-mcp
    2. Run the generator: bin/rails generate fast_mcp:install

    This creates a fast_mcp.rb initializer where you use FastMcp.mount_in_rails. The generator also creates app/tools and app/resources directories and provides base classes ApplicationTool (aliased to ActionTool::Base) and ApplicationResource (aliased to ActionResource::Base).

    In the initializer, you can use server.register_tools(*ApplicationTool.descendants) to automatically discover all tools inheriting from your base class.

    ```shell
    bundle add fast-mcp
    bin/rails generate fast_mcp:install

    Example initializer configuration

    FastMcp.mount_in_rails( Rails.application, name: 'my-app', version: '1.0.0', path_prefix: '/mcp', messages_route: 'messages', sse_route: 'sse' ) do |server| Rails.application.config.after_initialize do server.register_tools(*ApplicationTool.descendants) server.register_resources(*ApplicationResource.descendants) end end

  8. Integrate MCP using a Configuration Block

    main

    For complex applications, you can use FastMcp.rack_middleware with a configuration block. This allows you to define tools and resources using anonymous classes and register them directly with the server instance provided by the block.

    # Use the MCP middleware with a configuration block
    use FastMcp.rack_middleware, { name: 'sinatra-mcp-server', version: '1.0.0'} do |server|
      # Define a tool using an anonymous class
      tool = Class.new(Mcp::Tool) do
        description "An example tool"
        tool_name "Example"
    
        arguments  do
         required(:input).filled(:string).description("Input value")
        end
        
        def call(input:)
          "You provided: #{input}"
        end
      end
      server.register_tool(tool)
      
      # Register a resource using an anonymous class
      counter_resource = Class.new(FastMcp::Resource) do
        uri "example/counter"
        resource_name "Counter"
        description "A simple counter resource"
        mime_type "application/json"
    
        def initialize
          @count = 0
        end
    
        attr_accessor :count
    
        def content
          JSON.generate({ count: @count })
        end
      end
    
      server.register_resource(counter_resource)
    end
  9. Implement the Standalone Server approach

    main

    The standalone approach runs the MCP server as a separate process communicating via STDIO. This provides isolation and independent scaling. You define tools by inheriting from Mcp::Tool (or FastMcp::Tool) and resources by inheriting from FastMcp::Resource, then register them with a FastMcp::Server instance.

    require 'fast_mcp'
    
    # Create the server
    server = FastMcp::Server.new(name: 'my-mcp-server', version: '1.0.0')
    
    # Define tools
    class ExampleTool < Mcp::Tool
      description "An example tool"
      arguments do
       required(:input).filled(:string).description("Input value")
      end
      
      def call(input:)
        "You provided: #{input}"
      end
    end
    
    # Define resources
    class HelloWorld < FastMcp::Resource
      uri "example/counter.txt"
      name "Counter"
      description "A simple Hello World resource"
      mime_type "application/txt"
      
      def content
        "Hello, World!"
      end
    end
    
    # Register components
    server.register_tool(ExampleTool)
    server.register_resource(HelloWorld)
    
    # Start the server
    server.start
  10. Implement authentication for Standalone Servers

    main

    For standalone servers, authentication is typically handled within the tool's call method by requiring an authentication argument (like an api_key) and validating it against an environment variable or secure store.

    class SecureTool < FastMcp::Tool
      description "A secure tool"
      arguments do
        required(:api_key).filled(:string).description("API key for authentication")
        required(:input).filled(:string).description("Input value")
      end
      
      def call(api_key:, input:)
        unless api_key == ENV['API_KEY']
          raise "Invalid API key"
        end
        
        { output: "Success: #{input}" }
      end
    end
  11. Enable Token Authentication

    main

    Secure your MCP endpoints by enabling token-based authentication using FastMcp.authenticated_rack_middleware. This ensures that only clients providing the correct auth_token can interact with your server.

    # Enable authentication
    FastMcp.authenticated_rack_middleware(app,
      auth_token: 'your-secret-token',
      # other options...
    )
  12. Integrate with Claude Desktop

    main

    To use your Fast MCP server with Claude Desktop, add it to your claude_desktop_config.json file.

    File Locations:

    • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
    • Windows: %APPDATA%\Claude\claude_desktop_config.json

    Configuration Example:

    {
      "mcpServers": {
        "my-great-server": {
          "command": "ruby",
          "args": ["/Users/path/to/your/awesome/fast-mcp/server.rb"]
        }
      }
    }