Skip to content

Upgrade mcp dependency from 1.x to 2.0 #39

Description

@ajtritt

Summary

Bump the mcp (Model Context Protocol Python SDK) dependency from the current >=1.0.0 (installed: 1.28.0) to mcp>=2.0.0,<3.0.0. This is a major version with breaking API changes, but the blast radius in dsagt is small and fully scoped below.

Background

mcp 2.0.0 reworked the low-level Server API (dsagt uses mcp.server.lowlevel.Server, not FastMCP) to support the 2026-07-28 MCP spec. Verified directly against the installed mcp==2.0.0 package (not from training-data memory, which predates this release):

  • @server.list_tools() / @server.call_tool() decorators are removed. Handlers are now passed as on_list_tools= / on_call_tool= kwargs to the Server() constructor.
  • on_list_tools(ctx, params) must return types.ListToolsResult(tools=[...]), not a bare list.
  • on_call_tool(ctx, params: types.CallToolRequestParams) replaces (tool_name: str, arguments: dict) — read params.name / params.arguments.
  • on_call_tool must return types.CallToolResult(content=[...]), not a bare list[TextContent].
  • server.request_handlers[SomeRequestClass] (dict keyed by pydantic request classes) is removed. Public replacement: server.get_request_handler("tools/call") / "tools/list" (string-keyed), returning HandlerEntry(params_type, handler).
  • Response wrappers no longer have .root (the RootModel union pattern is gone) — read result.content[0].text directly.

Confirmed unaffected by a real import/construction smoke test against mcp==2.0.0: mcp.server.stdio.stdio_server, mcp.server.lowlevel.NotificationOptions, mcp.server.models.InitializationOptions, server.get_capabilities(notification_options=, experimental_capabilities=), and types.Tool(inputSchema=...) construction (camelCase kwargs still populate via pydantic alias).

Practical implication: registry_tools.py, knowledge_tools.py, memory_tools.py, skill_tools.py never touch the SDK Server object directly — they only build types.Tool(...) and return plain str/dict, which is a dsagt-internal contract. Only build_dispatch_server() in src/dsagt/mcp/server.py (~50 lines) talks to the Server class.

Scope

  1. pyproject.toml / uv.lock — bump to mcp>=2.0.0,<3.0.0 (pin the upper bound given the demonstrated history of breaking majors), uv sync.

  2. src/dsagt/mcp/server.pybuild_dispatch_server() — replace the decorator-based list_tools()/call_tool() with on_list_tools/on_call_tool callables passed to Server(name, on_list_tools=..., on_call_tool=...). Preserve the existing per-tool dispatch logic (span opening, ValueError → error-dict, str-vs-dict formatting) as the body of on_call_tool; only the outer signature (ctx, params in; types.CallToolResult out) and on_list_tools (wrap in types.ListToolsResult) change. _run_stdio, create_dsagt_server, and main() are untouched — they only call server.run(...) / server.get_capabilities(...), both stable across the bump.

  3. Test harness — 5 files reach into server.request_handlers[...] / .root, which no longer exist:

    • tests/mcp_helpers.py (call_tool_sync / call_tool_async, used broadly)
    • tests/test_dsagt_server.py (_list_tools / _call helpers)
    • tests/test_kb_search_filters.py, tests/test_knowledge_server.py, tests/test_memory_tools.py (inline copies of the same pattern)

    Fix: replace server.request_handlers[types.CallToolRequest] with server.get_request_handler("tools/call").handler (pass ctx=None — dsagt's own handler never touches it), same for "tools/list", and drop the .root unwrap. Verified this pattern end-to-end against real mcp==2.0.0.

    tests/mcp_helpers.py's subprocess-based helpers (mcp_call_tool, mcp_initialize, real JSON-RPC over stdio) are unaffected — they exercise the wire protocol, not SDK internals.

    Open question: the 3 inline copies (test_kb_search_filters.py, test_knowledge_server.py, test_memory_tools.py) duplicate the same helper logic already in mcp_helpers.py. Since all three need to be touched anyway, consider consolidating them onto the shared mcp_helpers.py versions instead of patching each copy in place.

Verification

  • uv run black .
  • uv run ruff check .
  • uv run --no-sync python -m pytest tests/test_dsagt_server.py tests/test_registry_server.py tests/test_knowledge_server.py tests/test_memory_tools.py tests/test_skill_tools.py tests/test_kb_search_filters.py -q
  • Skip the network/subprocess integration suites (test_integration.py, test_*_integration.py, test_server_startup.py, test_dependency_integration.py) unless something above regresses them.

Metadata

Metadata

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