Merge commit from fork

* Add secure default for DNS rebinding, use in examples

* Update README examples to use createMcpExpressApp, bump to 1.23.0

- Update all server examples to use createMcpExpressApp() for secure defaults
- Rewrite DNS Rebinding Protection section to document new middleware approach
- Bump version from 1.23.0-beta.0 to 1.23.0

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Add IPv6 support, allowedHosts option, and warning for unprotected servers

- Use native URL API to parse Host headers (handles IPv6 correctly)
- Add [::1] (IPv6 localhost) to localhostHostValidation
- Add ::1 to list of localhost hosts that get automatic protection
- Add allowedHosts option to createMcpExpressApp for custom host validation
- Warn when binding to 0.0.0.0 or :: without allowedHosts configured
- Add tests for IPv6 and allowedHosts functionality

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Update remaining examples to use createMcpExpressApp and add DNS rebinding docs

- Update simpleTaskInteractive.ts, ssePollingExample.ts, and elicitationUrlExample.ts
  to use createMcpExpressApp() instead of express() + express.json()
- Add DNS rebinding protection section to docs/server.md explaining how to use
  createMcpExpressApp() and hostHeaderValidation middleware

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Paul Carleton 2025-12-02 13:23:06 +00:00 • committed by GitHub
commit 09623e2aa5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 387 additions and 77 deletions

View file

@ -66,6 +66,34 @@ For a minimal “getting started” experience:
For more detailed patterns (stateless vs stateful, JSON response mode, CORS, DNS rebind protection), see the examples above and the MCP spec sections on transports.
## DNS rebinding protection
MCP servers running on localhost are vulnerable to DNS rebinding attacks. Use `createMcpExpressApp()` to create an Express app with DNS rebinding protection enabled by default:
```typescript
import { createMcpExpressApp } from '@modelcontextprotocol/sdk/server/index.js';
// Protection auto-enabled (default host is 127.0.0.1)
const app = createMcpExpressApp();
// Protection auto-enabled for localhost
const app = createMcpExpressApp({ host: 'localhost' });
// No auto protection when binding to all interfaces
const app = createMcpExpressApp({ host: '0.0.0.0' });
```
For custom host validation, use the middleware directly:
```typescript
import express from 'express';
import { hostHeaderValidation } from '@modelcontextprotocol/sdk/server/middleware/hostHeaderValidation.js';
const app = express();
app.use(express.json());
app.use(hostHeaderValidation(['localhost', '127.0.0.1', 'myhost.local']));
```
## Tools, resources, and prompts
### Tools

View file

