Skip to content

fix(mcp): keep the bridge importable under the system python3 (3.9) - #4

Open
tonydzi wants to merge 2 commits into
LambdaLabsML:mainfrom
tonydzi:fix/mcp-bridge-python39
Open

tonydzi wants to merge 2 commits into
LambdaLabsML:mainfrom
tonydzi:fix/mcp-bridge-python39

Conversation

@tonydzi

@tonydzi tonydzi commented Sep 16, 2026

Copy link
Copy Markdown

Problem

I set up the_lab.api on a Mac and the agent had no labapi tools.

the-lab-agent (and the .mcp.json that the-lab init writes) starts the MCP bridge with a bare python3 from PATH, not with the Python that the-lab is installed in. On macOS that is /usr/bin/python3, which is 3.9.6.

The bridge has list[str] | None in two function signatures (_events_longpoll, _watch). Python 3.9 evaluates these when it imports the file and fails:

File ".../the_lab/agent_skills/skills/lab_api_mcp.py", line 495, in <module>
    types: list[str] | None,
TypeError: unsupported operand type(s) for |: 'types.GenericAlias' and 'NoneType'

The bridge exits before initialize, so the agent starts without any tools and nothing tells you why.

Fix

Add from __future__ import annotations at the top of the bridge. Annotations are no longer evaluated at import time. I checked the rest of the file: there is no other 3.10-only syntax, so this one line is enough and the bridge stays "zero dependencies, any python3".

How I tested

I ran the-lab . on a scratch repo and sent initialize + notifications/initialized + tools/list to the bridge over stdio with different interpreters:

Interpreter Before After
/usr/bin/python3 3.9.6 (macOS system) exit 1, TypeError, no response 39 tools
uv CPython 3.9.18 exit 1, TypeError 39 tools
3.12 39 tools 39 tools
3.14 39 tools 39 tools

Another option would be to launch the bridge with sys.executable in agent_cli.py. I did not do that because the .mcp.json written by init would still say python3, and the one-line change fixes both paths.

Regression test

A second commit adds tests/test_mcp_bridge.py so this does not come back quietly. It starts the bridge as a subprocess, sends initialize + tools/list over stdio and checks that spec-derived tools (orient, create_idea, list_ideas, get_instructions) are listed. The spec comes from the real app (app.openapi()), served by a local http.server, so there is no network and no account. LAB_MCP_PYTHON picks the interpreter that runs the bridge.

Red/green with the bridge under /usr/bin/python3 3.9.6:

  • fix reverted: FAIL ... AssertionError: 1 != 0 ... TypeError: unsupported operand type(s) for |: 'types.GenericAlias' and 'NoneType'
  • with the fix: Ran 1 test ... OK

ci.yml gets an mcp-bridge job that runs it with bridge Python 3.9 and 3.12. There was no test suite yet, so I used stdlib unittest (python -m unittest tests.test_mcp_bridge) rather than add pytest. Happy to move it if you prefer another layout.

— Anton Dziatkovskii · github.com/tonydzi

tonydzi and others added 2 commits September 16, 2026 04:53
the-lab-agent and `the-lab init` both launch the bridge with a bare `python3`
from PATH, not the interpreter the-lab is installed in. On macOS that is
/usr/bin/python3 3.9.6. The `list[str] | None` annotations on the watch
helpers are evaluated at import time there and raise TypeError, so the bridge
exits before `initialize` and the agent runs with no labapi tools.

`from __future__ import annotations` postpones annotation evaluation. The
bridge has no other 3.10-only syntax, so it imports and serves tools on 3.9.

Verified against a running `the-lab .`: initialize + tools/list under
/usr/bin/python3 3.9.6 exited 1 with the TypeError before the change, and
returns 39 tools after it; 3.12 and 3.14 unchanged (39 tools).

Assisted-by: Claude Code / claude-opus-5
Machine: MacBook-Anton
Account: tonydzi
Operator: anton
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A subprocess test that speaks JSON-RPC to lab_api_mcp.py the way a client
does (initialize, tools/list) against a local HTTP server serving the real
app's OpenAPI spec. No network, no account. It asserts spec-derived tools
(orient, create_idea, ...) are listed, so it fails both when the bridge
cannot start and when its INCLUDE table drifts from the API.

LAB_MCP_PYTHON picks the interpreter that runs the bridge. CI gets an
mcp-bridge job with bridge Python 3.9 and 3.12.

Red/green on macOS with the bridge under /usr/bin/python3 3.9.6: with the
previous commit reverted the test fails with the TypeError from
`list[str] | None`; with it, OK.

Assisted-by: Claude Code / claude-opus-5
Machine: MacBook-Anton
Account: dzyatkovskiy.a@gmail.com
Operator: anton
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant