Skip to content

grapharc[mcp] is broken on arrival: mcp>=1.2 resolves to 2.0, where mcp.server.fastmcp no longer exists #101

Description

@Shashankss1205

Found by reproducing the build and check the distribution CI job locally. This ships broken today — the lockfile hides it from every local test.

What happens

grapharc/mcp/server.py builds on from mcp.server.fastmcp import FastMCP. That module exists in the mcp 1.x line and was removed in 2.0. The extra was declared unbounded:

mcp = ["mcp>=1.2"]

So a fresh install resolves to mcp 2.0.0 and the server cannot import:

$ pip install 'grapharc[mcp]'      # gets mcp 2.0.0
$ python -c "import grapharc.mcp"
  File ".../grapharc/mcp/server.py", line 33, in <module>
    from mcp.server.fastmcp import FastMCP
ModuleNotFoundError: No module named 'mcp.server.fastmcp'

mcp.server in 2.0 exposes lowlevel, mcpserver, session, stdio, … and no fastmcp.

Why it was invisible

uv.lock pins mcp==1.28.1, so the whole dev environment and every local pytest run uses 1.x and passes. Only a resolver starting from pyproject.toml — that is, a real user — gets 2.0. The one job that catches it is CI's clean-environment wheel check.

The second defect it exposed

CI reported this as "in the source tree but not in the wheel: ['grapharc.mcp.driver', 'grapharc.mcp.server']", which is untrue — both files are in the wheel. pkgutil.walk_packages imports a package to walk into it, and grapharc/mcp/__init__.py eagerly did:

from grapharc.mcp.server import FORBIDDEN_TOOL_WORDS, build_server, serve_stdio

so any environment without a working mcp SDK made the whole subpackage unwalkable and it read as missing. grapharc/slack/__init__.py already documents the right posture for this — "Only bot.py needs the slack extra, and it imports it lazily … so a wheel without the extra still imports" — and grapharc/mcp/driver.py needs no SDK at all.

Fixed in the same change

  • mcp = ["mcp>=1.2,<2"]. Raising that ceiling means porting off FastMCP, which should be a diff someone writes, not a resolver accident.
  • grapharc/mcp/__init__.py resolves build_server / serve_stdio / FORBIDDEN_TOOL_WORDS lazily via PEP 562, so import grapharc.mcp works without the extra and from grapharc.mcp import build_server still works with it. 130 modules now import from a clean [all] wheel.

Worth considering separately

Every other extra is unbounded too (langchain-openai>=0.2, fastapi>=0.115, slack-bolt>=1.20, real-ladybug>=0.15.3, anthropic>=0.40, the three OTel packages). Each is one major release away from the same failure, and the lockfile will hide each of them the same way. A periodic CI job that resolves from pyproject.toml rather than uv.lock would catch the next one before a user does.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions