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)
name: 'no-dns-rebinding-protection-server', function createMcpServer() {
version: '1.0.0' const server = new McpServer({
}); name: 'no-dns-rebinding-protection-server',
version: '1.0.0'
});
// Add a simple tool server.registerTool(
server.tool( 'echo',
'echo', {
'Echo the input back', description: 'Echo the input back',
{ message: { type: 'string' } }, inputSchema: { message: { type: 'string' } }
async ({ message }) => ({ },
content: [{ type: 'text', text: `Echo: ${message}` }] async ({ 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,43 +41,49 @@ export function createServer(
tokenVerifier, tokenVerifier,
prmResourceOverride prmResourceOverride
} = options; } = options;
const server = new Server( // Factory: create a fresh Server per request to avoid "Already connected" errors
{ // after the v1.26.0 security fix (GHSA-345p-7cg4-v4c7)
name: 'auth-prm-pathbased-server', function createMcpServer() {
version: '1.0.0' const server = new Server(
}, {
{ name: 'auth-prm-pathbased-server',
capabilities: { version: '1.0.0'
tools: {} },
} {
} capabilities: {
); tools: {}
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'test-tool',
inputSchema: { type: 'object' }
} }
]
};
});
server.setRequestHandler(
CallToolRequestSchema,
async (request): Promise<CallToolResult> => {
if (request.params.name === 'test-tool') {
return {
content: [{ type: 'text', text: 'test' }]
};
} }
throw new McpError( );
ErrorCode.InvalidParams,
`Tool ${request.params.name} not found` server.setRequestHandler(ListToolsRequestSchema, async () => {
); return {
} tools: [
); {
name: 'test-tool',
inputSchema: { type: 'object' }
}
]
};
});
server.setRequestHandler(
CallToolRequestSchema,
async (request): Promise<CallToolResult> => {
if (request.params.name === 'test-tool') {
return {
content: [{ type: 'text', text: 'test' }]
};
}
throw new McpError(
ErrorCode.InvalidParams,
`Tool ${request.params.name} not found`
);
}
);
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
}); });