Skip to content

Add structured error envelope and server codes for MCP errors (#1581) - #2230

Merged
Widthdom merged 3 commits into
mainfrom
fix-issue1581
May 16, 2026
Merged

Add structured error envelope and server codes for MCP errors (#1581)#2230
Widthdom merged 3 commits into
mainfrom
fix-issue1581

Conversation

@Widthdom

Copy link
Copy Markdown
Owner

Summary

  • Adds a structured error envelope (data.category, data.suggestion, data.retry_safe) to every MCP JSON-RPC error and tool-result error so clients can branch on a stable category instead of parsing message strings (MCP error model uses bare -32603 for everything; no data envelope or server codes #1581).
  • Introduces server-defined JSON-RPC codes in the reserved -32000..-32099 range — -32000 rate_limited, -32001 permission_denied, -32010 index_missing, -32011 index_stale, -32012 index_corrupted, -32015 request_cancelled — and keeps the standard -32700..-32603 codes for envelope-level errors.
  • Classifies unhandled exceptions via McpErrorEnvelope.ClassifyException so SqliteException patterns map to index_stale (retry after --rebuild) vs index_corrupted (delete and rebuild) without leaking the raw message (MCP error path leaks raw ex.Message; DbDebug env-flag risks content leak #1530 stays intact).
  • Preserves backward compatibility with the No rate limiting or per-tool quotas on MCP calls #1560 rate-limit contract: legacy fields (error_category, tool, caller, retry_after_ms) sit alongside the new canonical envelope, and tool_disabled keeps its existing -32601 wire code.

Validation

  • dotnet build CodeIndex.sln -c Release --no-restore -m:1 — succeeded (0 errors, 0 warnings).
  • dotnet test CodeIndex.sln -c Release --no-restore --no-build -m:1 — 5066 passed, 3 performance tests skipped (expected), 0 failed.
  • MCP-specific filter (--filter FullyQualifiedName~Mcp) — 355 passed, 0 failed.
  • Codex adversarial review: round 1 surfaced 3 findings (envelope missing on unsupported-protocol path, index_missing.retry_safe doc/code mismatch, message_too_large/tool_unknown row collisions in the doc table); all addressed in 5f52c67. Round 2 found no further actionable issues.

Docs / changelog

Follow-ups

None identified during this change. All categories listed in the issue (index_stale, index_corrupted, resource_limit, unsupported_language, missing_parameter, permission_denied) are covered either by a dedicated server code or by mapping onto an existing JSON-RPC envelope-level code with the new data.category field.

Fixes #1581

Widthdom added 3 commits May 16, 2026 23:09
Every JSON-RPC error and tool-result error from the MCP server now
carries a canonical `data` envelope (`category`, `suggestion`,
`retry_safe`) so MCP clients can branch on a stable machine-readable
category instead of parsing the human `message` string.

Server-defined codes are allocated in the JSON-RPC reserved range
`-32000..-32099`:
- `-32000 rate_limited`     (#1560 legacy fields preserved)
- `-32001 permission_denied`
- `-32010 index_missing`
- `-32011 index_stale`      (SqliteException: no such table or column)
- `-32012 index_corrupted`  (SqliteException: malformed, not a db, encrypted)
- `-32015 request_cancelled`

Standard codes (`-32700`, `-32600..-32603`) keep their semantics with
new categories layered on top: `parse_error`, `invalid_request`,
`message_too_large`, `method_not_found`, `tool_unknown`, `tool_disabled`,
`missing_parameter`, `invalid_argument`, `internal_error`.

The classifier maps unhandled exceptions to `index_stale`,
`index_corrupted`, `request_cancelled`, or `internal_error` from the
exception type plus selected SQLite message substrings. The raw
exception message is never leaked on the wire (per #1530).

Documentation: full code and category map in DEVELOPER_GUIDE.md
(English and Japanese).

Fixes #1581
- Add the canonical data envelope to the unsupported-protocol handshake
  error (#1554 helper) so the version-negotiation path now also carries
  `category=invalid_argument`, `suggestion`, and `retry_safe=false`
  alongside the pre-existing `supportedVersions` / `requestedVersion`
  fields.
- Fix DEVELOPER_GUIDE.md code/category tables (EN + JA) to match the
  emitted wire values: `message_too_large` rides `-32700` (frame
  reader, pre-parse), `tool_unknown` rides `-32602` (invalid params),
  `tool_disabled` keeps `-32601`. Split the previous row groupings so
  each (code, category) pair has its own line.
- Flip `index_missing.retry_safe` documentation from `false` to `true`
  to match the emitted value and the regression test: the implementation
  drops cached DB context so a subsequent retry can reopen after the
  operator runs an index build.
- Extend the existing `Initialize_UnsupportedProtocolVersion_*` test to
  assert the envelope is present.

Refs #1581
@Widthdom
Widthdom merged commit c6651ac into main May 16, 2026
7 checks passed
@Widthdom
Widthdom deleted the fix-issue1581 branch May 16, 2026 15:52
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.

MCP error model uses bare -32603 for everything; no data envelope or server codes

1 participant