mcp-datetime-ruby 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/CHANGELOG.md +16 -0
- data/LICENSE.txt +21 -0
- data/README.md +322 -0
- data/bin/mcp-datetime-ruby +8 -0
- data/lib/mcp/datetime/server.rb +294 -0
- data/lib/mcp/datetime/tools.rb +41 -0
- data/lib/mcp/datetime/version.rb +8 -0
- data/lib/mcp/datetime.rb +14 -0
- data/lib/rubygems_plugin.rb +65 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6de1db88b3a1ab058beed308b8f6f5dc13182e21bd1583dddab47cd3163bf6b5
|
4
|
+
data.tar.gz: f1e050574dc6efb282a8855ecd75122f981a2996c9c7c8614a96169298abdd3c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7691d9741f8f531497fb0a910766bb4ce138724af591b3e55dada6acd144e1475899f8aba30d4ece79b842093db797c5f2bbe314e90cc21f7b4bfba611e82f3a
|
7
|
+
data.tar.gz: 36890bbf01deff8516c58030897faf98d06bad558b65e6b66ac82103ec21782ad67bcf52b6c94842e61a2363dca8f97be5bbc4a231bcd1133ee9aabed0065f12
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,16 @@
|
|
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
|
+
## [0.1.0] - 2025-06-10
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Initial release
|
12
|
+
- MCP server implementation for datetime tools
|
13
|
+
- `get_current_datetime` tool with multiple format options
|
14
|
+
- `get_date_info` tool for detailed date information
|
15
|
+
- Support for timezone handling
|
16
|
+
- Debug logging to `/tmp/mcp_datetime_debug.log`
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 Wolfgang Teuber
|
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,322 @@
|
|
1
|
+
# MCP DateTime Ruby
|
2
|
+
|
3
|
+
A Ruby implementation of an MCP (Model Context Protocol) server that provides datetime tools for AI assistants. This server enables AI assistants to get current date/time information and detailed date information in various formats.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- Get current date and time in multiple formats (ISO, human-readable, Unix timestamp, etc.)
|
8
|
+
- Support for timezone conversions
|
9
|
+
- Detailed date information (weekday, quarter, leap year status, etc.)
|
10
|
+
- Full MCP protocol compliance
|
11
|
+
- Automatic executable installation via RubyGems plugin
|
12
|
+
|
13
|
+
## Requirements
|
14
|
+
|
15
|
+
- Ruby 3.1.0 or higher
|
16
|
+
- Bundler
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
### From RubyGems (Once Published)
|
21
|
+
|
22
|
+
```bash
|
23
|
+
gem install mcp-datetime-ruby
|
24
|
+
```
|
25
|
+
|
26
|
+
The gem will automatically install an executable at `~/bin/mcp-datetime-ruby` during installation.
|
27
|
+
|
28
|
+
### Local Installation (Development)
|
29
|
+
|
30
|
+
1. **Clone the repository**:
|
31
|
+
```bash
|
32
|
+
git clone https://github.com/wteuber/mcp-datetime-ruby.git
|
33
|
+
cd mcp-datetime-ruby
|
34
|
+
```
|
35
|
+
|
36
|
+
2. **Install dependencies**:
|
37
|
+
```bash
|
38
|
+
bundle install
|
39
|
+
```
|
40
|
+
|
41
|
+
3. **Build and install the gem locally**:
|
42
|
+
```bash
|
43
|
+
gem build mcp-datetime-ruby.gemspec
|
44
|
+
gem install ./mcp-datetime-ruby-0.1.0.gem
|
45
|
+
```
|
46
|
+
|
47
|
+
This will automatically create the executable at `~/bin/mcp-datetime-ruby`.
|
48
|
+
|
49
|
+
4. **Or run directly without installing**:
|
50
|
+
```bash
|
51
|
+
# Run directly from the repository
|
52
|
+
./bin/mcp-datetime-ruby
|
53
|
+
|
54
|
+
# Or with bundle exec
|
55
|
+
bundle exec ruby bin/mcp-datetime-ruby
|
56
|
+
```
|
57
|
+
|
58
|
+
### Verifying Installation
|
59
|
+
|
60
|
+
After installation, verify the executable is available:
|
61
|
+
|
62
|
+
```bash
|
63
|
+
# Check if the executable exists
|
64
|
+
which mcp-datetime-ruby
|
65
|
+
|
66
|
+
# Or if ~/bin is not in your PATH
|
67
|
+
ls ~/bin/mcp-datetime-ruby
|
68
|
+
```
|
69
|
+
|
70
|
+
## Configuration
|
71
|
+
|
72
|
+
### Adding to Cursor
|
73
|
+
|
74
|
+
To use this MCP server with Cursor:
|
75
|
+
|
76
|
+
1. **Open your MCP configuration file**:
|
77
|
+
```bash
|
78
|
+
# Create the file if it doesn't exist
|
79
|
+
touch ~/.cursor/mcp.json
|
80
|
+
```
|
81
|
+
|
82
|
+
2. **Add the datetime server configuration**:
|
83
|
+
|
84
|
+
If `~/bin` is in your PATH:
|
85
|
+
```json
|
86
|
+
{
|
87
|
+
"mcpServers": {
|
88
|
+
"datetime": {
|
89
|
+
"command": "mcp-datetime-ruby"
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
```
|
94
|
+
|
95
|
+
If you need to use the full path:
|
96
|
+
```json
|
97
|
+
{
|
98
|
+
"mcpServers": {
|
99
|
+
"datetime": {
|
100
|
+
"command": "/Users/yourusername/bin/mcp-datetime-ruby"
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
104
|
+
```
|
105
|
+
|
106
|
+
If running from the repository directly:
|
107
|
+
```json
|
108
|
+
{
|
109
|
+
"mcpServers": {
|
110
|
+
"datetime": {
|
111
|
+
"command": "/path/to/mcp-datetime-ruby/bin/mcp-datetime-ruby"
|
112
|
+
}
|
113
|
+
}
|
114
|
+
}
|
115
|
+
```
|
116
|
+
|
117
|
+
3. **If you have existing MCP servers**, add to the existing configuration:
|
118
|
+
```json
|
119
|
+
{
|
120
|
+
"mcpServers": {
|
121
|
+
"existing-server": {
|
122
|
+
"command": "existing-command"
|
123
|
+
},
|
124
|
+
"datetime": {
|
125
|
+
"command": "mcp-datetime-ruby"
|
126
|
+
}
|
127
|
+
}
|
128
|
+
}
|
129
|
+
```
|
130
|
+
|
131
|
+
4. **Restart Cursor** for the changes to take effect.
|
132
|
+
|
133
|
+
5. **Verify the server is working** by asking the AI to "get the current time" or "what's today's date".
|
134
|
+
|
135
|
+
## Available Tools
|
136
|
+
|
137
|
+
### `get_current_datetime`
|
138
|
+
|
139
|
+
Get the current date and time in various formats.
|
140
|
+
|
141
|
+
**Parameters:**
|
142
|
+
- `format` (optional): Output format
|
143
|
+
- `iso` (default): ISO 8601 format (e.g., "2024-01-15T14:30:45-05:00")
|
144
|
+
- `human`: Human-readable format (e.g., "January 15, 2024 at 02:30 PM")
|
145
|
+
- `date_only`: Date only (e.g., "2024-01-15")
|
146
|
+
- `time_only`: Time only (e.g., "14:30:45")
|
147
|
+
- `unix`: Unix timestamp (e.g., "1705342245")
|
148
|
+
- `timezone` (optional): IANA timezone name (e.g., "America/New_York", "Europe/London", "Asia/Tokyo")
|
149
|
+
|
150
|
+
**Example Response:**
|
151
|
+
```json
|
152
|
+
{
|
153
|
+
"datetime": "2024-01-15T14:30:45-05:00",
|
154
|
+
"timestamp": 1705342245.123456,
|
155
|
+
"year": 2024,
|
156
|
+
"month": 1,
|
157
|
+
"day": 15,
|
158
|
+
"hour": 14,
|
159
|
+
"minute": 30,
|
160
|
+
"second": 45,
|
161
|
+
"weekday": "Monday",
|
162
|
+
"timezone": "EST"
|
163
|
+
}
|
164
|
+
```
|
165
|
+
|
166
|
+
### `get_date_info`
|
167
|
+
|
168
|
+
Get detailed information about the current date.
|
169
|
+
|
170
|
+
**Parameters:** None
|
171
|
+
|
172
|
+
**Example Response:**
|
173
|
+
```json
|
174
|
+
{
|
175
|
+
"date": "2024-01-15",
|
176
|
+
"year": 2024,
|
177
|
+
"month": 1,
|
178
|
+
"month_name": "January",
|
179
|
+
"day": 15,
|
180
|
+
"weekday": "Monday",
|
181
|
+
"weekday_number": 1,
|
182
|
+
"day_of_year": 15,
|
183
|
+
"week_of_year": 3,
|
184
|
+
"quarter": 1,
|
185
|
+
"is_weekend": false,
|
186
|
+
"is_leap_year": true,
|
187
|
+
"days_in_month": 31,
|
188
|
+
"timezone": "EST"
|
189
|
+
}
|
190
|
+
```
|
191
|
+
|
192
|
+
## Development
|
193
|
+
|
194
|
+
### Setup
|
195
|
+
|
196
|
+
After cloning the repository:
|
197
|
+
|
198
|
+
```bash
|
199
|
+
# Install dependencies
|
200
|
+
bundle install
|
201
|
+
|
202
|
+
# Run tests to verify everything is working
|
203
|
+
bundle exec rake test
|
204
|
+
```
|
205
|
+
|
206
|
+
### Code Style
|
207
|
+
|
208
|
+
This project uses RuboCop for code style enforcement. Run RuboCop to check for style violations:
|
209
|
+
|
210
|
+
```bash
|
211
|
+
# Check for style violations
|
212
|
+
bundle exec rubocop
|
213
|
+
|
214
|
+
# Auto-correct correctable violations
|
215
|
+
bundle exec rubocop -a
|
216
|
+
|
217
|
+
# Auto-correct with more aggressive corrections
|
218
|
+
bundle exec rubocop -A
|
219
|
+
```
|
220
|
+
|
221
|
+
The project includes a `.rubocop.yml` configuration file that customizes the rules for this codebase.
|
222
|
+
|
223
|
+
### Running Tests
|
224
|
+
|
225
|
+
The gem includes comprehensive tests using Minitest:
|
226
|
+
|
227
|
+
```bash
|
228
|
+
# Run unit tests only (default)
|
229
|
+
bundle exec rake test
|
230
|
+
|
231
|
+
# Run integration tests only
|
232
|
+
bundle exec rake integration
|
233
|
+
|
234
|
+
# Run all tests (unit + integration)
|
235
|
+
bundle exec rake test_all
|
236
|
+
|
237
|
+
# Run tests with verbose output
|
238
|
+
bundle exec rake test TESTOPTS="--verbose"
|
239
|
+
|
240
|
+
# Run a specific test file
|
241
|
+
bundle exec ruby -Ilib:test test/mcp/datetime/server_test.rb
|
242
|
+
```
|
243
|
+
|
244
|
+
The test suite includes:
|
245
|
+
- Unit tests for all MCP protocol methods
|
246
|
+
- Tests for each datetime format and timezone handling
|
247
|
+
- Edge case and error handling tests
|
248
|
+
- RubyGems plugin tests
|
249
|
+
- Integration tests for full server communication
|
250
|
+
|
251
|
+
### Building the Gem
|
252
|
+
|
253
|
+
```bash
|
254
|
+
# Build the gem
|
255
|
+
gem build mcp-datetime-ruby.gemspec
|
256
|
+
|
257
|
+
# Install locally for testing
|
258
|
+
gem install ./mcp-datetime-ruby-0.1.0.gem
|
259
|
+
```
|
260
|
+
|
261
|
+
### Debugging
|
262
|
+
|
263
|
+
The server logs debug information to `/tmp/mcp_datetime_debug.log`. You can tail this file to see server activity:
|
264
|
+
|
265
|
+
```bash
|
266
|
+
tail -f /tmp/mcp_datetime_debug.log
|
267
|
+
```
|
268
|
+
|
269
|
+
## Uninstalling
|
270
|
+
|
271
|
+
### Uninstall the Gem
|
272
|
+
|
273
|
+
```bash
|
274
|
+
# Uninstall the gem (this will also remove the executable from ~/bin)
|
275
|
+
gem uninstall mcp-datetime-ruby
|
276
|
+
|
277
|
+
# If multiple versions are installed
|
278
|
+
gem uninstall mcp-datetime-ruby --all
|
279
|
+
```
|
280
|
+
|
281
|
+
### Remove from Cursor Configuration
|
282
|
+
|
283
|
+
Remove the datetime server entry from `~/.cursor/mcp.json`:
|
284
|
+
|
285
|
+
```json
|
286
|
+
{
|
287
|
+
"mcpServers": {
|
288
|
+
// Remove this entire "datetime" section
|
289
|
+
"datetime": {
|
290
|
+
"command": "mcp-datetime-ruby"
|
291
|
+
}
|
292
|
+
}
|
293
|
+
}
|
294
|
+
```
|
295
|
+
|
296
|
+
### Clean Up Development Files
|
297
|
+
|
298
|
+
If you cloned the repository:
|
299
|
+
|
300
|
+
```bash
|
301
|
+
# Remove the cloned repository
|
302
|
+
rm -rf /path/to/mcp-datetime-ruby
|
303
|
+
|
304
|
+
# Remove any locally built gem files
|
305
|
+
rm mcp-datetime-ruby-*.gem
|
306
|
+
```
|
307
|
+
|
308
|
+
## Contributing
|
309
|
+
|
310
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/wteuber/mcp-datetime-ruby.
|
311
|
+
|
312
|
+
1. Fork the repository
|
313
|
+
2. Create your feature branch (`git checkout -b feature/my-new-feature`)
|
314
|
+
3. Write tests for your changes
|
315
|
+
4. Make your changes and ensure all tests pass
|
316
|
+
5. Commit your changes (`git commit -am 'Add some feature'`)
|
317
|
+
6. Push to the branch (`git push origin feature/my-new-feature`)
|
318
|
+
7. Create a new Pull Request
|
319
|
+
|
320
|
+
## License
|
321
|
+
|
322
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,294 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative 'tools'
|
5
|
+
|
6
|
+
module MCP
|
7
|
+
module DateTime
|
8
|
+
# MCP DateTime Server implementation
|
9
|
+
# Provides datetime tools via the Model Context Protocol
|
10
|
+
class Server
|
11
|
+
include Tools
|
12
|
+
|
13
|
+
LOG_FILE = '/tmp/mcp_datetime_debug.log'
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@server_info = {
|
17
|
+
name: 'mcp-datetime-ruby',
|
18
|
+
version: VERSION
|
19
|
+
}
|
20
|
+
setup_io_streams
|
21
|
+
setup_signal_handlers
|
22
|
+
log_startup_info
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
warn '[MCP::DateTime] Starting server...'
|
27
|
+
process_requests
|
28
|
+
warn '[MCP::DateTime] Server stopped'
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def setup_io_streams
|
34
|
+
$stderr.sync = true
|
35
|
+
$stdout.sync = true
|
36
|
+
end
|
37
|
+
|
38
|
+
def setup_signal_handlers
|
39
|
+
Signal.trap('INT') { exit(0) }
|
40
|
+
Signal.trap('TERM') { exit(0) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def log_startup_info
|
44
|
+
log("Starting MCP DateTime server (Ruby #{RUBY_VERSION})")
|
45
|
+
log("Script location: #{__FILE__}")
|
46
|
+
log("Working directory: #{Dir.pwd}")
|
47
|
+
log("Environment: #{ENV.to_h}")
|
48
|
+
end
|
49
|
+
|
50
|
+
def process_requests
|
51
|
+
loop do
|
52
|
+
line = read_request
|
53
|
+
next unless line
|
54
|
+
|
55
|
+
handle_request_line(line)
|
56
|
+
rescue JSON::ParserError => e
|
57
|
+
handle_parse_error(e)
|
58
|
+
rescue Interrupt
|
59
|
+
handle_interrupt
|
60
|
+
rescue StandardError => e
|
61
|
+
handle_error(e)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def read_request
|
66
|
+
line = $stdin.gets
|
67
|
+
if line.nil?
|
68
|
+
handle_eof
|
69
|
+
return nil
|
70
|
+
end
|
71
|
+
line.strip.empty? ? nil : line
|
72
|
+
end
|
73
|
+
|
74
|
+
def handle_eof
|
75
|
+
log('EOF received, shutting down gracefully')
|
76
|
+
warn '[MCP::DateTime] EOF received, shutting down gracefully'
|
77
|
+
exit(0)
|
78
|
+
end
|
79
|
+
|
80
|
+
def handle_request_line(line)
|
81
|
+
request = JSON.parse(line.strip)
|
82
|
+
log("Received request: #{request['method']}")
|
83
|
+
warn "[MCP::DateTime] Received request: #{request['method']}"
|
84
|
+
|
85
|
+
response = handle_request(request)
|
86
|
+
send_response(request, response)
|
87
|
+
end
|
88
|
+
|
89
|
+
def send_response(request, response)
|
90
|
+
return unless request['id'] && response
|
91
|
+
|
92
|
+
$stdout.puts(JSON.generate(response))
|
93
|
+
$stdout.flush
|
94
|
+
end
|
95
|
+
|
96
|
+
def handle_parse_error(error)
|
97
|
+
log("Parse error: #{error.message}")
|
98
|
+
error_response = build_error_response(-32_700, "Parse error: #{error.message}")
|
99
|
+
$stdout.puts(JSON.generate(error_response))
|
100
|
+
$stdout.flush
|
101
|
+
end
|
102
|
+
|
103
|
+
def handle_interrupt
|
104
|
+
log('Interrupted, shutting down')
|
105
|
+
warn '[MCP::DateTime] Interrupted, shutting down'
|
106
|
+
exit(0)
|
107
|
+
end
|
108
|
+
|
109
|
+
def handle_error(error)
|
110
|
+
log("Error: #{error.message}")
|
111
|
+
log(error.backtrace.join("\n"))
|
112
|
+
warn "[MCP::DateTime] Error: #{error.message}"
|
113
|
+
warn error.backtrace.join("\n")
|
114
|
+
end
|
115
|
+
|
116
|
+
def log(message)
|
117
|
+
File.open(LOG_FILE, 'a') do |f|
|
118
|
+
f.puts "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}] #{message}"
|
119
|
+
end
|
120
|
+
rescue StandardError => e
|
121
|
+
warn "Failed to write to log: #{e.message}"
|
122
|
+
end
|
123
|
+
|
124
|
+
def handle_request(request)
|
125
|
+
method = request['method']
|
126
|
+
params = request['params'] || {}
|
127
|
+
id = request['id']
|
128
|
+
|
129
|
+
return handle_notification(method) if notification?(method)
|
130
|
+
|
131
|
+
result = dispatch_method(method, params)
|
132
|
+
build_success_response(result, id)
|
133
|
+
rescue StandardError => e
|
134
|
+
build_error_response(-32_603, e.message, id)
|
135
|
+
end
|
136
|
+
|
137
|
+
def notification?(method)
|
138
|
+
method == 'notifications/initialized'
|
139
|
+
end
|
140
|
+
|
141
|
+
def handle_notification(method)
|
142
|
+
return unless method == 'notifications/initialized'
|
143
|
+
|
144
|
+
log('Client initialized notification received')
|
145
|
+
warn '[MCP::DateTime] Client initialized notification received'
|
146
|
+
nil
|
147
|
+
end
|
148
|
+
|
149
|
+
def dispatch_method(method, params)
|
150
|
+
case method
|
151
|
+
when 'initialize'
|
152
|
+
handle_initialize(params)
|
153
|
+
when 'tools/list'
|
154
|
+
handle_list_tools
|
155
|
+
when 'tools/call'
|
156
|
+
handle_call_tool(params)
|
157
|
+
else
|
158
|
+
raise "Unknown method: #{method}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def build_success_response(result, id)
|
163
|
+
{
|
164
|
+
jsonrpc: '2.0',
|
165
|
+
result:,
|
166
|
+
id:
|
167
|
+
}
|
168
|
+
end
|
169
|
+
|
170
|
+
def build_error_response(code, message, id = nil)
|
171
|
+
{
|
172
|
+
jsonrpc: '2.0',
|
173
|
+
error: {
|
174
|
+
code:,
|
175
|
+
message:
|
176
|
+
},
|
177
|
+
id:
|
178
|
+
}
|
179
|
+
end
|
180
|
+
|
181
|
+
def handle_initialize(_params)
|
182
|
+
{
|
183
|
+
protocolVersion: '2024-11-05',
|
184
|
+
capabilities: {
|
185
|
+
tools: {
|
186
|
+
listChanged: false
|
187
|
+
}
|
188
|
+
},
|
189
|
+
serverInfo: @server_info
|
190
|
+
}
|
191
|
+
end
|
192
|
+
|
193
|
+
def handle_list_tools
|
194
|
+
{
|
195
|
+
tools: [
|
196
|
+
datetime_tool_definition,
|
197
|
+
date_info_tool_definition
|
198
|
+
]
|
199
|
+
}
|
200
|
+
end
|
201
|
+
|
202
|
+
def handle_call_tool(params)
|
203
|
+
tool_name = params['name']
|
204
|
+
arguments = params['arguments'] || {}
|
205
|
+
|
206
|
+
case tool_name
|
207
|
+
when 'get_current_datetime'
|
208
|
+
current_datetime(arguments)
|
209
|
+
when 'get_date_info'
|
210
|
+
date_info
|
211
|
+
else
|
212
|
+
raise "Unknown tool: #{tool_name}"
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def current_datetime(arguments)
|
217
|
+
format_type = arguments['format'] || 'iso'
|
218
|
+
timezone = arguments['timezone']
|
219
|
+
|
220
|
+
now = get_time_in_timezone(timezone)
|
221
|
+
formatted = format_datetime(now, format_type)
|
222
|
+
|
223
|
+
build_content_response(datetime_data(now, formatted))
|
224
|
+
end
|
225
|
+
|
226
|
+
def get_time_in_timezone(timezone)
|
227
|
+
ENV['TZ'] = timezone if timezone
|
228
|
+
Time.now
|
229
|
+
end
|
230
|
+
|
231
|
+
def format_datetime(time, format_type)
|
232
|
+
case format_type
|
233
|
+
when 'human' then time.strftime('%B %d, %Y at %I:%M %p')
|
234
|
+
when 'date_only' then time.strftime('%Y-%m-%d')
|
235
|
+
when 'time_only' then time.strftime('%H:%M:%S')
|
236
|
+
when 'unix' then time.to_i.to_s
|
237
|
+
else time.iso8601
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def datetime_data(time, formatted)
|
242
|
+
{
|
243
|
+
datetime: formatted,
|
244
|
+
timestamp: time.to_f,
|
245
|
+
year: time.year,
|
246
|
+
month: time.month,
|
247
|
+
day: time.day,
|
248
|
+
hour: time.hour,
|
249
|
+
minute: time.min,
|
250
|
+
second: time.sec,
|
251
|
+
weekday: time.strftime('%A'),
|
252
|
+
timezone: time.zone
|
253
|
+
}
|
254
|
+
end
|
255
|
+
|
256
|
+
def date_info
|
257
|
+
now = Time.now
|
258
|
+
today = Date.today
|
259
|
+
|
260
|
+
build_content_response(date_info_data(today, now))
|
261
|
+
end
|
262
|
+
|
263
|
+
def date_info_data(date, time)
|
264
|
+
{
|
265
|
+
date: date.to_s,
|
266
|
+
year: date.year,
|
267
|
+
month: date.month,
|
268
|
+
month_name: date.strftime('%B'),
|
269
|
+
day: date.day,
|
270
|
+
weekday: date.strftime('%A'),
|
271
|
+
weekday_number: date.wday,
|
272
|
+
day_of_year: date.yday,
|
273
|
+
week_of_year: date.cweek,
|
274
|
+
quarter: ((date.month - 1) / 3) + 1,
|
275
|
+
is_weekend: [0, 6].include?(date.wday),
|
276
|
+
is_leap_year: date.leap?,
|
277
|
+
days_in_month: Date.new(date.year, date.month, -1).day,
|
278
|
+
timezone: time.zone
|
279
|
+
}
|
280
|
+
end
|
281
|
+
|
282
|
+
def build_content_response(data)
|
283
|
+
{
|
284
|
+
content: [
|
285
|
+
{
|
286
|
+
type: 'text',
|
287
|
+
text: JSON.pretty_generate(data)
|
288
|
+
}
|
289
|
+
]
|
290
|
+
}
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module MCP
|
5
|
+
module DateTime
|
6
|
+
# Tool definitions for the MCP DateTime server
|
7
|
+
module Tools
|
8
|
+
def datetime_tool_definition
|
9
|
+
{
|
10
|
+
name: 'get_current_datetime',
|
11
|
+
description: 'Get the current date and time',
|
12
|
+
inputSchema: {
|
13
|
+
type: 'object',
|
14
|
+
properties: {
|
15
|
+
format: {
|
16
|
+
type: 'string',
|
17
|
+
description: 'Optional datetime format',
|
18
|
+
enum: %w[iso human date_only time_only unix]
|
19
|
+
},
|
20
|
+
timezone: {
|
21
|
+
type: 'string',
|
22
|
+
description: 'Optional timezone (e.g., "America/New_York", "Europe/London")'
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def date_info_tool_definition
|
30
|
+
{
|
31
|
+
name: 'get_date_info',
|
32
|
+
description: 'Get detailed information about the current date',
|
33
|
+
inputSchema: {
|
34
|
+
type: 'object',
|
35
|
+
properties: {}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/mcp/datetime.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
require 'time'
|
6
|
+
require 'date'
|
7
|
+
require_relative 'datetime/version'
|
8
|
+
require_relative 'datetime/server'
|
9
|
+
|
10
|
+
module MCP
|
11
|
+
module DateTime
|
12
|
+
class Error < StandardError; end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
# This plugin creates an executable file after gem installation
|
7
|
+
Gem.post_install do |installer|
|
8
|
+
next unless installer.spec.name == 'mcp-datetime-ruby'
|
9
|
+
|
10
|
+
# Get the gem's installation directory
|
11
|
+
gem_dir = installer.spec.gem_dir
|
12
|
+
|
13
|
+
# Create the executable content with the proper paths
|
14
|
+
executable_content = <<~RUBY
|
15
|
+
#!#{Gem.ruby}
|
16
|
+
# frozen_string_literal: true
|
17
|
+
|
18
|
+
# Add the gem's lib directory to the load path
|
19
|
+
$LOAD_PATH.unshift("#{gem_dir}/lib")
|
20
|
+
|
21
|
+
require 'json'
|
22
|
+
require 'date'
|
23
|
+
require 'time'
|
24
|
+
require 'mcp/datetime/version'
|
25
|
+
require 'mcp/datetime/server'
|
26
|
+
|
27
|
+
# Run the server
|
28
|
+
MCP::DateTime::Server.new.run
|
29
|
+
RUBY
|
30
|
+
|
31
|
+
# Determine the bin directory in user's home
|
32
|
+
home_bin_dir = File.expand_path('~/bin')
|
33
|
+
executable_path = File.join(home_bin_dir, 'mcp-datetime-ruby')
|
34
|
+
|
35
|
+
# Create the bin directory if it doesn't exist
|
36
|
+
FileUtils.mkdir_p(home_bin_dir) unless File.directory?(home_bin_dir)
|
37
|
+
|
38
|
+
# Check if executable already exists
|
39
|
+
if File.exist?(executable_path)
|
40
|
+
puts "Updating existing executable: #{executable_path}"
|
41
|
+
else
|
42
|
+
puts "Creating new executable: #{executable_path}"
|
43
|
+
end
|
44
|
+
|
45
|
+
# Write the executable file
|
46
|
+
File.write(executable_path, executable_content)
|
47
|
+
|
48
|
+
# Make it executable
|
49
|
+
File.chmod(0o755, executable_path)
|
50
|
+
|
51
|
+
puts "Executable ready at: #{executable_path}"
|
52
|
+
puts 'Make sure ~/bin is in your PATH to use the executable' unless ENV['PATH'].include?(home_bin_dir)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Clean up the executable on uninstall
|
56
|
+
Gem.pre_uninstall do |uninstaller|
|
57
|
+
next unless uninstaller.spec.name == 'mcp-datetime-ruby'
|
58
|
+
|
59
|
+
executable_path = File.expand_path('~/bin/mcp-datetime-ruby')
|
60
|
+
|
61
|
+
if File.exist?(executable_path)
|
62
|
+
File.delete(executable_path)
|
63
|
+
puts "Removed executable: #{executable_path}"
|
64
|
+
end
|
65
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mcp-datetime-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Wolfgang Teuber
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-06-10 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: json
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '2.0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '2.0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: bundler
|
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.0'
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '5.0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: rake
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '13.0'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '13.0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: rubocop
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '1.0'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '1.0'
|
82
|
+
description: A Ruby implementation of an MCP server that provides datetime tools for
|
83
|
+
AI assistants
|
84
|
+
email:
|
85
|
+
- knugie@gmx.net
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- CHANGELOG.md
|
91
|
+
- LICENSE.txt
|
92
|
+
- README.md
|
93
|
+
- bin/mcp-datetime-ruby
|
94
|
+
- lib/mcp/datetime.rb
|
95
|
+
- lib/mcp/datetime/server.rb
|
96
|
+
- lib/mcp/datetime/tools.rb
|
97
|
+
- lib/mcp/datetime/version.rb
|
98
|
+
- lib/rubygems_plugin.rb
|
99
|
+
homepage: https://github.com/wteuber/mcp-datetime-ruby
|
100
|
+
licenses:
|
101
|
+
- MIT
|
102
|
+
metadata:
|
103
|
+
homepage_uri: https://github.com/wteuber/mcp-datetime-ruby
|
104
|
+
source_code_uri: https://github.com/wteuber/mcp-datetime-ruby
|
105
|
+
changelog_uri: https://github.com/wteuber/mcp-datetime-ruby/blob/main/CHANGELOG.md
|
106
|
+
rubygems_mfa_required: 'true'
|
107
|
+
rdoc_options: []
|
108
|
+
require_paths:
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: 3.1.0
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
requirements: []
|
121
|
+
rubygems_version: 3.6.2
|
122
|
+
specification_version: 4
|
123
|
+
summary: MCP (Model Context Protocol) DateTime Server for Ruby
|
124
|
+
test_files: []
|