Skip to content

Commit 22fe82c

Browse files
committed
fixup trace logic to be shareable
1 parent 293443d commit 22fe82c

File tree

2 files changed

+109
-162
lines changed

2 files changed

+109
-162
lines changed

auth-compat/src/cli/index.ts

Lines changed: 19 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import { Command } from 'commander';
44
import { spawn } from 'child_process';
55
import { ValidationServer } from '../server/validation/index.js';
6-
import { ComplianceReport } from '../types.js';
6+
import { ComplianceReport, HttpTrace } from '../types.js';
7+
import { displayTraces } from '../middleware/http-trace.js';
78

89
const program = new Command();
910

@@ -82,23 +83,21 @@ async function runSingleTest(
8283

8384
// Run the client
8485
const clientProcess = spawn(executable, args, {
85-
stdio: verbose ? 'inherit' : 'pipe',
86+
stdio: 'pipe',
8687
shell: true,
8788
timeout
8889
});
8990

90-
// Capture stdout/stderr when not in verbose mode
91-
if (!verbose) {
92-
if (clientProcess.stdout) {
93-
clientProcess.stdout.on('data', (data) => {
94-
clientStdout += data.toString();
95-
});
96-
}
97-
if (clientProcess.stderr) {
98-
clientProcess.stderr.on('data', (data) => {
99-
clientStderr += data.toString();
100-
});
101-
}
91+
// Capture stdout/stderr
92+
if (clientProcess.stdout) {
93+
clientProcess.stdout.on('data', (data) => {
94+
clientStdout += data.toString();
95+
});
96+
}
97+
if (clientProcess.stderr) {
98+
clientProcess.stderr.on('data', (data) => {
99+
clientStderr += data.toString();
100+
});
102101
}
103102

104103
// Wait for client to finish
@@ -146,8 +145,8 @@ async function runSingleTest(
146145
if (options.json) {
147146
console.log(JSON.stringify(report, null, 2));
148147
} else {
149-
const clientOutput = verbose ? { stdout: clientStdout, stderr: clientStderr } : null;
150-
printCompactReport(report, verbose ? behavior : null, verbose ? authServerTrace : null, clientOutput);
148+
const clientOutput = { stdout: clientStdout, stderr: clientStderr };
149+
printCompactReport(report, verbose ? behavior : null, verbose ? authServerTrace : undefined, clientOutput);
151150
}
152151

153152
// Stop server
@@ -164,60 +163,7 @@ async function runSingleTest(
164163
}
165164
}
166165

167-
function printHttpTrace(traces: any[], label: string) {
168-
console.log(`\n ====== ${label} ======`);
169-
traces.forEach((trace: any, index: number) => {
170-
console.log(`\n --- Request #${index + 1} ---`);
171-
172-
// Request line
173-
console.log(` ${trace.method} ${trace.url} HTTP/1.1`);
174-
175-
// Request headers
176-
if (trace.headers) {
177-
Object.entries(trace.headers).forEach(([key, value]) => {
178-
console.log(` ${key}: ${value}`);
179-
});
180-
}
181-
182-
// Request body
183-
if (trace.body) {
184-
console.log('');
185-
const bodyStr = typeof trace.body === 'string' ? trace.body : JSON.stringify(trace.body);
186-
console.log(` ${bodyStr}`);
187-
}
188-
189-
// Response
190-
if (trace.response) {
191-
console.log(`\n HTTP/1.1 ${trace.response.status} ${getStatusText(trace.response.status)}`);
192-
193-
// Response headers
194-
if (trace.response.headers) {
195-
Object.entries(trace.response.headers).forEach(([key, value]) => {
196-
console.log(` ${key}: ${value}`);
197-
});
198-
}
199-
200-
// Response body
201-
if (trace.response.body) {
202-
console.log('');
203-
const bodyStr = typeof trace.response.body === 'string'
204-
? trace.response.body
205-
: JSON.stringify(trace.response.body);
206-
207-
// Truncate very long responses
208-
if (bodyStr.length > 1000) {
209-
console.log(` ${bodyStr.substring(0, 1000)}... [truncated]`);
210-
} else {
211-
console.log(` ${bodyStr}`);
212-
}
213-
}
214-
}
215-
console.log('');
216-
});
217-
console.log(' ========================\n');
218-
}
219-
220-
function printCompactReport(report: ComplianceReport, behavior?: any, authServerTrace?: any[], clientOutput?: { stdout: string, stderr: string }) {
166+
function printCompactReport(report: ComplianceReport, behavior?: any, authServerTrace?: HttpTrace[], clientOutput?: { stdout: string, stderr: string }) {
221167
const passed = report.overall_result === 'PASS';
222168
const icon = passed ? '✅' : '❌';
223169

@@ -239,80 +185,7 @@ function printCompactReport(report: ComplianceReport, behavior?: any, authServer
239185

240186
// Show HTTP trace and detailed behavior in verbose mode
241187
if (behavior) {
242-
// Collect all traces and interleave them by timestamp
243-
const allTraces: any[] = [];
244-
245-
// Add validation server traces with source label
246-
if (behavior.httpTrace && behavior.httpTrace.length > 0) {
247-
behavior.httpTrace.forEach((trace: any) => {
248-
allTraces.push({ ...trace, source: 'VALIDATION' });
249-
});
250-
}
251-
252-
// Add auth server traces with source label
253-
if (authServerTrace && authServerTrace.length > 0) {
254-
authServerTrace.forEach((trace: any) => {
255-
allTraces.push({ ...trace, source: 'AUTH' });
256-
});
257-
}
258-
259-
// Sort all traces by timestamp for interleaved view
260-
if (allTraces.length > 0) {
261-
allTraces.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
262-
263-
// Print interleaved traces
264-
console.log('\n ====== INTERLEAVED HTTP TRACE ======');
265-
allTraces.forEach((trace: any, index: number) => {
266-
console.log(`\n --- [${trace.source}] Request #${index + 1} ---`);
267-
console.log(` Timestamp: ${trace.timestamp}`);
268-
269-
// Request line
270-
console.log(` ${trace.method} ${trace.url} HTTP/1.1`);
271-
272-
// Request headers
273-
if (trace.headers) {
274-
Object.entries(trace.headers).forEach(([key, value]) => {
275-
console.log(` ${key}: ${value}`);
276-
});
277-
}
278-
279-
// Request body
280-
if (trace.body) {
281-
console.log('');
282-
const bodyStr = typeof trace.body === 'string' ? trace.body : JSON.stringify(trace.body);
283-
console.log(` ${bodyStr}`);
284-
}
285-
286-
// Response
287-
if (trace.response) {
288-
console.log(`\n HTTP/1.1 ${trace.response.status} ${getStatusText(trace.response.status)}`);
289-
290-
// Response headers
291-
if (trace.response.headers) {
292-
Object.entries(trace.response.headers).forEach(([key, value]) => {
293-
console.log(` ${key}: ${value}`);
294-
});
295-
}
296-
297-
// Response body
298-
if (trace.response.body) {
299-
console.log('');
300-
const bodyStr = typeof trace.response.body === 'string'
301-
? trace.response.body
302-
: JSON.stringify(trace.response.body);
303-
304-
// Truncate very long responses
305-
if (bodyStr.length > 1000) {
306-
console.log(` ${bodyStr.substring(0, 1000)}... [truncated]`);
307-
} else {
308-
console.log(` ${bodyStr}`);
309-
}
310-
}
311-
}
312-
console.log('');
313-
});
314-
console.log(' ========================\n');
315-
}
188+
displayTraces(behavior.httpTrace, authServerTrace || [])
316189

317190
// Show other behavior details
318191
console.log(' Client Behavior Summary:');
@@ -329,7 +202,7 @@ function printCompactReport(report: ComplianceReport, behavior?: any, authServer
329202
console.log(' ' + clientOutput.stdout.split('\n').join('\n '));
330203
console.log(' ========================\n');
331204
}
332-
205+
333206
if (clientOutput.stderr) {
334207
console.log('\n ====== CLIENT STDERR ======');
335208
console.log(' ' + clientOutput.stderr.split('\n').join('\n '));
@@ -338,20 +211,6 @@ function printCompactReport(report: ComplianceReport, behavior?: any, authServer
338211
}
339212
}
340213

341-
function getStatusText(status: number): string {
342-
const statusTexts: Record<number, string> = {
343-
200: 'OK',
344-
201: 'Created',
345-
202: 'Accepted',
346-
302: 'Found',
347-
400: 'Bad Request',
348-
401: 'Unauthorized',
349-
403: 'Forbidden',
350-
404: 'Not Found',
351-
405: 'Method Not Allowed',
352-
500: 'Internal Server Error'
353-
};
354-
return statusTexts[status] || '';
355-
}
214+
356215

357216
program.parse();

auth-compat/src/middleware/http-trace.ts

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export function createHttpTraceMiddleware(collector: HttpTraceCollector) {
3535
}
3636

3737
const body = Buffer.concat(chunks).toString('utf8');
38-
38+
3939
// Capture response details
4040
trace.response = {
4141
status: res.statusCode,
@@ -50,4 +50,92 @@ export function createHttpTraceMiddleware(collector: HttpTraceCollector) {
5050

5151
next();
5252
};
53-
}
53+
}
54+
55+
export function displayTraces(serverTrace: HttpTrace[], authServerTrace: HttpTrace[]) {
56+
// Collect all traces and interleave them by timestamp
57+
const allTraces: any[] = [];
58+
59+
serverTrace.forEach((trace: any) => {
60+
allTraces.push({ ...trace, source: 'MCP SERVER' });
61+
});
62+
63+
authServerTrace.forEach((trace: any) => {
64+
allTraces.push({ ...trace, source: 'AUTH' });
65+
});
66+
67+
// Sort all traces by timestamp for interleaved view
68+
if (allTraces.length > 0) {
69+
allTraces.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
70+
71+
// Print interleaved traces
72+
console.log('\n ====== INTERLEAVED HTTP TRACE ======');
73+
allTraces.forEach((trace: any, index: number) => {
74+
console.log(`\n --- [${trace.source}] Request #${index + 1} ---`);
75+
console.log(` Timestamp: ${trace.timestamp}`);
76+
77+
// Request line
78+
console.log(` ${trace.method} ${trace.url} HTTP/1.1`);
79+
80+
// Request headers
81+
if (trace.headers) {
82+
Object.entries(trace.headers).forEach(([key, value]) => {
83+
console.log(` ${key}: ${value}`);
84+
});
85+
}
86+
87+
// Request body
88+
if (trace.body) {
89+
console.log('');
90+
const bodyStr = typeof trace.body === 'string' ? trace.body : JSON.stringify(trace.body);
91+
console.log(` ${bodyStr}`);
92+
}
93+
94+
// Response
95+
if (trace.response) {
96+
console.log(`\n HTTP/1.1 ${trace.response.status} ${getStatusText(trace.response.status)}`);
97+
98+
// Response headers
99+
if (trace.response.headers) {
100+
Object.entries(trace.response.headers).forEach(([key, value]) => {
101+
console.log(` ${key}: ${value}`);
102+
});
103+
}
104+
105+
// Response body
106+
if (trace.response.body) {
107+
console.log('');
108+
const bodyStr = typeof trace.response.body === 'string'
109+
? trace.response.body
110+
: JSON.stringify(trace.response.body);
111+
112+
// Truncate very long responses
113+
if (bodyStr.length > 1000) {
114+
console.log(` ${bodyStr.substring(0, 1000)}... [truncated]`);
115+
} else {
116+
console.log(` ${bodyStr}`);
117+
}
118+
}
119+
}
120+
console.log('');
121+
});
122+
console.log(' ========================\n');
123+
}
124+
}
125+
126+
127+
function getStatusText(status: number): string {
128+
const statusTexts: Record<number, string> = {
129+
200: 'OK',
130+
201: 'Created',
131+
202: 'Accepted',
132+
302: 'Found',
133+
400: 'Bad Request',
134+
401: 'Unauthorized',
135+
403: 'Forbidden',
136+
404: 'Not Found',
137+
405: 'Method Not Allowed',
138+
500: 'Internal Server Error'
139+
};
140+
return statusTexts[status] || '';
141+
}

0 commit comments

Comments
 (0)