Repository navigation
fix(auth): operator messages through a checked registry; refuse unrenderable auth config at startup; README /mcp rule (see Consiliency/pmcp#326) - #340
Merged
Conversation
…derable auth config at startup; README /mcp rule (see #326) Implements the merged plan .consiliency/plans/detailed-326-auth-messages-20261003-0226.md: - AuthMessage registry (32 members) and render_auth_message, the one runtime guard (identity membership, exact placeholder fields with checked shapes), used by the auth error constructors, _auth_response/_reject (headers keyword-only, a mapping) and all 13 builtin ValueError/HTTPError sites. - Four texts reworded that pmcp's own sanitiser rewrote. - check_auth_config: refuse at startup, on values as stored, a JWKS URL, metadata URL or required scope a message could not carry (CLI/env only for HTTP + resource-server; create_http_app checks the metadata URL as configured and scopes only in resource-server mode). - Metadata route refuses to start rather than publish an empty resource. - README: the /mcp rule behind a prefix-stripping proxy; follow-up #334 for the metadata 404. - Tests: new tests/test_auth_operator_messages.py; existing tests moved onto registry members; the pinned silent metadata drop now expects refusal. The "every operator-facing message" promise is qualified against the plan's non-goals (metadata diagnostics, UNVERIFIED_URL_CAVEAT, fetch_json_metadata strings, plain 413/429/504 bodies, log templates). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DsWXuxhZkEQrfn7TN9GCN2
ViperJuice
added a commit
that referenced
this pull request
Oct 4, 2026
…velope checked before dispatch on every transport; repr rule takes every spelling; re-embedded on main c9206a9 (see #297) Round-12 board on rev 12 (48b7a89): B1 (claude seat and codex) and B2 (codex). B1: a rejected gateway.cancel id is null in the response and names no server in the audit; every output or audit field that copies a caller input is derived from the models and the code and triaged. B2: the JSON-RPC 2.0 / MCP envelope gates dispatch on stdio, and a strict adapter gates the SDK client transports; a malformed frame settles nothing. The claude notes: the repr rule in every spelling, keyed by site; the record wording and scope. Re-embedded on main c9206a9 (#337, #339, #340). The merge with #337 exposed a dropped task hint kept in raw and in invoke's size; both closed. Code: wip/297-code @ ebcf4fc (local only). Repro 11/11 -> 0/11; full suite 6004 passed; mutation 85/86 and 81/86 (NO_STATIC; M25 equivalent; M19, M20, M22, M35 static-only); embedding proof on c9206a9: 46 patches cmp-identical. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NxgMZ9qVCrCaV4aBRBAGNM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the merged plan
.consiliency/plans/detailed-326-auth-messages-20261003-0226.md(#333) for #326.What changes
One registry for auth operator messages.
pmcp.auth.AuthMessage(32 members) holds every fixed message thatauth.pyandtransport/http.pyraise or send as an auth rejection.render_auth_messageis the one runtime guard. It checks membership by identity, requires exactly the member's placeholder fields with valid shapes (absolute public http(s) URL; RFC 6749 scope list), and refuses anything else withTypeError. The auth error constructors,_auth_response/_reject(whoseheadersare now keyword-only and must be a mapping) and all 13 builtinValueError/HTTPErrorsites go through it.Four texts reworded, because pmcp's own sanitiser rewrote them (
Token [REDACTED] not be verified…,Missing bearer [REDACTED],Unsupported token [REDACTED],shared-secret [REDACTED] mode…).Startup refusal.
check_auth_configchecks values as they will be stored, using the renderer's own validators:AsyncJWKS;create_http_app, the metadata URL as configured, and required scopes only in resource-server mode.A URL that
sanitize_public_auth_urlrefuses keeps its specific registry message.No empty
resource. The metadata route refuses to start rather than publish an emptyresource.README. It documents the
/mcprule behind a prefix-stripping proxy, separately for CLI and embedding deployments.Tests.
test_auth.py,test_http_transport.pyandtest_scoped_advisor_audit.py.test_transport_http.pythat pinned the old silent metadata drop now expects the refusal.tests/test_auth_operator_messages.py, with 244 tests.Behaviour changes
create_http_apprefuses a protected-resource metadata URL that normalisation used to drop silently, which omitted the route. That means a relative, non-http(s), plain-http non-loopback or non-public-IP URL. An https URL with a?token=query still starts and serves its route.404behind a prefix-stripping proxy is auth: protected-resource metadata route 404s behind a prefix-stripping proxy or an ASGI root_path #334.Guarantee vs guard rail
The runtime registry is the guarantee. The static check in the test module is a guard rail for seven listed shape families: unknown raises, non-member messages, arity, fields, named raises,
pyjwt_textplacement and payload mutation. It is not exhaustive against deliberate obfuscation.Deviations from the plan
AuthMessage, the test module and_site_violations) and the CHANGELOG name what is out of scope, matching the plan's own non-goals: the metadata diagnostics lists,UNVERIFIED_URL_CAVEAT,fetch_json_metadata's strings, the plain 413/429/504 bodies and the log templates. The README makes no such claim.Everything else is the plan's three embedded patches and test module, applied exactly. Each was cmp-identical to the plan's extraction before items 1–3 were added.
Gates
Measured on this branch, on
516d689:ruff check src tests: All checks passed!ruff format --check src tests: 173 files already formattedmypy src/: Success: no issues found in 52 source filestests/test_auth_operator_messages.py: 244 passedscripts/check_security_claims.py: OK, 129 cited node id(s)OSError: [Errno 122] Disk quota exceeded, no assertion failures), so it was rerun with--basetempand the log on the workspace volumeNot merged; needs a cross-vendor panel CR before merge.
🤖 Generated with Claude Code
https://claude.ai/code/session_01DsWXuxhZkEQrfn7TN9GCN2
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.