DEV Community

Cover image for Your tools/list is stamped. That is not the same as cached.
wolfejam.dev
wolfejam.dev Subscriber

Posted on

Your tools/list is stamped. That is not the same as cached.

A stamp on tools/list is a claim about later reuse. Presence is not proof the next call is cacheable.

TL;DR — The 7/28 spec put ttlMs and cacheScope on tools/list. Most write-ups treat that stamp as a cache. It is a claim about later reuse. Presence means the server said something. Truth needs an observation that can fail. We shipped a liar and a probe. The catalog grew. The probe still said OK. The stamp half was along for the ride. One mutant per clause. Name which negatives are still live.


The victory lap

The 2026-07-28 spec added list-cache stamps (SEP-2549), modeled on HTTP Cache-Control. The official sentence is some version of: clients know exactly how long tools/list is fresh.

That sentence is doing a lot of work.

HTTP already taught this. Cache-Control: max-age=60 is a statement. It is not proof the next GET returns the same bytes. MCP imported the words. It did not import a test.

A server can print ttlMs: 60000, cacheScope: Public and change its catalog on the next call. The stamp is still well-formed. A probe that only checks presence will still pass.


We built a liar. Then the lie got lazy.

Last piece: a labeled companion, mcp-worse, and one command — contrast-smoke — that passes only if the good server meets the BETTER list contract and the bad one fails it.

The checker looked like this:

fn is_lying_surface(p: &ListProbe) -> bool {
    let unstamped = p.ttl_ms.is_none() || p.cache_scope.is_none();
    let wrong_order = p.names != better_names();
    unstamped || wrong_order
}
Enter fullscreen mode Exit fullscreen mode

That OR is the hole.

mcp-better grew a third tool, confirm_echo. mcp-worse still lists two: echo, health. So wrong_order (really: names ≠ the good catalog) is always true. The stamp half stopped carrying observable weight.

If worse grew with_ttl_ms and with_cache_scope tomorrow, the example would still print OK. It would fail for contents only. The negative case for ttlMs would be gone, and nothing would say so.

The “companion must stay a reliable liar” guard only fires when every clause goes green at once. Partial decay is invisible.

A comment on that post named it. They were right.

A liar that fails for two reasons is weaker evidence than two liars that each fail for one. Steal-the-pattern already said: one smallest lie per claim. We had one binary that violated every clause, and an OR that hid which ones were still live.


Presence is not truth

Order is a property of the list the probe is holding. You can see it.

ttlMs and cacheScope are statements about how that list may be reused later. Confirming the fields exist and look well-formed proves the server made a claim. It does not prove a client would be right to skip the next tools/list.

order-restart-smoke does the right thing for order: two processes, same names. It applies that shape to TTL as ttl_a == ttl_b — the stamp is restart-stable. That is a property of the number, not of the caching behavior the number describes.

Falsifying a TTL claim takes an observation pair that straddles a change. Our catalog is compiled in. The TTL claim cannot be violated yet. Declared, not falsified. Once the catalog goes dynamic, ttlMs is the first stamp with room to lie — and it is the clause with the least behind it.

This lab reads this list. Not the next call. Not a client cache.


Run the audit

The probe now requires each teaching clause on the companion. Stamp decay fails closed and names the clause.

// examples/contrast_smoke.rs — current tree
fn is_unstamped(p: &ListProbe) -> bool {
    p.ttl_ms.is_none() || p.cache_scope.is_none()
}

fn wrong_names(p: &ListProbe) -> bool {
    p.names != better_names()
}
Enter fullscreen mode Exit fullscreen mode

Both must stay true on mcp-worse. If either goes green, the example exits non-zero and says which.

Step 1 — Clone and build both binaries

git clone https://github.com/Wolfe-Jam/mcp-better.git
cd mcp-better
cargo build --bins
Enter fullscreen mode Exit fullscreen mode

This is the tree, not cargo install mcp-better. The published v0.5.0 tag still has the OR. The named-clause OK line is the honesty cut on current main.

Step 2 — Run contrast-smoke

cargo run --example contrast-smoke
Enter fullscreen mode Exit fullscreen mode

Expect (captured 2026-08-19, current tree):

better names=["health", "echo", "confirm_echo"] ttl=Some(60000) scope=Some(Public)
worse  names=["echo", "health"]                 ttl=None        scope=None
contrast-smoke: OK (better contract · worse unstamped · worse names≠health,echo,confirm_echo)
Enter fullscreen mode Exit fullscreen mode

The OK line is the point. It names which negatives are still live.

Step 3 — What you just proved

Claim Evidence Not proved
Good list is ordered + stamped Wire: three names, ttlMs > 0, cacheScope == Public That a client should cache it
Companion stays unstamped unstamped asserted, not OR-ed away Cache behavior
Companion catalog ≠ better names ≠ health,echo,confirm_echo (today: two tools, reversed) That “wrong names” is only order
Partial decay is visible Stamp clause going green fails the example A second mutant (stamped-but-reversed)

That last empty cell is honest. One companion that fails for two reasons is still weaker than two mutants that each fail for one. This cut makes the live negatives named. It does not ship a second dummy.


What this is not

  • Not a cache test. No observation pair straddling a change. The catalog is compiled in.
  • Not a rewrite of the last post. That one built the liar. This one asks what the probe actually falsified.
  • Not a version diary. No new tool. No new crate.
  • Not “every BETTER server must implement a mutant factory.”
  • Not a security scanner.

Steal the pattern

Same shape as last time — finish the last step:

  1. Write down every claim the docs make about the wire.
  2. For each claim, the smallest change that would make it false.
  3. One mutant per claim — or, at minimum, assert each clause so one leftover violation cannot hide another.
  4. Print which negatives are still live. An OK line that does not name them is an OR in disguise.
  5. If the claim is about later reuse (ttlMs, cacheScope), a presence check is “a claim was made.” Do not call it verified until something can fail.

If you cannot say which clause is still live, you do not have a negative case. You have a dummy that is wrong in a pile.


Further reading


Close

A README cannot lie to a test that reads the wire. A stamp can still lie to a README.

ttlMs on the list is a reuse claim. contrast-smoke now says which teaching clauses are still live. That is the list. Not the cache.

Claim = wire. Ask what you actually falsified.

Which clause on your tools/list is only present — not proven?

I'm an AAIF Ambassador. This piece is public MCP education — the kind of practical path the program exists for.

Top comments (0)