Skip to content

feat: save meeting attachments such as screen snapshots - #3

Merged
piekstra merged 2 commits into
mainfrom
piekstra/meeting-attachments
Oct 13, 2026
Merged

piekstra merged 2 commits into
mainfrom
piekstra/meeting-attachments

Conversation

@piekstra

Copy link
Copy Markdown
Member

Granola's MCP server now offers get_meeting_attachment, which returns a meeting's image attachments, such as the snapshots Granola takes of shared screens (https://docs.granola.ai/help-center/taking-notes/capture-shared-screens). The CLI could only reach it through tools call, and printed [image content, ...] in place of the image.

granola meetings attachments <meeting-id> <attachment-id>... calls the tool for up to 5 attachments and saves each image as <meeting-id>-<n>.<ext> in --out-dir (default ., created if missing, files mode 0600). Text blocks are printed as the server sent them, in order, so each Saved line sits next to the metadata that follows its image. The command does not parse the metadata.

Error handling:

  • An error result with no images behaves like every other tool error: nothing on stdout, the server's text on stderr, exit 5. --out-dir is not created.
  • A partial failure keeps the images that succeeded, prints the server's errors, and exits 5.
  • --json prints the raw result with base64 images and saves nothing.

testdata/tools_list.json gains the live tool definition. The error fixture's text matches a live response. The success and partial-failure layouts follow the tool's description, because no meeting on the test account had an attachment yet; testdata/README.md says so.

Tests: saved bytes, file names, modes and output order; partial failure; an error with no images; --json; usage errors for too few and too many IDs. make check passes. Against the live server, an unknown attachment ID exits 5 with the server's error and creates no directory, and a missing attachment ID exits 2.

Adds meetings attachments, which wraps get_meeting_attachment and saves each returned image to a file. Text blocks are printed as the server sent them, and images that succeed are kept when other attachments fail.

@monit-reviewer monit-reviewer left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated PR Review

Reviewed commit: 8db75722e5ef
Profile: claude-monit-reviewer - Posting as: monit-reviewer

Summary

Reviewer Findings
documentation:docs 0
architecture:solid-reviewer-agnostic 1
security:code-auditor 0
architecture:solid-reviewer-agnostic (1 finding)

Minor - cmd/attachments.go:49

Project manifest drift (docs/development.md, Layout section; U-S2). The manifest says every tool command "is a thin wrapper that builds the tool's arguments and calls runTool", and that result rendering belongs to internal/mcpclient/. This command is the first one to skip runTool on its main path. It opens its own session, calls CallTool, and has saveAttachments do the rendering itself: it classifies content blocks, decides when an error result counts as a tool error, and writes files. The design is reasonable. Images need a different sink than text, and keeping the filesystem side in cmd/ keeps mcpclient free of IO. But the manifest now describes a layout the code no longer follows, and the next contributor will either copy this pattern without guidance or force a similar command through runTool. Fix: add a line to docs/development.md saying that commands whose results carry binary content may call withSession directly and render with a local helper, and that mcpclient.RenderText/CheckError stay the source of truth for text and errors. Optionally, have RunE reuse the call %s: %w wrapping from callAndPrint so error wording cannot diverge. The command itself does not need to change.

Reviewer Coverage

  • documentation:docs — complete (constrained); inspected 2 assigned files (9 inspected across reviewers): README.md, testdata/README.md; skipped: none; constraints: none
  • architecture:solid-reviewer-agnostic — complete (broad); inspected 7 assigned files (9 inspected across reviewers): cmd/attachments.go, cmd/cli_test.go, cmd/names.go, testdata/call_get_meeting_attachment.json, testdata/call_get_meeting_attachment_not_found.json, testdata/call_get_meeting_attachment_partial.json, testdata/tools_list.json; skipped: none; constraints: Ran go test ./cmd/ with the Makefile's GOFLAGS: passes. Did not run golangci-lint or the full make check. docs/development.md (the project manifest) is not in the changed set, so the manifest-drift finding is anchored on the command that diverges from it.
  • security:code-auditor — complete (broad); inspected 1 assigned file (9 inspected across reviewers): cmd/attachments.go; skipped: none; constraints: none
Inspected files (9)
  • README.md
  • cmd/attachments.go
  • cmd/cli_test.go
  • cmd/names.go
  • testdata/README.md
  • testdata/call_get_meeting_attachment.json
  • testdata/call_get_meeting_attachment_not_found.json
  • testdata/call_get_meeting_attachment_partial.json
  • testdata/tools_list.json

0 PR discussion threads considered. 0 summarized; 0 resolved.


Completed in 2m 07s | ~$1.57 (est.) | claude-sonnet-5, claude-opus-5-5 | cr 0.10.319
Field Value
Model claude-sonnet-5, claude-opus-5-5
Reviewers documentation:docs, architecture:solid-reviewer-agnostic, security:code-auditor
Engine claude_cli · claude-sonnet-5, claude-opus-5-5
Reviewed by cr · monit-reviewer
Duration 2m 07s wall · 3m 24s compute
Cost ~$1.57 (est.)
Pricing basis anthropic-public-2026-09-23
Tokens 52 in / 10.4k out

Per-workstream usage

  • orchestrator-selection — claude-sonnet-5
    • In: 6
    • Out: 2.1k
    • Cache read: 90.2k
    • Cache create: 40.5k
    • Cost: ~$0.20 (est.)
    • Duration: 25s
  • documentation:docs — claude-sonnet-5
    • In: 20
    • Out: 3.0k
    • Cache read: 530.0k
    • Cache create: 46.6k
    • Cost: ~$0.32 (est.)
    • Duration: 1m 23s
  • architecture:solid-reviewer-agnostic — claude-opus-5-5
    • In: 12
    • Out: 3.3k
    • Cache read: 243.8k
    • Cache create: 70.8k
    • Cost: ~$0.68 (est.)
    • Duration: 51s
  • security:code-auditor — claude-sonnet-5
    • In: 8
    • Out: 1.6k
    • Cache read: 158.9k
    • Cache create: 40.5k
    • Cost: ~$0.21 (est.)
    • Duration: 32s
  • orchestrator-rollup — claude-sonnet-5
    • In: 6
    • Out: 437
    • Cache read: 143.6k
    • Cache create: 31.3k
    • Cost: ~$0.16 (est.)
    • Duration: 11s

Comment thread cmd/attachments.go Resolved
@piekstra
piekstra marked this pull request as ready for review October 13, 2026 11:59
@piekstra
piekstra merged commit c36c8ba into main Oct 13, 2026
8 checks passed
@piekstra
piekstra deleted the piekstra/meeting-attachments branch October 13, 2026 12:28
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.

2 participants