Skip to content

fix(mcp): structured error for param-shape mismatch (#1351)#1500

Merged
igorls merged 2 commits into
MemPalace:developfrom
mvalentsev:fix/mcp-typed-error-1351
May 14, 2026
Merged

fix(mcp): structured error for param-shape mismatch (#1351)#1500
igorls merged 2 commits into
MemPalace:developfrom
mvalentsev:fix/mcp-typed-error-1351

Conversation

@mvalentsev

@mvalentsev mvalentsev commented May 13, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

  • On tools/call, split the bare except Exception so that a TypeError matching Python's signature-mismatch shape returns a structured -32602 Invalid params naming the offending parameter(s), instead of the opaque -32000 Internal tool error. Multi-arg cases list each name.
  • Match is gated on the function name in the TypeError equalling the dispatched handler's __qualname__, so a downstream helper that happens to raise a similar-looking TypeError falls through to the generic path. Handler-internal TypeError and all non-TypeError exceptions stay on the existing generic -32000 path with no exception strings reaching the client.
  • tool_args.pop("wait_for_previous", None) is moved one line above the try: so the new except TypeError branch only sees exceptions from the dispatched handler call.
  • Adds TestParamShapeDiagnostics in tests/test_mcp_server.py with 6 cases: single missing-required, two missing-required listing both names, handler-internal TypeError with unrelated message stays generic, handler-internal TypeError with signature-mismatch shape stays generic, handler-raised got an unexpected keyword argument shape stays generic, and RuntimeError stays generic with no path leak.

How to test

uv run pytest tests/test_mcp_server.py -v -k ParamShape
uv run pytest tests/ -q --ignore=tests/benchmarks
uv run ruff check . && uv run ruff format --check .

End-to-end repro from #1351:

echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"mempalace_diary_write","arguments":{"agent_name":"test"}}}' \
  | uv run python -m mempalace.mcp_server

Before this PR returns -32000 "Internal tool error". After: -32602 "Missing required parameter 'entry' for tool mempalace_diary_write". With both args omitted: -32602 "Missing required parameters 'agent_name', 'entry' for tool mempalace_diary_write".

Checklist

  • Tests pass (python -m pytest tests/ -v)
  • No hardcoded paths
  • Linter passes (ruff check .)

Why

Addresses the missing-required half of #1351. The reporter surfaces a missing-required call as -32000 Internal tool error, which hides which parameter is wrong; agents (LLM and otherwise) end up retrying the same wrong-shape call multiple times before checking the schema. The wrong-name case (e.g. text= vs content=) is silently absorbed by the schema-filter at mempalace/mcp_server.py:2235 before dispatch and is being tracked as a follow-up by @anastasiiaanfimova.

-32602 matches the JSON-RPC 2.0 spec for invalid params and the existing precedent in this file (line 2207 for missing name on tools/call, line 2251 for the type-coercion failure path). Only parameter names that are already public via tools/list reach the client; handler-internal TypeError and any non-TypeError exception still resolve to generic -32000, so ChromaDB or filesystem internals never leak.

Complementary to the error-hardening direction in #181: the catch-all path stays generic, only param-shape errors become structured.

@anastasiiaanfimova's comment on #1351 outlined the dispatcher-TypeError reformatting approach this PR builds on; she's added as Co-authored-by on the commit.

Split the bare except in handle_request tools/call: TypeError matching
Python's signature-mismatch shape (gated on handler __qualname__) returns
-32602 with parsed parameter name(s); handler-internal TypeError and
non-TypeError exceptions stay on the existing generic -32000 path so
internals never leak. Adds TestParamShapeDiagnostics with 6 cases.

Closes MemPalace#1351

Co-authored-by: anastasiiaanfimova <271004602+anastasiiaanfimova@users.noreply.github.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances the MCP server's error reporting by specifically catching TypeError exceptions during tool execution to identify missing required parameters. By verifying the error source against the handler's name, it provides detailed -32602 (Invalid params) responses without leaking internal helper signatures. Feedback suggests refactoring the error handling logic to eliminate duplication between the new TypeError block and the existing catch-all Exception block.

Comment thread mempalace/mcp_server.py Outdated
Address gemini-code-assist review on PR MemPalace#1500: dedup the two -32000
return branches in handle_request tools/call. Behavior unchanged;
TestParamShapeDiagnostics 6/6 green.

Co-authored-by: anastasiiaanfimova <271004602+anastasiiaanfimova@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves MCP error reporting by detecting dispatcher-level TypeErrors caused by missing required parameters and returning a structured JSON-RPC -32602 Invalid params error that names the offending parameter(s), instead of the opaque -32000 Internal tool error. The match is gated on the function name in the TypeError message equalling the dispatched handler's __qualname__, so handler-internal TypeErrors and any non-TypeError exception remain on the generic path and cannot leak internal helper/parameter names or filesystem/DB internals.

