fix: version drift, bypassed output validation, and tests shipped in the wheel - #5
Open
PietjePuh wants to merge 3 commits into
Open
Conversation
The server hardcoded version = "0.1.3" while pyproject.toml declared 0.1.6, so every MCP client was told the wrong server version. Read the version from installed distribution metadata instead, with a fallback for source-checkout runs (`fastmcp run jules_mcp/jules_mcp.py`) where no distribution metadata exists.
conftest monkeypatched ParsedFunction.from_function to rewrite the return
annotation of create_session, get_session and wait_for_session_completion
to `dict`, which disabled MCP output-schema validation for those three
tools. It was there to accommodate a malformed fixture: mock_session_dict
carried a top-level "source" key (not a field on models.Session) and
"source_context": {"source_name": ...}, but SourceContext requires
"source".
Correct the fixture and drop the patch, so the three session tools are
now covered by real output-schema validation. Also add TestVersion to
guard against the version drift fixed in the previous commit.
Author
|
Follow-up: I've now validated this against a live Jules API account, not just mocks. That was the one real risk in this PR — the existing fixtures return plain Read-only calls only ( Two things this confirms:
Account-identifying details (source names, session titles) omitted deliberately. |
pyproject.toml had no [build-system] table, and
[tool.setuptools.packages.find] had no include filter. Two consequences:
1. setuptools discovered `tests` alongside `jules_mcp`, so the published
wheel exported BOTH as top-level packages. top_level.txt read
"jules_mcp\ntests", meaning `pip install jules-mcp` dropped a
top-level `tests` package into the user's site-packages, where it can
collide with anything else of that name.
2. Without [build-system], uv classified the project as virtual
(`source = { virtual = "." }` in uv.lock) and never installed it, so
importlib.metadata could not resolve the distribution and the version
lookup always took the source-checkout fallback path.
Declare the setuptools backend and constrain discovery to jules_mcp*.
The wheel now contains only jules_mcp/ and top_level.txt reads
"jules_mcp"; uv.lock flips to `source = { editable = "." }` and the
project installs, so the version is read from real metadata.
uv.lock is regenerated because `uv sync --locked` in CI would otherwise
fail against the changed project source type.
Adds TestPackaging to guard both regressions. Verified `uvx twine check`
passes on the rebuilt wheel and sdist.
tests shipped in the wheel
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.
Three independent fixes, one commit each. Full CI gate passes locally (
ruff check,ruff format --check,mypy,pytest), plusuvx twine checkon the rebuilt artifacts and a read-only smoke test against the live Jules API.1. The server advertises the wrong version
jules_mcp/jules_mcp.pyhardcodedversion: Final[str] = "0.1.3"whilepyproject.tomldeclares0.1.6.FastMCP("Jules MCP Server", version=version)passes that straight to clients, so every MCP client has been told the wrong version.Now read from installed distribution metadata via
importlib.metadata, with a fallback constant for source-checkout runs (fastmcp run jules_mcp/jules_mcp.py, used by the Dockerfile andMCP.json).2. Output-schema validation was disabled for three tools
tests/test_jules_mcp/conftest.pymonkeypatched FastMCP internals at module-load time:Rewriting the return annotation to
dictreplaces the generatedSessionoutput schema with a permissive one, so a quarter of the tool surface ran with no meaningful output validation.It was compensating for a malformed fixture, not a library bug.
mock_session_dicthad a top-level"source"key — not a field onmodels.Session— and"source_context": {"source_name": ...}wheremodels.SourceContextrequiressource. The old fixture fails withOutput validation error: 'source' is a required propertyonce the patch is removed.Fixture corrected, patch deleted. Verified all 12 tools still expose a non-null
outputSchemawithout it.3. The published wheel ships a top-level
testspackagepyproject.tomlhad no[build-system]table, and[tool.setuptools.packages.find]had noincludefilter. Building0.1.6as-is produces:So
pip install jules-mcpinstalls a top-leveltestspackage into the user'ssite-packages, where it can shadow or collide with anything else of that name. This affects the released artifact, not just the repo.The missing
[build-system]had a second effect: uv classified the project as virtual (source = { virtual = "." }inuv.lock) and never installed it, soimportlib.metadatacould not resolve the distribution and the fix in (1) always took its fallback path.Declaring the setuptools backend and constraining discovery to
jules_mcp*fixes both. After the change:uv.lockis regenerated in the same commit because CI runsuv sync --locked, which would otherwise fail against the changed project source type. The lock diff is a single line.Regression guards
Added
TestVersionandTestPackaging. Both were confirmed to fail when the original defects are reintroduced, rather than passing vacuously:0.1.3→ both version assertions failincludefilter →test_tests_package_is_not_shippedfailsVerification
Also exercised read-only against a live Jules API account —
list_sources,get_all_sources,list_sessions,get_session,list_activitiesall pass, confirming realSessionpayloads validate against the schema this PR re-enables. Details in the comment below.Not included
The Dockerfile looks like it has a separate issue:
RUN uv sync --no-devbuilds/app/.venv, but theENTRYPOINTisuv run --with fastmcp --with jules-agent-sdk --with requests fastmcp run .... Those unpinned--withflags re-resolve dependencies at container start, bypassinguv.lockand requiring network at runtime. I had no Docker daemon available to build-test a change, so I left it out rather than ship something unverified. Happy to open a separate PR.