fast_mcp_jwt_auth 0.1.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 +7 -0
- data/.DS_Store +0 -0
- data/.mcp.json +30 -0
- data/.mcp.json.example +30 -0
- data/.rubocop.yml +34 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +38 -0
- data/CLAUDE.md +148 -0
- data/LICENSE.txt +21 -0
- data/README.md +268 -0
- data/Rakefile +12 -0
- data/lib/fast_mcp_jwt_auth/configuration.rb +21 -0
- data/lib/fast_mcp_jwt_auth/rack_transport_patch.rb +165 -0
- data/lib/fast_mcp_jwt_auth/railtie.rb +24 -0
- data/lib/fast_mcp_jwt_auth/version.rb +5 -0
- data/lib/fast_mcp_jwt_auth.rb +40 -0
- data/sig/fast_mcp_jwt_auth.rbs +4 -0
- metadata +147 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a40c2263c74d33678f1b189fd831d51e5ea5ebf4e498fb3334e93f3763997e99
|
|
4
|
+
data.tar.gz: ef51aae1fa0e79848a23f67cf4a77194d67dfd244594edcc9f4ffcd356069249
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 357905685ab8feab45e48cfa5dc0b2c302b8e795c82038b98046f1ae732edbd6e685787e3b3b149c3967c4e4f711fab040fb95c489aceb1491448e65db248a0f
|
|
7
|
+
data.tar.gz: df3d0ace55cf03367db55b0c0cb290319d12b20a015f3ac771b4e72e0cbea499159ae6c684535507eea103f3afedd5dd8adaa070315985e2d679ee6578644976
|
data/.DS_Store
ADDED
|
Binary file
|
data/.mcp.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"workvector-production": {
|
|
4
|
+
"type": "sse",
|
|
5
|
+
"name": "WorkVector Production",
|
|
6
|
+
"url": "https://workvector.com/mcp/sse",
|
|
7
|
+
"headers": {
|
|
8
|
+
"Authorization": "Bearer ${WORKVECTOR_TOKEN}"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"filesystem-project": {
|
|
12
|
+
"type": "stdio",
|
|
13
|
+
"name": "Filesystem",
|
|
14
|
+
"command": "npx",
|
|
15
|
+
"args": [
|
|
16
|
+
"-y",
|
|
17
|
+
"@modelcontextprotocol/server-filesystem",
|
|
18
|
+
"${PWD}"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"llmmn-production": {
|
|
22
|
+
"type": "sse",
|
|
23
|
+
"name": "LLM Memory Notes Production",
|
|
24
|
+
"url": "https://llm-memory.com/mcp/sse",
|
|
25
|
+
"headers": {
|
|
26
|
+
"Authorization": "Bearer ${LLMMN_TOKEN}"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
data/.mcp.json.example
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"example-server": {
|
|
4
|
+
"type": "sse",
|
|
5
|
+
"name": "Example MCP Server",
|
|
6
|
+
"url": "https://example.com/mcp/sse",
|
|
7
|
+
"headers": {
|
|
8
|
+
"Authorization": "Bearer ${MCP_JWT_TOKEN}"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"workvector-production": {
|
|
12
|
+
"type": "sse",
|
|
13
|
+
"name": "WorkVector Production",
|
|
14
|
+
"url": "https://workvector.com/mcp/sse",
|
|
15
|
+
"headers": {
|
|
16
|
+
"Authorization": "Bearer ${WORKVECTOR_TOKEN}"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"filesystem-project": {
|
|
20
|
+
"type": "stdio",
|
|
21
|
+
"name": "Filesystem",
|
|
22
|
+
"command": "npx",
|
|
23
|
+
"args": [
|
|
24
|
+
"-y",
|
|
25
|
+
"@modelcontextprotocol/server-filesystem",
|
|
26
|
+
"${PWD}"
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.1
|
|
3
|
+
NewCops: enable
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
|
|
6
|
+
plugins:
|
|
7
|
+
- rubocop-minitest
|
|
8
|
+
|
|
9
|
+
Layout/LineLength:
|
|
10
|
+
Max: 200
|
|
11
|
+
|
|
12
|
+
Style/StringLiterals:
|
|
13
|
+
EnforcedStyle: double_quotes
|
|
14
|
+
|
|
15
|
+
Style/StringLiteralsInInterpolation:
|
|
16
|
+
EnforcedStyle: double_quotes
|
|
17
|
+
|
|
18
|
+
# Relax some metrics for reasonable code
|
|
19
|
+
Metrics/ClassLength:
|
|
20
|
+
Max: 150
|
|
21
|
+
|
|
22
|
+
Metrics/MethodLength:
|
|
23
|
+
Max: 30
|
|
24
|
+
|
|
25
|
+
Metrics/AbcSize:
|
|
26
|
+
Max: 35
|
|
27
|
+
|
|
28
|
+
# Allow longer module for JWT authentication patch - it's a cohesive logical unit
|
|
29
|
+
Metrics/ModuleLength:
|
|
30
|
+
Max: 130
|
|
31
|
+
|
|
32
|
+
# Allow development dependencies in gemspec for gems
|
|
33
|
+
Gemspec/DevelopmentDependencies:
|
|
34
|
+
Enabled: false
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ruby-3.4.2
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2025-08-19
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Initial release of FastMcp JWT Auth gem
|
|
14
|
+
- JWT token extraction from Authorization: Bearer headers
|
|
15
|
+
- Configurable JWT decoder callback for token decoding
|
|
16
|
+
- Configurable user finder callback for user lookup from decoded tokens
|
|
17
|
+
- Token validation with configurable callback (defaults to expiration check)
|
|
18
|
+
- Current user setter and resetter with configurable callbacks
|
|
19
|
+
- Automatic Rails integration via Railtie
|
|
20
|
+
- Lazy patch application for FastMcp::Transports::RackTransport
|
|
21
|
+
- Comprehensive test suite with mocked dependencies
|
|
22
|
+
- Error handling with graceful fallback to normal request processing
|
|
23
|
+
- Support for Rails 7.0+ and Ruby 3.1+
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- N/A (initial release)
|
|
27
|
+
|
|
28
|
+
### Deprecated
|
|
29
|
+
- N/A (initial release)
|
|
30
|
+
|
|
31
|
+
### Removed
|
|
32
|
+
- N/A (initial release)
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
- N/A (initial release)
|
|
36
|
+
|
|
37
|
+
### Security
|
|
38
|
+
- N/A (initial release)
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code when working with the `fast_mcp_jwt_auth` gem.
|
|
4
|
+
|
|
5
|
+
## Gem Overview
|
|
6
|
+
|
|
7
|
+
FastMCp Jwt Auth provides JWT authentication for FastMcp RackTransport, enabling secure user authentication in MCP requests.
|
|
8
|
+
It integrates seamlessly with Rails, allowing you to authenticate users using JWT tokens in a Rails application.
|
|
9
|
+
|
|
10
|
+
## Code Conventions
|
|
11
|
+
|
|
12
|
+
### Code Quality
|
|
13
|
+
- Max 200 chars/line (soft limit - prefer readability over strict compliance)
|
|
14
|
+
- breaking Ruby chain calls destroys the natural sentence flow and readability
|
|
15
|
+
- 14 lines/method, 110 lines/class
|
|
16
|
+
- Comments and tests in English
|
|
17
|
+
- KEEP CODE DRY (Don't Repeat Yourself)
|
|
18
|
+
|
|
19
|
+
### Ruby/Rails Philosophy
|
|
20
|
+
- **DO IT RUBY WAY OR RAILS WAY** - it's not Python, Java or PHP!
|
|
21
|
+
- Strong use of Ruby metaprogramming techniques
|
|
22
|
+
- code line should look like human sentence (e.g. `3.times do` not `for i in 0..2 do` - Ruby syntax reads like English)
|
|
23
|
+
- keep code raising exceptions when it's programmer's fault - DO NOT validate method parameters, expect them to be correct! Only validate user input
|
|
24
|
+
- do not repeat name of parameter in method name (e.g. `def create_new_user_from_user(user)` should be `def create_new_user_from(user)`)
|
|
25
|
+
- do not use extra variable if used only once - saves memory and reduces GC pressure under high traffic (e.g. `user = User.find(params[:id]); user.update(...)` should be `User.find(params[:id]).update(...)`) - use `.tap do` for chaining when you need to use the object later
|
|
26
|
+
- use metaprogramming instead of case statements (e.g. `self.send(method_name, params)` instead of `case method_name; when "find_slot"...` - let Ruby handle method dispatch and NoMethodError)
|
|
27
|
+
- PREFER FUNCTIONAL STYLE: use flat_map, map, select over loops and temp variables (e.g. `items.flat_map(&:children).uniq` not `results = []; items.each { |i| results.concat(i.children) }; results.uniq`)
|
|
28
|
+
- USE PATTERN MATCHING: Ruby 3.0+ `case/in` for complex conditionals instead of if/elsif chains - more expressive and catches unhandled cases
|
|
29
|
+
- ONE CLEAR RESPONSIBILITY: each method should do one thing well - if method has "and" in description, split it (e.g. `normalize_and_search` → `normalize` + `search`)
|
|
30
|
+
- FOLLOW KISS PRINCIPLE: Keep It Simple, Stupid - avoid unnecessary complexity, use simple solutions first
|
|
31
|
+
- ALWAYS TEST YOUR CODE
|
|
32
|
+
|
|
33
|
+
### Error Handling
|
|
34
|
+
- Use meaningful exception classes (not generic StandardError)
|
|
35
|
+
- Log errors with context using the configured logger
|
|
36
|
+
- Proper error propagation with fallback mechanisms
|
|
37
|
+
- Use `rescue_from` for common exceptions in Rails integration
|
|
38
|
+
|
|
39
|
+
### Performance Considerations
|
|
40
|
+
- Use database connection pooling efficiently
|
|
41
|
+
- Avoid blocking operations in main threads
|
|
42
|
+
- Cache expensive operations
|
|
43
|
+
- Monitor thread lifecycle and cleanup
|
|
44
|
+
|
|
45
|
+
### Thread Safety
|
|
46
|
+
- All operations must be thread-safe for cluster mode
|
|
47
|
+
- Use proper synchronization when accessing shared resources
|
|
48
|
+
- Handle thread lifecycle correctly (creation, monitoring, cleanup)
|
|
49
|
+
- Use connection checkout/checkin pattern for database operations
|
|
50
|
+
|
|
51
|
+
### Gem Specific Guidelines
|
|
52
|
+
|
|
53
|
+
#### Configuration
|
|
54
|
+
- Use configuration object pattern for all settings
|
|
55
|
+
- Provide sensible defaults that work out of the box
|
|
56
|
+
- Make all components configurable but not required
|
|
57
|
+
- Support both programmatic and initializer-based configuration
|
|
58
|
+
|
|
59
|
+
#### Rails Integration
|
|
60
|
+
- Use Railtie for automatic Rails integration
|
|
61
|
+
- Hook into appropriate Rails lifecycle events
|
|
62
|
+
- Respect Rails conventions for logging and error handling
|
|
63
|
+
- Provide manual configuration options for non-Rails usage
|
|
64
|
+
|
|
65
|
+
#### Error Recovery
|
|
66
|
+
- Implement automatic retry with backoff for transient errors
|
|
67
|
+
- Provide fallback mechanisms when PubSub fails
|
|
68
|
+
- Log errors appropriately without flooding logs
|
|
69
|
+
- Handle connection failures gracefully
|
|
70
|
+
|
|
71
|
+
#### Testing
|
|
72
|
+
- Test all public interfaces
|
|
73
|
+
- Mock external dependencies (PostgreSQL, FastMcp)
|
|
74
|
+
- Test error conditions and edge cases
|
|
75
|
+
- Provide test helpers for gem users
|
|
76
|
+
- Test both Rails and non-Rails usage
|
|
77
|
+
|
|
78
|
+
## Architecture
|
|
79
|
+
|
|
80
|
+
### Components
|
|
81
|
+
|
|
82
|
+
1. **FastMcpJwtAuth::Service** - Core JWT authentication service
|
|
83
|
+
- Handles JWT token generation and validation
|
|
84
|
+
- Integrates with FastMcp RackTransport for secure requests
|
|
85
|
+
2. **FastMcpJwtAuth::Configuration** - Configuration management
|
|
86
|
+
- Manages settings like JWT secret, expiration, and algorithm
|
|
87
|
+
3. **FastMcpJwtAuth::RackTransportPatch** - Monkey patch for FastMcp transport
|
|
88
|
+
- Overrides `send_message` to include JWT authentication
|
|
89
|
+
4. **FastMcpJwtAuth::Railtie** - Rails integration and lifecycle management
|
|
90
|
+
- Automatically patches FastMcp::Transports::RackTransport during Rails initialization
|
|
91
|
+
|
|
92
|
+
### Message Flow
|
|
93
|
+
|
|
94
|
+
1. **MCP Request Received** - FastMcp RackTransport receives HTTP request with Authorization header
|
|
95
|
+
2. **JWT Extraction** - Extract Bearer token from Authorization header (`HTTP_AUTHORIZATION`)
|
|
96
|
+
3. **Token Decoding** - Use configured `jwt_decoder` callback to decode JWT token
|
|
97
|
+
4. **Token Validation** - Validate token expiration and other claims using `token_validator` callback
|
|
98
|
+
5. **User Lookup** - Find user from decoded token using `user_finder` callback
|
|
99
|
+
6. **User Assignment** - Set current user in context using `current_user_setter` callback
|
|
100
|
+
7. **Request Processing** - Continue with normal MCP request handling
|
|
101
|
+
8. **Cleanup** - Clear current user context using `current_resetter` callback
|
|
102
|
+
|
|
103
|
+
### Thread Management
|
|
104
|
+
|
|
105
|
+
The gem is designed to be thread-safe for use in Rails applications:
|
|
106
|
+
|
|
107
|
+
- **Request Isolation** - Each MCP request runs in its own thread context
|
|
108
|
+
- **Current User Context** - Uses thread-local storage via Rails `Current` class for user context
|
|
109
|
+
- **Monkey Patching Safety** - Patch is applied only once using thread-safe flag checking
|
|
110
|
+
- **No Shared State** - All operations are stateless except for configuration (immutable after initialization)
|
|
111
|
+
- **Callback Thread Safety** - User-provided callbacks (`jwt_decoder`, `user_finder`, etc.) must be thread-safe
|
|
112
|
+
- **Automatic Cleanup** - Current user context is always cleared after request processing (even on exceptions)
|
|
113
|
+
|
|
114
|
+
## Dependencies
|
|
115
|
+
|
|
116
|
+
### Runtime Dependencies
|
|
117
|
+
- **rails** (>= 7.0) - Required for Rails integration, Current class, and logger support
|
|
118
|
+
|
|
119
|
+
### Development Dependencies
|
|
120
|
+
- **jwt** (~> 2.0) - Used in tests for JWT token generation and decoding examples
|
|
121
|
+
- **minitest** (~> 5.16) - Test framework
|
|
122
|
+
- **rubocop** (~> 1.21) - Ruby code style enforcement
|
|
123
|
+
- **rubocop-minitest** (~> 0.25) - Minitest-specific RuboCop rules
|
|
124
|
+
- **rubocop-rails** (~> 2.0) - Rails-specific RuboCop rules
|
|
125
|
+
|
|
126
|
+
### External Dependencies
|
|
127
|
+
- **FastMcp** - The gem monkey patches `FastMcp::Transports::RackTransport` (not declared as dependency to avoid circular dependencies)
|
|
128
|
+
- **JWT Library** - Users must provide their own JWT decoder implementation (commonly `jwt` gem)
|
|
129
|
+
|
|
130
|
+
## Development
|
|
131
|
+
|
|
132
|
+
### Running Tests
|
|
133
|
+
```bash
|
|
134
|
+
bundle exec rake test
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Linting
|
|
138
|
+
```bash
|
|
139
|
+
bundle exec rubocop
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Console
|
|
143
|
+
```bash
|
|
144
|
+
bundle exec rake console
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Project-Specific Info
|
|
148
|
+
- **LLM Memory identifier**: `fast_mcp_jwt_auth`
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 josefchmel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# FastMcp JWT Auth
|
|
2
|
+
|
|
3
|
+
**JWT Authorization header authentication extension for [FastMcp](https://github.com/yjacquin/fast-mcp) RackTransport.**
|
|
4
|
+
|
|
5
|
+
This gem extends the [FastMcp](https://github.com/yjacquin/fast-mcp) gem to enable JWT-based user authentication via Authorization headers in Rails applications. It provides configurable callbacks for token decoding, user lookup, and validation.
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
FastMcp::Transports::RackTransport doesn't have built-in JWT authentication support. For integrating with external MCP clients that use JWT tokens for authentication, you need a way to:
|
|
10
|
+
|
|
11
|
+
1. Extract JWT tokens from Authorization headers
|
|
12
|
+
2. Decode and validate the tokens
|
|
13
|
+
3. Find users based on token payload
|
|
14
|
+
4. Set `Current.user` for the request duration
|
|
15
|
+
|
|
16
|
+
## Solution
|
|
17
|
+
|
|
18
|
+
This gem provides a monkey patch for `FastMcp::Transports::RackTransport` that:
|
|
19
|
+
|
|
20
|
+
1. Extracts JWT tokens from `Authorization: Bearer` headers
|
|
21
|
+
2. Decodes tokens using configurable callbacks
|
|
22
|
+
3. Validates token expiration and other claims
|
|
23
|
+
4. Finds users using configurable lookup logic
|
|
24
|
+
5. Sets `Current.user` for request duration
|
|
25
|
+
6. Cleans up `Current` after request processing
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
**Prerequisites**: This gem requires the [fast-mcp](https://github.com/yjacquin/fast-mcp) gem to be installed first.
|
|
30
|
+
|
|
31
|
+
Add both gems to your application's Gemfile:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
gem 'fast-mcp' # Required base gem
|
|
35
|
+
gem 'fast_mcp_jwt_auth', github: 'jchsoft/fast_mcp_jwt_auth' # This extension
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
And then execute:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
bundle install
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Note**: The `fast-mcp` gem provides the core MCP (Model Context Protocol) server functionality, while this gem extends it with JWT authentication support.
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
### Automatic Integration
|
|
49
|
+
|
|
50
|
+
**No configuration needed for basic usage!** Just add the gem to your Gemfile and configure the callbacks.
|
|
51
|
+
|
|
52
|
+
The gem will:
|
|
53
|
+
- ✅ **Automatically patch** FastMcp::Transports::RackTransport during Rails initialization
|
|
54
|
+
- ✅ **Extract JWT tokens** from Authorization: Bearer headers automatically
|
|
55
|
+
- ✅ **Use Rails.logger** for logging (no configuration required)
|
|
56
|
+
- ✅ **Handle errors gracefully** with fallback to normal request processing
|
|
57
|
+
|
|
58
|
+
### MCP Server Configuration
|
|
59
|
+
|
|
60
|
+
⚠️ **IMPORTANT**: This gem enables JWT authentication for your Rails application when used with the `fast_mcp` gem. For MCP clients to authenticate with your Rails app, they need to send JWT tokens in the `Authorization: Bearer` header.
|
|
61
|
+
|
|
62
|
+
### Client-Side MCP Configuration
|
|
63
|
+
|
|
64
|
+
When your Rails app is running as an MCP server (using `fast_mcp` gem and `fast_mcp_jwt_auth` gem), MCP clients need to be configured with proper authentication headers to connect to it.
|
|
65
|
+
|
|
66
|
+
For example create or update your `.mcp.json` configuration file:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
cp .mcp.json.example .mcp.json
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Critical: The `headers` section with `Authorization: Bearer` is essential for JWT authentication:**
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"mcpServers": {
|
|
77
|
+
"your-rails-app": {
|
|
78
|
+
"type": "sse",
|
|
79
|
+
"name": "Your Rails MCP Server",
|
|
80
|
+
"url": "https://your-rails-app.com/mcp/sse",
|
|
81
|
+
"headers": {
|
|
82
|
+
"Authorization": "Bearer ${JWT_TOKEN}"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Real Example - WorkVector Integration
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"mcpServers": {
|
|
94
|
+
"workvector-production": {
|
|
95
|
+
"type": "sse",
|
|
96
|
+
"name": "WorkVector Production",
|
|
97
|
+
"url": "https://workvector.com/mcp/sse",
|
|
98
|
+
"headers": {
|
|
99
|
+
"Authorization": "Bearer ${WORKVECTOR_TOKEN}"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Why Headers are Critical
|
|
107
|
+
|
|
108
|
+
❌ **This WON'T work** - missing authentication:
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"mcpServers": {
|
|
112
|
+
"your-app": {
|
|
113
|
+
"type": "sse",
|
|
114
|
+
"url": "https://your-app.com/mcp/sse"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
✅ **This WILL work** - includes JWT authentication header:
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"mcpServers": {
|
|
124
|
+
"your-app": {
|
|
125
|
+
"type": "sse",
|
|
126
|
+
"url": "https://your-app.com/mcp/sse",
|
|
127
|
+
"headers": {
|
|
128
|
+
"Authorization": "Bearer ${JWT_TOKEN}"
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Environment Variables
|
|
136
|
+
|
|
137
|
+
Use environment variables for sensitive tokens in your `.mcp.json`:
|
|
138
|
+
|
|
139
|
+
- `${WORKVECTOR_TOKEN}` - Your WorkVector authentication token
|
|
140
|
+
- `${MCP_JWT_TOKEN}` - JWT token for other MCP servers
|
|
141
|
+
- `${PWD}` - Current working directory path
|
|
142
|
+
|
|
143
|
+
Set these in your environment or `.env` file:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
export WORKVECTOR_TOKEN="your_workvector_token_here"
|
|
147
|
+
export MCP_JWT_TOKEN="your_jwt_token_here"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Security Best Practices
|
|
151
|
+
|
|
152
|
+
- ✅ **Never commit** `.mcp.json` to version control (it's in `.gitignore`)
|
|
153
|
+
- ✅ **Use environment variables** for tokens instead of hardcoding them
|
|
154
|
+
- ✅ **Keep tokens secure** and rotate them regularly
|
|
155
|
+
- ✅ **Use the example file** as a template for new environments
|
|
156
|
+
|
|
157
|
+
## Configuration
|
|
158
|
+
|
|
159
|
+
Create an initializer to configure JWT decoding and user lookup:
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
# config/initializers/fast_mcp_jwt_auth.rb
|
|
163
|
+
FastMcpJwtAuth.configure do |config|
|
|
164
|
+
config.enabled = true
|
|
165
|
+
|
|
166
|
+
# JWT token decoding callback
|
|
167
|
+
config.jwt_decoder = ->(jwt_token) do
|
|
168
|
+
JWT.decode(jwt_token, Rails.application.credentials.secret_key_base, true, algorithm: 'HS256')[0]
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# User lookup callback
|
|
172
|
+
config.user_finder = ->(decoded_token) do
|
|
173
|
+
User.find_by(authentication_token: decoded_token['authentication_token'])
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Optional: Token validation callback (defaults to expiration check)
|
|
177
|
+
config.token_validator = ->(decoded_token) do
|
|
178
|
+
decoded_token['exp'].nil? || decoded_token['exp'] >= Time.current.to_i
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Optional: Custom current user setter (defaults to Current.user=)
|
|
182
|
+
config.current_user_setter = ->(user) do
|
|
183
|
+
Current.user = user
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Optional: Custom context resetter (defaults to Current.reset)
|
|
187
|
+
config.current_resetter = -> do
|
|
188
|
+
Current.reset
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### WorkVector Integration Example
|
|
194
|
+
|
|
195
|
+
For WorkVector-style JWT integration using JwtIdClaim:
|
|
196
|
+
|
|
197
|
+
```ruby
|
|
198
|
+
# config/initializers/fast_mcp_jwt_auth.rb
|
|
199
|
+
FastMcpJwtAuth.configure do |config|
|
|
200
|
+
config.enabled = true
|
|
201
|
+
|
|
202
|
+
# Use JwtIdClaim for token decoding (WorkVector pattern)
|
|
203
|
+
config.jwt_decoder = ->(jwt_token) do
|
|
204
|
+
JwtIdClaim.decode(jwt_token)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Find user by authentication_token from JWT payload
|
|
208
|
+
config.user_finder = ->(decoded_token) do
|
|
209
|
+
User.find_by(authentication_token: decoded_token[:authentication_token])
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Configuration Callbacks
|
|
215
|
+
|
|
216
|
+
The gem provides these configurable callbacks:
|
|
217
|
+
|
|
218
|
+
- **`jwt_decoder`**: Callback for JWT token decoding (required)
|
|
219
|
+
- **`user_finder`**: Callback for user lookup from decoded token (required)
|
|
220
|
+
- **`token_validator`**: Callback for token validation (optional, defaults to expiration check)
|
|
221
|
+
- **`current_user_setter`**: Callback for setting current user (optional, defaults to `Current.user=`)
|
|
222
|
+
- **`current_resetter`**: Callback for resetting current context (optional, defaults to `Current.reset`)
|
|
223
|
+
|
|
224
|
+
## How It Works
|
|
225
|
+
|
|
226
|
+
1. **Request Processing**: When FastMcp processes an MCP request, the patch intercepts it
|
|
227
|
+
2. **Header Extraction**: Looks for `Authorization: Bearer <token>` header
|
|
228
|
+
3. **Token Decoding**: Uses configured `jwt_decoder` callback to decode the JWT
|
|
229
|
+
4. **Token Validation**: Validates token using `token_validator` callback
|
|
230
|
+
5. **User Lookup**: Finds user using `user_finder` callback
|
|
231
|
+
6. **Context Setting**: Sets current user using `current_user_setter` callback
|
|
232
|
+
7. **Request Processing**: Continues with normal MCP request processing
|
|
233
|
+
8. **Cleanup**: Resets current context using `current_resetter` callback
|
|
234
|
+
|
|
235
|
+
## Error Handling
|
|
236
|
+
|
|
237
|
+
The gem handles errors gracefully:
|
|
238
|
+
- Invalid JWT tokens are logged as warnings but don't break request processing
|
|
239
|
+
- Missing or malformed Authorization headers are ignored silently
|
|
240
|
+
- Decoding errors fall back to normal request processing without authentication
|
|
241
|
+
- User lookup failures result in no authentication but normal request processing
|
|
242
|
+
|
|
243
|
+
## Requirements
|
|
244
|
+
|
|
245
|
+
- Ruby >= 3.1.0
|
|
246
|
+
- Rails >= 7.0
|
|
247
|
+
- FastMcp gem
|
|
248
|
+
|
|
249
|
+
## Development
|
|
250
|
+
|
|
251
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
252
|
+
|
|
253
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
254
|
+
|
|
255
|
+
## Testing
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
rake test
|
|
259
|
+
rubocop
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Contributing
|
|
263
|
+
|
|
264
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/jchsoft/fast_mcp_jwt_auth.
|
|
265
|
+
|
|
266
|
+
## License
|
|
267
|
+
|
|
268
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FastMcpJwtAuth
|
|
4
|
+
# Configuration class for FastMcpJwtAuth gem settings
|
|
5
|
+
class Configuration
|
|
6
|
+
attr_accessor :enabled, :logger, :jwt_decoder, :user_finder, :token_validator,
|
|
7
|
+
:current_user_setter, :current_resetter
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@enabled = true
|
|
11
|
+
@logger = defined?(Rails) ? Rails.logger : Logger.new($stdout)
|
|
12
|
+
@jwt_decoder = nil
|
|
13
|
+
@user_finder = nil
|
|
14
|
+
@token_validator = lambda do |decoded_token|
|
|
15
|
+
decoded_token[:exp].nil? || decoded_token[:exp] >= Time.current.to_i
|
|
16
|
+
end
|
|
17
|
+
@current_user_setter = ->(user) { Current.user = user }
|
|
18
|
+
@current_resetter = -> { Current.reset }
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Monkey patch for FastMcp::Transports::RackTransport
|
|
4
|
+
# Adds JWT authentication support via Authorization header
|
|
5
|
+
|
|
6
|
+
module FastMcpJwtAuth
|
|
7
|
+
# Lazy patch application - applies patch when FastMcp transport is first accessed
|
|
8
|
+
module RackTransportPatch
|
|
9
|
+
@patch_applied = false
|
|
10
|
+
|
|
11
|
+
def self.apply_patch!
|
|
12
|
+
return FastMcpJwtAuth.log_debug("RackTransport patch already applied, skipping") if @patch_applied
|
|
13
|
+
return FastMcpJwtAuth.log_debug("FastMcp::Transports::RackTransport not defined yet, skipping patch") unless defined?(FastMcp::Transports::RackTransport)
|
|
14
|
+
return FastMcpJwtAuth.log_debug("JWT authentication disabled, skipping patch") unless FastMcpJwtAuth.config.enabled
|
|
15
|
+
|
|
16
|
+
apply_patch_to_transport
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.apply_patch_to_transport
|
|
20
|
+
FastMcpJwtAuth.log_info "Applying JWT authentication patch to FastMcp::Transports::RackTransport"
|
|
21
|
+
patch_transport_class
|
|
22
|
+
@patch_applied = true
|
|
23
|
+
FastMcpJwtAuth.log_info "JWT authentication patch applied successfully"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.patch_transport_class
|
|
27
|
+
FastMcp::Transports::RackTransport.prepend(JwtAuthenticationPatch)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.patch_applied?
|
|
31
|
+
@patch_applied
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The actual patch module that gets prepended
|
|
35
|
+
module JwtAuthenticationPatch
|
|
36
|
+
def handle_mcp_request(request, env)
|
|
37
|
+
authenticate_user_from_jwt(request)
|
|
38
|
+
super
|
|
39
|
+
ensure
|
|
40
|
+
clear_current_user
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def authenticate_user_from_jwt(request)
|
|
46
|
+
extract_jwt_token(request)&.tap do |jwt_token|
|
|
47
|
+
FastMcpJwtAuth.log_debug "Extracted JWT token from Authorization header (length: #{jwt_token.length} chars)"
|
|
48
|
+
authenticate_user_with_token(jwt_token)
|
|
49
|
+
end
|
|
50
|
+
rescue StandardError => e
|
|
51
|
+
log_authentication_error(e)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def extract_jwt_token(request)
|
|
55
|
+
auth_header = request.env["HTTP_AUTHORIZATION"]
|
|
56
|
+
return log_and_return("No Authorization header found in request") unless auth_header
|
|
57
|
+
return log_and_return("Authorization header present but not Bearer token format: #{auth_header[0..20]}...") unless auth_header.start_with?("Bearer ")
|
|
58
|
+
|
|
59
|
+
auth_header.sub("Bearer ", "")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def log_and_return(message)
|
|
63
|
+
FastMcpJwtAuth.log_debug message
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def log_authentication_error(exception)
|
|
68
|
+
FastMcpJwtAuth.log_error "JWT token authentication failed with exception: #{exception.class.name} - #{exception.message}"
|
|
69
|
+
FastMcpJwtAuth.log_debug "JWT authentication error backtrace: #{exception.backtrace&.first(3)&.join("; ")}"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def authenticate_user_with_token(jwt_token)
|
|
73
|
+
return FastMcpJwtAuth.log_warn("JWT decoder not configured, skipping token authentication") unless FastMcpJwtAuth.config.jwt_decoder
|
|
74
|
+
|
|
75
|
+
decode_and_authenticate_token(jwt_token)
|
|
76
|
+
rescue StandardError => e
|
|
77
|
+
FastMcpJwtAuth.log_error "JWT token decoding failed: #{e.class.name} - #{e.message}"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def decode_and_authenticate_token(jwt_token)
|
|
81
|
+
FastMcpJwtAuth.log_debug "Attempting to decode JWT token"
|
|
82
|
+
return FastMcpJwtAuth.log_warn("JWT decoder returned nil - token may be invalid or malformed") unless (decoded_token = FastMcpJwtAuth.config.jwt_decoder.call(jwt_token))
|
|
83
|
+
|
|
84
|
+
FastMcpJwtAuth.log_debug "JWT token decoded successfully, checking validity"
|
|
85
|
+
return unless token_valid?(decoded_token)
|
|
86
|
+
|
|
87
|
+
FastMcpJwtAuth.log_debug "JWT token validation passed, looking up user"
|
|
88
|
+
authenticate_found_user(find_user_from_token(decoded_token))
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def authenticate_found_user(user)
|
|
92
|
+
if user
|
|
93
|
+
FastMcpJwtAuth.log_debug "Setting current user context"
|
|
94
|
+
assign_current_user(user)
|
|
95
|
+
FastMcpJwtAuth.log_info "User authentication completed successfully"
|
|
96
|
+
else
|
|
97
|
+
FastMcpJwtAuth.log_warn "Authentication failed: no user found for token"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def token_valid?(decoded_token)
|
|
102
|
+
return log_debug_and_return_true?("No token validator configured, considering token valid") unless FastMcpJwtAuth.config.token_validator
|
|
103
|
+
|
|
104
|
+
validate_decoded_token(decoded_token)
|
|
105
|
+
rescue StandardError => e
|
|
106
|
+
FastMcpJwtAuth.log_error "Token validation failed with exception: #{e.class.name} - #{e.message}"
|
|
107
|
+
false
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def validate_decoded_token(decoded_token)
|
|
111
|
+
FastMcpJwtAuth.log_debug "Running token validation"
|
|
112
|
+
FastMcpJwtAuth.config.token_validator.call(decoded_token).tap do |valid|
|
|
113
|
+
log_validation_result(valid)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def log_validation_result(valid)
|
|
118
|
+
if valid
|
|
119
|
+
FastMcpJwtAuth.log_debug "Token validation passed"
|
|
120
|
+
else
|
|
121
|
+
FastMcpJwtAuth.log_warn "Token validation failed - validator returned falsy value"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def log_debug_and_return_true?(message)
|
|
126
|
+
FastMcpJwtAuth.log_debug message
|
|
127
|
+
true
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def find_user_from_token(decoded_token)
|
|
131
|
+
return FastMcpJwtAuth.log_warn("User finder not configured, cannot authenticate user") unless FastMcpJwtAuth.config.user_finder
|
|
132
|
+
|
|
133
|
+
lookup_user_from_decoded_token(decoded_token)
|
|
134
|
+
rescue StandardError => e
|
|
135
|
+
FastMcpJwtAuth.log_error "User lookup failed with exception: #{e.class.name} - #{e.message}"
|
|
136
|
+
nil
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def lookup_user_from_decoded_token(decoded_token)
|
|
140
|
+
FastMcpJwtAuth.log_debug "Looking up user from decoded token"
|
|
141
|
+
FastMcpJwtAuth.config.user_finder.call(decoded_token).tap do |user|
|
|
142
|
+
log_user_lookup_result(user)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def log_user_lookup_result(user)
|
|
147
|
+
if user
|
|
148
|
+
FastMcpJwtAuth.log_debug "User found successfully: #{user}"
|
|
149
|
+
else
|
|
150
|
+
FastMcpJwtAuth.log_warn "User finder returned nil - user may not exist or be inactive"
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def assign_current_user(user)
|
|
155
|
+
FastMcpJwtAuth.config.current_user_setter&.call(user)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def clear_current_user
|
|
159
|
+
FastMcpJwtAuth.config.current_resetter&.call
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# NOTE: Patch is automatically applied by Railtie initializer when Rails loads
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FastMcpJwtAuth
|
|
4
|
+
# Rails integration for automatic FastMcpJwtAuth setup
|
|
5
|
+
class Railtie < Rails::Railtie
|
|
6
|
+
# Apply patch to FastMcp::Transports::RackTransport after all initializers are loaded
|
|
7
|
+
initializer "fast_mcp_jwt_auth.apply_patch", after: :load_config_initializers do
|
|
8
|
+
Rails.application.config.after_initialize do
|
|
9
|
+
FastMcpJwtAuth.config.enabled ? FastMcpJwtAuth::Railtie.apply_jwt_patch : FastMcpJwtAuth::Railtie.log_disabled_status
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def apply_jwt_patch
|
|
15
|
+
FastMcpJwtAuth.log_debug "Attempting to apply RackTransport patch"
|
|
16
|
+
FastMcpJwtAuth::RackTransportPatch.apply_patch!
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def log_disabled_status
|
|
20
|
+
FastMcpJwtAuth.log_info "JWT authentication disabled"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "fast_mcp_jwt_auth/version"
|
|
4
|
+
require_relative "fast_mcp_jwt_auth/configuration"
|
|
5
|
+
|
|
6
|
+
# JWT Authorization header authentication for FastMcp RackTransport.
|
|
7
|
+
# Enables FastMcp RackTransport to authenticate users via JWT tokens passed through Authorization headers
|
|
8
|
+
# with configurable callbacks for token decoding and user lookup.
|
|
9
|
+
module FastMcpJwtAuth
|
|
10
|
+
class Error < StandardError; end
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
attr_accessor :configuration
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.configure
|
|
17
|
+
self.configuration ||= Configuration.new
|
|
18
|
+
yield(configuration)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.config
|
|
22
|
+
self.configuration ||= Configuration.new
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Simple logging helper - uses Rails.logger since this gem is Rails-specific
|
|
26
|
+
def self.logger
|
|
27
|
+
Rails.logger
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# DRY logging with consistent prefix - Ruby metaprogramming way
|
|
31
|
+
%i[debug info warn error].each do |level|
|
|
32
|
+
define_singleton_method("log_#{level}") do |message|
|
|
33
|
+
logger&.public_send(level, "FastMcpJwtAuth: #{message}")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Load patch after module is fully defined
|
|
39
|
+
require_relative "fast_mcp_jwt_auth/rack_transport_patch"
|
|
40
|
+
require_relative "fast_mcp_jwt_auth/railtie" if defined?(Rails)
|
metadata
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: fast_mcp_jwt_auth
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- josefchmel
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2025-10-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '7.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: jwt
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: minitest
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '5.16'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '5.16'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rubocop
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '1.21'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '1.21'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rubocop-minitest
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0.25'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0.25'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: rubocop-rails
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '2.0'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '2.0'
|
|
96
|
+
description: Enables FastMcp RackTransport to authenticate users via JWT tokens passed
|
|
97
|
+
through Authorization headers with configurable callbacks for token decoding and
|
|
98
|
+
user lookup
|
|
99
|
+
email:
|
|
100
|
+
- chmel@jchsoft.cz
|
|
101
|
+
executables: []
|
|
102
|
+
extensions: []
|
|
103
|
+
extra_rdoc_files: []
|
|
104
|
+
files:
|
|
105
|
+
- ".DS_Store"
|
|
106
|
+
- ".mcp.json"
|
|
107
|
+
- ".mcp.json.example"
|
|
108
|
+
- ".rubocop.yml"
|
|
109
|
+
- ".ruby-version"
|
|
110
|
+
- CHANGELOG.md
|
|
111
|
+
- CLAUDE.md
|
|
112
|
+
- LICENSE.txt
|
|
113
|
+
- README.md
|
|
114
|
+
- Rakefile
|
|
115
|
+
- lib/fast_mcp_jwt_auth.rb
|
|
116
|
+
- lib/fast_mcp_jwt_auth/configuration.rb
|
|
117
|
+
- lib/fast_mcp_jwt_auth/rack_transport_patch.rb
|
|
118
|
+
- lib/fast_mcp_jwt_auth/railtie.rb
|
|
119
|
+
- lib/fast_mcp_jwt_auth/version.rb
|
|
120
|
+
- sig/fast_mcp_jwt_auth.rbs
|
|
121
|
+
homepage: https://github.com/jchsoft/fast_mcp_jwt_auth
|
|
122
|
+
licenses:
|
|
123
|
+
- MIT
|
|
124
|
+
metadata:
|
|
125
|
+
allowed_push_host: https://rubygems.org
|
|
126
|
+
homepage_uri: https://github.com/jchsoft/fast_mcp_jwt_auth
|
|
127
|
+
source_code_uri: https://github.com/jchsoft/fast_mcp_jwt_auth
|
|
128
|
+
changelog_uri: https://github.com/jchsoft/fast_mcp_jwt_auth/blob/main/CHANGELOG.md
|
|
129
|
+
rubygems_mfa_required: 'true'
|
|
130
|
+
rdoc_options: []
|
|
131
|
+
require_paths:
|
|
132
|
+
- lib
|
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - ">="
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: 3.1.0
|
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
|
+
requirements:
|
|
140
|
+
- - ">="
|
|
141
|
+
- !ruby/object:Gem::Version
|
|
142
|
+
version: '0'
|
|
143
|
+
requirements: []
|
|
144
|
+
rubygems_version: 3.6.2
|
|
145
|
+
specification_version: 4
|
|
146
|
+
summary: JWT Authorization header authentication for FastMcp RackTransport
|
|
147
|
+
test_files: []
|