Skip to content

fix(mcp): surface actionable errors through the MCP boundary - #166

Merged
HandyS11 merged 5 commits into
developfrom
revamp/mcp-error-surfacing
Jul 16, 2026
Merged

fix(mcp): surface actionable errors through the MCP boundary#166
HandyS11 merged 5 commits into
developfrom
revamp/mcp-error-surfacing

Conversation

@HandyS11

Copy link
Copy Markdown
Owner

Closes the audit's §3 #4 High residual and the two MCP Medium residuals (§4.4 #5/#6) plus the §5.4 snapshot-name Low, and adds the repo's first true end-to-end MCP test over a real stdio transport (§4.7 #18).

Spec: docs/superpowers/specs/2026-07-16-mcp-error-surfacing-design.md · Plan: docs/superpowers/plans/2026-07-16-mcp-error-surfacing.md

Why

ModelContextProtocol 1.4.1 replaces the message of every non-McpException thrown by a tool with a generic "An error occurred invoking '…'". Every piece of guidance the server produced through a BCL or library exception was therefore invisible to clients — including the exact scenario that made the original finding High: a relative path from a roots-unsupported client never saw "Please provide an absolute path."

What

  • WorkspaceRootService: all four throw sites (roots-unsupported, not-found, ambiguous match, wildcard) now throw McpException with the same guidance.
  • Tool boundary: RunAnalysisAsync wraps every library analysis call, converting ProjGraphException (base of AnalysisException/ParsingException) to McpException with the same message and the original as inner exception. Blank path arguments also get an actionable McpException instead of ArgumentException.
  • get_erd multi-DbContext: with several DbContexts and no contextName, the tool errors listing the candidates instead of silently analyzing the first (plausible-but-wrong output is the worst failure mode for LLM callers). A mistyped contextName now errors with the candidate list on both the DbContext and snapshot branches (the snapshot branch held the list but never validated against it).
  • get_class_diagram: the >50-files warning is appended after the diagram via the shared AppendWarningComments instead of prepended ahead of the YAML front-matter (which strict Mermaid parsers reject); the cached resource stores the appended form.

Tests

  • New McpTransportTests: launches the real self-contained server apphost over StdioClientTransport with a real McpClient — the first tests to cross the SDK layer where the stripping happens. The relative-path/no-roots pin failed with the literal stripped message before the fix and passes after; verified red→green live.
  • New hand-wired pins: multi-context candidates, named-context selection, context/snapshot contextName typos, warning placement + cached-resource agreement, blank-path rejection.
  • Existing pins that asserted the old BCL exception types were updated to McpException — that flip is the point of the change.

Full suite 878/878 green on Linux; dotnet format --verify-no-changes clean; no EF golden moved. Independent code review: zero Critical/Important findings; all six Minor findings addressed in the final commit.

🤖 Generated with Claude Code

HandyS11 and others added 5 commits July 16, 2026 19:34
Audit §3 #4 High residual + §4.4 #5/#6 residuals + §5.4 snapshot-name
validation, plus the first true e2e MCP transport test (§4.7 #18).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tion

ModelContextProtocol 1.4.1 replaces the message of any non-McpException
with "An error occurred invoking '…'", so the root service's guidance
(most importantly "provide an absolute path" for roots-unsupported
clients) and the library's AnalysisException/ParsingException messages
never reached clients (audit §3 #4 High residual).

- WorkspaceRootService: all four throw sites now McpException.
- ProjGraphTools: RunAnalysisAsync wraps every analysis call, converting
  ProjGraphException to McpException with the same message.
- New McpTransportTests: first true e2e tests over a real stdio
  transport (audit §4.7 #18) launching the self-contained server
  apphost; the relative-path/no-roots pin failed with the stripped
  generic message before this fix and passes after.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…xt choice

The DbContext branch silently analyzed the first context in the file
(FindContextClass FirstOrDefault) when several existed, and both
branches let a mistyped contextName fall through to analysis where it
surfaced as a stripped generic error (audit §4.4 #6 + §5.4). Both now
resolve against the discovered candidate list via a shared helper and
fail with the candidates named.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Prepending the >50-files warning ahead of the YAML front-matter breaks
strict Mermaid parsers (audit §4.4 #5 residual); get_project_graph
already appended. Both now share AppendWarningComments, and the cached
resource stores the appended form.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- RunAnalysisAsync preserves the inner ProjGraphException so server
  logs keep the original type and stack trace.
- PreparePathAsync rejects blank paths with McpException instead of
  ArgumentException (the last stripped-message path on the hottest
  parameter of all four tools); null/empty pins updated accordingly.
- Transport test apphost probe asserts the bin root exists and prefers
  the newest apphost across stale RID leftovers.
- Warning test pins that the cached resource matches the returned
  (appended) diagram, as the plan promised.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 16, 2026 18:54

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 the ProjGraph MCP server’s client-facing error quality by ensuring actionable guidance survives the MCP SDK’s tool-invocation boundary, while also fixing Mermaid warning placement and making ERD selection deterministic when multiple EF candidates exist. It adds the first true end-to-end MCP transport test that exercises the real stdio boundary where error-message stripping occurs.

Changes:

  • Convert key MCP-facing failures to McpException (workspace-roots resolution + analysis-layer ProjGraphException wrapping) so messages are not stripped by the MCP SDK.
  • Improve get_erd behavior for multi-DbContext / ModelSnapshot selection by validating/forcing explicit candidate selection and surfacing candidate lists on errors.
  • Add an end-to-end stdio transport integration test suite and update existing integration tests to assert McpException semantics; append class-diagram warnings after Mermaid content.

Reviewed changes

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

Show a summary per file
File Description
tests/ProjGraph.Tests.Integration.Mcp/ProjGraph.Tests.Integration.Mcp.csproj Adds MCP client SDK package reference for true transport-level tests.
tests/ProjGraph.Tests.Integration.Mcp/McpWarningsTests.cs Pins warning placement after Mermaid output and verifies cached resource matches returned diagram.
tests/ProjGraph.Tests.Integration.Mcp/McpTransportTests.cs New real stdio MCP client/server E2E tests validating tool listing + absolute-path guidance surfacing.
tests/ProjGraph.Tests.Integration.Mcp/McpRootsTests.cs Updates assertions to expect McpException so guidance survives the SDK boundary.
tests/ProjGraph.Tests.Integration.Mcp/McpIntegrationTests.cs Updates null/empty path assertions to McpException for MCP-client-visible guidance.
tests/ProjGraph.Tests.Integration.Mcp/McpErdTests.cs Adds pins for multi-context candidate selection + typo validation + whitespace-path guidance and updates exception expectations.
tests/ProjGraph.Tests.Integration.Mcp/Helpers/McpTestHelper.cs Allows injecting a shared DiagramResourceCache for cache/output consistency tests.
src/ProjGraph.Mcp/WorkspaceRootService.cs Converts all client-relevant failure paths to McpException with actionable messages.
src/ProjGraph.Mcp/ProjGraphTools.cs Adds RunAnalysisAsync wrapper for ProjGraphException, validates ERD candidate selection, and appends class-diagram warnings after Mermaid output.
docs/superpowers/specs/2026-07-16-mcp-error-surfacing-design.md Records the approved design for error surfacing, ERD candidate handling, and warning placement.
docs/superpowers/plans/2026-07-16-mcp-error-surfacing.md Captures the implementation plan and constraints for delivering the design.

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

@HandyS11
HandyS11 merged commit f0ccce3 into develop Jul 16, 2026
7 checks passed
@HandyS11
HandyS11 deleted the revamp/mcp-error-surfacing branch July 16, 2026 19:06
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.

2 participants