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
33 changed files with 12352 additions and 12153 deletions

View file

@ -1,14 +1,6 @@
{
"printWidth": 140,
"tabWidth": 4,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": true,
"bracketSameLine": false,
"proseWrap": "always",
"arrowParens": "avoid",
"overrides": [
{
"files": "**/*.md",

View file

@ -12,7 +12,10 @@ export default tseslint.config(
reportUnusedDisableDirectives: false
},
rules: {
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_' }
],
'@typescript-eslint/no-explicit-any': 'off'
}
},

View file

@ -51,7 +51,7 @@ async function main(): Promise<void> {
}
}
main().catch(error => {
main().catch((error) => {
console.error('Unhandled error:', error);
process.exit(1);
});

View file

@ -42,7 +42,7 @@ async function main(): Promise<void> {
}
}
main().catch(error => {
main().catch((error) => {
console.error('Unhandled error:', error);
process.exit(1);
});

View file

@ -45,7 +45,7 @@ async function main(): Promise<void> {
}
}
main().catch(error => {
main().catch((error) => {
console.error('Unhandled error:', error);
process.exit(1);
});

View file

@ -8,7 +8,10 @@
* we use tool() instead of registerTool() as there is a bug with logging in registerTool().
*/
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
import {
McpServer,
ResourceTemplate
} from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { z } from 'zod';
import express from 'express';
@ -24,10 +27,12 @@ const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
const servers: { [sessionId: string]: McpServer } = {};
// Sample base64 encoded 1x1 red PNG pixel for testing
const TEST_IMAGE_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==';
const TEST_IMAGE_BASE64 =
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==';
// Sample base64 encoded minimal WAV file for testing
const TEST_AUDIO_BASE64 = 'UklGRiYAAABXQVZFZm10IBAAAAABAAEAQB8AAAB9AAACABAAZGF0YQIAAAA=';
const TEST_AUDIO_BASE64 =
'UklGRiYAAABXQVZFZm10IBAAAAABAAEAQB8AAAB9AAACABAAZGF0YQIAAAA=';
// Function to create a new MCP server instance (one per session)
function createMcpServer() {
@ -56,7 +61,15 @@ function createMcpServer() {
// Helper to send log messages using the underlying server
function sendLog(
level: 'debug' | 'info' | 'notice' | 'warning' | 'error' | 'critical' | 'alert' | 'emergency',
level:
| 'debug'
| 'info'
| 'notice'
| 'warning'
| 'error'
| 'critical'
| 'alert'
| 'emergency',
message: string,
data?: any
) {
@ -77,11 +90,18 @@ function createMcpServer() {
// ===== TOOLS =====
// Simple text tool
mcpServer.tool('test_simple_text', 'Tests simple text content response', {}, async () => {
mcpServer.tool(
'test_simple_text',
'Tests simple text content response',
{},
async () => {
return {
content: [{ type: 'text', text: 'This is a simple text response for testing.' }]
content: [
{ type: 'text', text: 'This is a simple text response for testing.' }
]
};
});
}
);
// Image content tool
mcpServer.registerTool(
@ -91,7 +111,9 @@ function createMcpServer() {
},
async () => {
return {
content: [{ type: 'image', data: TEST_IMAGE_BASE64, mimeType: 'image/png' }]
content: [
{ type: 'image', data: TEST_IMAGE_BASE64, mimeType: 'image/png' }
]
};
}
);
@ -104,7 +126,9 @@ function createMcpServer() {
},
async () => {
return {
content: [{ type: 'audio', data: TEST_AUDIO_BASE64, mimeType: 'audio/wav' }]
content: [
{ type: 'audio', data: TEST_AUDIO_BASE64, mimeType: 'audio/wav' }
]
};
}
);
@ -135,7 +159,8 @@ function createMcpServer() {
mcpServer.registerTool(
'test_multiple_content_types',
{
description: 'Tests response with multiple content types (text, image, resource)'
description:
'Tests response with multiple content types (text, image, resource)'
},
async () => {
return {
@ -170,7 +195,7 @@ function createMcpServer() {
data: 'Tool execution started'
}
});
await new Promise(resolve => setTimeout(resolve, 50));
await new Promise((resolve) => setTimeout(resolve, 50));
await sendNotification({
method: 'notifications/message',
@ -179,7 +204,7 @@ function createMcpServer() {
data: 'Tool processing data'
}
});
await new Promise(resolve => setTimeout(resolve, 50));
await new Promise((resolve) => setTimeout(resolve, 50));
await sendNotification({
method: 'notifications/message',
@ -189,7 +214,9 @@ function createMcpServer() {
}
});
return {
content: [{ type: 'text', text: 'Tool with logging executed successfully' }]
content: [
{ type: 'text', text: 'Tool with logging executed successfully' }
]
};
}
);
@ -213,7 +240,7 @@ function createMcpServer() {
message: `Completed step ${0} of ${100}`
}
});
await new Promise(resolve => setTimeout(resolve, 50));
await new Promise((resolve) => setTimeout(resolve, 50));
await sendNotification({
method: 'notifications/progress',
@ -224,7 +251,7 @@ function createMcpServer() {
message: `Completed step ${50} of ${100}`
}
});
await new Promise(resolve => setTimeout(resolve, 50));
await new Promise((resolve) => setTimeout(resolve, 50));
await sendNotification({
method: 'notifications/progress',
@ -281,11 +308,16 @@ function createMcpServer() {
maxTokens: 100
}
},
z.object({ method: z.literal('sampling/createMessage') }).passthrough() as any
z
.object({ method: z.literal('sampling/createMessage') })
.passthrough() as any
);
const samplingResult = result as any;
const modelResponse = samplingResult.content?.text || samplingResult.message?.content?.text || 'No response';
const modelResponse =
samplingResult.content?.text ||
samplingResult.message?.content?.text ||
'No response';
return {
content: [
@ -337,7 +369,9 @@ function createMcpServer() {
}
}
},
z.object({ method: z.literal('elicitation/create') }).passthrough() as any
z
.object({ method: z.literal('elicitation/create') })
.passthrough() as any
);
const elicitResult = result as any;
@ -462,19 +496,25 @@ function createMcpServer() {
);
// Subscribe/Unsubscribe handlers
mcpServer.server.setRequestHandler(z.object({ method: z.literal('resources/subscribe') }).passthrough(), async (request: any) => {
mcpServer.server.setRequestHandler(
z.object({ method: z.literal('resources/subscribe') }).passthrough(),
async (request: any) => {
const uri = request.params.uri;
resourceSubscriptions.add(uri);
sendLog('info', `Subscribed to resource: ${uri}`);
return {};
});
}
);
mcpServer.server.setRequestHandler(z.object({ method: z.literal('resources/unsubscribe') }).passthrough(), async (request: any) => {
mcpServer.server.setRequestHandler(
z.object({ method: z.literal('resources/unsubscribe') }).passthrough(),
async (request: any) => {
const uri = request.params.uri;
resourceSubscriptions.delete(uri);
sendLog('info', `Unsubscribed from resource: ${uri}`);
return {};
});
}
);
// ===== PROMPTS =====
@ -490,7 +530,10 @@ function createMcpServer() {
messages: [
{
role: 'user',
content: { type: 'text', text: 'This is a simple prompt for testing.' }
content: {
type: 'text',
text: 'This is a simple prompt for testing.'
}
}
]
};
@ -508,7 +551,7 @@ function createMcpServer() {
arg2: z.string().describe('Second test argument')
}
},
async args => {
async (args) => {
const { arg1, arg2 } = args;
return {
messages: [
@ -534,7 +577,7 @@ function createMcpServer() {
resourceUri: z.string().describe('URI of the resource to embed')
}
},
async args => {
async (args) => {
const uri = args.resourceUri;
return {
messages: [
@ -551,7 +594,10 @@ function createMcpServer() {
},
{
role: 'user',
content: { type: 'text', text: 'Please process the embedded resource above.' }
content: {
type: 'text',
text: 'Please process the embedded resource above.'
}
}
]
};
@ -570,7 +616,11 @@ function createMcpServer() {
messages: [
{
role: 'user',
content: { type: 'image', data: TEST_IMAGE_BASE64, mimeType: 'image/png' }
content: {
type: 'image',
data: TEST_IMAGE_BASE64,
mimeType: 'image/png'
}
},
{
role: 'user',
@ -583,15 +633,20 @@ function createMcpServer() {
// ===== LOGGING =====
mcpServer.server.setRequestHandler(z.object({ method: z.literal('logging/setLevel') }).passthrough(), async (request: any) => {
mcpServer.server.setRequestHandler(
z.object({ method: z.literal('logging/setLevel') }).passthrough(),
async (request: any) => {
const level = request.params.level;
sendLog('info', `Log level set to: ${level}`);
return {};
});
}
);
// ===== COMPLETION =====
mcpServer.server.setRequestHandler(z.object({ method: z.literal('completion/complete') }).passthrough(), async (_request: any) => {
mcpServer.server.setRequestHandler(
z.object({ method: z.literal('completion/complete') }).passthrough(),
async (_request: any) => {
// Basic completion support - returns empty array for conformance
// Real implementations would provide contextual suggestions
return {
@ -601,7 +656,8 @@ function createMcpServer() {
hasMore: false
}
};
});
}
);
return mcpServer;
}
@ -641,7 +697,7 @@ app.post('/mcp', async (req, res) => {
transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
onsessioninitialized: newSessionId => {
onsessioninitialized: (newSessionId) => {
transports[newSessionId] = transport;
servers[newSessionId] = mcpServer;
console.log(`Session initialized with ID: ${newSessionId}`);
@ -743,6 +799,8 @@ app.delete('/mcp', async (req, res) => {
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`MCP Conformance Test Server running on http://localhost:${PORT}`);
console.log(
`MCP Conformance Test Server running on http://localhost:${PORT}`
);
console.log(` - MCP endpoint: http://localhost:${PORT}/mcp`);
});

1
package-lock.json generated
View file

@ -59,6 +59,7 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",

View file

@ -1,13 +1,21 @@
import { ConformanceCheck, CheckStatus } from '../types.js';
export function createServerInfoCheck(serverInfo: { name: string; version: string }): ConformanceCheck {
export function createServerInfoCheck(serverInfo: {
name: string;
version: string;
}): ConformanceCheck {
return {
id: 'server-info',
name: 'ServerInfo',
description: 'Test server info returned to client',
status: 'INFO',
timestamp: new Date().toISOString(),
specReferences: [{ id: 'MCP-Lifecycle', url: 'https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle' }],
specReferences: [
{
id: 'MCP-Lifecycle',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle'
}
],
details: {
serverName: serverInfo.name,
serverVersion: serverInfo.version
@ -15,15 +23,22 @@ export function createServerInfoCheck(serverInfo: { name: string; version: strin
};
}
export function createClientInitializationCheck(initializeRequest: any, expectedSpecVersion: string = '2025-06-18'): ConformanceCheck {
export function createClientInitializationCheck(
initializeRequest: any,
expectedSpecVersion: string = '2025-06-18'
): ConformanceCheck {
const protocolVersionSent = initializeRequest?.protocolVersion;
const versionMatch = protocolVersionSent === expectedSpecVersion;
const errors: string[] = [];
if (!protocolVersionSent) errors.push('Protocol version not provided');
if (!versionMatch) errors.push(`Version mismatch: expected ${expectedSpecVersion}, got ${protocolVersionSent}`);
if (!versionMatch)
errors.push(
`Version mismatch: expected ${expectedSpecVersion}, got ${protocolVersionSent}`
);
if (!initializeRequest?.clientInfo?.name) errors.push('Client name missing');
if (!initializeRequest?.clientInfo?.version) errors.push('Client version missing');
if (!initializeRequest?.clientInfo?.version)
errors.push('Client version missing');
const status: CheckStatus = errors.length === 0 ? 'SUCCESS' : 'FAILURE';
@ -33,7 +48,12 @@ export function createClientInitializationCheck(initializeRequest: any, expected
description: 'Validates that MCP client properly initializes with server',
status,
timestamp: new Date().toISOString(),
specReferences: [{ id: 'MCP-Lifecycle', url: 'https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle' }],
specReferences: [
{
id: 'MCP-Lifecycle',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle'
}
],
details: {
protocolVersionSent,
expectedSpecVersion,

View file

@ -22,7 +22,11 @@ function createResultDir(scenario: string): string {
return path.join('results', `${scenario}-${timestamp}`);
}
async function executeClient(command: string, serverUrl: string, timeout: number = 30000): Promise<ClientExecutionResult> {
async function executeClient(
command: string,
serverUrl: string,
timeout: number = 30000
): Promise<ClientExecutionResult> {
const commandParts = command.split(' ');
const executable = commandParts[0];
const args = [...commandParts.slice(1), serverUrl];
@ -31,7 +35,7 @@ async function executeClient(command: string, serverUrl: string, timeout: number
let stderr = '';
let timedOut = false;
return new Promise(resolve => {
return new Promise((resolve) => {
const process = spawn(executable, args, {
shell: true,
stdio: 'pipe'
@ -43,18 +47,18 @@ async function executeClient(command: string, serverUrl: string, timeout: number
}, timeout);
if (process.stdout) {
process.stdout.on('data', data => {
process.stdout.on('data', (data) => {
stdout += data.toString();
});
}
if (process.stderr) {
process.stderr.on('data', data => {
process.stderr.on('data', (data) => {
stderr += data.toString();
});
}
process.on('close', code => {
process.on('close', (code) => {
clearTimeout(timeoutHandle);
resolve({
exitCode: code || 0,
@ -64,7 +68,7 @@ async function executeClient(command: string, serverUrl: string, timeout: number
});
});
process.on('error', error => {
process.on('error', (error) => {
clearTimeout(timeoutHandle);
resolve({
exitCode: -1,
@ -100,11 +104,18 @@ export async function runConformanceTest(
console.log(`Executing client: ${clientCommand} ${urls.serverUrl}`);
try {
const clientOutput = await executeClient(clientCommand, urls.serverUrl, timeout);
const clientOutput = await executeClient(
clientCommand,
urls.serverUrl,
timeout
);
const checks = scenario.getChecks();
await fs.writeFile(path.join(resultDir, 'checks.json'), JSON.stringify(checks, null, 2));
await fs.writeFile(
path.join(resultDir, 'checks.json'),
JSON.stringify(checks, null, 2)
);
await fs.writeFile(path.join(resultDir, 'stdout.txt'), clientOutput.stdout);
@ -142,7 +153,10 @@ async function runInteractiveMode(scenarioName: string): Promise<void> {
console.log('\nShutting down...');
const checks = scenario.getChecks();
await fs.writeFile(path.join(resultDir, 'checks.json'), JSON.stringify(checks, null, 2));
await fs.writeFile(
path.join(resultDir, 'checks.json'),
JSON.stringify(checks, null, 2)
);
console.log(`\nChecks:\n${JSON.stringify(checks, null, 2)}`);
console.log(`\nChecks saved to ${resultDir}/checks.json`);
@ -173,8 +187,12 @@ async function main(): Promise<void> {
}
if (!scenario) {
console.error('Usage: runner --scenario <scenario> [--command "<command>"]');
console.error('Example: runner --scenario initialize --command "tsx examples/clients/typescript/test1.ts"');
console.error(
'Usage: runner --scenario <scenario> [--command "<command>"]'
);
console.error(
'Example: runner --scenario initialize --command "tsx examples/clients/typescript/test1.ts"'
);
console.error('Or run without --command for interactive mode');
process.exit(1);
}
@ -192,9 +210,11 @@ async function main(): Promise<void> {
try {
const result = await runConformanceTest(command, scenario);
const denominator = result.checks.filter(c => c.status === 'SUCCESS' || c.status == 'FAILURE').length;
const passed = result.checks.filter(c => c.status === 'SUCCESS').length;
const failed = result.checks.filter(c => c.status === 'FAILURE').length;
const denominator = result.checks.filter(
(c) => c.status === 'SUCCESS' || c.status == 'FAILURE'
).length;
const passed = result.checks.filter((c) => c.status === 'SUCCESS').length;
const failed = result.checks.filter((c) => c.status === 'FAILURE').length;
console.log(`Checks:\n${JSON.stringify(result.checks, null, 2)}`);
@ -204,8 +224,8 @@ async function main(): Promise<void> {
if (failed > 0) {
console.log('\nFailed Checks:');
result.checks
.filter(c => c.status === 'FAILURE')
.forEach(c => {
.filter((c) => c.status === 'FAILURE')
.forEach((c) => {
console.log(` - ${c.name}: ${c.description}`);
if (c.errorMessage) {
console.log(` Error: ${c.errorMessage}`);

Some files were not shown because too many files have changed in this diff Show more