@ -8,11 +8,11 @@
// to collect *sensitive* user input via a browser.
import { randomUUID } from 'node:crypto';
import cors from 'cors';
import express, { type Request, type Response } from 'express';
import { type Request, type Response } from 'express';
import { McpServer } from '../../server/mcp.js';
import { StreamableHTTPServerTransport } from '../../server/streamableHttp.js';
import { isInitializeRequest } from '../../types.js';
import { createMcpExpressApp } from '../../server/index.js';
// Create MCP server - it will automatically use AjvJsonSchemaValidator with sensible defaults
// The validator supports format validation (email, date, etc.) if ajv-formats is installed
@ -320,16 +320,7 @@ mcpServer.registerTool(
async function main() {
const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : 3000;
const app = express();
app.use(express.json());
// Allow CORS for all domains, expose the Mcp-Session-Id header
app.use(
cors({
origin: '*',
exposedHeaders: ['Mcp-Session-Id']
})
);
const app = createMcpExpressApp();
// Map to store transports by session ID
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};

View file

@ -11,6 +11,7 @@ import express, { Request, Response } from 'express';
import { randomUUID } from 'node:crypto';
import { z } from 'zod';
import { McpServer } from '../../server/mcp.js';
import { createMcpExpressApp } from '../../server/index.js';
import { StreamableHTTPServerTransport } from '../../server/streamableHttp.js';
import { getOAuthProtectedResourceMetadataUrl, mcpAuthMetadataRouter } from '../../server/auth/router.js';
import { requireBearerAuth } from '../../server/auth/middleware/bearerAuth.js';
@ -214,8 +215,7 @@ function completeURLElicitation(elicitationId: string) {
const MCP_PORT = process.env.MCP_PORT ? parseInt(process.env.MCP_PORT, 10) : 3000;
const AUTH_PORT = process.env.MCP_AUTH_PORT ? parseInt(process.env.MCP_AUTH_PORT, 10) : 3001;
const app = express();
app.use(express.json());
const app = createMcpExpressApp();
// Allow CORS all domains, expose the Mcp-Session-Id header
app.use(

View file

@ -1,10 +1,10 @@
import express, { Request, Response } from 'express';
import { Request, Response } from 'express';
import { randomUUID } from 'node:crypto';
import { McpServer } from '../../server/mcp.js';
import { StreamableHTTPServerTransport } from '../../server/streamableHttp.js';
import * as z from 'zod/v4';
import { CallToolResult, isInitializeRequest } from '../../types.js';
import cors from 'cors';
import { createMcpExpressApp } from '../../server/index.js';
// Create an MCP server with implementation details
const getServer = () => {
@ -90,16 +90,7 @@ const getServer = () => {
return server;
};
const app = express();
app.use(express.json());
// Configure CORS to expose Mcp-Session-Id header for browser-based clients
app.use(
cors({
origin: '*', // Allow all origins - adjust as needed for production
exposedHeaders: ['Mcp-Session-Id']
})
);
const app = createMcpExpressApp();
// Map to store transports by session ID
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};

View file

@ -1,8 +1,9 @@
import express, { Request, Response } from 'express';
import { Request, Response } from 'express';
import { McpServer } from '../../server/mcp.js';
import { SSEServerTransport } from '../../server/sse.js';
import * as z from 'zod/v4';
import { CallToolResult } from '../../types.js';
import { createMcpExpressApp } from '../../server/index.js';
/**
* This example server demonstrates the deprecated HTTP+SSE transport
@ -75,8 +76,7 @@ const getServer = () => {
return server;
};
const app = express();
app.use(express.json());
const app = createMcpExpressApp();
// Store transports by session ID
const transports: Record<string, SSEServerTransport> = {};

View file

@ -1,9 +1,9 @@
import express, { Request, Response } from 'express';
import { Request, Response } from 'express';
import { McpServer } from '../../server/mcp.js';
import { StreamableHTTPServerTransport } from '../../server/streamableHttp.js';
import * as z from 'zod/v4';
import { CallToolResult, GetPromptResult, ReadResourceResult } from '../../types.js';
import cors from 'cors';
import { createMcpExpressApp } from '../../server/index.js';
const getServer = () => {
// Create an MCP server with implementation details
@ -96,16 +96,7 @@ const getServer = () => {
return server;
};
const app = express();
app.use(express.json());
// Configure CORS to expose Mcp-Session-Id header for browser-based clients
app.use(
cors({
origin: '*', // Allow all origins - adjust as needed for production
exposedHeaders: ['Mcp-Session-Id']
})
);
const app = createMcpExpressApp();
app.post('/mcp', async (req: Request, res: Response) => {
const server = getServer();

View file

@ -1,10 +1,11 @@
import express, { Request, Response } from 'express';
import { Request, Response } from 'express';
import { randomUUID } from 'node:crypto';
import * as z from 'zod/v4';
import { McpServer } from '../../server/mcp.js';
import { StreamableHTTPServerTransport } from '../../server/streamableHttp.js';
import { getOAuthProtectedResourceMetadataUrl, mcpAuthMetadataRouter } from '../../server/auth/router.js';
import { requireBearerAuth } from '../../server/auth/middleware/bearerAuth.js';
import { createMcpExpressApp } from '../../server/index.js';
import {
CallToolResult,
ElicitResultSchema,
@ -20,8 +21,6 @@ import { setupAuthServer } from './demoInMemoryOAuthProvider.js';
import { OAuthMetadata } from '../../shared/auth.js';
import { checkResourceAllowed } from '../../shared/auth-utils.js';
import cors from 'cors';
// Check for OAuth flag
const useOAuth = process.argv.includes('--oauth');
const strictOAuth = process.argv.includes('--oauth-strict');
@ -507,16 +506,7 @@ const getServer = () => {
const MCP_PORT = process.env.MCP_PORT ? parseInt(process.env.MCP_PORT, 10) : 3000;
const AUTH_PORT = process.env.MCP_AUTH_PORT ? parseInt(process.env.MCP_AUTH_PORT, 10) : 3001;
const app = express();
app.use(express.json());
// Allow CORS all domains, expose the Mcp-Session-Id header
app.use(
cors({
origin: '*', // Allow all origins
exposedHeaders: ['Mcp-Session-Id']
})
);
const app = createMcpExpressApp();
// Set up OAuth if enabled
let authMiddleware = null;

View file

@ -9,9 +9,9 @@
* creates a task, and the result is fetched via tasks/result endpoint.
*/
import express, { Request, Response } from 'express';
import { Request, Response } from 'express';
import { randomUUID } from 'node:crypto';
import { Server } from '../../server/index.js';
import { createMcpExpressApp, Server } from '../../server/index.js';
import { StreamableHTTPServerTransport } from '../../server/streamableHttp.js';
import {
CallToolResult,
@ -630,8 +630,7 @@ const createServer = (): Server => {
// Express App Setup
// ============================================================================
const app = express();
app.use(express.json());
const app = createMcpExpressApp();
// Map to store transports by session ID
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};

View file

@ -1,4 +1,4 @@
import express, { Request, Response } from 'express';
import { Request, Response } from 'express';
import { randomUUID } from 'node:crypto';
import { McpServer } from '../../server/mcp.js';
import { StreamableHTTPServerTransport } from '../../server/streamableHttp.js';
@ -6,7 +6,7 @@ import { SSEServerTransport } from '../../server/sse.js';
import * as z from 'zod/v4';
import { CallToolResult, isInitializeRequest } from '../../types.js';
import { InMemoryEventStore } from '../shared/inMemoryEventStore.js';
import cors from 'cors';
import { createMcpExpressApp } from '../../server/index.js';
/**
* This example server demonstrates backwards compatibility with both:
@ -71,16 +71,7 @@ const getServer = () => {
};
// Create Express application
const app = express();
app.use(express.json());
// Configure CORS to expose Mcp-Session-Id header for browser-based clients
app.use(
cors({
origin: '*', // Allow all origins - adjust as needed for production
exposedHeaders: ['Mcp-Session-Id']
})
);
const app = createMcpExpressApp();
// Store transports by session ID
const transports: Record<string, StreamableHTTPServerTransport | SSEServerTransport> = {};

View file

@ -12,9 +12,10 @@
* Run with: npx tsx src/examples/server/ssePollingExample.ts
* Test with: curl or the MCP Inspector
*/
import express, { Request, Response } from 'express';
import { Request, Response } from 'express';
import { randomUUID } from 'node:crypto';
import { McpServer } from '../../server/mcp.js';
import { createMcpExpressApp } from '../../server/index.js';
import { StreamableHTTPServerTransport } from '../../server/streamableHttp.js';
import { CallToolResult } from '../../types.js';
import { InMemoryEventStore } from '../shared/inMemoryEventStore.js';
@ -103,7 +104,7 @@ server.tool(
);
// Set up Express app
const app = express();
const app = createMcpExpressApp();
app.use(cors());
// Create event store for resumability
@ -112,8 +113,8 @@ const eventStore = new InMemoryEventStore();
// Track transports by session ID for session reuse
const transports = new Map<string, StreamableHTTPServerTransport>();
// Handle all MCP requests - use express.json() only for this route
app.all('/mcp', express.json(), async (req: Request, res: Response) => {
// Handle all MCP requests
app.all('/mcp', async (req: Request, res: Response) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
// Reuse existing transport or create new one

View file

@ -1,8 +1,9 @@
import express, { Request, Response } from 'express';
import { Request, Response } from 'express';
import { randomUUID } from 'node:crypto';
import { McpServer } from '../../server/mcp.js';
import { StreamableHTTPServerTransport } from '../../server/streamableHttp.js';
import { isInitializeRequest, ReadResourceResult } from '../../types.js';
import { createMcpExpressApp } from '../../server/index.js';
// Create an MCP server with implementation details
const server = new McpServer({
@ -34,8 +35,7 @@ const resourceChangeInterval = setInterval(() => {
addResource(name, `Content for ${name}`);
}, 5000); // Change resources every 5 seconds for testing
const app = express();
app.use(express.json());
const app = createMcpExpressApp();
app.post('/mcp', async (req: Request, res: Response) => {
console.log('Received MCP request:', req.body);

View file

@ -1,7 +1,9 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
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 {
CreateMessageRequestSchema,
CreateMessageResultSchema,
@ -2057,6 +2059,167 @@ test('should respect log level for transport with sessionId', async () => {
expect(clientTransport.onmessage).toHaveBeenCalled();
});
describe('createMcpExpressApp', () => {
test('should create an Express app', () => {
const app = createMcpExpressApp();
expect(app).toBeDefined();
});
test('should parse JSON bodies', async () => {
const app = createMcpExpressApp({ host: '0.0.0.0' }); // Disable host validation for this test
app.post('/test', (req, res) => {
res.json({ received: req.body });
});
const response = await supertest(app).post('/test').send({ hello: 'world' }).set('Content-Type', 'application/json');
expect(response.status).toBe(200);
expect(response.body).toEqual({ received: { hello: 'world' } });
});
test('should reject requests with invalid Host header by default', async () => {
const app = createMcpExpressApp();
app.post('/test', (_req, res) => {
res.json({ success: true });
});
const response = await supertest(app).post('/test').set('Host', 'evil.com:3000').send({});
expect(response.status).toBe(403);
expect(response.body).toEqual({
jsonrpc: '2.0',
error: {
code: -32000,
message: 'Invalid Host: evil.com'
},
id: null
});
});
test('should allow requests with localhost Host header', async () => {
const app = createMcpExpressApp();
app.post('/test', (_req, res) => {
res.json({ success: true });
});
const response = await supertest(app).post('/test').set('Host', 'localhost:3000').send({});
expect(response.status).toBe(200);
expect(response.body).toEqual({ success: true });
});
test('should allow requests with 127.0.0.1 Host header', async () => {
const app = createMcpExpressApp();
app.post('/test', (_req, res) => {
res.json({ success: true });
});
const response = await supertest(app).post('/test').set('Host', '127.0.0.1:3000').send({});
expect(response.status).toBe(200);
expect(response.body).toEqual({ success: true });
});
test('should not apply host validation when host is 0.0.0.0', async () => {
const app = createMcpExpressApp({ host: '0.0.0.0' });
app.post('/test', (_req, res) => {
res.json({ success: true });
});
// Should allow any host when bound to 0.0.0.0
const response = await supertest(app).post('/test').set('Host', 'any-host.com:3000').send({});
expect(response.status).toBe(200);
expect(response.body).toEqual({ success: true });
});
test('should apply host validation when host is explicitly localhost', async () => {
const app = createMcpExpressApp({ host: 'localhost' });
app.post('/test', (_req, res) => {
res.json({ success: true });
});
// Should reject non-localhost hosts
const response = await supertest(app).post('/test').set('Host', 'evil.com:3000').send({});
expect(response.status).toBe(403);
});
test('should allow requests with IPv6 localhost Host header', async () => {
const app = createMcpExpressApp();
app.post('/test', (_req, res) => {
res.json({ success: true });
});
const response = await supertest(app).post('/test').set('Host', '[::1]:3000').send({});
expect(response.status).toBe(200);
expect(response.body).toEqual({ success: true });
});
test('should apply host validation when host is ::1 (IPv6 localhost)', async () => {
const app = createMcpExpressApp({ host: '::1' });
app.post('/test', (_req, res) => {
res.json({ success: true });
});
// Should reject non-localhost hosts
const response = await supertest(app).post('/test').set('Host', 'evil.com:3000').send({});
expect(response.status).toBe(403);
});
test('should warn when binding to 0.0.0.0', () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
createMcpExpressApp({ host: '0.0.0.0' });
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('0.0.0.0'));
warnSpy.mockRestore();
});
test('should warn when binding to :: (IPv6 all interfaces)', () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
createMcpExpressApp({ host: '::' });
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('::'));
warnSpy.mockRestore();
});
test('should use custom allowedHosts when provided', async () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const app = createMcpExpressApp({ host: '0.0.0.0', allowedHosts: ['myapp.local', 'localhost'] });
app.post('/test', (_req, res) => {
res.json({ success: true });
});
// Should not warn when allowedHosts is provided
expect(warnSpy).not.toHaveBeenCalled();
warnSpy.mockRestore();
// Should allow myapp.local
const allowedResponse = await supertest(app).post('/test').set('Host', 'myapp.local:3000').send({});
expect(allowedResponse.status).toBe(200);
// Should reject other hosts
const rejectedResponse = await supertest(app).post('/test').set('Host', 'evil.com:3000').send({});
expect(rejectedResponse.status).toBe(403);
});
test('should override default localhost validation when allowedHosts is provided', async () => {
// Even though host is localhost, we're using custom allowedHosts
const app = createMcpExpressApp({ host: 'localhost', allowedHosts: ['custom.local'] });
app.post('/test', (_req, res) => {
res.json({ success: true });
});
// Should reject localhost since it's not in allowedHosts
const response = await supertest(app).post('/test').set('Host', 'localhost:3000').send({});
expect(response.status).toBe(403);
// Should allow custom.local
const allowedResponse = await supertest(app).post('/test').set('Host', 'custom.local:3000').send({});
expect(allowedResponse.status).toBe(200);
});
});
describe('Task-based execution', () => {
test('server with TaskStore should handle task-based tool execution', async () => {
const taskStore = new InMemoryTaskStore();

View file

@ -1,4 +1,6 @@
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,
@ -667,3 +669,75 @@ 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;
}

View file

@ -0,0 +1,79 @@
import { Request, Response, NextFunction, RequestHandler } from 'express';
/**
* Express middleware for DNS rebinding protection.
* Validates Host header hostname (port-agnostic) against an allowed list.
*
* This is particularly important for servers without authorization or HTTPS,
* such as localhost servers or development servers. DNS rebinding attacks can
* bypass same-origin policy by manipulating DNS to point a domain to a
* localhost address, allowing malicious websites to access your local server.
*
* @param allowedHostnames - List of allowed hostnames (without ports).
* For IPv6, provide the address with brackets (e.g., '[::1]').
* @returns Express middleware function
*
* @example
* ```typescript
* const middleware = hostHeaderValidation(['localhost', '127.0.0.1', '[::1]']);
* app.use(middleware);
* ```
*/
export function hostHeaderValidation(allowedHostnames: string[]): RequestHandler {
return (req: Request, res: Response, next: NextFunction) => {
const hostHeader = req.headers.host;
if (!hostHeader) {
res.status(403).json({
jsonrpc: '2.0',
error: {
code: -32000,
message: 'Missing Host header'
},
id: null
});
return;
}
// Use URL API to parse hostname (handles IPv4, IPv6, and regular hostnames)
let hostname: string;
try {
hostname = new URL(`http://${hostHeader}`).hostname;
} catch {
res.status(403).json({
jsonrpc: '2.0',
error: {
code: -32000,
message: `Invalid Host header: ${hostHeader}`
},
id: null
});
return;
}
if (!allowedHostnames.includes(hostname)) {
res.status(403).json({
jsonrpc: '2.0',
error: {
code: -32000,
message: `Invalid Host: ${hostname}`
},
id: null
});
return;
}
next();
};
}
/**
* Convenience middleware for localhost DNS rebinding protection.
* Allows only localhost, 127.0.0.1, and [::1] (IPv6 localhost) hostnames.
*
* @example
* ```typescript
* app.use(localhostHostValidation());
* ```
*/
export function localhostHostValidation(): RequestHandler {
return hostHeaderValidation(['localhost', '127.0.0.1', '[::1]']);
}

View file

@ -16,18 +16,24 @@ 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.
*/
allowedHosts?: string[];
/**
* 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.
*/
allowedOrigins?: string[];
/**
* 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.
*/
enableDnsRebindingProtection?: boolean;
}

View file

@ -104,18 +104,24 @@ 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.
*/
allowedHosts?: string[];
/**
* 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.
*/
allowedOrigins?: string[];
/**
* 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.
*/
enableDnsRebindingProtection?: boolean;