Skip to content

Commit 2420e45

Browse files
committed
Merge branch 'main' into ni/disable-tools
* main: chore: auto generate apiClient (#64) refactor: rename state to session, combine tool registration, and clearer dependencies (#55) chore: add type-powered eslint rules (#62)
2 parents 1407270 + 9286078 commit 2420e45

39 files changed

+446
-561
lines changed

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ dist
22
coverage
33
package-lock.json
44
tests/tmp
5+
src/common/atlas/openapi.d.ts

.prettierrc.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@
55
"singleQuote": false,
66
"printWidth": 120,
77
"overrides": [
8+
{
9+
"files": "*.ts",
10+
"options": {
11+
"insertPragma": false,
12+
"proseWrap": "preserve"
13+
}
14+
},
815
{
916
"files": "*.json",
1017
"options": {

eslint.config.js

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,32 @@ import globals from "globals";
44
import tseslint from "typescript-eslint";
55
import eslintConfigPrettier from "eslint-config-prettier/flat";
66

7+
const files = ["src/**/*.ts", "scripts/**/*.ts", "tests/**/*.test.ts", "eslint.config.js", "jest.config.js"];
8+
79
export default defineConfig([
8-
{ files: ["src/**/*.ts"], plugins: { js }, extends: ["js/recommended"] },
9-
{ files: ["src/**/*.ts"], languageOptions: { globals: globals.node } },
10-
tseslint.configs.recommended,
10+
{ files, plugins: { js }, extends: ["js/recommended"] },
11+
{ files, languageOptions: { globals: globals.node } },
12+
tseslint.configs.recommendedTypeChecked,
13+
{
14+
languageOptions: {
15+
parserOptions: {
16+
projectService: true,
17+
tsconfigRootDir: import.meta.dirname,
18+
},
19+
},
20+
},
21+
{
22+
files,
23+
rules: {
24+
"@typescript-eslint/switch-exhaustiveness-check": "error",
25+
"@typescript-eslint/no-non-null-assertion": "error",
26+
},
27+
},
28+
// Ignore features specific to TypeScript resolved rules
29+
tseslint.config({
30+
// TODO: Configure tests and scripts to work with this.
31+
ignores: ["eslint.config.js", "jest.config.js", "tests/**/*.ts", "scripts/**/*.ts"],
32+
}),
33+
globalIgnores(["node_modules", "dist", "src/common/atlas/openapi.d.ts"]),
1134
eslintConfigPrettier,
12-
globalIgnores(["node_modules", "dist"]),
1335
]);

package-lock.json

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@
1919
"prepare": "npm run build",
2020
"build:clean": "rm -rf dist",
2121
"build:compile": "tsc",
22-
"build:addshebang": "echo '#!/usr/bin/env node' > dist/index2.js && cat dist/index.js >> dist/index2.js && mv dist/index2.js dist/index.js",
2322
"build:chmod": "chmod +x dist/index.js",
24-
"build": "npm run build:clean && npm run build:compile && npm run build:addshebang && npm run build:chmod",
23+
"build": "npm run build:clean && npm run build:compile && npm run build:chmod",
2524
"inspect": "npm run build && mcp-inspector -- dist/index.js",
2625
"prettier": "prettier",
2726
"check": "npm run build && npm run check:lint && npm run check:format",
@@ -38,6 +37,7 @@
3837
"@modelcontextprotocol/inspector": "^0.8.2",
3938
"@modelcontextprotocol/sdk": "^1.8.0",
4039
"@redocly/cli": "^1.34.2",
40+
"@types/express": "^5.0.1",
4141
"@types/jest": "^29.5.14",
4242
"@types/node": "^22.14.0",
4343
"@types/simple-oauth2": "^5.0.7",
@@ -60,7 +60,6 @@
6060
"dependencies": {
6161
"@mongodb-js/devtools-connect": "^3.7.2",
6262
"@mongosh/service-provider-node-driver": "^3.6.0",
63-
"@types/express": "^5.0.1",
6463
"bson": "^6.10.3",
6564
"mongodb": "^6.15.0",
6665
"mongodb-log-writer": "^2.4.1",

scripts/apply.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import fs from "fs/promises";
2+
import { OpenAPIV3_1 } from "openapi-types";
3+
import argv from "yargs-parser";
4+
5+
function findParamFromRef(ref: string, openapi: OpenAPIV3_1.Document): OpenAPIV3_1.ParameterObject {
6+
const paramParts = ref.split("/");
7+
paramParts.shift(); // Remove the first part which is always '#'
8+
let param: any = openapi; // eslint-disable-line @typescript-eslint/no-explicit-any
9+
while (true) {
10+
const part = paramParts.shift();
11+
if (!part) {
12+
break;
13+
}
14+
param = param[part];
15+
}
16+
return param;
17+
}
18+
19+
async function main() {
20+
const { spec, file } = argv(process.argv.slice(2));
21+
22+
if (!spec || !file) {
23+
console.error("Please provide both --spec and --file arguments.");
24+
process.exit(1);
25+
}
26+
27+
const specFile = (await fs.readFile(spec, "utf8")) as string;
28+
29+
const operations: {
30+
path: string;
31+
method: string;
32+
operationId: string;
33+
requiredParams: boolean;
34+
tag: string;
35+
}[] = [];
36+
37+
const openapi = JSON.parse(specFile) as OpenAPIV3_1.Document;
38+
for (const path in openapi.paths) {
39+
for (const method in openapi.paths[path]) {
40+
const operation: OpenAPIV3_1.OperationObject = openapi.paths[path][method];
41+
42+
if (!operation.operationId || !operation.tags?.length) {
43+
continue;
44+
}
45+
46+
let requiredParams = !!operation.requestBody;
47+
48+
for (const param of operation.parameters || []) {
49+
const ref = (param as OpenAPIV3_1.ReferenceObject).$ref as string | undefined;
50+
let paramObject: OpenAPIV3_1.ParameterObject = param as OpenAPIV3_1.ParameterObject;
51+
if (ref) {
52+
paramObject = findParamFromRef(ref, openapi);
53+
}
54+
if (paramObject.in === "path") {
55+
requiredParams = true;
56+
}
57+
}
58+
59+
operations.push({
60+
path,
61+
method: method.toUpperCase(),
62+
operationId: operation.operationId || "",
63+
requiredParams,
64+
tag: operation.tags[0],
65+
});
66+
}
67+
}
68+
69+
const operationOutput = operations
70+
.map((operation) => {
71+
const { operationId, method, path, requiredParams } = operation;
72+
return `async ${operationId}(options${requiredParams ? "" : "?"}: FetchOptions<operations["${operationId}"]>) {
73+
const { data } = await this.client.${method}("${path}", options);
74+
return data;
75+
}
76+
`;
77+
})
78+
.join("\n");
79+
80+
const templateFile = (await fs.readFile(file, "utf8")) as string;
81+
const output = templateFile.replace(
82+
/\/\/ DO NOT EDIT\. This is auto-generated code\.\n.*\/\/ DO NOT EDIT\. This is auto-generated code\./g,
83+
operationOutput
84+
);
85+
86+
await fs.writeFile(file, output, "utf8");
87+
}
88+
89+
main().catch((error) => {
90+
console.error("Error:", error);
91+
process.exit(1);
92+
});

scripts/filter.ts

100644100755
File mode changed.

scripts/generate.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ curl -Lo ./scripts/spec.json https://github.com/mongodb/openapi/raw/refs/heads/m
66
tsx ./scripts/filter.ts > ./scripts/filteredSpec.json < ./scripts/spec.json
77
redocly bundle --ext json --remove-unused-components ./scripts/filteredSpec.json --output ./scripts/bundledSpec.json
88
openapi-typescript ./scripts/bundledSpec.json --root-types-no-schema-prefix --root-types --output ./src/common/atlas/openapi.d.ts
9-
prettier --write ./src/common/atlas/openapi.d.ts
9+
tsx ./scripts/apply.ts --spec ./scripts/bundledSpec.json --file ./src/common/atlas/apiClient.ts
10+
prettier --write ./src/common/atlas/openapi.d.ts ./src/common/atlas/apiClient.ts
1011
rm -rf ./scripts/bundledSpec.json ./scripts/filteredSpec.json ./scripts/spec.json

0 commit comments

Comments
 (0)