otto 2.9.0 → 2.11.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/.github/dependabot.yml +5 -0
- data/.github/workflows/ci.yml +11 -8
- data/.github/workflows/claude-code-review.yml +38 -13
- data/.github/workflows/claude.yml +10 -8
- data/.github/workflows/code-smells.yml +5 -5
- data/.github/workflows/release-gem.yml +2 -2
- data/.github/workflows/ruby-lint.yml +3 -3
- data/.github/workflows/yardoc.yml +5 -5
- data/.gitignore +1 -5
- data/.rubocop_todo.yml +8 -5
- data/AGENTS.md +40 -1
- data/CHANGELOG.rst +235 -0
- data/Gemfile +3 -3
- data/Gemfile.lock +11 -15
- data/README.md +75 -37
- data/docs/README.md +166 -0
- data/docs/adr/README.md +16 -0
- data/docs/adr/adr-001-route-authentication-at-handler-boundary.md +38 -0
- data/docs/adr/adr-002-multi-strategy-authentication-and-authorization.md +50 -0
- data/docs/adr/adr-003-caddy-tls-route-based-integration.md +48 -0
- data/docs/adr/adr-004-separate-compatibility-from-security-maintenance.md +58 -0
- data/docs/guides/authentication.md +377 -0
- data/docs/guides/caddy-tls.md +205 -0
- data/docs/guides/configuration_freezing.md +157 -0
- data/docs/guides/enrichment.md +161 -0
- data/docs/guides/forwarded-authority.md +249 -0
- data/docs/guides/geo-country.md +168 -0
- data/docs/guides/ip_privacy.md +39 -0
- data/docs/guides/ipaddr-encoding-quirk.md +56 -0
- data/docs/guides/mcp.md +282 -0
- data/docs/guides/privacy.md +193 -0
- data/docs/guides/routing.md +293 -0
- data/docs/guides/structured_logging.md +281 -0
- data/docs/guides/testing-guide.md +391 -0
- data/docs/maintainers/github-actions.md +41 -0
- data/docs/maintainers/investigations/.gitignore +2 -0
- data/docs/migrating/v2.0.0.md +337 -0
- data/docs/reference/authentication.md +290 -0
- data/docs/reference/route-syntax.md +181 -0
- data/docs/reference/runtime-and-dependency-security.md +86 -0
- data/examples/advanced_routes/README.md +43 -57
- data/examples/authentication_strategies/README.md +37 -196
- data/examples/basic/README.md +24 -39
- data/examples/basic/config.ru +0 -1
- data/examples/caddy_tls_demo/README.md +8 -2
- data/examples/lambda_handlers/README.md +11 -2
- data/examples/mcp_demo/README.md +75 -161
- data/examples/mcp_demo/config.ru +1 -0
- data/examples/security_features/README.md +36 -234
- data/lib/otto/caddy_tls/localhost_guard.rb +22 -24
- data/lib/otto/core/configuration.rb +40 -23
- data/lib/otto/core/error_handler.rb +40 -2
- data/lib/otto/core/file_safety.rb +105 -31
- data/lib/otto/core/middleware_stack.rb +36 -36
- data/lib/otto/core/router.rb +94 -18
- data/lib/otto/core/static_mounts.rb +172 -0
- data/lib/otto/core.rb +1 -0
- data/lib/otto/env_keys.rb +20 -2
- data/lib/otto/mcp/auth/token.rb +10 -4
- data/lib/otto/mcp/core.rb +23 -5
- data/lib/otto/mcp/endpoint.rb +41 -0
- data/lib/otto/mcp/errors.rb +15 -0
- data/lib/otto/mcp/options.rb +292 -0
- data/lib/otto/mcp/protocol.rb +52 -22
- data/lib/otto/mcp/rate_limiting.rb +175 -97
- data/lib/otto/mcp/registry.rb +14 -14
- data/lib/otto/mcp/schema_validation.rb +20 -12
- data/lib/otto/mcp/server.rb +131 -32
- data/lib/otto/optional_dependency.rb +57 -0
- data/lib/otto/privacy/config.rb +10 -8
- data/lib/otto/security/authentication/route_auth_wrapper/role_authorization.rb +22 -5
- data/lib/otto/security/authentication/strategies/api_key_strategy.rb +181 -18
- data/lib/otto/security/authentication/strategies/permission_strategy.rb +1 -0
- data/lib/otto/security/authentication/strategies/role_strategy.rb +1 -0
- data/lib/otto/security/authentication/strategies/session_strategy.rb +1 -0
- data/lib/otto/security/authentication/strategy_result.rb +58 -28
- data/lib/otto/security/config.rb +328 -14
- data/lib/otto/security/configurator.rb +36 -6
- data/lib/otto/security/core.rb +19 -1
- data/lib/otto/security/middleware/ip_privacy_middleware.rb +105 -4
- data/lib/otto/security/middleware/rate_limit_middleware.rb +1 -6
- data/lib/otto/security/rate_limiter.rb +81 -46
- data/lib/otto/static.rb +38 -0
- data/lib/otto/utils.rb +50 -0
- data/lib/otto/version.rb +1 -1
- data/lib/otto.rb +130 -13
- data/otto.gemspec +0 -2
- metadata +32 -41
- data/docs/.gitignore +0 -10
- data/docs/1108-STREAMING_ARCHITECTURE_ANALYSIS.md +0 -1105
- data/docs/1108-STREAMING_SUPPORT_SUMMARY.md +0 -376
- data/docs/enrichment.md +0 -128
- data/docs/geo-country.md +0 -181
- data/docs/ipaddr-encoding-quirk.md +0 -34
- data/docs/migrating/v2.0.0-pre1.md +0 -276
- data/docs/migrating/v2.0.0-pre2.md +0 -338
- data/docs/modern-authentication-authorization-landscape.md +0 -558
- data/docs/multi-strategy-authentication-design.md +0 -1401
- data/docs/reverse-proxy-network-services.md +0 -371
data/lib/otto.rb
CHANGED
|
@@ -55,6 +55,7 @@ require_relative 'otto/logging_helpers'
|
|
|
55
55
|
class Otto
|
|
56
56
|
include Otto::Core::Router
|
|
57
57
|
include Otto::Core::FileSafety
|
|
58
|
+
include Otto::Core::StaticMounts
|
|
58
59
|
include Otto::Core::Configuration
|
|
59
60
|
include Otto::Core::ErrorHandler
|
|
60
61
|
include Otto::Core::UriGenerator
|
|
@@ -68,6 +69,10 @@ class Otto
|
|
|
68
69
|
|
|
69
70
|
LIB_HOME = __dir__ unless defined?(Otto::LIB_HOME)
|
|
70
71
|
|
|
72
|
+
# Parameter types (from Proc#parameters / Method#parameters) that consume
|
|
73
|
+
# one positional argument each. See {#fallback_call_args}.
|
|
74
|
+
POSITIONAL_PARAMETER_TYPES = %i[req opt].freeze
|
|
75
|
+
|
|
71
76
|
@debug = case ENV.fetch('OTTO_DEBUG', nil)
|
|
72
77
|
in 'true' | '1' | 'yes' | 'on'
|
|
73
78
|
true
|
|
@@ -76,14 +81,61 @@ class Otto
|
|
|
76
81
|
end
|
|
77
82
|
@logger = Logger.new($stdout, Logger::INFO)
|
|
78
83
|
|
|
79
|
-
attr_reader :routes, :routes_literal, :
|
|
84
|
+
attr_reader :routes, :routes_literal, :route_definitions,
|
|
80
85
|
:routes_by_definition, :option,
|
|
81
86
|
:static_route, :security_config, :locale_config, :auth_config,
|
|
82
87
|
:route_handler_factory, :mcp_server, :caddy_tls_server, :security, :middleware,
|
|
83
|
-
:error_handlers, :request_class, :response_class
|
|
84
|
-
|
|
88
|
+
:error_handlers, :request_class, :response_class,
|
|
89
|
+
:not_found, :server_error
|
|
90
|
+
|
|
91
|
+
# Configure the response returned when no route (and no +/404+ route) matches.
|
|
92
|
+
#
|
|
93
|
+
# Accepts either a Rack triple +[status, headers, body]+ (the body must
|
|
94
|
+
# respond to +each+, or +call+ for a streaming body) or anything that
|
|
95
|
+
# responds to +call(env)+ and returns one. A static triple is never handed
|
|
96
|
+
# back to the Rack stack by reference: Otto returns a per-request copy whose
|
|
97
|
+
# headers (and Array-valued header entries, and Array body) are fresh
|
|
98
|
+
# containers, so cookie middleware such as rack-session or Otto's own CSRF
|
|
99
|
+
# middleware cannot accumulate +Set-Cookie+ values on the configured object
|
|
100
|
+
# and replay them to later clients. Prefer the callable form when the
|
|
101
|
+
# response should vary per request.
|
|
102
|
+
#
|
|
103
|
+
# @param response [Array, #call, nil] a Rack triple, a callable, or nil to
|
|
104
|
+
# restore the built-in {Otto::Static.not_found} response
|
|
105
|
+
# @raise [ArgumentError] when +response+ is neither a Rack triple nor callable
|
|
106
|
+
#
|
|
107
|
+
# @example Static triple (copied per request)
|
|
108
|
+
# otto.not_found = [404, { 'content-type' => 'application/json' }, ['{"error":"Not Found"}']]
|
|
109
|
+
#
|
|
110
|
+
# @example Callable, built fresh on every miss
|
|
111
|
+
# otto.not_found = ->(env) { [404, { 'content-type' => 'text/plain' }, ["No #{env['PATH_INFO']}"]] }
|
|
112
|
+
def not_found=(response)
|
|
113
|
+
@not_found = validate_fallback_response!(:not_found, response)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Configure the response returned for an unhandled error when no +/500+
|
|
117
|
+
# route is configured and the client does not prefer JSON.
|
|
118
|
+
#
|
|
119
|
+
# Accepts either a Rack triple or anything that responds to +call(env, error)+
|
|
120
|
+
# and returns one; a callable declaring a single positional parameter (or
|
|
121
|
+
# none) receives only what it declares. +env['otto.error_id']+ carries the
|
|
122
|
+
# logged correlation id. As with {#not_found=}, a static triple is copied
|
|
123
|
+
# per request so header writes by cookie middleware never touch the
|
|
124
|
+
# configured object. A callable that raises is logged and replaced by the
|
|
125
|
+
# built-in secure error response.
|
|
126
|
+
#
|
|
127
|
+
# @param response [Array, #call, nil] a Rack triple, a callable, or nil to
|
|
128
|
+
# restore the built-in secure error response
|
|
129
|
+
# @raise [ArgumentError] when +response+ is neither a Rack triple nor callable
|
|
130
|
+
#
|
|
131
|
+
# @example Callable receiving the error
|
|
132
|
+
# otto.server_error = ->(env, error) { [500, { 'content-type' => 'text/plain' }, ['Oops']] }
|
|
133
|
+
def server_error=(response)
|
|
134
|
+
@server_error = validate_fallback_response!(:server_error, response)
|
|
135
|
+
end
|
|
85
136
|
|
|
86
137
|
def initialize(path = nil, opts = {})
|
|
138
|
+
constructed = false
|
|
87
139
|
initialize_core_state
|
|
88
140
|
initialize_options(path, opts)
|
|
89
141
|
initialize_configurations(opts)
|
|
@@ -106,6 +158,13 @@ class Otto
|
|
|
106
158
|
# but before processing requests.
|
|
107
159
|
@freeze_mutex = Mutex.new
|
|
108
160
|
@configuration_frozen = false
|
|
161
|
+
constructed = true
|
|
162
|
+
ensure
|
|
163
|
+
# A config that committed to a forwarding family during configure_security
|
|
164
|
+
# is held process-wide; withdraw it if construction failed afterwards
|
|
165
|
+
# (e.g. a bad routes path), or the dead app would keep vetoing other
|
|
166
|
+
# families for the life of the process.
|
|
167
|
+
Otto::Security::Config.release_rack_forwarding_family!(@security_config) if @security_config && !constructed
|
|
109
168
|
end
|
|
110
169
|
alias options option
|
|
111
170
|
|
|
@@ -172,16 +231,6 @@ class Otto
|
|
|
172
231
|
private
|
|
173
232
|
|
|
174
233
|
def initialize_core_state
|
|
175
|
-
# The GET cache is a Concurrent::Map, not a plain Hash: lazy static-file
|
|
176
|
-
# discovery (Core::Router#handle_request, Core::FileSafety#add_static_path)
|
|
177
|
-
# writes into it at request time, after freeze_configuration! has already
|
|
178
|
-
# deep-frozen the rest of the routing state. Deep-freezing this cache too
|
|
179
|
-
# would turn every as-yet-uncached static file request into a 500
|
|
180
|
-
# (FrozenError) in production (issue #185), so it is intentionally excluded
|
|
181
|
-
# from deep_freeze_value in Configuration#freeze_configuration! and kept as
|
|
182
|
-
# a structure that is both mutable post-freeze and safe under concurrent
|
|
183
|
-
# request threads.
|
|
184
|
-
@routes_static = { GET: Concurrent::Map.new }
|
|
185
234
|
@routes = { GET: [] }
|
|
186
235
|
@routes_literal = { GET: {} }
|
|
187
236
|
@route_definitions = {}
|
|
@@ -190,6 +239,10 @@ class Otto
|
|
|
190
239
|
# so reverse lookups (Otto#uri) consult this index instead of the
|
|
191
240
|
# single-route @route_definitions entry (issue #190).
|
|
192
241
|
@routes_by_definition = {}
|
|
242
|
+
# Explicit static mounts (Core::StaticMounts#mount_static). Always a
|
|
243
|
+
# frozen snapshot, replaced wholesale on registration; dispatch reads it
|
|
244
|
+
# without locking.
|
|
245
|
+
@static_mounts = [].freeze
|
|
193
246
|
@security_config = Otto::Security::Config.new
|
|
194
247
|
@middleware = Otto::Core::MiddlewareStack.new
|
|
195
248
|
# Initialize @auth_config first so it can be shared with the configurator
|
|
@@ -303,6 +356,70 @@ class Otto
|
|
|
303
356
|
end
|
|
304
357
|
end
|
|
305
358
|
|
|
359
|
+
# Validate a value assigned to {#not_found=} or {#server_error=}.
|
|
360
|
+
#
|
|
361
|
+
# @param name [Symbol] the setting name, for the error message
|
|
362
|
+
# @param response [Object] the assigned value
|
|
363
|
+
# @return [Array, #call, nil] the value, when acceptable
|
|
364
|
+
# @raise [ArgumentError] otherwise
|
|
365
|
+
def validate_fallback_response!(name, response)
|
|
366
|
+
return response if response.nil? || response.respond_to?(:call)
|
|
367
|
+
return response if rack_triple?(response)
|
|
368
|
+
|
|
369
|
+
raise ArgumentError,
|
|
370
|
+
"#{name} must be a Rack triple [status, headers, body] or respond to #call, got #{response.inspect}"
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# A Rack triple: an Integer-like status, Hash-like headers, and a body that
|
|
374
|
+
# responds to +each+ (or +call+, for a streaming body).
|
|
375
|
+
def rack_triple?(response)
|
|
376
|
+
return false unless response.is_a?(Array) && response.length == 3
|
|
377
|
+
|
|
378
|
+
status, headers, body = response
|
|
379
|
+
status.respond_to?(:to_int) && headers.respond_to?(:each_pair) &&
|
|
380
|
+
(body.respond_to?(:each) || body.respond_to?(:call))
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# Resolve a configured fallback into a fresh Rack triple for one request.
|
|
384
|
+
#
|
|
385
|
+
# A callable is invoked with as many of +args+ as it accepts (see
|
|
386
|
+
# {#fallback_call_args}); a static triple is used as-is. Either way the
|
|
387
|
+
# result is copied (see {Otto::Static.copy_response}) so the Rack stack
|
|
388
|
+
# never receives a container shared with the configuration or with another
|
|
389
|
+
# request.
|
|
390
|
+
#
|
|
391
|
+
# @param name [Symbol] the setting name, for the error message
|
|
392
|
+
# @param fallback [Array, #call] the configured value
|
|
393
|
+
# @param args [Array] positional arguments offered to a callable fallback
|
|
394
|
+
# @return [Array] a new Rack triple
|
|
395
|
+
# @raise [TypeError] when a callable returns something other than a Rack triple
|
|
396
|
+
def resolve_fallback_response(name, fallback, *args)
|
|
397
|
+
response = fallback.respond_to?(:call) ? fallback.call(*fallback_call_args(fallback, args)) : fallback
|
|
398
|
+
unless rack_triple?(response)
|
|
399
|
+
raise TypeError,
|
|
400
|
+
"#{name} callable must return a Rack triple [status, headers, body], got #{response.inspect}"
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
Otto::Static.copy_response(response)
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
# Trim +args+ to the positional parameters +callable+ declares, so a lambda
|
|
407
|
+
# or Method that takes fewer (or optional) parameters is never handed an
|
|
408
|
+
# argument it would reject. A splat parameter receives everything. Arity
|
|
409
|
+
# alone cannot express this: +->(env = nil) {}+ has arity -1, the same as
|
|
410
|
+
# +proc { |*a| }+, yet accepts at most one argument.
|
|
411
|
+
#
|
|
412
|
+
# @param callable [#call]
|
|
413
|
+
# @param args [Array] the arguments on offer, in order
|
|
414
|
+
# @return [Array] the leading subset of +args+ the callable accepts
|
|
415
|
+
def fallback_call_args(callable, args)
|
|
416
|
+
params = callable.respond_to?(:parameters) ? callable.parameters : callable.method(:call).parameters
|
|
417
|
+
return args if params.any? { |type, _name| type == :rest }
|
|
418
|
+
|
|
419
|
+
args.first(params.count { |type, _name| POSITIONAL_PARAMETER_TYPES.include?(type) })
|
|
420
|
+
end
|
|
421
|
+
private :validate_fallback_response!, :rack_triple?, :resolve_fallback_response, :fallback_call_args
|
|
422
|
+
|
|
306
423
|
# Class methods for Otto framework providing singleton access and configuration
|
|
307
424
|
module ClassMethods
|
|
308
425
|
def default
|
data/otto.gemspec
CHANGED
|
@@ -34,8 +34,6 @@ Gem::Specification.new do |spec|
|
|
|
34
34
|
spec.add_dependency 'logger', '~> 1', '< 2.0'
|
|
35
35
|
|
|
36
36
|
spec.add_dependency 'rack', '~> 3.1', '< 4.0'
|
|
37
|
-
spec.add_dependency 'rack-parser', '~> 0.7'
|
|
38
|
-
spec.add_dependency 'rexml', '~> 3.4'
|
|
39
37
|
|
|
40
38
|
# Security dependencies
|
|
41
39
|
spec.add_dependency 'loofah', '~> 2.20'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: otto
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Delano Mandelbaum
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: concurrent-ruby
|
|
@@ -70,34 +70,6 @@ dependencies:
|
|
|
70
70
|
- - "<"
|
|
71
71
|
- !ruby/object:Gem::Version
|
|
72
72
|
version: '4.0'
|
|
73
|
-
- !ruby/object:Gem::Dependency
|
|
74
|
-
name: rack-parser
|
|
75
|
-
requirement: !ruby/object:Gem::Requirement
|
|
76
|
-
requirements:
|
|
77
|
-
- - "~>"
|
|
78
|
-
- !ruby/object:Gem::Version
|
|
79
|
-
version: '0.7'
|
|
80
|
-
type: :runtime
|
|
81
|
-
prerelease: false
|
|
82
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
83
|
-
requirements:
|
|
84
|
-
- - "~>"
|
|
85
|
-
- !ruby/object:Gem::Version
|
|
86
|
-
version: '0.7'
|
|
87
|
-
- !ruby/object:Gem::Dependency
|
|
88
|
-
name: rexml
|
|
89
|
-
requirement: !ruby/object:Gem::Requirement
|
|
90
|
-
requirements:
|
|
91
|
-
- - "~>"
|
|
92
|
-
- !ruby/object:Gem::Version
|
|
93
|
-
version: '3.4'
|
|
94
|
-
type: :runtime
|
|
95
|
-
prerelease: false
|
|
96
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
97
|
-
requirements:
|
|
98
|
-
- - "~>"
|
|
99
|
-
- !ruby/object:Gem::Version
|
|
100
|
-
version: '3.4'
|
|
101
73
|
- !ruby/object:Gem::Dependency
|
|
102
74
|
name: loofah
|
|
103
75
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -144,18 +116,32 @@ files:
|
|
|
144
116
|
- bin/rspec
|
|
145
117
|
- changelog.d/README.md
|
|
146
118
|
- changelog.d/scriv.ini
|
|
147
|
-
- docs
|
|
148
|
-
- docs/
|
|
149
|
-
- docs/
|
|
150
|
-
- docs/
|
|
151
|
-
- docs/
|
|
152
|
-
- docs/
|
|
153
|
-
- docs/
|
|
154
|
-
- docs/
|
|
119
|
+
- docs/README.md
|
|
120
|
+
- docs/adr/README.md
|
|
121
|
+
- docs/adr/adr-001-route-authentication-at-handler-boundary.md
|
|
122
|
+
- docs/adr/adr-002-multi-strategy-authentication-and-authorization.md
|
|
123
|
+
- docs/adr/adr-003-caddy-tls-route-based-integration.md
|
|
124
|
+
- docs/adr/adr-004-separate-compatibility-from-security-maintenance.md
|
|
125
|
+
- docs/guides/authentication.md
|
|
126
|
+
- docs/guides/caddy-tls.md
|
|
127
|
+
- docs/guides/configuration_freezing.md
|
|
128
|
+
- docs/guides/enrichment.md
|
|
129
|
+
- docs/guides/forwarded-authority.md
|
|
130
|
+
- docs/guides/geo-country.md
|
|
131
|
+
- docs/guides/ip_privacy.md
|
|
132
|
+
- docs/guides/ipaddr-encoding-quirk.md
|
|
133
|
+
- docs/guides/mcp.md
|
|
134
|
+
- docs/guides/privacy.md
|
|
135
|
+
- docs/guides/routing.md
|
|
136
|
+
- docs/guides/structured_logging.md
|
|
137
|
+
- docs/guides/testing-guide.md
|
|
138
|
+
- docs/maintainers/github-actions.md
|
|
139
|
+
- docs/maintainers/investigations/.gitignore
|
|
140
|
+
- docs/migrating/v2.0.0.md
|
|
155
141
|
- docs/migrating/v2.3.0.md
|
|
156
|
-
- docs/
|
|
157
|
-
- docs/
|
|
158
|
-
- docs/
|
|
142
|
+
- docs/reference/authentication.md
|
|
143
|
+
- docs/reference/route-syntax.md
|
|
144
|
+
- docs/reference/runtime-and-dependency-security.md
|
|
159
145
|
- examples/.gitignore
|
|
160
146
|
- examples/advanced_routes/README.md
|
|
161
147
|
- examples/advanced_routes/app.rb
|
|
@@ -237,6 +223,7 @@ files:
|
|
|
237
223
|
- lib/otto/core/middleware_management.rb
|
|
238
224
|
- lib/otto/core/middleware_stack.rb
|
|
239
225
|
- lib/otto/core/router.rb
|
|
226
|
+
- lib/otto/core/static_mounts.rb
|
|
240
227
|
- lib/otto/core/uri_generator.rb
|
|
241
228
|
- lib/otto/design_system.rb
|
|
242
229
|
- lib/otto/env_keys.rb
|
|
@@ -251,12 +238,16 @@ files:
|
|
|
251
238
|
- lib/otto/mcp.rb
|
|
252
239
|
- lib/otto/mcp/auth/token.rb
|
|
253
240
|
- lib/otto/mcp/core.rb
|
|
241
|
+
- lib/otto/mcp/endpoint.rb
|
|
242
|
+
- lib/otto/mcp/errors.rb
|
|
243
|
+
- lib/otto/mcp/options.rb
|
|
254
244
|
- lib/otto/mcp/protocol.rb
|
|
255
245
|
- lib/otto/mcp/rate_limiting.rb
|
|
256
246
|
- lib/otto/mcp/registry.rb
|
|
257
247
|
- lib/otto/mcp/route_parser.rb
|
|
258
248
|
- lib/otto/mcp/schema_validation.rb
|
|
259
249
|
- lib/otto/mcp/server.rb
|
|
250
|
+
- lib/otto/optional_dependency.rb
|
|
260
251
|
- lib/otto/privacy.rb
|
|
261
252
|
- lib/otto/privacy/anonymizer_resolver.rb
|
|
262
253
|
- lib/otto/privacy/asn_resolver.rb
|
data/docs/.gitignore
DELETED