Skip to content

GraphQL schema drops fields JMAP already returns #40

Description

@radiosilence

The GraphQL layer should not hide or drop data JMAP already hands us. Two classes
of gap exist today: fields we deserialize and then discard, and fields we never
request even though adding them to an existing call costs no extra round trip.

Discarding them is worse than it looks for an LLM client — a field absent from the
schema isn't a field the model works around, it's a capability the model cannot
discover exists.

Already in src/models/mod.rs, dropped before reaching the schema

No API cost to expose; the data is already in memory.

  • EmailBodyValue.isTruncated / isEncodingProblem. Email.textBody and
    Email.htmlBody resolve to a bare String, so a truncated body is
    indistinguishable from a complete one. This is the one that produces
    silently wrong answers rather than merely incomplete ones, and should lead.
  • EmailBodyPart.cid. The only way to match an inline image to
    <img src="cid:…"> in htmlBody. Currently unreachable, so inline images
    can't be placed.
  • EmailBodyPart.charset. Needed to decode text attachments correctly.
  • EmailBodyPart.partId. Part addressing.
  • Body parts beyond the first. Email::text_content / html_content take
    .first() on the part list, so multipart bodies lose everything after part 1
    with no indication that anything was omitted.

Never requested, free to add to an existing call

Extending the properties list on a call we already make is the same round trip.

  • Mailbox.isSubscribed, Mailbox.myRights (RFC 8621 §2) — absent from
    list_mailboxes. myRights in particular governs whether a write will be
    refused.
  • Email.sender — distinct from from; the meaningful one for list mail.
  • Email.headers / bodyStructure. Note the asymmetry introduced by the
    filter work: a caller can filter on header: ["List-Id", "rust-lang"] but
    cannot read List-Id back off a result.

Not gaps

Verified complete, so no work needed: Identity (all 8 model fields exposed),
MaskedEmail (all 9), Mailbox model → GraphQL (all 9), Thread, and every
field in EMAIL_SUMMARY_PROPERTIES.

Constraint

The lazy boundary must survive this. EMAIL_SUMMARY_PROPERTIES exists so lists
don't pay for bodies, and anything added has to land on the correct side of that
split — a new field that drags the full-record fetch into a cheap listing would
regress the batching this schema is built around. src/mcp/graphql/tests.rs
asserts on the actual HTTP calls made, so a regression there fails a test rather
than going unnoticed.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions