- pact.MCPSubjectChanged and pier.Event gain TokenID and ClientID; a
narrowed revoked event closes only matching sessions and stateless servers
- cabana fires the admin token revoke with the token row id
- towel.WithClientIP/ClientIP; pier puts the address (surf applies
http.trusted_proxies) on the resolve context and ResolveBackendToken logs it
- pact.MCPPrompt.Writes (additive; the zero value keeps today's listing)
- pier's allowsPrompt hides a prompt declared with Writes from an identity
without write, as it hides a tool that is not read-only (C-IN-01
framework part, P-28)
- a path field must be required in the reflected schema (boot error), and
a missing value, an empty, . or .. segment, or a / inside one segment is
a tool error before dispatch (A-WR-01, A-WR-02)
- a failed guard dispatch logs its cause and answers a fixed tool text;
only an invalid token and an unknown or deactivated account keep their
own fixed texts (A-WR-08)
- path and query scalars decode with UseNumber, so integers above 2^53
keep their digits (A-IN-04)
- the dispatched request carries the outer Host, X-Forwarded-Proto and
X-Forwarded-Host (A-IN-05)
- an embedded struct on a GET or DELETE tool fails boot (A-IN-07)
- a dispatch whose guard list or principal kind contradicts the tool's
guard is refused (A-IN-08)
- Identity.fingerprint covers scopes, both subjects, the ceiling and the
token kind; a stateful lookup or stateless hit whose fingerprint changed
re-filters the server with the event diff under its own mutex (A-WR-04)
- Mount warms a Redis store in the background with a 1 s to 30 s backoff
and every request kicks a pending open without waiting, so stateless-only
instances subscribe to fan-out (A-WR-05)
- a stateless subscriptions/listen POST re-verifies its bearer every
recheck interval; an LRU-evicted stateless server closes its sessions
(A-WR-06)
- resolvePersonalToken refuses a subject with TokenID 0, and the
pact.MCPPersonalTokenResolver contract says one resolver per application,
false means unauthorized and TokenID keys the binding (A-WR-09, A-IN-03)
- personal tokens only behind mcp.allow_personal_tokens (401 without a
lookup otherwise); an admin token yields a backend-only identity with
its scope limit per dispatched method, a user token a frontend-only one
- read/write ceiling and prompts proven by the tracer's subtests; the
audit line asserted to carry the tool and never the arguments or token
- the mounted *pier.Pier is published on the application (test seams in
export_test.go)
- docs/services/mcp.md with the tested acme.blog example, site.yaml note,
cross-links from authentication.md, registration.md and the surf README