-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(compat): McpError/ErrorCode/JSONRPCError/StreamableHTTPError + OAuth subclass aliases #1903
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c1d3cd8
c558a3b
f369da5
9696988
8db3141
96c1476
599b267
481bf4f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| '@modelcontextprotocol/client': patch | ||
| '@modelcontextprotocol/server': patch | ||
| --- | ||
|
|
||
| Add v1-compat `@deprecated` error aliases: `McpError`/`ErrorCode` (alias `ProtocolError`/`ProtocolErrorCode`, with `ConnectionClosed`/`RequestTimeout` from `SdkErrorCode`), `OAuthError.errorCode` getter (alias `.code`), `JSONRPCError`/`isJSONRPCError`, 16 of the 17 v1 OAuth error subclasses (`InvalidTokenError`, `ServerError`, … — `InvalidRequestError` is omitted from the public surface to avoid colliding with the `InvalidRequestError` interface in `types.ts`) as thin wrappers around `OAuthError` + `OAuthErrorCode`, and `StreamableHTTPError` as an `SdkError` subclass that the StreamableHTTP client transport now throws (so `instanceof StreamableHTTPError` matches; `.status` carries the HTTP status code). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| /** | ||
| * v1-compat: OAuth error subclasses. | ||
| * | ||
| * v1 shipped one `Error` subclass per OAuth error code (e.g. `InvalidTokenError`). | ||
| * v2 also exposes the consolidated {@link OAuthError} + {@link OAuthErrorCode} enum. | ||
| * These thin wrappers preserve `throw new InvalidTokenError(msg)` and `instanceof` | ||
| * patterns from v1 and set `.code` to the matching enum value. | ||
| */ | ||
|
|
||
| import { OAuthError, OAuthErrorCode } from '../auth/errors.js'; | ||
|
|
||
| type OAuthErrorSubclass = { | ||
| new (message: string, errorUri?: string): OAuthError; | ||
| /** @deprecated Use the instance `.code` property. */ | ||
| errorCode: string; | ||
| }; | ||
|
|
||
| function sub(code: OAuthErrorCode, name: string): OAuthErrorSubclass { | ||
| const Sub = class extends OAuthError { | ||
| static errorCode = code; | ||
| constructor(message: string, errorUri?: string) { | ||
| super(code, message, errorUri); | ||
| this.name = name; | ||
| } | ||
| }; | ||
| Object.defineProperty(Sub, 'name', { value: name, configurable: true }); | ||
| return Sub; | ||
| } | ||
|
|
||
| /* eslint-disable @typescript-eslint/naming-convention, @typescript-eslint/no-redeclare */ | ||
|
|
||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InvalidRequest`. */ | ||
| export const InvalidRequestError = sub(OAuthErrorCode.InvalidRequest, 'InvalidRequestError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InvalidRequest`. */ | ||
| export type InvalidRequestError = InstanceType<typeof InvalidRequestError>; | ||
|
claude[bot] marked this conversation as resolved.
|
||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InvalidClient`. */ | ||
| export const InvalidClientError = sub(OAuthErrorCode.InvalidClient, 'InvalidClientError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InvalidClient`. */ | ||
| export type InvalidClientError = InstanceType<typeof InvalidClientError>; | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InvalidGrant`. */ | ||
| export const InvalidGrantError = sub(OAuthErrorCode.InvalidGrant, 'InvalidGrantError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InvalidGrant`. */ | ||
| export type InvalidGrantError = InstanceType<typeof InvalidGrantError>; | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.UnauthorizedClient`. */ | ||
| export const UnauthorizedClientError = sub(OAuthErrorCode.UnauthorizedClient, 'UnauthorizedClientError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.UnauthorizedClient`. */ | ||
| export type UnauthorizedClientError = InstanceType<typeof UnauthorizedClientError>; | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.UnsupportedGrantType`. */ | ||
| export const UnsupportedGrantTypeError = sub(OAuthErrorCode.UnsupportedGrantType, 'UnsupportedGrantTypeError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.UnsupportedGrantType`. */ | ||
| export type UnsupportedGrantTypeError = InstanceType<typeof UnsupportedGrantTypeError>; | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InvalidScope`. */ | ||
| export const InvalidScopeError = sub(OAuthErrorCode.InvalidScope, 'InvalidScopeError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InvalidScope`. */ | ||
| export type InvalidScopeError = InstanceType<typeof InvalidScopeError>; | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.AccessDenied`. */ | ||
| export const AccessDeniedError = sub(OAuthErrorCode.AccessDenied, 'AccessDeniedError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.AccessDenied`. */ | ||
| export type AccessDeniedError = InstanceType<typeof AccessDeniedError>; | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.ServerError`. */ | ||
| export const ServerError = sub(OAuthErrorCode.ServerError, 'ServerError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.ServerError`. */ | ||
| export type ServerError = InstanceType<typeof ServerError>; | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.TemporarilyUnavailable`. */ | ||
| export const TemporarilyUnavailableError = sub(OAuthErrorCode.TemporarilyUnavailable, 'TemporarilyUnavailableError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.TemporarilyUnavailable`. */ | ||
| export type TemporarilyUnavailableError = InstanceType<typeof TemporarilyUnavailableError>; | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.UnsupportedResponseType`. */ | ||
| export const UnsupportedResponseTypeError = sub(OAuthErrorCode.UnsupportedResponseType, 'UnsupportedResponseTypeError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.UnsupportedResponseType`. */ | ||
| export type UnsupportedResponseTypeError = InstanceType<typeof UnsupportedResponseTypeError>; | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.UnsupportedTokenType`. */ | ||
| export const UnsupportedTokenTypeError = sub(OAuthErrorCode.UnsupportedTokenType, 'UnsupportedTokenTypeError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.UnsupportedTokenType`. */ | ||
| export type UnsupportedTokenTypeError = InstanceType<typeof UnsupportedTokenTypeError>; | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InvalidToken`. */ | ||
| export const InvalidTokenError = sub(OAuthErrorCode.InvalidToken, 'InvalidTokenError'); | ||
|
felixweinberger marked this conversation as resolved.
|
||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InvalidToken`. */ | ||
| export type InvalidTokenError = InstanceType<typeof InvalidTokenError>; | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.MethodNotAllowed`. */ | ||
| export const MethodNotAllowedError = sub(OAuthErrorCode.MethodNotAllowed, 'MethodNotAllowedError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.MethodNotAllowed`. */ | ||
| export type MethodNotAllowedError = InstanceType<typeof MethodNotAllowedError>; | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.TooManyRequests`. */ | ||
| export const TooManyRequestsError = sub(OAuthErrorCode.TooManyRequests, 'TooManyRequestsError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.TooManyRequests`. */ | ||
| export type TooManyRequestsError = InstanceType<typeof TooManyRequestsError>; | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InvalidClientMetadata`. */ | ||
| export const InvalidClientMetadataError = sub(OAuthErrorCode.InvalidClientMetadata, 'InvalidClientMetadataError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InvalidClientMetadata`. */ | ||
| export type InvalidClientMetadataError = InstanceType<typeof InvalidClientMetadataError>; | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InsufficientScope`. */ | ||
| export const InsufficientScopeError = sub(OAuthErrorCode.InsufficientScope, 'InsufficientScopeError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InsufficientScope`. */ | ||
| export type InsufficientScopeError = InstanceType<typeof InsufficientScopeError>; | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InvalidTarget`. */ | ||
| export const InvalidTargetError = sub(OAuthErrorCode.InvalidTarget, 'InvalidTargetError'); | ||
| /** @deprecated Use `OAuthError` with `OAuthErrorCode.InvalidTarget`. */ | ||
| export type InvalidTargetError = InstanceType<typeof InvalidTargetError>; | ||
|
|
||
| /** | ||
| * @deprecated Construct {@link OAuthError} directly with a custom code string. | ||
| * | ||
| * v1 pattern was `class MyErr extends CustomOAuthError { static errorCode = 'my_code' }`; | ||
| * this preserves that by reading `static errorCode` from the concrete subclass. | ||
| */ | ||
| export class CustomOAuthError extends OAuthError { | ||
| static errorCode: string; | ||
| constructor(message: string, errorUri?: string) { | ||
| super((new.target as typeof CustomOAuthError).errorCode, message, errorUri); | ||
| } | ||
| } | ||
|
Comment on lines
+107
to
+112
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 User subclasses of Extended reasoning...What's the gapv1's class MyErr extends CustomOAuthError { static errorCode = 'my_code' }— v1 produced In this PR, v2's Why nothing else covers itThe 17 The existing test in Step-by-step proof
ImpactLow — only affects Fixexport class CustomOAuthError extends OAuthError {
static errorCode: string;
constructor(message: string, errorUri?: string) {
super((new.target as typeof CustomOAuthError).errorCode, message, errorUri);
this.name = new.target.name;
}
}
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import type { SdkErrorCode } from './sdkErrors.js'; | ||
| import { SdkError } from './sdkErrors.js'; | ||
|
|
||
| /** | ||
| * @deprecated Use {@linkcode SdkError}. | ||
| * | ||
| * Subclass thrown by the StreamableHTTP client transport for HTTP-level errors. | ||
| * `instanceof StreamableHTTPError` and `instanceof SdkError` both match. Note that | ||
| * `.code` is now the {@linkcode SdkErrorCode} (a `ClientHttp*` string), not the HTTP | ||
| * status number as in v1; the status is available as `.status`. | ||
| */ | ||
| export class StreamableHTTPError extends SdkError { | ||
|
claude[bot] marked this conversation as resolved.
|
||
| public readonly status: number | undefined; | ||
|
|
||
| constructor(code: SdkErrorCode, message: string, data?: { status?: number } & Record<string, unknown>) { | ||
| super(code, message, data); | ||
| this.name = 'StreamableHTTPError'; | ||
| this.status = data?.status; | ||
| } | ||
| } | ||
|
felixweinberger marked this conversation as resolved.
|
||
Uh oh!
There was an error while loading. Please reload this page.