mcp-proxy: support both mcp 1.x and 2.x (drop the <2 cap) (#307) - #338
Merged
sunishsheth2009 merged 3 commits intoAug 14, 2026
Conversation
Collaborator
Author
|
Reworked per maintainer direction: instead of keeping the One code path handles both because mcp 1.28+ and 2.x both export Verified against both mcp 1.28.1 and mcp 2.0.0 in a real venv: proxy imports, transport composes, 21 proxy tests + |
This was referenced Aug 14, 2026
sunishsheth2009
marked this pull request as ready for review
August 14, 2026 19:50
xsh310
reviewed
Aug 14, 2026
xsh310
approved these changes
Aug 14, 2026
sunishsheth2009
force-pushed
the
sunish-sheth_data/mcp-proxy-compat-guard
branch
from
August 14, 2026 21:09
0e8cabe to
cbd037c
Compare
…atabricks#307) A fresh `ucode` install used to resolve `mcp 2.0.0` off an unconstrained `mcp>=1.28.0`, and `ucode mcp-proxy` then crashed on import because 2.x renamed `streamablehttp_client` (and swapped httpx for httpx2). Every MCP server configured through the proxy failed to connect, surfaced to the coding agent as a generic connection failure with no hint at the real cause. The dependency cap (`mcp>=1.28.0,<2`) already landed in databricks#311. This adds the remaining two acceptance criteria from the issue: - **Actionable compatibility error.** `ucode mcp-proxy` catches the ImportError at the proxy import site and prints a reinstall instruction naming the installed version and the supported range, then exits non-zero — instead of an opaque traceback. The message is built in cli.py (not mcp_proxy) because mcp_proxy is exactly the module that fails to import. - **Compat tests.** Assert the pin stays `<2`; assert the *resolved* mcp SDK satisfies it and that `mcp_proxy` imports against it (the clean-install guard, at test time); and cover the cli guard end-to-end (exit 1 + guidance). Verified: simulated an mcp-2.x-style import failure and confirmed the CLI exits 1 with the reinstall hint and no traceback. Full suite: 1788 passed (2 pre-existing live-gateway e2e failures unrelated); ruff + ty clean. Fixes databricks#307 Co-authored-by: Isaac
) A fresh `ucode` install resolved mcp 2.0.0 off `mcp>=1.28.0` and `ucode mcp-proxy` crashed on import, because 2.x renamed `streamablehttp_client` to `streamable_http_client` and moved from httpx to httpx2. databricks#311 had capped `mcp<2` to stop the bad resolution; this instead makes the proxy work against both majors so the cap can be removed and a clean install on mcp 2.x just works. Key realization: both mcp 1.28+ and 2.x export the 2.x-native `streamable_http_client(url, http_client=<AsyncClient>)` (1.x's `streamablehttp_client` is a deprecated shim over it). So a single code path works if we (a) build the AsyncClient/Auth from the httpx flavor the installed SDK uses, and (b) tolerate the yield arity change. - `_httpx()` resolves httpx2 (mcp 2.x) else httpx (mcp 1.x), via importlib so the type checker/CI (pinned to 1.x, no httpx2) don't choke on a static import. - `_build_token_auth()` subclasses that flavor's `Auth` — its `auth_flow` generator contract is identical in httpx and httpx2. - `_run` unpacks `streams[0], streams[1]`: mcp 1.x yields (read, write, get_session_id), mcp 2.x yields (read, write); we don't use the trailing callback. - pyproject: `mcp>=1.28.0,<2` -> `mcp>=1.28.0`; httpx stays a direct dep for the 1.x path, httpx2 comes in transitively with mcp 2.x (no direct dep needed). Verified against BOTH majors in a real venv: proxy tests (21) pass and `ty check src/` is clean under mcp 1.28.1 AND mcp 2.0.0; under 2.x the proxy imports (the databricks#307 crash point), selects httpx2, composes httpx2.AsyncClient(auth=...) + streamable_http_client(http_client=...), and reaches the auth preflight. Full suite: 1788 passed (2 pre-existing live-gateway e2e failures, unrelated). ruff clean. Fixes databricks#307 Co-authored-by: Isaac
`ucode mcp-proxy --use-pat` didn't actually work: `databricks auth token` only
reads OAuth caches, so a static-PAT profile is never resolved by the per-request
token mint on its own — the PAT has to be exported as DATABRICKS_BEARER first
(ensure_pat_bearer). Nothing in the proxy did that, so PAT — a previously
supported auth path — silently failed in the mcp-2 rework.
`serve` now calls `ensure_pat_bearer(profile)` when `use_pat` is set (before the
token preflight), exporting the profile's PAT so every per-request mint takes
the DATABRICKS_BEARER short-circuit; it fails fast with an actionable message if
no PAT is found. This lives in `serve` (which already owns the proxy's auth
preflight + fail-fast), not the CLI, so the command stays a thin forwarder:
serve(url, workspace, profile, use_pat=use_pat or bool(state.get("use_pat")))
`_run` takes no use_pat — once the PAT is in the env, the transport path is
identical to OAuth.
Tests: test_mcp_proxy covers serve's PAT handling (exports the bearer before
serving; missing PAT exits before the bridge opens; OAuth path never consults
ensure_pat_bearer); test_cli asserts the flag/saved-state is forwarded to serve.
Full suite 1794 passed (2 pre-existing live-gateway e2e failures). ruff + ty clean.
Co-authored-by: Isaac
sunishsheth2009
force-pushed
the
sunish-sheth_data/mcp-proxy-compat-guard
branch
from
August 14, 2026 22:11
cbd037c to
39d6482
Compare
sunishsheth2009
enabled auto-merge (squash)
August 14, 2026 22:21
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
Fixes #307. A fresh
ucodeinstall resolved mcp 2.0.0 offmcp>=1.28.0, anducode mcp-proxycrashed on import — 2.x renamedstreamablehttp_client→streamable_http_clientand moved from httpx to httpx2. Every MCP server through the proxy then failed to connect.PR #311 capped
mcp<2to stop the bad resolution. This PR instead makes the proxy work against both mcp 1.x and 2.x and removes the cap, so a clean install on either major just works.How one code path covers both majors
The key realization: both mcp 1.28+ and 2.x export the 2.x-native call shape
streamable_http_client(url, http_client=<AsyncClient>)(1.x'sstreamablehttp_clientis a deprecated shim over it). Only two things differ across the majors, and both are handled:httpxhttpx2_httpx()resolves whichever the SDK uses(read, write, get_session_id)(read, write)_runtakesstreams[0], streams[1]httpx.Authhttpx2.Auth_build_token_auth()subclasses the resolved flavor_httpx()importshttpx2(mcp 2.x) elsehttpx(mcp 1.x), viaimportlibso the type checker / CI — pinned to mcp 1.x, no httpx2 present — don't choke on a staticimport httpx2._build_token_auth()builds the per-request bearerAuthfrom that flavor;auth_flow's generator contract is identical in httpx and httpx2.mcp>=1.28.0,<2→mcp>=1.28.0.httpxstays a direct dep (the 1.x path);httpx2arrives transitively with mcp 2.x, so no direct httpx2 dep is declared.The fail-fast auth behavior from #201's follow-up is preserved unchanged.
How do you know it works?
Verified in a real venv against both majors (not just the pinned one):
ty check src/clean; full suite 1788 passed.--no-sync): the proxy imports (the [Bug] Fresh ucode install pulls mcp 2.0 and mcp-proxy crashes on a removed API #307 crash point is gone),_httpx()selectshttpx2, andhttpx2.AsyncClient(auth=…)+streamable_http_client(http_client=…)compose without error; 21 proxy tests pass;ty check src/clean.streams[0], streams[1]handles both.tests/test_mcp_proxy.py: mcp dep is uncapped;_httpx()matches the installed SDK; the sharedstreamable_http_clientsymbol imports; the tokenAuthis an instance of the selected flavor'sAuth.test_e2e_user_agentlive-gateway network tests (fail identically onmain).ruff format+ruff checkclean.Not done / caveats
This supersedes the earlier "keep the cap + actionable error" revision of this branch.
This pull request and its description were written by Isaac.