Skip to content

Update js-yaml to v5 and improve error handling for MCP servers#129

Open
Minitour wants to merge 12 commits into
mainfrom
develop
Open

Update js-yaml to v5 and improve error handling for MCP servers#129
Minitour wants to merge 12 commits into
mainfrom
develop

Conversation

@Minitour

Copy link
Copy Markdown
Collaborator

This pull request focuses on improving error handling, reliability, and security in server interactions, particularly around unreachable servers and OAuth2 detection. It also includes dependency updates and test coverage enhancements. The main themes are robust error propagation, sanitization of API error messages, and dependency upgrades.

Error handling and API robustness:

  • Added comprehensive error handling to CapaServer's request handler, returning sanitized error messages and distinguishing between authentication failures and unreachable servers in tool-listing endpoints. This prevents leaking internal errors or stack traces to clients and improves UI feedback. [1] [2] [3] [4] [5]
  • Globally catches unhandled promise rejections and uncaught exceptions, logging them instead of crashing the process or leaking to stderr.

MCP server and tool-listing improvements:

  • listServerTools now accepts a throwOnError option, ensuring that connection/authentication failures are surfaced to the API/UI instead of being silently ignored. [1] [2]
  • The MCP proxy’s client connection now uses a timeout (connectWithTimeout) to prevent hangs on unresponsive servers, with tests ensuring no unhandled rejections or timer leaks. [1] [2] [3] [4] [5]

OAuth2 detection and error reporting:

  • OAuth2 detection failures are caught and logged, never surfacing raw errors to the client. The detection logic is tested to ensure unreachable servers do not throw. [1] [2]

Dependency updates and compatibility:

  • Updated js-yaml to v5 and removed its type definitions import, as they are now included. Updated @types/node to v26. [1] [2] [3]
  • Updated GitHub Actions workflows to use actions/checkout@v7 for improved CI reliability. [1] [2] [3] [4] [5] [6]

Test coverage enhancements:

  • Added integration and unit tests for error propagation in tool listing, connection timeouts, and OAuth2 detection, ensuring robust and correct behavior in edge cases. [1] [2] [3]

These changes collectively improve the server's resilience, security, and user experience when dealing with network or authentication issues.

dependabot Bot and others added 12 commits June 20, 2026 18:54
Bumps the production-deps group with 1 update: [js-yaml](https://github.com/nodeca/js-yaml).


Updates `js-yaml` from 4.2.0 to 5.0.0
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](nodeca/js-yaml@4.2.0...5.0.0)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: production-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
…erver against empty responses

Fixes #127 — remote skills whose URL is on a known git host (e.g.
raw.githubusercontent.com) could not be installed when the user had a
stored-but-expired GitHub token. AuthenticatedFetch now returns null
from ensureFreshIntegration instead of throwing, so public URLs proceed
unauthenticated; private URLs still surface a 401/403 which the existing
handler converts into the friendly re-auth prompt.

Fixes #126 — the local capa server could return net::ERR_EMPTY_RESPONSE
when an MCP server was unreachable:
- Added a top-level try/catch in handleRequest so no request can ever
  close the connection without sending an HTTP response.
- Wrapped handleGetOAuth2Servers (whole handler + per-server OAuth
  detection loop) in try/catch, mirroring the pattern used by the
  configure endpoint.
- Added OAUTH_DETECT_TIMEOUT_MS (10 s) to all outbound fetches in
  OAuth2Manager.detectOAuth2Requirement so a hung server fails fast.
- Added MCP_CONNECT_TIMEOUT_MS (15 s) race in MCPProxy.createHttpClient
  and createStdioClient so a non-responsive MCP server does not stall
  indefinitely.

Co-authored-by: Cursor <cursoragent@cursor.com>
… fetch

Co-authored-by: Cursor <cursoragent@cursor.com>
…osed errors

Follow-up to the #126 work, addressing two remaining symptoms when an MCP
server is unreachable (e.g. VPN dropped):

1. Stray "The socket connection was closed unexpectedly" printed during
   install even though it succeeded. The connect-timeout added previously
   used a Promise.race that left the losing promise dangling: when the
   socket closed after we'd timed out (or the timer fired after a
   successful connect), the rejection surfaced as an unhandled rejection.
   Replaced it with connectWithTimeout(), which attaches a no-op catch to
   the connect promise and clears the timer in finally. Added process-level
   unhandledRejection/uncaughtException handlers as a safety net for the MCP
   SDK transport's background sockets.

2. The /tools endpoint returned 200 { tools: [] } for unreachable servers,
   so the UI could not tell "no tools" apart from "unreachable". listServerTools
   now accepts options and handleGetServerTools calls it with throwOnError,
   returning 502 with a clear message ("Server unreachable: ..."; the
   OAuth-disconnected message is passed through untouched). The web UI tools
   panel now renders that error, and useServerTools no longer retries so the
   error shows immediately.

Co-authored-by: Cursor <cursoragent@cursor.com>
… rejections

Half-open MCP servers (accept the TCP connection but never complete the
handshake, e.g. return an empty response) made client.connect() hang until
our timeout fired. Two edge cases could still leak a stray "MCP connect
timed out after 15000ms" unhandled rejection out to the process/UI:

- If client.connect() threw synchronously, the timeout timer was never
  cleared and rejected ~15s later into the void.
- On timeout we left the half-open client/transport open, so its socket
  could emit further late errors.

connectWithTimeout now: builds the timer only after connect starts, always
clears it in finally (covering synchronous throws), best-effort closes the
client + transport on timeout, and keeps the no-op catch on the connect
promise so a late socket-close rejection stays handled. The timeout is now
parameterized so it can be unit-tested with a short window.

Adds tests asserting the timeout path tears down the connection and never
produces an unhandled rejection across the timeout, late-rejection,
success, and synchronous-throw cases.

Co-authored-by: Cursor <cursoragent@cursor.com>
… text

CodeQL (js/stack-trace-exposure) flagged the /tools handler returning raw
error.message to the client, which can leak stack-trace-derived detail.

Both handleGetServerTools and the handleGetOAuth2Servers catch now log the
full detail server-side but return controlled, generic messages: tools
failures report "Server unreachable: \"<id>\" could not be contacted" (or an
auth-required prompt for OAuth-disconnected servers), and the oauth-servers
handler returns a generic "Failed to load OAuth2 servers". The UI still
shows a clear, actionable error without exposing internal exception text.

Co-authored-by: Cursor <cursoragent@cursor.com>
…chable

fix: remote skill install on expired token + MCP unreachable empty response
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the development-deps group with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@types/node` from 25.9.4 to 26.0.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: development-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
js-yaml 5.0.0 removed the default export (and now ships its own types),
which broke `import yaml from 'js-yaml'` and every suite that loads YAML.

- Switch to namespace imports (`import * as yaml`) in lockfile,
  capabilities, rules-installer, and the lockfile test.
- Guard empty input in parseCapabilitiesFile since v5 `load()` throws on
  empty input instead of returning undefined, preserving our clearer
  "empty or not an object" error.
- Drop @types/js-yaml; v5 bundles its own type definitions.

The dump options in use (indent, lineWidth, noRefs) remain valid in v5.

Co-authored-by: Cursor <cursoragent@cursor.com>
…_yarn/production-deps-ae0456aad4

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	package.json
…tion-deps-ae0456aad4

chore(deps): bump js-yaml from 4.2.0 to 5.0.0 in the production-deps group
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.

1 participant