Changes:

  • Adds an except TypeError branch in handle_request's tools/call dispatch that regex-matches Python's "missing N required ... arguments" message and returns -32602 with the parameter name(s) when the qualname matches.
  • Moves tool_args.pop("wait_for_previous", None) above the try: so it cannot itself produce a TypeError caught by the new branch, and factors the generic -32000 response into _internal_tool_error.
  • Adds TestParamShapeDiagnostics covering 6 scenarios: single/multi missing required, handler-internal TypeError (unrelated message and signature-shape), unexpected-kwarg shape, and non-TypeError (RuntimeError) — all asserting no internal-string leakage.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
mempalace/mcp_server.py Adds qualname-gated TypeError handling for param-shape errors returning -32602; extracts _internal_tool_error; moves wait_for_previous pop outside try.
tests/test_mcp_server.py Adds TestParamShapeDiagnostics with 6 cases asserting structured -32602 for missing-required and generic -32000 (no leakage) for handler-internal/other exceptions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@igorls igorls merged commit 2b3af2b into MemPalace:develop May 14, 2026
10 checks passed
igorls pushed a commit that referenced this pull request May 15, 2026
Unknown kwargs (wrong parameter name, e.g. text= instead of content=)
were silently dropped by the schema-filter, surfacing only indirectly
as a later "Missing required 'X'". Emit -32602 naming the offending
kwarg instead — symmetric with #1500's missing-required path. Gated by
the existing accepts_var_keyword check; wait_for_previous excluded.

Closes #1512.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@igorls igorls added this to the v3.3.6 milestone May 15, 2026
arnoldwender pushed a commit to arnoldwender/mempalace that referenced this pull request May 24, 2026
Bumps version 3.3.5 → 3.3.6 across pyproject.toml, version.py, plugin
manifests (.claude-plugin/plugin.json, .claude-plugin/marketplace.json,
.codex-plugin/plugin.json), README badge, and uv.lock. Flips CHANGELOG.md
from ``[Unreleased]`` to ``[3.3.6] — 2026-05-24`` and backfills the
major user-facing entries that landed without changelog entries during
the cycle:

Features:
- MemPalace#1555 office-document mining via --mode extract + virtual line numbers
- MemPalace#1584 surgical closet pointers with date+line locators (Tier 6a)
- MemPalace#1558 + MemPalace#1560 within-wing hallways (entity co-occurrence graph)
- MemPalace#1565 cross-wing tunnels auto-promoted from hallways
- MemPalace#1578 Hebbian potentiation + Ebbinghaus decay on hallways/tunnels
- MemPalace#1236 API-tool transcripts auto-route to wing_api
- MemPalace#711 hooks.auto_save toggle for silent-mode sessions
- MemPalace#1605 COCA content-word filter for entity detection
- MemPalace#1557 case-insensitive entity matching at mine time
- MemPalace#1483 multilingual embeddings (embeddinggemma-300m) by default

Bug Fixes (selected, user-visible):
- MemPalace#1540 silent data loss in three unchunked upsert sites
- MemPalace#1538 paragraph chunker oversized chunks
- MemPalace#1554 per-file chunk cap too low for transcripts
- MemPalace#1562 Windows hook subprocess/ChromaDB deadlock
- MemPalace#1529 create_tunnel corrupted hyphenated wing names
- MemPalace#1424 save-hook truncated hyphenated project folders
- MemPalace#1383 KG cache duplicated graphs for symlinked/cased paths
- MemPalace#1466 silent symlink skip now logged
- MemPalace#1441 macOS stock-bash 3.2 hook compatibility
- MemPalace#1500 / MemPalace#1513 structured JSON-RPC errors on bad MCP input
- MemPalace#1523 VACUUM + FTS5 rebuild after repair
- MemPalace#1548 FTS5 validation at end of mine
- plus MemPalace#1216, MemPalace#1408, MemPalace#1438, MemPalace#1439, MemPalace#1445, MemPalace#1452, MemPalace#1459, MemPalace#1461, MemPalace#1466,
  MemPalace#1470, MemPalace#1477, MemPalace#1485, MemPalace#1500, MemPalace#1513, MemPalace#1528, MemPalace#1532, MemPalace#1543, MemPalace#1546, MemPalace#1585

Performance:
- MemPalace#1474 convo miner pre-fetches mined-set
- MemPalace#1487 rebuild_index progress callback
- MemPalace#1530 MCP cold-start diagnostics + opt-in warmup

Lint passes (ruff 0.15.14); mempalace-mcp entry point alignment
verified per RELEASING.md.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants