mongodb-mcp-v3-migration
Migrates external consumer code from the mongodb-mcp-server v1/v2 single-package API to the v3 scoped-package structure. In v3, mongodb-mcp-server is a binary-only package (npx / MCPB); library embedding uses @mongodb-js/mcp-cli, @mongodb-js/mcp-core, @mongodb-js/mcp-http-runners, @mongodb-js/mcp-tools-*, and the other @mongodb-js/mcp-* packages. Use when asked to migrate a project embedding or…
12 findings in 2 files. Read them before installing.
- References credential files or secrets on disk · skills/mongodb-mcp-v3-migration/SKILL.md:28
> (connections, exports, API client, telemetry, metrics, keychain) is built once per - References credential files or secrets on disk · skills/mongodb-mcp-v3-migration/SKILL.md:68
| Host MCP over stdio | `@mongodb-js/mcp-core` | `StdioRunner`, `SessionStore`, `Keychain`, `Elicitation`, `N - References credential files or secrets on disk · skills/mongodb-mcp-v3-migration/SKILL.md:205
build. App-level services (`keychain`, `connectionStore`, `exportsManager`, `apiClient`, - References credential files or secrets on disk · skills/mongodb-mcp-v3-migration/SKILL.md:284
- new LoggerBase(keychain); // also ConsoleLogger, DiskLogger - References credential files or secrets on disk · skills/mongodb-mcp-v3-migration/SKILL.md:285
+ new LoggerBase({ keychain }); - References credential files or secrets on disk · skills/mongodb-mcp-v3-migration/SKILL.md:288
+ new CompositeLogger({ loggers: [a, b], keychain }); - References credential files or secrets on disk · skills/mongodb-mcp-v3-migration/SKILL.md:311
+ keychain, - References credential files or secrets on disk · skills/mongodb-mcp-v3-migration/SKILL.md:318
`getCommonProperties()`, calling `super`. `keychain` and `serverMetadata` are now - References credential files or secrets on disk · skills/mongodb-mcp-v3-migration/SKILL.md:376
const keychain = createKeychainFromConfig({ config }); // @mongodb-js/mcp-cli - References credential files or secrets on disk · skills/mongodb-mcp-v3-migration/SKILL.md:377
const logger = await createLoggerFromConfig({ config, keychain }); - References credential files or secrets on disk · skills/mongodb-mcp-v3-migration/SKILL.md:404
Names that survive but changed shape: `Keychain` (now `IRedactor`-compatible — - References credential files or secrets on disk · skills/mongodb-mcp-v3-migration/scripts/inventory-consumer-code.sh:32
StdioRunner|CliStdioRunner|SessionStore|Keychain|Elicitation|NoopTelemetry|InMemoryTransport|NoopLogger|toToolExecutionContext|formatUntrustedData) echo "@mongo
This is a pattern scan of the skill folder, not a judgment of intent. Some matches are legitimate (an installer, a quoted warning). Checked on Oct 21, 2026. How the scan works.
npx degit mongodb-js/mongodb-mcp-server/skills/mongodb-mcp-v3-migration .claude/skills/mongodb-mcp-v3-migration12 alternatives · Best backend skills · Author? Prove this skill
SKILL.md
# MongoDB MCP Server v1 → v3 migration
This skill migrates **consumer code**: projects that embed, customize, or extend
`mongodb-mcp-server` as a library (custom CLIs, HTTP hosts, custom tools, selective tool
registration, request-scoped configuration). It runs in the **consumer's repository**, not the
mongodb-mcp-server repo.
**`MCP_SERVER_LIBRARY.md` now documents the v3 API surface** (the scoped `@mongodb-js/mcp-*`
packages, `runMcpCli`, `CliServer`, `MCPHttpServer.createServerForRequest`, `ToolBase`/
`ToolClass`, …). Use it as the reference for the migration target; the before/after
sections in this skill and its inventory script map v1/v2 consumer code onto that surface.
> **v3 is sessionless.** The v3 server has no `Session` / `CliSession` object and no
> per-client session state anywhere. Each HTTP request (or stdio connection) gets a fresh
> **request-scoped** `CliServer` built by `createServerFromConfig`; every heavy dependency
> (connections, exports, API client, telemetry, metrics, keychain) is built once per
> process inside `SharedServerServices` and shared. Tools/resources read services off
> `this.server` (there is no `session`), and per-client identity travels on the tool
> request (`ToolExecutionContext.request.clientInfo`) rather than on a session object.
## The core rule
**`mongodb-mcp-server` is not a library in v3.**
- End users: `npx mongodb-mcp-server` or the MCPB binary only.
- **Do not** `npm install mongodb-mcp-server` and `import { … } from "mongodb-mcp-server"` in application code.
- **Do not** use the legacy `mongodb-mcp-server/tools` or `mongodb-mcp-server/web` entry points.
Embed via the scoped packages instead: **`@mongodb-js/mcp-cli`** (custom CLI), **`@mongodb-js/mcp-*`** for everything else.
## Step 1 — Inventory consumer code
```bash
# repo root of the consumer project; resolves skill-relative scripts against this skill's dir
scripts/inventory-consumer-code.sh .
```
The script lists every file that imports `mongodb-mcp-server`, shows the matched import
lines, and classifies each imported symbol to its v3 package. Anything reported as
`unrecognized — manual review` is a symbol the table doesn't know: look it up in the
v3 migration guide and the package's API report before deciding.
Then get the full picture of every usage site:
```bash
rg -n 'mongodb-mcp-server|from "mongodb-mcp-server"|require\("mongodb-mcp-server"' --glob '!node_modules' --glob '!dist' .
```
## Step 2 — Classify the use case
Pick the row(s) that match what the consumer does; install those packages (v3):
| Use case | `npm install` | Primary v3 imports |
| --------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| Custom CLI (most v1 embeds) | `@mongodb-js/mcp-cli` + needed tool packages | `runMcpCli`, `createRunnerFromConfig`, `create*FromConfig`, `Resources`, `CliServer` |
| Host MCP over stdio | `@mongodb-js/mcp-core` | `StdioRunner`, `SessionStore`, `Keychain`, `Elicitation`, `NoopTelemetry`, `InMemoryTransport` |
| Host MCP over HTTP | `@mongodb-js/mcp-http-runners` `@mongodb-js/mcp-core` | `StreamableHttpRunner`, `MCPHttpServer`, `MonitoringServer` |
| Embed server (advanced) | cli + core + http-runners + metrics + logging + telemetry + tools | `CliServer`, `createSharedServicesFromConfig`, `createServerFromConfig`, `createRunnerFromConfig`, `startRunner` |
| Config parsing / overrides | `@mongodb-js/mcp-cli` | `UserConfig`, `UserConfigSchema`, `parseUserConfig`, `applyConfigOverrides`, `configRegistry` |
| Custom tools (any category) | `@mongodb-js/mcp-core` `@mongodb-js/mcp-types` | `ToolBase`, `ToolClass`, `OperationType`, `ToolCategory` |
| MongoDB tools + connections | `@mongodb-js/mcp-tools-mongodb` | `FindTool`, `MongoDBToolBase`, `MCPConnectionManager`, `ErrorCodes`, `MongoDBError` |
| Atlas Admin API tools | `@mongodb-js/mcp-tools-atlas` `@mongodb-js/mcp-atlas-api-client` | `AtlasTools`, `ApiClient`, `ClientCredentialsAuthProvider` |
| Atlas Local tools | `@mongodb-js/mcp-tools-atlas-local` | `AtlasLocalTools`, `createAtlasLocalClient` |
| Assistant / knowledge tools | `@mongodb-js/mcp-tools-assistant` | `AssistantTools` |
| Telemetry | `@mongodb-js/mcp-atlas-telemetry` | `AtlasTelemetry`, `EventCache`, `TelemetryConfig` |
| Logging | `@mongodb-js/mcp-logging` | `ConsoleLogger`, `DiskLogger`, `McpLogger` |
| Metrics | `@mongodb-js/mcp-metrics` | `PrometheusMetrics`, `createDefaultMetrics` |
| MCP UI resources | `@mongodb-js/mcp-ui` | `UIRegistry` |
| Shared types | `@mongodb-js/mcp-types` | `TransportRequestContext`, `ITransportRunner`, `ToolServer`, `ToolServices`, `ServerMetadata` |
## Step 3 — Install and migrate the big three use cases
### 3a. Custom CLI → `runMcpCli`
Most v1 embeds become one `runMcpCli` call (same flow as the official v3 binary):
parse config → handlers → create server → start stdio/HTTP.
```typescript
import {
runMcpCli,
Resources,
DryRunHandler,
HelpHandler,
VersionHandler,
} from "@mongodb-js/mcp-cli";
import { MongoDBTools } from "@mongodb-js/mcp-tools-mongodb";
import { AtlasTools } from "@mongodb-js/mcp-tools-atlas";
import type { ServerMetadata } from "@mongodb-js/mcp-types";
/** Ideally read/generated from package.json */
const serverMetadata: ServerMetadata = {
mcpServerName: "my-product-mcp",
version: "1.0.0",
engines: { node: ">=24" },
};
const tools = [...MongoDBTools, ...AtlasTools /*, MyTool */];
await runMcpCli({
args: process.argv.slice(2),
serverMetadata,
consoleLogger: console,
onExit: (code) => process.exit(code),
tools,
resources: Resources,
handlers: [
new HelpHandler(),
new VersionHandler(),
new DryRunHandler({ tools, resources: Resources }),
],
});
```
Escalation ladder if they need more control: `createServerFromConfig` / `createRunnerFromConfig` + `startRunner`
(both `@mongodb-js/mcp-cli`) → `CliServer` + `@mongodb-js/mcp-http-runners` for per-request
HTTP.
### 3b. HTTP host → `MCPHttpServer` + `StreamableHttpRunner`
Per-request server creation **moved off the runner**. In v1,
`createServerForRequest` lived on `StreamableHttpRunner`; in v3 it lives on
**`MCPHttpServer`**. Runners no longer accept `userConfig` or build the server — build the
server first, attach transport. The simplest path is the CLI's own
`CliMcpHttpServer` + `createHttpTransportRunnerFromConfig`, which wires an
`MCPHttpServer` that builds a fresh request-scoped `CliServer` per request from
`SharedServerServices`:
```typescript
import {
createSharedServicesFromConfig,
createHttpTransportRunnerFromConfig,
} from "@mongodb-js/mcp-cli";
const sharedServices = await createSharedServicesFromConfig({
config,
serverMetadata,
tools,
resources,
logger,
});
const runner = createHttpTransportRunnerFromConfig(sharedServices);
await runner.start();
```
To also apply per-request config overrides or control connection isolation, subclass
`MCPHttpServer` and override `createServerForRequest` to return a request-scoped
`CliServer` built with `createServerFromConfig`.
```diff
- class CustomRunner extends StreamableHttpRunner {
- protected override async createServerForRequest({ request }) {
- return this.createServer({ userConfig: sessionConfig });
- }
- }
+ const connectionScope = (request: TransportRequestContext): string | undefined => {
+ // Fail closed: a missing / non-string / empty sub is not a usable principal.
+ const sub = request.authInfo?.extra?.sub;
+ if (typeof sub !== "string" || sub === "") {
+ return undefined;
+ }
+ // JSON-encode the tuple so it is injective regardless of the claim values
+ // (a sub containing a delimiter or quote cannot collide).
+ return `user:${JSON.stringify([request.authInfo.clientId, sub])}`;
+ };
+ class MyMCPHttpServer extends MCPHttpServer<CliServer> {
+ protected override async createServerForRequest(
+ request: TransportRequestContext
+ ): Promise<CliServer> {
+ const config = applyConfigOverrides({ baseConfig: this.sharedServices.config, request });
+ return createServerFromConfig({ config, sharedServices: this.sharedServices, request, connectionScope });
+ }
+ }
+ const mcpHttpServer = new MyMCPHttpServer({
+ options: {
+ http: {
+ host: config.httpHost,
+ port: config.httpPort,
+ bodyLimit: config.httpBodyLimit,
+ headers: config.httpHeaders,
+ responseType: config.httpResponseType,
+ },
+ },
+ logger,
+ metrics,
+ });
+ const runner = new StreamableHttpRunner({ logger, metrics, mcpHttpServer });
```
Note the v3 `MCPHttpServer` takes `options.http` and an optional `sessionOptions` for the
legacy 2025-era lifecycle — there is no `session:` block and no `SessionStore` to
build. App-level services (`keychain`, `connectionStore`, `exportsManager`, `apiClient`,
`telemetry`, …) are built once by `createSharedServicesFromConfig` and passed in as
`SharedServerServices`; the request-scoped server holds no per-client session state
(hosts that require verified identity enforce it in their own middleware and inject it
as `req.auth`, which both the modern and legacy paths forward as the request's
`authInfo`; connections are scoped per request via the `connectionScope` policy, which
keys on the request's verified identity). See
[Use Case 2](../MCP_SERVER_LIBRARY.md#use-case-2-request-scoped-configuration).
Still may `extends StreamableHttpRunner` to customize `start()`/`close()` or bundle the
wiring in a constructor — just don't override `createServerForRequest` there.
Stdio, for completeness: `new StdioRunner({ userConfig: config })` → subclass
`StdioRunner` (or use `CliStdioRunner`) and override `createServer()` (import
`@mongodb-js/mcp-core`; the constructor takes only `{ logger }`). The runner serves
through the SDK's `serveStdio` entry (protocol revision 2026-07-28 and 2025-era):
`createServer()` returns a **registered** `CliServer` (`await server.register()` before
returning `server.mcpServer`), built fresh per stdio connection.
### 3c. Config
```diff
- import { parseUserConfig, applyConfigOverrides, type UserConfig } from "mongodb-mcp-server";
+ import { parseUserConfig, applyConfigOverrides, type UserConfig } from "@mongodb-js/mcp-cli";
- applyConfigOverrides({ baseConfig, request?: RequestContext });
+ applyConfigOverrides({ baseConfig, request?: TransportRequestContext }); // type from @mongodb-js/mcp-types
```
`parseArgsWithCliOptions` → `parseUserConfig`. Config moved from the server onto the
request-scoped server: **`server.userConfig` → `server.config`** (there is no session
object — tools/resources read config off their construction-time `this.server`).
`applyConfigOverrides` applies request-level overrides to a base `UserConfig`; on HTTP
each request produces its own config via `applyConfigOverrides({ baseConfig, request })`.
## Step 4 — Migrate remaining symbols
### Renamed symbols
| v1 (old) | v3 (new) | Package |
| -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `Server` / `ServerOptions` | `CliServer` / `CliServerOptions` (request-scoped) | `@mongodb-js/mcp-cli` |
| `Session` / `SessionOptions` | **removed** — the per-client session is gone. Config now lives on the request-scoped server (`server.config`); per-client identity travels on the tool request (`ToolExecutionContext.request.clientInfo`). No `CliSession` exists in v3. | — |
| `Telemetry` | `AtlasTelemetry` | `@mongodb-js/mcp-atlas-telemetry` |
| `BaseEvent` | `TelemetryBaseEvent` | `@mongodb-js/mcp-atlas-telemetry` |
| `CommonProperties` | `TelemetryCommonProperties` | `@mongodb-js/mcp-atlas-telemetry` |
| `NullLogger` | `NoopLogger` | `@mongodb-js/mcp-core` |
| `RequestContext` | `TransportRequestContext` | `@mongodb-js/mcp-types` |
| `TransportRunnerBase` | `ITransportRunner` | `@mongodb-js/mcp-types` |
| `Metrics<T>` / `DefaultMetrics` | `IMetrics<T>` / `DefaultMetricDefinitions` | `@mongodb-js/mcp-types` |
| `MCPHttpServerConstructorArgs` | `MCPHttpServerOptions` | `@mongodb-js/mcp-http-runners` |
| `MonitoringServerConstructorArgs` | `MonitoringServerOptions` | `@mongodb-js/mcp-http-runners` |
| `StreamableHttpTransportRunnerConfig` | `StreamableHttpRunnerOptions` + wired `MCPHttpServer` | `@mongodb-js/mcp-http-runners` |
| `defaultCreateApiClient` | `createApiClientFromConfig` or construct `ApiClient` | `@mongodb-js/mcp-cli` / `@mongodb-js/mcp-atlas-api-client` |
| `defaultCreateAtlasLocalClient` | `createAtlasLocalClient` | `@mongodb-js/mcp-tools-atlas-local` |
| `defaultCreateConnectionManager` / `createMCPConnectionManager` | `createConnectionManagerFromConfig` or `new MCPConnectionManager({...})` | `@mongodb-js/mcp-cli` / `@mongodb-js/mcp-tools-mongodb` |
| `createDefaultMcpHttpServer` / `createDefaultMonitoringServer` / `createDefaultSessionStore` | `new MCPHttpServer(...)` / `new MonitoringServer(...)` / `new SessionStore(...)` | `@mongodb-js/mcp-http-runners` / `@mongodb-js/mcp-core` |
| `createServicesFromUserConfig` | `createServerFromConfig` + `createRunnerFromConfig` | `@mongodb-js/mcp-cli` |
| `parseArgsWithCliOptions` Shown under the source repo’s Apache-2.0 license. Read the full folder on GitHub.