All notable changes to the Things 3 MCP Server will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Upgraded things.py to v1.0.0 to fix SQLite lock contention with Things 3
- things.py 0.x held open SQLite connections via the
things3module, blocking Things 3's WAL commits during cloud sync - things.py 1.0.0 uses read-only connections with proper cleanup via
weakref.finalize() - Resolves intermittent Things 3 UI freezes caused by
btreeInvokeBusyHandlerblocking on sync commits
- things.py 0.x held open SQLite connections via the
- Fixed Claude API JSON schema error - Removed
Uniontypes from tool return annotations- Claude API rejects schemas with
oneOf/allOf/anyOfat top level - FastMCP generates
oneOffromUnion[...]return type annotations - Changed 5 tools (
get_inbox,get_today,get_upcoming,get_anytime,get_someday) fromUnion[List[Dict], Dict]toDict[str, Any] - These functions already wrap responses via
context_manager.optimize_response(), so the type annotation now matches actual behavior
- Claude API rejects schemas with
- Consolidated
get_upcomingAPI - Added optionaldaysparameter toget_upcomingget_upcoming()- Returns items from Things 3's Upcoming list (unchanged)get_upcoming(days=30)- Returns todos due/activating within 30 days (new)- Removed redundant
get_upcoming_in_daystool - useget_upcoming(days=N)instead - Simpler, more intuitive API with one tool instead of two
- Fixed validation error -
get_upcoming(days=30)now works correctly- Previously failed with "Unexpected keyword argument" error
- Checklist API improvement - Changed checklist parameters from newline-delimited strings to List[str]
add_todo(checklist_items=...)now acceptsList[str]instead ofstradd_checklist_items(items=...)now acceptsList[str]instead ofstrprepend_checklist_items(items=...)now acceptsList[str]instead ofstrreplace_checklist_items(items=...)now acceptsList[str]instead ofstr- More idiomatic API design - pass lists directly instead of manually joining with newlines
- Internal conversion to URL scheme format happens transparently
- Updated CLAUDE.md with List[str] examples for all checklist operations
- Updated test examples to use list format
- Checklist item support - Full support for creating and managing checklist items via Things URL scheme
add_todo()automatically uses URL scheme whenchecklist_itemsparameter is provided- New
add_checklist_items()tool to append items to existing todo checklists - New
prepend_checklist_items()tool to prepend items to existing todo checklists - New
replace_checklist_items()tool to replace all checklist items - Checklist items returned in todo queries with status (complete/incomplete)
- Maximum 100 checklist items per todo
- Smart hybrid approach -
add_todo()now automatically selects optimal creation method- Uses Things URL scheme when checklist items are provided (only way to create checklists)
- Uses AppleScript for non-checklist todos (faster, more reliable)
- No API changes required - transparent to users
- Removed checklist limitation - Previous limitation documented in CLAUDE.md is now resolved
- Checklists were not supported via AppleScript (Things 3 API limitation)
- Now fully supported via Things URL scheme integration
- Added comprehensive checklist usage examples to CLAUDE.md
- Added checklist architecture documentation to ARCHITECTURE.md
- Updated known limitations section (checklist support now complete)
- CRITICAL: Project initial todos not retrieved - Fixed parser consuming multiple records as single field value
- AppleScript parser now correctly handles "missing value" in date fields
- Prevents field bleeding when date values are missing
- All initial todos now properly returned by get_todos(project_uuid=...)
- HIGH: Summary mode empty preview - Fixed preview showing null IDs and empty names
- Updated to check both uuid/id and title/name dictionary keys
- Summary mode now displays actual todo/project information
- Backwards compatible with different data schemas
- Bug fix: NoneType error in mode='standard' - Fixed crash when notes field is None
- Added null check before len() operation in context_manager.py
- Affected get_todos() and other operations using standard response mode
- Bug fix: Date field formatting - Fixed §COLON§ markers and field bleeding in dates
- Added comma escaping to AppleScript date formatting
- Prevents date values from breaking field boundaries
- Affects creation_date, modification_date, activation_date, due_date fields
- Documentation: add_project todos parameter - Corrected CLAUDE.md to reflect that todos parameter works correctly
- Removed incorrect "Known Limitation" entry
- Added usage examples and best practices
- Test infrastructure - Added comprehensive integration and unit test suites
- 17 integration tests with automatic cleanup mechanism
- 27 new unit tests for date utilities and edge cases
- Test fixtures and shared test data
- Integration test documentation and verification tools
- NEW: State machine AppleScript parser - Default parser changed from legacy string manipulation to state machine (BREAKING: fixes bugs)
- New state machine parser is now the default (
use_new_applescript_parser=True) - Legacy parser deprecated with warning messages
- Set
use_new_applescript_parser=Falseto temporarily use legacy parser - Legacy parser will be removed in v2.0.0
- New state machine parser is now the default (
- Bug fix: completion_date parsing - New parser correctly handles completion_date with commas
- Legacy parser left §COMMA§ placeholders (bug)
- New parser correctly parses dates: "Monday, January 15, 2024 at 2:30:00 PM"
- Bug fix: cancellation_date parsing - New parser correctly handles cancellation_date with commas
- Same §COMMA§ placeholder bug fixed
- Dates now properly parsed to ISO format
- Bug fix: Date validation - Added validation for when/deadline parameters across all operations
- Validates dates before sending to AppleScript, preventing silent failures
- Supports relative dates (today, tomorrow, someday) and absolute dates (YYYY-MM-DD)
- Applied to add_todo, update_todo, bulk_update_todos, add_project, update_project
- Bug fix: Status parameter normalization - Handle MCP passing string "None" for status parameter
- MCP clients may pass status="None" as a string instead of null
- Now correctly normalizes to None for get_todos and other operations
- Bug fix: Parameter sanitization - Filter out None values from sanitized parameters
- Prevents None values from being included in validated parameter dictionaries
- Improves reliability of bulk operations and tag handling
- Feature flag: use_new_applescript_parser - Configuration option to control parser selection
- Default: true (new state machine parser)
- Set to false for legacy behavior (deprecated)
- State machine parser implementation - Clean room implementation with proper state machine
- Handles quoted strings with commas and colons correctly
- Handles nested lists with braces properly
- Intelligent date field parsing
- No placeholder workarounds needed
- Comprehensive parser tests - 62 new test cases added
- 44 unit tests for state machine parser
- 18 integration tests comparing old vs new parsers
- All tests validate parser equivalence
- Performance: Optimized search operations - 10-100x faster using things.py instead of AppleScript
- get_due_in_days now uses database queries for instant results
- get_activating_in_days optimized with direct database access
- search_advanced now searches entire database including project todos (previously limited to lists only)
- Legacy string manipulation parser - Will be removed in v2.0.0
- Warning logged on initialization if legacy parser is used
- Known bugs: completion_date and cancellation_date field parsing
- Recommend setting
use_new_applescript_parser=True
- THINGS_MCP_SERVER_VERSION environment variable - Removed unused configuration option
- Version is now automatically managed from package metadata (
__version__in__init__.py) - No need for manual version configuration
- Updated README.md to remove this configuration example
- Version is now automatically managed from package metadata (
- Release process - Added comprehensive release process documentation to CLAUDE.md
- Step-by-step guide for version updates across all files
- Git tagging and GitHub release creation instructions
- PyPI publishing workflow
- Release checklist to ensure consistency
- Version consistency notes explaining where versions live
- Version reporting - Server now correctly reports actual package version (was hardcoded to "2.0")
- Added
__version__variable tosrc/things_mcp/__init__.py - Updated
get_server_capabilities()to use dynamic__version__instead of hardcoded string - When AI asks "what version is running?", it now correctly reports 1.2.6 instead of 2.0
- Version is automatically synced with pyproject.toml
- Added
- Version management - Single source of truth for version number
__version__in package init.py- Imported by server.py for runtime reporting
- Ensures pyproject.toml and runtime version always match
- Critical: bulk_update_todos tag handling - Added extra defensive code to handle edge case where tags parameter might be passed as string instead of list
- If tags is a string, it's now automatically split by comma before processing
- Prevents individual characters from being treated as separate tags
- Fixes AppleScript error: "Can't make {"E", "v", "a", ...} into type text" (-1700)
- Added comprehensive unit tests to verify the fix
- BUG FIX #8: This adds an extra safety layer on top of server.py's string-to-list conversion
- Test coverage - Added
test_bulk_update_tags_string_bug.pywith 3 test cases- Test single-tag string handling without splitting into characters
- Test comma-separated tag string splitting
- Test list format handling (correct format)
- USER_EXAMPLES.md complete rewrite - Comprehensive tested workflows (935 lines)
- All examples verified with actual Things 3 MCP server operations
- GTD-focused workflows: inbox processing, weekly review, context switching
- Document/email parsing examples with real action item extraction
- Bulk operations: quarterly cleanup, quick wins sprints, multi-field updates
- Smart queries: stalled work detection, deadline dashboards, tag-based filtering
- Advanced automation: meeting preparation, time-blocked planning, energy-based scheduling
- Progressive learning path from simple to power user workflows
- Generic, non-personal data used throughout all examples
- Includes exact MCP function calls with parameters and expected results
- 15 major workflow categories with copy-paste conversation starters
- Troubleshooting guide and best practices for mode parameters
- Creative use cases: reading challenges, learning paths, habit tracking
- Test artifacts in .gitignore - Added pytest.log, *.log, htmlcov/, .pytest_cache/
- Prevents test logs and coverage reports from being committed
- Cleaner git status for development workflow
- Status filtering enhancements - Improved
get_todos()status parameter handling- Fixed status filtering logic to properly use AppleScript status property values
- Automatically includes Logbook when searching for completed or canceled todos
- Properly maps between MCP status values ('incomplete', 'completed', 'canceled') and AppleScript ('open', 'completed', 'canceled')
- Project todo assignment - Fixed
list_idparameter handling inadd_todo()- Now correctly uses
project id "UUID"syntax to assign todos to projects - Handles both
projectandlist_idparameters for backward compatibility
- Now correctly uses
- Project query reliability - Implemented hybrid approach for project-filtered queries
- Uses AppleScript for project queries to avoid SQLite database sync timing issues
- Ensures queries return immediately accurate results after AppleScript writes
- Falls back to things.py database queries when AppleScript unavailable
- Enhanced test coverage - Added 4 comprehensive unit test suites
test_tag_management_comprehensive.py- 29 tests for all tag operationstest_status_filter.py- Tests for status filtering edge casestest_search_advanced_status_filter.py- Advanced search status teststest_delete_validation.py- Delete operation validation tests- All tests passing (327 total unit tests)
- CLAUDE.md enhancements - Comprehensive updates to AI assistant instructions
- Added detailed status filtering documentation with examples
- Documented project/area hierarchical organization best practices
- Enhanced common pitfalls section with tag management guides
- Added multi-field bulk update usage examples
- Repository cleanup - Removed 87 temporary analysis and test report files
- Cleaned up docs/ directory (removed temporary FIX_STRATEGY files)
- Removed diagnostic test scripts and log files
- Improved repository organization and maintainability
- Status parameter now defaults to 'incomplete' for
get_todos()(explicit default) - Project queries optimized for real-time accuracy using application state
- Improved error messages for validation failures
- Tag concatenation bug - Fixed critical bug where multi-tag operations concatenated tags into single malformed tag (#5)
add_tags(),remove_tags(), andbulk_update_todos()now properly handle comma-separated tags- Changed from AppleScript list syntax to comma-separated string format per Things 3 API requirements
- Example:
add_tags(tags="test,urgent,High")now creates 3 separate tags instead of "testurgentHigh"
- Bulk update multi-field support - Fixed bug where only last field was applied in multi-field updates
bulk_update_todos()now correctly applies all specified fields (tags, when, deadline, notes, etc.)- Enhanced with separate scheduling via reliable_scheduler to prevent field conflicts
- Zero limit handling - Fixed search operations returning all results when
limit=0- Now correctly returns empty list when
limit=0is specified - Added explicit zero-check validation in search operations
- Now correctly returns empty list when
- Empty result handling - Fixed inconsistent empty result behavior in time-based queries
get_todos_due_in_days(),get_todos_activating_in_days(),get_recent()now consistently return empty lists- Added informative logging for empty result scenarios
- Status update parameters - Fixed string boolean parameter handling in todo updates
update_todo()now accepts both string ("true"/"false") and boolean (True/False) parameters- Added
_convert_to_boolean()helper method for comprehensive type conversion - Supports case-insensitive string values: "true", "True", "TRUE", etc.
- Comprehensive parameter validation layer - New
parameter_validator.pymodule- Validates limit, offset, days, status, dates, periods, tags, and more
- Standardized error responses with field-specific validation messages
- Type conversion for flexible parameter handling
- 76 unit tests covering all validation methods
- Enhanced test coverage - 14 new regression tests for bug fixes
- 6 tests for tag removal string parsing (
TestRemoveTags) - 8 tests for bulk update multi-field operations (
TestBulkUpdateTodos) - 11 tests for empty result handling (
tests/unit/test_empty_results.py) - All tests passing (100% success rate)
- 6 tests for tag removal string parsing (
- Debug logging enhancements - Added detailed logging for edge cases
- Zero limit scenarios
- Empty result detection
- Boolean parameter conversion
- AppleScript generation for troubleshooting
- Test pass rate improved - From 92% (46/50) to 100% (50/50) after bug fixes
- Quality score increased - From 90% to 95% (production-ready)
- Tag operation architecture - Complete rewrite of tag handling pattern
- All tag operations now use comma-separated string format
- Hybrid approach: Parse in Python, set as string in AppleScript
- Improved reliability and consistency across all tag operations
- Updated CLAUDE.md with comprehensive bug fix documentation
- Tag management best practices and pitfalls
- Bulk operation multi-field usage examples
- Common error scenarios and solutions
- Added detailed inline code comments explaining AppleScript API quirks
- Enhanced validation documentation with usage examples
- Tag concatenation bug in
add_tagsfunction (#5) - Tags now properly joined with commas instead of being concatenated
- Bulk update functionality for efficient batch operations
bulk_update_todos()method for updating multiple todos at oncebulk_move_records()method for moving multiple todos efficiently
- Improved context management for large operations
- Enhanced response optimization modes
- Fixed deadline property name in Things 3 AppleScript API (#4)
- Corrected property name from
due_datetodeadlinein AppleScript commands
- Missing dateparser dependency in requirements
- Updated README with correct PyPI vs source installation instructions
- Clarified configuration documentation
- Tag validation and simplified codebase architecture (#3)
- Improved error handling for tag operations
- Context-aware response optimization
- Progressive disclosure modes (auto/summary/minimal/standard/detailed/raw)
- Smart limiting for search operations
- Date validation bug in scheduling operations
- Initial public release
- MCP server implementation for Things 3
- Hybrid architecture (things.py for reads, AppleScript for writes)
- Support for todos, projects, areas, tags
- Comprehensive test suite
This release resolves critical bugs discovered during comprehensive edge case testing, improving reliability and production readiness.
-
Tag Concatenation Bug (CRITICAL)
- Severity: HIGH - Data corruption in tag management
- Impact: Multi-tag operations created single malformed tags
- Resolution: Complete rewrite of tag operations using comma-separated strings
- Files Modified:
src/things_mcp/tools.py(3 functions) - Tests Added: 6 regression tests in
TestRemoveTags
-
Bulk Update Multi-Field Bug (CRITICAL)
- Severity: HIGH - Only last field applied in batch operations
- Impact: Multi-field bulk updates failed silently
- Resolution: Enhanced architecture with separate scheduling handling
- Files Modified:
src/things_mcp/tools.py(bulk_update_todos) - Tests Added: 8 regression tests in
TestBulkUpdateTodos
-
Zero Limit Bug (MEDIUM)
- Severity: MEDIUM - Edge case in search operations
- Impact:
limit=0returned all results instead of empty list - Resolution: Added explicit zero validation
- Location:
src/things_mcp/tools.py:266-268 - Test:
test_zero_limitnow passing
-
Empty Result Handling Bug (MEDIUM)
- Severity: MEDIUM - Inconsistent behavior in time queries
- Impact: 3 functions returned unpredictable values for empty results
- Resolution: Added empty list validation with logging
- Location:
src/things_mcp/pure_applescript_scheduler.py(3 functions) - Tests Added: 11 tests in
test_empty_results.py
-
Status Update Bug (HIGH)
- Severity: HIGH - Core functionality broken
- Impact: Could not complete/cancel todos via API
- Resolution: Added
_convert_to_boolean()with comprehensive type conversion - Location:
src/things_mcp/pure_applescript_scheduler.py:275-311 - Tests:
test_complete_todo,test_cancel_todonow passing
- Before: 46/50 tests passing (92%)
- After: 50/50 tests passing (100%) ✅
- Before: 90% (Production-ready after fixes)
- After: 95% (Production-ready) ✅
src/things_mcp/tools.py- Tag operations, zero limit, bulk updatesrc/things_mcp/pure_applescript_scheduler.py- Empty results, boolean conversionsrc/things_mcp/parameter_validator.py- New validation layer (295 lines)tests/unit/test_tools.py- 14 new regression teststests/unit/test_empty_results.py- 11 new tests for empty result handlingtests/unit/test_parameter_validator.py- 76 validation testsCLAUDE.md- Updated with bug fix documentation and best practices
None - All fixes maintain backward compatibility with existing API.
No migration needed - all bug fixes are transparent to existing code.
- Tag operations: Slight increase (< 0.2s per operation) due to additional AppleScript call
- Validation layer: Negligible overhead (< 1ms per operation)
- Overall: No noticeable performance degradation
- Update immediately - This release fixes critical data corruption bugs
- Verify existing tags - Check for any concatenated tags (e.g., "testurgentHigh")
- Test multi-field bulk updates - Ensure all fields are being applied as expected
- Review status updates - Verify completed/canceled operations work as expected
- Project
todosparameter still non-functional (create project first, then add todos separately) - Project content queries via
get_todos(project_uuid=...)have known issues (usesearch_todos()instead)
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: ebowman@boboco.ie