Skip to content

All MCP tool calls crash: @server.call_tool() decorator on _ainl_unavailable_payload instead of call_tool #1

Description

@sbhooley

Summary

Every MCP tool call fails with:

_ainl_unavailable_payload() takes 0 positional arguments but 2 were given

This affects all memory_*, ainl_*, and a2a_* tools — the entire MCP surface is non-functional. Hooks and direct DB writes are unaffected (they bypass the tool dispatcher), so the graph memory DB keeps updating and the breakage is easy to miss until you actually invoke a tool.

Root cause

In mcp_server/server.py, the @server.call_tool() decorator is attached to the wrong function. It sits on the 0-argument helper _ainl_unavailable_payload() (~line 833) instead of the real async dispatcher call_tool(name, arguments) (~line 843):

@server.call_tool()                                   # ❌ registers the 0-arg helper as THE handler
def _ainl_unavailable_payload() -> Dict[str, Any]:
    return { "ok": False, "error": "AINL tools not available...", ... }

async def call_tool(name: str, arguments: dict) -> list[TextContent]:   # ❌ never registered
    ...

The MCP SDK therefore registers _ainl_unavailable_payload as the call-tool handler and invokes it with (name, arguments) for every call → TypeError. The actual dispatcher call_tool is never wired up.

Introduced in commit 068521a; present on main (a6052b0).

Fix

Move the decorator off the helper and onto the dispatcher:

-@server.call_tool()
 def _ainl_unavailable_payload() -> Dict[str, Any]:
     return { "ok": False, "error": "AINL tools not available...", ... }

+@server.call_tool()
 async def call_tool(name: str, arguments: dict) -> list[TextContent]:
     """Handle tool calls"""

_ainl_unavailable_payload is still called internally with 0 args (e.g. the ainl_* branches when ainl_tools is unavailable), so it stays a plain helper.

Verified

  • py_compile clean.
  • Imported the patched module in a separate process and called call_tool("memory_store_semantic", {...}) → returned a node_id (success, no TypeError).

Operational note for the fix

/reload-plugins does not restart an already-running MCP server subprocess, so the patch won't take effect until the existing mcp_server.server process is killed (or Claude Code is fully restarted). After killing the stale process, a /reload-plugins spawns a fresh server with the fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions