Class: TalkToYourApp::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/talk_to_your_app/configuration.rb

Overview

The single mutable configuration object. Held as a memoized singleton on the TalkToYourApp module, so calling ‘TalkToYourApp.configure` more than once merges into the same instance rather than replacing it.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConfiguration

Returns a new instance of Configuration.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/talk_to_your_app/configuration.rb', line 37

def initialize
  @mount_at = "/mcp"
  @server_name = "talk_to_your_app"
  @server_version = TalkToYourApp::VERSION
  @server_title = nil
  @server_description = nil
  @instructions = nil
  @connections = {}
  @enabled_plugins = {}
  @logger = nil
  @api_keys = {}
  @allowed_origins = []
  @basic_auth = nil
  @log_level = :info
  @authorizer = nil
end

Instance Attribute Details

#allowed_originsObject

Origins permitted for browser-originated requests (DNS-rebinding protection). Empty allowlist permits non-browser clients (no Origin).



35
36
37
# File 'lib/talk_to_your_app/configuration.rb', line 35

def allowed_origins
  @allowed_origins
end

#api_keysObject

Named API keys, { “principal-name” => “secret-key” }. The name is logged as the principal. Supports multiple keys for rotation.



31
32
33
# File 'lib/talk_to_your_app/configuration.rb', line 31

def api_keys
  @api_keys
end

#connectionsObject (readonly)

Declared named connections, keyed by gem-internal symbol name.



89
90
91
# File 'lib/talk_to_your_app/configuration.rb', line 89

def connections
  @connections
end

#enabled_pluginsObject (readonly)

Enabled plugins, keyed by name => options hash. Plugins are off by default.



92
93
94
# File 'lib/talk_to_your_app/configuration.rb', line 92

def enabled_plugins
  @enabled_plugins
end

#instructionsObject

MCP server identity, surfaced to clients in the ‘initialize` handshake (serverInfo + instructions). All optional except name/version, which default sensibly.



19
20
21
# File 'lib/talk_to_your_app/configuration.rb', line 19

def instructions
  @instructions
end

#log_levelObject

Global audit log level (default :info). Overridable per plugin via the plugin DSL’s ‘log_level`.



27
28
29
# File 'lib/talk_to_your_app/configuration.rb', line 27

def log_level
  @log_level
end

#loggerObject

Audit logger. Defaults to Rails.logger at boot; swappable to any object implementing the Logger interface.



23
24
25
# File 'lib/talk_to_your_app/configuration.rb', line 23

def logger
  @logger
end

#mount_atObject

Path the MCP endpoint is mounted at in the host app’s router. Default “/mcp”.



14
15
16
# File 'lib/talk_to_your_app/configuration.rb', line 14

def mount_at
  @mount_at
end

#server_descriptionObject

MCP server identity, surfaced to clients in the ‘initialize` handshake (serverInfo + instructions). All optional except name/version, which default sensibly.



19
20
21
# File 'lib/talk_to_your_app/configuration.rb', line 19

def server_description
  @server_description
end

#server_nameObject

MCP server identity, surfaced to clients in the ‘initialize` handshake (serverInfo + instructions). All optional except name/version, which default sensibly.



19
20
21
# File 'lib/talk_to_your_app/configuration.rb', line 19

def server_name
  @server_name
end

#server_titleObject

MCP server identity, surfaced to clients in the ‘initialize` handshake (serverInfo + instructions). All optional except name/version, which default sensibly.



19
20
21
# File 'lib/talk_to_your_app/configuration.rb', line 19

def server_title
  @server_title
end

#server_versionObject

MCP server identity, surfaced to clients in the ‘initialize` handshake (serverInfo + instructions). All optional except name/version, which default sensibly.



19
20
21
# File 'lib/talk_to_your_app/configuration.rb', line 19

def server_version
  @server_version
end

Instance Method Details

#auth_configured?Boolean

True when at least one authentication mechanism is configured.

Returns:

  • (Boolean)


64
65
66
# File 'lib/talk_to_your_app/configuration.rb', line 64

def auth_configured?
  api_keys.any? || !@basic_auth.nil?
end

#authorize(&block) ⇒ Object

Optional per-principal tool authorization. The block receives (principal, tool_name) and returns truthy to allow the call. With no authorizer configured, every authenticated principal may call every tool.

config.authorize { |principal, tool| principal == "admin" || tool.start_with?("db.") }


73
74
75
76
# File 'lib/talk_to_your_app/configuration.rb', line 73

def authorize(&block)
  @authorizer = block if block
  @authorizer
end

#authorized?(principal, tool_name) ⇒ Boolean

Returns:

  • (Boolean)


78
79
80
81
82
83
84
85
86
# File 'lib/talk_to_your_app/configuration.rb', line 78

def authorized?(principal, tool_name)
  return true if @authorizer.nil?

  @authorizer.call(principal, tool_name)
rescue StandardError => e
  # A raising authorizer denies (fail-closed), mirroring basic_auth handling.
  warn("talk_to_your_app: authorizer raised: #{e.class}: #{e.message}")
  false
end

#basic_auth(&block) ⇒ Object

Sets or reads the HTTP Basic auth callable. The block receives (username, password) and returns truthy to authenticate.

config.basic_auth { |user, pass| User.authenticate(user, pass) }


58
59
60
61
# File 'lib/talk_to_your_app/configuration.rb', line 58

def basic_auth(&block)
  @basic_auth = block if block
  @basic_auth
end

#connection(name, database:, role:, replica: false, statement_timeout: nil) ⇒ Object

Declares a named connection plugins can reference.

config.connection :replica_readonly, database: "primary", role: :reading

database is a database.yml config key. role is :reading or :writing; a :reading connection prevents writes at the Rails layer. replica: true marks the connection as pointing at a replica (informational; combining it with role: :writing is rejected as nonsensical).



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/talk_to_your_app/configuration.rb', line 110

def connection(name, database:, role:, replica: false, statement_timeout: nil)
  role = role.to_sym
  unless %i[reading writing].include?(role)
    raise ConfigurationError, "connection #{name.inspect}: role must be :reading or :writing, got #{role.inspect}."
  end
  if replica && role == :writing
    raise ConfigurationError,
      "connection #{name.inspect}: `replica: true` with `role: :writing` is nonsensical — a replica cannot accept writes."
  end

  @connections[name.to_sym] = ConnectionRegistry::ConnectionSpec.new(
    name: name.to_sym,
    database: database.to_sym,
    role: role,
    replica: replica,
    statement_timeout: statement_timeout,
  )
end

#plugin(name, **options) ⇒ Object

Enables a registered plugin, with optional per-plugin options.

config.plugin :db
config.plugin :jobs, adapter: :sidekiq


98
99
100
# File 'lib/talk_to_your_app/configuration.rb', line 98

def plugin(name, **options)
  @enabled_plugins[name.to_sym] = options
end