fix: create fresh Server per request in auth test helpers (#141)

The auth test server helper and no-dns-rebinding-protection example
reused a single Server instance across requests, calling connect()
on each new transport. SDK v1.26.0's security fix (GHSA-345p-7cg4-v4c7)
now throws "Already connected to a transport" in this case.

Fix by wrapping Server creation in a factory function called per
request, matching the pattern already used by tools_call.ts and
everything-server.ts.

Also updates no-dns-rebinding-protection.ts to use registerTool()
instead of the deprecated tool() API.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Felix Weinberger 2026-02-10 19:22:16 +00:00 • committed by GitHub
commit 37225cef7d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 61 additions and 47 deletions

View file

@ -13,21 +13,27 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import express from 'express'; import express from 'express';
// Create minimal MCP server // Create a fresh MCP server per request to avoid "Already connected" errors
const server = new McpServer({ // after the v1.26.0 security fix (GHSA-345p-7cg4-v4c7)
function createMcpServer() {
const server = new McpServer({
name: 'no-dns-rebinding-protection-server', name: 'no-dns-rebinding-protection-server',
version: '1.0.0' version: '1.0.0'
}); });
// Add a simple tool server.registerTool(
server.tool(
'echo', 'echo',
'Echo the input back', {
{ message: { type: 'string' } }, description: 'Echo the input back',
inputSchema: { message: { type: 'string' } }
},
async ({ message }) => ({ async ({ message }) => ({
content: [{ type: 'text', text: `Echo: ${message}` }] content: [{ type: 'text', text: `Echo: ${message}` }]
}) })
); );
return server;
}
// === VULNERABLE EXPRESS APP === // === VULNERABLE EXPRESS APP ===
// This intentionally does NOT use createMcpExpressApp() or localhostHostValidation() // This intentionally does NOT use createMcpExpressApp() or localhostHostValidation()
@ -37,6 +43,7 @@ app.use(express.json());
app.post('/mcp', async (req, res) => { app.post('/mcp', async (req, res) => {
try { try {
const server = createMcpServer();
// Stateless: no session ID // Stateless: no session ID
const transport = new StreamableHTTPServerTransport({ const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined sessionIdGenerator: undefined

View file

@ -41,6 +41,9 @@ export function createServer(
tokenVerifier, tokenVerifier,
prmResourceOverride prmResourceOverride
} = options; } = options;
// Factory: create a fresh Server per request to avoid "Already connected" errors
// after the v1.26.0 security fix (GHSA-345p-7cg4-v4c7)
function createMcpServer() {
const server = new Server( const server = new Server(
{ {
name: 'auth-prm-pathbased-server', name: 'auth-prm-pathbased-server',
@ -79,6 +82,9 @@ export function createServer(
} }
); );
return server;
}
const app = express(); const app = express();
app.use(express.json()); app.use(express.json());
@ -151,6 +157,7 @@ export function createServer(
authMiddleware(req, res, async (err?: any) => { authMiddleware(req, res, async (err?: any) => {
if (err) return next(err); if (err) return next(err);
const server = createMcpServer();
const transport = new StreamableHTTPServerTransport({ const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined sessionIdGenerator: undefined
}); });