Skip to content

fix: surface the real reason an MCP server is unavailable - #1159

Draft
sahrizvi wants to merge 1 commit into
mainfrom
fix/mcp-error-diagnostics
Draft

fix: surface the real reason an MCP server is unavailable#1159
sahrizvi wants to merge 1 commit into
mainfrom
fix/mcp-error-diagnostics

Conversation

@sahrizvi

@sahrizvisahrizvi commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes#1121
Closes#701

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Two defects where the diagnostic information already exists in the process and is thrown away before the user sees it.

1. The connect error is discarded (#1121). When an MCP server fails to connect, the warning logged status.status — which on that branch is always the constant "failed" — and dropped status.error, the field holding the real message (401 Unauthorized, a transport error, Invalid MCP URL for "<key>"). The reporter had to read the source to find out why their server wouldn't connect. unavailableLogFields() is split out as a pure function so the payload is testable without a live transport, and so a later edit can't silently drop the field again.

2. Blanked environment variables are never named (#701). A {env:VAR} with nothing set becomes "". The config parses clean, the server launches with a blank credential — usually a password — and fails later with an error naming neither the variable nor the file. The names are now recorded at both substitution sites (per-server for discovered configs like .vscode/mcp.json; per-file for the main config) and shown in /mcps and mcp list. They're shown even when the server reports connected, because a blank credential often connects and only fails on first real use.

Deliberately not reported: an unresolved bare ${VAR} is left literal by the config layer on purpose, so a later runtime layer can fill it — the bedrock provider fills ${AWS_REGION} from the effective region. Warning there would be a false positive on a supported setup.

Known limitation: the main-config report is file-scoped, not server-scoped. Substitution runs on raw config text before any structure exists, so a blanked variable belonging to a non-MCP field would also show under mcp list. The message says resolved to empty in <file> rather than attributing it to a server. Narrowing it would need offset-to-JSON-path mapping.

How did you verify your code works?

  • Full opencode suite: 12,323 tests, 0 fail.
  • New e2e drives the real CLI in an isolated HOME against a temp project, asserting the variable is named and that a fully-resolved config stays silent.
  • Mutation-tested: removing the status.error field, or the blank-env recording, each fails exactly one test — so neither test passes vacuously.
  • Marker Guard passes. No new formatting violations: several touched files were already non-conformant with the repo's prettier config, so I compared violation counts against main rather than reformatting them.

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Stacked

A follow-up PR (#1160) adds mcp status and discovered-config drift reporting on top of this branch.

🤖 Generated with Claude Code

https://claude.ai/code/session_018fJ3X7pcGT4R9yzjsJnqsV

Two defects where the diagnostic information already exists in the
process and is discarded before it reaches the user.
`server unavailable` logged `status.status` — the constant string
`"failed"` on that branch — and dropped `status.error`, the field
holding the actual message (`401 Unauthorized`, a transport error,
`Invalid MCP URL for "<key>"`). Extracted `unavailableLogFields()` as a
pure function so the payload is testable without standing up a
transport, and so a later edit cannot quietly drop the field again.
Environment variables that resolve to empty were never named. A
`{env:VAR}` with nothing set becomes `""`, the config parses clean, and
the server launches with a blank credential — usually a password —
failing later with an error naming neither the variable nor the file.
The names are now recorded at both substitution sites: per-server for
discovered external configs, per-file for the main config. They surface
in `/mcps` and `mcp list`, shown even when the server reports connected,
because a blank credential often connects and fails on first real use.
An unresolved bare `${VAR}` is deliberately left literal by the config
layer so a later runtime layer can fill it (the bedrock provider fills
`${AWS_REGION}` from the effective region). That case is not reported.
Closes#1121Closes#701
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fJ3X7pcGT4R9yzjsJnqsV
@coderabbitai

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
auto_review:
drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions

Copy link
Copy Markdown

Thanks for updating your PR! It now meets our contributing guidelines. 👍

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

1 participant

@sahrizvi