No client-side rate limiting, which is a stated shared requirement for five servers about to be built on this runtime #57
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
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/mcp-beaver#57
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?
What this is
coilyco-bridge/deploy#465commits to building five keyless media MCP servers on this runtime, and lists as a shared requirement applying to all five:There is no such construct. Searching this repository for
rate.limit,ratelimit,throttlereturns nothing, and neither the wrap grammar (base-url | auth | restrict | can | proxy) nor the grant grammar (path | query | body | set | fail-when) has a place to express one.So the requirement cannot be met by any guardfile, and the first of the five — Open Library,
coilyco-bridge/deploy#468— shipped without it.Why it matters more than it sounds
The consuming pod has one IP and many users behind it. A public-good API sees one caller whose request rate is the sum of a Discord community's curiosity, with no bucket in between. Open Library publishes no limit, which the deploy issue correctly reads as "not a licence to hammer a nonprofit's service."
The sharpest case is MusicBrainz (
coilyco-bridge/deploy#470) at ~1 req/sec published. Two concurrent turns exceed it. There is currently no way to write a guardfile that does not.What would be enough
A per-wrap bucket would cover every case in the batch:
Serialising at the outbound client rather than rejecting the caller is probably the right behaviour — a queued tool call is slower, a 503 is a failed turn. Per-grant granularity is not needed for this batch.
What I verified
Read against
mcp-beavermainandcli-guard v0.131.0on 2026-08-13. I did not attempt an implementation.Related
coilyco-bridge/deploy#465— the epic carrying the requirementcoilyco-bridge/deploy#468— Open Library, shipped without itcoilyco-bridge/deploy#470— MusicBrainz, where this binds hardestLanded in
18463c7.Per-server and process-wide, matching the exposure you described: the pod has one IP, so the upstream sees one caller whose request rate is the sum of a whole community's curiosity. Per-grant granularity not offered, per your note that the batch does not need it - and because an upstream publishes one limit, not one per endpoint.
It waits rather than rejecting, which was your call and is the right one. The wait is bounded for free by the request deadline from #49, so a call that would queue past it fails with a stated timeout instead of holding a slot indefinitely. That interaction is tested.
Two placement choices that differ from the sketch, both deliberate.
Stated as a sibling of
wrap, not inside it. Your sketch putrate-limit 1/1sin the wrap body. That body is opcore's frozen grammar and the umbra pin, and the runtime's half of this needs no grammar change - so it rides besidewraplikeserver-infoandconfirm. Costs one line of indentation, avoids a version bump on a shared dependency.Applied at the tool handler, not by swapping opcore's HTTP client. Wrapping the client was the obvious implementation and it is a trap:
RuntimeConfig.Clientis nil-means-default, and that default is the redirect-guarding client - the one that refuses to follow a POST redirect, as seen in #55. Passing a limiter-wrapped client would have silently traded that guard away for a rate limit.Grant-backed tools only. The info tool and
withholdstubs reach no upstream, so charging them would throttle the fleet's liveness probe on behalf of a service it never calls. Placed inside anyconfirmgate, so a call waiting on a human does not hold a slot and a declined call has not spent one.For the five servers: MusicBrainz is
rate-limit "1/1s". Open Library has no published limit, so pick a courtesy rate rather than leaving it unset - the node is absent-means-unlimited, and this is a nonprofit's service.