Skip to content

Migrate novem.comments.MCP() to mcp 2.x #265

Description

@bjornars

novem.comments.MCP() targets the mcp v1 API. mcp 2.0.0 (published 2026-07-28) is a breaking release and the extra is pinned to <2 in #264 as a stopgap. This issue tracks the actual migration.

Verified breaking changes

Established by installing mcp==1.28.1 and mcp==2.0.0 side by side on Python 3.10 and comparing the surfaces novem actually uses:

v1.28.1 v2.0.0
Server module mcp.server.fastmcp mcp.server.mcpserver (old path removed)
Server class FastMCP MCPServer
Tool schema attr t.inputSchema t.input_schema
call_tool() returns tuple(list[Content], dict) CallToolResult (not subscriptable)
Image content attr item.mimeType item.mime_type

What is not broken, so the migration surface is small: MCPServer(name) and .run(transport='stdio') keep compatible signatures, @server.tool() is unchanged, arbitrary attribute assignment (server.ctx = ...) still works, Image(data=, format=) still works, and v2 installs cleanly on Python 3.10.

Sites to change

  • novem/comments.py:666 — from mcp.server.fastmcp import FastMCP
  • novem/comments.py:887 — from mcp.server.fastmcp import Image
  • novem/comments.py:808 — t.inputSchema raises AttributeError on v2
  • novem/comments.py:669-670 — bare except ImportError swallows ModuleNotFoundError, producing the misleading "install the mcp extra" message when mcp is installed
  • examples/mcp_mention_responder.py:110,173 — result[0] raises TypeError: 'CallToolResult' object is not subscriptable
  • examples/mcp_mention_responder.py:180 — item.mimeType is missing on v2
  • MCP() docstring references FastMCP (cosmetic)

Proposed approach

Support both majors rather than moving the pin to >=2. novem is a published library and third-party scripts may already be pinned to either major, so the extra should stay open at mcp>=1.0.0.

  1. _check_mcp_deps() tries mcp.server.mcpserver first, falls back to mcp.server.fastmcp, and only reports "install the extra" when mcp itself is genuinely absent — distinguish a missing package from a moved symbol.
  2. api_tools() reads getattr(t, "input_schema", None) or t.inputSchema.
  3. Image import uses the same two-path fallback.
  4. Update the example to normalize CallToolResult vs. tuple, and mime_type vs. mimeType.
  5. Keep MCP()'s public signature and return contract unchanged.

Add CI coverage

This rotted silently because nothing exercises it: uv sync does not install optional extras and no test imports mcp. The lockfile bump in #263 would have gone green.

Add a job that installs the mcp extra and smoke-tests MCP(...).api_tools() against both majors — asserting the six tool names resolve is enough to catch every break listed above.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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