Skip to content

Commit f7d4c8b

Browse files
wolfieschclaudehappy-otter
authored
Performance improvements and benchmark framework (#7)
* docs(CLAUDE.md): clarify MCP integration priority for local tools Add critical guidance for tool selection priority to prevent duplicate integrations. Emphasizes local MCP servers (Gmail, Calendar, Reminders) over Rube/Composio fallbacks. Key changes: - Add explicit tool selection priority checklist - Document when to use local MCP vs Rube/Composio - Clarify fallback scenarios for external integrations Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering> * feat(reminders): add timing instrumentation and increase timeouts Performance and reliability improvements for Reminders MCP server: - Add timing instrumentation for all API operations - Increase AppleScript timeout from 10s to 15s - Increase EventKit fetch timeout from 10s to 15s - Add [TIMING] markers for benchmark capture Timing markers enable performance profiling and help identify bottlenecks in AppleScript and EventKit async operations. Timeout increases reduce intermittent failures caused by Reminders.app latency on system load. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering> * feat(texting): add FDA-free database access with security-scoped bookmarks Major UX improvement for Messages database access without Full Disk Access: Core Features: - Security-scoped bookmark support via file picker - Lazy contacts sync with tiered approach (daemon → Rust CLI → Python) - Permission checking and guided setup - TTL-based sync caching (30min default) New Files: - db_access.py: Security-scoped bookmark manager - file_picker.py: NSOpenPanel integration for bookmark creation iMessage Client Improvements: - Auto-detect running contacts daemon - Prefer Rust CLI sync (~500ms) over Python (~700ms) - Interactive permission prompts when appropriate - Graceful fallback to legacy FDA path Messages Interface Improvements: - Bookmark-first initialization (use_bookmark=True default) - Comprehensive permission checking API - Backward compatibility with explicit path mode This eliminates the need for users to grant Full Disk Access, using Apple's security-scoped bookmark API instead. Users pick the Messages database once via file picker, bookmark is stored, and future access works without FDA. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering> * feat(gmail): add batch optimization and CLI with daemon support Major performance improvements and new CLI interface: Performance Optimizations (5x speedup): - Implement BatchHttpRequest for parallel email fetching - Eliminate N+1 query pattern (was: 1 list + N detail calls) - Add timing instrumentation for profiling - OAuth token caching with load/refresh timing New CLI Interface (gmail_cli.py): - Standalone CLI for terminal/scripting use - Daemon mode support (6.2x faster than MCP) - JSON output for automation - Operations: unread, list, search, send - Shares OAuth tokens with MCP server Performance Benchmarks: - Unread count: MCP 1,030ms → CLI+daemon 167ms (6.2x) - List 10 emails: MCP 1,180ms → CLI+daemon 318ms (3.7x) - Search: MCP 1,160ms → CLI+daemon 287ms (4.1x) Documentation: - Add performance comparison table - Document CLI vs MCP use cases - Reference google_daemon setup Use MCP for Claude Code integration, CLI+daemon for high-frequency operations and scripting. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering> * feat(calendar): add CLI with daemon support and timing instrumentation New CLI interface and performance improvements for Google Calendar: New CLI Interface (calendar_cli.py): - Standalone CLI for terminal/scripting use - Daemon mode support for faster operations - JSON output for automation - Operations: list, today, week, upcoming, find-free, create - Shares OAuth tokens with MCP server Performance Instrumentation: - Add timing context manager for profiling - Track OAuth operations (load, refresh, auth) - Track API calls for performance analysis - [TIMING] markers for benchmark capture Documentation Updates: - Add high-performance CLI section - Document daemon setup and usage - Performance comparison guidance - CLI vs MCP use case matrix Similar to Gmail integration, provides both MCP server for Claude Code integration and CLI for high-frequency scripting. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering> * feat(google-daemon): add shared daemon for Gmail and Calendar services New daemon infrastructure for high-performance Google API access: Core Daemon (google_daemon/server.py): - Shared credential and API client management - Background refresh for hot startup (<50ms) - Unix domain socket for IPC - Process lifecycle management (start/stop/status/restart) - Automatic OAuth token refresh - Graceful shutdown and error recovery Architecture: - Single daemon serves both Gmail and Calendar CLIs - Eliminates per-request OAuth overhead - Maintains warm API connections - 6.2x faster than MCP for high-frequency operations Client Integration: - Unix socket protocol for request/response - JSON-based command/response format - Timeout handling and connection retry - Shared by gmail_cli.py and calendar_cli.py Lifecycle Commands: - start: Launch daemon in background - stop: Graceful shutdown - status: Check daemon health - restart: Stop and restart daemon Testing: - Integration test suite for daemon lifecycle - Request/response validation - Error handling verification This daemon enables the performance improvements documented in Gmail and Calendar CLI tools. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering> * feat(benchmarks): add comprehensive iMessage MCP benchmark framework New benchmark infrastructure for evaluating MCP server performance: Normalized Workload Benchmarking (normalized_workload_benchmarks.py): - Real-world workload simulation (conversation history, search, etc.) - Multi-server comparison (photon, sameelarif, mcp_imessage, imcp) - Operation-level timing breakdown (parsing, execution, serialization) - Headline metrics: overall latency, server processing time - Validation of results against expected schemas - Statistical analysis and ranking Visualization Tools (visualize_benchmark_story*.py): - Generate performance comparison tables - Create comprehensive Markdown reports - Workload ranking and analysis - Tool mapping and coverage analysis - Combined result aggregation across test runs Benchmark Methodology: - Realistic workloads based on actual usage patterns - Timing instrumentation via [TIMING] markers - Client-side and server-side timing capture - Multiple iterations for statistical validity - Result validation to ensure correctness Output Formats: - JSON: Raw benchmark data with full timing breakdown - CSV: Tabular data for analysis and graphing - Markdown: Human-readable reports with tables - Summary: Aggregated statistics and rankings This framework enabled the performance optimizations documented in the iMessage gateway and identified the 19x speedup over vanilla MCP implementations. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering> * chore(benchmarks): add iMessage MCP performance benchmark results Comprehensive benchmark data documenting performance across multiple MCP server implementations and configurations: Test Configurations: - photon (custom MCP server with FastAPI) - sameelarif (community MCP server) - mcp_imessage (reference implementation) - imcp (legacy implementation) - node22 environment variants - Various timeout and validation configurations Result Files: - JSON: Raw timing data with operation-level breakdown - CSV: Tabular data (combined, server summary, tool mapping, rankings) - Markdown: Human-readable performance tables - Debug payloads: Request/response validation data Key Findings (from results): - photon achieves 19x speedup over vanilla MCP (40ms vs 763ms) - node22 timeout tuning reduces failures - Validation overhead minimal (<5ms) - Batch operations show consistent performance Benchmark Dates: January 7-8, 2026 These results informed the Gateway CLI design decision and validated the performance gains documented in README. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering> * docs(plans): add Rust MCP clients handoff documentation Planning document for implementing high-performance Rust-based MCP clients for Gmail and Calendar integrations. Objective: - Replace Python daemon with native Rust implementation - Achieve sub-100ms latency for common operations - Reduce memory footprint and startup time - Maintain compatibility with existing CLI interfaces Key Design Points: - Async Rust with tokio runtime - Unix domain socket IPC protocol - Shared OAuth token management - Hot credential caching - Graceful degradation to Python fallback Target Performance: - Gmail unread count: <80ms (current: 167ms with daemon) - Calendar list: <90ms (current: ~150ms with daemon) - Memory: <20MB resident (current: ~80MB Python) - Startup: <10ms cold, <1ms hot Next Steps: - Project structure setup - OAuth client implementation - Gmail API client - Calendar API client - Integration with existing CLIs Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering> * feat(imessage): implement Phase 3 - analytics, discovery, and groups commands Implements complete Phase 3 of Rust CLI migration with 9 new commands: Analytics commands: - analytics: Conversation statistics with 6 SQL queries (message counts, busiest hour/day, top contacts, attachments, reactions) - followup: Detect unanswered questions and stale conversations - reactions: Query tapback messages (already implemented in reading.rs) Discovery commands: - handles: List all unique phone/email handles - unknown: Find messages from non-contacts - discover: Frequent texters not in contacts - scheduled: Scheduled messages stub (not supported by Messages.db) Groups commands: - groups: List all group chats with participants - group-messages: Get messages from specific groups (by group_id or participant) All commands support both JSON and human-readable output formats. Development time: ~15 minutes across 3 sprints (3A, 3B, 3C) Build time: <2s Test coverage: Manual testing verified all commands working Phase 3 completes feature parity with Python gateway for analytics, discovery, and groups functionality. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(imessage): add Phase 4 daemon infrastructure and fix PR review comments Phase 4 Daemon Infrastructure (wolfies-imessage): - Add daemon module with NDJSON protocol over UNIX socket - Create wolfies-imessage-daemon binary (start/stop/status commands) - Create wolfies-imessage-client thin client binary (3.8MB) - Hot SQLite connection + contact cache for sub-2ms latency - Health endpoint achieving 1.2ms avg (18x faster than CLI baseline) - Phase 4A: Contact caching with Arc-based sharing - Phase 4B: Parallel queries with rayon (followup 7% faster) PR #7 Review Comment Fixes: - Add comprehensive docstring to _extract_target_from_response (Gemini HIGH) - Change broad Exception catch to specific json.JSONDecodeError (Gemini MEDIUM) - Add daemon support to calendar_cli events/get/create/delete commands (Codex P2) - Add stale pidfile safety check in google_daemon cmd_stop (Codex P2) New Dependencies: - uuid, daemonize, shellexpand, libc (Rust daemon) - rayon 1.8 (parallel queries) Performance Results: - Daemon health: 1.2ms avg (vs 22ms CLI baseline) = 18x faster - Followup command: 6.2ms (7% improvement with parallel queries) - Analytics: Hot connection ready for Phase 5 command handlers Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Wolfgang Schoenberger <221313372+wolfiesch@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Happy <yesreply@happy.engineering>
1 parent 3705952 commit f7d4c8b

File tree

140 files changed

+48519
-209
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

140 files changed

+48519
-209
lines changed

CLAUDE.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,19 @@ Default integration paths for this repo:
185185
- Configure OAuth credentials in `config/google_credentials/`
186186
- If tools fail but show “Connected,” check logs under `logs/`
187187

188-
**Rube/Composio (optional) usage:**
188+
**Tool Selection Priority (CRITICAL):**
189+
When handling requests, ALWAYS check for local MCP tools FIRST:
190+
1. **Gmail**: Use `mcp__gmail__*` tools (NOT Rube's GMAIL_* tools)
191+
2. **Calendar**: Use `mcp__google-calendar__*` tools (NOT Rube's GOOGLECALENDAR_* tools)
192+
3. **Reminders**: Use `mcp__reminders-life-planner__*` tools
193+
4. **iMessage**: Use `/wolfies-imessage` skill (Gateway CLI)
194+
195+
**Only use Rube/Composio when:**
196+
- Local MCP is not available for the service (e.g., Slack, Twitter writes)
197+
- Local MCP explicitly fails and you've confirmed it's not a transient error
198+
- User explicitly requests Rube integration
199+
200+
**Rube/Composio usage (fallback only):**
189201
1. `RUBE_SEARCH_TOOLS` to discover tools
190202
2. `RUBE_MULTI_EXECUTE_TOOL` to execute requests
191203

Plans/Rust_MCP_Clients_Handoff.md

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Rust MCP Client Refactor - Handoff Prompt
2+
3+
**For:** Fresh Claude agent
4+
**Project:** LIFE-PLANNER
5+
**Date:** 01/08/2026
6+
7+
---
8+
9+
## Context
10+
11+
We successfully built a high-performance iMessage CLI using the **Rust client + Python daemon** pattern, achieving **19x faster performance** than the original MCP-based approach (40ms vs 763ms per operation).
12+
13+
The architecture:
14+
- **Rust CLI** (`wolfies-imessage`): Fast ~3ms spawn, sends requests via Unix socket
15+
- **Python daemon** (`imessage_daemon.py`): Warm process handling iMessage operations
16+
- **Protocol**: JSON-RPC over Unix socket at `~/.wolfies-imessage/daemon.sock`
17+
18+
This is now distributed via Homebrew (`brew install wolfiesch/executive-function/wolfies-imessage`).
19+
20+
---
21+
22+
## Task: Extend Pattern to Other MCP Servers
23+
24+
Three Python MCP servers currently exist that could benefit from Rust CLI wrappers:
25+
26+
| Module | Current Location | MCP Tools |
27+
|--------|-----------------|-----------|
28+
| **Gmail** | `src/integrations/gmail/server.py` | list_emails, get_email, search_emails, send_email, get_unread_count |
29+
| **Calendar** | `src/integrations/google_calendar/server.py` | list_events, get_event, create_event, find_free_time |
30+
| **Reminders** | `Reminders/mcp_server/server.py` | create_reminder, list_reminders, complete_reminder, delete_reminder, list_reminder_lists |
31+
32+
---
33+
34+
## Reference Implementation
35+
36+
Study these files to understand the pattern:
37+
38+
### Rust Client (shared library + iMessage binary)
39+
```
40+
Texting/gateway/wolfies-client/
41+
├── Cargo.toml # Workspace definition
42+
├── crates/
43+
│ ├── wolfies-core/ # Shared library
44+
│ │ ├── src/lib.rs # Protocol, client, response types
45+
│ │ └── Cargo.toml
46+
│ └── wolfies-imessage/ # iMessage-specific binary
47+
│ ├── src/main.rs # CLI entry point
48+
│ └── Cargo.toml
49+
```
50+
51+
### Python Daemon
52+
```
53+
Texting/gateway/
54+
├── imessage_daemon.py # Unix socket server, handles JSON-RPC
55+
├── imessage_daemon_client.py # Python client for testing
56+
└── output_utils.py # Shared output formatting
57+
```
58+
59+
### Key Files to Read
60+
1. `Texting/gateway/wolfies-client/crates/wolfies-core/src/lib.rs` - Protocol definition
61+
2. `Texting/gateway/wolfies-client/crates/wolfies-imessage/src/main.rs` - CLI structure
62+
3. `Texting/gateway/imessage_daemon.py` - Daemon architecture
63+
4. `Plans/Rust_Gateway_Framework_2026-01-08.md` - Original implementation plan
64+
65+
---
66+
67+
## Benchmarking Approach
68+
69+
We used a normalized workload benchmark to compare implementations:
70+
71+
### Benchmark Script
72+
`Texting/benchmarks/normalized_workload_benchmarks.py`
73+
74+
### Workloads Tested
75+
1. **recent** - Get recent messages (light read)
76+
2. **unread** - Get unread messages (light read)
77+
3. **find** - Search messages (medium read)
78+
4. **send** - Send message (write operation)
79+
5. **contacts** - List contacts (metadata read)
80+
81+
### Metrics Captured
82+
- Cold start time (first request)
83+
- Warm request time (subsequent requests)
84+
- P50, P95, P99 latencies
85+
- Memory usage
86+
- Throughput (requests/second)
87+
88+
### Before/After Comparison
89+
Create baseline benchmarks BEFORE implementing Rust clients, then compare after.
90+
91+
---
92+
93+
## Proposed Phases
94+
95+
### Phase 1: Gmail Rust Client
96+
1. Create `wolfies-gmail` crate in workspace
97+
2. Convert `gmail/server.py` to daemon mode (Unix socket)
98+
3. Implement Rust CLI with subcommands: `list`, `search`, `read`, `send`, `unread`
99+
4. Benchmark before/after
100+
5. Update Homebrew formula
101+
102+
### Phase 2: Calendar Rust Client
103+
1. Create `wolfies-calendar` crate
104+
2. Convert `google_calendar/server.py` to daemon mode
105+
3. Implement Rust CLI: `list`, `get`, `create`, `free-time`
106+
4. Benchmark before/after
107+
5. Update Homebrew formula
108+
109+
### Phase 3: Reminders Rust Client
110+
1. Create `wolfies-reminders` crate
111+
2. Convert `Reminders/mcp_server/server.py` to daemon mode
112+
3. Implement Rust CLI: `create`, `list`, `complete`, `delete`, `lists`
113+
4. Benchmark before/after
114+
5. Update Homebrew formula
115+
116+
---
117+
118+
## Questions to Consider
119+
120+
1. **Is Rust worth it for these?** iMessage benefited because it's used for quick lookups (`unread`, `recent`). Gmail/Calendar/Reminders are primarily MCP servers for Claude Code - direct CLI use is less common. Benchmark first to see if there's a real bottleneck.
121+
122+
2. **Daemon vs Direct?** iMessage uses a daemon because the database reads are expensive to initialize. Gmail/Calendar use OAuth which has its own warm-up cost. Profile to see where time is spent.
123+
124+
3. **Shared credentials?** Gmail and Calendar could share OAuth tokens. Consider a unified Google daemon.
125+
126+
---
127+
128+
## Quick Start Commands
129+
130+
```bash
131+
# Read the iMessage implementation
132+
cat Plans/Rust_Gateway_Framework_2026-01-08.md
133+
134+
# Check the Rust workspace structure
135+
ls -la Texting/gateway/wolfies-client/crates/
136+
137+
# Read the core library
138+
cat Texting/gateway/wolfies-client/crates/wolfies-core/src/lib.rs
139+
140+
# Run existing benchmarks
141+
python3 -m Texting.benchmarks.normalized_workload_benchmarks --help
142+
143+
# Check current MCP servers
144+
cat src/integrations/gmail/server.py | head -100
145+
cat src/integrations/google_calendar/server.py | head -100
146+
cat Reminders/mcp_server/server.py | head -100
147+
```
148+
149+
---
150+
151+
## Success Criteria
152+
153+
- [ ] Baseline benchmarks for each MCP server
154+
- [ ] Rust CLI achieving <50ms for common operations
155+
- [ ] Homebrew formulas updated with Rust binaries
156+
- [ ] Before/after performance comparison documented
157+
- [ ] All existing functionality preserved
158+
159+
---
160+
161+
## Notes
162+
163+
- The Homebrew tap is at `wolfiesch/homebrew-executive-function`
164+
- Release workflow at `.github/workflows/release.yml` handles multi-module builds
165+
- Current version is v0.2.0

Reminders/mcp_server/server.py

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,52 @@
88
99
Usage:
1010
python mcp_server/server.py
11+
12+
CHANGELOG (recent first, max 5 entries):
13+
01/08/2026 - Added timing instrumentation for performance profiling (Claude)
1114
"""
1215

1316
import sys
1417
import json
1518
import logging
19+
import time
1620
from pathlib import Path
17-
from typing import Optional
21+
from typing import Optional, Any
1822

1923
# Add parent directory to path for imports
2024
sys.path.insert(0, str(Path(__file__).parent.parent))
2125

26+
27+
# =============================================================================
28+
# TIMING INSTRUMENTATION (for profiling)
29+
# =============================================================================
30+
31+
class TimingContext:
32+
"""
33+
Context manager that logs timing to stderr for benchmark capture.
34+
35+
Timing markers are in format: [TIMING] phase_name=XX.XXms
36+
These are parsed by the benchmark runner to capture server-side timing.
37+
"""
38+
39+
def __init__(self, phase_name: str):
40+
self.phase = phase_name
41+
self.start: float = 0
42+
43+
def __enter__(self) -> "TimingContext":
44+
self.start = time.perf_counter()
45+
return self
46+
47+
def __exit__(self, *args: Any) -> None:
48+
elapsed_ms = (time.perf_counter() - self.start) * 1000
49+
print(f"[TIMING] {self.phase}={elapsed_ms:.2f}ms", file=sys.stderr)
50+
51+
52+
def _timing(phase: str) -> TimingContext:
53+
"""Convenience function to create a timing context."""
54+
return TimingContext(phase)
55+
56+
2257
from mcp.server import Server
2358
from mcp.server.stdio import stdio_server
2459
from mcp import types
@@ -267,15 +302,16 @@ async def handle_call_tool(name: str, arguments: dict) -> list[types.TextContent
267302
)]
268303

269304
# Create reminder with validation
270-
result = reminder_manager.create_reminder(
271-
title=title,
272-
list_name=arguments.get("list_name"),
273-
due_date=arguments.get("due_date"),
274-
notes=arguments.get("notes"),
275-
priority=validated_priority,
276-
tags=validated_tags,
277-
recurrence=validated_recurrence
278-
)
305+
with _timing("api_create_reminder"):
306+
result = reminder_manager.create_reminder(
307+
title=title,
308+
list_name=arguments.get("list_name"),
309+
due_date=arguments.get("due_date"),
310+
notes=arguments.get("notes"),
311+
priority=validated_priority,
312+
tags=validated_tags,
313+
recurrence=validated_recurrence
314+
)
279315

280316
return [types.TextContent(
281317
type="text",
@@ -297,12 +333,13 @@ async def handle_call_tool(name: str, arguments: dict) -> list[types.TextContent
297333
)]
298334

299335
# List reminders
300-
reminders = reminders_interface.list_reminders(
301-
list_name=arguments.get("list_name"),
302-
completed=arguments.get("completed", False),
303-
limit=limit,
304-
tag_filter=arguments.get("tag_filter")
305-
)
336+
with _timing("api_list_reminders"):
337+
reminders = reminders_interface.list_reminders(
338+
list_name=arguments.get("list_name"),
339+
completed=arguments.get("completed", False),
340+
limit=limit,
341+
tag_filter=arguments.get("tag_filter")
342+
)
306343

307344
return [types.TextContent(
308345
type="text",
@@ -324,7 +361,8 @@ async def handle_call_tool(name: str, arguments: dict) -> list[types.TextContent
324361
)]
325362

326363
# Complete reminder
327-
result = reminder_manager.complete_reminder(reminder_id)
364+
with _timing("api_complete_reminder"):
365+
result = reminder_manager.complete_reminder(reminder_id)
328366

329367
return [types.TextContent(
330368
type="text",
@@ -333,7 +371,8 @@ async def handle_call_tool(name: str, arguments: dict) -> list[types.TextContent
333371

334372
elif name == "list_reminder_lists":
335373
# List all available reminder lists
336-
lists = reminders_interface.list_reminder_lists()
374+
with _timing("api_list_lists"):
375+
lists = reminders_interface.list_reminder_lists()
337376

338377
return [types.TextContent(
339378
type="text",
@@ -355,7 +394,8 @@ async def handle_call_tool(name: str, arguments: dict) -> list[types.TextContent
355394
)]
356395

357396
# Delete reminder
358-
result = reminder_manager.delete_reminder(reminder_id)
397+
with _timing("api_delete_reminder"):
398+
result = reminder_manager.delete_reminder(reminder_id)
359399

360400
return [types.TextContent(
361401
type="text",

Reminders/src/reminders_interface.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
Hybrid approach:
88
- AppleScript for creating reminders (simple, reliable)
99
- EventKit for reading, completing, deleting (robust querying)
10+
11+
CHANGELOG (recent first, max 5 entries):
12+
01/09/2026 - Increased timeouts from 10s to 15s to reduce intermittent failures (Claude)
1013
"""
1114

1215
import subprocess
@@ -309,7 +312,7 @@ def create_reminder(
309312
['osascript', '-e', script],
310313
capture_output=True,
311314
text=True,
312-
timeout=10
315+
timeout=15 # Increased from 10s to handle Reminders.app latency
313316
)
314317

315318
if result.returncode == 0:
@@ -579,7 +582,8 @@ def fetch_callback(reminders):
579582
)
580583

581584
# Wait for callback to complete (with timeout)
582-
fetch_complete.wait(timeout=10.0)
585+
# Increased from 10s to 15s to handle EventKit async latency
586+
fetch_complete.wait(timeout=15.0)
583587

584588
if not fetch_complete.is_set():
585589
logger.error("EventKit fetch timed out")

0 commit comments

Comments
 (0)