Skip to content

Commit f7942de

Browse files
committed
fix: use conventional inheritance pattern
1 parent 4ea91c3 commit f7942de

File tree

3 files changed

+110
-85
lines changed

3 files changed

+110
-85
lines changed

src/resources/common/config.ts

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
11
import { ReactiveResource } from "../resource.js";
22
import { config } from "../../common/config.js";
33
import type { UserConfig } from "../../common/config.js";
4+
import type { Server } from "../../server.js";
5+
import type { Telemetry } from "../../telemetry/telemetry.js";
46

5-
export class ConfigResource extends ReactiveResource(
6-
{
7-
name: "config",
8-
uri: "config://config",
9-
config: {
10-
description:
11-
"Server configuration, supplied by the user either as environment variables or as startup arguments",
12-
},
13-
},
14-
{
15-
initial: { ...config },
16-
events: [],
7+
export class ConfigResource extends ReactiveResource<UserConfig, readonly []> {
8+
constructor(server: Server, telemetry: Telemetry) {
9+
super(
10+
{
11+
name: "config",
12+
uri: "config://config",
13+
config: {
14+
description:
15+
"Server configuration, supplied by the user either as environment variables or as startup arguments",
16+
},
17+
},
18+
{
19+
initial: { ...config },
20+
events: [],
21+
},
22+
server,
23+
telemetry
24+
);
1725
}
18-
) {
1926
reduce(eventName: undefined, event: undefined): UserConfig {
2027
void eventName;
2128
void event;

src/resources/common/debug.ts

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { ReactiveResource } from "../resource.js";
2+
import type { Server } from "../../server.js";
3+
import type { Telemetry } from "../../telemetry/telemetry.js";
24

35
type ConnectionStateDebuggingInformation = {
46
readonly tag: "connected" | "connecting" | "disconnected" | "errored";
@@ -8,20 +10,28 @@ type ConnectionStateDebuggingInformation = {
810
readonly errorReason?: string;
911
};
1012

11-
export class DebugResource extends ReactiveResource(
12-
{
13-
name: "debug-mongodb",
14-
uri: "debug://mongodb",
15-
config: {
16-
description:
17-
"Debugging information for MongoDB connectivity issues. Tracks the last connectivity error and attempt information.",
18-
},
19-
},
20-
{
21-
initial: { tag: "disconnected" } as ConnectionStateDebuggingInformation,
22-
events: ["connect", "disconnect", "close", "connection-error"],
13+
export class DebugResource extends ReactiveResource<
14+
ConnectionStateDebuggingInformation,
15+
readonly ["connect", "disconnect", "close", "connection-error"]
16+
> {
17+
constructor(server: Server, telemetry: Telemetry) {
18+
super(
19+
{
20+
name: "debug-mongodb",
21+
uri: "debug://mongodb",
22+
config: {
23+
description:
24+
"Debugging information for MongoDB connectivity issues. Tracks the last connectivity error and attempt information.",
25+
},
26+
},
27+
{
28+
initial: { tag: "disconnected" } as ConnectionStateDebuggingInformation,
29+
events: ["connect", "disconnect", "close", "connection-error"],
30+
},
31+
server,
32+
telemetry
33+
);
2334
}
24-
) {
2535
reduce(
2636
eventName: "connect" | "disconnect" | "close" | "connection-error",
2737
event: string | undefined

src/resources/resource.ts

Lines changed: 67 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -8,76 +8,84 @@ import { LogId } from "../common/logger.js";
88

99
type PayloadOf<K extends keyof SessionEvents> = SessionEvents[K][0];
1010

11-
type ResourceConfiguration = { name: string; uri: string; config: ResourceMetadata };
11+
export type ResourceConfiguration = {
12+
name: string;
13+
uri: string;
14+
config: ResourceMetadata;
15+
};
1216

13-
export function ReactiveResource<Value, RelevantEvents extends readonly (keyof SessionEvents)[]>(
14-
{ name, uri, config: resourceConfig }: ResourceConfiguration,
15-
{
16-
initial,
17-
events,
18-
}: {
19-
initial: Value;
20-
events: RelevantEvents;
21-
}
22-
) {
23-
type SomeEvent = RelevantEvents[number];
17+
export type ReactiveResourceOptions<Value, RelevantEvents extends readonly (keyof SessionEvents)[]> = {
18+
initial: Value;
19+
events: RelevantEvents;
20+
};
2421

25-
abstract class NewReactiveResource {
26-
protected readonly session: Session;
27-
protected readonly config: UserConfig;
28-
protected current: Value;
22+
export abstract class ReactiveResource<Value, RelevantEvents extends readonly (keyof SessionEvents)[]> {
23+
protected readonly session: Session;
24+
protected readonly config: UserConfig;
25+
protected current: Value;
26+
protected readonly name: string;
27+
protected readonly uri: string;
28+
protected readonly resourceConfig: ResourceMetadata;
29+
protected readonly events: RelevantEvents;
2930

30-
constructor(
31-
protected readonly server: Server,
32-
protected readonly telemetry: Telemetry,
33-
current?: Value
34-
) {
35-
this.current = current ?? initial;
36-
this.session = server.session;
37-
this.config = server.userConfig;
31+
constructor(
32+
resourceConfiguration: ResourceConfiguration,
33+
options: ReactiveResourceOptions<Value, RelevantEvents>,
34+
protected readonly server: Server,
35+
protected readonly telemetry: Telemetry,
36+
current?: Value
37+
) {
38+
this.name = resourceConfiguration.name;
39+
this.uri = resourceConfiguration.uri;
40+
this.resourceConfig = resourceConfiguration.config;
41+
this.events = options.events;
42+
this.current = current ?? options.initial;
43+
this.session = server.session;
44+
this.config = server.userConfig;
3845

39-
for (const event of events) {
40-
this.session.on(event, (...args: SessionEvents[typeof event]) => {
41-
this.reduceApply(event, (args as unknown[])[0] as PayloadOf<typeof event>);
42-
void this.triggerUpdate();
43-
});
44-
}
45-
}
46+
this.setupEventListeners();
47+
}
4648

47-
public register(): void {
48-
this.server.mcpServer.registerResource(name, uri, resourceConfig, this.resourceCallback);
49+
private setupEventListeners(): void {
50+
for (const event of this.events) {
51+
this.session.on(event, (...args: SessionEvents[typeof event]) => {
52+
this.reduceApply(event, (args as unknown[])[0] as PayloadOf<typeof event>);
53+
void this.triggerUpdate();
54+
});
4955
}
56+
}
5057

51-
private resourceCallback: ReadResourceCallback = (uri) => ({
52-
contents: [
53-
{
54-
text: this.toOutput(),
55-
mimeType: "application/json",
56-
uri: uri.href,
57-
},
58-
],
59-
});
58+
public register(): void {
59+
this.server.mcpServer.registerResource(this.name, this.uri, this.resourceConfig, this.resourceCallback);
60+
}
6061

61-
private async triggerUpdate() {
62-
try {
63-
await this.server.mcpServer.server.sendResourceUpdated({ uri });
64-
this.server.mcpServer.sendResourceListChanged();
65-
} catch (error: unknown) {
66-
this.session.logger.warning({
67-
id: LogId.resourceUpdateFailure,
68-
context: "resource",
69-
message: `Could not send the latest resources to the client: ${error as string}`,
70-
});
71-
}
72-
}
62+
private resourceCallback: ReadResourceCallback = (uri) => ({
63+
contents: [
64+
{
65+
text: this.toOutput(),
66+
mimeType: "application/json",
67+
uri: uri.href,
68+
},
69+
],
70+
});
7371

74-
reduceApply(eventName: SomeEvent, ...event: PayloadOf<SomeEvent>[]): void {
75-
this.current = this.reduce(eventName, ...event);
72+
private async triggerUpdate(): Promise<void> {
73+
try {
74+
await this.server.mcpServer.server.sendResourceUpdated({ uri: this.uri });
75+
this.server.mcpServer.sendResourceListChanged();
76+
} catch (error: unknown) {
77+
this.session.logger.warning({
78+
id: LogId.resourceUpdateFailure,
79+
context: "resource",
80+
message: `Could not send the latest resources to the client: ${error as string}`,
81+
});
7682
}
83+
}
7784

78-
protected abstract reduce(eventName: SomeEvent, ...event: PayloadOf<SomeEvent>[]): Value;
79-
abstract toOutput(): string;
85+
protected reduceApply(eventName: RelevantEvents[number], ...event: PayloadOf<RelevantEvents[number]>[]): void {
86+
this.current = this.reduce(eventName, ...event);
8087
}
8188

82-
return NewReactiveResource;
89+
protected abstract reduce(eventName: RelevantEvents[number], ...event: PayloadOf<RelevantEvents[number]>[]): Value;
90+
public abstract toOutput(): string;
8391
}

0 commit comments

Comments
 (0)