From 24002db4989bb6de50c7ebb8735d96641a3da011 Mon Sep 17 00:00:00 2001 From: Gregor Schmidlin Date: Fri, 6 Mar 2026 16:25:15 +0100 Subject: [PATCH 1/3] fix(mcp): convert bare YYYYMMDD dates to UTC datetime for CalDAV + delete corrupted state files - toICalDateTime(): bare 8-digit date (e.g. 20260307) is now expanded to full UTC datetime (20260307T000000Z) required by Sabre/DAV , preventing HTTP 500 from list_events when from/to are date-only strings - loadJson(): after logging a parse-error warning, unlink the corrupted file so subsequent restarts see ENOENT (silent) instead of repeating the warning Co-Authored-By: Claude Sonnet 4.6 --- mcp-server/src/auth/store.ts | 5 +++++ mcp-server/src/tools/apps/calendar.ts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/mcp-server/src/auth/store.ts b/mcp-server/src/auth/store.ts index 1efe3d7..befbb40 100644 --- a/mcp-server/src/auth/store.ts +++ b/mcp-server/src/auth/store.ts @@ -45,6 +45,11 @@ function loadJson(filePath: string): T | null { } catch (err: unknown) { if ((err as NodeJS.ErrnoException).code !== 'ENOENT') { logger.warn({ file: filePath, err }, '[state] Failed to load state file — starting fresh'); + try { + unlinkSync(filePath); + } catch { + /* ignore */ + } } return null; } diff --git a/mcp-server/src/tools/apps/calendar.ts b/mcp-server/src/tools/apps/calendar.ts index 6a481a1..565bb2b 100644 --- a/mcp-server/src/tools/apps/calendar.ts +++ b/mcp-server/src/tools/apps/calendar.ts @@ -118,6 +118,11 @@ function icalNow(): string { function toICalDateTime(dateStr: string): string { // Already in iCal format if (/^\d{8}(T\d{6}Z?)?$/.test(dateStr)) { + if (dateStr.length === 8) { + // Parse as local-midnight date, then convert to UTC for CalDAV time-range + const d = new Date(+dateStr.slice(0, 4), +dateStr.slice(4, 6) - 1, +dateStr.slice(6, 8)); + return d.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z'; + } return dateStr; } // ISO format -> iCal UTC From 93d6f503e3e7a52acefb5976d611aa4ac301d2c2 Mon Sep 17 00:00:00 2001 From: Gregor Schmidlin Date: Fri, 6 Mar 2026 16:32:26 +0100 Subject: [PATCH 2/3] feat(mcp): add get_local_time tool for timezone-aware scheduling Gives the model a reliable time anchor before creating or listing calendar events, and to resolve scheduling ambiguities with the user. Co-Authored-By: Claude Sonnet 4.6 --- mcp-server/src/tools/system/status.ts | 42 ++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/mcp-server/src/tools/system/status.ts b/mcp-server/src/tools/system/status.ts index 7c89a90..af5e841 100644 --- a/mcp-server/src/tools/system/status.ts +++ b/mcp-server/src/tools/system/status.ts @@ -98,7 +98,47 @@ export const setupChecksTool = { }, }; +/** + * Get the current local time and timezone of the MCP server + */ +export const getLocalTimeTool = { + name: 'get_local_time', + description: + 'Get the current local time and timezone of the MCP server. ' + + 'Call this to establish a time reference before creating or listing calendar events, ' + + 'or to resolve scheduling ambiguities with the user.', + inputSchema: z.object({}), + handler: async () => { + const now = new Date(); + const tz = Intl.DateTimeFormat().resolvedOptions().timeZone; + const offsetMin = now.getTimezoneOffset(); // positive = behind UTC, negative = ahead + const sign = offsetMin <= 0 ? '+' : '-'; + const abs = Math.abs(offsetMin); + const h = String(Math.floor(abs / 60)).padStart(2, '0'); + const m = String(abs % 60).padStart(2, '0'); + const utcOffset = `${sign}${h}:${m}`; + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify( + { + localTime: now.toLocaleString('sv-SE', { timeZone: tz }), // "2026-03-06 14:32:00" + utcTime: now.toISOString(), + timezone: tz, // e.g. "Europe/Berlin" + utcOffset, // e.g. "+01:00" + }, + null, + 2 + ), + }, + ], + }; + }, +}; + /** * Export all System Status tools */ -export const statusTools = [systemStatusTool, setupChecksTool]; +export const statusTools = [systemStatusTool, setupChecksTool, getLocalTimeTool]; From 7b33839c8cf2007527457a50a84fc4e0b9ccb6f1 Mon Sep 17 00:00:00 2001 From: Gregor Schmidlin Date: Fri, 6 Mar 2026 16:37:46 +0100 Subject: [PATCH 3/3] fix(mcp): use system locale by default in get_local_time, add MCP_LOCALE override Replace hardcoded 'sv-SE' locale with process.env.MCP_LOCALE so localTime uses the system locale when unset. Users can set MCP_LOCALE=sv-SE to restore the previous ISO-like format. Documents the new env var in CLAUDE.md. Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 1 + mcp-server/src/tools/system/status.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index dcd2bd7..16b01cf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -80,6 +80,7 @@ See `docs/dev/mcp-server-architecture.md` for full architecture details. | `MCP_AUTH_SECRET` | If auth | `openssl rand -hex 32` | | `MCP_AUTH_ISSUER` | If auth | public HTTPS URL of this server | | `MCP_AUTH_STATE_DIR` | No | Directory for persisted OAuth state (default `/app/state`) | +| `MCP_LOCALE` | No | BCP 47 locale tag for `localTime` in `get_local_time` (e.g. `sv-SE`); defaults to system locale | | `LOG_LEVEL` | No | `trace`/`debug`/`info`/`warn`/`error`/`fatal` | ## Tests diff --git a/mcp-server/src/tools/system/status.ts b/mcp-server/src/tools/system/status.ts index af5e841..fdd3323 100644 --- a/mcp-server/src/tools/system/status.ts +++ b/mcp-server/src/tools/system/status.ts @@ -124,7 +124,7 @@ export const getLocalTimeTool = { type: 'text' as const, text: JSON.stringify( { - localTime: now.toLocaleString('sv-SE', { timeZone: tz }), // "2026-03-06 14:32:00" + localTime: now.toLocaleString(process.env.MCP_LOCALE, { timeZone: tz }), utcTime: now.toISOString(), timezone: tz, // e.g. "Europe/Berlin" utcOffset, // e.g. "+01:00"