Best-in-class MCP server for Things 3 — token-efficient, full CRUD, AppleScript + URL scheme
Project description
Things 3 MCP Server
Best-in-class MCP server for Things 3 — token-efficient, full CRUD, AppleScript + URL scheme.
Built to be the most capable and efficient Things integration for Claude Desktop, Claude Code, and any MCP-compatible client.
Features
- Token-efficient output — concise one-line-per-item by default, detailed on demand
- 30+ tools — full coverage of every Things 3 view and operation
- Smart Someday filtering — correctly handles inherited Someday status (matches Things UI exactly)
- Random sampling —
get_random_inbox(5)for manageable LLM context instead of dumping hundreds of items - Summary mode — full GTD overview (
get_summary) in ~20 lines - AppleScript + URL scheme — reliable writes with UUID feedback; automatic fallback
- Bulk JSON import/export — Things URL scheme JSON format
- HTTP transport — optional remote access with API key authentication
- SKILL.md — self-teaching instructions so Claude uses tools optimally
Requirements
- macOS (Things 3 is macOS/iOS only)
- Things 3 installed and running
- Python 3.12+
- uv (recommended) or pip
Quick Start
Install and run
# Clone and install
git clone https://github.com/Groupthink-dev/things3-blade-mcp.git
cd things3-blade-mcp
uv sync
# Run (stdio transport — for Claude Desktop)
uv run things3-mcp
Claude Desktop configuration
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"things3": {
"command": "uv",
"args": ["run", "--directory", "/path/to/things3-blade-mcp", "things3-mcp"]
}
}
}
Claude Code
# Add as MCP server
claude mcp add things3 -- uv run --directory /path/to/things3-blade-mcp things3-mcp
Token-Saving Tips
The server is designed to minimize LLM context consumption:
- Start with
get_summary()— 20-line GTD overview, not hundreds of tasks - Use random sampling —
get_random_inbox(5)beforeget_inbox(limit=100) - Defaults are efficient —
concise=True,limit=10on all tools - One-line format:
□ Buy groceries [3F8A2B1C] | today | deadline:2026-03-01 | #errands - Null fields omitted — no "Notes: None" or "Tags: []" clutter
- Batch lookups — no N+1 queries; project/area names resolved in bulk
All Tools (30)
List Views (9)
| Tool | Description |
|---|---|
get_inbox |
Unprocessed tasks |
get_today |
Today's schedule (Someday-filtered) |
get_upcoming |
Future scheduled tasks |
get_anytime |
Available tasks |
get_someday |
Deferred tasks (includes inherited) |
get_logbook |
Completed tasks (configurable period) |
get_trash |
Trashed tasks |
get_deadlines |
All tasks with deadlines, sorted |
get_summary |
Full GTD overview in ~20 lines |
Random Sampling (4) — recommended entry points
| Tool | Description |
|---|---|
get_random_inbox |
Random sample from inbox |
get_random_today |
Random sample from today |
get_random_anytime |
Random sample from anytime |
get_random_todos |
Random sample, optional project filter |
Entity Views (5)
| Tool | Description |
|---|---|
get_todos |
All open todos (optional project filter) |
get_projects |
Projects with open/done counts |
get_areas |
High-level categories |
get_tags |
All tags |
get_tagged_items |
Todos with a specific tag |
Search & Detail (4)
| Tool | Description |
|---|---|
search_todos |
Search by title/notes |
search_advanced |
Multi-filter search (status, date, tag, area) |
get_recent |
Recently created items |
show_item |
Single item by UUID with full details |
Write (4) — AppleScript primary, URL scheme fallback
| Tool | Description |
|---|---|
add_todo |
Create a todo (returns UUID) |
add_project |
Create a project with optional todos |
update_todo |
Update any todo field |
update_project |
Update any project field |
Bulk & Navigation (4)
| Tool | Description |
|---|---|
json_import |
Bulk create via Things JSON format |
json_export |
Export todos as compact JSON |
show_in_things |
Reveal item in Things app |
search_in_things |
Open Things search UI |
HTTP Transport
For remote access (e.g., from a different machine or mobile):
# Set environment variables
export THINGS_MCP_TRANSPORT=http
export THINGS_MCP_HOST=127.0.0.1
export THINGS_MCP_PORT=8765
export THINGS_MCP_API_KEY=your-secret-key-here # auto-generated if empty
# Run
uv run things3-mcp
Bearer Token Authentication
When exposing the server through a tunnel or reverse proxy, set THINGS_MCP_API_TOKEN to require a bearer token on every request:
export THINGS_MCP_API_TOKEN=your-secret-token-here
All HTTP requests must then include the header:
Authorization: Bearer your-secret-token-here
Requests without a valid token receive 401 Unauthorized. If the env var is unset or empty, bearer auth is disabled and the server behaves as before (suitable for localhost-only access).
Security Warning
Never expose the HTTP port directly to the internet. Use a reverse proxy with TLS:
- Cloudflare Tunnel (recommended — free, zero-config TLS)
- Caddy (automatic HTTPS)
- Nginx with Let's Encrypt
Cloudflare Tunnel example
# Install cloudflared
brew install cloudflare/cloudflare/cloudflared
# Create tunnel
cloudflared tunnel create things-mcp
cloudflared tunnel route dns things-mcp things-mcp.yourdomain.com
# Run tunnel
cloudflared tunnel --url http://localhost:8765 run things-mcp
Testing the HTTP endpoint
# Health check
curl http://localhost:8765/
# Call a tool (via MCP protocol)
curl -X POST http://localhost:8765/mcp \
-H "Content-Type: application/json" \
-H "X-API-Key: your-secret-key-here" \
-d '{"method": "tools/call", "params": {"name": "get_summary", "arguments": {}}}'
Environment Variables
| Variable | Default | Description |
|---|---|---|
THINGS_MCP_TRANSPORT |
stdio |
stdio or http |
THINGS_MCP_HOST |
127.0.0.1 |
HTTP bind address |
THINGS_MCP_PORT |
8765 |
HTTP port |
THINGS_MCP_API_TOKEN |
(none) | Bearer token for HTTP auth — if set, requires Authorization: Bearer <token> |
THINGS_MCP_API_KEY |
(auto) | API key for HTTP auth (X-API-Key header) |
THINGS_AUTH_TOKEN |
(auto) | Things URL scheme auth token |
Architecture
src/things3_mcp/
├── server.py # FastMCP instance + all 30 @mcp.tool definitions
├── formatters.py # Two-tier output: concise (1-line) + detailed
├── someday.py # Someday filtering (matches Things UI behavior)
├── applescript.py # AppleScript bridge for writes (temp-file approach)
├── url_scheme.py # URL scheme builder (fallback writes + checklist items)
├── sampling.py # Random sampling helpers
├── auth.py # API key auth for HTTP transport
└── models.py # Shared constants
Design principles:
- Reads via
things.py(direct SQLite — fast, no app needed) - Writes via AppleScript (reliable, returns UUIDs, no auth token needed)
- Fallback writes via URL scheme (for checklist items or when AppleScript fails)
- Batch lookups eliminate N+1 queries in formatters
- Someday filtering handles the Things 3 project-inheritance edge case
Development
# Install with dev/test dependencies
uv sync --all-extras
# Run tests
uv run pytest tests/ -v
# Lint
uv run ruff check src/ tests/
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file things3_blade_mcp-0.1.1.tar.gz.
File metadata
- Download URL: things3_blade_mcp-0.1.1.tar.gz
- Upload date:
- Size: 105.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d270aabbb3a51fbc54d6d2068d97b2734626f403bccf8880ddd2eee65ecce8b9
|
|
| MD5 |
bcd7d036590a04953a473ebc659906dc
|
|
| BLAKE2b-256 |
37fdb6cbe9ea2963a6f972ed6776538ece0e0bda8385c737f2ab2ac511c4cfcd
|
Provenance
The following attestation bundles were made for things3_blade_mcp-0.1.1.tar.gz:
Publisher:
publish.yml on Groupthink-dev/things3-blade-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
things3_blade_mcp-0.1.1.tar.gz -
Subject digest:
d270aabbb3a51fbc54d6d2068d97b2734626f403bccf8880ddd2eee65ecce8b9 - Sigstore transparency entry: 1395698425
- Sigstore integration time:
-
Permalink:
Groupthink-dev/things3-blade-mcp@edad16ce4162b142fe021a96223194ae42636dbc -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Groupthink-dev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@edad16ce4162b142fe021a96223194ae42636dbc -
Trigger Event:
push
-
Statement type:
File details
Details for the file things3_blade_mcp-0.1.1-py3-none-any.whl.
File metadata
- Download URL: things3_blade_mcp-0.1.1-py3-none-any.whl
- Upload date:
- Size: 24.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0272bf999d7fc7b4acae4e1a8877ce5991fef65383e9446aa773e4dc72c418b8
|
|
| MD5 |
6a803657a6ed6b3dde69f83c4652ef42
|
|
| BLAKE2b-256 |
46f73abcec02bb285e8fcf1114a1279f47a40979d3aa8fa55ff570433f8a9332
|
Provenance
The following attestation bundles were made for things3_blade_mcp-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on Groupthink-dev/things3-blade-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
things3_blade_mcp-0.1.1-py3-none-any.whl -
Subject digest:
0272bf999d7fc7b4acae4e1a8877ce5991fef65383e9446aa773e4dc72c418b8 - Sigstore transparency entry: 1395698437
- Sigstore integration time:
-
Permalink:
Groupthink-dev/things3-blade-mcp@edad16ce4162b142fe021a96223194ae42636dbc -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Groupthink-dev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@edad16ce4162b142fe021a96223194ae42636dbc -
Trigger Event:
push
-
Statement type: