From 0fdb480e12caaf894838886ca6db2ad68e3d67b6 Mon Sep 17 00:00:00 2001 From: Oleksii Pylypchuk Date: Sat, 18 Apr 2026 01:55:43 +0300 Subject: [PATCH 1/4] fix(mcp): handle null JSON-RPC request payloads safely When the MCP client sends a malformed or null top-level request, prevent the AttributeError on request.get() by explicitly validating that the request is a dictionary. Returns standard JSON-RPC Error -32600 (Invalid Request) instead of crashing the server. --- mempalace/mcp_server.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mempalace/mcp_server.py b/mempalace/mcp_server.py index ca71f60..71a96df 100644 --- a/mempalace/mcp_server.py +++ b/mempalace/mcp_server.py @@ -1968,6 +1968,8 @@ SUPPORTED_PROTOCOL_VERSIONS = [ def handle_request(request): + if not isinstance(request, dict): + return {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}} method = request.get("method") or "" params = request.get("params") or {} req_id = request.get("id") From 55d79dc8cd03d408644b05cb44aa277edaf2eb86 Mon Sep 17 00:00:00 2001 From: Oleksii Pylypchuk Date: Sat, 18 Apr 2026 16:53:53 +0300 Subject: [PATCH 2/4] fix: include null id in JSON-RPC invalid request error responses and add validation tests --- mempalace/mcp_server.py | 2 +- tests/test_mcp_server.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/mempalace/mcp_server.py b/mempalace/mcp_server.py index 71a96df..227e91a 100644 --- a/mempalace/mcp_server.py +++ b/mempalace/mcp_server.py @@ -1969,7 +1969,7 @@ SUPPORTED_PROTOCOL_VERSIONS = [ def handle_request(request): if not isinstance(request, dict): - return {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}} + return {"jsonrpc": "2.0", "id": None, "error": {"code": -32600, "message": "Invalid Request"}} method = request.get("method") or "" params = request.get("params") or {} req_id = request.get("id") diff --git a/tests/test_mcp_server.py b/tests/test_mcp_server.py index b036afd..0a020d3 100644 --- a/tests/test_mcp_server.py +++ b/tests/test_mcp_server.py @@ -190,6 +190,13 @@ class TestHandleRequest: resp = handle_request({"method": None, "id": 99, "params": {}}) assert resp["error"]["code"] == -32601 + @pytest.mark.parametrize("payload", [None, [], "plain", 42, True]) + def test_handle_request_invalid_payload_returns_jsonrpc_error(self, payload): + from mempalace.mcp_server import handle_request + + resp = handle_request(payload) + assert resp == {"jsonrpc": "2.0", "id": None, "error": {"code": -32600, "message": "Invalid Request"}} + def test_tools_call_dispatches(self, monkeypatch, config, palace_path, seeded_kg): _patch_mcp_server(monkeypatch, config, seeded_kg) from mempalace.mcp_server import handle_request From a85d432b544060795ff57796d713c597122f6293 Mon Sep 17 00:00:00 2001 From: Oleksii Pylypchuk Date: Sat, 18 Apr 2026 22:05:34 +0300 Subject: [PATCH 3/4] feat: add validation for missing name parameter in tools/call requests --- mempalace/mcp_server.py | 6 ++++++ tests/test_mcp_server.py | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/mempalace/mcp_server.py b/mempalace/mcp_server.py index 227e91a..25439f2 100644 --- a/mempalace/mcp_server.py +++ b/mempalace/mcp_server.py @@ -2007,6 +2007,12 @@ def handle_request(request): }, } elif method == "tools/call": + if not isinstance(params, dict) or "name" not in params: + return { + "jsonrpc": "2.0", + "id": req_id, + "error": {"code": -32602, "message": "Invalid params: 'name' is required for tools/call"}, + } tool_name = params.get("name") tool_args = params.get("arguments") or {} if tool_name not in TOOLS: diff --git a/tests/test_mcp_server.py b/tests/test_mcp_server.py index 0a020d3..fc56b34 100644 --- a/tests/test_mcp_server.py +++ b/tests/test_mcp_server.py @@ -148,6 +148,20 @@ class TestHandleRequest: ) assert resp["error"]["code"] == -32601 + def test_tools_call_missing_params(self): + from mempalace.mcp_server import handle_request + + for bad_params in [None, {}, {"arguments": {}}]: + resp = handle_request( + { + "method": "tools/call", + "id": 15, + "params": bad_params, + } + ) + assert resp["error"]["code"] == -32602 + assert "Invalid params" in resp["error"]["message"] + def test_unknown_method(self): from mempalace.mcp_server import handle_request From 869ab3809570fa578e9dd634ec31b0b1817dddc7 Mon Sep 17 00:00:00 2001 From: Igor Lins e Silva <4753812+igorls@users.noreply.github.com> Date: Wed, 6 May 2026 01:54:11 -0300 Subject: [PATCH 4/4] style: ruff format mcp_server.py + test_mcp_server.py (CI lint) --- mempalace/mcp_server.py | 11 +++++++++-- tests/test_mcp_server.py | 6 +++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/mempalace/mcp_server.py b/mempalace/mcp_server.py index 25439f2..30a0bea 100644 --- a/mempalace/mcp_server.py +++ b/mempalace/mcp_server.py @@ -1969,7 +1969,11 @@ SUPPORTED_PROTOCOL_VERSIONS = [ def handle_request(request): if not isinstance(request, dict): - return {"jsonrpc": "2.0", "id": None, "error": {"code": -32600, "message": "Invalid Request"}} + return { + "jsonrpc": "2.0", + "id": None, + "error": {"code": -32600, "message": "Invalid Request"}, + } method = request.get("method") or "" params = request.get("params") or {} req_id = request.get("id") @@ -2011,7 +2015,10 @@ def handle_request(request): return { "jsonrpc": "2.0", "id": req_id, - "error": {"code": -32602, "message": "Invalid params: 'name' is required for tools/call"}, + "error": { + "code": -32602, + "message": "Invalid params: 'name' is required for tools/call", + }, } tool_name = params.get("name") tool_args = params.get("arguments") or {} diff --git a/tests/test_mcp_server.py b/tests/test_mcp_server.py index fc56b34..c073830 100644 --- a/tests/test_mcp_server.py +++ b/tests/test_mcp_server.py @@ -209,7 +209,11 @@ class TestHandleRequest: from mempalace.mcp_server import handle_request resp = handle_request(payload) - assert resp == {"jsonrpc": "2.0", "id": None, "error": {"code": -32600, "message": "Invalid Request"}} + assert resp == { + "jsonrpc": "2.0", + "id": None, + "error": {"code": -32600, "message": "Invalid Request"}, + } def test_tools_call_dispatches(self, monkeypatch, config, palace_path, seeded_kg): _patch_mcp_server(monkeypatch, config, seeded_kg)