mcp-server-tester-sse-http-stdio
Version:
MCP Server Tester with SSE support - Test MCP servers using HTTP, SSE, and STDIO transports
203 lines (202 loc) • 9.02 kB
JavaScript
/**
* Server metadata tests for MCP server lifecycle
*/
import { DiagnosticTest } from '../DiagnosticTest.js';
import { TEST_SEVERITY } from '../types.js';
export class ServerMetadataTests extends DiagnosticTest {
name = 'Lifecycle: Server Info & Metadata';
description = 'Tests MCP server metadata validation and compliance';
category = 'lifecycle';
feature = 'initialization';
severity = TEST_SEVERITY.INFO;
async execute(client, _config) {
try {
const results = await Promise.allSettled([
this.testServerInfo(client),
this.testImplementationDetails(client),
this.testInstructionsField(client),
this.testMetadataFormat(client),
]);
const issues = [];
const warnings = [];
const details = {};
// Process test results
results.forEach((result, index) => {
const testNames = [
'ServerInfo',
'ImplementationDetails',
'InstructionsField',
'MetadataFormat',
];
if (result.status === 'rejected') {
const error = String(result.reason);
if (error.includes('instructions') || error.includes('title')) {
warnings.push(`${testNames[index]}: ${error}`);
}
else {
issues.push(`${testNames[index]}: ${error}`);
}
}
else {
details[testNames[index]] = result.value;
}
});
if (issues.length > 0) {
return this.createResult(false, `Server metadata has ${issues.length} issue(s) and ${warnings.length} warning(s)`, { issues, warnings, details }, [
'Ensure server provides valid name and version in server info',
'Verify implementation details follow MCP specification format',
'Consider adding server title and instructions for better user experience',
]);
}
if (warnings.length > 0) {
return this.createResult(true, `Server metadata valid with ${warnings.length} recommendation(s)`, { warnings, details }, [
'Consider adding instructions field to help users understand server capabilities',
'Add title field for better server identification',
]);
}
return this.createResult(true, 'Server metadata is complete and compliant', details);
}
catch (error) {
return this.createResult(false, 'Server metadata test failed', { error: error instanceof Error ? error.message : String(error) }, ['Check server metadata implementation and MCP protocol compliance']);
}
}
async testServerInfo(client) {
try {
const serverVersion = client.sdk.getServerVersion();
if (!serverVersion || typeof serverVersion !== 'object') {
throw new Error('Server did not provide version information');
}
const serverInfo = serverVersion;
// Validate required fields
if (!serverInfo.name || typeof serverInfo.name !== 'string') {
throw new Error('Server name is missing or invalid');
}
if (!serverInfo.version || typeof serverInfo.version !== 'string') {
throw new Error('Server version is missing or invalid');
}
// Check for semantic versioning pattern
const semverPattern = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/;
const isValidSemver = semverPattern.test(serverInfo.version);
return {
status: 'passed',
message: `Server info valid (name: "${serverInfo.name}", version: "${serverInfo.version}")`,
serverInfo: {
name: serverInfo.name,
version: serverInfo.version,
title: serverInfo.title,
hasTitle: !!serverInfo.title,
isValidSemver,
},
};
}
catch (error) {
throw new Error(`Server info validation failed: ${error}`);
}
}
async testImplementationDetails(client) {
try {
const serverVersion = client.sdk.getServerVersion();
const implementationDetails = serverVersion;
// Validate implementation details structure
const validation = {
hasName: !!implementationDetails.name,
hasVersion: !!implementationDetails.version,
nameType: typeof implementationDetails.name,
versionType: typeof implementationDetails.version,
};
if (!validation.hasName || validation.nameType !== 'string') {
throw new Error('Implementation name is missing or not a string');
}
if (!validation.hasVersion || validation.versionType !== 'string') {
throw new Error('Implementation version is missing or not a string');
}
return {
status: 'passed',
message: 'Implementation details are valid',
validation,
implementation: {
name: implementationDetails.name,
version: implementationDetails.version,
},
};
}
catch (error) {
throw new Error(`Implementation details validation failed: ${error}`);
}
}
async testInstructionsField(client) {
try {
const instructions = client.sdk.getInstructions();
if (instructions === null || instructions === undefined) {
throw new Error('Server does not provide instructions field');
}
if (typeof instructions === 'string') {
return {
status: 'passed',
message: 'Server provides instructions field',
instructions: {
present: true,
type: 'string',
length: instructions.length,
content: instructions.slice(0, 100) + (instructions.length > 100 ? '...' : ''),
},
};
}
else {
return {
status: 'passed',
message: 'Server provides instructions field (non-string format)',
instructions: {
present: true,
type: typeof instructions,
content: instructions,
},
};
}
}
catch (error) {
throw new Error(`Instructions field not available or accessible: ${error}`);
}
}
async testMetadataFormat(client) {
try {
const serverVersion = client.sdk.getServerVersion();
const capabilities = client.sdk.getServerCapabilities();
// Validate overall metadata format compliance
const formatValidation = {
serverVersionPresent: !!serverVersion,
capabilitiesPresent: !!capabilities,
serverVersionIsObject: typeof serverVersion === 'object',
capabilitiesIsObject: typeof capabilities === 'object',
};
// Check for any extra/unknown fields that might indicate format issues
const serverVersionObj = serverVersion;
const knownServerFields = ['name', 'version', 'title'];
const unknownServerFields = Object.keys(serverVersionObj).filter(key => !knownServerFields.includes(key));
if (!formatValidation.serverVersionPresent) {
throw new Error('Server version metadata missing');
}
if (!formatValidation.capabilitiesPresent) {
throw new Error('Server capabilities metadata missing');
}
if (!formatValidation.serverVersionIsObject) {
throw new Error('Server version metadata is not an object');
}
if (!formatValidation.capabilitiesIsObject) {
throw new Error('Server capabilities metadata is not an object');
}
return {
status: 'passed',
message: 'Metadata format compliance verified',
formatValidation,
analysis: {
unknownServerFields,
hasUnknownFields: unknownServerFields.length > 0,
},
};
}
catch (error) {
throw new Error(`Metadata format compliance test failed: ${error}`);
}
}
}