Fix Granola connector silently returning zero meetings (#79)#92
Merged
Conversation
…query (#79) The connector built the `created_after` filter with datetime.isoformat(), which emits microseconds and a +00:00 offset. Granola's public API rejects that with HTTP 400. The 400 was swallowed to None, treated as "no more pages", and surfaced as {"success": true, "count": 0} — so every meeting looked like no meetings. The health check missed it because it queried /v1/notes with no created_after, the one path that doesn't use the broken parameter. Three-part fix: 1. _cutoff_iso now emits seconds-precision RFC3339 with a Z suffix (no microseconds, no numeric offset). NOTE: this format is inferred from #79, not verified against the live API — hence part 2. 2. API failures are no longer swallowed. _api_get raises GranolaAPIError on non-retryable HTTP/network errors (429 still retries once); the list tools catch it and return {"success": false, "error": "..."} instead of a fake count:0. This is the format-agnostic safety net: even if the date format is still wrong, the user sees an honest failure, never a silent "no meetings". 3. granola_check_available now exercises the same filtered list path real queries use, so it can't report healthy while every query 400s. Adds core/mcp/tests/test_granola_server.py (11 cases) covering the 400 path, the health probe, partial-page failures, and — critically — that a legitimate empty 200 is still success:true/count:0. Gates: 157 pytest pass · ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Azij97SEksTxraikqVWiaG
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Azij97SEksTxraikqVWiaG
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #79.
The bug
The connector built the
created_afterfilter withdatetime.isoformat()→2026-07-03T09:14:22.481907+00:00(microseconds + numeric offset). Granola's public API rejects that with HTTP 400. The 400 was swallowed toNone, treated as "no more pages", and surfaced as{"success": true, "count": 0}. Every meeting looked like no meetings.The health check missed it entirely because it queried
/v1/noteswith nocreated_after— the one path that doesn't use the broken parameter. Green light, dead queries.Reported (with full root-cause) by @michrwright.
The fix — three parts
_cutoff_isonow emits seconds-precision RFC3339 with aZsuffix._api_getraisesGranolaAPIErroron non-retryable HTTP/network errors (429 still retries once); every list tool catches it and returns{"success": false, "error": "…"}. So even if the date format is still wrong, the user sees an honest "Granola query failed (HTTP 400)" — never a silent "no meetings".granola_check_availablenow issues a filtered query, so it can't report healthy while queries 400.Tests
New
core/mcp/tests/test_granola_server.py(11 cases): the 400 path, URL errors, 429 retry, the filtered health probe, partial-page failures, and — critically — that a legitimate empty 200 stayssuccess: true / count: 0.Not verified
The exact accepted date format could not be checked against the live API (no key in the build env). The safety net makes that non-fatal, but confirming the format live would verify meetings actually return.
Gates: 157 pytest pass · ruff clean.
🤖 Generated with Claude Code