things-mcp 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/Gemfile +11 -0
- data/LICENSE +21 -0
- data/README.md +221 -0
- data/Rakefile +6 -0
- data/bin/test_connection +118 -0
- data/bin/things_mcp_server +8 -0
- data/lib/things_mcp/database.rb +500 -0
- data/lib/things_mcp/formatters.rb +152 -0
- data/lib/things_mcp/handlers.rb +257 -0
- data/lib/things_mcp/server.rb +120 -0
- data/lib/things_mcp/tools.rb +463 -0
- data/lib/things_mcp/url_scheme.rb +156 -0
- data/lib/things_mcp.rb +24 -0
- metadata +97 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3b5dce71de1e5a5690e3acbf04a39844482fd77a225255e6c0f6ed6174c14746
|
4
|
+
data.tar.gz: 2ae46521ad48efabb5ea24351f9bae9fc36df2ea1017e0f5a3ff4ef1b7ca6362
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cb4149f6a999b9928d25d889a8b0d48f998d76c07573588a6b1dfff94a86ccfa2a4ee75156093c9c22483d7822852cbb2559de0418b58217e229cfb8d2f21cf5
|
7
|
+
data.tar.gz: d919bf9e96b98c7b8e54d69067f8a7aec30d4e53a0f62398fb30c54eadaedce0c18a95c1e12b2878444600356496f4c45a9332a143b8542feb876f81f05be23d
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2024 Things MCP Ruby Contributors
|
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 all
|
13
|
+
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 THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
# Things 3 MCP Server (Ruby)
|
2
|
+
|
3
|
+
A Model Context Protocol (MCP) server for Things 3, implemented in Ruby.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- Access Things 3 todos, projects, areas, and tags
|
8
|
+
- Create and update todos and projects
|
9
|
+
- Search todos with advanced filters
|
10
|
+
- Access built-in lists (Inbox, Today, Upcoming, etc.)
|
11
|
+
- Complete Things URL scheme support
|
12
|
+
|
13
|
+
## Prerequisites
|
14
|
+
|
15
|
+
1. **Things 3** must be installed on your Mac
|
16
|
+
2. **Ruby 3.2+** installed (required by the MCP gem dependency)
|
17
|
+
3. **No authentication required** - the server reads from Things' local database
|
18
|
+
|
19
|
+
> **Note for macOS users**: The system Ruby (typically 2.6.x) is too old. Install a modern Ruby version:
|
20
|
+
> ```bash
|
21
|
+
> # Using Homebrew (recommended)
|
22
|
+
> brew install ruby
|
23
|
+
>
|
24
|
+
> # Or using rbenv
|
25
|
+
> brew install rbenv
|
26
|
+
> rbenv install 3.4.1
|
27
|
+
> rbenv global 3.4.1
|
28
|
+
> ```
|
29
|
+
|
30
|
+
## Installation
|
31
|
+
|
32
|
+
1. Clone this repository:
|
33
|
+
|
34
|
+
```bash
|
35
|
+
git clone https://github.com/hakanensari/things-mcp-ruby.git
|
36
|
+
cd things-mcp-ruby
|
37
|
+
```
|
38
|
+
|
39
|
+
2. Install dependencies:
|
40
|
+
|
41
|
+
```bash
|
42
|
+
bundle install
|
43
|
+
```
|
44
|
+
|
45
|
+
## Testing the Installation
|
46
|
+
|
47
|
+
1. **Basic test** (database + create operations):
|
48
|
+
|
49
|
+
```bash
|
50
|
+
bin/test_connection
|
51
|
+
```
|
52
|
+
|
53
|
+
2. **Full test** (including update operations):
|
54
|
+
|
55
|
+
```bash
|
56
|
+
THINGS_AUTH_TOKEN=your_token_here bin/test_connection
|
57
|
+
```
|
58
|
+
|
59
|
+
3. **Via Rake:**
|
60
|
+
|
61
|
+
```bash
|
62
|
+
bundle exec rake test
|
63
|
+
```
|
64
|
+
|
65
|
+
The test script will:
|
66
|
+
|
67
|
+
- ✓ Check if Things app is running
|
68
|
+
- ✓ Test database connectivity
|
69
|
+
- ✓ Test handler functionality
|
70
|
+
- ✓ Create a test todo via URL scheme
|
71
|
+
- ✓ Verify the todo in the database
|
72
|
+
- ✓ Test update operations by completing the test todo (if auth token provided)
|
73
|
+
|
74
|
+
2. Run the MCP server:
|
75
|
+
|
76
|
+
```bash
|
77
|
+
bundle exec ruby bin/things_mcp_server
|
78
|
+
```
|
79
|
+
|
80
|
+
## Configuration
|
81
|
+
|
82
|
+
This MCP server implements the [Model Context Protocol](https://modelcontextprotocol.io/) and can be used with any MCP-compatible AI system.
|
83
|
+
|
84
|
+
### Claude Desktop
|
85
|
+
|
86
|
+
### Basic Configuration (Read + Create Operations)
|
87
|
+
|
88
|
+
Add to your Claude Desktop configuration file:
|
89
|
+
|
90
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
91
|
+
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
92
|
+
|
93
|
+
```json
|
94
|
+
{
|
95
|
+
"mcpServers": {
|
96
|
+
"things": {
|
97
|
+
"command": "ruby",
|
98
|
+
"args": ["bin/things_mcp_server"],
|
99
|
+
"cwd": "/path/to/things-mcp-ruby"
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}
|
103
|
+
```
|
104
|
+
|
105
|
+
### Full Configuration (Including Update Operations)
|
106
|
+
|
107
|
+
For `update-todo` and `update-project` operations, you need to provide a Things authorization token:
|
108
|
+
|
109
|
+
1. **Get your authorization token from Things:**
|
110
|
+
|
111
|
+
- Open Things 3
|
112
|
+
- Go to **Things → Settings → General**
|
113
|
+
- Click **Enable Things URLs**
|
114
|
+
- Click **Manage**
|
115
|
+
- Copy the authorization token
|
116
|
+
|
117
|
+
2. **Add the token to your configuration:**
|
118
|
+
|
119
|
+
```json
|
120
|
+
{
|
121
|
+
"mcpServers": {
|
122
|
+
"things": {
|
123
|
+
"command": "ruby",
|
124
|
+
"args": ["bin/things_mcp_server"],
|
125
|
+
"cwd": "/path/to/things-mcp-ruby",
|
126
|
+
"env": {
|
127
|
+
"THINGS_AUTH_TOKEN": "your_authorization_token_here"
|
128
|
+
}
|
129
|
+
}
|
130
|
+
}
|
131
|
+
}
|
132
|
+
```
|
133
|
+
|
134
|
+
⚠️ **Security Note:** Keep your authorization token private. It allows full access to modify your Things data.
|
135
|
+
|
136
|
+
### Other MCP Clients
|
137
|
+
|
138
|
+
For other MCP-compatible clients, run the server manually and connect via stdio:
|
139
|
+
|
140
|
+
```bash
|
141
|
+
# Start the MCP server
|
142
|
+
bundle exec ruby bin/things_mcp_server
|
143
|
+
|
144
|
+
# The server communicates via JSON-RPC over stdio
|
145
|
+
# See the MCP specification for integration details:
|
146
|
+
# https://modelcontextprotocol.io/specification/
|
147
|
+
```
|
148
|
+
|
149
|
+
## Usage
|
150
|
+
|
151
|
+
Once configured, you can use your MCP-compatible AI client to:
|
152
|
+
|
153
|
+
- "Show me my todos from today"
|
154
|
+
- "Create a new todo called 'Buy groceries' for tomorrow"
|
155
|
+
- "Search for todos containing 'meeting'"
|
156
|
+
- "Show all my projects"
|
157
|
+
|
158
|
+
## Available Tools
|
159
|
+
|
160
|
+
### Basic Operations
|
161
|
+
|
162
|
+
- `get-todos` - Get todos from Things, optionally filtered by project
|
163
|
+
- `get-projects` - Get all projects from Things
|
164
|
+
- `get-areas` - Get all areas from Things
|
165
|
+
|
166
|
+
### List Views
|
167
|
+
|
168
|
+
- `get-inbox` - Get todos from Inbox
|
169
|
+
- `get-today` - Get todos due today
|
170
|
+
- `get-upcoming` - Get upcoming todos
|
171
|
+
- `get-anytime` - Get todos from Anytime list
|
172
|
+
- `get-someday` - Get todos from Someday list
|
173
|
+
- `get-logbook` - Get completed todos from Logbook
|
174
|
+
- `get-trash` - Get trashed todos
|
175
|
+
|
176
|
+
### Tag Operations
|
177
|
+
|
178
|
+
- `get-tags` - Get all tags
|
179
|
+
- `get-tagged-items` - Get items with a specific tag
|
180
|
+
|
181
|
+
### Search Operations
|
182
|
+
|
183
|
+
- `search-todos` - Search todos by title or notes
|
184
|
+
- `search-advanced` - Advanced todo search with multiple filters
|
185
|
+
|
186
|
+
### Recent Items
|
187
|
+
|
188
|
+
- `get-recent` - Get recently created items
|
189
|
+
|
190
|
+
### Modification Operations
|
191
|
+
|
192
|
+
- `add-todo` - Create a new todo in Things
|
193
|
+
- `add-project` - Create a new project in Things
|
194
|
+
- `update-todo` - Update an existing todo ⚠️ _Requires auth token_
|
195
|
+
- `update-project` - Update an existing project ⚠️ _Requires auth token_
|
196
|
+
- `show-item` - Show a specific item or list in Things
|
197
|
+
- `search-items` - Search for items and open in Things
|
198
|
+
|
199
|
+
## Development
|
200
|
+
|
201
|
+
Run tests:
|
202
|
+
|
203
|
+
```bash
|
204
|
+
bundle exec rake test
|
205
|
+
```
|
206
|
+
|
207
|
+
Run linter:
|
208
|
+
|
209
|
+
```bash
|
210
|
+
bundle exec rake rubocop
|
211
|
+
```
|
212
|
+
|
213
|
+
Run both tests and linter:
|
214
|
+
|
215
|
+
```bash
|
216
|
+
bundle exec rake
|
217
|
+
```
|
218
|
+
|
219
|
+
## License
|
220
|
+
|
221
|
+
MIT
|
data/Rakefile
ADDED
data/bin/test_connection
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require_relative "../lib/things_mcp"
|
6
|
+
|
7
|
+
puts "Testing Things MCP Ruby Server"
|
8
|
+
puts "=" * 40
|
9
|
+
|
10
|
+
# Test 1: Things app availability
|
11
|
+
puts "\n1. Testing Things app availability..."
|
12
|
+
begin
|
13
|
+
if ThingsMcp::Database.things_app_available?
|
14
|
+
puts "✅ Things app is running"
|
15
|
+
else
|
16
|
+
puts "❌ Things app is not running"
|
17
|
+
puts " Please start Things 3 and try again"
|
18
|
+
@test_failed = true
|
19
|
+
end
|
20
|
+
rescue => e
|
21
|
+
puts "❌ Error checking Things app: #{e.message}"
|
22
|
+
@test_failed = true
|
23
|
+
end
|
24
|
+
|
25
|
+
# Test 2: Database access
|
26
|
+
puts "\n2. Testing database access..."
|
27
|
+
begin
|
28
|
+
todos = ThingsMcp::Database.get_inbox
|
29
|
+
puts "✅ Database accessible"
|
30
|
+
puts " Found #{todos.size} todos in Inbox"
|
31
|
+
rescue => e
|
32
|
+
puts "❌ Database error: #{e.message}"
|
33
|
+
puts " #{e.backtrace.first}"
|
34
|
+
@test_failed = true
|
35
|
+
end
|
36
|
+
|
37
|
+
# Test 3: Handler functionality
|
38
|
+
puts "\n3. Testing handler functionality..."
|
39
|
+
begin
|
40
|
+
result = ThingsMcp::Handlers.handle_tool_call("get-inbox", {})
|
41
|
+
puts "✅ Handler working"
|
42
|
+
puts " Result preview: #{result[0..100]}..."
|
43
|
+
rescue => e
|
44
|
+
puts "❌ Handler error: #{e.message}"
|
45
|
+
puts " #{e.backtrace.first}"
|
46
|
+
@test_failed = true
|
47
|
+
end
|
48
|
+
|
49
|
+
# Test 4: URL Scheme - Create todo
|
50
|
+
puts "\n4. Testing URL scheme (create todo)..."
|
51
|
+
test_todo_title = "Test MCP server #{Time.now.strftime("%H:%M:%S")}"
|
52
|
+
begin
|
53
|
+
result = ThingsMcp::UrlScheme.add_todo({
|
54
|
+
"title" => test_todo_title,
|
55
|
+
"notes" => "This is a test todo created by the MCP server test script",
|
56
|
+
})
|
57
|
+
|
58
|
+
if result[:success]
|
59
|
+
puts "✅ URL scheme working - test todo created"
|
60
|
+
puts " Todo: '#{test_todo_title}'"
|
61
|
+
|
62
|
+
# Test update operations if auth token is available
|
63
|
+
if ENV["THINGS_AUTH_TOKEN"]
|
64
|
+
puts "\n5. Testing URL scheme (update/cleanup)..."
|
65
|
+
puts " Using auth token for update operation..."
|
66
|
+
|
67
|
+
# Brief retry to find the created todo (database sync is not immediate)
|
68
|
+
puts " Searching for created todo in database..."
|
69
|
+
test_todo = nil
|
70
|
+
3.times do |i|
|
71
|
+
inbox_todos = ThingsMcp::Database.get_inbox
|
72
|
+
test_todo = inbox_todos.find { |t| t[:title] == test_todo_title }
|
73
|
+
break if test_todo
|
74
|
+
sleep(1) if i < 2 # Don't sleep after the last attempt
|
75
|
+
end
|
76
|
+
|
77
|
+
if test_todo
|
78
|
+
puts "✅ Todo found in database"
|
79
|
+
complete_result = ThingsMcp::UrlScheme.update_todo({
|
80
|
+
"id" => test_todo[:uuid],
|
81
|
+
"completed" => true,
|
82
|
+
})
|
83
|
+
|
84
|
+
if complete_result[:success]
|
85
|
+
puts "✅ Update operations working - test todo completed and cleaned up"
|
86
|
+
else
|
87
|
+
puts "❌ Could not complete test todo: #{complete_result[:error]}"
|
88
|
+
puts " You may need to manually complete: '#{test_todo_title}'"
|
89
|
+
@test_failed = true
|
90
|
+
end
|
91
|
+
else
|
92
|
+
puts "❌ Could not find test todo in database after 3 attempts"
|
93
|
+
puts " Database sync delay longer than expected - please manually complete: '#{test_todo_title}'"
|
94
|
+
@test_failed = true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
else
|
98
|
+
puts "❌ URL scheme failed: #{result[:error]}"
|
99
|
+
@test_failed = true
|
100
|
+
end
|
101
|
+
rescue => e
|
102
|
+
puts "❌ URL scheme error: #{e.message}"
|
103
|
+
puts " #{e.backtrace.first}"
|
104
|
+
@test_failed = true
|
105
|
+
end
|
106
|
+
|
107
|
+
puts "\n" + "=" * 40
|
108
|
+
puts "Test completed!"
|
109
|
+
|
110
|
+
if ENV["THINGS_AUTH_TOKEN"]
|
111
|
+
puts "✅ Full testing (with auth token)"
|
112
|
+
else
|
113
|
+
puts "⚠️ Partial testing (no auth token for updates)"
|
114
|
+
puts " Set THINGS_AUTH_TOKEN=your_token to test update operations"
|
115
|
+
end
|
116
|
+
|
117
|
+
# Exit with error if there were any failures
|
118
|
+
exit 1 if defined?(@test_failed)
|