v2 backcompat: server legacy package (#2206)

Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
This commit is contained in:
Konstantin Konstantinov 2026-06-01 17:33:32 +03:00 • committed by GitHub
commit e03bca90c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
55 changed files with 5797 additions and 71 deletions

View file

@ -0,0 +1,5 @@
---
'@modelcontextprotocol/server-legacy': minor
---
Add @modelcontextprotocol/server-legacy package with frozen v1 SSE transport and OAuth Authorization Server helpers for migration from v1 to v2.

View file

@ -0,0 +1,5 @@
---
'@modelcontextprotocol/codemod': minor
---
Codemod now resolves SSE server and OAuth auth imports to @modelcontextprotocol/server-legacy sub-paths instead of removing them. An info diagnostic suggests eventual migration to v2 equivalents.

View file

@ -17,6 +17,8 @@
"@modelcontextprotocol/hono": "2.0.0-alpha.0",
"@modelcontextprotocol/node": "2.0.0-alpha.0",
"@modelcontextprotocol/server": "2.0.0-alpha.0",
"@modelcontextprotocol/server-legacy": "2.0.0-alpha.0",
"@modelcontextprotocol/codemod": "2.0.0-alpha.0",
"@modelcontextprotocol/test-conformance": "2.0.0-alpha.0",
"@modelcontextprotocol/test-helpers": "2.0.0-alpha.0",
"@modelcontextprotocol/test-integration": "2.0.0-alpha.0"

View file

@ -39,5 +39,5 @@ jobs:
- name: Publish preview packages
run:
pnpm dlx pkg-pr-new publish --packageManager=npm --pnpm './packages/server' './packages/client'
pnpm dlx pkg-pr-new publish --packageManager=npm --pnpm './packages/server' './packages/server-legacy' './packages/client'
'./packages/codemod' './packages/middleware/express' './packages/middleware/fastify' './packages/middleware/hono' './packages/middleware/node'

View file

@ -71,7 +71,7 @@ The [server quickstart](./server-quickstart.md) walks you through building a wea
### Where are the server auth helpers?
Resource Server helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `OAuthTokenVerifier`) are first-class in `@modelcontextprotocol/express`. The Authorization Server helpers (`mcpAuthRouter`, `ProxyOAuthServerProvider`, etc.) have been removed from the core SDK; new code should use a dedicated IdP/OAuth library. Example packages provide a demo with `better-auth`.
Resource Server helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `OAuthTokenVerifier`) are first-class in `@modelcontextprotocol/express`. Authorization Server helpers (`mcpAuthRouter`, `ProxyOAuthServerProvider`, etc.) are available from `@modelcontextprotocol/server-legacy/auth` (deprecated, frozen v1 copy). New code should use a dedicated IdP/OAuth library for the AS. Example packages provide a demo with `better-auth`.
### Why did we remove `server` SSE transport?

View file

