adjust prettier to use more defaults (#8)

* simplify prettier to use more defaults

* apply new prettier
This commit is contained in:
Paul Carleton 2025-11-04 15:34:27 +00:00 • committed by GitHub
commit b377251b15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 12352 additions and 12153 deletions

View file

@ -4,80 +4,91 @@
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import { LoggingMessageNotificationSchema, ProgressNotificationSchema } from '@modelcontextprotocol/sdk/types.js';
import {
LoggingMessageNotificationSchema,
ProgressNotificationSchema
} from '@modelcontextprotocol/sdk/types.js';
export interface MCPClientConnection {
client: Client;
close: () => Promise<void>;
client: Client;
close: () => Promise<void>;
}
/**
* Create and connect an MCP client to a server
*/
export async function connectToServer(serverUrl: string): Promise<MCPClientConnection> {
const client = new Client(
{
name: 'conformance-test-client',
version: '1.0.0'
},
{
capabilities: {
// Client capabilities
sampling: {},
elicitation: {}
}
}
);
export async function connectToServer(
serverUrl: string
): Promise<MCPClientConnection> {
const client = new Client(
{
name: 'conformance-test-client',
version: '1.0.0'
},
{
capabilities: {
// Client capabilities
sampling: {},
elicitation: {}
}
}
);
const transport = new StreamableHTTPClientTransport(new URL(serverUrl));
const transport = new StreamableHTTPClientTransport(new URL(serverUrl));
await client.connect(transport);
await client.connect(transport);
return {
client,
close: async () => {
await client.close();
}
};
return {
client,
close: async () => {
await client.close();
}
};
}
/**
* Helper to collect notifications (logging and progress)
*/
export class NotificationCollector {
private loggingNotifications: any[] = [];
private progressNotifications: any[] = [];
private loggingNotifications: any[] = [];
private progressNotifications: any[] = [];
constructor(client: Client) {
// Set up notification handler for logging messages
client.setNotificationHandler(LoggingMessageNotificationSchema, notification => {
this.loggingNotifications.push(notification);
});
constructor(client: Client) {
// Set up notification handler for logging messages
client.setNotificationHandler(
LoggingMessageNotificationSchema,
(notification) => {
this.loggingNotifications.push(notification);
}
);
// Set up notification handler for progress notifications
client.setNotificationHandler(ProgressNotificationSchema, notification => {
this.progressNotifications.push(notification);
});
}
// Set up notification handler for progress notifications
client.setNotificationHandler(
ProgressNotificationSchema,
(notification) => {
this.progressNotifications.push(notification);
}
);
}
/**
* Get all collected logging notifications
*/
getLoggingNotifications(): any[] {
return this.loggingNotifications;
}
/**
* Get all collected logging notifications
*/
getLoggingNotifications(): any[] {
return this.loggingNotifications;
}
/**
* Get all collected progress notifications
*/
getProgressNotifications(): any[] {
return this.progressNotifications;
}
/**
* Get all collected progress notifications
*/
getProgressNotifications(): any[] {
return this.progressNotifications;
}
/**
* Get all notifications (for backward compatibility)
*/
getNotifications(): any[] {
return this.loggingNotifications;
}
/**
* Get all notifications (for backward compatibility)
*/
getNotifications(): any[] {
return this.loggingNotifications;
}
}

View file

@ -6,53 +6,55 @@ import { ClientScenario, ConformanceCheck } from '../../types.js';
import { connectToServer } from './client-helper.js';
export class ServerInitializeScenario implements ClientScenario {
name = 'server-initialize';
description = 'Test basic server initialization handshake';
name = 'server-initialize';
description = 'Test basic server initialization handshake';
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
try {
const connection = await connectToServer(serverUrl);
try {
const connection = await connectToServer(serverUrl);
// The connection process already does initialization
// Check that we have a connected client
checks.push({
id: 'server-initialize',
name: 'ServerInitialize',
description: 'Server responds to initialize request with valid structure',
status: 'SUCCESS',
timestamp: new Date().toISOString(),
specReferences: [
{
id: 'MCP-Initialize',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle#initialization'
}
],
details: {
serverUrl,
connected: true
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'server-initialize',
name: 'ServerInitialize',
description: 'Server responds to initialize request with valid structure',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed to initialize: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Initialize',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle#initialization'
}
]
});
// The connection process already does initialization
// Check that we have a connected client
checks.push({
id: 'server-initialize',
name: 'ServerInitialize',
description:
'Server responds to initialize request with valid structure',
status: 'SUCCESS',
timestamp: new Date().toISOString(),
specReferences: [
{
id: 'MCP-Initialize',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle#initialization'
}
],
details: {
serverUrl,
connected: true
}
});
return checks;
await connection.close();
} catch (error) {
checks.push({
id: 'server-initialize',
name: 'ServerInitialize',
description:
'Server responds to initialize request with valid structure',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed to initialize: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Initialize',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle#initialization'
}
]
});
}
return checks;
}
}

