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
@@ -0,0 +1,463 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ThingsMcp
|
4
|
+
# MCP tool definitions for Things 3 integration
|
5
|
+
#
|
6
|
+
# This module defines all available MCP tools with their schemas, descriptions, and parameter definitions for
|
7
|
+
# interaction with Things 3.
|
8
|
+
module Tools
|
9
|
+
class << self
|
10
|
+
# Returns all available MCP tool definitions
|
11
|
+
#
|
12
|
+
# @return [Array<Hash>] Array of tool definition hashes
|
13
|
+
def all
|
14
|
+
[
|
15
|
+
# Basic operations
|
16
|
+
{
|
17
|
+
name: "get-todos",
|
18
|
+
description: "Get todos from Things, optionally filtered by project",
|
19
|
+
inputSchema: {
|
20
|
+
type: "object",
|
21
|
+
properties: {
|
22
|
+
project_uuid: {
|
23
|
+
type: "string",
|
24
|
+
description: "Optional UUID of a specific project to get todos from",
|
25
|
+
},
|
26
|
+
include_items: {
|
27
|
+
type: "boolean",
|
28
|
+
description: "Include checklist items",
|
29
|
+
default: true,
|
30
|
+
},
|
31
|
+
},
|
32
|
+
required: [],
|
33
|
+
},
|
34
|
+
},
|
35
|
+
{
|
36
|
+
name: "get-projects",
|
37
|
+
description: "Get all projects from Things",
|
38
|
+
inputSchema: {
|
39
|
+
type: "object",
|
40
|
+
properties: {
|
41
|
+
include_items: {
|
42
|
+
type: "boolean",
|
43
|
+
description: "Include tasks within projects",
|
44
|
+
default: false,
|
45
|
+
},
|
46
|
+
},
|
47
|
+
required: [],
|
48
|
+
},
|
49
|
+
},
|
50
|
+
{
|
51
|
+
name: "get-areas",
|
52
|
+
description: "Get all areas from Things",
|
53
|
+
inputSchema: {
|
54
|
+
type: "object",
|
55
|
+
properties: {
|
56
|
+
include_items: {
|
57
|
+
type: "boolean",
|
58
|
+
description: "Include projects and tasks within areas",
|
59
|
+
default: false,
|
60
|
+
},
|
61
|
+
},
|
62
|
+
required: [],
|
63
|
+
},
|
64
|
+
},
|
65
|
+
|
66
|
+
# List views
|
67
|
+
{
|
68
|
+
name: "get-inbox",
|
69
|
+
description: "Get todos from Inbox",
|
70
|
+
inputSchema: {
|
71
|
+
type: "object",
|
72
|
+
properties: {},
|
73
|
+
required: [],
|
74
|
+
},
|
75
|
+
},
|
76
|
+
{
|
77
|
+
name: "get-today",
|
78
|
+
description: "Get todos due today",
|
79
|
+
inputSchema: {
|
80
|
+
type: "object",
|
81
|
+
properties: {},
|
82
|
+
required: [],
|
83
|
+
},
|
84
|
+
},
|
85
|
+
{
|
86
|
+
name: "get-upcoming",
|
87
|
+
description: "Get upcoming todos",
|
88
|
+
inputSchema: {
|
89
|
+
type: "object",
|
90
|
+
properties: {},
|
91
|
+
required: [],
|
92
|
+
},
|
93
|
+
},
|
94
|
+
{
|
95
|
+
name: "get-anytime",
|
96
|
+
description: "Get todos from Anytime list",
|
97
|
+
inputSchema: {
|
98
|
+
type: "object",
|
99
|
+
properties: {},
|
100
|
+
required: [],
|
101
|
+
},
|
102
|
+
},
|
103
|
+
{
|
104
|
+
name: "get-someday",
|
105
|
+
description: "Get todos from Someday list",
|
106
|
+
inputSchema: {
|
107
|
+
type: "object",
|
108
|
+
properties: {},
|
109
|
+
required: [],
|
110
|
+
},
|
111
|
+
},
|
112
|
+
{
|
113
|
+
name: "get-logbook",
|
114
|
+
description: "Get completed todos from Logbook, defaults to last 7 days",
|
115
|
+
inputSchema: {
|
116
|
+
type: "object",
|
117
|
+
properties: {
|
118
|
+
period: {
|
119
|
+
type: "string",
|
120
|
+
description: "Time period to look back (e.g., '3d', '1w', '2m', '1y'). Defaults to '7d'",
|
121
|
+
pattern: '^\\d+[dwmy]$',
|
122
|
+
},
|
123
|
+
limit: {
|
124
|
+
type: "integer",
|
125
|
+
description: "Maximum number of entries to return. Defaults to 50",
|
126
|
+
minimum: 1,
|
127
|
+
maximum: 100,
|
128
|
+
},
|
129
|
+
},
|
130
|
+
required: [],
|
131
|
+
},
|
132
|
+
},
|
133
|
+
{
|
134
|
+
name: "get-trash",
|
135
|
+
description: "Get trashed todos",
|
136
|
+
inputSchema: {
|
137
|
+
type: "object",
|
138
|
+
properties: {},
|
139
|
+
required: [],
|
140
|
+
},
|
141
|
+
},
|
142
|
+
|
143
|
+
# Tag operations
|
144
|
+
{
|
145
|
+
name: "get-tags",
|
146
|
+
description: "Get all tags",
|
147
|
+
inputSchema: {
|
148
|
+
type: "object",
|
149
|
+
properties: {
|
150
|
+
include_items: {
|
151
|
+
type: "boolean",
|
152
|
+
description: "Include items tagged with each tag",
|
153
|
+
default: false,
|
154
|
+
},
|
155
|
+
},
|
156
|
+
required: [],
|
157
|
+
},
|
158
|
+
},
|
159
|
+
{
|
160
|
+
name: "get-tagged-items",
|
161
|
+
description: "Get items with a specific tag",
|
162
|
+
inputSchema: {
|
163
|
+
type: "object",
|
164
|
+
properties: {
|
165
|
+
tag: {
|
166
|
+
type: "string",
|
167
|
+
description: "Tag title to filter by",
|
168
|
+
},
|
169
|
+
},
|
170
|
+
required: ["tag"],
|
171
|
+
},
|
172
|
+
},
|
173
|
+
|
174
|
+
# Search operations
|
175
|
+
{
|
176
|
+
name: "search-todos",
|
177
|
+
description: "Search todos by title or notes",
|
178
|
+
inputSchema: {
|
179
|
+
type: "object",
|
180
|
+
properties: {
|
181
|
+
query: {
|
182
|
+
type: "string",
|
183
|
+
description: "Search term to look for in todo titles and notes",
|
184
|
+
},
|
185
|
+
},
|
186
|
+
required: ["query"],
|
187
|
+
},
|
188
|
+
},
|
189
|
+
{
|
190
|
+
name: "search-advanced",
|
191
|
+
description: "Advanced todo search with multiple filters",
|
192
|
+
inputSchema: {
|
193
|
+
type: "object",
|
194
|
+
properties: {
|
195
|
+
status: {
|
196
|
+
type: "string",
|
197
|
+
enum: ["incomplete", "completed", "canceled"],
|
198
|
+
description: "Filter by todo status",
|
199
|
+
},
|
200
|
+
start_date: {
|
201
|
+
type: "string",
|
202
|
+
description: "Filter by start date (YYYY-MM-DD)",
|
203
|
+
},
|
204
|
+
deadline: {
|
205
|
+
type: "string",
|
206
|
+
description: "Filter by deadline (YYYY-MM-DD)",
|
207
|
+
},
|
208
|
+
tag: {
|
209
|
+
type: "string",
|
210
|
+
description: "Filter by tag",
|
211
|
+
},
|
212
|
+
area: {
|
213
|
+
type: "string",
|
214
|
+
description: "Filter by area UUID",
|
215
|
+
},
|
216
|
+
type: {
|
217
|
+
type: "string",
|
218
|
+
enum: ["to-do", "project", "heading"],
|
219
|
+
description: "Filter by item type",
|
220
|
+
},
|
221
|
+
},
|
222
|
+
required: [],
|
223
|
+
},
|
224
|
+
},
|
225
|
+
|
226
|
+
# Recent items
|
227
|
+
{
|
228
|
+
name: "get-recent",
|
229
|
+
description: "Get recently created items",
|
230
|
+
inputSchema: {
|
231
|
+
type: "object",
|
232
|
+
properties: {
|
233
|
+
period: {
|
234
|
+
type: "string",
|
235
|
+
description: "Time period (e.g., '3d', '1w', '2m', '1y')",
|
236
|
+
pattern: '^\\d+[dwmy]$',
|
237
|
+
},
|
238
|
+
},
|
239
|
+
required: ["period"],
|
240
|
+
},
|
241
|
+
},
|
242
|
+
|
243
|
+
# Things URL Scheme tools
|
244
|
+
{
|
245
|
+
name: "add-todo",
|
246
|
+
description: "Create a new todo in Things",
|
247
|
+
inputSchema: {
|
248
|
+
type: "object",
|
249
|
+
properties: {
|
250
|
+
title: {
|
251
|
+
type: "string",
|
252
|
+
description: "Title of the todo",
|
253
|
+
},
|
254
|
+
notes: {
|
255
|
+
type: "string",
|
256
|
+
description: "Notes for the todo",
|
257
|
+
},
|
258
|
+
when: {
|
259
|
+
type: "string",
|
260
|
+
description: "When to schedule the todo (today, tomorrow, evening, anytime, someday, or YYYY-MM-DD)",
|
261
|
+
},
|
262
|
+
deadline: {
|
263
|
+
type: "string",
|
264
|
+
description: "Deadline for the todo (YYYY-MM-DD)",
|
265
|
+
},
|
266
|
+
tags: {
|
267
|
+
type: "array",
|
268
|
+
items: { type: "string" },
|
269
|
+
description: "Tags to apply to the todo",
|
270
|
+
},
|
271
|
+
checklist_items: {
|
272
|
+
type: "array",
|
273
|
+
items: { type: "string" },
|
274
|
+
description: "Checklist items to add",
|
275
|
+
},
|
276
|
+
list_id: {
|
277
|
+
type: "string",
|
278
|
+
description: "ID of project/area to add to",
|
279
|
+
},
|
280
|
+
list_title: {
|
281
|
+
type: "string",
|
282
|
+
description: "Title of project/area to add to",
|
283
|
+
},
|
284
|
+
heading: {
|
285
|
+
type: "string",
|
286
|
+
description: "Heading to add under",
|
287
|
+
},
|
288
|
+
},
|
289
|
+
required: ["title"],
|
290
|
+
},
|
291
|
+
},
|
292
|
+
{
|
293
|
+
name: "add-project",
|
294
|
+
description: "Create a new project in Things",
|
295
|
+
inputSchema: {
|
296
|
+
type: "object",
|
297
|
+
properties: {
|
298
|
+
title: {
|
299
|
+
type: "string",
|
300
|
+
description: "Title of the project",
|
301
|
+
},
|
302
|
+
notes: {
|
303
|
+
type: "string",
|
304
|
+
description: "Notes for the project",
|
305
|
+
},
|
306
|
+
when: {
|
307
|
+
type: "string",
|
308
|
+
description: "When to schedule the project",
|
309
|
+
},
|
310
|
+
deadline: {
|
311
|
+
type: "string",
|
312
|
+
description: "Deadline for the project",
|
313
|
+
},
|
314
|
+
tags: {
|
315
|
+
type: "array",
|
316
|
+
items: { type: "string" },
|
317
|
+
description: "Tags to apply to the project",
|
318
|
+
},
|
319
|
+
area_id: {
|
320
|
+
type: "string",
|
321
|
+
description: "ID of area to add to",
|
322
|
+
},
|
323
|
+
area_title: {
|
324
|
+
type: "string",
|
325
|
+
description: "Title of area to add to",
|
326
|
+
},
|
327
|
+
todos: {
|
328
|
+
type: "array",
|
329
|
+
items: { type: "string" },
|
330
|
+
description: "Initial todos to create in the project",
|
331
|
+
},
|
332
|
+
},
|
333
|
+
required: ["title"],
|
334
|
+
},
|
335
|
+
},
|
336
|
+
{
|
337
|
+
name: "update-todo",
|
338
|
+
description: "Update an existing todo in Things",
|
339
|
+
inputSchema: {
|
340
|
+
type: "object",
|
341
|
+
properties: {
|
342
|
+
id: {
|
343
|
+
type: "string",
|
344
|
+
description: "ID of the todo to update",
|
345
|
+
},
|
346
|
+
title: {
|
347
|
+
type: "string",
|
348
|
+
description: "New title",
|
349
|
+
},
|
350
|
+
notes: {
|
351
|
+
type: "string",
|
352
|
+
description: "New notes",
|
353
|
+
},
|
354
|
+
when: {
|
355
|
+
type: "string",
|
356
|
+
description: "New schedule",
|
357
|
+
},
|
358
|
+
deadline: {
|
359
|
+
type: "string",
|
360
|
+
description: "New deadline",
|
361
|
+
},
|
362
|
+
tags: {
|
363
|
+
type: "array",
|
364
|
+
items: { type: "string" },
|
365
|
+
description: "New tags",
|
366
|
+
},
|
367
|
+
completed: {
|
368
|
+
type: "boolean",
|
369
|
+
description: "Mark as completed",
|
370
|
+
},
|
371
|
+
canceled: {
|
372
|
+
type: "boolean",
|
373
|
+
description: "Mark as canceled",
|
374
|
+
},
|
375
|
+
},
|
376
|
+
required: ["id"],
|
377
|
+
},
|
378
|
+
},
|
379
|
+
{
|
380
|
+
name: "update-project",
|
381
|
+
description: "Update an existing project in Things",
|
382
|
+
inputSchema: {
|
383
|
+
type: "object",
|
384
|
+
properties: {
|
385
|
+
id: {
|
386
|
+
type: "string",
|
387
|
+
description: "ID of the project to update",
|
388
|
+
},
|
389
|
+
title: {
|
390
|
+
type: "string",
|
391
|
+
description: "New title",
|
392
|
+
},
|
393
|
+
notes: {
|
394
|
+
type: "string",
|
395
|
+
description: "New notes",
|
396
|
+
},
|
397
|
+
when: {
|
398
|
+
type: "string",
|
399
|
+
description: "New schedule",
|
400
|
+
},
|
401
|
+
deadline: {
|
402
|
+
type: "string",
|
403
|
+
description: "New deadline",
|
404
|
+
},
|
405
|
+
tags: {
|
406
|
+
type: "array",
|
407
|
+
items: { type: "string" },
|
408
|
+
description: "New tags",
|
409
|
+
},
|
410
|
+
completed: {
|
411
|
+
type: "boolean",
|
412
|
+
description: "Mark as completed",
|
413
|
+
},
|
414
|
+
canceled: {
|
415
|
+
type: "boolean",
|
416
|
+
description: "Mark as canceled",
|
417
|
+
},
|
418
|
+
},
|
419
|
+
required: ["id"],
|
420
|
+
},
|
421
|
+
},
|
422
|
+
{
|
423
|
+
name: "search-items",
|
424
|
+
description: "Search for items in Things",
|
425
|
+
inputSchema: {
|
426
|
+
type: "object",
|
427
|
+
properties: {
|
428
|
+
query: {
|
429
|
+
type: "string",
|
430
|
+
description: "Search query",
|
431
|
+
},
|
432
|
+
},
|
433
|
+
required: ["query"],
|
434
|
+
},
|
435
|
+
},
|
436
|
+
{
|
437
|
+
name: "show-item",
|
438
|
+
description: "Show a specific item or list in Things",
|
439
|
+
inputSchema: {
|
440
|
+
type: "object",
|
441
|
+
properties: {
|
442
|
+
id: {
|
443
|
+
type: "string",
|
444
|
+
description: "ID of item to show, or one of: inbox, today, upcoming, anytime, someday, logbook",
|
445
|
+
},
|
446
|
+
query: {
|
447
|
+
type: "string",
|
448
|
+
description: "Optional query to filter by",
|
449
|
+
},
|
450
|
+
filter_tags: {
|
451
|
+
type: "array",
|
452
|
+
items: { type: "string" },
|
453
|
+
description: "Optional tags to filter by",
|
454
|
+
},
|
455
|
+
},
|
456
|
+
required: ["id"],
|
457
|
+
},
|
458
|
+
},
|
459
|
+
]
|
460
|
+
end
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
require "json"
|
5
|
+
require "open3"
|
6
|
+
|
7
|
+
module ThingsMcp
|
8
|
+
# Things 3 URL scheme integration
|
9
|
+
#
|
10
|
+
# This module provides methods for creating and updating todos and projects using Things 3's URL scheme. It handles
|
11
|
+
# authentication tokens for update operations and manages the Things 3 application lifecycle.
|
12
|
+
module UrlScheme
|
13
|
+
extend self
|
14
|
+
|
15
|
+
THINGS_URL_BASE = "things:///"
|
16
|
+
|
17
|
+
def add_todo(params)
|
18
|
+
url_params = {
|
19
|
+
"title" => params["title"],
|
20
|
+
}
|
21
|
+
|
22
|
+
# Optional parameters
|
23
|
+
url_params["notes"] = params["notes"] if params["notes"]
|
24
|
+
url_params["when"] = params["when"] if params["when"]
|
25
|
+
url_params["deadline"] = params["deadline"] if params["deadline"]
|
26
|
+
url_params["tags"] = params["tags"].join(",") if params["tags"]
|
27
|
+
url_params["checklist-items"] = params["checklist_items"].join("\n") if params["checklist_items"]
|
28
|
+
url_params["list-id"] = params["list_id"] if params["list_id"]
|
29
|
+
url_params["list"] = params["list_title"] if params["list_title"]
|
30
|
+
url_params["heading"] = params["heading"] if params["heading"]
|
31
|
+
|
32
|
+
execute_url("#{THINGS_URL_BASE}add", url_params)
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_project(params)
|
36
|
+
url_params = {
|
37
|
+
"title" => params["title"],
|
38
|
+
"type" => "project",
|
39
|
+
}
|
40
|
+
|
41
|
+
# Optional parameters
|
42
|
+
url_params["notes"] = params["notes"] if params["notes"]
|
43
|
+
url_params["when"] = params["when"] if params["when"]
|
44
|
+
url_params["deadline"] = params["deadline"] if params["deadline"]
|
45
|
+
url_params["tags"] = params["tags"].join(",") if params["tags"]
|
46
|
+
url_params["area-id"] = params["area_id"] if params["area_id"]
|
47
|
+
url_params["area"] = params["area_title"] if params["area_title"]
|
48
|
+
url_params["to-dos"] = params["todos"].join("\n") if params["todos"]
|
49
|
+
|
50
|
+
execute_url("#{THINGS_URL_BASE}add-project", url_params)
|
51
|
+
end
|
52
|
+
|
53
|
+
def update_todo(params)
|
54
|
+
url_params = {
|
55
|
+
"id" => params["id"],
|
56
|
+
}
|
57
|
+
|
58
|
+
# Optional update parameters
|
59
|
+
url_params["title"] = params["title"] if params["title"]
|
60
|
+
url_params["notes"] = params["notes"] if params["notes"]
|
61
|
+
url_params["when"] = params["when"] if params["when"]
|
62
|
+
url_params["deadline"] = params["deadline"] if params["deadline"]
|
63
|
+
# Use add-tags to append tags (might work better for new tags)
|
64
|
+
url_params["add-tags"] = params["tags"].join(",") if params["tags"]
|
65
|
+
url_params["completed"] = "true" if params["completed"]
|
66
|
+
url_params["canceled"] = "true" if params["canceled"]
|
67
|
+
|
68
|
+
execute_url("#{THINGS_URL_BASE}update", url_params)
|
69
|
+
end
|
70
|
+
|
71
|
+
def update_project(params)
|
72
|
+
url_params = {
|
73
|
+
"id" => params["id"],
|
74
|
+
"type" => "project",
|
75
|
+
}
|
76
|
+
|
77
|
+
# Optional update parameters
|
78
|
+
url_params["title"] = params["title"] if params["title"]
|
79
|
+
url_params["notes"] = params["notes"] if params["notes"]
|
80
|
+
url_params["when"] = params["when"] if params["when"]
|
81
|
+
url_params["deadline"] = params["deadline"] if params["deadline"]
|
82
|
+
url_params["tags"] = params["tags"].join(",") if params["tags"]
|
83
|
+
url_params["completed"] = "true" if params["completed"]
|
84
|
+
url_params["canceled"] = "true" if params["canceled"]
|
85
|
+
|
86
|
+
execute_url("#{THINGS_URL_BASE}update-project", url_params)
|
87
|
+
end
|
88
|
+
|
89
|
+
def search_items(query)
|
90
|
+
execute_url("#{THINGS_URL_BASE}search", { "query" => query })
|
91
|
+
end
|
92
|
+
|
93
|
+
def show_item(params)
|
94
|
+
url_params = {
|
95
|
+
"id" => params["id"],
|
96
|
+
}
|
97
|
+
|
98
|
+
url_params["query"] = params["query"] if params["query"]
|
99
|
+
url_params["filter"] = params["filter_tags"].join(",") if params["filter_tags"]
|
100
|
+
|
101
|
+
execute_url("#{THINGS_URL_BASE}show", url_params)
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def execute_url(base_url, params = {})
|
107
|
+
# Add auth token for operations that require it
|
108
|
+
if needs_auth_token?(base_url) && ENV["THINGS_AUTH_TOKEN"]
|
109
|
+
params["auth-token"] = ENV["THINGS_AUTH_TOKEN"]
|
110
|
+
end
|
111
|
+
|
112
|
+
# Build query string and convert + to %20 for spaces
|
113
|
+
query_string = URI.encode_www_form(params)
|
114
|
+
query_string = query_string.gsub("+", "%20") unless query_string.empty?
|
115
|
+
|
116
|
+
full_url = query_string.empty? ? base_url : "#{base_url}?#{query_string}"
|
117
|
+
|
118
|
+
|
119
|
+
# Ensure Things is running
|
120
|
+
unless things_running?
|
121
|
+
launch_things
|
122
|
+
sleep(2)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Open the URL
|
126
|
+
_, stderr, status = Open3.capture3("open", full_url)
|
127
|
+
|
128
|
+
if status.success?
|
129
|
+
{ success: true }
|
130
|
+
else
|
131
|
+
{ success: false, error: stderr }
|
132
|
+
end
|
133
|
+
rescue => e
|
134
|
+
{ success: false, error: e.message }
|
135
|
+
end
|
136
|
+
|
137
|
+
def things_running?
|
138
|
+
system('pgrep -x "Things3" > /dev/null 2>&1')
|
139
|
+
end
|
140
|
+
|
141
|
+
def launch_things
|
142
|
+
system("open -a Things3")
|
143
|
+
end
|
144
|
+
|
145
|
+
def needs_auth_token?(url)
|
146
|
+
# Operations that require authentication token
|
147
|
+
auth_required_operations = [
|
148
|
+
"things:///update",
|
149
|
+
"things:///update-project",
|
150
|
+
]
|
151
|
+
|
152
|
+
auth_required_operations.any? { |op| url.start_with?(op) }
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
data/lib/things_mcp.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mcp"
|
4
|
+
require_relative "things_mcp/server"
|
5
|
+
require_relative "things_mcp/database"
|
6
|
+
require_relative "things_mcp/handlers"
|
7
|
+
require_relative "things_mcp/tools"
|
8
|
+
require_relative "things_mcp/url_scheme"
|
9
|
+
require_relative "things_mcp/formatters"
|
10
|
+
|
11
|
+
# ThingsMcp provides a Model Context Protocol (MCP) server for Things 3
|
12
|
+
#
|
13
|
+
# This module enables integration with Things 3 through both database access and URL scheme operations, providing read
|
14
|
+
# access to todos, projects, areas, and tags, as well as create and update operations.
|
15
|
+
module ThingsMcp
|
16
|
+
# Base error class for ThingsMcp
|
17
|
+
class Error < StandardError; end
|
18
|
+
|
19
|
+
# Error raised when database operations fail
|
20
|
+
class DatabaseError < Error; end
|
21
|
+
|
22
|
+
# Error raised when URL scheme operations fail
|
23
|
+
class UrlSchemeError < Error; end
|
24
|
+
end
|