Skip to content

Commit fec2c90

Browse files
committed
first passing test
1 parent 98bf5aa commit fec2c90

File tree

5 files changed

+165
-205
lines changed

5 files changed

+165
-205
lines changed

src/checks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export function createServerInfoCheck(
5050
return {
5151
id: 'server-info',
5252
name: 'ServerInfo',
53-
description: 'Validates server implementation includes required name and version',
53+
description: 'Test server info returned to client',
5454
status,
5555
timestamp: new Date().toISOString(),
5656
specReferences: [

src/scenarios/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { Scenario } from '../types.js';
2+
import { initializeScenario } from './initialize/index.js';
23

3-
export const scenarios = new Map<string, Scenario>();
4+
export const scenarios = new Map<string, Scenario>([
5+
['initialize', initializeScenario],
6+
]);
47

58
export function registerScenario(name: string, scenario: Scenario): void {
69
scenarios.set(name, scenario);

src/scenarios/initialize/TODO.md

Lines changed: 0 additions & 203 deletions
This file was deleted.

src/scenarios/initialize/index.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Scenario, ScenarioUrls, ConformanceCheck } from '../../types.js';
2+
import { InitializeTestServer } from './server.js';
3+
4+
export class InitializeScenario implements Scenario {
5+
name = 'initialize';
6+
description = 'Tests MCP client initialization handshake';
7+
8+
private server: InitializeTestServer | null = null;
9+
10+
async start(): Promise<ScenarioUrls> {
11+
this.server = new InitializeTestServer();
12+
const port = await this.server.start();
13+
return {
14+
serverUrl: `http://localhost:${port}`
15+
};
16+
}
17+
18+
async stop(): Promise<void> {
19+
if (this.server) {
20+
await this.server.stop();
21+
this.server = null;
22+
}
23+
}
24+
25+
getChecks(): ConformanceCheck[] {
26+
if (!this.server) {
27+
return [];
28+
}
29+
return this.server.getChecks();
30+
}
31+
}
32+
33+
export const initializeScenario = new InitializeScenario();

src/scenarios/initialize/server.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import http from 'http';
2+
import { ConformanceCheck } from '../../types.js';
3+
import { createClientInitializationCheck, createServerInfoCheck } from '../../checks.js';
4+
5+
export class InitializeTestServer {
6+
private server: http.Server | null = null;
7+
private checks: ConformanceCheck[] = [];
8+
private port: number = 0;
9+
10+
async start(): Promise<number> {
11+
return new Promise((resolve, reject) => {
12+
this.server = http.createServer((req, res) => {
13+
this.handleRequest(req, res);
14+
});
15+
16+
this.server.on('error', reject);
17+
18+
this.server.listen(0, () => {
19+
const address = this.server!.address();
20+
if (address && typeof address === 'object') {
21+
this.port = address.port;
22+
resolve(this.port);
23+
} else {
24+
reject(new Error('Failed to get server address'));
25+
}
26+
});
27+
});
28+
}
29+
30+
async stop(): Promise<void> {
31+
return new Promise((resolve, reject) => {
32+
if (this.server) {
33+
this.server.close((err) => {
34+
if (err) {
35+
reject(err);
36+
} else {
37+
this.server = null;
38+
resolve();
39+
}
40+
});
41+
} else {
42+
resolve();
43+
}
44+
});
45+
}
46+
47+
getChecks(): ConformanceCheck[] {
48+
return this.checks;
49+
}
50+
51+
private handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void {
52+
let body = '';
53+
54+
req.on('data', (chunk) => {
55+
body += chunk.toString();
56+
});
57+
58+
req.on('end', () => {
59+
try {
60+
const request = JSON.parse(body);
61+
62+
if (request.method === 'initialize') {
63+
this.handleInitialize(request, res);
64+
} else if (request.method === 'tools/list') {
65+
this.handleToolsList(request, res);
66+
} else {
67+
res.writeHead(200, { 'Content-Type': 'application/json' });
68+
res.end(JSON.stringify({
69+
jsonrpc: '2.0',
70+
id: request.id,
71+
result: {}
72+
}));
73+
}
74+
} catch (error) {
75+
res.writeHead(400, { 'Content-Type': 'application/json' });
76+
res.end(JSON.stringify({
77+
jsonrpc: '2.0',
78+
error: {
79+
code: -32700,
80+
message: 'Parse error'
81+
}
82+
}));
83+
}
84+
});
85+
}
86+
87+
private handleInitialize(request: any, res: http.ServerResponse): void {
88+
const initializeRequest = request.params;
89+
90+
const check = createClientInitializationCheck(initializeRequest);
91+
this.checks.push(check);
92+
93+
const serverInfo = {
94+
name: 'test-server',
95+
version: '1.0.0'
96+
};
97+
98+
const serverInfoCheck = createServerInfoCheck(serverInfo);
99+
this.checks.push(serverInfoCheck);
100+
101+
const response = {
102+
jsonrpc: '2.0',
103+
id: request.id,
104+
result: {
105+
protocolVersion: '2025-06-18',
106+
serverInfo,
107+
capabilities: {}
108+
}
109+
};
110+
111+
res.writeHead(200, { 'Content-Type': 'application/json' });
112+
res.end(JSON.stringify(response));
113+
}
114+
115+
private handleToolsList(request: any, res: http.ServerResponse): void {
116+
const response = {
117+
jsonrpc: '2.0',
118+
id: request.id,
119+
result: {
120+
tools: []
121+
}
122+
};
123+
124+
res.writeHead(200, { 'Content-Type': 'application/json' });
125+
res.end(JSON.stringify(response));
126+
}
127+
}

0 commit comments

Comments
 (0)