v2: Errors refactor (ProtocolError, SdkError, OAuthError) (#1454)
This commit is contained in:
parent
160902eb2e
commit
0f0a4eb7c8
5 changed files with 1086 additions and 705 deletions
|
|
@ -4,11 +4,12 @@ import {
|
|||
CallToolResultSchema,
|
||||
CreateTaskResultSchema,
|
||||
ElicitResultSchema,
|
||||
ErrorCode,
|
||||
InMemoryTransport,
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
ListToolsResultSchema,
|
||||
McpError,
|
||||
ProtocolErrorCode,
|
||||
SdkError,
|
||||
SdkErrorCode,
|
||||
SUPPORTED_PROTOCOL_VERSIONS
|
||||
} from '@modelcontextprotocol/core';
|
||||
import { InMemoryTaskStore, McpServer, Server } from '@modelcontextprotocol/server';
|
||||
|
|
@ -815,7 +816,7 @@ test('should reject form-mode elicitation when client only supports URL mode', a
|
|||
const response = (await responsePromise) as { id: number; error: { code: number; message: string } };
|
||||
|
||||
expect(response.id).toBe(requestId);
|
||||
expect(response.error.code).toBe(ErrorCode.InvalidParams);
|
||||
expect(response.error.code).toBe(ProtocolErrorCode.InvalidParams);
|
||||
expect(response.error.message).toContain('Client does not support form-mode elicitation requests');
|
||||
expect(handler).not.toHaveBeenCalled();
|
||||
|
||||
|
|
@ -954,7 +955,7 @@ test('should reject URL-mode elicitation when client only supports form mode', a
|
|||
const response = (await responsePromise) as { id: number; error: { code: number; message: string } };
|
||||
|
||||
expect(response.id).toBe(requestId);
|
||||
expect(response.error.code).toBe(ErrorCode.InvalidParams);
|
||||
expect(response.error.code).toBe(ProtocolErrorCode.InvalidParams);
|
||||
expect(response.error.message).toContain('Client does not support URL-mode elicitation requests');
|
||||
expect(handler).not.toHaveBeenCalled();
|
||||
|
||||
|
|
@ -1074,8 +1075,8 @@ test('should handle client cancelling a request', async () => {
|
|||
});
|
||||
controller.abort('Cancelled by test');
|
||||
|
||||
// Request should be rejected with an McpError
|
||||
await expect(listResourcesPromise).rejects.toThrow(McpError);
|
||||
// Request should be rejected with an SdkError (local timeout/cancellation)
|
||||
await expect(listResourcesPromise).rejects.toThrow(SdkError);
|
||||
});
|
||||
|
||||
/***
|
||||
|
|
@ -1123,7 +1124,7 @@ test('should handle request timeout', async () => {
|
|||
|
||||
// Request with 0 msec timeout should fail immediately
|
||||
await expect(client.listResources(undefined, { timeout: 0 })).rejects.toMatchObject({
|
||||
code: ErrorCode.RequestTimeout
|
||||
code: SdkErrorCode.RequestTimeout
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ErrorCode, McpError } from '@modelcontextprotocol/core';
|
||||
import { ProtocolError, ProtocolErrorCode } from '@modelcontextprotocol/core';
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import { createInMemoryTaskEnvironment } from '../../helpers/mcp.js';
|
||||
|
|
@ -88,9 +88,9 @@ describe('Task Listing with Pagination', () => {
|
|||
});
|
||||
|
||||
// Try to use an invalid cursor - should return -32602 (Invalid params) per MCP spec
|
||||
await expect(client.experimental.tasks.listTasks('invalid-cursor')).rejects.toSatisfy((error: McpError) => {
|
||||
expect(error).toBeInstanceOf(McpError);
|
||||
expect(error.code).toBe(ErrorCode.InvalidParams);
|
||||
await expect(client.experimental.tasks.listTasks('invalid-cursor')).rejects.toSatisfy((error: ProtocolError) => {
|
||||
expect(error).toBeInstanceOf(ProtocolError);
|
||||
expect(error.code).toBe(ProtocolErrorCode.InvalidParams);
|
||||
expect(error.message).toContain('Invalid cursor');
|
||||
return true;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,10 +13,12 @@ import {
|
|||
CreateMessageResultSchema,
|
||||
CreateTaskResultSchema,
|
||||
ElicitResultSchema,
|
||||
ErrorCode,
|
||||
InMemoryTransport,
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
McpError,
|
||||
ProtocolError,
|
||||
ProtocolErrorCode,
|
||||
SdkError,
|
||||
SdkErrorCode,
|
||||
SUPPORTED_PROTOCOL_VERSIONS
|
||||
} from '@modelcontextprotocol/core';
|
||||
import { createMcpExpressApp } from '@modelcontextprotocol/express';
|
||||
|
|
@ -1356,8 +1358,8 @@ test('should handle server cancelling a request', async () => {
|
|||
);
|
||||
controller.abort('Cancelled by test');
|
||||
|
||||
// Request should be rejected with an McpError
|
||||
await expect(createMessagePromise).rejects.toThrow(McpError);
|
||||
// Request should be rejected with an SdkError (local timeout/cancellation)
|
||||
await expect(createMessagePromise).rejects.toThrow(SdkError);
|
||||
});
|
||||
|
||||
test('should handle request timeout', async () => {
|
||||
|
|
@ -1417,7 +1419,7 @@ test('should handle request timeout', async () => {
|
|||
{ timeout: 0 }
|
||||
)
|
||||
).rejects.toMatchObject({
|
||||
code: ErrorCode.RequestTimeout
|
||||
code: SdkErrorCode.RequestTimeout
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import type { CallToolResult, Notification, TextContent } from '@modelcontextpro
|
|||
import {
|
||||
CallToolResultSchema,
|
||||
CompleteResultSchema,
|
||||
ErrorCode,
|
||||
getDisplayName,
|
||||
GetPromptResultSchema,
|
||||
InMemoryTaskStore,
|
||||
|
|
@ -12,6 +11,7 @@ import {
|
|||
ListResourcesResultSchema,
|
||||
ListResourceTemplatesResultSchema,
|
||||
ListToolsResultSchema,
|
||||
ProtocolErrorCode,
|
||||
ReadResourceResultSchema,
|
||||
UriTemplate,
|
||||
UrlElicitationRequiredError
|
||||
|
|
@ -1571,9 +1571,9 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
});
|
||||
|
||||
/***
|
||||
* Test: McpError for Invalid Tool Name
|
||||
* Test: ProtocolError for Invalid Tool Name
|
||||
*/
|
||||
test('should throw McpError for invalid tool name', async () => {
|
||||
test('should throw ProtocolError for invalid tool name', async () => {
|
||||
const mcpServer = new McpServer({
|
||||
name: 'test server',
|
||||
version: '1.0'
|
||||
|
|
@ -1666,7 +1666,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
.catch(error => {
|
||||
expect(error).toBeInstanceOf(UrlElicitationRequiredError);
|
||||
if (error instanceof UrlElicitationRequiredError) {
|
||||
expect(error.code).toBe(ErrorCode.UrlElicitationRequired);
|
||||
expect(error.code).toBe(ProtocolErrorCode.UrlElicitationRequired);
|
||||
expect(error.elicitations).toEqual([elicitationParams]);
|
||||
}
|
||||
});
|
||||
|
|
@ -2612,9 +2612,9 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
});
|
||||
|
||||
/***
|
||||
* Test: McpError for Invalid Resource URI
|
||||
* Test: ProtocolError for Invalid Resource URI
|
||||
*/
|
||||
test('should throw McpError for invalid resource URI', async () => {
|
||||
test('should throw ProtocolError for invalid resource URI', async () => {
|
||||
const mcpServer = new McpServer({
|
||||
name: 'test server',
|
||||
version: '1.0'
|
||||
|
|
@ -3537,9 +3537,9 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
});
|
||||
|
||||
/***
|
||||
* Test: McpError for Invalid Prompt Name
|
||||
* Test: ProtocolError for Invalid Prompt Name
|
||||
*/
|
||||
test('should throw McpError for invalid prompt name', async () => {
|
||||
test('should throw ProtocolError for invalid prompt name', async () => {
|
||||
const mcpServer = new McpServer({
|
||||
name: 'test server',
|
||||
version: '1.0'
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ import {
|
|||
CallToolResultSchema,
|
||||
CreateTaskResultSchema,
|
||||
ElicitResultSchema,
|
||||
ErrorCode,
|
||||
InMemoryTaskMessageQueue,
|
||||
InMemoryTaskStore,
|
||||
McpError,
|
||||
McpServer,
|
||||
ProtocolError,
|
||||
ProtocolErrorCode,
|
||||
RELATED_TASK_META_KEY,
|
||||
TaskSchema
|
||||
} from '@modelcontextprotocol/server';
|
||||
|
|
@ -394,9 +394,9 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
expect(task.status).toBe('completed');
|
||||
|
||||
// Try to cancel via tasks/cancel request (should fail with -32602)
|
||||
await expect(client.experimental.tasks.cancelTask(taskId)).rejects.toSatisfy((error: McpError) => {
|
||||
expect(error).toBeInstanceOf(McpError);
|
||||
expect(error.code).toBe(ErrorCode.InvalidParams);
|
||||
await expect(client.experimental.tasks.cancelTask(taskId)).rejects.toSatisfy((error: ProtocolError) => {
|
||||
expect(error).toBeInstanceOf(ProtocolError);
|
||||
expect(error.code).toBe(ProtocolErrorCode.InvalidParams);
|
||||
expect(error.message).toContain('Cannot cancel task in terminal status');
|
||||
return true;
|
||||
});
|
||||
|
|
@ -788,9 +788,9 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
await client.connect(transport);
|
||||
|
||||
// Try to get non-existent task via tasks/get request
|
||||
await expect(client.experimental.tasks.getTask('non-existent-task-id')).rejects.toSatisfy((error: McpError) => {
|
||||
expect(error).toBeInstanceOf(McpError);
|
||||
expect(error.code).toBe(ErrorCode.InvalidParams);
|
||||
await expect(client.experimental.tasks.getTask('non-existent-task-id')).rejects.toSatisfy((error: ProtocolError) => {
|
||||
expect(error).toBeInstanceOf(ProtocolError);
|
||||
expect(error.code).toBe(ProtocolErrorCode.InvalidParams);
|
||||
expect(error.message).toContain('Task not found');
|
||||
return true;
|
||||
});
|
||||
|
|
@ -808,9 +808,9 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
await client.connect(transport);
|
||||
|
||||
// Try to cancel non-existent task via tasks/cancel request
|
||||
await expect(client.experimental.tasks.cancelTask('non-existent-task-id')).rejects.toSatisfy((error: McpError) => {
|
||||
expect(error).toBeInstanceOf(McpError);
|
||||
expect(error.code).toBe(ErrorCode.InvalidParams);
|
||||
await expect(client.experimental.tasks.cancelTask('non-existent-task-id')).rejects.toSatisfy((error: ProtocolError) => {
|
||||
expect(error).toBeInstanceOf(ProtocolError);
|
||||
expect(error.code).toBe(ProtocolErrorCode.InvalidParams);
|
||||
expect(error.message).toContain('Task not found');
|
||||
return true;
|
||||
});
|
||||
|
|
@ -836,9 +836,9 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
},
|
||||
CallToolResultSchema
|
||||
)
|
||||
).rejects.toSatisfy((error: McpError) => {
|
||||
expect(error).toBeInstanceOf(McpError);
|
||||
expect(error.code).toBe(ErrorCode.InvalidParams);
|
||||
).rejects.toSatisfy((error: ProtocolError) => {
|
||||
expect(error).toBeInstanceOf(ProtocolError);
|
||||
expect(error.code).toBe(ProtocolErrorCode.InvalidParams);
|
||||
expect(error.message).toContain('Task not found');
|
||||
return true;
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue