Skip to content

chore(build): build a universal ESM and CommonJS package #371

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 36 commits into from
Aug 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6eed483
chore(tests): switch to vitest
gagik Jul 14, 2025
463b6cc
fix: remove traces of jest
gagik Jul 14, 2025
3b03327
fix: add and fix vitest linting
gagik Jul 14, 2025
2d63956
fix: coverage
gagik Jul 14, 2025
6e24d64
fix: use --exclude
gagik Jul 14, 2025
3f10cbd
fix: increase hook timeout
gagik Jul 14, 2025
52ccf24
fix: from feedback
gagik Jul 15, 2025
cbebf3e
feat: use custom toIncludeSameMembers matcher
gagik Jul 15, 2025
8fc12bc
Merge branch 'main' of github.com:mongodb-js/mongodb-mcp-server into …
gagik Jul 15, 2025
3bf00e6
fix: remove unneeded globals
gagik Jul 15, 2025
c138c1e
WIP:
gagik Jul 15, 2025
2e76d9a
wip
gagik Jul 15, 2025
49dccbe
fix: add test, keep backwards compatibility
gagik Jul 15, 2025
a262914
Merge branch 'main' of github.com:mongodb-js/mongodb-mcp-server into …
gagik Jul 15, 2025
5a12fec
fix: update package-lock
gagik Jul 15, 2025
ad51a41
fix: less scripts
gagik Jul 15, 2025
7064ad0
fix: clearer typescript
gagik Jul 15, 2025
70cd5c9
fix: add dist/cjs
gagik Jul 15, 2025
00a1b19
fix: conver to proper file URLs
gagik Jul 15, 2025
515b7c5
fix: use arelative
gagik Jul 15, 2025
6f5540e
fix: use project root
gagik Jul 15, 2025
c61e643
fix: use esm/lib.js
gagik Jul 15, 2025
9e4d91b
fix: resolve directly
gagik Jul 15, 2025
bf35972
fix: use a script to ensure windows support
gagik Jul 15, 2025
c70853e
fix: use esm as default
gagik Jul 15, 2025
384a3be
fix: remove redundant bits
gagik Jul 16, 2025
6a70bbe
fix: add backwards compatibility
gagik Jul 16, 2025
820292c
fix: add types
gagik Jul 16, 2025
cce6ddb
chore: package only dist and other req files
himanshusinghs Jun 25, 2025
9555dcd
Merge branch 'main' of github.com:mongodb-js/mongodb-mcp-server into …
gagik Aug 7, 2025
4ea91c3
fix: update to match
gagik Aug 7, 2025
f7942de
fix: use conventional inheritance pattern
gagik Aug 7, 2025
cd0c393
fix: remove unnecessary cast
gagik Aug 7, 2025
be5c06b
fix: make reducapply public
gagik Aug 7, 2025
f7e5167
fix: remove event listeners bit
gagik Aug 7, 2025
0e28212
fix: bring back event listeners
gagik Aug 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ jobs:
rm -rf node_modules
npm pkg set scripts.prepare="exit 0"
npm install --omit=dev
- run: npx -y @modelcontextprotocol/inspector --cli --method tools/list -- node dist/index.js
- run: npx -y @modelcontextprotocol/inspector --cli --method tools/list -- node dist/esm/index.js
1 change: 1 addition & 0 deletions .github/workflows/prepare_release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
id: bump-version
run: |
echo "NEW_VERSION=$(npm version ${{ inputs.version }} --no-git-tag-version)" >> $GITHUB_OUTPUT
npm run build:update-package-version
- name: Create release PR
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # 7.0.8
id: create-pr
Expand Down
2 changes: 1 addition & 1 deletion .smithery/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ RUN npm ci --production --ignore-scripts
# Expose no ports (stdio only)

# Default command
CMD ["node", "dist/index.js"]
CMD ["node", "dist/esm/index.js"]
2 changes: 1 addition & 1 deletion .smithery/smithery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ startCommand:
# A function that produces the CLI command to start the MCP on stdio.
|-
(config) => {
const args = ['dist/index.js'];
const args = ['dist/esm/index.js'];
if (config) {
if (config.atlasClientId) {
args.push('--apiClientId');
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ This project implements a Model Context Protocol (MCP) server for MongoDB and Mo
{
"mcpServers": {
"MongoDB": {
"command": "/path/to/mongodb-mcp-server/dist/index.js"
"command": "/path/to/mongodb-mcp-server/dist/esm/index.js"
}
}
}
Expand Down Expand Up @@ -104,7 +104,7 @@ npm run inspect
This is equivalent to:

```shell
npx @modelcontextprotocol/inspector -- node dist/index.js
npx @modelcontextprotocol/inspector -- node dist/esm/index.js
```

## Pull Request Guidelines
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 26 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,47 @@
"name": "mongodb-mcp-server",
"description": "MongoDB Model Context Protocol Server",
"version": "0.2.0",
"main": "dist/index.js",
"type": "module",
"exports": {
".": {
"import": {
"types": "./dist/esm/lib.d.ts",
"default": "./dist/esm/lib.js"
},
"require": {
"types": "./dist/cjs/lib.d.ts",
"default": "./dist/cjs/lib.js"
}
}
},
"main": "./dist/cjs/lib.js",
"types": "./dist/cjs/lib.d.ts",
"author": "MongoDB <info@mongodb.com>",
"homepage": "https://github.com/mongodb-js/mongodb-mcp-server",
"repository": {
"url": "https://github.com/mongodb-js/mongodb-mcp-server.git"
},
"bin": {
"mongodb-mcp-server": "dist/index.js"
"mongodb-mcp-server": "dist/esm/index.js"
},
"publishConfig": {
"access": "public"
},
"type": "module",
"files": [
"dist"
],
"scripts": {
"start": "node dist/index.js --transport http --loggers stderr mcp",
"start:stdio": "node dist/index.js --transport stdio --loggers stderr mcp",
"prepare": "npm run build",
"build:clean": "rm -rf dist",
"build:compile": "tsc --project tsconfig.build.json",
"build:chmod": "chmod +x dist/index.js",
"build": "npm run build:clean && npm run build:compile && npm run build:chmod",
"inspect": "npm run build && mcp-inspector -- dist/index.js",
"build:update-package-version": "tsx scripts/updatePackageVersion.ts",
"build:esm": "tsc --project tsconfig.esm.json",
"build:cjs": "tsc --project tsconfig.cjs.json",
"build:universal-package": "tsx scripts/createUniversalPackage.ts",
"build:chmod": "chmod +x dist/esm/index.js",
"build": "npm run build:clean && npm run build:esm && npm run build:cjs && npm run build:universal-package && npm run build:chmod",
"inspect": "npm run build && mcp-inspector -- dist/esm/index.js",
"prettier": "prettier",
"check": "npm run build && npm run check:types && npm run check:lint && npm run check:format",
"check:lint": "eslint .",
Expand Down
25 changes: 25 additions & 0 deletions scripts/createUniversalPackage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env tsx

import { writeFileSync, mkdirSync } from "fs";
import { resolve } from "path";

const distDir = resolve("dist");

/**
* Node uses the package.json to know whether files with a .js extensions
* should be interpreted as CommonJS or ESM.
*/
// ESM package.json
const esmPath = resolve(distDir, "esm", "package.json");
mkdirSync(resolve(distDir, "esm"), { recursive: true });
writeFileSync(esmPath, JSON.stringify({ type: "module" }));

// CJS package.json
const cjsPath = resolve(distDir, "cjs", "package.json");
mkdirSync(resolve(distDir, "cjs"), { recursive: true });
writeFileSync(cjsPath, JSON.stringify({ type: "commonjs" }));

// Create a dist/index.js file that imports the ESM index.js file
// To minimize breaking changes from pre-universal package time.
const indexPath = resolve(distDir, "index.js");
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just out of precaution in case some of our deployments end up looking for dist/index.js... can be removed though

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally think its fine to have it.

writeFileSync(indexPath, `import "./esm/index.js";`);
22 changes: 22 additions & 0 deletions scripts/updatePackageVersion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env node

import { readFileSync, writeFileSync } from "fs";
import { join } from "path";

// Read package.json
const packageJsonPath = join(import.meta.dirname, "..", "package.json");
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as {
version: string;
};

// Define the packageInfo.ts content
const packageInfoContent = `// This file was generated by scripts/updatePackageVersion.ts - Do not edit it manually.
export const packageInfo = {
version: "${packageJson.version}",
mcpServerName: "MongoDB MCP Server",
};
`;

// Write to packageInfo.ts
const packageInfoPath = join(import.meta.dirname, "..", "src", "common", "packageInfo.ts");
writeFileSync(packageInfoPath, packageInfoContent);
5 changes: 2 additions & 3 deletions src/common/packageInfo.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import packageJson from "../../package.json" with { type: "json" };

// This file was generated by scripts/updatePackageVersion.ts - Do not edit it manually.
export const packageInfo = {
version: packageJson.version,
version: "0.2.0",
mcpServerName: "MongoDB MCP Server",
};
4 changes: 4 additions & 0 deletions src/lib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { Server, type ServerOptions } from "./server.js";
export { Telemetry } from "./telemetry/telemetry.js";
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can consider exporting a single class which would make the setup easier but I'll leave that to future followups

export { Session, type SessionOptions } from "./common/session.js";
export type { UserConfig, ConnectOptions } from "./common/config.js";
33 changes: 20 additions & 13 deletions src/resources/common/config.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
import { ReactiveResource } from "../resource.js";
import { config } from "../../common/config.js";
import type { UserConfig } from "../../common/config.js";
import type { Server } from "../../server.js";
import type { Telemetry } from "../../telemetry/telemetry.js";

export class ConfigResource extends ReactiveResource(
{
name: "config",
uri: "config://config",
config: {
description:
"Server configuration, supplied by the user either as environment variables or as startup arguments",
},
},
{
initial: { ...config },
events: [],
export class ConfigResource extends ReactiveResource<UserConfig, readonly []> {
constructor(server: Server, telemetry: Telemetry) {
super(
{
name: "config",
uri: "config://config",
config: {
description:
"Server configuration, supplied by the user either as environment variables or as startup arguments",
},
},
{
initial: { ...config },
events: [],
},
server,
telemetry
);
}
) {
reduce(eventName: undefined, event: undefined): UserConfig {
void eventName;
void event;
Expand Down
36 changes: 23 additions & 13 deletions src/resources/common/debug.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { ReactiveResource } from "../resource.js";
import type { Server } from "../../server.js";
import type { Telemetry } from "../../telemetry/telemetry.js";

type ConnectionStateDebuggingInformation = {
readonly tag: "connected" | "connecting" | "disconnected" | "errored";
Expand All @@ -8,20 +10,28 @@ type ConnectionStateDebuggingInformation = {
readonly errorReason?: string;
};

export class DebugResource extends ReactiveResource(
{
name: "debug-mongodb",
uri: "debug://mongodb",
config: {
description:
"Debugging information for MongoDB connectivity issues. Tracks the last connectivity error and attempt information.",
},
},
{
initial: { tag: "disconnected" } as ConnectionStateDebuggingInformation,
events: ["connect", "disconnect", "close", "connection-error"],
export class DebugResource extends ReactiveResource<
ConnectionStateDebuggingInformation,
readonly ["connect", "disconnect", "close", "connection-error"]
> {
constructor(server: Server, telemetry: Telemetry) {
super(
{
name: "debug-mongodb",
uri: "debug://mongodb",
config: {
description:
"Debugging information for MongoDB connectivity issues. Tracks the last connectivity error and attempt information.",
},
},
{
initial: { tag: "disconnected" },
events: ["connect", "disconnect", "close", "connection-error"],
},
server,
telemetry
);
}
) {
reduce(
eventName: "connect" | "disconnect" | "close" | "connection-error",
event: string | undefined
Expand Down
Loading
Loading