Module: Otto::MCP
- Defined in:
- lib/otto/mcp/endpoint.rb,
lib/otto/mcp/core.rb,
lib/otto/mcp/errors.rb,
lib/otto/mcp/server.rb,
lib/otto/mcp/options.rb,
lib/otto/mcp/protocol.rb,
lib/otto/mcp/registry.rb,
lib/otto/mcp/auth/token.rb,
lib/otto/mcp/route_parser.rb,
lib/otto/mcp/rate_limiting.rb,
lib/otto/mcp/schema_validation.rb
Overview
Model Context Protocol support.
Defined Under Namespace
Modules: Auth, Core, Options Classes: InternalHandler, Protocol, RateLimitMiddleware, RateLimiter, Registry, RouteParser, SchemaValidationMiddleware, Server, ToolNotFoundError, ValidationError, Validator
Class Method Summary collapse
-
.endpoint_path?(path, endpoint) ⇒ Boolean
Whether
pathis the MCP endpointendpoint: the one request the router dispatches to the MCP handler, and nothing else.
Class Method Details
.endpoint_path?(path, endpoint) ⇒ Boolean
Whether path is the MCP endpoint endpoint: the one request the router
dispatches to the MCP handler, and nothing else.
The MCP route is a literal route. The router normalizes PATH_INFO with Otto::Utils.normalize_path and looks it up by exact equality, so it never dispatches a sibling path to the MCP handler. The throttles, the throttled responder, the log subscriber, the token middleware and the schema validator all used to classify by prefix (start_with?) instead: with MCP mounted at /a, every /admin request was counted by 'mcp_requests:/a' and could be answered with an MCP JSON-RPC 429, or a 401, for a path the MCP handler can never receive; with MCP at / that was every route in the app.
Both sides go through the router's normalize_path, as LocalhostGuard does for the Caddy endpoint, so a trailing slash or percent-encoding on either side cannot make these guards and the router disagree about a request.
Callers must pass PATH_INFO (Rack::Request#path_info), never
Rack::Request#path, which prepends SCRIPT_NAME. The router matches on
PATH_INFO alone, so when the host app mounts Otto under a prefix
(map '/api' { run otto }) the endpoint /_mcp is dispatched for
PATH_INFO=/_mcp while #path reads /api/_mcp and never matches.
35 36 37 38 39 |
# File 'lib/otto/mcp/endpoint.rb', line 35 def self.endpoint_path?(path, endpoint) return false if endpoint.nil? Otto::Utils.normalize_path(path) == Otto::Utils.normalize_path(endpoint) end |