View file

@ -6,345 +6,352 @@ import { ClientScenario, ConformanceCheck } from '../../types.js';
import { connectToServer } from './client-helper.js';
export class PromptsListScenario implements ClientScenario {
name = 'prompts-list';
description = 'Test listing available prompts';
name = 'prompts-list';
description = 'Test listing available prompts';
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
try {
const connection = await connectToServer(serverUrl);
try {
const connection = await connectToServer(serverUrl);
const result = await connection.client.listPrompts();
const result = await connection.client.listPrompts();
// Validate response structure
const errors: string[] = [];
if (!result.prompts) {
errors.push('Missing prompts array');
} else {
if (!Array.isArray(result.prompts)) {
errors.push('prompts is not an array');
}
result.prompts.forEach((prompt, index) => {
if (!prompt.name) errors.push(`Prompt ${index}: missing name`);
if (!prompt.description) errors.push(`Prompt ${index}: missing description`);
});
}
checks.push({
id: 'prompts-list',
name: 'PromptsList',
description: 'Server lists available prompts with valid structure',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Prompts-List',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#listing-prompts'
}
],
details: {
promptCount: result.prompts?.length || 0,
prompts: result.prompts?.map(p => p.name)
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'prompts-list',
name: 'PromptsList',
description: 'Server lists available prompts with valid structure',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Prompts-List',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#listing-prompts'
}
]
});
// Validate response structure
const errors: string[] = [];
if (!result.prompts) {
errors.push('Missing prompts array');
} else {
if (!Array.isArray(result.prompts)) {
errors.push('prompts is not an array');
}
return checks;
result.prompts.forEach((prompt, index) => {
if (!prompt.name) errors.push(`Prompt ${index}: missing name`);
if (!prompt.description)
errors.push(`Prompt ${index}: missing description`);
});
}
checks.push({
id: 'prompts-list',
name: 'PromptsList',
description: 'Server lists available prompts with valid structure',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Prompts-List',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#listing-prompts'
}
],
details: {
promptCount: result.prompts?.length || 0,
prompts: result.prompts?.map((p) => p.name)
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'prompts-list',
name: 'PromptsList',
description: 'Server lists available prompts with valid structure',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Prompts-List',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#listing-prompts'
}
]
});
}
return checks;
}
}
export class PromptsGetSimpleScenario implements ClientScenario {
name = 'prompts-get-simple';
description = 'Test getting a simple prompt without arguments';
name = 'prompts-get-simple';
description = 'Test getting a simple prompt without arguments';
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
try {
const connection = await connectToServer(serverUrl);
try {
const connection = await connectToServer(serverUrl);
const result = await connection.client.getPrompt({
name: 'test_simple_prompt'
});
const result = await connection.client.getPrompt({
name: 'test_simple_prompt'
});
// Validate response
const errors: string[] = [];
if (!result.messages) errors.push('Missing messages array');
if (!Array.isArray(result.messages)) errors.push('messages is not an array');
if (result.messages.length === 0) errors.push('messages array is empty');
// Validate response
const errors: string[] = [];
if (!result.messages) errors.push('Missing messages array');
if (!Array.isArray(result.messages))
errors.push('messages is not an array');
if (result.messages.length === 0) errors.push('messages array is empty');
result.messages.forEach((message: any, index: number) => {
if (!message.role) errors.push(`Message ${index}: missing role`);
if (!message.content) errors.push(`Message ${index}: missing content`);
});
result.messages.forEach((message: any, index: number) => {
if (!message.role) errors.push(`Message ${index}: missing role`);
if (!message.content) errors.push(`Message ${index}: missing content`);
});
checks.push({
id: 'prompts-get-simple',
name: 'PromptsGetSimple',
description: 'Get simple prompt successfully',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Prompts-Get',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#getting-prompts'
}
],
details: {
messageCount: result.messages?.length || 0
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'prompts-get-simple',
name: 'PromptsGetSimple',
description: 'Get simple prompt successfully',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Prompts-Get',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#getting-prompts'
}
]
});
checks.push({
id: 'prompts-get-simple',
name: 'PromptsGetSimple',
description: 'Get simple prompt successfully',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Prompts-Get',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#getting-prompts'
}
],
details: {
messageCount: result.messages?.length || 0
}
});
return checks;
await connection.close();
} catch (error) {
checks.push({
id: 'prompts-get-simple',
name: 'PromptsGetSimple',
description: 'Get simple prompt successfully',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Prompts-Get',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#getting-prompts'
}
]
});
}
return checks;
}
}
export class PromptsGetWithArgsScenario implements ClientScenario {
name = 'prompts-get-with-args';
description = 'Test parameterized prompt';
name = 'prompts-get-with-args';
description = 'Test parameterized prompt';
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
try {
const connection = await connectToServer(serverUrl);
try {
const connection = await connectToServer(serverUrl);
const result = await connection.client.getPrompt({
name: 'test_prompt_with_arguments',
arguments: {
arg1: 'testValue1',
arg2: 'testValue2'
}
});
// Validate response
const errors: string[] = [];
if (!result.messages) errors.push('Missing messages array');
if (result.messages.length === 0) errors.push('messages array is empty');
// Check if arguments were substituted
const messageText = JSON.stringify(result.messages);
if (!messageText.includes('testValue1')) {
errors.push('arg1 not substituted in prompt');
}
if (!messageText.includes('testValue2')) {
errors.push('arg2 not substituted in prompt');
}
checks.push({
id: 'prompts-get-with-args',
name: 'PromptsGetWithArgs',
description: 'Get parameterized prompt with argument substitution',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Prompts-Get',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#getting-prompts'
}
],
details: {
messageCount: result.messages?.length || 0,
messages: result.messages
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'prompts-get-with-args',
name: 'PromptsGetWithArgs',
description: 'Get parameterized prompt with argument substitution',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Prompts-Get',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#getting-prompts'
}
]
});
const result = await connection.client.getPrompt({
name: 'test_prompt_with_arguments',
arguments: {
arg1: 'testValue1',
arg2: 'testValue2'
}
});
return checks;
// Validate response
const errors: string[] = [];
if (!result.messages) errors.push('Missing messages array');
if (result.messages.length === 0) errors.push('messages array is empty');
// Check if arguments were substituted
const messageText = JSON.stringify(result.messages);
if (!messageText.includes('testValue1')) {
errors.push('arg1 not substituted in prompt');
}
if (!messageText.includes('testValue2')) {
errors.push('arg2 not substituted in prompt');
}
checks.push({
id: 'prompts-get-with-args',
name: 'PromptsGetWithArgs',
description: 'Get parameterized prompt with argument substitution',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Prompts-Get',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#getting-prompts'
}
],
details: {
messageCount: result.messages?.length || 0,
messages: result.messages
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'prompts-get-with-args',
name: 'PromptsGetWithArgs',
description: 'Get parameterized prompt with argument substitution',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Prompts-Get',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#getting-prompts'
}
]
});
}
return checks;
}
}
export class PromptsGetEmbeddedResourceScenario implements ClientScenario {
name = 'prompts-get-embedded-resource';
description = 'Test prompt with embedded resource content';
name = 'prompts-get-embedded-resource';
description = 'Test prompt with embedded resource content';
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
try {
const connection = await connectToServer(serverUrl);
try {
const connection = await connectToServer(serverUrl);
const result = await connection.client.getPrompt({
name: 'test_prompt_with_embedded_resource',
arguments: {
resourceUri: 'test://example-resource'
}
});
// Validate response
const errors: string[] = [];
if (!result.messages) errors.push('Missing messages array');
// Look for resource content
const hasResourceContent = result.messages.some(
(msg: any) => msg.content?.type === 'resource' || msg.content?.resource !== undefined
);
if (!hasResourceContent) {
errors.push('No embedded resource found in prompt');
}
checks.push({
id: 'prompts-get-embedded-resource',
name: 'PromptsGetEmbeddedResource',
description: 'Get prompt with embedded resource',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Prompts-Embedded-Resources',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#embedded-resources'
}
],
details: {
messageCount: result.messages?.length || 0,
messages: result.messages
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'prompts-get-embedded-resource',
name: 'PromptsGetEmbeddedResource',
description: 'Get prompt with embedded resource',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Prompts-Embedded-Resources',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#embedded-resources'
}
]
});
const result = await connection.client.getPrompt({
name: 'test_prompt_with_embedded_resource',
arguments: {
resourceUri: 'test://example-resource'
}
});
return checks;
// Validate response
const errors: string[] = [];
if (!result.messages) errors.push('Missing messages array');
// Look for resource content
const hasResourceContent = result.messages.some(
(msg: any) =>
msg.content?.type === 'resource' ||
msg.content?.resource !== undefined
);
if (!hasResourceContent) {
errors.push('No embedded resource found in prompt');
}
checks.push({
id: 'prompts-get-embedded-resource',
name: 'PromptsGetEmbeddedResource',
description: 'Get prompt with embedded resource',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Prompts-Embedded-Resources',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#embedded-resources'
}
],
details: {
messageCount: result.messages?.length || 0,
messages: result.messages
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'prompts-get-embedded-resource',
name: 'PromptsGetEmbeddedResource',
description: 'Get prompt with embedded resource',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Prompts-Embedded-Resources',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#embedded-resources'
}
]
});
}
return checks;
}
}
export class PromptsGetWithImageScenario implements ClientScenario {
name = 'prompts-get-with-image';
description = 'Test prompt with image content';
name = 'prompts-get-with-image';
description = 'Test prompt with image content';
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
try {
const connection = await connectToServer(serverUrl);
try {
const connection = await connectToServer(serverUrl);
const result = await connection.client.getPrompt({
name: 'test_prompt_with_image'
});
const result = await connection.client.getPrompt({
name: 'test_prompt_with_image'
});
// Validate response
const errors: string[] = [];
if (!result.messages) errors.push('Missing messages array');
// Validate response
const errors: string[] = [];
if (!result.messages) errors.push('Missing messages array');
// Look for image content
const hasImageContent = result.messages.some(
(msg: any) => msg.content?.type === 'image' && msg.content?.data && msg.content?.mimeType
);
// Look for image content
const hasImageContent = result.messages.some(
(msg: any) =>
msg.content?.type === 'image' &&
msg.content?.data &&
msg.content?.mimeType
);
if (!hasImageContent) {
errors.push('No image content found in prompt');
}
if (!hasImageContent) {
errors.push('No image content found in prompt');
}
checks.push({
id: 'prompts-get-with-image',
name: 'PromptsGetWithImage',
description: 'Get prompt with image content',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Prompts-Image',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#image-content'
}
],
details: {
messageCount: result.messages?.length || 0
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'prompts-get-with-image',
name: 'PromptsGetWithImage',
description: 'Get prompt with image content',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Prompts-Image',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#image-content'
}
]
});
checks.push({
id: 'prompts-get-with-image',
name: 'PromptsGetWithImage',
description: 'Get prompt with image content',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Prompts-Image',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#image-content'
}
],
details: {
messageCount: result.messages?.length || 0
}
});
return checks;
await connection.close();
} catch (error) {
checks.push({
id: 'prompts-get-with-image',
name: 'PromptsGetWithImage',
description: 'Get prompt with image content',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Prompts-Image',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#image-content'
}
]
});
}
return checks;
}
}

View file

@ -6,384 +6,386 @@ import { ClientScenario, ConformanceCheck } from '../../types.js';
import { connectToServer } from './client-helper.js';
export class ResourcesListScenario implements ClientScenario {
name = 'resources-list';
description = 'Test listing available resources';
name = 'resources-list';
description = 'Test listing available resources';
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
try {
const connection = await connectToServer(serverUrl);
try {
const connection = await connectToServer(serverUrl);
const result = await connection.client.listResources();
const result = await connection.client.listResources();
// Validate response structure
const errors: string[] = [];
if (!result.resources) {
errors.push('Missing resources array');
} else {
if (!Array.isArray(result.resources)) {
errors.push('resources is not an array');
}
result.resources.forEach((resource, index) => {
if (!resource.uri) errors.push(`Resource ${index}: missing uri`);
if (!resource.name) errors.push(`Resource ${index}: missing name`);
});
}
checks.push({
id: 'resources-list',
name: 'ResourcesList',
description: 'Server lists available resources with valid structure',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Resources-List',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#listing-resources'
}
],
details: {
resourceCount: result.resources?.length || 0,
resources: result.resources?.map(r => r.uri)
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'resources-list',
name: 'ResourcesList',
description: 'Server lists available resources with valid structure',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Resources-List',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#listing-resources'
}
]
});
// Validate response structure
const errors: string[] = [];
if (!result.resources) {
errors.push('Missing resources array');
} else {
if (!Array.isArray(result.resources)) {
errors.push('resources is not an array');
}
return checks;
result.resources.forEach((resource, index) => {
if (!resource.uri) errors.push(`Resource ${index}: missing uri`);
if (!resource.name) errors.push(`Resource ${index}: missing name`);
});
}
checks.push({
id: 'resources-list',
name: 'ResourcesList',
description: 'Server lists available resources with valid structure',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Resources-List',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#listing-resources'
}
],
details: {
resourceCount: result.resources?.length || 0,
resources: result.resources?.map((r) => r.uri)
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'resources-list',
name: 'ResourcesList',
description: 'Server lists available resources with valid structure',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Resources-List',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#listing-resources'
}
]
});
}
return checks;
}
}
export class ResourcesReadTextScenario implements ClientScenario {
name = 'resources-read-text';
description = 'Test reading text resource';
name = 'resources-read-text';
description = 'Test reading text resource';
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
try {
const connection = await connectToServer(serverUrl);
try {
const connection = await connectToServer(serverUrl);
const result = await connection.client.readResource({
uri: 'test://static-text'
});
const result = await connection.client.readResource({
uri: 'test://static-text'
});
// Validate response
const errors: string[] = [];
if (!result.contents) errors.push('Missing contents array');
if (!Array.isArray(result.contents)) errors.push('contents is not an array');
if (result.contents.length === 0) errors.push('contents array is empty');
// Validate response
const errors: string[] = [];
if (!result.contents) errors.push('Missing contents array');
if (!Array.isArray(result.contents))
errors.push('contents is not an array');
if (result.contents.length === 0) errors.push('contents array is empty');
const content = result.contents[0];
if (content) {
if (!content.uri) errors.push('Content missing uri');
if (!content.mimeType) errors.push('Content missing mimeType');
if (!content.text) errors.push('Content missing text field');
}
const content = result.contents[0];
if (content) {
if (!content.uri) errors.push('Content missing uri');
if (!content.mimeType) errors.push('Content missing mimeType');
if (!content.text) errors.push('Content missing text field');
}
checks.push({
id: 'resources-read-text',
name: 'ResourcesReadText',
description: 'Read text resource successfully',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Resources-Read',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#reading-resources'
}
],
details: {
uri: content?.uri,
mimeType: content?.mimeType,
hasText: !!content?.text
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'resources-read-text',
name: 'ResourcesReadText',
description: 'Read text resource successfully',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Resources-Read',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#reading-resources'
}
]
});
checks.push({
id: 'resources-read-text',
name: 'ResourcesReadText',
description: 'Read text resource successfully',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Resources-Read',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#reading-resources'
}
],
details: {
uri: content?.uri,
mimeType: content?.mimeType,
hasText: !!content?.text
}
});
return checks;
await connection.close();
} catch (error) {
checks.push({
id: 'resources-read-text',
name: 'ResourcesReadText',
description: 'Read text resource successfully',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Resources-Read',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#reading-resources'
}
]
});
}
return checks;
}
}
export class ResourcesReadBinaryScenario implements ClientScenario {
name = 'resources-read-binary';
description = 'Test reading binary resource';
name = 'resources-read-binary';
description = 'Test reading binary resource';
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
try {
const connection = await connectToServer(serverUrl);
try {
const connection = await connectToServer(serverUrl);
const result = await connection.client.readResource({
uri: 'test://static-binary'
});
const result = await connection.client.readResource({
uri: 'test://static-binary'
});
// Validate response
const errors: string[] = [];
if (!result.contents) errors.push('Missing contents array');
if (result.contents.length === 0) errors.push('contents array is empty');
// Validate response
const errors: string[] = [];
if (!result.contents) errors.push('Missing contents array');
if (result.contents.length === 0) errors.push('contents array is empty');
const content = result.contents[0];
if (content) {
if (!content.uri) errors.push('Content missing uri');
if (!content.mimeType) errors.push('Content missing mimeType');
if (!content.blob) errors.push('Content missing blob field');
}
const content = result.contents[0];
if (content) {
if (!content.uri) errors.push('Content missing uri');
if (!content.mimeType) errors.push('Content missing mimeType');
if (!content.blob) errors.push('Content missing blob field');
}
checks.push({
id: 'resources-read-binary',
name: 'ResourcesReadBinary',
description: 'Read binary resource successfully',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Resources-Read',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#reading-resources'
}
],
details: {
uri: content?.uri,
mimeType: content?.mimeType,
hasBlob: !!content?.blob
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'resources-read-binary',
name: 'ResourcesReadBinary',
description: 'Read binary resource successfully',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Resources-Read',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#reading-resources'
}
]
});
checks.push({
id: 'resources-read-binary',
name: 'ResourcesReadBinary',
description: 'Read binary resource successfully',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Resources-Read',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#reading-resources'
}
],
details: {
uri: content?.uri,
mimeType: content?.mimeType,
hasBlob: !!content?.blob
}
});
return checks;
await connection.close();
} catch (error) {
checks.push({
id: 'resources-read-binary',
name: 'ResourcesReadBinary',
description: 'Read binary resource successfully',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Resources-Read',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#reading-resources'
}
]
});
}
return checks;
}
}
export class ResourcesTemplateReadScenario implements ClientScenario {
name = 'resources-templates-read';
description = 'Test reading resource from template';
name = 'resources-templates-read';
description = 'Test reading resource from template';
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
try {
const connection = await connectToServer(serverUrl);
try {
const connection = await connectToServer(serverUrl);
const result = await connection.client.readResource({
uri: 'test://template/123/data'
});
const result = await connection.client.readResource({
uri: 'test://template/123/data'
});
// Validate response
const errors: string[] = [];
if (!result.contents) errors.push('Missing contents array');
if (result.contents.length === 0) errors.push('contents array is empty');
// Validate response
const errors: string[] = [];
if (!result.contents) errors.push('Missing contents array');
if (result.contents.length === 0) errors.push('contents array is empty');
const content = result.contents[0];
if (content) {
if (!content.uri) errors.push('Content missing uri');
if (!content.text && !content.blob) errors.push('Content missing text or blob');
const content = result.contents[0];
if (content) {
if (!content.uri) errors.push('Content missing uri');
if (!content.text && !content.blob)
errors.push('Content missing text or blob');
// Check if parameter was substituted
const text = content.text || (content.blob ? '[binary]' : '');
if (typeof text === 'string' && !text.includes('123')) {
errors.push('Parameter substitution not reflected in content');
}
}
checks.push({
id: 'resources-templates-read',
name: 'ResourcesTemplateRead',
description: 'Read resource from template with parameter substitution',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Resources-Templates',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-templates'
}
],
details: {
uri: content?.uri,
content: content?.text || content?.blob
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'resources-templates-read',
name: 'ResourcesTemplateRead',
description: 'Read resource from template with parameter substitution',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Resources-Templates',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-templates'
}
]
});
// Check if parameter was substituted
const text = content.text || (content.blob ? '[binary]' : '');
if (typeof text === 'string' && !text.includes('123')) {
errors.push('Parameter substitution not reflected in content');
}
}
return checks;
checks.push({
id: 'resources-templates-read',
name: 'ResourcesTemplateRead',
description: 'Read resource from template with parameter substitution',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Resources-Templates',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-templates'
}
],
details: {
uri: content?.uri,
content: content?.text || content?.blob
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'resources-templates-read',
name: 'ResourcesTemplateRead',
description: 'Read resource from template with parameter substitution',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Resources-Templates',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-templates'
}
]
});
}
return checks;
}
}
export class ResourcesSubscribeScenario implements ClientScenario {
name = 'resources-subscribe';
description = 'Test subscribing to resource updates';
name = 'resources-subscribe';
description = 'Test subscribing to resource updates';
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
try {
const connection = await connectToServer(serverUrl);
try {
const connection = await connectToServer(serverUrl);
await connection.client.subscribeResource({
uri: 'test://watched-resource'
});
await connection.client.subscribeResource({
uri: 'test://watched-resource'
});
checks.push({
id: 'resources-subscribe',
name: 'ResourcesSubscribe',
description: 'Subscribe to resource successfully',
status: 'SUCCESS',
timestamp: new Date().toISOString(),
specReferences: [
{
id: 'MCP-Resources-Subscribe',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-subscriptions'
}
]
});
checks.push({
id: 'resources-subscribe',
name: 'ResourcesSubscribe',
description: 'Subscribe to resource successfully',
status: 'SUCCESS',
timestamp: new Date().toISOString(),
specReferences: [
{
id: 'MCP-Resources-Subscribe',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-subscriptions'
}
]
});
await connection.close();
} catch (error) {
checks.push({
id: 'resources-subscribe',
name: 'ResourcesSubscribe',
description: 'Subscribe to resource successfully',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Resources-Subscribe',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-subscriptions'
}
]
});
}
return checks;
await connection.close();
} catch (error) {
checks.push({
id: 'resources-subscribe',
name: 'ResourcesSubscribe',
description: 'Subscribe to resource successfully',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Resources-Subscribe',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-subscriptions'
}
]
});
}
return checks;
}
}
export class ResourcesUnsubscribeScenario implements ClientScenario {
name = 'resources-unsubscribe';
description = 'Test unsubscribing from resource';
name = 'resources-unsubscribe';
description = 'Test unsubscribing from resource';
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
try {
const connection = await connectToServer(serverUrl);
try {
const connection = await connectToServer(serverUrl);
// First subscribe
await connection.client.subscribeResource({
uri: 'test://watched-resource'
});
// First subscribe
await connection.client.subscribeResource({
uri: 'test://watched-resource'
});
// Then unsubscribe
await connection.client.unsubscribeResource({
uri: 'test://watched-resource'
});
// Then unsubscribe
await connection.client.unsubscribeResource({
uri: 'test://watched-resource'
});
checks.push({
id: 'resources-unsubscribe',
name: 'ResourcesUnsubscribe',
description: 'Unsubscribe from resource successfully',
status: 'SUCCESS',
timestamp: new Date().toISOString(),
specReferences: [
{
id: 'MCP-Resources-Subscribe',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-subscriptions'
}
]
});
checks.push({
id: 'resources-unsubscribe',
name: 'ResourcesUnsubscribe',
description: 'Unsubscribe from resource successfully',
status: 'SUCCESS',
timestamp: new Date().toISOString(),
specReferences: [
{
id: 'MCP-Resources-Subscribe',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-subscriptions'
}
]
});
await connection.close();
} catch (error) {
checks.push({
id: 'resources-unsubscribe',
name: 'ResourcesUnsubscribe',
description: 'Unsubscribe from resource successfully',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Resources-Subscribe',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-subscriptions'
}
]
});
}
return checks;
await connection.close();
} catch (error) {
checks.push({
id: 'resources-unsubscribe',
name: 'ResourcesUnsubscribe',
description: 'Unsubscribe from resource successfully',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Resources-Subscribe',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-subscriptions'
}
]
});
}
return checks;
}
}

