Repository navigation
schema: nullable arguments as anyOf [X, {type: null}]; gate reports unchanged (see Consiliency/pmcp#369) - #371
Merged
Merged
Conversation
…nchanged (see #369) Optional arguments were advertised as type: [X, "null"], with null among an enum's values. OpenCode's Google provider turns that enum null into the string "null" and drops null from type arrays. They are now advertised as anyOf: [X, {"type": "null"}] (input_schema_for / _canonical_nullable), the spelling the AI SDK sends to Gemini as {type: X, nullable: true}. The gate accepts and refuses exactly what it did. gate_error_for unfolds a nullable union's refusal into X's own errors before best_match ranks them, so the reported message, keyword and path match the old spelling (enum messages no longer list None). Unfold and ranking failures fall back to jsonschema's choice and then to the first raw error, so a refusal is always reported and audited. The migration guide's checker uses the same choice. Implements the plan merged in #370. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qq9UsYfWoENCKhQsuqRkNK
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
ViperJuice
added a commit
that referenced
this pull request
Oct 5, 2026
Clean merge; CHANGELOG keeps both entries. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qq9UsYfWoENCKhQsuqRkNK
ViperJuice
added a commit
that referenced
this pull request
Oct 6, 2026
…they are made, on every transport; SDK server logs masked; exception readers registry-guarded (see #297) Round 18 on #314 (claude DISAGREE, codex F001). - Merge origin/main 23edd92 (#371 nullable anyOf, #364 docs) with a real merge: server.py keeps main's validate_at_gate. A gate refusal is described as #371's gate_error_for reports it; a nullable X reads "X or null"; the sweep reads anyOf [X, null] as X (135 -> 252 cases; gateway.sync_environment covered). The migration guide's gate texts and its checker use the structural description; a versioned package pattern is a fixed-text error. - claude F001: pmcp.sdk_rejections wraps the SDK's handler_exception_to_error_data (dispatcher and runner) and the modern ladder writer: an SDK-origin error keeps its code and id, its message only if reviewed, its data only in a reviewed shape (method name dropped, requested only if a revision). pmcp handler errors are marked by _described_errors and pass unchanged. The HTTP layer keeps only the out-of-session id-null bodies. Every error construction in the whole mcp package is classified by AST, exact both ways. - claude N2: mcp.server/mcp.shared/sse_starlette records mask text args and f-string placeholders; stdio and HTTP tested at DEBUG. - codex F001: parse_url_elicitation_error refuses a registered error's chain; the sink guard seeds annotated exception parameters, accepts a read only after the registry check, and every exempt callee carries a test-enforced reason (predicate, scanned, guarded, handoff, passthrough, reregisters). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W2ovNe4qBzpdunYGYTM6kR
ViperJuice
added a commit
that referenced
this pull request
Oct 6, 2026
…; exception readers registry-guarded; re-based on main 23edd92 (see #297) Answers round 18 on #314 @ acf99e9. - claude F001: pmcp.sdk_rejections rebuilds every SDK-origin JSON-RPC error at the one exception-to-wire mapping (stdio, legacy and modern HTTP): code and id kept; message only if an SDK literal or a reviewed template; data only in a reviewed shape (method name dropped, requested only if a revision). pmcp's handler errors are marked and pass unchanged. All 49 non-literal error constructions in the mcp package are classified by AST, exact both ways. - claude N2: SDK server-side and sse_starlette records masked at creation; stdio and HTTP grids run at DEBUG. - codex F001: the elicitation parser and pyjwt_text refuse a registered error's chain; the sink guard seeds annotated exception parameters (22 new reads found), accepts a read only after the registry check, and every exemption carries a test-enforced reason. - claude N1: a real merge of main 23edd92 (#371, #364); nullable unions described as gate_error_for reports them; the sweep reads anyOf [X, null] (135 -> 252 cases). - Spike wip/297-code @ f89527e; 51 patches, cmp-identical on 23edd92. 137 mutants: 135 / 130 killed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W2ovNe4qBzpdunYGYTM6kR
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.
See #369. This implements the plan merged in #370, rev 2, with the two edits the ruling required.
What changes
All 42 nullable arguments are now advertised as
anyOf: [X, {"type": "null"}]. Before, they usedtype: [X, "null"], plusnullamong an enum's values. Nownullnever appears as anenumvalue, aconst, adefault, or a member of atypearray. The change is made in one place,src/pmcp/tools/schema.py::_canonical_nullable, and the snapshot diff is exactly those 42 nodes.Hosts now receive (captured from the real binaries for the plan):
{type: X, nullable: true}. Before, it sent the string"null"in 4 enums and droppednullfrom all 42 arguments.The gate
The gate accepts and refuses exactly what it did before.
gate_error_forunfolds a nullable union's refusal intoX's own errors beforebest_matchranks them. So a refused call reports the same message, keyword and path as 3.0, including calls with several failures. The one exception is that enum messages no longer listNone.validate_at_gateis the server's only gate call. The migration guide's checker usesgate_error_for.Ruling edits (on top of the plan's patch)
best_matchnow runs inside the fallback'stry. If it fails, the gate uses jsonschema's ownbest_match, and if that also fails, the first raw error. A new test,test_a_refusal_survives_a_ranking_that_raises, forcesbest_matchto raise. It checks that the call is still refused and that the scoped audit records exactly oneaudit.rejectionwithterminal_status: "invalid_arguments".null… as the handlers always did".Evidence (head
6439b21, baseb9bf9d3, dev0)ruff check,ruff format --check(182 files),mypy src/pmcp(53 files) andscripts/check_security_claims.py(131 cited node ids) all pass.best_matchmoved outside thetry(M18, the rev-2 shape);https://claude.ai/code/session_01Qq9UsYfWoENCKhQsuqRkNK
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.