Temporal Cortex MCP
The complete temporal stack for AI agents. Context. Computation. Calendars. Correct.
The Problem
LLMs get date and time tasks wrong roughly 60% of the time (AuthenHallu benchmark). Ask "What time is it?" and the model hallucinates. Ask "Schedule for next Tuesday at 2pm" and it picks the wrong Tuesday. Ask "Am I free at 3pm?" and it checks the wrong timezone. Then it double-books your calendar.
Every other Calendar MCP server is a thin CRUD wrapper that passes these failures through to Google Calendar — no temporal awareness, no conflict detection, no safety net.
What's Different
- Temporal awareness — Agents call
get_temporal_contextto know the actual time and timezone.resolve_datetimeturns"next Tuesday at 2pm"into a precise RFC 3339 timestamp. No hallucination. - Atomic booking — Lock the time slot, verify no conflicts exist, then write. Two agents booking the same 2pm slot? Exactly one succeeds. The other gets a clear error. No double-bookings.
- Computed availability — Merges free/busy data across multiple calendars into a single unified view. The AI sees actual availability, not a raw dump of events to misinterpret.
- Deterministic RRULE expansion — Handles DST transitions,
BYSETPOS=-1(last weekday of month),EXDATEwith timezones, leap year recurrences, andINTERVAL>1withBYDAY. Powered by Truth Engine, not LLM inference. - Token-efficient output — TOON format compresses calendar data to ~40% fewer tokens than standard JSON, reducing costs and context window usage.
Prerequisites
- Node.js 18+ (for
npxto download and run the binary) - Google account with Google Calendar
- Google OAuth credentials — setup guide
Quick Start
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"temporal-cortex": {
"command": "npx",
"args": ["-y", "@temporal-cortex/cortex-mcp"],
"env": {
"GOOGLE_CLIENT_ID": "your-client-id.apps.googleusercontent.com",
"GOOGLE_CLIENT_SECRET": "your-client-secret",
"TIMEZONE": "America/New_York"
}
}
}
}
Cursor
Add to Cursor's MCP settings (~/.cursor/mcp.json):
{
"mcpServers": {
"temporal-cortex": {
"command": "npx",
"args": ["-y", "@temporal-cortex/cortex-mcp"],
"env": {
"GOOGLE_CLIENT_ID": "your-client-id.apps.googleusercontent.com",
"GOOGLE_CLIENT_SECRET": "your-client-secret",
"TIMEZONE": "America/New_York"
}
}
}
}
Windsurf
Add to Windsurf's MCP config (~/.codeium/windsurf/mcp_config.json):
{
"mcpServers": {
"temporal-cortex": {
"command": "npx",
"args": ["-y", "@temporal-cortex/cortex-mcp"],
"env": {
"GOOGLE_CLIENT_ID": "your-client-id.apps.googleusercontent.com",
"GOOGLE_CLIENT_SECRET": "your-client-secret",
"TIMEZONE": "America/New_York"
}
}
}
}
Need Google OAuth credentials? See docs/google-cloud-setup.md for a step-by-step guide.
First-Time Setup
On first run, the server needs Google Calendar access. You have two options:
Option A — Run auth before connecting:
npx @temporal-cortex/cortex-mcp auth
This opens your browser for Google OAuth consent. After authorizing, credentials are saved to ~/.config/temporal-cortex/credentials.json and reused automatically.
Option B — The server prompts automatically when an MCP client connects and no credentials are found.
During auth, the server detects your system timezone and prompts you to confirm or override it. This is stored in ~/.config/temporal-cortex/config.json and used by all temporal tools. You can override it per-session with the TIMEZONE env var, or per-call via the timezone parameter.
After authentication, verify it works by asking your AI assistant: "What time is it?" — the agent should call get_temporal_context and return your current local time.
Available Tools (11)
Layer 1 — Temporal Context
| Tool | Description |
|---|---|
get_temporal_context |
Current time, timezone, UTC offset, DST status, day of week. Call this first. |
resolve_datetime |
Resolve human expressions ("next Tuesday at 2pm", "tomorrow morning", "+2h") to RFC 3339. |
convert_timezone |
Convert any RFC 3339 datetime between IANA timezones. |
compute_duration |
Duration between two timestamps (days, hours, minutes, human-readable). |
adjust_timestamp |
DST-aware timestamp adjustment ("+1d" across spring-forward = same wall-clock). |
Layer 2 — Calendar Operations
| Tool | Description |
|---|---|
list_events |
List calendar events in a time range. Output in TOON (~40% fewer tokens) or JSON. |
find_free_slots |
Find available time slots in a calendar. Computes actual gaps between events. |
expand_rrule |
Expand recurrence rules into concrete instances. Handles DST, BYSETPOS, leap years. |
check_availability |
Check if a specific time slot is available against events and active locks. |
Layer 3 — Availability
| Tool | Description |
|---|---|
get_availability |
Merged free/busy view across multiple calendars with privacy controls. |
Layer 4 — Booking
| Tool | Description |
|---|---|
book_slot |
Book a calendar slot safely. Lock → verify → write with Two-Phase Commit. |
See docs/tools.md for full input/output schemas and usage examples.
The RRULE Challenge
Most AI models and calendar tools silently fail on recurrence rule edge cases. Run the challenge to see the difference:
npx @temporal-cortex/cortex-mcp rrule-challenge
5 cases where LLMs consistently fail
1. "Third Tuesday of every month" across DST (March 2026, America/New_York)
The third Tuesday is March 17. Spring-forward on March 8 shifts UTC offsets from -05:00 to -04:00. LLMs often produce the wrong UTC time or skip the month entirely.
2. "Last Friday of every month" (BYSETPOS=-1)
RRULE:FREQ=MONTHLY;BYDAY=FR;BYSETPOS=-1 — LLMs frequently return the first Friday instead of the last, or fail to handle months with 4 vs 5 Fridays.
3. "Every weekday except holidays" (EXDATE with timezone)
EXDATE values with explicit timezone offsets require exact matching against generated instances. LLMs often ignore EXDATE entirely or apply it to the wrong date.
4. "Biweekly on Monday, Wednesday, Friday" (INTERVAL=2 + BYDAY)
RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE,FR — The INTERVAL=2 applies to weeks, not individual days. LLMs frequently generate every-week occurrences instead of every-other-week.
5. "February 29 yearly" (leap year recurrence)
RRULE:FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=29 — Should only produce instances in leap years (2028, 2032...). LLMs often generate Feb 28 or Mar 1 in non-leap years.
Truth Engine handles all of these deterministically using the RFC 5545 specification. No inference, no hallucination.
How It Works
The MCP server is a single Rust binary distributed via npm. It runs locally on your machine and communicates with MCP clients over stdio (standard input/output) or streamable HTTP.
- Truth Engine handles all date/time computation: temporal resolution, timezone conversion, RRULE expansion, availability merging, conflict detection. Deterministic, not inference-based.
- TOON (Token-Oriented Object Notation) compresses calendar data for LLM consumption — fewer tokens, same information.
- Two-Phase Commit ensures booking safety: acquire lock, verify the slot is free, write the event, release lock. If any step fails, everything rolls back.
Stdio vs HTTP Transport
Transport mode is auto-detected — set HTTP_PORT to switch from stdio to HTTP.
- Stdio (default): Standard MCP transport for local clients (Claude Desktop, VS Code, Cursor). The server reads/writes JSON-RPC messages over stdin/stdout.
- HTTP (when
HTTP_PORTis set): Streamable HTTP transport per MCP 2025-11-25 spec. The server listens onhttp://{HTTP_HOST}:{HTTP_PORT}/mcpwith SSE streaming, session management (Mcp-Session-Idheader), and Origin validation. Requests with an invalidOriginheader are rejected with HTTP 403.
# HTTP mode example
HTTP_PORT=8009 npx @temporal-cortex/cortex-mcp
Lite Mode vs Full Mode
Mode is auto-detected — there is no configuration flag.
- Lite Mode (default): No infrastructure required. Uses in-memory locking and local file credential storage. Designed for individual developers with a single Google Calendar account.
- Full Mode (activated when
REDIS_URLSis set): Uses Redis-based distributed locking (Redlock) for multi-process safety. Designed for production deployments with multiple concurrent agents.
Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
GOOGLE_CLIENT_ID |
Yes* | — | Google OAuth Client ID from Cloud Console |
GOOGLE_CLIENT_SECRET |
Yes* | — | Google OAuth Client Secret |
GOOGLE_OAUTH_CREDENTIALS |
No | — | Path to Google OAuth JSON credentials file (alternative to CLIENT_ID + CLIENT_SECRET) |
TIMEZONE |
No | auto-detected | IANA timezone override (e.g., America/New_York). Overrides stored config and OS detection. |
REDIS_URLS |
No | — | Comma-separated Redis URLs. When set, activates Full Mode with distributed locking. |
TENANT_ID |
No | auto-generated | UUID for tenant isolation |
LOCK_TTL_SECS |
No | 30 |
Lock time-to-live in seconds |
OAUTH_REDIRECT_PORT |
No | 8085 |
Port for the local OAuth callback server |
HTTP_PORT |
No | — | Port for HTTP transport. When set, enables streamable HTTP mode instead of stdio. |
HTTP_HOST |
No | 127.0.0.1 |
Bind address for HTTP transport. Use 0.0.0.0 only behind a reverse proxy. |
ALLOWED_ORIGINS |
No | — | Comma-separated allowed Origin headers for HTTP mode (e.g., http://localhost:3000). All cross-origin requests rejected if unset. |
* Either GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET, or GOOGLE_OAUTH_CREDENTIALS must be set.
See docs/google-cloud-setup.md for a complete setup guide.
Troubleshooting
| Problem | Solution |
|---|---|
| "No credentials found" | Run npx @temporal-cortex/cortex-mcp auth to authenticate with Google |
| OAuth error / "Access blocked" | Verify GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are correct. Ensure the OAuth consent screen is configured in Google Cloud Console. |
| Port 8085 already in use | Set OAUTH_REDIRECT_PORT to a different port (e.g., 8086) |
| Server not appearing in MCP client | Ensure Node.js 18+ is installed (node --version). Check your MCP client's logs for errors. |
See docs/google-cloud-setup.md for detailed OAuth troubleshooting.
Going to Production?
The MCP server in Lite Mode handles single-provider computation with conflict prevention for individual use. For teams and production deployments:
- Multi-provider unification — Google + Outlook + CalDAV (iCloud, Fastmail) in one query
- Distributed Two-Phase Commit — Redis Redlock quorum for multi-process, multi-host safety
- Enterprise OAuth management — HashiCorp Vault integration, per-tenant credential isolation
- Usage metering — Per-operation billing with daily aggregation
| Operation | Price |
|---|---|
| Calendar read | $0.001 |
| Availability check | $0.002 |
| Booking (with 2PC safety) | $0.01 |
| Connected account | $0.50/mo |
| Free tier | 100 bookings/mo + 5 accounts |
No credit card required.
Comparison with Alternatives
| Feature | temporal-cortex-mcp | temporal-awareness-mcp | google-calendar-mcp (nspady) | calendar-mcp (rauf543) |
|---|---|---|---|---|
| Temporal context (time/timezone) | Yes (5 tools) | Yes (1 tool) | No | No |
| Human expression parsing | Yes ("next Tuesday at 2pm") |
No | No | No |
| Double-booking prevention (2PC) | Yes | No | No | No |
| Deterministic RRULE expansion | Yes | No | No | Partial |
| Multi-calendar availability merge | Yes | No | No | No |
| Prompt injection firewall | Yes | No | No | No |
| TOON token compression | Yes | No | No | No |
| Multi-provider (Google + Outlook) | Yes | No | No | Yes |
| Price | Free (Lite) | Free | Free | Free |
Built on Temporal Cortex Core
The computation layer is open source:
- temporal-cortex-core — Truth Engine (temporal resolution, RRULE expansion, availability merging, timezone conversion) + TOON (token compression)
- Available on crates.io, npm, and PyPI
- 510+ Rust tests, 42 JS tests, 30 Python tests, ~9,000 property-based tests
Contributing
Bug reports and feature requests are welcome. See CONTRIBUTING.md.
License
MIT