Class: Prescient::MCP::Rack

Inherits:
Object
  • Object
show all
Defined in:
lib/prescient/mcp/rack.rb

Overview

Optional Rack-compatible MCP HTTP handler with explicit authentication. rubocop:disable Metrics/ClassLength

Constant Summary collapse

PROTOCOL_VERSION =

MCP protocol version supported by the HTTP transport.

Returns:

  • (String) —

    Protocol version identifier

"2025-06-18"
SESSION_HEADER =

Rack environment key containing the MCP session identifier.

Returns:

  • (String) —

    Rack header key

"HTTP_MCP_SESSION_ID"
PROTOCOL_HEADER =

Rack environment key containing the MCP protocol version.

Returns:

  • (String) —

    Rack header key

"HTTP_MCP_PROTOCOL_VERSION"
ALLOWED_ORIGINS_DEFAULT =

Default policy allowing requests without an Origin header restriction.

Returns:

  • (Array<String>) —

    Empty Origin allowlist

[].freeze

Instance Method Summary collapse

Constructor Details

#initialize(authentication:, server: Server.new, request_context: nil, max_body_bytes: Configuration::DEFAULT_MAX_INPUT_BYTES, allowed_origins: ALLOWED_ORIGINS_DEFAULT, session_ttl: 3600) ⇒ Rack

Returns a new instance of Rack.



26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/prescient/mcp/rack.rb', line 26

def initialize(authentication:, server: Server.new, request_context: nil,
               max_body_bytes: Configuration::DEFAULT_MAX_INPUT_BYTES,
               allowed_origins: ALLOWED_ORIGINS_DEFAULT, session_ttl: 3600)
  @server = server
  @authentication = authentication
  @request_context = request_context
  @max_body_bytes = validate_limit(max_body_bytes)
  @allowed_origins = Array(allowed_origins).map(&:to_s).freeze
  @session_ttl = validate_session_ttl(session_ttl)
  @sessions = {}
  @sessions_lock = Monitor.new
end

Instance Method Details

#call(env) ⇒ Array

Handle one MCP Streamable HTTP request.

Parameters:

  • env (Hash) —

    Rack environment

Returns:

  • (Array) —

    Rack response



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/prescient/mcp/rack.rb', line 42

def call(env)
  method = env.fetch("REQUEST_METHOD", "GET").upcase
  unless %w[POST GET DELETE].include?(method)
    return response(
      405, { error: "method_not_allowed" }, { "allow" => "POST, GET, DELETE" }
    )
  end
  return response(403, { error: "origin_not_allowed" }) unless origin_allowed?(env)

  authentication = authenticate(env)
  unless authentication
    return response(401, { error: "authentication_required" }, { "www-authenticate" => "Bearer" })
  end

  return post(env, authentication) if method == "POST"
  return get(env, authentication) if method == "GET"

  delete(env, authentication)
rescue JSON::ParserError
  response(400, { error: "invalid_json" })
rescue SessionNotFoundError
  response(404, { error: "session_not_found" })
rescue AuthenticationError
  response(401, { error: "authentication_failed" })
rescue ArgumentError
  response(400, { error: "invalid_request" })
rescue StandardError
  response(500, { error: "internal_error" })
end