actionmcp 0.7.1 → 0.8.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.
- checksums.yaml +4 -4
- data/app/controllers/action_mcp/messages_controller.rb +4 -1
- data/app/controllers/action_mcp/sse_controller.rb +20 -7
- data/lib/action_mcp/engine.rb +4 -0
- data/lib/action_mcp/resource_template.rb +2 -0
- data/lib/action_mcp/tool.rb +1 -1
- data/lib/action_mcp/version.rb +1 -1
- metadata +1 -1
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 7a031f1969028d88d34f7f19bc18777fee99067dee692ce73754bbb001477f14
         | 
| 4 | 
            +
              data.tar.gz: 35c84b344020e97ce87d9c7e033220138732c29bd7d7cee0634e4f9c7f38cc13
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 953dd645cb4006c027ca2643aa8274a5011db73910159f4a0a3d5c60e80df82deae14f37ec41de869e863e7b4a8e1440579863bc33daa21185dc169abc4ddcbe
         | 
| 7 | 
            +
              data.tar.gz: 22f1e9622b4330afb13bc1fd943142904f8fcbd07df734e571707c428b2d11c343775900221a33a74df354cae4a47fa36bd13e97d3809aa930b4ad2af40b66b5
         | 
| @@ -21,6 +21,9 @@ module ActionMCP | |
| 21 21 | 
             
                end
         | 
| 22 22 |  | 
| 23 23 | 
             
                def handle_post_message(params, response)
         | 
| 24 | 
            +
                  if params[:method] == "initialize"
         | 
| 25 | 
            +
                    mcp_session.initialize!
         | 
| 26 | 
            +
                  end
         | 
| 24 27 | 
             
                  json_rpc_handler.call(params)
         | 
| 25 28 |  | 
| 26 29 | 
             
                  response.status = :accepted
         | 
| @@ -31,7 +34,7 @@ module ActionMCP | |
| 31 34 | 
             
                end
         | 
| 32 35 |  | 
| 33 36 | 
             
                def mcp_session
         | 
| 34 | 
            -
                  Session. | 
| 37 | 
            +
                  @mcp_session ||= Session.find_or_create_by(id: params[:session_id])
         | 
| 35 38 | 
             
                end
         | 
| 36 39 |  | 
| 37 40 | 
             
                def clean_params
         | 
| @@ -1,7 +1,6 @@ | |
| 1 1 | 
             
            module ActionMCP
         | 
| 2 2 | 
             
              class SSEController < ApplicationController
         | 
| 3 3 | 
             
                HEARTBEAT_INTERVAL = 30 # TODO: The frequency of pings SHOULD be configurable
         | 
| 4 | 
            -
                INITIALIZATION_TIMEOUT = 2
         | 
| 5 4 | 
             
                include ActionController::Live
         | 
| 6 5 |  | 
| 7 6 | 
             
                # @route GET /sse (sse_out)
         | 
| @@ -21,12 +20,21 @@ module ActionMCP | |
| 21 20 |  | 
| 22 21 | 
             
                    # Start listener and process messages via the transport
         | 
| 23 22 | 
             
                    listener = SSEListener.new(mcp_session)
         | 
| 23 | 
            +
                    message_received = false
         | 
| 24 24 | 
             
                    if listener.start do |message|
         | 
| 25 25 | 
             
                      # Send with proper SSE formatting
         | 
| 26 26 | 
             
                      sse.write(message)
         | 
| 27 | 
            +
                      message_received = true
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                    sleep 1
         | 
| 30 | 
            +
                    # Heartbeat loop
         | 
| 31 | 
            +
                    unless message_received
         | 
| 32 | 
            +
                      Rails.logger.warn "No message received within 1 second, closing connection for session: #{session_id}"
         | 
| 33 | 
            +
                      error = JsonRpc::Response.new(id: SecureRandom.uuid_v7, error: JsonRpc::JsonRpcError.new(:server_error, message: "No message received within 1 second").to_h).to_h
         | 
| 34 | 
            +
                      sse.write(error)
         | 
| 35 | 
            +
                      return
         | 
| 27 36 | 
             
                    end
         | 
| 28 37 |  | 
| 29 | 
            -
                      # Heartbeat loop
         | 
| 30 38 | 
             
                      until response.stream.closed?
         | 
| 31 39 | 
             
                        sleep HEARTBEAT_INTERVAL
         | 
| 32 40 | 
             
                        # mcp_session.send_ping!
         | 
| @@ -40,8 +48,8 @@ module ActionMCP | |
| 40 48 | 
             
                  rescue => e
         | 
| 41 49 | 
             
                    Rails.logger.error "SSE: Unexpected error: #{e.class} - #{e.message}\n#{e.backtrace.join("\n")}"
         | 
| 42 50 | 
             
                  ensure
         | 
| 43 | 
            -
                    listener.stop
         | 
| 44 51 | 
             
                    response.stream.close
         | 
| 52 | 
            +
                    listener.stop
         | 
| 45 53 | 
             
                    Rails.logger.debug "SSE: Connection closed for session: #{session_id}"
         | 
| 46 54 | 
             
                  end
         | 
| 47 55 | 
             
                end
         | 
| @@ -60,12 +68,16 @@ module ActionMCP | |
| 60 68 | 
             
                end
         | 
| 61 69 |  | 
| 62 70 | 
             
                def mcp_session
         | 
| 63 | 
            -
                  @mcp_session ||= Session. | 
| 71 | 
            +
                  @mcp_session ||= Session.new
         | 
| 64 72 | 
             
                end
         | 
| 65 73 |  | 
| 66 74 | 
             
                def session_id
         | 
| 67 75 | 
             
                  @session_id ||= mcp_session.id
         | 
| 68 76 | 
             
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                def cache_key
         | 
| 79 | 
            +
                  "action_mcp:session:#{session_id}"
         | 
| 80 | 
            +
                end
         | 
| 69 81 | 
             
              end
         | 
| 70 82 |  | 
| 71 83 | 
             
              class SSEListener
         | 
| @@ -101,7 +113,7 @@ module ActionMCP | |
| 101 113 | 
             
                  }
         | 
| 102 114 |  | 
| 103 115 | 
             
                  # Subscribe using the ActionCable adapter
         | 
| 104 | 
            -
                   | 
| 116 | 
            +
                  adapter.subscribe(session_key, message_callback, success_callback)
         | 
| 105 117 |  | 
| 106 118 | 
             
                  # Give some time for the subscription to be established
         | 
| 107 119 | 
             
                  sleep 0.5
         | 
| @@ -111,8 +123,9 @@ module ActionMCP | |
| 111 123 |  | 
| 112 124 | 
             
                def stop
         | 
| 113 125 | 
             
                  @stopped = true
         | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 126 | 
            +
                  if (mcp_session = Session.find_by(id: session_key))
         | 
| 127 | 
            +
                    mcp_session.close
         | 
| 128 | 
            +
                  end
         | 
| 116 129 | 
             
                  Rails.logger.debug "Unsubscribed from: #{session_key}"
         | 
| 117 130 | 
             
                end
         | 
| 118 131 | 
             
              end
         | 
    
        data/lib/action_mcp/engine.rb
    CHANGED
    
    | @@ -15,6 +15,10 @@ module ActionMCP | |
| 15 15 | 
             
                # Provide a configuration namespace for ActionMCP
         | 
| 16 16 | 
             
                config.action_mcp = ActionMCP.configuration
         | 
| 17 17 |  | 
| 18 | 
            +
                config.to_prepare do
         | 
| 19 | 
            +
                  ActionMCP::ResourceTemplate.registered_templates.clear
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 18 22 | 
             
                # Configure autoloading for the mcp/tools directory
         | 
| 19 23 | 
             
                initializer "action_mcp.autoloading", before: :set_autoload_paths do |app|
         | 
| 20 24 | 
             
                  mcp_path = app.root.join("app/mcp")
         | 
| @@ -179,6 +179,8 @@ module ActionMCP | |
| 179 179 | 
             
                    ResourceTemplate.registered_templates.each do |registered_class|
         | 
| 180 180 | 
             
                      next if registered_class == self || registered_class.abstract?
         | 
| 181 181 | 
             
                      next unless registered_class.uri_template
         | 
| 182 | 
            +
                      # Ignore conflicts with resource templates that have the same name
         | 
| 183 | 
            +
                      next if registered_class.name == self.name
         | 
| 182 184 |  | 
| 183 185 | 
             
                      existing_template_data = parse_uri_template(registered_class.uri_template)
         | 
| 184 186 |  | 
    
        data/lib/action_mcp/tool.rb
    CHANGED
    
    | @@ -74,7 +74,7 @@ module ActionMCP | |
| 74 74 |  | 
| 75 75 | 
             
                  return unless %w[number integer].include?(type)
         | 
| 76 76 |  | 
| 77 | 
            -
                  validates prop_name, numericality: true
         | 
| 77 | 
            +
                  validates prop_name, numericality: true, allow_nil: true
         | 
| 78 78 | 
             
                end
         | 
| 79 79 |  | 
| 80 80 | 
             
                # --------------------------------------------------------------------------
         | 
    
        data/lib/action_mcp/version.rb
    CHANGED