fix(sdk): honor manifest python command, ABI-validate before spawn#106
Open
mgoldsborough wants to merge 4 commits intomainfrom
Open
fix(sdk): honor manifest python command, ABI-validate before spawn#106mgoldsborough wants to merge 4 commits intomainfrom
mgoldsborough wants to merge 4 commits intomainfrom
Conversation
- Add `compatibility` block (`runtimes.python`, `runtimes.node`, `platforms`, plus catchall for client version constraints). - Make `mcp_config` optional on `ManifestServerSchema` — MCPB v0.4 lets `type: "uv"` bundles omit it for host-managed execution. - Make `mcp_config.command` and `mcp_config.args` optional within `McpConfigSchema`; resolver supplies sensible defaults per server type. - Normalize SDK resolver to read `mcp_config` via nullish coalescing so the now-optional shape is a no-op for existing bundles. - Add manifest.test.ts with 16 cases including the verbatim hello-world-uv manifest from the MCPB spec, pinning conformance. Pre-step for issue #90 work: surfaces `compatibility.runtimes.python` to the resolver and unblocks parsing of spec-compliant uv bundles. Refs #90
Replaces the candidate-name parade in `findPythonCommand` with a single
ABI-validated probe.
Resolution order:
1. MPAK_PYTHON env var (explicit override)
2. mcp_config.command from the manifest
3. literal "python3"
Validation:
- Walks deps/**/*.{so,pyd,dylib} to extract the cpython ABI tag
(e.g. "cpython-313"). For abi3 wheels, reads the floor from the
extension filename and accepts any cpython >= floor.
- Probes the chosen interpreter once via
`python -c "sys.implementation.cache_tag"` and requires it to match.
- Cross-checks `compatibility.runtimes.python` (semver) when declared.
Failure path emits an actionable message — including how to set
MPAK_PYTHON or install the right interpreter — instead of letting the
bundle crash mid-import with `ModuleNotFoundError: No module named 'rpds.rpds'`.
On success, writes one stderr line at startup so users never have to
ask "which Python is mpak actually running?":
[mpak] python: python (3.13.0, cpython-313) via manifest
Test seam: optional `pythonProbe` on `MpakOptions` lets tests stub the
interpreter probe so the suite doesn't depend on which Python is on the
runner's PATH.
Closes #90
Two gaps in the existing `case 'uv'` resolver:
1. Bundles requesting type:uv on a host without uv installed crashed
mid-spawn with a raw ENOENT — the user had no signal that "uv is
the missing piece." Now the resolver probes `uv --version` first
and throws an error with platform-specific install commands.
2. Default args (`["run", join(cacheDir, entry_point)]`) only worked
because the CLI happens to set `cwd: cacheDir` — embedders that
don't honor `server.cwd` couldn't find pyproject.toml. Default now
matches the upstream `examples/hello-world-uv` shape exactly:
`["run", "--directory", cacheDir, entry_point]`. CWD-independent.
Adds optional `uvProbe` test seam on `MpakOptions` (mirrors `pythonProbe`).
Logs resolved uv version to stderr at startup, mirroring the python
resolver pattern.
7 new tests:
- uv-resolver.test.ts (5): default args, manifest args passthrough,
absolute uv path, missing-uv error, version surfacing
- mpak.test.ts (2): end-to-end resolution with spec-canonical default,
missing-uv propagates through prepareServer
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.
Closes #90.
Summary
python→python3rewrite with a single ABI-validated probe. Resolution order:MPAK_PYTHONenv override →mcp_config.commandfrom the manifest → literalpython3.deps/**/*.{so,pyd,dylib}to extract the cpython ABI tag; probe the chosen interpreter once viasys.implementation.cache_tagand require it to match. abi3 wheels read the floor from the extension filename and accept any cpython ≥ floor.compatibility.runtimes.python(semver) when declared.MPAK_PYTHONor install the right interpreter — instead of letting the bundle crash mid-import withModuleNotFoundError: No module named 'rpds.rpds'.[mpak] using python = /Users/.../python (3.13.5)— removes "what's it actually running?" entirely.Plus two adjacent fixes the same investigation surfaced:
type: uvresolver gapsuv --versionfirst; throw with platform-specific install commands when missing instead of crashing mid-spawn with rawENOENT.type: uvnow match the upstreamexamples/hello-world-uvshape exactly:["run", "--directory", cacheDir, entry_point]— CWD-independent. The previous default only worked because the CLI happened to setcwd: cacheDir; embedders that don't honorserver.cwdcouldn't findpyproject.toml.Schema alignment with MCPB v0.4
compatibilityblock (runtimes.python,runtimes.node,platforms, plus catchall for client version constraints).mcp_configoptional onManifestServerSchema— MCPB v0.4 letstype: "uv"bundles omit it for host-managed execution.mcp_config.commandandmcp_config.argsoptional withinMcpConfigSchema; resolver supplies sensible defaults per server type.compatibility.runtimes.pythonto the resolver and unblocks parsing of spec-compliant uv bundles.Test plan
pnpm typecheck— greenpnpm lint— greenpnpm test— green@nimblebrain/mpak-schemas: 115 tests (was 99 — +16 manifest cases)@nimblebrain/mpak-sdk: 277 tests (was 244 — +33: 18 python-resolver, 5 uv-resolver, 10 mpak end-to-end)@nimblebrain/mpak-registry: 130 tests (unchanged)@nimblebrain/mpak-web: 61 tests (unchanged)@nimblebrain/mpak: 174 tests (unchanged)Repro from the issue
python3(e.g. macOS system/usr/bin/python3is 3.9; Homebrew is 3.11).uv python install 3.13 --default --preview— installs 3.13 and creates~/.local/bin/pythonbut notpython3.mpak bundle run @nimblebraininc/synapse-todo-boardBefore this PR:
ModuleNotFoundError: No module named 'rpds.rpds'deep in an import chain. ~2 hours to debug to root cause.After this PR:
Rebased
Rebased onto current main (which includes the just-merged #100). One conflict in
packages/schemas/src/manifest.ts+tests/manifest.test.ts— kept main'sSafeRelativePathSchemasecurity check onentry_pointand adopted this branch's optional-mcp_configchange for v0.4 compliance. Both test suites union'd cleanly.