refactor: make Server class framework-agnostic by moving express to separate module (#1223)
This commit is contained in:
parent
401a189fb8
commit
7c9257cdf5
5 changed files with 93 additions and 92 deletions
74
src/server/express.ts
Normal file
74
src/server/express.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import express, { Express } from 'express';
|
||||
import { hostHeaderValidation, localhostHostValidation } from './middleware/hostHeaderValidation.js';
|
||||
|
||||
/**
|
||||
* Options for creating an MCP Express application.
|
||||
*/
|
||||
export interface CreateMcpExpressAppOptions {
|
||||
/**
|
||||
* The hostname to bind to. Defaults to '127.0.0.1'.
|
||||
* When set to '127.0.0.1', 'localhost', or '::1', DNS rebinding protection is automatically enabled.
|
||||
*/
|
||||
host?: string;
|
||||
|
||||
/**
|
||||
* List of allowed hostnames for DNS rebinding protection.
|
||||
* If provided, host header validation will be applied using this list.
|
||||
* For IPv6, provide addresses with brackets (e.g., '[::1]').
|
||||
*
|
||||
* This is useful when binding to '0.0.0.0' or '::' but still wanting
|
||||
* to restrict which hostnames are allowed.
|
||||
*/
|
||||
allowedHosts?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Express application pre-configured for MCP servers.
|
||||
*
|
||||
* When the host is '127.0.0.1', 'localhost', or '::1' (the default is '127.0.0.1'),
|
||||
* DNS rebinding protection middleware is automatically applied to protect against
|
||||
* DNS rebinding attacks on localhost servers.
|
||||
*
|
||||
* @param options - Configuration options
|
||||
* @returns A configured Express application
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Basic usage - defaults to 127.0.0.1 with DNS rebinding protection
|
||||
* const app = createMcpExpressApp();
|
||||
*
|
||||
* // Custom host - DNS rebinding protection only applied for localhost hosts
|
||||
* const app = createMcpExpressApp({ host: '0.0.0.0' }); // No automatic DNS rebinding protection
|
||||
* const app = createMcpExpressApp({ host: 'localhost' }); // DNS rebinding protection enabled
|
||||
*
|
||||
* // Custom allowed hosts for non-localhost binding
|
||||
* const app = createMcpExpressApp({ host: '0.0.0.0', allowedHosts: ['myapp.local', 'localhost'] });
|
||||
* ```
|
||||
*/
|
||||
export function createMcpExpressApp(options: CreateMcpExpressAppOptions = {}): Express {
|
||||
const { host = '127.0.0.1', allowedHosts } = options;
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
// If allowedHosts is explicitly provided, use that for validation
|
||||
if (allowedHosts) {
|
||||
app.use(hostHeaderValidation(allowedHosts));
|
||||
} else {
|
||||
// Apply DNS rebinding protection automatically for localhost hosts
|
||||
const localhostHosts = ['127.0.0.1', 'localhost', '::1'];
|
||||
if (localhostHosts.includes(host)) {
|
||||
app.use(localhostHostValidation());
|
||||
} else if (host === '0.0.0.0' || host === '::') {
|
||||
// Warn when binding to all interfaces without DNS rebinding protection
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`Warning: Server is binding to ${host} without DNS rebinding protection. ` +
|
||||
'Consider using the allowedHosts option to restrict allowed hosts, ' +
|
||||
'or use authentication to protect your server.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import supertest from 'supertest';
|
|||
import { Client } from '../client/index.js';
|
||||
import { InMemoryTransport } from '../inMemory.js';
|
||||
import type { Transport } from '../shared/transport.js';
|
||||
import { createMcpExpressApp } from './index.js';
|
||||
import { createMcpExpressApp } from './express.js';
|
||||
import {
|
||||
CreateMessageRequestSchema,
|
||||
CreateMessageResultSchema,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import express, { Express } from 'express';
|
||||
import { mergeCapabilities, Protocol, type NotificationOptions, type ProtocolOptions, type RequestOptions } from '../shared/protocol.js';
|
||||
import { hostHeaderValidation, localhostHostValidation } from './middleware/hostHeaderValidation.js';
|
||||
import {
|
||||
type ClientCapabilities,
|
||||
type CreateMessageRequest,
|
||||
|
|
@ -669,75 +667,3 @@ export class Server<
|
|||
return this.notification({ method: 'notifications/prompts/list_changed' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for creating an MCP Express application.
|
||||
*/
|
||||
export interface CreateMcpExpressAppOptions {
|
||||
/**
|
||||
* The hostname to bind to. Defaults to '127.0.0.1'.
|
||||
* When set to '127.0.0.1', 'localhost', or '::1', DNS rebinding protection is automatically enabled.
|
||||
*/
|
||||
host?: string;
|
||||
|
||||
/**
|
||||
* List of allowed hostnames for DNS rebinding protection.
|
||||
* If provided, host header validation will be applied using this list.
|
||||
* For IPv6, provide addresses with brackets (e.g., '[::1]').
|
||||
*
|
||||
* This is useful when binding to '0.0.0.0' or '::' but still wanting
|
||||
* to restrict which hostnames are allowed.
|
||||
*/
|
||||
allowedHosts?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Express application pre-configured for MCP servers.
|
||||
*
|
||||
* When the host is '127.0.0.1', 'localhost', or '::1' (the default is '127.0.0.1'),
|
||||
* DNS rebinding protection middleware is automatically applied to protect against
|
||||
* DNS rebinding attacks on localhost servers.
|
||||
*
|
||||
* @param options - Configuration options
|
||||
* @returns A configured Express application
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Basic usage - defaults to 127.0.0.1 with DNS rebinding protection
|
||||
* const app = createMcpExpressApp();
|
||||
*
|
||||
* // Custom host - DNS rebinding protection only applied for localhost hosts
|
||||
* const app = createMcpExpressApp({ host: '0.0.0.0' }); // No automatic DNS rebinding protection
|
||||
* const app = createMcpExpressApp({ host: 'localhost' }); // DNS rebinding protection enabled
|
||||
*
|
||||
* // Custom allowed hosts for non-localhost binding
|
||||
* const app = createMcpExpressApp({ host: '0.0.0.0', allowedHosts: ['myapp.local', 'localhost'] });
|
||||
* ```
|
||||
*/
|
||||
export function createMcpExpressApp(options: CreateMcpExpressAppOptions = {}): Express {
|
||||
const { host = '127.0.0.1', allowedHosts } = options;
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
// If allowedHosts is explicitly provided, use that for validation
|
||||
if (allowedHosts) {
|
||||
app.use(hostHeaderValidation(allowedHosts));
|
||||
} else {
|
||||
// Apply DNS rebinding protection automatically for localhost hosts
|
||||
const localhostHosts = ['127.0.0.1', 'localhost', '::1'];
|
||||
if (localhostHosts.includes(host)) {
|
||||
app.use(localhostHostValidation());
|
||||
} else if (host === '0.0.0.0' || host === '::') {
|
||||
// Warn when binding to all interfaces without DNS rebinding protection
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`Warning: Server is binding to ${host} without DNS rebinding protection. ` +
|
||||
'Consider using the allowedHosts option to restrict allowed hosts, ' +
|
||||
'or use authentication to protect your server.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export interface SSEServerTransportOptions {
|
|||
* List of allowed host header values for DNS rebinding protection.
|
||||
* If not specified, host validation is disabled.
|
||||
* @deprecated Use the `hostHeaderValidation` middleware from `@modelcontextprotocol/sdk/server/middleware/hostHeaderValidation.js` instead,
|
||||
* or use `createMcpExpressApp` from `@modelcontextprotocol/sdk/server/index.js` which includes localhost protection by default.
|
||||
* or use `createMcpExpressApp` from `@modelcontextprotocol/sdk/server/express.js` which includes localhost protection by default.
|
||||
*/
|
||||
allowedHosts?: string[];
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ export interface SSEServerTransportOptions {
|
|||
* List of allowed origin header values for DNS rebinding protection.
|
||||
* If not specified, origin validation is disabled.
|
||||
* @deprecated Use the `hostHeaderValidation` middleware from `@modelcontextprotocol/sdk/server/middleware/hostHeaderValidation.js` instead,
|
||||
* or use `createMcpExpressApp` from `@modelcontextprotocol/sdk/server/index.js` which includes localhost protection by default.
|
||||
* or use `createMcpExpressApp` from `@modelcontextprotocol/sdk/server/express.js` which includes localhost protection by default.
|
||||
*/
|
||||
allowedOrigins?: string[];
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ export interface SSEServerTransportOptions {
|
|||
* Enable DNS rebinding protection (requires allowedHosts and/or allowedOrigins to be configured).
|
||||
* Default is false for backwards compatibility.
|
||||
* @deprecated Use the `hostHeaderValidation` middleware from `@modelcontextprotocol/sdk/server/middleware/hostHeaderValidation.js` instead,
|
||||
* or use `createMcpExpressApp` from `@modelcontextprotocol/sdk/server/index.js` which includes localhost protection by default.
|
||||
* or use `createMcpExpressApp` from `@modelcontextprotocol/sdk/server/express.js` which includes localhost protection by default.
|
||||
*/
|
||||
enableDnsRebindingProtection?: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ export interface StreamableHTTPServerTransportOptions {
|
|||
* List of allowed host header values for DNS rebinding protection.
|
||||
* If not specified, host validation is disabled.
|
||||
* @deprecated Use the `hostHeaderValidation` middleware from `@modelcontextprotocol/sdk/server/middleware/hostHeaderValidation.js` instead,
|
||||
* or use `createMcpExpressApp` from `@modelcontextprotocol/sdk/server/index.js` which includes localhost protection by default.
|
||||
* or use `createMcpExpressApp` from `@modelcontextprotocol/sdk/server/express.js` which includes localhost protection by default.
|
||||
*/
|
||||
allowedHosts?: string[];
|
||||
|
||||
|
|
@ -113,7 +113,7 @@ export interface StreamableHTTPServerTransportOptions {
|
|||
* List of allowed origin header values for DNS rebinding protection.
|
||||
* If not specified, origin validation is disabled.
|
||||
* @deprecated Use the `hostHeaderValidation` middleware from `@modelcontextprotocol/sdk/server/middleware/hostHeaderValidation.js` instead,
|
||||
* or use `createMcpExpressApp` from `@modelcontextprotocol/sdk/server/index.js` which includes localhost protection by default.
|
||||
* or use `createMcpExpressApp` from `@modelcontextprotocol/sdk/server/express.js` which includes localhost protection by default.
|
||||
*/
|
||||
allowedOrigins?: string[];
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ export interface StreamableHTTPServerTransportOptions {
|
|||
* Enable DNS rebinding protection (requires allowedHosts and/or allowedOrigins to be configured).
|
||||
* Default is false for backwards compatibility.
|
||||
* @deprecated Use the `hostHeaderValidation` middleware from `@modelcontextprotocol/sdk/server/middleware/hostHeaderValidation.js` instead,
|
||||
* or use `createMcpExpressApp` from `@modelcontextprotocol/sdk/server/index.js` which includes localhost protection by default.
|
||||
* or use `createMcpExpressApp` from `@modelcontextprotocol/sdk/server/express.js` which includes localhost protection by default.
|
||||
*/
|
||||
enableDnsRebindingProtection?: boolean;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue