Skip to content

Commit 264af6b

Browse files
committed
impl
1 parent 61e4121 commit 264af6b

File tree

3 files changed

+164
-234
lines changed

3 files changed

+164
-234
lines changed

src/lib/event-emitter.ts

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { ServerResponse, type IncomingMessage } from "node:http";
2+
import {
3+
type McpErrorEvent,
4+
type McpEvent,
5+
type McpRequestEvent,
6+
type McpSessionEvent,
7+
createEvent,
8+
} from "./log-helper";
9+
10+
export class EventEmittingResponse extends ServerResponse {
11+
private onEvent?: (event: McpEvent) => void;
12+
private sessionId?: string;
13+
private requestId: string;
14+
private startTime: number;
15+
16+
constructor(
17+
req: IncomingMessage,
18+
onEvent?: (event: McpEvent) => void,
19+
sessionId?: string
20+
) {
21+
super(req);
22+
this.onEvent = onEvent;
23+
this.sessionId = sessionId;
24+
this.requestId = crypto.randomUUID();
25+
this.startTime = Date.now();
26+
}
27+
28+
emitEvent(event: Omit<McpEvent, "timestamp" | "sessionId" | "requestId">) {
29+
if (this.onEvent) {
30+
this.onEvent(
31+
createEvent({
32+
...event,
33+
sessionId: this.sessionId,
34+
requestId: this.requestId,
35+
} as Omit<McpEvent, "timestamp">)
36+
);
37+
}
38+
}
39+
40+
startSession(
41+
transport: "SSE" | "HTTP",
42+
clientInfo?: { userAgent?: string; ip?: string }
43+
) {
44+
this.emitEvent({
45+
type: "SESSION_STARTED",
46+
transport,
47+
clientInfo,
48+
} as Omit<McpSessionEvent, "timestamp" | "sessionId" | "requestId">);
49+
}
50+
51+
endSession(transport: "SSE" | "HTTP") {
52+
this.emitEvent({
53+
type: "SESSION_ENDED",
54+
transport,
55+
} as Omit<McpSessionEvent, "timestamp" | "sessionId" | "requestId">);
56+
}
57+
58+
requestReceived(method: string, parameters?: unknown) {
59+
this.emitEvent({
60+
type: "REQUEST_RECEIVED",
61+
method,
62+
parameters,
63+
status: "success",
64+
} as Omit<McpRequestEvent, "timestamp" | "sessionId" | "requestId">);
65+
}
66+
67+
requestCompleted(method: string, result?: unknown, error?: Error | string) {
68+
this.emitEvent({
69+
type: "REQUEST_COMPLETED",
70+
method,
71+
result,
72+
duration: Date.now() - this.startTime,
73+
status: error ? "error" : "success",
74+
} as Omit<McpRequestEvent, "timestamp" | "sessionId" | "requestId">);
75+
76+
if (error) {
77+
this.error(error, `Error executing request ${method}`, "request");
78+
}
79+
}
80+
81+
error(
82+
error: Error | string,
83+
context?: string,
84+
source: McpErrorEvent["source"] = "system",
85+
severity: McpErrorEvent["severity"] = "error"
86+
) {
87+
this.emitEvent({
88+
type: "ERROR",
89+
error,
90+
context,
91+
source,
92+
severity,
93+
} as Omit<McpErrorEvent, "timestamp" | "sessionId" | "requestId">);
94+
}
95+
96+
end(
97+
chunk?: unknown,
98+
encoding?: BufferEncoding | (() => void),
99+
cb?: () => void
100+
): this {
101+
let finalChunk = chunk;
102+
let finalEncoding = encoding;
103+
let finalCallback = cb;
104+
105+
if (typeof chunk === "function") {
106+
finalCallback = chunk as () => void;
107+
finalChunk = undefined;
108+
finalEncoding = undefined;
109+
} else if (typeof encoding === "function") {
110+
finalCallback = encoding as () => void;
111+
finalEncoding = undefined;
112+
}
113+
114+
return super.end(
115+
finalChunk as string | Buffer,
116+
finalEncoding as BufferEncoding,
117+
finalCallback
118+
);
119+
}
120+
}

src/lib/log-helper.ts

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
export type McpEventType =
22
| "SESSION_STARTED" // When a new client session begins (either HTTP or SSE)
33
| "SESSION_ENDED" // When a client session ends (SSE disconnection)
4-
| "TOOL_CALLED" // When a tool is invoked by the client
5-
| "TOOL_COMPLETED" // When a tool execution completes
6-
| "FUNCTION_CALLED" // When a function is called by the client
7-
| "FUNCTION_COMPLETED" // When a function call completes
4+
| "REQUEST_RECEIVED" // When a request is received from the client
5+
| "REQUEST_COMPLETED" // When a request completes
86
| "ERROR"; // When an error occurs during any operation
97

108
export interface McpEventBase {
@@ -23,37 +21,24 @@ export interface McpSessionEvent extends McpEventBase {
2321
};
2422
}
2523

26-
export interface McpToolEvent extends McpEventBase {
27-
type: "TOOL_CALLED" | "TOOL_COMPLETED";
28-
toolName: string;
24+
export interface McpRequestEvent extends McpEventBase {
25+
type: "REQUEST_RECEIVED" | "REQUEST_COMPLETED";
26+
method: string;
2927
parameters?: unknown;
3028
result?: unknown;
31-
duration?: number; // For TOOL_COMPLETED events
32-
status: "success" | "error";
33-
}
34-
35-
export interface McpFunctionEvent extends McpEventBase {
36-
type: "FUNCTION_CALLED" | "FUNCTION_COMPLETED";
37-
functionName: string;
38-
parameters?: unknown;
39-
result?: unknown;
40-
duration?: number; // For FUNCTION_COMPLETED events
29+
duration?: number; // For REQUEST_COMPLETED events
4130
status: "success" | "error";
4231
}
4332

4433
export interface McpErrorEvent extends McpEventBase {
4534
type: "ERROR";
4635
error: Error | string;
4736
context?: string;
48-
source: "tool" | "function" | "session" | "system";
37+
source: "request" | "session" | "system";
4938
severity: "warning" | "error" | "fatal";
5039
}
5140

52-
export type McpEvent =
53-
| McpSessionEvent
54-
| McpToolEvent
55-
| McpFunctionEvent
56-
| McpErrorEvent;
41+
export type McpEvent = McpSessionEvent | McpRequestEvent | McpErrorEvent;
5742

5843
export function createEvent<T extends McpEvent>(
5944
event: Omit<T, "timestamp">

0 commit comments

Comments
 (0)