MCP server: publish real per-tool JSON Schemas (#506) - #528
Merged
Conversation
The MCP server built every tool from an async (**kwargs) closure and assigned __annotations__ / __defaults__ afterwards, which cannot add parameters to a function that has none. Every registered tool was therefore published to MCP clients with an empty inputSchema: no parameter names, types, required/optional split, enums, or bounds. Registration now constructs explicit Tool objects whose `parameters` field, the schema the server publishes verbatim, is exactly the registry's spec["input_schema"] from get_tool_specs(). Argument validation stays where it always lived: execute_tool_call validates against each tool's real pydantic input model, so the hand-built FuncMetadata passes arguments through unchanged. The server now targets the MCP 2.x SDK (mcp.server.mcpserver.MCPServer, renamed from 1.x's FastMCP); the installed `mcp>=1.0` pin already resolved to 2.x, under which the old `from mcp.server.fastmcp import FastMCP` failed at import (and was mypy's one standing error). The `mcp` extra now pins `mcp>=2.0`, and the module is no longer omitted from coverage measurement (100% covered by the new tests, which also compare every published schema against the registry through the real list_tools() path). Drive-by from #513: the concurrent-dispatch test asserted on wall-clock elapsed time and could flake on loaded CI runners; it now proves the overlap with a barrier both in-flight calls must reach. Version 1.73.2; CITATION.cff and CHANGELOG.md updated in step. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU
kgdunn
pushed a commit
that referenced
this pull request
Aug 29, 2026
The typecheck gate is red on main itself: the unpinned mcp extra now resolves to mcp 2.x, where FastMCP was renamed MCPServer, so mypy fails on mcp_server.py on every branch. This ports the migrated server and the mcp>=2.0 floor verbatim from #528; the diff no-ops once #528 merges. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU
kgdunn
pushed a commit
that referenced
this pull request
Aug 29, 2026
The typecheck gate is red on main itself: the unpinned mcp extra now resolves to mcp 2.x, where FastMCP was renamed MCPServer, so mypy fails on mcp_server.py on every branch. This ports the migrated server and the mcp>=2.0 floor verbatim from #528; the diff no-ops once #528 merges. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU
kgdunn
pushed a commit
that referenced
this pull request
Aug 29, 2026
The typecheck gate is red on main itself: the unpinned mcp extra now resolves to mcp 2.x, where FastMCP was renamed MCPServer, so mypy fails on mcp_server.py on every branch. This ports the migrated server and the mcp>=2.0 floor verbatim from #528; the diff no-ops once #528 merges. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU
kgdunn
pushed a commit
that referenced
this pull request
Aug 29, 2026
The typecheck gate is red on main itself: the unpinned mcp extra now resolves to mcp 2.x, where FastMCP was renamed MCPServer, so mypy fails on mcp_server.py on every branch. This ports the migrated server and the mcp>=2.0 floor verbatim from #528; the diff no-ops once #528 merges. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU
This was referenced Aug 29, 2026
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
This was referenced Aug 29, 2026
kgdunn
added a commit
that referenced
this pull request
Aug 29, 2026
…riteria search the design class (#496-#499) (#524) * chore: start OMARS design generation fixes (#496-#499) * fix(experiments): make OMARS n_runs the total run count and selection criteria exact where enumerable n_runs now counts the centre runs (issue #496): the design returned for generate_omars(n_runs=n, center_runs=c) has exactly n rows, and n_runs - center_runs must be a positive even number. The a_optimal, d_efficiency, and min_second_order_correlation criteria (and the D axis of the default dominance rule) previously post-hoc ranked whatever the binary randomized-objective ILP multistart surfaced, which cannot even represent designs that repeat a half-run; the exhaustive optima in issues #497, #498 and #499 need such repetition. The search now enumerates every feasible half-design multiset (counts per sign class) up to four factors at moderate sizes, scores the count vectors in vectorised batches without materialising designs, and picks the winner exactly; larger problems keep the multistart, reported honestly as search_mode="multistart". Regression tests pin all tractable cells of the three issue tables to their exhaustive optima. A design with a constant second-order column now scores inf on the selection correlation metric instead of having the column skipped, so min_second_order_correlation no longer favours degenerate designs. Fixes #496. Fixes #497. Fixes #498. Fixes #499. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * chore: bump version to 1.72.0 with CITATION and CHANGELOG in sync Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * fix: port the mcp 2.x migration from #528 so typecheck is green The typecheck gate is red on main itself: the unpinned mcp extra now resolves to mcp 2.x, where FastMCP was renamed MCPServer, so mypy fails on mcp_server.py on every branch. This ports the migrated server and the mcp>=2.0 floor verbatim from #528; the diff no-ops once #528 merges. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU --------- Co-authored-by: Claude <noreply@anthropic.com>
…e-library-issues-hr7xxk-mcp # Conflicts: # CHANGELOG.md # CITATION.cff # pyproject.toml
kgdunn
added a commit
that referenced
this pull request
Aug 29, 2026
…#527) * chore: start sklearn clone-contract fix (#505) * fix(multivariate): stop fit() mutating constructor parameters, restoring the clone contract PCA, PLS, and OPLS wrote the resolved component count (and PLS the resolved missing-data settings) back onto their constructor parameters inside fit(). sklearn's contract requires __init__ arguments to stay verbatim, because get_params/clone/GridSearchCV treat them as the requested configuration: PLS.cross_validate clones the fitted parent, so every resample silently inherited the clamped component count and the mutated missing-data settings, and PCA(n_components=None) became a concrete int after one fit, freezing the first dataset's clamp. The resolved count is now the fitted attribute n_components_, set only in fit(); the parameter is untouched. Internal post-fit readers (the T2 limit mixin, ellipse_coordinates, spe_limit, the plot pre-checks, PLS prediction intervals) read n_components_, MBPCA/MBPLS expose the same fitted attribute for those shared helpers, and OPLS maps the old attribute name to a rename message. Clamping logic and its SpecificationWarning are unchanged. Fixes #505. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * chore: bump version to 1.73.0 with CITATION and CHANGELOG in sync Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * fix: port the mcp 2.x migration from #528 so typecheck is green The typecheck gate is red on main itself: the unpinned mcp extra now resolves to mcp 2.x, where FastMCP was renamed MCPServer, so mypy fails on mcp_server.py on every branch. This ports the migrated server and the mcp>=2.0 floor verbatim from #528; the diff no-ops once #528 merges. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * test: cover the _fitted_n_components fallback branches Codecov flagged the TPLS-parent delegation, the plain-integer fallback, and the unfitted-model raise as uncovered on the patch; pin all four resolution paths directly. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU --------- Co-authored-by: Claude <noreply@anthropic.com>
…e-library-issues-hr7xxk-mcp # Conflicts: # CHANGELOG.md # CITATION.cff # pyproject.toml
kgdunn
added a commit
that referenced
this pull request
Aug 29, 2026
… (#526) * Make Plotly annotation tests assert the rendered structure; stop theme leak The four TestPlotlyAnnotationRendering tests asserted only that the figure dict has a "data" key, which is true of every Plotly figure, so they passed whether or not the annotation rendered at all. They now pin layout.shapes (count, x0/x1 vs y0/y1, axis refs, fillcolor) and layout.annotations, and the skipped-band test compares against a band-free baseline. test_set_theme_changes_default restored the package DEFAULT_THEME instead of the value in effect before the test, mutating plotly.io.templates.default for every later test in the worker; it now restores the captured prior value via monkeypatch. The background-colour test's docstring claimed each theme has a distinct background while asserting two identical white values; it now pins all four values without the distinctness claim. Closes #512. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * Fix ECharts adapter silent-wrong-output defects; raise on unimplemented members Annotations now attach to a series of the panel they belong to: the single-panel and multi-panel paths share one _attach_annotations helper, and a panel with annotations but no layers gets an empty carrier series bound to its own axes instead of painting the previous panel (the old code attached to the cumulative all_series[-1]). Missing data fields raise KeyError instead of silently plotting 0, in _paired_data and _wireframe_series, matching the Plotly adapter's direct row[field] lookups. A ragged z_matrix in _heatmap_series raises ValueError naming the expected shape instead of filling missing cells with 0. Per-point style lists (colors, error_y) whose length differs from the data raise ValueError naming the layer and the style key; the three previously non-strict zips are now strict=True, matching visualization/tools.py. MarkType.area and AnnotationType.label are declared in types.py but were implemented in neither adapter; both adapters now raise NotImplementedError for them instead of silently falling back to a scatter trace or dropping the annotation. Closes #509. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * Bump version to 1.73.1; add changelog entry for #509 and #512 Version assumes the queued PRs carrying 1.72.x/1.73.0 merge first. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * fix: port the mcp 2.x migration from #528 so typecheck is green The typecheck gate is red on main itself: the unpinned mcp extra now resolves to mcp 2.x, where FastMCP was renamed MCPServer, so mypy fails on mcp_server.py on every branch. This ports the migrated server and the mcp>=2.0 floor verbatim from #528; the diff no-ops once #528 merges. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU --------- Co-authored-by: Claude <noreply@anthropic.com>
…e-library-issues-hr7xxk-mcp # Conflicts: # CHANGELOG.md # CITATION.cff # pyproject.toml
test_budget_clamped_to_minimum_model_size failed intermittently on this PR's CI (macOS, assert 3 >= 4) in code this PR does not touch. Root cause and fix are in #531 (a k+1 budget floor on the pyoptex path, a greedy run-count completion in point_exchange, and a singular-design scoring guard); ported verbatim here so this PR's CI is deterministic. No-ops once #531 merges. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU
Owner
Author
|
The macOS failure on this head was Generated by Claude Code |
kgdunn
added a commit
that referenced
this pull request
Aug 29, 2026
…least-privilege jobs (#507) (#525) * fix(release): close the dispatch, SBOM, and Makefile holes in the publish pipeline (#507) The ENG-21 gate promised that a typo in pyproject.toml on main cannot ship a release, but three paths around the gate remained. This closes them: - publish.yml: the version guard now also runs on workflow_dispatch, which becomes a republish-only path: the checked-out commit must already carry the v<version> tag for the version in pyproject.toml. - publish.yml: the build job additionally asserts CHANGELOG.md has a matching "## [X.Y.Z]" heading (mirroring the extraction regex), and the notes-extraction step fails loudly instead of silently falling back to auto-generated notes. - publish.yml: gh release create passes --target with the SHA of the checked-out commit (HEAD, not GITHUB_SHA, since a dispatch run may check out inputs.ref), so the release tag lands on the published commit rather than the default branch head. - publish.yml: the SBOM is generated from a fresh venv containing only the built wheel and its runtime closure, with pip/setuptools/wheel removed; cyclonedx-bom runs from a separate tooling venv so build tooling no longer ships as runtime components. - publish.yml: permissions are per-job. The build job, which executes repo code via PEP 517 hooks, holds only contents: read; id-token, contents, and attestations write scopes stay on the publish job. - publish.yml: pypa/gh-action-pypi-publish is pinned to the commit SHA that release/v1 resolves to (v1.14.2) instead of the mutable branch. - Makefile: the release target no longer builds or publishes; it prints the tag-gated release instructions and exits nonzero. Version 1.73.3; CITATION.cff and CHANGELOG.md updated in step. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * fix: port the mcp 2.x migration from #528 so typecheck is green The typecheck gate is red on main itself: the unpinned mcp extra now resolves to mcp 2.x, where FastMCP was renamed MCPServer, so mypy fails on mcp_server.py on every branch. This ports the migrated server and the mcp>=2.0 floor verbatim from #528; the diff no-ops once #528 merges. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * fix: port the point_exchange/pyoptex-budget deflake from #531 test_budget_clamped_to_minimum_model_size fails intermittently on any PR's CI (macOS, assert 3 >= 4) in code this PR does not touch. Root cause and fix are in #531; ported verbatim so this PR's CI is deterministic. No-ops once #531 merges. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU --------- Co-authored-by: Claude <noreply@anthropic.com>
kgdunn
added a commit
that referenced
this pull request
Aug 29, 2026
…at can fail (#508, #510, #511) (#529) * fix(experiments): bound the remote dataset fetch with an explicit timeout pd.read_csv(url) carries no timeout, so distillateflow() / oildoe() against a black-holing host blocked the caller indefinitely (#508). _read_remote_csv now fetches with urllib.request.urlopen(url, timeout=...) into a buffer and hands that to pandas. The default of 30 s lives on the config settings singleton as dataset_fetch_timeout, overridable via PROCESS_IMPROVE_DATASET_FETCH_TIMEOUT or a caller argument. Timeouts and URL errors still surface as the documented RuntimeError naming the URL. An on-disk cache stays out of scope. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * test(perf): replace non-asserting benchmarks with deterministic ENG-18 assertions The tests in tests/perf/ contained no assertions, had no stored baseline, and pytest-benchmark disables itself under the default -n auto, so a 100x slowdown passed green while the docstrings claimed regression tracking (#511). Option 2 from the issue: assert the cost-shape properties the ENG-18 lazy-frame work actually needs protected. The public DataFrame views are built exactly once and then cached (identity plus a counted single build), pickling excludes the cache and the views rebuild correctly, repeated transform / predict / diagnose calls cause zero lazy-frame rebuilds, and check_random_state passes a Generator through by identity while resolving an int seed to a fresh generator with bit-identical draws. Verified the new tests fail when the _LazyFrame cache write is removed. CONTRIBUTING.md's performance-regression policy now describes this honestly; the ENG-15 wall-clock benchmark CI job stays planned. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * test(experiments): skip the live openmv.net tests on the wrapped RuntimeError The loaders now wrap network failures (including timeouts) in the module's documented RuntimeError, so the offline skip helper must catch that too instead of failing when openmv.net is unreachable. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * fix: port the mcp 2.x migration from #528 so typecheck is green The typecheck gate is red on main itself: the unpinned mcp dependency resolves to mcp 2.x, where FastMCP was renamed MCPServer, so mypy fails on src/process_improve/mcp_server.py. This ports the #528 fix verbatim (mcp_server.py plus the mcp>=2.0 pin in both pyproject.toml occurrences) so this PR's CI can pass on its own. It becomes a no-op once #528 merges; this PR's own version bump and CHANGELOG entries for #508/#510/#511 are in a separate commit. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * test: make the ENG-29 test tiers real with strict markers and measured slow marks The tiers were documented but unused: -m 'not slow' deselected almost nothing and -m 'not dataset' still hit the network (#510). - pytest.ini addopts gains --strict-markers, so a typo'd marker is a collection error instead of a silent warning. - test_pca_foods in tests/test_multivariate.py, the one test fetching from openmv.net, now carries @pytest.mark.dataset; a tests/ sweep for urlopen / openmv / http found no other unmarked network fetches. - 42 tests measured at >= 5 s with --durations on this runner now carry @pytest.mark.slow (threshold is 2 s per CONTRIBUTING.md; the margin keeps the marks honest on faster machines). The two end-to-end tests crossing library boundaries (sensory pipeline, sklearn halving grid search) also carry @pytest.mark.integration. - Wall clock: full suite 6:15, -m 'not slow and not dataset' 1:32 on the same runner. - Both CI test jobs run plain pytest with no -m filter, so every tier still executes in CI. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * chore: bump version to 1.73.4 with citation and changelog in sync PATCH bump for the #508/#510/#511 test-hygiene fixes. CITATION.cff carries the same version and today's date-released; CHANGELOG.md gains the 1.73.4 section and updated link footer. The version assumes the earlier queued 1.73.x PRs merge first. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * test: bind both accesses to names in the loadings cache-identity assertion CodeQL flags a self-comparison; two named reads assert the same cache-identity property without the identical-values pattern. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * fix: port the optimal-design budget floor from #531 so the flaky clamp test is deterministic test_budget_clamped_to_minimum_model_size fails intermittently because the pyoptex path never implemented the documented k + 1 budget floor; ported verbatim from #531, no-ops once it merges. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * fix: port the point_exchange run-count completion from #531 The dispatcher budget floor alone was not sufficient: the shortfall happens inside point_exchange, which could end below number_points on 1 to 2% of unseeded runs. Ported verbatim from #531; no-ops once it merges. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * fix: port the point_exchange singular-design scoring guard from #531 Completes the earlier port: non-finite scores from a numerically singular X'X now read as unacceptable rather than unbeatable, which was the underlying freeze behind the run-count shortfall. No-ops once #531 merges. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU --------- Co-authored-by: Claude <noreply@anthropic.com>
kgdunn
added a commit
that referenced
this pull request
Aug 29, 2026
…ic multiblock starts, one TPLS T2 (#502, #503, #504) (#530) * Make NIPALS convergence scale-relative across PCA, PLS, MBPCA and MBPLS terminate_check compared the raw norm of the change between successive score vectors against an absolute tolerance, so whether the loop converged depended on the magnitude of the data: large-magnitude X burned every md_max_iter iteration, tiny X converged instantly. The criterion is now the relative form already used by TPLS's _has_converged: the norm of the change divided by the norm of the current vector, floored via _nz so a fully-deflated all-zero score vector cannot divide by zero. The signature and the md_tol settings key are unchanged; md_tol is now interpreted as a relative tolerance. The MBPCA and MBPLS inner loops get the same relative criterion, and their absolute default tolerances at machine precision (eps**(9/10) and eps**(6/7)), which as relative tolerances sit below the floating-point oscillation floor, become the shared epsqrt default used by PCA, PLS and TPLS. In the same loops, the hard-coded np.random.default_rng(0) start for the MBPCA super-score and the MBPLS u vector is replaced by a deterministic seed from the column with the largest sum of squares, the way single-block PCA / PLS already seed (#195): no RNG remains in these fit paths, satisfying the reproducibility contract (the determinism tests for #503 follow in the next commit). Fitted components are unchanged up to the existing sign convention. Fixes #504. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * Add determinism tests for the multiblock deterministic NIPALS start MBPCA and MBPLS previously seeded their NIPALS super-score / u initialisation from a hard-coded np.random.default_rng(0), violating the reproducibility contract in docs/development/reproducibility.rst, and exposed no random_state. The previous commit removed the RNG from both fit paths in favour of a deterministic seed from the column with the largest sum of squares, matching single-block PCA / PLS (#195), so no random_state parameter is needed at all. These tests pin the contract: repeated fits are bit-identical, and no non-convergence warning fires on well-conditioned data. Fitted components on the reference and oracle suites are unchanged up to the existing sign convention. Fixes #503. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * Compute one Hotelling's T2 in TPLS, shared by fit() and diagnose() TPLS.fit() computed a full-Mahalanobis T2 over the super-scores with the covariance divided by N, shape (n_obs, 1), while diagnose() computed the cumulative diagonal form, shape (n_obs, n_components): two different statistics under one name, compared against one limit. Both now use the cumulative per-component form sum_a (t_a / s_a)^2 already used by PCA and PLS, which is what the class docstring promised, matches the sibling estimators, and extends to new observations without the training covariance. The score standard deviations (scaling_factor_for_scores, which also feeds ellipse_coordinates) use the unbiased N - 1 divisor, the estimator the F-distribution form in hotellings_t2_limit is derived for; the previous N divisor inflated T2 by N / (N - 1) and drew score ellipses slightly too small. The pinned test values change accordingly: the training super-scores are orthogonal (normalized off-diagonal cross-products ~1e-17), so the old full-Mahalanobis values equal the diagonal form with the N divisor, and the new pins are exactly the old ones times (N - 1) / N = 104 / 105. A new test asserts fit() and diagnose() return the same T2, at the same shape, for the same rows. Fixes #502. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * Bump version to 1.74.0 and add the changelog entry for #502/#503/#504 MINOR bump: all three fixes change numerical results or iteration behaviour, because the previous values were wrong. CITATION.cff is kept in sync in the same commit. The version assumes the queued sibling PRs land first in order. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * Appease ruff: rename a shadowing loop variable and apply formatting Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * fix: port the mcp 2.x migration from #528 so typecheck is green The typecheck gate is red on main itself: the unpinned mcp dependency resolves to mcp 2.x, where FastMCP was renamed MCPServer, so mypy fails on src/process_improve/mcp_server.py. This ports the #528 fix verbatim (mcp_server.py plus the two mcp>=2.0 requirement bumps) and no-ops once #528 merges. The version bump and changelog entries for #528 are deliberately not taken here. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * Always run the first NIPALS iteration; relax one kernel-vs-NIPALS rtol Two CI follow-ups on the scale-relative convergence rework (#504). The PCA / PLS loops prime their convergence check with a seed offset of +1.0. Under the relative criterion that offset can be negligible for a large-magnitude seed column (a fuzzed Y entry of ~1.9e154), so the loop reported convergence before its first iteration: PLS then raised UnboundLocalError for the never-assigned t_a through the fit_pls tool boundary, and PCA would have silently returned the unrefined seed as the score with an all-zero loading. Both loops now force the first iteration with an explicit itern == 0 check, restoring the pre-rework behaviour on such inputs, and a deterministic (non-hypothesis) regression test pins the fuzz payload. test_adaptive_pls_initial_matches_batch_ldpe compared kernel-PLS and NIPALS beta coefficients at the assert_allclose default rtol of 1e-7; the relative stopping rule now ends the batch fit once successive score iterations agree to epsqrt (~1.49e-8) relative accuracy, and the agreement on this fixture sits at 1.07e-7 relative on one of 70 elements. Both solvers are converged to their own criteria, so that one assertion is relaxed to rtol=1e-6 with an explanatory comment; nothing else is loosened. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * fix: port the point_exchange/pyoptex-budget deflake from #531 test_budget_clamped_to_minimum_model_size fails intermittently on any PR's CI (macOS, assert 3 >= 4) in code this PR does not touch. Root cause and fix are in #531; ported verbatim so this PR's CI is deterministic. No-ops once #531 merges. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU --------- Co-authored-by: Claude <noreply@anthropic.com>
kgdunn
added a commit
that referenced
this pull request
Aug 29, 2026
…s CI) (#531) * fix(experiments): floor the optimal-design budget at k + 1 on the pyoptex path The point-exchange fallback already raises a budget below k + 1 to k + 1 (the smallest run count that can estimate the main-effects model), and the tests document that clamp as the contract. The pyoptex path passed the budget through unclamped, and pyoptex handles an infeasible run count unpredictably, which made test_budget_clamped_to_minimum_model_size fail intermittently across unrelated PRs. Apply the same floor in the d-, i-, and a-optimal dispatchers, except when the requested budget cannot even hold the fixed_runs, so that validation still raises on the requested value. Part of the DOE cluster tracked in #513. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * chore: bump version to 1.74.1, sync CITATION and CHANGELOG, mcp>=2.0 floor The mcp floor accompanies the mcp 2.x server port from #528 included in the previous commit; both no-op once #528 merges. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * fix(experiments): point_exchange always returns the requested run count The exchange loop grows the design only when an addition improves the criterion, and a candidate consumed by a replacement is never considered for addition, so an unlucky shuffle ended below number_points on roughly 1 to 2% of unseeded runs; this is the actual mechanism behind the intermittent 'assert 3 >= 4' in the D-optimal dispatch test (the dispatcher-level budget floor in the previous commit is necessary for the contract but not sufficient, because the shortfall happened inside point_exchange itself). Complete the design greedily with the remaining candidates that best preserve D-optimality; measured 500/500 correct sizes after the fix versus 492/500 before. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * test: pin seeds that exercise the point_exchange completion path Seeds 122, 146, 199, 319, and 338 ended with 3 rows under the pre-fix algorithm, so they cover the greedy completion loop that codecov flagged as untested; the low seeds keep the ordinary path covered. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU * fix(experiments): score numerically singular designs as unacceptable in point_exchange optimization_function guarded only the exactly singular case via LinAlgError; a numerically singular X'X passes np.linalg.inv and its garbage inverse can carry an exactly zero determinant, scoring log|det| = -inf. Under the lower-is-better convention that ranked the singular design as unbeatable, so no replacement or addition could ever improve on it: this froze the exchange loop (the underlying mechanism of the run-count shortfall) and let the new completion step pick a rank-deficient row (the pinned regression seeds caught it via a non-finite d_opt). Map any non-finite score to +inf, matching the existing convention for the exactly singular case. Verified 300/300 unseeded runs and all pinned seeds now return full-rank, correctly sized designs with finite scores. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019C3XXbJkuSYH9fMryLqNcU --------- Co-authored-by: Claude <noreply@anthropic.com>
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.
Summary
@tool_spectool from anasync def handler(**kwargs)closure and assigned__annotations__/__defaults__afterwards, which cannot synthesise a real signature, so all tools were published to MCP clients with an emptyinputSchema. Tools are now registered as explicitmcp.server.mcpserver.tools.Toolobjects whoseparametersfield, published verbatim bylist_tools(), is exactly the registry'sspec["input_schema"]fromget_tool_specs(). Types, required vs optional, enums, bounds, descriptions, andanyOfunions (int | None,Literal[...]) all survive; nothing defaults to string.MCPServer, renamed from 1.x'sFastMCP). The existingmcp>=1.0pin already resolved to 2.x, under which the oldfrom mcp.server.fastmcp import FastMCPfailed at import (the tests silently skipped, and this was mypy's one standing error, now gone). Themcpextra pinsmcp>=2.0. Argument validation is unchanged and stays inexecute_tool_call, which validates against each tool's real pydantic input model (SEC-15); the hand-builtFuncMetadatapasses arguments through untouched.mcp_server.pyis removed from the.coveragercomit list; the new tests bring it to 100% line and branch coverage, so the 92% gate is unaffected.elapsed < 1.7 * _SLEEP) and could flake on loaded CI runners; it now proves the overlap with athreading.Barrierboth in-flight calls must reach, which a serialised dispatch can never satisfy.Version is bumped to 1.73.2 (PATCH), with
CITATION.cffandCHANGELOG.mdupdated in the same commit. The version assumes the earlier queued PRs (through 1.73.1) merge first.Test plan
test_published_schemas_match_registry_exactlydrives the real registration path (create_server()thenlist_tools()) and asserts, for every registered tool, that the publishedinputSchemaand description equalget_tool_specs()'s entries.test_optional_and_union_parameters_surviveassertsanyOfunions andrequiredlists reach the published schemas (guards against regressing to signature introspection).test_call_tool_dispatches_raw_argumentsexercises the realcall_tool()path end to end.ToolSafetyErrorpayload, generic non-leaking error, safe-mode routing, non-dict results) andmain()covered;mcp_server.pyat 100% coverage.uv run pytest tests/test_mcp_server.py tests/test_tool_spec.py --no-cov -q: 87 passed. Requires themcpextra (uv sync --dev --all-extras); the tests skip without it viapytest.importorskip("mcp.server.mcpserver").test_oildoe_loads,test_distillateflow_loads) blocked by this sandbox's proxy (openmv.net returns 403); they are unrelated to this change.uv run ruff check .anduv run ruff format --check .both pass.uv run mypy src/process_improve: no issues (previously 1 error in this module).Checklist
pyproject.toml(PATCH for fixes/docs/config, MINOR for new features)ruff check .passesCHANGELOG.mdupdatedGenerated by Claude Code