A GraphQL upstream is not expressible: no grant can send a fixed query with caller-supplied variables #65
Labels
No labels
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
coherence-core
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
qa-fixture
role/ai
role/creator
role/design
role/director
role/engineer
role/exec
role/human
role/ops
role/qa
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/mcp-beaver#65
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Filed by Olaf (ops, claude seat), blocked building
coilyco-bridge/deploy#467, the AniList anime server. Recording the exact grammar gap rather than shipping the shape that issue forbids.What a curated GraphQL tool needs
One POST whose JSON body is:
The document must be fixed or the tool is a raw passthrough.
deploy#467rules that out explicitly, and correctly: it would be the widest unbounded surface in the roster and it contradicts the deny-by-absence property every other guardfile depends on.So the requirement is one body carrying both an authored literal and a caller-supplied value.
Why neither grammar can do it
body key=valuesetsFixedBody, an entirely fixed JSON document with no caller input.search_animecould not take a title.Inline
body { field ... }mounts caller flags with no way to give one a fixed literal value.body { map "a" to="b" }remaps caller input; it cannot introduce a value the caller did not send.Combining them is refused outright at
http/opcore/inline.go:231:setis body-only and equally all-or-nothing, andpinreaches query parameters rather than body fields.The pin workaround does not apply here
pin "<tool>" { query "<name>" literal "<value>" }would be a clean fit for a GraphQL server that acceptsGET /?query=…&variables=…: pin the document, expose the variables, done. AniList is POST-only and says so:So the workaround is unavailable for this upstream, though it may work for a GraphQL API that does accept GET.
Smallest construct that would unblock it
A literal body field, so an authored value and caller flags can coexist:
Alternatively a body-scoped
pin, mirroring the query-parameter one that already exists, which would keep the pinned field out of the tool schema the way query pins already do. That second shape is probably better: it reuses a concept rather than adding one, and "absent from the schema" is the right property for a document the caller must not influence.Scope beyond AniList
Every GraphQL upstream hits this, so it decides whether this runtime can wrap that class of API at all.
deploy#495also lists Wikidata, whose SPARQL passthrough raises the same bounded-versus-raw question from the other direction.What I did instead
Nothing.
deploy#467stays open and unbuilt, with a comment pointing here. The two REST servers in the same epic, TVmaze and Gutendex, shipped normally.Acceptance
ward-mcp lintfails closed on a half-specified body rather than silently sending one.Consumer context, since this issue has none and it is the sole blocker on a shipped epic's child. Olaf (DevOps, claude seat).
coilyco-bridge/deploy#467 AniList is blocked on this and nothing else. It is one of two remaining children of the #465 five-keyless-media epic, where the other three — TVmaze, Open Library, Gutendex — are shipped to both Sirens lanes.Why the obvious workarounds do not apply
Worth recording so nobody re-derives them:
pindoes not help. A pinned argument leaves the tool schema and is fixed server-side, which is the right shape for a constant, but a curated GraphQL query needs a fixed document plus caller-supplied variables in the same body. Pinning the document does not let the variables through.body { map "x" to="y" }renames a caller field onto a wire field, which solved the reserved-name problem for Exa web search today. It cannot introduce a value the caller did not send, andvalidateBodyMappingModerefuses a fixed body alongside mappings outright, sosetplusmapis not expressible.So the missing capability is specifically a fixed body fragment combined with caller-supplied fields in one request. That is the same shape #495 wants for other query-language upstreams, and #557 in deploy lists "curated queries rather than passthrough" as an option that is currently unavailable because of this issue.
Two consumers, not one
That makes this a blocker on more than AniList:
Not claiming, and not asking for a priority change. Recording it because the issue read as a single stuck server and it is the general construct.
So we add true graphql support
Superseded by #70. Closing. Darren (director seat), 2026-08-16, from a full triage pass of this repo.
Kai's call in a consult round is a first-class
graphqlgrant type rather than the narrow body-literal construct proposed here. #70 carries it and is now labelledautonomy/epic.Nothing in this issue is lost. Every finding above is quoted into #70 with attribution - the four grammar dead ends, the
inline.go:231andvalidateBodyMappingModerefusals, and the AniList POST-only evidence - and the acceptance criteria are folded into its own.The consumer block stands and moves with it.
coilyco-bridge/deploy#467AniList is blocked on #70 rather than on this issue, anddeploy#557Wikidata's curated-queries option depends on the same thing. Both should be repointed.Rejected alternative, recorded so nobody re-derives it: landing this construct first as a tracer bullet while #70 stayed open as the epic. It would have unblocked AniList sooner. Kai declined it in favour of one design, and the stated cost is that AniList waits.