Skip to content

Fix Granola connector silently returning zero meetings (#79)#92

Merged
davekilleen merged 4 commits into
mainfrom
fix/granola-silent-zero-meetings
Jul 11, 2026
Merged

Fix Granola connector silently returning zero meetings (#79)#92
davekilleen merged 4 commits into
mainfrom
fix/granola-silent-zero-meetings

Conversation

@davekilleen

Copy link
Copy Markdown
Owner

Closes #79.

The bug

The connector built the created_after filter with datetime.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 to None, 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/notes with no created_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

  1. Date format: _cutoff_iso now emits seconds-precision RFC3339 with a Z suffix. ⚠️ This format is inferred from the issue, not verified against the live API — which is exactly why part 2 exists.
  2. Stop swallowing failures (format-agnostic safety net): _api_get raises GranolaAPIError on 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".
  3. Health check exercises the real path: granola_check_available now 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 stays success: 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

davekilleen and others added 4 commits July 10, 2026 18:51
…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
@davekilleen davekilleen merged commit 919947b into main Jul 11, 2026
4 checks passed
@davekilleen davekilleen deleted the fix/granola-silent-zero-meetings branch July 11, 2026 06:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Granola connector silently returns zero meetings — malformed created_after rejected with HTTP 400

1 participant