MCP server for Things 3 (Bun + bun:sqlite + Things URL scheme)
Strong foundation with 17 well-named tools, comprehensive schemas, and proper tool annotations. All tools have descriptions (avg 156 chars) and input schemas with typed properties. Read tools are well-documented; write tools include batch operations. Key gaps: output schemas are not formally documented (only inferred from handler code), parameter descriptions lack format/constraint details (e.g., UUID format, date format YYYY-MM-DD not stated in schema), and error handling lacks recovery guidance. Tool annotations correctly mark read/write/update intent. Naming follows verb_noun pattern consistently (get_*, add_*, update_*). Composition is sound, tools chain well (e.g., get_projects returns UUIDs that update_projects accepts).
Add a new project with optional nested headings and tasks. To create a project with sections and tasks, pass them all in the items array in one call.
Add one or more todos. Pass an array of 1 for a single todo; pass many to do them all in one URL call. Returns a summary of created items.
Get the Anytime list — tasks ready to do with no specific schedule.
Get all areas (top-level life buckets that contain projects and standalone tasks).
Get project headings (sub-sections), optionally scoped to one project.
Get todos from Inbox (unsorted captures with no list assigned).
Output schemas not formally documented. Handler code shows formatTodo/formatProject/etc. produce structured objects, but no JSON Schema is exposed to the LLM. LLMs cannot plan downstream calls or extract fields without knowing the response structure.
Parameter descriptions lack format constraints. 'project_uuid' and 'area_id' are described as strings but UUID format is not stated. 'start_date' and 'deadline' in search() mention YYYY-MM-DD in description but not in schema. LLMs cannot validate input without explicit format/pattern rules.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 62 | 2026-07-28+ | v2 |
Get completed and canceled tasks from the Logbook over a time period (defaults to last 7 days, max 50 items).
Get all active projects.
Get the Someday list — deferred tasks. Includes tasks living inside projects marked Someday (which the raw SQL would otherwise miss).
Get all tag definitions.
Get the Today list — scheduled for today, plus past-due scheduled tasks and past-deadline tasks. Output mirrors the Things UI grouping (by parent project/area) when that setting is enabled.
Get all todos, optionally filtered to a single project by UUID.
Get trashed todos.
Get tasks scheduled for future dates (Things' Upcoming view).
Search todos by full-text query and/or any combination of filters (status, dates, tag, area, type, creation recency). All parameters are optional; combine any. Prefer the named views (get_today, get_inbox, etc.) when one fits — they match the Things UI ordering and grouping.
Update one or more projects by UUID. Pass an array of updates; all are applied in one URL call.
Update one or more todos by UUID. Pass an array of updates; all are applied in one URL call.
Error handling lacks recovery guidance. Handlers throw generic Error() with minimal context (e.g., 'Missing required string argument: key'). No guidance on what the LLM should do next, retry, ask user, or call a discovery tool. Errors should categorize as retryable/user-fixable/fatal.
Nested object schemas in add_todos and add_project lack descriptions. The 'todos' array contains objects with properties (title, notes, when, deadline, tags, checklist_items, list_id, list, heading, heading_id) but no per-property descriptions. LLMs cannot infer which fields are required, what format 'when' accepts, or how list_id vs list differ.
No pagination or result limits documented. get_todos, get_projects, get_areas, search could return hundreds of items. No limit parameter, no cursor/offset, no total count in response. Large result sets blow context windows and degrade LLM reasoning.