MCPL RFC-005 follow-up: fetch_reference + lazy-default autofetch - #138
Conversation
|
AF #138 — CHANGES REQUESTED at exact head The fetcher itself is solid: dialed-origin-only with ws→http normalization, bearer applied host-side only after the origin check passes, 1. Dedup key mismatch —
2. Vector 2 — inherited from #137 (inline data + disposition inlined at all five sites). Nonblocking:
Receipts:
Also inherits #137's KV-lineage blocker (rebase both onto |
0fc92cc to
9c72f96
Compare
af94ff6 to
c969aba
Compare
|
AF #138 — CHANGES REQUESTED (still) at exact head The rebase carried #137's fixes through cleanly (vector 2 and stable ids now hold here too), but blocker 1 from the first round is unchanged: the dedup key mismatch.
Probe (register with serverId, mark fetched, serialize the block) still fails on all three assertions at Fix options, either is fine:
Nonblocking list from round one stands unchanged (stale testimony on dedup — now demonstrable: same uri re-registered with a new digest keeps the old one; serial eager fetches; budget race; header-not-sniffed MIME; Receipts:
No merge/deploy performed. Re-review immediate on a corrected head. |
…utofetch
The verb for RFC-005 stubs. New src/mcpl/reference-fetcher.ts is the
only code that dereferences a reference, fail-closed per the RFC:
authenticated fetches only to the reference server's dialed origin
(connection credential applied as an Authorization header host-side,
never in a URI; third-party origins not fetched at all in v1),
https/http-co-origin scheme allowlist, no redirect traversal,
actual-byte streaming ceiling regardless of claimed size, digest
verified while streaming (mismatch discarded), expired testimony
refused locally, storage names host-generated from the reference id.
Autofetch defaults per the sanity discussion: lazy above the eager
ceiling; eager (256KB default, per-server override via config
autofetch{maxBytes,maxTotalBytes}) only from the connection origin --
anything under that threshold could have shipped inline, so eager
materialization is never worse than the status quo. Scripts get
"eagerly lazy": handleScriptToolCall materializes every referenced
payload at result delivery, since a script receiving a result is the
strongest demand signal available. Cumulative per-server byte budgets
cap appetite.
fetch_reference tool (registered with the workspace tools) resolves a
[ref id] to a workspace file and returns path + verified size/type;
stubs now end in "saved: <path>" or "fetch with fetch_reference".
Registry gains uri+server dedup (dispatch-time registration and
stub-time registration share a record) and findByUri for the script
hook; unknown/evicted ids stay a defined miss.
Vectors 4, 5, 10, 11, 12, 16 + origin/budget/eager-eligibility run
against a live local HTTP server in test/mcpl-reference-fetcher.test.ts.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Z6NaM8gWPuNXCcn7jGR9E
c969aba to
511b722
Compare
|
Blocker fixed at Dedup key mismatch — took your second option: the uri alone is the identity key, and a registration that knows the server upgrades the record's binding in place instead of forking it. The registry comment now explains why (stub sites are free functions with no server context; threading serverId through 11 sites is the structural change this avoids). If two servers ever reference one uri, the first binding wins — and the record's serverId feeds the origin check, which fails closed on any mismatch, so a wrong winner can't fetch from the wrong place. Your probe is now the test, verbatim shape: register with serverId → mark fetched → serialize via the history lane → asserts stub id === dispatch id, Nonblockings taken:
Nonblockings acknowledged but not taken (deliberately):
Receipts:
🤖 Generated with Claude Code |
|
AF #138 — ACCEPT at exact head Blocker closed: uri is the sole identity key and a later registration that knows the server upgrades the binding in place. My probe (register with serverId → mark fetched → serialize via the history lane) now passes all three assertions — same id, Nonblockings verified as taken: parallel eager fetch with a cap of 8 per result; testimony refresh while unfetched and frozen after fetch (my round-one probe for this passes too); magic-byte sniff ahead of the header for the verified type and storage extension; Two new nonblocking notes from the delta, both follow-up material:
Receipts (worktree at exact head, deps symlinked from the shared checkout):
|
Reviewed and accepted at exact head fec2fc4; main advanced by RFC-005 (#137/#138) after the PR base, so this is a merge commit rather than a fast-forward to keep the reviewed identity intact. Composed tree built clean and passed the full suite (670 pass / 0 fail / 4 skipped). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wZa66BukZkjxMK25sCHkV
Stacked on #137 (host treatment). Gives the reference stubs their verb: a host-mediated fetcher implementing RFC-005 §6/§7 fail-closed, a
fetch_referencetool, and the autofetch policy discussed in review of the design:Fetcher (
src/mcpl/reference-fetcher.ts)ws→http/wss→httpsnormalized, default ports elided). The connection credential rides as anAuthorization: Bearerheader applied by this code alone — never in a URI, never visible to model/script. Third-party origins are not fetched at all in v1 (deferred with declared reference origins).sizeBytes(vector 10) — with over-ceiling claims refused before dialing (vector 4), digest verified while streaming with mismatches discarded (vector 5), expired/unparseable-expiry testimony refused locally (vector 16), storage filenames host-generated from the reference id + observed type (§7.3 — the server'snamestays display-only).Autofetch policy (host default, not protocol)
autofetch: {maxBytes, maxTotalBytes}in themcplServersentry. Cumulative per-server byte budgets.handleScriptToolCallmaterializes every referenced payload at result delivery — a script receiving a result is the strongest demand signal available (scripts process, they don't browse) — so the stub a script sees carries a workspace path numpy canopen().Surface
fetch_reference {ref_id, max_bytes?}tool, registered alongside the workspace tools; unknown/evicted ids return the defined miss (vector 20), never a different record.saved: <mount>/refs/<ref_id>.<ext>(already materialized) orfetch with fetch_reference(on demand).findByUrifor the script hook.Tests
test/mcpl-reference-fetcher.test.ts: 12 tests against a live local HTTP server — the fetcher half of the RFC's executable-vector acceptance criterion (4/5/10/11/12/16, origin refusal, budget exhaustion, eager eligibility matrix, header-not-URI credential assertion, registry dedup/miss). Full adjacent suite: 68 pass, typecheck clean.Live counterpart: vst-mcpl's
/filesendpoint now acceptsAuthorization: Bearer(verified 200/401 on the deployed server), so the fetcher's header-auth path works end-to-end against the RFC's motivating emitter.🤖 Generated with Claude Code
https://claude.ai/code/session_017Z6NaM8gWPuNXCcn7jGR9E