Skip to content

Commit faf26b0

Browse files
authored
chore: add type check for CI (#157)
1 parent 1357fbb commit faf26b0

File tree

18 files changed

+101
-73
lines changed

18 files changed

+101
-73
lines changed

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"name": "Launch Program",
1111
"skipFiles": ["<node_internals>/**"],
1212
"program": "${workspaceFolder}/dist/index.js",
13-
"preLaunchTask": "tsc: build - tsconfig.json",
13+
"preLaunchTask": "tsc: build - tsconfig.build.json",
1414
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
1515
}
1616
]

eslint.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export default defineConfig([
2929
files,
3030
languageOptions: {
3131
parserOptions: {
32-
project: "./tsconfig.lint.json",
32+
project: "./tsconfig.json",
3333
tsconfigRootDir: import.meta.dirname,
3434
},
3535
},

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@
1818
"scripts": {
1919
"prepare": "npm run build",
2020
"build:clean": "rm -rf dist",
21-
"build:compile": "tsc",
21+
"build:compile": "tsc --project tsconfig.build.json",
2222
"build:chmod": "chmod +x dist/index.js",
2323
"build": "npm run build:clean && npm run build:compile && npm run build:chmod",
2424
"inspect": "npm run build && mcp-inspector -- dist/index.js",
2525
"prettier": "prettier",
26-
"check": "npm run build && npm run check:lint && npm run check:format",
26+
"check": "npm run build && npm run check:types && npm run check:lint && npm run check:format",
2727
"check:lint": "eslint .",
2828
"check:format": "prettier -c .",
29+
"check:types": "tsc --noEmit --project tsconfig.json",
2930
"reformat": "prettier --write .",
3031
"generate": "./scripts/generate.sh",
3132
"test": "jest --coverage"

scripts/apply.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ async function main() {
4444
const openapi = JSON.parse(specFile) as OpenAPIV3_1.Document;
4545
for (const path in openapi.paths) {
4646
for (const method in openapi.paths[path]) {
47+
// @ts-expect-error This is a workaround for the OpenAPI types
4748
const operation = openapi.paths[path][method] as OpenAPIV3_1.OperationObject;
4849

4950
if (!operation.operationId || !operation.tags?.length) {

scripts/filter.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,14 @@ function filterOpenapi(openapi: OpenAPIV3_1.Document): OpenAPIV3_1.Document {
4343
for (const path in openapi.paths) {
4444
const filteredMethods = {} as OpenAPIV3_1.PathItemObject;
4545
for (const method in openapi.paths[path]) {
46+
// @ts-expect-error This is a workaround for the OpenAPI types
4647
if (allowedOperations.includes((openapi.paths[path][method] as { operationId: string }).operationId)) {
48+
// @ts-expect-error This is a workaround for the OpenAPI types
4749
filteredMethods[method] = openapi.paths[path][method] as OpenAPIV3_1.OperationObject;
4850
}
4951
}
5052
if (Object.keys(filteredMethods).length > 0) {
53+
// @ts-expect-error This is a workaround for the OpenAPI types
5154
filteredPaths[path] = filteredMethods;
5255
}
5356
}

src/common/atlas/apiClient.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { FetchOptions } from "openapi-fetch";
33
import { AccessToken, ClientCredentials } from "simple-oauth2";
44
import { ApiClientError } from "./apiClientError.js";
55
import { paths, operations } from "./openapi.js";
6-
import { BaseEvent } from "../../telemetry/types.js";
6+
import { CommonProperties, TelemetryEvent } from "../../telemetry/types.js";
77
import { packageInfo } from "../../packageInfo.js";
88

99
const ATLAS_API_VERSION = "2025-03-12";
@@ -123,7 +123,7 @@ export class ApiClient {
123123
}>;
124124
}
125125

126-
async sendEvents(events: BaseEvent[]): Promise<void> {
126+
async sendEvents(events: TelemetryEvent<CommonProperties>[]): Promise<void> {
127127
let endpoint = "api/private/unauth/telemetry/events";
128128
const headers: Record<string, string> = {
129129
Accept: "application/json",

src/server.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ export class Server {
107107
timestamp: new Date().toISOString(),
108108
source: "mdbmcp",
109109
properties: {
110-
...this.telemetry.getCommonProperties(),
111110
result: "success",
112111
duration_ms: commandDuration,
113112
component: "server",
@@ -119,7 +118,7 @@ export class Server {
119118
if (command === "start") {
120119
event.properties.startup_time_ms = commandDuration;
121120
event.properties.read_only_mode = this.userConfig.readOnly || false;
122-
event.properties.disallowed_tools = this.userConfig.disabledTools || [];
121+
event.properties.disabled_tools = this.userConfig.disabledTools || [];
123122
}
124123
if (command === "stop") {
125124
event.properties.runtime_duration_ms = Date.now() - this.startTime;

src/telemetry/eventCache.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class EventCache {
1313
private cache: LRUCache<number, BaseEvent>;
1414
private nextId = 0;
1515

16-
private constructor() {
16+
constructor() {
1717
this.cache = new LRUCache({
1818
max: EventCache.MAX_EVENTS,
1919
// Using FIFO eviction strategy for events

src/telemetry/telemetry.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,12 @@ export class Telemetry {
113113
*/
114114
private async sendEvents(client: ApiClient, events: BaseEvent[]): Promise<EventResult> {
115115
try {
116-
await client.sendEvents(events);
116+
await client.sendEvents(
117+
events.map((event) => ({
118+
...event,
119+
properties: { ...this.getCommonProperties(), ...event.properties },
120+
}))
121+
);
117122
return { success: true };
118123
} catch (error) {
119124
return {

src/telemetry/types.ts

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,49 +8,46 @@ export type TelemetryBoolSet = "true" | "false";
88
/**
99
* Base interface for all events
1010
*/
11-
export interface Event {
11+
export type TelemetryEvent<T> = {
1212
timestamp: string;
1313
source: "mdbmcp";
14-
properties: Record<string, unknown>;
15-
}
16-
17-
export interface BaseEvent extends Event {
18-
properties: CommonProperties & {
14+
properties: T & {
1915
component: string;
2016
duration_ms: number;
2117
result: TelemetryResult;
2218
category: string;
23-
} & Event["properties"];
24-
}
19+
};
20+
};
21+
22+
export type BaseEvent = TelemetryEvent<unknown>;
2523

2624
/**
2725
* Interface for tool events
2826
*/
29-
export interface ToolEvent extends BaseEvent {
30-
properties: {
31-
command: string;
32-
error_code?: string;
33-
error_type?: string;
34-
project_id?: string;
35-
org_id?: string;
36-
cluster_name?: string;
37-
is_atlas?: boolean;
38-
} & BaseEvent["properties"];
39-
}
27+
export type ToolEventProperties = {
28+
command: string;
29+
error_code?: string;
30+
error_type?: string;
31+
project_id?: string;
32+
org_id?: string;
33+
cluster_name?: string;
34+
is_atlas?: boolean;
35+
};
4036

37+
export type ToolEvent = TelemetryEvent<ToolEventProperties>;
4138
/**
4239
* Interface for server events
4340
*/
44-
export interface ServerEvent extends BaseEvent {
45-
properties: {
46-
command: ServerCommand;
47-
reason?: string;
48-
startup_time_ms?: number;
49-
runtime_duration_ms?: number;
50-
read_only_mode?: boolean;
51-
disabled_tools?: string[];
52-
} & BaseEvent["properties"];
53-
}
41+
export type ServerEventProperties = {
42+
command: ServerCommand;
43+
reason?: string;
44+
startup_time_ms?: number;
45+
runtime_duration_ms?: number;
46+
read_only_mode?: boolean;
47+
disabled_tools?: string[];
48+
};
49+
50+
export type ServerEvent = TelemetryEvent<ServerEventProperties>;
5451

5552
/**
5653
* Interface for static properties, they can be fetched once and reused.
@@ -69,6 +66,7 @@ export type CommonStaticProperties = {
6966
* Common properties for all events that might change.
7067
*/
7168
export type CommonProperties = {
69+
device_id?: string;
7270
mcp_client_version?: string;
7371
mcp_client_name?: string;
7472
config_atlas_auth?: TelemetryBoolSet;

src/tools/tool.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ export abstract class ToolBase {
4343
timestamp: new Date().toISOString(),
4444
source: "mdbmcp",
4545
properties: {
46-
...this.telemetry.getCommonProperties(),
4746
command: this.name,
4847
category: this.category,
4948
component: "tool",

tests/integration/inMemoryTransport.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
22
import { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
33

44
export class InMemoryTransport implements Transport {
5-
private outputController: ReadableStreamDefaultController<JSONRPCMessage>;
5+
private outputController: ReadableStreamDefaultController<JSONRPCMessage> | undefined;
66

77
private startPromise: Promise<unknown>;
88

@@ -35,13 +35,13 @@ export class InMemoryTransport implements Transport {
3535
}
3636

3737
send(message: JSONRPCMessage): Promise<void> {
38-
this.outputController.enqueue(message);
38+
this.outputController?.enqueue(message);
3939
return Promise.resolve();
4040
}
4141

4242
// eslint-disable-next-line @typescript-eslint/require-await
4343
async close(): Promise<void> {
44-
this.outputController.close();
44+
this.outputController?.close();
4545
this.onclose?.();
4646
}
4747
onclose?: (() => void) | undefined;

tests/integration/tools/atlas/atlasHelpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export function parseTable(text: string): Record<string, string>[] {
7474
return data
7575
.filter((_, index) => index >= 2)
7676
.map((cells) => {
77-
const row = {};
77+
const row: Record<string, string> = {};
7878
cells.forEach((cell, index) => {
7979
row[headers[index]] = cell;
8080
});

tests/unit/telemetry.test.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,23 @@ describe("Telemetry", () => {
2121

2222
// Helper function to create properly typed test events
2323
function createTestEvent(options?: {
24-
source?: string;
2524
result?: TelemetryResult;
2625
component?: string;
2726
category?: string;
2827
command?: string;
2928
duration_ms?: number;
30-
}): BaseEvent {
29+
}): Omit<BaseEvent, "properties"> & {
30+
properties: {
31+
component: string;
32+
duration_ms: number;
33+
result: TelemetryResult;
34+
category: string;
35+
command: string;
36+
};
37+
} {
3138
return {
3239
timestamp: new Date().toISOString(),
33-
source: options?.source || "mdbmcp",
40+
source: "mdbmcp",
3441
properties: {
3542
component: options?.component || "test-component",
3643
duration_ms: options?.duration_ms || 100,
@@ -48,6 +55,12 @@ describe("Telemetry", () => {
4855
appendEventsCalls = 0,
4956
sendEventsCalledWith = undefined,
5057
appendEventsCalledWith = undefined,
58+
}: {
59+
sendEventsCalls?: number;
60+
clearEventsCalls?: number;
61+
appendEventsCalls?: number;
62+
sendEventsCalledWith?: BaseEvent[] | undefined;
63+
appendEventsCalledWith?: BaseEvent[] | undefined;
5164
} = {}) {
5265
const { calls: sendEvents } = mockApiClient.sendEvents.mock;
5366
const { calls: clearEvents } = mockEventCache.clearEvents.mock;
@@ -58,7 +71,15 @@ describe("Telemetry", () => {
5871
expect(appendEvents.length).toBe(appendEventsCalls);
5972

6073
if (sendEventsCalledWith) {
61-
expect(sendEvents[0]?.[0]).toEqual(sendEventsCalledWith);
74+
expect(sendEvents[0]?.[0]).toEqual(
75+
sendEventsCalledWith.map((event) => ({
76+
...event,
77+
properties: {
78+
...telemetry.getCommonProperties(),
79+
...event.properties,
80+
},
81+
}))
82+
);
6283
}
6384

6485
if (appendEventsCalledWith) {
@@ -71,7 +92,7 @@ describe("Telemetry", () => {
7192
jest.clearAllMocks();
7293

7394
// Setup mocked API client
74-
mockApiClient = new MockApiClient() as jest.Mocked<ApiClient>;
95+
mockApiClient = new MockApiClient({ baseUrl: "" }) as jest.Mocked<ApiClient>;
7596
mockApiClient.sendEvents = jest.fn().mockResolvedValue(undefined);
7697
mockApiClient.hasCredentials = jest.fn().mockReturnValue(true);
7798

tsconfig.build.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es2020",
4+
"module": "nodenext",
5+
"moduleResolution": "nodenext",
6+
"rootDir": "./src",
7+
"outDir": "./dist",
8+
"strict": true,
9+
"strictNullChecks": true,
10+
"esModuleInterop": true,
11+
"types": ["node", "jest"],
12+
"sourceMap": true,
13+
"skipLibCheck": true,
14+
"resolveJsonModule": true,
15+
"allowSyntheticDefaultImports": true,
16+
"typeRoots": ["./node_modules/@types", "./src/types"]
17+
},
18+
"include": ["src/**/*.ts"]
19+
}

tsconfig.jest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"extends": "./tsconfig.json",
2+
"extends": "./tsconfig.build.json",
33
"compilerOptions": {
44
"module": "esnext",
55
"target": "esnext",

tsconfig.json

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,9 @@
11
{
2+
"extends": "./tsconfig.build.json",
23
"compilerOptions": {
3-
"target": "es2020",
4-
"module": "nodenext",
5-
"moduleResolution": "nodenext",
6-
"rootDir": "./src",
7-
"outDir": "./dist",
8-
"strict": true,
9-
"strictNullChecks": true,
10-
"esModuleInterop": true,
11-
"types": ["node", "jest"],
12-
"sourceMap": true,
13-
"skipLibCheck": true,
14-
"resolveJsonModule": true,
15-
"allowSyntheticDefaultImports": true,
16-
"typeRoots": ["./node_modules/@types", "./src/types"]
4+
"rootDir": ".",
5+
"types": ["jest"],
6+
"skipLibCheck": true
177
},
18-
"include": ["src/**/*.ts"]
8+
"include": ["**/*"]
199
}

tsconfig.lint.json

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

0 commit comments

Comments
 (0)