v2 backcompat: server legacy package (#2206)
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
This commit is contained in:
parent
83a54a454d
commit
e03bca90c1
55 changed files with 5797 additions and 71 deletions
5
.changeset/add-server-legacy-package.md
Normal file
5
.changeset/add-server-legacy-package.md
Normal 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.
|
||||
5
.changeset/codemod-resolve-legacy-imports.md
Normal file
5
.changeset/codemod-resolve-legacy-imports.md
Normal 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.
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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> = {};
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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': {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
|
|||
39
packages/server-legacy/README.md
Normal file
39
packages/server-legacy/README.md
Normal 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
|
||||
12
packages/server-legacy/eslint.config.mjs
Normal file
12
packages/server-legacy/eslint.config.mjs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// @ts-check
|
||||
|
||||
import baseConfig from '@modelcontextprotocol/eslint-config';
|
||||
|
||||
export default [
|
||||
...baseConfig,
|
||||
{
|
||||
settings: {
|
||||
'import/internal-regex': '^@modelcontextprotocol/core'
|
||||
}
|
||||
}
|
||||
];
|
||||
104
packages/server-legacy/package.json
Normal file
104
packages/server-legacy/package.json
Normal 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
Loading…
Reference in a new issue