@lcajigasm/things3-mcp 1.0.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/LICENSE +21 -0
- package/README.md +150 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +374 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Luis Cajigas
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# things3-mcp
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@lcajigasm/things3-mcp)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
|
|
6
|
+
A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server that connects Claude to [Things 3](https://culturedcode.com/things/) on macOS. Ask Claude to add to-dos and projects, search, open lists, and read your Today/Upcoming/Inbox — all from a Claude conversation.
|
|
7
|
+
|
|
8
|
+
> **Unofficial project.** Not affiliated with, endorsed by, or supported by Cultured Code. Things 3 is a trademark of Cultured Code GmbH & Co. KG.
|
|
9
|
+
|
|
10
|
+
## How it works
|
|
11
|
+
|
|
12
|
+
Things 3 doesn't expose a network API, so this server uses two different mechanisms:
|
|
13
|
+
|
|
14
|
+
- **Writing** (`add_todo`, `add_project`, `update_todo`, `update_project`, `search`, `show`) opens Things' [URL scheme](https://culturedcode.com/things/help/url-scheme/) in the background via `open -g things:///...`. This requires Things 3 to be running.
|
|
15
|
+
- **Reading** (`list_today`, `list_upcoming`, `list_projects`, `list_inbox`, `get_todo`) queries Things' local SQLite database directly, in read-only mode. The URL scheme has no way to read data back, so this is the only option.
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
- **macOS** with **Things 3** installed and running
|
|
20
|
+
- **Node.js 18+**
|
|
21
|
+
- To use `update_todo` / `update_project`: enable Things URLs in **Things → Settings → General → Enable Things URLs** and copy the auth token shown there
|
|
22
|
+
|
|
23
|
+
This server is macOS-only and talks to a local app and a local file — it is not a remote/OAuth-capable MCP server and cannot be hosted or used from another machine.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
### Claude Desktop
|
|
28
|
+
|
|
29
|
+
Add the following to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"mcpServers": {
|
|
34
|
+
"things3": {
|
|
35
|
+
"command": "npx",
|
|
36
|
+
"args": ["-y", "@lcajigasm/things3-mcp"],
|
|
37
|
+
"env": {
|
|
38
|
+
"THINGS_AUTH_TOKEN": "your-token-here"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Omit `THINGS_AUTH_TOKEN` if you don't need `update_todo` / `update_project`. Restart Claude Desktop afterwards.
|
|
46
|
+
|
|
47
|
+
### Claude Code
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
claude mcp add things3 --env THINGS_AUTH_TOKEN=your-token-here -- npx -y @lcajigasm/things3-mcp
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### From source
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/lcajigasm/things3-mcp.git
|
|
57
|
+
cd things3-mcp
|
|
58
|
+
npm install
|
|
59
|
+
npm run build
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Then point Claude Desktop at the built file:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"mcpServers": {
|
|
67
|
+
"things3": {
|
|
68
|
+
"command": "node",
|
|
69
|
+
"args": ["/absolute/path/to/things3-mcp/dist/index.js"],
|
|
70
|
+
"env": {
|
|
71
|
+
"THINGS_AUTH_TOKEN": "your-token-here"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Environment variables
|
|
79
|
+
|
|
80
|
+
| Variable | Required | Description |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `THINGS_AUTH_TOKEN` | Only for `update_todo` / `update_project` | Auth token from Things → Settings → General → Enable Things URls. Never logged or echoed back by this server. |
|
|
83
|
+
|
|
84
|
+
## Available tools
|
|
85
|
+
|
|
86
|
+
### Writing (via URL scheme)
|
|
87
|
+
|
|
88
|
+
| Tool | Description |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `add_todo` | Create a to-do: title, notes, `when` (today/tomorrow/evening/someday/date), deadline, tags, checklist items, destination list. |
|
|
91
|
+
| `add_project` | Create a project: title, notes, area, tags, initial to-dos. |
|
|
92
|
+
| `update_todo` | Update a to-do by id. Requires `THINGS_AUTH_TOKEN`. |
|
|
93
|
+
| `update_project` | Update a project by id. Requires `THINGS_AUTH_TOKEN`. |
|
|
94
|
+
| `search` | Open Things and search for a term. |
|
|
95
|
+
| `show` | Open a built-in list (Today, Upcoming, Anytime, Someday, Logbook, Inbox) or a specific project/area/to-do by id. |
|
|
96
|
+
|
|
97
|
+
### Reading (via local database, read-only)
|
|
98
|
+
|
|
99
|
+
| Tool | Description |
|
|
100
|
+
|---|---|
|
|
101
|
+
| `list_today` | To-dos scheduled for Today. |
|
|
102
|
+
| `list_upcoming` | To-dos scheduled for a future date or with an upcoming deadline. |
|
|
103
|
+
| `list_projects` | Open (incomplete, non-trashed) projects. |
|
|
104
|
+
| `list_inbox` | Unclassified to-dos in the Inbox. |
|
|
105
|
+
| `get_todo` | Full detail for one to-do by id, including its checklist. |
|
|
106
|
+
|
|
107
|
+
## Example prompts
|
|
108
|
+
|
|
109
|
+
- "Add a to-do 'Renew passport' with a deadline of 2026-09-01"
|
|
110
|
+
- "Create a project called 'Q3 planning' in my Work area with to-dos for budget, hiring, roadmap"
|
|
111
|
+
- "What's on my plate today?"
|
|
112
|
+
- "What's coming up in the next few weeks?"
|
|
113
|
+
- "Show me everything sitting in my Inbox"
|
|
114
|
+
- "Mark to-do abc-123 as completed"
|
|
115
|
+
- "Open Things and search for 'invoice'"
|
|
116
|
+
|
|
117
|
+
## Limitations
|
|
118
|
+
|
|
119
|
+
- macOS + Things 3 only. No Windows/Linux/iOS support, no remote/hosted mode.
|
|
120
|
+
- Writing is fire-and-forget: the URL scheme doesn't return the created item's id, so `add_todo`/`add_project` can't tell you the new id directly (use `search` or `list_inbox` to find it afterwards).
|
|
121
|
+
- Reading queries Things' internal SQLite schema, which is undocumented and could change in future Things versions.
|
|
122
|
+
- `update_todo`/`update_project` need `THINGS_AUTH_TOKEN`; without it those two tools return an error, everything else still works.
|
|
123
|
+
|
|
124
|
+
## Troubleshooting
|
|
125
|
+
|
|
126
|
+
**"things3-mcp only works on macOS"**
|
|
127
|
+
This server calls `open -g things:///...` and reads a macOS-only app container path — there's no way around this.
|
|
128
|
+
|
|
129
|
+
**"Could not find the Things 3 database"**
|
|
130
|
+
Make sure Things 3 is installed and has been opened at least once (the database is created on first launch).
|
|
131
|
+
|
|
132
|
+
**"THINGS_AUTH_TOKEN environment variable is required"**
|
|
133
|
+
Only `update_todo`/`update_project` need it. Get it from Things → Settings → General → Enable Things URLs, then add it to your MCP config's `env` block.
|
|
134
|
+
|
|
135
|
+
**Claude doesn't see the Things 3 tools**
|
|
136
|
+
- Restart Claude Desktop after editing the config file
|
|
137
|
+
- Validate the JSON in `claude_desktop_config.json` (a trailing comma will break it)
|
|
138
|
+
- Run `npx @lcajigasm/things3-mcp` manually in a terminal to check for Node errors
|
|
139
|
+
|
|
140
|
+
## Development
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npm install
|
|
144
|
+
npm run dev # watch mode via tsx
|
|
145
|
+
npm run build # compile to dist/
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
MIT — see [LICENSE](LICENSE). Not affiliated with Cultured Code.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
+
import Database from "better-sqlite3";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Configuration
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
if (process.platform !== "darwin") {
|
|
16
|
+
console.error("things3-mcp only works on macOS — Things 3 is a macOS-only app.");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const AUTH_TOKEN = process.env.THINGS_AUTH_TOKEN;
|
|
20
|
+
const DB_PATH = path.join(homedir(), "Library/Group Containers/JLMPQHK86H.com.culturedcode.ThingsMac/Things Database.thingsdatabase/main.sqlite");
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Things URL scheme (write operations)
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
class ThingsError extends Error {
|
|
25
|
+
}
|
|
26
|
+
function buildThingsUrl(action, params) {
|
|
27
|
+
const pairs = [];
|
|
28
|
+
for (const [key, value] of Object.entries(params)) {
|
|
29
|
+
if (value === undefined || value === null || value === "")
|
|
30
|
+
continue;
|
|
31
|
+
pairs.push(`${key}=${encodeURIComponent(String(value))}`);
|
|
32
|
+
}
|
|
33
|
+
return `things:///${action}${pairs.length ? "?" + pairs.join("&") : ""}`;
|
|
34
|
+
}
|
|
35
|
+
async function openThingsUrl(url) {
|
|
36
|
+
try {
|
|
37
|
+
await execFileAsync("open", ["-g", url]);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
41
|
+
throw new ThingsError(`Failed to open Things URL: ${msg}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function requireAuthToken() {
|
|
45
|
+
if (!AUTH_TOKEN) {
|
|
46
|
+
throw new ThingsError("THINGS_AUTH_TOKEN environment variable is required for update operations. " +
|
|
47
|
+
"Get it from Things → Settings → General → Enable Things URLs.");
|
|
48
|
+
}
|
|
49
|
+
return AUTH_TOKEN;
|
|
50
|
+
}
|
|
51
|
+
function toolResult(data) {
|
|
52
|
+
return {
|
|
53
|
+
content: [
|
|
54
|
+
{
|
|
55
|
+
type: "text",
|
|
56
|
+
text: typeof data === "string" ? data : JSON.stringify(data, null, 2),
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function errorResult(err) {
|
|
62
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
63
|
+
return { content: [{ type: "text", text: `Error: ${message}` }] };
|
|
64
|
+
}
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Things date encoding
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Things packs startDate/deadline into a single integer as
|
|
69
|
+
// (year << 16) | (month << 12) | (day << 7), which conveniently also sorts
|
|
70
|
+
// correctly as a plain integer.
|
|
71
|
+
function encodeThingsDate(date) {
|
|
72
|
+
const year = date.getFullYear();
|
|
73
|
+
const month = date.getMonth() + 1;
|
|
74
|
+
const day = date.getDate();
|
|
75
|
+
return (year << 16) | (month << 12) | (day << 7);
|
|
76
|
+
}
|
|
77
|
+
function decodeThingsDate(value) {
|
|
78
|
+
if (value === null)
|
|
79
|
+
return null;
|
|
80
|
+
const year = value >> 16;
|
|
81
|
+
const month = (value >> 12) & 0xf;
|
|
82
|
+
const day = (value >> 7) & 0x1f;
|
|
83
|
+
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
84
|
+
}
|
|
85
|
+
// Core Data timestamps are seconds since 2001-01-01, not the Unix epoch.
|
|
86
|
+
const CORE_DATA_EPOCH_OFFSET = 978307200;
|
|
87
|
+
function decodeCoreDataDate(value) {
|
|
88
|
+
if (value === null)
|
|
89
|
+
return null;
|
|
90
|
+
return new Date((value + CORE_DATA_EPOCH_OFFSET) * 1000).toISOString();
|
|
91
|
+
}
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// SQLite (read operations)
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
let db = null;
|
|
96
|
+
function getDb() {
|
|
97
|
+
if (db)
|
|
98
|
+
return db;
|
|
99
|
+
if (!existsSync(DB_PATH)) {
|
|
100
|
+
throw new ThingsError(`Could not find the Things 3 database at "${DB_PATH}". ` +
|
|
101
|
+
"Make sure Things 3 is installed and has been opened at least once.");
|
|
102
|
+
}
|
|
103
|
+
db = new Database(DB_PATH, { readonly: true, fileMustExist: true });
|
|
104
|
+
return db;
|
|
105
|
+
}
|
|
106
|
+
const STATUS_LABELS = {
|
|
107
|
+
0: "open",
|
|
108
|
+
2: "canceled",
|
|
109
|
+
3: "completed",
|
|
110
|
+
};
|
|
111
|
+
function formatTaskRow(row) {
|
|
112
|
+
return {
|
|
113
|
+
id: row.uuid,
|
|
114
|
+
title: row.title,
|
|
115
|
+
notes: row.notes || undefined,
|
|
116
|
+
status: STATUS_LABELS[row.status] ?? String(row.status),
|
|
117
|
+
when: decodeThingsDate(row.startDate) ?? undefined,
|
|
118
|
+
deadline: decodeThingsDate(row.deadline) ?? undefined,
|
|
119
|
+
completedAt: decodeCoreDataDate(row.stopDate) ?? undefined,
|
|
120
|
+
tags: row.tags ? row.tags.split(", ") : undefined,
|
|
121
|
+
project: row.project ?? undefined,
|
|
122
|
+
area: row.area ?? undefined,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const TASK_SELECT = `
|
|
126
|
+
SELECT
|
|
127
|
+
t.uuid, t.title, t.notes, t.status, t.startDate, t.deadline, t.stopDate,
|
|
128
|
+
(SELECT group_concat(tag.title, ', ')
|
|
129
|
+
FROM TMTaskTag tt JOIN TMTag tag ON tt.tags = tag.uuid
|
|
130
|
+
WHERE tt.tasks = t.uuid) AS tags,
|
|
131
|
+
p.title AS project,
|
|
132
|
+
a.title AS area
|
|
133
|
+
FROM TMTask t
|
|
134
|
+
LEFT JOIN TMTask p ON t.project = p.uuid
|
|
135
|
+
LEFT JOIN TMArea a ON t.area = a.uuid
|
|
136
|
+
`;
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// MCP Server
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
const server = new McpServer({
|
|
141
|
+
name: "things3-mcp",
|
|
142
|
+
version: "1.0.0",
|
|
143
|
+
});
|
|
144
|
+
// --- Write tools -----------------------------------------------------------
|
|
145
|
+
server.tool("add_todo", "Create a new to-do in Things 3.", {
|
|
146
|
+
title: z.string().describe("The to-do's title (required)"),
|
|
147
|
+
notes: z.string().optional().describe("Notes for the to-do"),
|
|
148
|
+
when: z
|
|
149
|
+
.string()
|
|
150
|
+
.optional()
|
|
151
|
+
.describe("When to schedule it: 'today', 'tomorrow', 'evening', 'someday', or a YYYY-MM-DD date"),
|
|
152
|
+
deadline: z.string().optional().describe("Deadline as a YYYY-MM-DD date"),
|
|
153
|
+
tags: z.array(z.string()).optional().describe("Tags to apply (must already exist in Things)"),
|
|
154
|
+
checklistItems: z.array(z.string()).optional().describe("Checklist item titles to add"),
|
|
155
|
+
list: z.string().optional().describe("Name of the destination project or area"),
|
|
156
|
+
}, async ({ title, notes, when, deadline, tags, checklistItems, list }) => {
|
|
157
|
+
try {
|
|
158
|
+
const url = buildThingsUrl("add", {
|
|
159
|
+
title,
|
|
160
|
+
notes,
|
|
161
|
+
when,
|
|
162
|
+
deadline,
|
|
163
|
+
tags: tags?.join(","),
|
|
164
|
+
"checklist-items": checklistItems?.join("\n"),
|
|
165
|
+
list,
|
|
166
|
+
});
|
|
167
|
+
await openThingsUrl(url);
|
|
168
|
+
return toolResult({ status: "ok", url });
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
return errorResult(err);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
server.tool("add_project", "Create a new project in Things 3.", {
|
|
175
|
+
title: z.string().describe("The project's title (required)"),
|
|
176
|
+
notes: z.string().optional().describe("Notes for the project"),
|
|
177
|
+
area: z.string().optional().describe("Name of the destination area"),
|
|
178
|
+
tags: z.array(z.string()).optional().describe("Tags to apply (must already exist in Things)"),
|
|
179
|
+
todos: z.array(z.string()).optional().describe("Titles of initial to-dos to create inside the project"),
|
|
180
|
+
}, async ({ title, notes, area, tags, todos }) => {
|
|
181
|
+
try {
|
|
182
|
+
const url = buildThingsUrl("add-project", {
|
|
183
|
+
title,
|
|
184
|
+
notes,
|
|
185
|
+
area,
|
|
186
|
+
tags: tags?.join(","),
|
|
187
|
+
"to-dos": todos?.join("\n"),
|
|
188
|
+
});
|
|
189
|
+
await openThingsUrl(url);
|
|
190
|
+
return toolResult({ status: "ok", url });
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
return errorResult(err);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
server.tool("update_todo", "Update an existing to-do in Things 3. Requires THINGS_AUTH_TOKEN to be set.", {
|
|
197
|
+
id: z.string().describe("The to-do's id (required)"),
|
|
198
|
+
title: z.string().optional().describe("New title"),
|
|
199
|
+
notes: z.string().optional().describe("New notes (replaces existing notes)"),
|
|
200
|
+
when: z.string().optional().describe("New schedule: 'today', 'tomorrow', 'evening', 'someday', or a YYYY-MM-DD date"),
|
|
201
|
+
deadline: z.string().optional().describe("New deadline as a YYYY-MM-DD date"),
|
|
202
|
+
tags: z.array(z.string()).optional().describe("Replace all tags with this set"),
|
|
203
|
+
checklistItems: z.array(z.string()).optional().describe("Replace the checklist with these items"),
|
|
204
|
+
completed: z.boolean().optional().describe("Mark as completed"),
|
|
205
|
+
canceled: z.boolean().optional().describe("Mark as canceled"),
|
|
206
|
+
}, async ({ id, tags, checklistItems, ...rest }) => {
|
|
207
|
+
try {
|
|
208
|
+
const token = requireAuthToken();
|
|
209
|
+
const url = buildThingsUrl("update", {
|
|
210
|
+
id,
|
|
211
|
+
"auth-token": token,
|
|
212
|
+
...rest,
|
|
213
|
+
tags: tags?.join(","),
|
|
214
|
+
"checklist-items": checklistItems?.join("\n"),
|
|
215
|
+
});
|
|
216
|
+
await openThingsUrl(url);
|
|
217
|
+
return toolResult({ status: "ok" });
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
return errorResult(err);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
server.tool("update_project", "Update an existing project in Things 3. Requires THINGS_AUTH_TOKEN to be set.", {
|
|
224
|
+
id: z.string().describe("The project's id (required)"),
|
|
225
|
+
title: z.string().optional().describe("New title"),
|
|
226
|
+
notes: z.string().optional().describe("New notes (replaces existing notes)"),
|
|
227
|
+
area: z.string().optional().describe("Move the project to this area"),
|
|
228
|
+
tags: z.array(z.string()).optional().describe("Replace all tags with this set"),
|
|
229
|
+
completed: z.boolean().optional().describe("Mark as completed"),
|
|
230
|
+
canceled: z.boolean().optional().describe("Mark as canceled"),
|
|
231
|
+
}, async ({ id, tags, ...rest }) => {
|
|
232
|
+
try {
|
|
233
|
+
const token = requireAuthToken();
|
|
234
|
+
const url = buildThingsUrl("update-project", {
|
|
235
|
+
id,
|
|
236
|
+
"auth-token": token,
|
|
237
|
+
...rest,
|
|
238
|
+
tags: tags?.join(","),
|
|
239
|
+
});
|
|
240
|
+
await openThingsUrl(url);
|
|
241
|
+
return toolResult({ status: "ok" });
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
return errorResult(err);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
server.tool("search", "Open Things 3 and search for a term.", {
|
|
248
|
+
query: z.string().describe("The search term"),
|
|
249
|
+
}, async ({ query }) => {
|
|
250
|
+
try {
|
|
251
|
+
const url = buildThingsUrl("search", { query });
|
|
252
|
+
await openThingsUrl(url);
|
|
253
|
+
return toolResult({ status: "ok" });
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
return errorResult(err);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
server.tool("show", "Open a built-in list (Today, Upcoming, Anytime, Someday, Logbook, Inbox) or a project/area by id in Things 3.", {
|
|
260
|
+
list: z.string().optional().describe("Built-in list name, e.g. 'today', 'upcoming', 'anytime', 'someday', 'logbook', 'inbox'"),
|
|
261
|
+
id: z.string().optional().describe("Id of a specific project, area, or to-do to open"),
|
|
262
|
+
}, async ({ list, id }) => {
|
|
263
|
+
try {
|
|
264
|
+
if (!list && !id) {
|
|
265
|
+
throw new ThingsError("Provide either 'list' or 'id'.");
|
|
266
|
+
}
|
|
267
|
+
const url = buildThingsUrl("show", { list, id });
|
|
268
|
+
await openThingsUrl(url);
|
|
269
|
+
return toolResult({ status: "ok" });
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
return errorResult(err);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
// --- Read tools --------------------------------------------------------
|
|
276
|
+
server.tool("list_today", "List to-dos scheduled for Today.", {}, async () => {
|
|
277
|
+
try {
|
|
278
|
+
const rows = getDb()
|
|
279
|
+
.prepare(`${TASK_SELECT}
|
|
280
|
+
WHERE t.type = 0 AND t.trashed = 0 AND t.status = 0
|
|
281
|
+
AND t.start = 1 AND t.startDate IS NOT NULL
|
|
282
|
+
ORDER BY t."index" ASC`)
|
|
283
|
+
.all();
|
|
284
|
+
return toolResult(rows.map(formatTaskRow));
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
return errorResult(err);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
server.tool("list_upcoming", "List to-dos scheduled for a future date or with an upcoming deadline.", {
|
|
291
|
+
limit: z.number().int().min(1).max(200).default(50).describe("Max number of results (default 50)"),
|
|
292
|
+
}, async ({ limit }) => {
|
|
293
|
+
try {
|
|
294
|
+
const todayEncoded = encodeThingsDate(new Date());
|
|
295
|
+
const rows = getDb()
|
|
296
|
+
.prepare(`${TASK_SELECT}
|
|
297
|
+
WHERE t.type = 0 AND t.trashed = 0 AND t.status = 0
|
|
298
|
+
AND (
|
|
299
|
+
(t.startDate IS NOT NULL AND t.startDate > @today)
|
|
300
|
+
OR (t.deadline IS NOT NULL AND t.deadline >= @today)
|
|
301
|
+
)
|
|
302
|
+
ORDER BY COALESCE(t.startDate, t.deadline) ASC
|
|
303
|
+
LIMIT @limit`)
|
|
304
|
+
.all({ today: todayEncoded, limit });
|
|
305
|
+
return toolResult(rows.map(formatTaskRow));
|
|
306
|
+
}
|
|
307
|
+
catch (err) {
|
|
308
|
+
return errorResult(err);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
server.tool("list_projects", "List open (non-trashed, incomplete) projects.", {}, async () => {
|
|
312
|
+
try {
|
|
313
|
+
const rows = getDb()
|
|
314
|
+
.prepare(`${TASK_SELECT}
|
|
315
|
+
WHERE t.type = 1 AND t.trashed = 0 AND t.status = 0
|
|
316
|
+
ORDER BY t."index" ASC`)
|
|
317
|
+
.all();
|
|
318
|
+
return toolResult(rows.map(formatTaskRow));
|
|
319
|
+
}
|
|
320
|
+
catch (err) {
|
|
321
|
+
return errorResult(err);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
server.tool("list_inbox", "List unclassified to-dos in the Inbox.", {}, async () => {
|
|
325
|
+
try {
|
|
326
|
+
const rows = getDb()
|
|
327
|
+
.prepare(`${TASK_SELECT}
|
|
328
|
+
WHERE t.type = 0 AND t.trashed = 0 AND t.status = 0 AND t.start = 0
|
|
329
|
+
ORDER BY t."index" ASC`)
|
|
330
|
+
.all();
|
|
331
|
+
return toolResult(rows.map(formatTaskRow));
|
|
332
|
+
}
|
|
333
|
+
catch (err) {
|
|
334
|
+
return errorResult(err);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
server.tool("get_todo", "Get full details for a single to-do by id, including its checklist.", {
|
|
338
|
+
id: z.string().describe("The to-do's id"),
|
|
339
|
+
}, async ({ id }) => {
|
|
340
|
+
try {
|
|
341
|
+
const row = getDb()
|
|
342
|
+
.prepare(`${TASK_SELECT} WHERE t.uuid = @id AND t.type = 0`)
|
|
343
|
+
.get({ id });
|
|
344
|
+
if (!row) {
|
|
345
|
+
throw new ThingsError(`No to-do found with id "${id}".`);
|
|
346
|
+
}
|
|
347
|
+
const checklist = getDb()
|
|
348
|
+
.prepare(`SELECT title, status FROM TMChecklistItem WHERE task = @id ORDER BY "index" ASC`)
|
|
349
|
+
.all({ id });
|
|
350
|
+
return toolResult({
|
|
351
|
+
...formatTaskRow(row),
|
|
352
|
+
checklist: checklist.map((item) => ({
|
|
353
|
+
title: item.title,
|
|
354
|
+
status: STATUS_LABELS[item.status] ?? String(item.status),
|
|
355
|
+
})),
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
return errorResult(err);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
// ---------------------------------------------------------------------------
|
|
363
|
+
// Start
|
|
364
|
+
// ---------------------------------------------------------------------------
|
|
365
|
+
async function main() {
|
|
366
|
+
const transport = new StdioServerTransport();
|
|
367
|
+
await server.connect(transport);
|
|
368
|
+
console.error("Things 3 MCP server running on stdio");
|
|
369
|
+
}
|
|
370
|
+
main().catch((err) => {
|
|
371
|
+
console.error("Fatal error:", err);
|
|
372
|
+
process.exit(1);
|
|
373
|
+
});
|
|
374
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;IAClC,OAAO,CAAC,KAAK,CACX,iEAAiE,CAClE,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;AAEjD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CACvB,OAAO,EAAE,EACT,2GAA2G,CAC5G,CAAC;AAEF,8EAA8E;AAC9E,uCAAuC;AACvC,8EAA8E;AAE9E,MAAM,WAAY,SAAQ,KAAK;CAAG;AAElC,SAAS,cAAc,CAAC,MAAc,EAAE,MAA+B;IACrE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE;YAAE,SAAS;QACpE,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,aAAa,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAC3E,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,GAAW;IACtC,IAAI,CAAC;QACH,MAAM,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,MAAM,IAAI,WAAW,CAAC,8BAA8B,GAAG,EAAE,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB;IACvB,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,WAAW,CACnB,4EAA4E;YAC1E,+DAA+D,CAClE,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,UAAU,CAAC,IAAa;IAC/B,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;aACtE;SACF;KACF,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,GAAY;IAC/B,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;AACpE,CAAC;AAED,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAC9E,2DAA2D;AAC3D,2EAA2E;AAC3E,gCAAgC;AAEhC,SAAS,gBAAgB,CAAC,IAAU;IAClC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAClC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;IAC3B,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAoB;IAC5C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;IACzB,MAAM,KAAK,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,GAAG,CAAC;IAClC,MAAM,GAAG,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;IAChC,OAAO,GAAG,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AACrF,CAAC;AAED,yEAAyE;AACzE,MAAM,sBAAsB,GAAG,SAAS,CAAC;AAEzC,SAAS,kBAAkB,CAAC,KAAoB;IAC9C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,OAAO,IAAI,IAAI,CAAC,CAAC,KAAK,GAAG,sBAAsB,CAAC,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;AACzE,CAAC;AAED,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E,IAAI,EAAE,GAA6B,IAAI,CAAC;AAExC,SAAS,KAAK;IACZ,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC;IAClB,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,WAAW,CACnB,4CAA4C,OAAO,KAAK;YACtD,oEAAoE,CACvE,CAAC;IACJ,CAAC;IACD,EAAE,GAAG,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,aAAa,GAA2B;IAC5C,CAAC,EAAE,MAAM;IACT,CAAC,EAAE,UAAU;IACb,CAAC,EAAE,WAAW;CACf,CAAC;AAeF,SAAS,aAAa,CAAC,GAAY;IACjC,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,IAAI;QACZ,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,SAAS;QAC7B,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;QACvD,IAAI,EAAE,gBAAgB,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,SAAS;QAClD,QAAQ,EAAE,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS;QACrD,WAAW,EAAE,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS;QAC1D,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;QACjD,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,SAAS;QACjC,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,SAAS;KAC5B,CAAC;AACJ,CAAC;AAED,MAAM,WAAW,GAAG;;;;;;;;;;;CAWnB,CAAC;AAEF,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,aAAa;IACnB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,8EAA8E;AAE9E,MAAM,CAAC,IAAI,CACT,UAAU,EACV,iCAAiC,EACjC;IACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;IAC1D,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;IAC5D,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,sFAAsF,CAAC;IACnG,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+BAA+B,CAAC;IACzE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8CAA8C,CAAC;IAC7F,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;IACvF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;CAChF,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,EAAE,EAAE;IACrE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,cAAc,CAAC,KAAK,EAAE;YAChC,KAAK;YACL,KAAK;YACL,IAAI;YACJ,QAAQ;YACR,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;YACrB,iBAAiB,EAAE,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC;YAC7C,IAAI;SACL,CAAC,CAAC;QACH,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QACzB,OAAO,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,IAAI,CACT,aAAa,EACb,mCAAmC,EACnC;IACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;IAC5D,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;IAC9D,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;IACpE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8CAA8C,CAAC;IAC7F,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uDAAuD,CAAC;CACxG,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;IAC5C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,cAAc,CAAC,aAAa,EAAE;YACxC,KAAK;YACL,KAAK;YACL,IAAI;YACJ,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;YACrB,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC;SAC5B,CAAC,CAAC;QACH,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QACzB,OAAO,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,IAAI,CACT,aAAa,EACb,6EAA6E,EAC7E;IACE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC;IACpD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC;IAClD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;IAC5E,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+EAA+E,CAAC;IACrH,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;IAC7E,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;IAC/E,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;IACjG,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC;IAC/D,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kBAAkB,CAAC;CAC9D,EACD,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE;IAC9C,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE;YACnC,EAAE;YACF,YAAY,EAAE,KAAK;YACnB,GAAG,IAAI;YACP,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;YACrB,iBAAiB,EAAE,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC;SAC9C,CAAC,CAAC;QACH,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QACzB,OAAO,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,IAAI,CACT,gBAAgB,EAChB,+EAA+E,EAC/E;IACE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;IACtD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC;IAClD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;IAC5E,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+BAA+B,CAAC;IACrE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;IAC/E,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC;IAC/D,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kBAAkB,CAAC;CAC9D,EACD,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE;IAC9B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,cAAc,CAAC,gBAAgB,EAAE;YAC3C,EAAE;YACF,YAAY,EAAE,KAAK;YACnB,GAAG,IAAI;YACP,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;SACtB,CAAC,CAAC;QACH,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QACzB,OAAO,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,IAAI,CACT,QAAQ,EACR,sCAAsC,EACtC;IACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;CAC9C,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;IAClB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAChD,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QACzB,OAAO,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,IAAI,CACT,MAAM,EACN,+GAA+G,EAC/G;IACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wFAAwF,CAAC;IAC9H,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kDAAkD,CAAC;CACvF,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE;IACrB,IAAI,CAAC;QACH,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,WAAW,CAAC,gCAAgC,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,GAAG,GAAG,cAAc,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACjD,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QACzB,OAAO,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC,CACF,CAAC;AAEF,0EAA0E;AAE1E,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,kCAAkC,EAAE,EAAE,EAAE,KAAK,IAAI,EAAE;IAC3E,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,KAAK,EAAE;aACjB,OAAO,CACN,GAAG,WAAW;;;gCAGU,CACzB;aACA,GAAG,EAAe,CAAC;QACtB,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,IAAI,CACT,eAAe,EACf,uEAAuE,EACvE;IACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,oCAAoC,CAAC;CACnG,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;IAClB,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,gBAAgB,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,KAAK,EAAE;aACjB,OAAO,CACN,GAAG,WAAW;;;;;;;wBAOA,CACf;aACA,GAAG,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,CAAc,CAAC;QACpD,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC,CACF,CAAC;AAEF,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,+CAA+C,EAAE,EAAE,EAAE,KAAK,IAAI,EAAE;IAC3F,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,KAAK,EAAE;aACjB,OAAO,CACN,GAAG,WAAW;;gCAEU,CACzB;aACA,GAAG,EAAe,CAAC;QACtB,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,wCAAwC,EAAE,EAAE,EAAE,KAAK,IAAI,EAAE;IACjF,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,KAAK,EAAE;aACjB,OAAO,CACN,GAAG,WAAW;;gCAEU,CACzB;aACA,GAAG,EAAe,CAAC;QACtB,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,IAAI,CACT,UAAU,EACV,qEAAqE,EACrE;IACE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;CAC1C,EACD,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;IACf,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,KAAK,EAAE;aAChB,OAAO,CAAC,GAAG,WAAW,oCAAoC,CAAC;aAC3D,GAAG,CAAC,EAAE,EAAE,EAAE,CAAwB,CAAC;QACtC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,WAAW,CAAC,2BAA2B,EAAE,IAAI,CAAC,CAAC;QAC3D,CAAC;QACD,MAAM,SAAS,GAAG,KAAK,EAAE;aACtB,OAAO,CAAC,iFAAiF,CAAC;aAC1F,GAAG,CAAC,EAAE,EAAE,EAAE,CAA6C,CAAC;QAC3D,OAAO,UAAU,CAAC;YAChB,GAAG,aAAa,CAAC,GAAG,CAAC;YACrB,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAClC,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,MAAM,EAAE,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;aAC1D,CAAC,CAAC;SACJ,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC,CACF,CAAC;AAEF,8EAA8E;AAC9E,QAAQ;AACR,8EAA8E;AAE9E,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;AACxD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lcajigasm/things3-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server connecting Claude to Things 3 on macOS via its URL scheme (write) and local database (read)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"things3-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "tsx watch src/index.ts",
|
|
12
|
+
"start": "node dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"things",
|
|
20
|
+
"things3",
|
|
21
|
+
"claude",
|
|
22
|
+
"task-management",
|
|
23
|
+
"todo",
|
|
24
|
+
"productivity",
|
|
25
|
+
"model-context-protocol"
|
|
26
|
+
],
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/lcajigasm/things3-mcp.git"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/lcajigasm/things3-mcp#readme",
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/lcajigasm/things3-mcp/issues"
|
|
34
|
+
},
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"os": [
|
|
40
|
+
"darwin"
|
|
41
|
+
],
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
47
|
+
"better-sqlite3": "^12.11.1",
|
|
48
|
+
"zod": "^3.24.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/better-sqlite3": "^7.6.12",
|
|
52
|
+
"@types/node": "^22.0.0",
|
|
53
|
+
"tsx": "^4.19.0",
|
|
54
|
+
"typescript": "^5.8.0"
|
|
55
|
+
},
|
|
56
|
+
"allowScripts": {
|
|
57
|
+
"better-sqlite3@12.11.1": true
|
|
58
|
+
}
|
|
59
|
+
}
|