Skip to main content

What the model can see is a declaration, not a prompt

Mode is usually introduced as context economy: hide the fields the model does not need, spend fewer tokens. That is true, and it is the smaller half. The larger half is that the LLM view of a schema is computed from declarations, deterministically, before any request is made. A field marked Mode("response_only") or Mode("disabled") is not stripped out of a prompt after the fact and it is not filtered by a model asked nicely to ignore it — it is absent from the type the model is handed. It cannot be sent to the model provider, because nothing in the pipeline can construct it. That is field-level data minimisation toward your model vendor, enforced by construction rather than by policy.
message_schema.py
Gmail returns payload — the entire MIME tree, base64 attachment bodies and all. Marked response_only, it is never part of what the model is offered, so it never crosses into a context window. The response is still available to your code; it is the model that does not receive it.

The half the deployment owns

Mode is declared by whoever wrote the pack, and is fixed by the time you install it. Projections are the other half, and they belong to you:
narrow.py
Google’s documents scope grants all 33 kinds of edit as one indivisible grant. There is no scope for “may edit text, may not delete content”, so the union member is the only place that permission can be said. pin does the same for values: a pinned field leaves the schema and its value is sent on every request, so the agent can neither see it nor change it. Both print in the map below, with a pinned field carrying its value.

The audit artifact

Because the boundary is declared, it can be printed. egress_map reads the same declarations the runtime executes, so it cannot drift from what actually happens:
print_egress_map.py
body.raw is a Format field, so the model is offered an EmailContent rather than the base64 MIME string the API takes, and the map names the fields of it. What a field is reported as is what the model is handed, never the wire type behind it. egress_map returns the same content as a plain dict, so it serialises to JSON for a review packet or a CI artifact:
egress_map_json.py
Useful ways to spend it:
  • Security review. The answer to “what can the model see?” is a file, generated from source, not a paragraph someone wrote from memory.
  • CI. Snapshot the map and diff it. A schema change that widens what the model can see shows up as a reviewable diff instead of a silent broadening.
  • Vendor questionnaires. Per-field, per-tool, with the reason attached.
Run it over a session’s tools rather than the pack’s and it reports the surface that deployment actually exposes, mode and projections included. Each entry carries the mode that resolved it, and a field withheld by a label says which one: [mode=max], not [filtered]. An audit that describes the wide surface while the deployment ships a narrow one is worse than no audit, because the review passes on a document about a deployment that does not exist. Entries are keyed by report_key, so a surface assembled from several packs gets one row per tool rather than one per name: gdrive_comments_list and notion_comments_list are two rows, and each carries its own name and pack. Two tools that would share a row raise rather than one overwriting the other.

The same move, for the rules an API keeps in prose

An egress map prints what Mode and a projection enforce. It is not the only property that is enforced by construction and therefore needs printing. ConflictsWith declares which parameters an endpoint refuses together, on the field it is about, so adding a parameter cannot forget the rule and removing one cannot leave it behind. That is the right place to declare it and the wrong place to read the set of them, because “what does this pack refuse together?” becomes a walk over every field. format_conflicts is that walk, reconstructed from the same declarations the runtime enforces:
Google Calendar states that rule in a paragraph of its reference. Here it is a property of the schema, checked on every call, and printable.

What it does and does not promise

It promises: no field marked response_only or disabled, at any nesting depth, appears in the type given to the model. It does not promise anything about what you do with the response after the runtime returns it. If your own code takes a withheld field out of the response and puts it in a prompt, that is your prompt. The boundary governs the boundary. Nor is it authorization — Mode is schema visibility, not permission. Two users calling the same tool see the same shape; who may call it at all is your application’s decision.