View file

@ -2,81 +2,86 @@ import { ClientScenario, ConformanceCheck } from '../../types.js';
import { serverChecks } from '../../checks/index.js';
export class ServerInitializeClientScenario implements ClientScenario {
name = 'server-initialize';
description = 'Acts as MCP client to test external server initialization';
name = 'server-initialize';
description = 'Acts as MCP client to test external server initialization';
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
try {
const response = await fetch(serverUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json, text/event-stream'
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'initialize',
params: {
protocolVersion: '2025-06-18',
capabilities: {},
clientInfo: {
name: 'conformance-test-client',
version: '1.0.0'
}
}
})
});
if (!response.ok) {
const responseBody = await response.text();
throw new Error(`HTTP ${response.status}: ${response.statusText}. Response body: ${responseBody}`);
try {
const response = await fetch(serverUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json, text/event-stream'
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'initialize',
params: {
protocolVersion: '2025-06-18',
capabilities: {},
clientInfo: {
name: 'conformance-test-client',
version: '1.0.0'
}
}
})
});
const responseText = await response.text();
if (!response.ok) {
const responseBody = await response.text();
throw new Error(
`HTTP ${response.status}: ${response.statusText}. Response body: ${responseBody}`
);
}
// Handle SSE format
let result;
if (responseText.startsWith('event:') || responseText.includes('\ndata:')) {
// Parse SSE format - extract JSON from data: lines
const lines = responseText.split('\n');
const dataLines = lines.filter(line => line.startsWith('data: '));
if (dataLines.length > 0) {
const jsonData = dataLines[0].substring(6); // Remove 'data: ' prefix
result = JSON.parse(jsonData);
} else {
throw new Error(`SSE response without data line: ${responseText}`);
}
} else {
// Regular JSON response
result = JSON.parse(responseText);
}
const responseText = await response.text();
const check = serverChecks.createServerInitializationCheck(result);
checks.push(check);
} catch (error) {
checks.push({
id: 'server-initialize-request',
name: 'ServerInitializeRequest',
description: 'Tests server response to initialize request',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed to send initialize request: ${error instanceof Error ? error.message : String(error)}`,
details: {
error: error instanceof Error ? error.message : String(error),
serverUrl
},
specReferences: [
{
id: 'MCP-Initialize',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle#initialization'
}
]
});
// Handle SSE format
let result;
if (
responseText.startsWith('event:') ||
responseText.includes('\ndata:')
) {
// Parse SSE format - extract JSON from data: lines
const lines = responseText.split('\n');
const dataLines = lines.filter((line) => line.startsWith('data: '));
if (dataLines.length > 0) {
const jsonData = dataLines[0].substring(6); // Remove 'data: ' prefix
result = JSON.parse(jsonData);
} else {
throw new Error(`SSE response without data line: ${responseText}`);
}
} else {
// Regular JSON response
result = JSON.parse(responseText);
}
return checks;
const check = serverChecks.createServerInitializationCheck(result);
checks.push(check);
} catch (error) {
checks.push({
id: 'server-initialize-request',
name: 'ServerInitializeRequest',
description: 'Tests server response to initialize request',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed to send initialize request: ${error instanceof Error ? error.message : String(error)}`,
details: {
error: error instanceof Error ? error.message : String(error),
serverUrl
},
specReferences: [
{
id: 'MCP-Initialize',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle#initialization'
}
]
});
}
return checks;
}
}

File diff suppressed because it is too large Load diff

View file

@ -6,131 +6,133 @@ import { ClientScenario, ConformanceCheck } from '../../types.js';
import { connectToServer } from './client-helper.js';
export class LoggingSetLevelScenario implements ClientScenario {
name = 'logging-set-level';
description = 'Test setting logging level';
name = 'logging-set-level';
description = 'Test setting logging level';
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
try {
const connection = await connectToServer(serverUrl);
try {
const connection = await connectToServer(serverUrl);
// Send logging/setLevel request
const result = await connection.client.setLoggingLevel('info');
// Send logging/setLevel request
const result = await connection.client.setLoggingLevel('info');
// Validate response (should return empty object {})
const errors: string[] = [];
if (result && Object.keys(result).length > 0) {
errors.push('Expected empty object {} response');
}
// Validate response (should return empty object {})
const errors: string[] = [];
if (result && Object.keys(result).length > 0) {
errors.push('Expected empty object {} response');
}
checks.push({
id: 'logging-set-level',
name: 'LoggingSetLevel',
description: 'Server accepts logging level setting',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Logging',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/logging'
}
],
details: {
result
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'logging-set-level',
name: 'LoggingSetLevel',
description: 'Server accepts logging level setting',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Logging',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/logging'
}
]
});
checks.push({
id: 'logging-set-level',
name: 'LoggingSetLevel',
description: 'Server accepts logging level setting',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Logging',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/logging'
}
],
details: {
result
}
});
return checks;
await connection.close();
} catch (error) {
checks.push({
id: 'logging-set-level',
name: 'LoggingSetLevel',
description: 'Server accepts logging level setting',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Logging',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/logging'
}
]
});
}
return checks;
}
}
export class CompletionCompleteScenario implements ClientScenario {
name = 'completion-complete';
description = 'Test completion endpoint';
name = 'completion-complete';
description = 'Test completion endpoint';
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
try {
const connection = await connectToServer(serverUrl);
try {
const connection = await connectToServer(serverUrl);
// Send completion/complete request
const result = await connection.client.complete({
ref: {
type: 'ref/prompt',
name: 'test_prompt_with_arguments'
},
argument: {
name: 'arg1',
value: 'test'
}
});
// Validate response structure
const errors: string[] = [];
if (!result.completion) {
errors.push('Missing completion field');
} else {
if (!result.completion.values) errors.push('Missing values array in completion');
if (!Array.isArray(result.completion.values)) errors.push('completion.values is not an array');
}
checks.push({
id: 'completion-complete',
name: 'CompletionComplete',
description: 'Server responds to completion requests',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Completion',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/completion'
}
],
details: {
result
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'completion-complete',
name: 'CompletionComplete',
description: 'Server responds to completion requests',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Completion',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/completion'
}
]
});
// Send completion/complete request
const result = await connection.client.complete({
ref: {
type: 'ref/prompt',
name: 'test_prompt_with_arguments'
},
argument: {
name: 'arg1',
value: 'test'
}
});
return checks;
// Validate response structure
const errors: string[] = [];
if (!result.completion) {
errors.push('Missing completion field');
} else {
if (!result.completion.values)
errors.push('Missing values array in completion');
if (!Array.isArray(result.completion.values))
errors.push('completion.values is not an array');
}
checks.push({
id: 'completion-complete',
name: 'CompletionComplete',
description: 'Server responds to completion requests',
status: errors.length === 0 ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
specReferences: [
{
id: 'MCP-Completion',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/completion'
}
],
details: {
result
}
});
await connection.close();
} catch (error) {
checks.push({
id: 'completion-complete',
name: 'CompletionComplete',
description: 'Server responds to completion requests',
status: 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
specReferences: [
{
id: 'MCP-Completion',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/completion'
}
]
});
}
return checks;
}
}