things-mcp-server 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.
- package/README.md +187 -0
- package/dist/adapters/apple-script-adapter.js +1623 -0
- package/dist/adapters/sqlite-read-adapter.js +496 -0
- package/dist/adapters/url-scheme-adapter.js +95 -0
- package/dist/constants.js +26 -0
- package/dist/errors.js +20 -0
- package/dist/index.js +13 -0
- package/dist/schemas/tool-schemas.js +199 -0
- package/dist/server.js +30 -0
- package/dist/services/things-service.js +324 -0
- package/dist/tools/register-read-tools.js +183 -0
- package/dist/tools/register-write-tools.js +144 -0
- package/dist/types.js +1 -0
- package/dist/utils/command-runner.js +72 -0
- package/dist/utils/redact.js +8 -0
- package/dist/utils/tool-response.js +14 -0
- package/dist/utils/url.js +15 -0
- package/dist/utils/validators.js +62 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# things-mcp-server
|
|
2
|
+
|
|
3
|
+
A local MCP server for controlling [Things](https://culturedcode.com/things/) on macOS.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Read:
|
|
8
|
+
- `things_get_server_status`
|
|
9
|
+
- `things_list_todos`
|
|
10
|
+
- `things_list_projects`
|
|
11
|
+
- `things_list_areas`
|
|
12
|
+
- `things_list_area_items`
|
|
13
|
+
- `things_list_project_todos`
|
|
14
|
+
- `things_search_todos`
|
|
15
|
+
- `things_get_todo`
|
|
16
|
+
- Write:
|
|
17
|
+
- `things_add_area`
|
|
18
|
+
- `things_add_todo`
|
|
19
|
+
- `things_add_project`
|
|
20
|
+
- `things_update_todo`
|
|
21
|
+
- `things_complete_todo`
|
|
22
|
+
- `things_move_todo`
|
|
23
|
+
- `things_delete_todo`
|
|
24
|
+
- UI helper:
|
|
25
|
+
- `things_show_item`
|
|
26
|
+
|
|
27
|
+
## Security Model
|
|
28
|
+
|
|
29
|
+
- Strict command whitelist: only `osascript` and `open` are executable.
|
|
30
|
+
- No shell interpolation: subprocesses run with `shell: false`.
|
|
31
|
+
- Things URL command whitelist: only approved commands (`add`, `add-project`, `update`, `show`, `search`).
|
|
32
|
+
- Input validation for IDs, dates, title/notes/tag sizes.
|
|
33
|
+
- All write/modify operations require `confirm=true`.
|
|
34
|
+
- Sensitive strings (auth token) are redacted from tool error output.
|
|
35
|
+
|
|
36
|
+
## Requirements
|
|
37
|
+
|
|
38
|
+
- macOS with Things installed (`Things3` AppleScript app name)
|
|
39
|
+
- Node.js 20+
|
|
40
|
+
- Things URL Scheme auth token (`THINGS_AUTH_TOKEN`) for URL-scheme write operations
|
|
41
|
+
|
|
42
|
+
Get auth token in Things:
|
|
43
|
+
|
|
44
|
+
1. Open Things.
|
|
45
|
+
2. Go to Settings/Preferences.
|
|
46
|
+
3. Open URL scheme settings and copy the token.
|
|
47
|
+
|
|
48
|
+
## Setup
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pnpm install
|
|
52
|
+
pnpm build
|
|
53
|
+
pnpm test
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Run
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
THINGS_AUTH_TOKEN=your_token_here pnpm start
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Use the same Node runtime for install/build/start and MCP host process. Native dependency (`better-sqlite3`) must match Node ABI.
|
|
63
|
+
|
|
64
|
+
Or in development:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
THINGS_AUTH_TOKEN=your_token_here pnpm dev
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## MCP Config (Local Source Checkout)
|
|
71
|
+
|
|
72
|
+
This repository includes `mcp.config.example.json` for running the built local server:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"mcpServers": {
|
|
77
|
+
"things": {
|
|
78
|
+
"command": "/opt/homebrew/bin/node",
|
|
79
|
+
"args": ["/Users/your-user/Code/things-mcp/dist/index.js"],
|
|
80
|
+
"env": {
|
|
81
|
+
"THINGS_AUTH_TOKEN": "your-token-here"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Notes:
|
|
89
|
+
|
|
90
|
+
- Run `pnpm build` first so `dist/index.js` exists.
|
|
91
|
+
- Update `args[0]` to your actual local absolute path.
|
|
92
|
+
- Keep `command` aligned with the same Node runtime used for install/build to avoid `better-sqlite3` ABI mismatch.
|
|
93
|
+
|
|
94
|
+
## Publish and Install (No Source Checkout)
|
|
95
|
+
|
|
96
|
+
You can publish to npm and run it via `npx`, so client machines do not need this source repo.
|
|
97
|
+
|
|
98
|
+
Publish:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pnpm test
|
|
102
|
+
pnpm publish --access public
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
MCP config example (installed on demand from npm):
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"mcpServers": {
|
|
110
|
+
"things": {
|
|
111
|
+
"command": "npx",
|
|
112
|
+
"args": ["-y", "things-mcp-server"],
|
|
113
|
+
"env": {
|
|
114
|
+
"THINGS_AUTH_TOKEN": "your-token-here"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Optional SQLite read path override:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
THINGS_DB_PATH="/Users/you/Library/Group Containers/JLMPQHK86H.com.culturedcode.ThingsMac/ThingsData-XXXX/Things Database.thingsdatabase/main.sqlite"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
By default, the server auto-discovers Things databases under Group Containers, including backup bundles. In most setups, `THINGS_DB_PATH` is not required.
|
|
128
|
+
|
|
129
|
+
Troubleshooting recurring items with missing date:
|
|
130
|
+
|
|
131
|
+
1. Call `things_get_server_status` and verify `sqlite.enabled=true`.
|
|
132
|
+
2. If false, inspect `resolvedPath`, `existingCandidateCount`, and `lastOpenError` from `things_get_server_status`.
|
|
133
|
+
3. If still false, set `THINGS_DB_PATH` explicitly to your `main.sqlite`.
|
|
134
|
+
4. If `lastOpenError` indicates permissions, grant your MCP host app (for example Alma) Full Disk Access.
|
|
135
|
+
5. Restart the MCP server and reconnect client.
|
|
136
|
+
|
|
137
|
+
If `lastOpenError` includes `NODE_MODULE_VERSION` mismatch:
|
|
138
|
+
|
|
139
|
+
1. Align Node runtime used by MCP host and by local install.
|
|
140
|
+
2. Rebuild native module with that runtime: `pnpm rebuild better-sqlite3`.
|
|
141
|
+
3. In MCP host config, prefer absolute Node path in `command` (for example `/opt/homebrew/bin/node`) instead of plain `node`.
|
|
142
|
+
|
|
143
|
+
## Tool Safety Notes
|
|
144
|
+
|
|
145
|
+
- `things_add_area`, `things_add_todo`, `things_add_project`, `things_update_todo`, `things_complete_todo`, `things_move_todo`, and `things_delete_todo` all require `confirm=true`.
|
|
146
|
+
- This server does not execute arbitrary scripts.
|
|
147
|
+
- Reads are done via AppleScript.
|
|
148
|
+
- URL-scheme-supported operations use Things URL scheme (`add_todo`, `add_project`, `update_todo`, `move_todo`, `complete_todo`, `show_item`).
|
|
149
|
+
- AppleScript is used for unsupported write operations (`add_area`, `delete_todo`) and read operations.
|
|
150
|
+
|
|
151
|
+
## Read Detail Fields
|
|
152
|
+
|
|
153
|
+
`things_get_todo` returns extended metadata, including:
|
|
154
|
+
|
|
155
|
+
- `dueDate`, `activationDate`
|
|
156
|
+
- `creationDate`, `modificationDate`, `completionDate`, `cancellationDate`
|
|
157
|
+
- `reminderTime` (best-effort, extracted from private JSON when available)
|
|
158
|
+
- `tagNames`, `project`, `area`, `contactName`
|
|
159
|
+
- `rawJson` (best-effort from Things private experimental JSON field when available)
|
|
160
|
+
|
|
161
|
+
`things_list_todos`, `things_search_todos`, `things_list_area_items`, and `things_list_project_todos` also return:
|
|
162
|
+
|
|
163
|
+
- `inferredDate`: fallback date inferred from direct fields or recurring metadata
|
|
164
|
+
- `isRepeating` / `recurrenceRule`: best-effort recurring task hints
|
|
165
|
+
- `recurrence`: structured recurring info (`mode`, `nextDate`, `interval`, `frequency`, `byDay`, `byMonthDay`, `byMonth`, `byWeekNo`, `byYearDay`, `bySetPos`, `byHour`, `byMinute`, `bySecond`, `weekStart`, `count`, `until`, `anchorDate`, `offsets`, `rrule`, `rruleParts`, `rawFields`)
|
|
166
|
+
|
|
167
|
+
Read path strategy:
|
|
168
|
+
|
|
169
|
+
- AppleScript is still used to preserve Things list/search semantics.
|
|
170
|
+
- SQLite (read-only) is used to enrich due/start/reminder/recurrence details, especially repeating templates.
|
|
171
|
+
- If SQLite is unavailable, the server automatically falls back to AppleScript-only reads.
|
|
172
|
+
|
|
173
|
+
Container support:
|
|
174
|
+
|
|
175
|
+
- `things_add_area` creates a new area.
|
|
176
|
+
- `things_add_project` supports creating projects inside area via `area` or `areaId`.
|
|
177
|
+
- `things_add_todo` supports direct placement via `list/listId` and optional `heading/headingId`.
|
|
178
|
+
- `things_move_todo` supports moving into standard list, project, area, or heading.
|
|
179
|
+
|
|
180
|
+
## Known Limitations
|
|
181
|
+
|
|
182
|
+
- AppleScript access requires first-run macOS Automation permission approval.
|
|
183
|
+
- If Things is not running, macOS may launch it during tool execution.
|
|
184
|
+
- URL scheme writes are asynchronous from Things' perspective; tool success means request dispatch succeeded.
|
|
185
|
+
- Advanced recurring metadata is parsed from Things hidden `_private_experimental_ json` and may change across app versions.
|
|
186
|
+
- SQLite recurrence internals (`rt1_*`) are private implementation details and can also change across Things versions.
|
|
187
|
+
- Some repeating tasks may not expose a concrete upcoming date in public fields; in those cases `nextDate` can be empty while `recurrence`/`rawFields` still provide rule details.
|