@ -53,8 +53,8 @@ Replace all `@modelcontextprotocol/sdk/...` imports using this table.
| `@modelcontextprotocol/sdk/server/index.js` | `@modelcontextprotocol/server` |
| `@modelcontextprotocol/sdk/server/stdio.js` | `@modelcontextprotocol/server/stdio` |
| `@modelcontextprotocol/sdk/server/streamableHttp.js` | `@modelcontextprotocol/node` (class renamed to `NodeStreamableHTTPServerTransport`) OR `@modelcontextprotocol/server` (web-standard `WebStandardStreamableHTTPServerTransport` for Cloudflare Workers, Deno, etc.) |
| `@modelcontextprotocol/sdk/server/sse.js` | REMOVED (migrate to Streamable HTTP) |
| `@modelcontextprotocol/sdk/server/auth/*` | RS helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `OAuthTokenVerifier`) → `@modelcontextprotocol/express`; AS helpers removed (use external IdP/OAuth library) |
| `@modelcontextprotocol/sdk/server/sse.js` | REMOVED (migrate to Streamable HTTP); legacy bridge: `@modelcontextprotocol/server-legacy/sse` |
| `@modelcontextprotocol/sdk/server/auth/*` | RS helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `OAuthTokenVerifier`) → `@modelcontextprotocol/express`; AS helpers (`mcpAuthRouter`, `OAuthServerProvider`, etc.) → `@modelcontextprotocol/server-legacy/auth` (deprecated); migrate AS to an external IdP/OAuth library |
| `@modelcontextprotocol/sdk/server/middleware.js` | `@modelcontextprotocol/express` (signature changed, see section 8) |
### Types / shared imports
@ -328,11 +328,11 @@ new URL(ctx.http?.req?.url).searchParams.get('debug')
### SSE server transport
`SSEServerTransport` removed entirely. Migrate to `NodeStreamableHTTPServerTransport` (from `@modelcontextprotocol/node`). Client-side `SSEClientTransport` still available for connecting to legacy servers.
`SSEServerTransport` removed entirely. Migrate to `NodeStreamableHTTPServerTransport` (from `@modelcontextprotocol/node`). Client-side `SSEClientTransport` still available for connecting to legacy servers. Legacy bridge: `import { SSEServerTransport } from '@modelcontextprotocol/server-legacy/sse'` (deprecated, frozen v1 copy).
### Server-side auth
Resource Server helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `getOAuthProtectedResourceMetadataUrl`, `OAuthTokenVerifier`) are first-class in `@modelcontextprotocol/express`. Authorization Server helpers (`mcpAuthRouter`, `OAuthServerProvider`, `ProxyOAuthServerProvider`, `authenticateClient`, `allowedMethods`, etc.) are removed from the core SDK; use an external IdP/OAuth library. See `examples/server/src/` for demos.
Resource Server helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `getOAuthProtectedResourceMetadataUrl`, `OAuthTokenVerifier`) are first-class in `@modelcontextprotocol/express`. Authorization Server helpers (`mcpAuthRouter`, `OAuthServerProvider`, `ProxyOAuthServerProvider`, `authenticateClient`, `allowedMethods`, etc.) are available from `@modelcontextprotocol/server-legacy/auth` (deprecated, frozen v1 copy). Migrate AS to an external IdP/OAuth library for production use. See `examples/server/src/` for demos.
### Host header validation (Express)
@ -544,6 +544,6 @@ Validator behavior:
6. Replace plain header objects with `new Headers({...})` and bracket access (`headers['x']`) with `.get()` calls per section 7
7. If using `hostHeaderValidation` from server, update import and signature per section 8
8. If using server SSE transport, migrate to Streamable HTTP
9. If using server auth from the SDK: RS helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`) → `@modelcontextprotocol/express`; AS helpers → external IdP/OAuth library
9. If using server auth from the SDK: RS helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `OAuthTokenVerifier`) → `@modelcontextprotocol/express`; AS helpers → `@modelcontextprotocol/server-legacy/auth` (deprecated); migrate AS to external IdP/OAuth library
10. If relying on `listTools()`/`listPrompts()`/etc. throwing on missing capabilities, set `enforceStrictCapabilities: true`
11. Verify: build with `tsc` / run tests

View file

@ -113,6 +113,14 @@ const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: ()
The SSE transport has been removed from the server. Servers should migrate to Streamable HTTP. The client-side SSE transport remains available for connecting to legacy SSE servers.
If you need a temporary bridge during migration, `@modelcontextprotocol/server-legacy/sse` provides a frozen copy of the v1 `SSEServerTransport`:
```typescript
import { SSEServerTransport } from '@modelcontextprotocol/server-legacy/sse';
```
This package is deprecated and will not receive new features.
### `WebSocketClientTransport` removed
`WebSocketClientTransport` has been removed. WebSocket is not a spec-defined MCP transport, and keeping it in the SDK encouraged transport proliferation without a conformance baseline.
@ -135,7 +143,7 @@ const transport = new StreamableHTTPClientTransport(new URL('http://localhost:30
### Server auth split
Resource Server helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `getOAuthProtectedResourceMetadataUrl`, `OAuthTokenVerifier`) are now first-class in `@modelcontextprotocol/express`.
Resource Server helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `getOAuthProtectedResourceMetadataUrl`, `OAuthTokenVerifier`) are first-class in `@modelcontextprotocol/express`.
Authorization Server helpers (`mcpAuthRouter`, `OAuthServerProvider`, `ProxyOAuthServerProvider`, `authenticateClient`, `allowedMethods`, etc.) have been removed from the core SDK; new code should use a dedicated IdP/OAuth library. See the [examples](../examples/server/src/) for a working demo with `better-auth`.
@ -812,6 +820,14 @@ The following individual error classes have been removed in favor of `OAuthError
The `OAUTH_ERRORS` constant has also been removed.
If you need the v1 OAuth error classes and `mcpAuthRouter` during migration, `@modelcontextprotocol/server-legacy/auth` provides a frozen copy:
```typescript
import { mcpAuthRouter, InvalidClientError } from '@modelcontextprotocol/server-legacy/auth';
```
This package is deprecated and will not receive new features. Use a dedicated OAuth provider in production.
**Before (v1):**
```typescript

View file

@ -25,20 +25,20 @@ pnpm tsx src/simpleStreamableHttp.ts
## Example index
| Scenario | Description | File |
| ----------------------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Streamable HTTP server (stateful) | Feature-rich server with tools/resources/prompts, logging, tasks, sampling, and optional OAuth. | [`src/simpleStreamableHttp.ts`](src/simpleStreamableHttp.ts) |
| Streamable HTTP server (stateless) | No session tracking; good for simple API-style servers. | [`src/simpleStatelessStreamableHttp.ts`](src/simpleStatelessStreamableHttp.ts) |
| Resource-Server-only auth | Minimal OAuth RS using SDK's `mcpAuthMetadataRouter` + `requireBearerAuth` (no better-auth). | [`src/resourceServerOnly.ts`](src/resourceServerOnly.ts) |
| JSON response mode (no SSE) | Streamable HTTP with JSON-only responses and limited notifications. | [`src/jsonResponseStreamableHttp.ts`](src/jsonResponseStreamableHttp.ts) |
| Server notifications over Streamable HTTP | Demonstrates server-initiated notifications via GET+SSE. | [`src/standaloneSseWithGetStreamableHttp.ts`](src/standaloneSseWithGetStreamableHttp.ts) |
| Output schema server | Demonstrates tool output validation with structured output schemas. | [`src/mcpServerOutputSchema.ts`](src/mcpServerOutputSchema.ts) |
| Form elicitation server | Collects **non-sensitive** user input via schema-driven forms. | [`src/elicitationFormExample.ts`](src/elicitationFormExample.ts) |
| URL elicitation server | Secure browser-based flows for **sensitive** input (API keys, OAuth, payments). | [`src/elicitationUrlExample.ts`](src/elicitationUrlExample.ts) |
| Sampling + tasks server | Demonstrates sampling and experimental task-based execution. | [`src/toolWithSampleServer.ts`](src/toolWithSampleServer.ts) |
| Task interactive server | Task-based execution with interactive server→client requests. | [`src/simpleTaskInteractive.ts`](src/simpleTaskInteractive.ts) |
| Hono Streamable HTTP server | Streamable HTTP server built with Hono instead of Express. | [`src/honoWebStandardStreamableHttp.ts`](src/honoWebStandardStreamableHttp.ts) |
| SSE polling demo server | Legacy SSE server intended for polling demos. | [`src/ssePollingExample.ts`](src/ssePollingExample.ts) |
| Scenario | Description | File |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Streamable HTTP server (stateful) | Feature-rich server with tools/resources/prompts, logging, tasks, sampling, and optional OAuth. | [`src/simpleStreamableHttp.ts`](src/simpleStreamableHttp.ts) |
| Streamable HTTP server (stateless) | No session tracking; good for simple API-style servers. | [`src/simpleStatelessStreamableHttp.ts`](src/simpleStatelessStreamableHttp.ts) |
| Resource-Server-only auth | Minimal OAuth RS using `mcpAuthMetadataRouter` + `requireBearerAuth` from `@modelcontextprotocol/express` (no better-auth). | [`src/resourceServerOnly.ts`](src/resourceServerOnly.ts) |
| JSON response mode (no SSE) | Streamable HTTP with JSON-only responses and limited notifications. | [`src/jsonResponseStreamableHttp.ts`](src/jsonResponseStreamableHttp.ts) |
| Server notifications over Streamable HTTP | Demonstrates server-initiated notifications via GET+SSE. | [`src/standaloneSseWithGetStreamableHttp.ts`](src/standaloneSseWithGetStreamableHttp.ts) |
| Output schema server | Demonstrates tool output validation with structured output schemas. | [`src/mcpServerOutputSchema.ts`](src/mcpServerOutputSchema.ts) |
| Form elicitation server | Collects **non-sensitive** user input via schema-driven forms. | [`src/elicitationFormExample.ts`](src/elicitationFormExample.ts) |
| URL elicitation server | Secure browser-based flows for **sensitive** input (API keys, OAuth, payments). | [`src/elicitationUrlExample.ts`](src/elicitationUrlExample.ts) |
| Sampling + tasks server | Demonstrates sampling and experimental task-based execution. | [`src/toolWithSampleServer.ts`](src/toolWithSampleServer.ts) |
| Task interactive server | Task-based execution with interactive server→client requests. | [`src/simpleTaskInteractive.ts`](src/simpleTaskInteractive.ts) |
| Hono Streamable HTTP server | Streamable HTTP server built with Hono instead of Express. | [`src/honoWebStandardStreamableHttp.ts`](src/honoWebStandardStreamableHttp.ts) |
| SSE polling demo server | Legacy SSE server intended for polling demos. | [`src/ssePollingExample.ts`](src/ssePollingExample.ts) |
## OAuth demo flags (Streamable HTTP server)

View file

@ -9,7 +9,8 @@ const PACKAGE_DIRS: Record<string, string> = {
'@modelcontextprotocol/client': 'client',
'@modelcontextprotocol/server': 'server',
'@modelcontextprotocol/node': 'middleware/node',
'@modelcontextprotocol/express': 'middleware/express'
'@modelcontextprotocol/express': 'middleware/express',
'@modelcontextprotocol/server-legacy': 'server-legacy'
};
const versions: Record<string, string> = {};

View file

@ -3,5 +3,6 @@ export const V2_PACKAGE_VERSIONS: Record<string, string> = {
'@modelcontextprotocol/client': '^2.0.0-alpha.2',
'@modelcontextprotocol/server': '^2.0.0-alpha.2',
'@modelcontextprotocol/node': '^2.0.0-alpha.2',
'@modelcontextprotocol/express': '^2.0.0-alpha.2'
'@modelcontextprotocol/express': '^2.0.0-alpha.2',
'@modelcontextprotocol/server-legacy': '^2.0.0-alpha.2'
};

View file

@ -7,6 +7,8 @@ export interface ImportMapping {
removalMessage?: string;
/** No entries currently set this; scaffolding for when a v1 symbol has no v2 equivalent yet. */
isV2Gap?: boolean;
/** Emitted as an info diagnostic after a successful move, suggesting eventual migration to v2 equivalents. */
migrationHint?: string;
/**
* Subpath suffix appended after `RESOLVE_BY_CONTEXT` resolves the base package (e.g. `/validators/ajv`).
* The final target becomes `@modelcontextprotocol/{client,server}<subpathSuffix>`.
@ -74,9 +76,10 @@ export const IMPORT_MAP: Record<string, ImportMapping> = {
status: 'moved'
},
'@modelcontextprotocol/sdk/server/sse.js': {
target: '',
status: 'removed',
removalMessage: 'SSE server transport removed in v2. Migrate to NodeStreamableHTTPServerTransport from @modelcontextprotocol/node.'
target: '@modelcontextprotocol/server-legacy/sse',
status: 'moved',
migrationHint:
'SSEServerTransport is deprecated. Migrate to NodeStreamableHTTPServerTransport from @modelcontextprotocol/node or WebStandardStreamableHTTPServerTransport from @modelcontextprotocol/server.'
},
'@modelcontextprotocol/sdk/server/middleware.js': {
target: '@modelcontextprotocol/express',
@ -90,34 +93,29 @@ export const IMPORT_MAP: Record<string, ImportMapping> = {
},
'@modelcontextprotocol/sdk/server/auth/types.js': {
target: '',
status: 'removed',
removalMessage:
'Server auth removed in v2. AuthInfo is now re-exported by @modelcontextprotocol/client and @modelcontextprotocol/server.'
target: '@modelcontextprotocol/server-legacy/auth',
status: 'moved',
migrationHint: 'Legacy auth types. AuthInfo is also re-exported by @modelcontextprotocol/server.'
},
'@modelcontextprotocol/sdk/server/auth/provider.js': {
target: '',
status: 'removed',
removalMessage:
'Server auth provider removed in v2. For Resource-Server auth (token verification), see @modelcontextprotocol/express. For full OAuth AS, see @modelcontextprotocol/server-auth-legacy (PR #1908).'
target: '@modelcontextprotocol/server-legacy/auth',
status: 'moved',
migrationHint: 'Legacy OAuth AS provider. For RS-only auth, see requireBearerAuth from @modelcontextprotocol/express.'
},
'@modelcontextprotocol/sdk/server/auth/router.js': {
target: '',
status: 'removed',
removalMessage:
'Server auth router removed in v2. For metadata endpoints, see mcpAuthMetadataRouter from @modelcontextprotocol/express. For full OAuth AS router, see @modelcontextprotocol/server-auth-legacy (PR #1908).'
target: '@modelcontextprotocol/server-legacy/auth',
status: 'moved',
migrationHint: 'Legacy OAuth AS router. For metadata-only endpoints, see mcpAuthMetadataRouter from @modelcontextprotocol/express.'
},
'@modelcontextprotocol/sdk/server/auth/middleware.js': {
target: '',
status: 'removed',
removalMessage:
'Server auth middleware removed in v2. For bearer token validation, see requireBearerAuth from @modelcontextprotocol/express. For full OAuth AS, see @modelcontextprotocol/server-auth-legacy (PR #1908).'
target: '@modelcontextprotocol/server-legacy/auth',
status: 'moved',
migrationHint: 'Legacy OAuth AS middleware. For bearer-only auth, see requireBearerAuth from @modelcontextprotocol/express.'
},
'@modelcontextprotocol/sdk/server/auth/errors.js': {
target: '',
status: 'removed',
removalMessage:
'Auth error subclasses consolidated in v2. Use OAuthError + OAuthErrorCode from @modelcontextprotocol/server. See also @modelcontextprotocol/server-auth-legacy (PR #1908).'
target: '@modelcontextprotocol/server-legacy/auth',
status: 'moved',
migrationHint: 'Legacy error subclasses. v2 consolidates to OAuthError + OAuthErrorCode in @modelcontextprotocol/server.'
},
'@modelcontextprotocol/sdk/types.js': {

View file

@ -2,7 +2,7 @@ import type { SourceFile } from 'ts-morph';
import type { Diagnostic, Transform, TransformContext, TransformResult } from '../../../types.js';
import { renameAllReferences } from '../../../utils/astUtils.js';
import { v2Gap, warning } from '../../../utils/diagnostics.js';
import { info, v2Gap, warning } from '../../../utils/diagnostics.js';
import { addOrMergeImport, getSdkExports, getSdkImports, isTypeOnlyImport } from '../../../utils/importUtils.js';
import { resolveTypesPackage } from '../../../utils/projectAnalyzer.js';
import { IMPORT_MAP, isAuthImport } from '../mappings/importMap.js';
@ -75,10 +75,9 @@ export const importPathsTransform: Transform = {
if (!mapping && isAuthImport(specifier)) {
mapping = {
target: '',
status: 'removed',
removalMessage:
'Server auth removed in v2. For RS auth, see @modelcontextprotocol/express. For full OAuth AS, see @modelcontextprotocol/server-auth-legacy (PR #1908).'
target: '@modelcontextprotocol/server-legacy/auth',
status: 'moved',
migrationHint: 'Legacy auth module. For RS-only auth, see @modelcontextprotocol/express.'
};
}
@ -156,6 +155,9 @@ export const importPathsTransform: Transform = {
}
}
changesCount++;
if (mapping.migrationHint) {
diagnostics.push(info(filePath, line, mapping.migrationHint));
}
for (const [oldName, newName] of symbolsToRenameInFile) {
renameAllReferences(sourceFile, oldName, newName);
}
@ -172,6 +174,9 @@ export const importPathsTransform: Transform = {
}
imp.remove();
changesCount++;
if (mapping.migrationHint) {
diagnostics.push(info(filePath, line, mapping.migrationHint));
}
for (const [oldName, newName] of symbolsToRenameInFile) {
renameAllReferences(sourceFile, oldName, newName);
}
@ -222,10 +227,9 @@ function rewriteExportDeclarations(
if (!mapping && isAuthImport(specifier)) {
mapping = {
target: '',
status: 'removed',
removalMessage:
'Server auth removed in v2. For RS auth, see @modelcontextprotocol/express. For full OAuth AS, see @modelcontextprotocol/server-auth-legacy (PR #1908).'
target: '@modelcontextprotocol/server-legacy/auth',
status: 'moved',
migrationHint: 'Legacy auth module. For RS-only auth, see @modelcontextprotocol/express.'
};
}
@ -288,6 +292,9 @@ function rewriteExportDeclarations(
}
}
changesCount++;
if (mapping.migrationHint) {
diagnostics.push(info(filePath, line, mapping.migrationHint));
}
}
return changesCount;

View file

@ -49,7 +49,7 @@ function resolveTarget(
| { removed: true; isV2Gap?: boolean; removalMessage?: string }
| null {
const mapping = IMPORT_MAP[specifier];
if (!mapping && isAuthImport(specifier)) return { removed: true };
if (!mapping && isAuthImport(specifier)) return { target: '@modelcontextprotocol/server-legacy/auth' };
if (!mapping) return null;
if (mapping.status === 'removed') return { removed: true, isV2Gap: mapping.isV2Gap, removalMessage: mapping.removalMessage };

View file

@ -23,7 +23,7 @@ afterEach(() => {
});
describe('CLI diagnostic behavior', () => {
it('warnings do not produce errors-level diagnostics', () => {
it('info diagnostics do not produce error-level diagnostics', () => {
const dir = createTempDir();
writeFileSync(
path.join(dir, 'server.ts'),
@ -36,9 +36,9 @@ describe('CLI diagnostic behavior', () => {
const result = run(migration, { targetDir: dir });
const warnings = result.diagnostics.filter(d => d.level === DiagnosticLevel.Warning);
const infos = result.diagnostics.filter(d => d.level === DiagnosticLevel.Info);
const errors = result.diagnostics.filter(d => d.level === DiagnosticLevel.Error);
expect(warnings.length).toBeGreaterThan(0);
expect(infos.length).toBeGreaterThan(0);
expect(errors.length).toBe(0);
});

View file

@ -573,7 +573,7 @@ describe('integration', () => {
expect(result.packageJsonChanges!.removed).toContain('@modelcontextprotocol/sdk');
});
it('emits diagnostics for removed imports', () => {
it('emits info diagnostics for legacy-moved imports', () => {
const dir = createTempDir();
const input = [
`import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';`,
@ -586,7 +586,7 @@ describe('integration', () => {
const result = run(migration, { targetDir: dir });
expect(result.diagnostics.length).toBeGreaterThan(0);
expect(result.diagnostics.some(d => d.level === DiagnosticLevel.Warning)).toBe(true);
expect(result.diagnostics.some(d => d.level === DiagnosticLevel.Info)).toBe(true);
});
it('transform ordering: critical dependencies are maintained', () => {

View file

@ -59,14 +59,19 @@ describe('import-paths transform', () => {
expect(result.diagnostics[0]!.message).toContain('WebSocketClientTransport');
});
it('removes SSE server import with warning', () => {
it('moves SSE server import to server-legacy/sse with info diagnostic', () => {
const input = `import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';\n`;
const ctx: TransformContext = { projectType: 'server' };
const project = new Project({ useInMemoryFileSystem: true });
const sourceFile = project.createSourceFile('test.ts', input);
const result = importPathsTransform.apply(sourceFile, ctx);
const output = sourceFile.getFullText();
expect(output).toContain('@modelcontextprotocol/server-legacy/sse');
expect(output).toContain('SSEServerTransport');
expect(output).not.toContain('@modelcontextprotocol/sdk');
expect(result.changesCount).toBeGreaterThan(0);
expect(result.diagnostics.length).toBeGreaterThan(0);
expect(result.diagnostics[0]!.message).toContain('SSE server transport');
expect(result.diagnostics[0]!.message).toContain('SSEServerTransport is deprecated');
});
it('resolves sdk/types.js based on sibling client imports', () => {
@ -270,13 +275,79 @@ describe('import-paths transform', () => {
expect(result).toContain('NodeStreamableHTTPServerTransport as StreamableHTTPServerTransport');
});
it('removes auth imports with warning', () => {
it('moves auth router import to server-legacy/auth with info diagnostic', () => {
const input = `import { mcpAuthRouter } from '@modelcontextprotocol/sdk/server/auth/router.js';\n`;
const project = new Project({ useInMemoryFileSystem: true });
const sourceFile = project.createSourceFile('test.ts', input);
const result = importPathsTransform.apply(sourceFile, { projectType: 'server' });
const output = sourceFile.getFullText();
expect(output).toContain('@modelcontextprotocol/server-legacy/auth');
expect(output).toContain('mcpAuthRouter');
expect(output).not.toContain('@modelcontextprotocol/sdk');
expect(result.changesCount).toBeGreaterThan(0);
expect(result.diagnostics.length).toBeGreaterThan(0);
expect(result.diagnostics[0]!.message).toContain('auth router removed');
expect(result.diagnostics[0]!.message).toContain('Legacy OAuth AS router');
});
it('moves auth provider import to server-legacy/auth', () => {
const input = `import type { OAuthServerProvider } from '@modelcontextprotocol/sdk/server/auth/provider.js';\n`;
const result = applyTransform(input, { projectType: 'server' });
expect(result).toContain('@modelcontextprotocol/server-legacy/auth');
expect(result).toContain('OAuthServerProvider');
expect(result).not.toContain('@modelcontextprotocol/sdk');
});
it('moves auth middleware import to server-legacy/auth', () => {
const input = `import { requireBearerAuth } from '@modelcontextprotocol/sdk/server/auth/middleware.js';\n`;
const result = applyTransform(input, { projectType: 'server' });
expect(result).toContain('@modelcontextprotocol/server-legacy/auth');
expect(result).toContain('requireBearerAuth');
expect(result).not.toContain('@modelcontextprotocol/sdk');
});
it('moves auth errors import to server-legacy/auth', () => {
const input = `import { InvalidTokenError, OAuthError } from '@modelcontextprotocol/sdk/server/auth/errors.js';\n`;
const result = applyTransform(input, { projectType: 'server' });
expect(result).toContain('@modelcontextprotocol/server-legacy/auth');
expect(result).toContain('InvalidTokenError');
expect(result).toContain('OAuthError');
expect(result).not.toContain('@modelcontextprotocol/sdk');
});
it('consolidates multiple auth subpath imports into single server-legacy/auth import', () => {
const input = [
`import { mcpAuthRouter } from '@modelcontextprotocol/sdk/server/auth/router.js';`,
`import { requireBearerAuth } from '@modelcontextprotocol/sdk/server/auth/middleware.js';`,
''
].join('\n');
const result = applyTransform(input, { projectType: 'server' });
expect(result).toContain('mcpAuthRouter');
expect(result).toContain('requireBearerAuth');
expect(result).toContain('@modelcontextprotocol/server-legacy/auth');
const importLines = result.split('\n').filter((l: string) => l.includes('@modelcontextprotocol/server-legacy/auth'));
expect(importLines.length).toBe(1);
});
it('moves SSE server re-export to server-legacy/sse', () => {
const input = `export { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';\n`;
const project = new Project({ useInMemoryFileSystem: true });
const sourceFile = project.createSourceFile('test.ts', input);
const result = importPathsTransform.apply(sourceFile, { projectType: 'server' });
const output = sourceFile.getFullText();
expect(output).toContain('@modelcontextprotocol/server-legacy/sse');
expect(output).toContain('SSEServerTransport');
expect(result.diagnostics.some(d => d.message.includes('SSEServerTransport is deprecated'))).toBe(true);
});
it('includes server-legacy in usedPackages for SSE import', () => {
const project = new Project({ useInMemoryFileSystem: true });
const sourceFile = project.createSourceFile(
'test.ts',
`import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';\n`
);
const result = importPathsTransform.apply(sourceFile, { projectType: 'server' });
expect(result.usedPackages).toBeDefined();
expect(result.usedPackages!.has('@modelcontextprotocol/server-legacy/sse')).toBe(true);
});
it('handles per-specifier type modifiers', () => {
@ -373,14 +444,17 @@ describe('import-paths transform', () => {
expect(result.diagnostics[0]!.message).toContain('removed');
});
it('handles unknown auth subpath via catch-all', () => {
it('moves unknown auth subpath to server-legacy/auth via catch-all', () => {
const input = `import { SomeType } from '@modelcontextprotocol/sdk/server/auth/handler.js';\n`;
const project = new Project({ useInMemoryFileSystem: true });
const sourceFile = project.createSourceFile('test.ts', input);
const result = importPathsTransform.apply(sourceFile, { projectType: 'server' });
expect(result.changesCount).toBe(1);
expect(sourceFile.getFullText()).not.toContain('@modelcontextprotocol/sdk');
expect(result.diagnostics.some(d => d.message.includes('Server auth removed'))).toBe(true);
const output = sourceFile.getFullText();
expect(output).toContain('@modelcontextprotocol/server-legacy/auth');
expect(output).toContain('SomeType');
expect(output).not.toContain('@modelcontextprotocol/sdk');
expect(result.diagnostics.some(d => d.message.includes('Legacy auth module'))).toBe(true);
});
it('rewrites InMemoryTransport to @modelcontextprotocol/server for server projects', () => {

View file

@ -193,13 +193,22 @@ describe('mock-paths transform', () => {
expect(result.diagnostics[0]!.message).toContain('Unknown SDK dynamic import path');
});
it('emits warning for removed SDK dynamic import path', () => {
it('rewrites SSE dynamic import to server-legacy/sse', () => {
const input = [`const m = await import('@modelcontextprotocol/sdk/server/sse.js');`, ''].join('\n');
const project = new Project({ useInMemoryFileSystem: true });
const sourceFile = project.createSourceFile('test.ts', input);
const result = mockPathsTransform.apply(sourceFile, ctx);
expect(result.changesCount).toBeGreaterThan(0);
expect(sourceFile.getFullText()).toContain('@modelcontextprotocol/server-legacy/sse');
});
it('emits warning for removed SDK dynamic import path', () => {
const input = [`const m = await import('@modelcontextprotocol/sdk/client/websocket.js');`, ''].join('\n');
const project = new Project({ useInMemoryFileSystem: true });
const sourceFile = project.createSourceFile('test.ts', input);
const result = mockPathsTransform.apply(sourceFile, ctx);
expect(result.diagnostics.length).toBeGreaterThan(0);
expect(result.diagnostics[0]!.message).toContain('SSE server transport removed in v2');
expect(result.diagnostics[0]!.message).toContain('WebSocketClientTransport removed in v2');
});
it('emits warning for unknown SDK mock path', () => {

View file

@ -0,0 +1,39 @@
# @modelcontextprotocol/server-legacy
> **Deprecated** — This package is a frozen copy of v1 code for migration purposes only. It will not receive new features and is planned for removal in v3.
Provides two pieces of v1 server functionality removed in v2:
- **SSE Transport** (`./sse`) — The `SSEServerTransport` class, replaced in v2 by `NodeStreamableHTTPServerTransport` (from `@modelcontextprotocol/node`) or `WebStandardStreamableHTTPServerTransport` (from `@modelcontextprotocol/server`)
- **OAuth Authorization Server** (`./auth`) — The `mcpAuthRouter` and related helpers, removed in v2 because MCP servers should use dedicated OAuth providers
## Installation
```bash
npm install @modelcontextprotocol/server-legacy
```
## Usage
### SSE Transport (no Express dependency required)
```ts
import { SSEServerTransport } from '@modelcontextprotocol/server-legacy/sse';
```
### OAuth Auth Router (requires Express)
```ts
import { mcpAuthRouter } from '@modelcontextprotocol/server-legacy/auth';
```
### Everything (requires Express)
```ts
import { SSEServerTransport, mcpAuthRouter } from '@modelcontextprotocol/server-legacy';
```
## Migration
- **SSE → StreamableHTTP**: Use `NodeStreamableHTTPServerTransport` from `@modelcontextprotocol/node` (Node.js) or `WebStandardStreamableHTTPServerTransport` from `@modelcontextprotocol/server` (Web Standard / Cloudflare Workers)
- **Auth router → Dedicated IdP**: Use a dedicated OAuth provider (Auth0, Keycloak, etc.) instead of the built-in OAuth Authorization Server

View file

@ -0,0 +1,12 @@
// @ts-check
import baseConfig from '@modelcontextprotocol/eslint-config';
export default [
...baseConfig,
{
settings: {
'import/internal-regex': '^@modelcontextprotocol/core'
}
}
];

View file

@ -0,0 +1,104 @@
{
"name": "@modelcontextprotocol/server-legacy",
"private": false,
"version": "2.0.0-alpha.2",
"description": "Frozen v1 SSE transport and OAuth Authorization Server helpers for the Model Context Protocol TypeScript SDK. Deprecated; use StreamableHTTP and a dedicated OAuth server in production.",
"deprecated": "This package is a frozen copy of v1's SSE transport and OAuth Authorization Server helpers for migration purposes only. Use StreamableHTTP from @modelcontextprotocol/server and a dedicated OAuth server in production. Will not receive new features.",
"license": "MIT",
"author": "Anthropic, PBC (https://anthropic.com)",
"homepage": "https://modelcontextprotocol.io",
"bugs": "https://github.com/modelcontextprotocol/typescript-sdk/issues",
"type": "module",
"repository": {
"type": "git",
"url": "git+https://github.com/modelcontextprotocol/typescript-sdk.git"
},
"engines": {
"node": ">=20"
},
"keywords": [
"modelcontextprotocol",
"mcp",
"server",
"legacy",
"sse",
"oauth"
],
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs"
},
"./sse": {
"types": "./dist/sse/index.d.mts",
"import": "./dist/sse/index.mjs"
},
"./auth": {
"types": "./dist/auth/index.d.mts",
"import": "./dist/auth/index.mjs"
}
},
"types": "./dist/index.d.mts",
"typesVersions": {
"*": {
"sse": [
"dist/sse/index.d.mts"
],
"auth": [
"dist/auth/index.d.mts"
]
}
},
"files": [
"dist"
],
"scripts": {
"typecheck": "tsgo -p tsconfig.json --noEmit",
"build": "tsdown",
"build:watch": "tsdown --watch",
"prepack": "pnpm run build",
"lint": "eslint src/ && prettier --ignore-path ../../.prettierignore --check .",
"lint:fix": "eslint src/ --fix && prettier --ignore-path ../../.prettierignore --write .",
"check": "pnpm run typecheck && pnpm run lint",
"test": "vitest run",
"test:watch": "vitest"
},
"dependencies": {
"zod": "catalog:runtimeShared",
"raw-body": "catalog:runtimeServerOnly",
"content-type": "catalog:runtimeServerOnly",
"cors": "catalog:runtimeServerOnly",
"express-rate-limit": "^8.2.1",
"pkce-challenge": "catalog:runtimeShared"
},
"peerDependencies": {
"express": "^4.18.0 || ^5.0.0"
},
"peerDependenciesMeta": {
"express": {
"optional": true
}
},
"devDependencies": {
"@modelcontextprotocol/core": "workspace:^",
"@modelcontextprotocol/tsconfig": "workspace:^",
"@modelcontextprotocol/eslint-config": "workspace:^",
"@modelcontextprotocol/vitest-config": "workspace:^",
"@types/content-type": "catalog:devTools",
"@types/cors": "catalog:devTools",
"@types/express": "catalog:devTools",
"@types/express-serve-static-core": "catalog:devTools",
"@types/supertest": "catalog:devTools",
"@eslint/js": "catalog:devTools",
"@typescript/native-preview": "catalog:devTools",
"eslint": "catalog:devTools",
"eslint-config-prettier": "catalog:devTools",
"eslint-plugin-n": "catalog:devTools",
"prettier": "catalog:devTools",
"supertest": "catalog:devTools",
"tsdown": "catalog:devTools",
"typescript": "catalog:devTools",
"typescript-eslint": "catalog:devTools",
"vitest": "catalog:devTools"
}
}

Some files were not shown because too many files have changed in this diff Show more